[
  {
    "path": ".codacy.yml",
    "content": "exclude_paths:\n  - test/*\n  - test/augmenters/*\n  - test/augmentables/*\n  - checks/*\n  - imgaug/external/*\n  - old_version/*\n  - generate_documentation_images.py\n  - generate_example_images.py\n"
  },
  {
    "path": ".github/workflows/build_wheels.yml",
    "content": "# This action generates wheel files for python 2 and 3.\nname: build wheels\n\non:\n  push:\n    branches:\n      - 'master'\n\njobs:\n  build:\n\n    # There were errors on Mac that would lead to non-stop printing of\n    # error messages forever instead of the job crashing. To prevent this,\n    # a timeout is placed here (default value is otherwise 360min).\n    timeout-minutes: 30\n\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest, windows-latest]\n        # see supported versions at\n        # https://raw.githubusercontent.com/actions/python-versions/master/versions-manifest.json\n        python-version: [2.7, 3.5, 3.6, 3.7, 3.8]\n        exclude:\n          - os: windows-latest\n            python-version: 2.7  # causes a Shapely install error\n    env:\n      OS: ${{ matrix.os }}\n      PYTHON: ${{ matrix.python-version }}\n    steps:\n    - uses: actions/checkout@v2\n\n    # ----------------\n    # Install python and base packages\n    # ----------------\n    - name: Set up python ${{ matrix.python-version }} on ${{ runner.os }}\n      uses: actions/setup-python@v2\n      with:\n        python-version: ${{ matrix.python-version }}\n\n    - name: Display python version\n      run: |\n        python -c \"import sys; print(sys.version)\"\n\n    - name: Display system information\n      run : |\n        python -c \"import sys; print(sys.maxsize);\"\n        python -c \"import platform; print(platform.uname());\"\n        python -c \"import platform; print(platform.platform());\"\n        python -c \"import platform; print(platform.architecture());\"\n        python -c \"import platform; print(platform.processor());\"\n        python -c \"import platform; print(platform.python_compiler());\"\n\n    - name: Upgrade basic packages\n      run: |\n        python -m pip install --upgrade pip setuptools wheel\n\n    # ----------------\n    # Set up pip cache\n    # ----------------\n    - name: Get Date\n      id: get-date\n      run: |\n        echo \"::set-output name=date::$(/bin/date -u \"+%Y%m%d\")\"\n      shell: bash\n\n    - uses: actions/cache@v1\n      if: startsWith(runner.os, 'Linux')\n      with:\n        path: ~/.cache/pip\n        key: ${{ runner.os }}-pip-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/requirements.txt') }}\n        restore-keys: |\n          ${{ runner.os }}-pip-\n\n    - uses: actions/cache@v1\n      if: startsWith(runner.os, 'macOS')\n      with:\n        path: ~/Library/Caches/pip\n        key: ${{ runner.os }}-pip-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/requirements.txt') }}\n        restore-keys: |\n          ${{ runner.os }}-pip-\n\n    - uses: actions/cache@v1\n      if: startsWith(runner.os, 'Windows')\n      with:\n        path: ~\\AppData\\Local\\pip\\Cache\n        key: ${{ runner.os }}-pip-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/requirements.txt') }}\n        restore-keys: |\n          ${{ runner.os }}-pip-\n\n    # ----------------\n    # Install dependencies\n    # ----------------\n    - name: Install dependencies\n      run: |\n        pip install -r requirements.txt\n\n    # ----------------\n    # Generate wheels\n    # ----------------\n    - name: Generate wheels\n      run: |\n        python setup.py sdist\n        python setup.py bdist_wheel\n\n    # ----------------\n    # Upload artifacts\n    # ----------------\n    - uses: actions/upload-artifact@v2\n      with:\n        name: ${{ runner.os }}-py${{ matrix.python-version }}-dist\n        path: dist/\n"
  },
  {
    "path": ".github/workflows/test_master.yml",
    "content": "# This is effectively identical to pr_or_push.yml, with the exceptions of:\n# (1) This is only executed upon pushes to master\n# (2) This executes tests for more different python versions\nname: test master\n\non:\n  push:\n    branches:\n      - 'master'\n\njobs:\n  build:\n\n    # There were errors on Mac that would lead to non-stop printing of\n    # error messages forever instead of the job crashing. To prevent this,\n    # a timeout is placed here (default value is otherwise 360min).\n    # Usually, jobs currently run through in around 10min.\n    timeout-minutes: 60\n\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest, windows-latest]\n        # see supported versions at\n        # https://raw.githubusercontent.com/actions/python-versions/master/versions-manifest.json\n        python-version: [2.7, 3.5, 3.6, 3.7, 3.8]\n        exclude:\n          - os: windows-latest\n            python-version: 2.7  # causes a Shapely install error\n    env:\n      OS: ${{ matrix.os }}\n      PYTHON: ${{ matrix.python-version }}\n    steps:\n    - uses: actions/checkout@v2\n\n    # ----------------\n    # Install python and base packages\n    # ----------------\n    - name: Set up Python ${{ matrix.python-version }} on ${{ runner.os }}\n      uses: actions/setup-python@v2\n      with:\n        python-version: ${{ matrix.python-version }}\n\n    - name: Display python version\n      run: |\n        python -c \"import sys; print(sys.version)\"\n\n    - name: Display system information\n      run : |\n        python -c \"import sys; print(sys.maxsize);\"\n        python -c \"import platform; print(platform.uname());\"\n        python -c \"import platform; print(platform.platform());\"\n        python -c \"import platform; print(platform.architecture());\"\n        python -c \"import platform; print(platform.processor());\"\n        python -c \"import platform; print(platform.python_compiler());\"\n\n    - name: Upgrade basic packages\n      run: |\n        python -m pip install --upgrade pip setuptools wheel\n\n    # ----------------\n    # Set up pip cache\n    # ----------------\n    - name: Get Date\n      id: get-date\n      run: |\n        echo \"::set-output name=date::$(/bin/date -u \"+%Y%m%d\")\"\n      shell: bash\n\n    - uses: actions/cache@v1\n      if: startsWith(runner.os, 'Linux')\n      with:\n        path: ~/.cache/pip\n        key: ${{ runner.os }}-pip-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/requirements.txt') }}\n        restore-keys: |\n          ${{ runner.os }}-pip-\n\n    - uses: actions/cache@v1\n      if: startsWith(runner.os, 'macOS')\n      with:\n        path: ~/Library/Caches/pip\n        key: ${{ runner.os }}-pip-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/requirements.txt') }}\n        restore-keys: |\n          ${{ runner.os }}-pip-\n\n    - uses: actions/cache@v1\n      if: startsWith(runner.os, 'Windows')\n      with:\n        path: ~\\AppData\\Local\\pip\\Cache\n        key: ${{ runner.os }}-pip-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/requirements.txt') }}\n        restore-keys: |\n          ${{ runner.os }}-pip-\n\n    # ----------------\n    # Install dependencies\n    # ----------------\n    - name: Install dependencies\n      run: |\n        pip install -r requirements.txt\n\n    - name: Install test dependencies\n      run: |\n        pip install --upgrade -r test/requirements.txt\n\n    - name: Install further test tools\n      run: |\n        pip install coverage pytest-cov flake8\n\n    # ----------------\n    # Install library\n    # ----------------\n    - name: Install library\n      run: |\n        pip install .\n\n    # ----------------\n    # Run checks and tests\n    # ----------------\n    - name: Run flake8\n      run: |\n        flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics --exclude=\".svn,CVS,.bzr,.hg,.git,__pycache__,poly_point_isect.py\"\n\n    - name: Run tests\n      run: |\n        python -m pytest --verbose --xdoctest-modules -s --durations=50 -Walways\n\n    # ----------------\n    # Code coverage reports\n    # ----------------\n    # Add 'coverage html -d out_foldername' to add html reports\n    # Dont deactivate -Walways here, otherwise some tests fail as warnings\n    # are no longer produced.\n    - name: Generate code coverage report\n      run: |\n        coverage run --source imgaug -m pytest --verbose -Walways\n        coverage xml\n        coverage report\n\n    #- name: Upload coverage report to codacy\n    #  uses: codacy/codacy-coverage-reporter-action@master\n    #  with:\n    #    project-token: ${{ secrets.CODACY_TOKEN }}\n    #    coverage-reports: coverage.xml\n\n    - name: Upload coverage to Codecov\n      uses: codecov/codecov-action@v1\n      with:\n        token: ${{ secrets.CODECOV_TOKEN }}\n        file: ./coverage.xml\n        flags: unittests\n        # right now the env_vars argument causes a warning, see\n        # https://github.com/codecov/codecov-action/issues/80\n        #env_vars: OS,PYTHON\n        name: codecov-umbrella\n        fail_ci_if_error: false\n"
  },
  {
    "path": ".github/workflows/test_pull_requests.yml",
    "content": "name: test pull requests\n\non:\n  push:\n    branches:\n      - '!master'\n  pull_request:\n\njobs:\n  build:\n\n    # There were errors on Mac that would lead to non-stop printing of\n    # error messages forever instead of the job crashing. To prevent this,\n    # a timeout is placed here (default value is otherwise 360min).\n    # Usually, jobs currently run through in around 10min.\n    timeout-minutes: 45\n\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest, windows-latest]\n        # see supported versions at\n        # https://raw.githubusercontent.com/actions/python-versions/master/versions-manifest.json\n        python-version: [2.7, 3.5, 3.6, 3.7, 3.8]\n        # test only 2.7 and the latest 3.x on mac\n        # test only the latest 3.x on windows\n        exclude:\n          - os: macos-latest\n            python-version: 3.5\n          - os: macos-latest\n            python-version: 3.6\n          - os: macos-latest\n            python-version: 3.7\n          - os: windows-latest\n            python-version: 2.7  # causes a Shapely install error\n          - os: windows-latest\n            python-version: 3.5\n          - os: windows-latest\n            python-version: 3.6\n          - os: windows-latest\n            python-version: 3.7\n    env:\n      OS: ${{ matrix.os }}\n      PYTHON: ${{ matrix.python-version }}\n    steps:\n    - uses: actions/checkout@v2\n\n    # ----------------\n    # Install python and base packages\n    # ----------------\n    - name: Set up Python ${{ matrix.python-version }} on ${{ runner.os }}\n      uses: actions/setup-python@v2\n      with:\n        python-version: ${{ matrix.python-version }}\n\n    - name: Display python version\n      run: |\n        python -c \"import sys; print(sys.version)\"\n\n    - name: Display system information\n      run : |\n        python -c \"import sys; print(sys.maxsize);\"\n        python -c \"import platform; print(platform.uname());\"\n        python -c \"import platform; print(platform.platform());\"\n        python -c \"import platform; print(platform.architecture());\"\n        python -c \"import platform; print(platform.processor());\"\n        python -c \"import platform; print(platform.python_compiler());\"\n\n    - name: Upgrade basic packages\n      run: |\n        python -m pip install --upgrade pip setuptools wheel\n\n    # ----------------\n    # Set up pip cache\n    # ----------------\n    - name: Get Date\n      id: get-date\n      run: |\n        echo \"::set-output name=date::$(/bin/date -u \"+%Y%m%d\")\"\n      shell: bash\n\n    - uses: actions/cache@v1\n      if: startsWith(runner.os, 'Linux')\n      with:\n        path: ~/.cache/pip\n        key: ${{ runner.os }}-pip-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/requirements.txt') }}\n        restore-keys: |\n          ${{ runner.os }}-pip-\n\n    - uses: actions/cache@v1\n      if: startsWith(runner.os, 'macOS')\n      with:\n        path: ~/Library/Caches/pip\n        key: ${{ runner.os }}-pip-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/requirements.txt') }}\n        restore-keys: |\n          ${{ runner.os }}-pip-\n\n    - uses: actions/cache@v1\n      if: startsWith(runner.os, 'Windows')\n      with:\n        path: ~\\AppData\\Local\\pip\\Cache\n        key: ${{ runner.os }}-pip-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/requirements.txt') }}\n        restore-keys: |\n          ${{ runner.os }}-pip-\n\n    # ----------------\n    # Install dependencies\n    # ----------------\n    - name: Install dependencies\n      run: |\n        pip install -r requirements.txt\n\n    - name: Install test dependencies\n      run: |\n        pip install --upgrade -r test/requirements.txt\n\n    - name: Install further test tools\n      run: |\n        pip install coverage pytest-cov flake8\n\n    # ----------------\n    # Install library\n    # ----------------\n    - name: Install library\n      run: |\n        pip install .\n\n    # ----------------\n    # Run checks and tests\n    # ----------------\n    - name: Run flake8\n      run: |\n        flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics --exclude=\".svn,CVS,.bzr,.hg,.git,__pycache__,poly_point_isect.py\"\n\n    - name: Run tests\n      run: |\n        python -m pytest --verbose --xdoctest-modules -s --durations=50 -Walways\n\n    # ----------------\n    # Code coverage reports\n    # ----------------\n    # Add 'coverage html -d out_foldername' to add html reports\n    # Dont deactivate -Walways here, otherwise some tests fail as warnings\n    # are no longer produced.\n    - name: Generate code coverage report\n      run: |\n        coverage run --source imgaug -m pytest --verbose -Walways\n        coverage xml\n        coverage report\n\n    #- name: Upload coverage report to codacy\n    #  uses: codacy/codacy-coverage-reporter-action@master\n    #  with:\n    #    project-token: ${{ secrets.CODACY_TOKEN }}\n    #    coverage-reports: coverage.xml\n\n    - name: Upload coverage to Codecov\n      uses: codecov/codecov-action@v1\n      with:\n        token: ${{ secrets.CODECOV_TOKEN }}\n        file: ./coverage.xml\n        flags: unittests\n        # right now the env_vars argument causes a warning, see\n        # https://github.com/codecov/codecov-action/issues/80\n        #env_vars: OS,PYTHON\n        name: codecov-umbrella\n        fail_ci_if_error: false\n"
  },
  {
    "path": ".gitignore",
    "content": "*.py~\n*.rst~\n*.md~\n*.bak\n*.lprof\nreinstall.sh\nreinstall_conda.sh\ntodo.txt\npypi-install-guide.txt\nchecks/bb_aug.jpg\nchecks/elastic_transformations.jpg\nimgaug/parameters-testcode.py\nimgaug/bak/*\nimgaug/quokka_depth_map.xcf\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nenv/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# PyCHarm\n.idea/\n\n# virtualenv\nvenv/\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n"
  },
  {
    "path": ".pylintrc",
    "content": "[MASTER]\n\n# A comma-separated list of package or module names from where C extensions may\n# be loaded. Extensions are loading into the active Python interpreter and may\n# run arbitrary code.\nextension-pkg-whitelist=cv2,\n                        scipy,\n                        scipy.spatial,\n                        numpy,\n                        numpy.random,\n                        numpy.random.bit_generator,\n                        PIL,\n                        PIL.Image,\n                        PIL.ImageOps,\n                        skimage,\n                        skimage.feature,\n                        skimage.transform,\n                        skimage.segmentation\n\n# Add files or directories to the blacklist. They should be base names, not\n# paths.\nignore=CVS\n\n# Add files or directories matching the regex patterns to the blacklist. The\n# regex matches against base names, not paths.\nignore-patterns=opensimplex\\.py,\n                poly_point_isect_py2py3\\.py\n\n# Python code to execute, usually for sys.path manipulation such as\n# pygtk.require().\n#init-hook=\n\n# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the\n# number of processors available to use.\njobs=1\n\n# Control the amount of potential inferred values when inferring a single\n# object. This can help the performance when dealing with large functions or\n# complex, nested conditions.\nlimit-inference-results=100\n\n# List of plugins (as comma separated values of python module names) to load,\n# usually to register additional checkers.\nload-plugins=\n\n# Pickle collected data for later comparisons.\npersistent=yes\n\n# Specify a configuration file.\n#rcfile=\n\n# When enabled, pylint would attempt to guess common misconfiguration and emit\n# user-friendly hints instead of false-positive error messages.\nsuggestion-mode=yes\n\n# Allow loading of arbitrary C extensions. Extensions are imported into the\n# active Python interpreter and may run arbitrary code.\nunsafe-load-any-extension=no\n\n\n[MESSAGES CONTROL]\n\n# Only show warnings with the listed confidence levels. Leave empty to show\n# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.\nconfidence=\n\n# Disable the message, report, category or checker with the given id(s). You\n# can either give multiple identifiers separated by comma (,) or put this\n# option multiple times (only on the command line, not in the configuration\n# file where it should appear only once). You can also use \"--disable=all\" to\n# disable everything first and then reenable specific checks. For example, if\n# you want to run only the similarities checker, you can use \"--disable=all\n# --enable=similarities\". If you want to run only the classes checker, but have\n# no Warning level messages displayed, use \"--disable=all --enable=classes\n# --disable=W\".\ndisable=print-statement,\n        parameter-unpacking,\n        unpacking-in-except,\n        old-raise-syntax,\n        backtick,\n        long-suffix,\n        old-ne-operator,\n        old-octal-literal,\n        import-star-module-level,\n        non-ascii-bytes-literal,\n        raw-checker-failed,\n        bad-inline-option,\n        locally-disabled,\n        file-ignored,\n        suppressed-message,\n        useless-suppression,\n        deprecated-pragma,\n        use-symbolic-message-instead,\n        apply-builtin,\n        basestring-builtin,\n        buffer-builtin,\n        cmp-builtin,\n        coerce-builtin,\n        execfile-builtin,\n        file-builtin,\n        long-builtin,\n        raw_input-builtin,\n        reduce-builtin,\n        standarderror-builtin,\n        unicode-builtin,\n        xrange-builtin,\n        coerce-method,\n        delslice-method,\n        getslice-method,\n        setslice-method,\n        no-absolute-import,\n        old-division,\n        dict-iter-method,\n        dict-view-method,\n        next-method-called,\n        metaclass-assignment,\n        indexing-exception,\n        raising-string,\n        reload-builtin,\n        oct-method,\n        hex-method,\n        nonzero-method,\n        cmp-method,\n        input-builtin,\n        round-builtin,\n        intern-builtin,\n        unichr-builtin,\n        map-builtin-not-iterating,\n        zip-builtin-not-iterating,\n        range-builtin-not-iterating,\n        filter-builtin-not-iterating,\n        using-cmp-argument,\n        eq-without-hash,\n        div-method,\n        idiv-method,\n        rdiv-method,\n        exception-message-attribute,\n        invalid-str-codec,\n        sys-max-int,\n        bad-python3-import,\n        deprecated-string-function,\n        deprecated-str-translate-call,\n        deprecated-itertools-function,\n        deprecated-types-field,\n        next-method-defined,\n        dict-items-not-iterating,\n        dict-keys-not-iterating,\n        dict-values-not-iterating,\n        deprecated-operator-function,\n        deprecated-urllib-function,\n        xreadlines-attribute,\n        deprecated-sys-function,\n        exception-escape,\n        comprehension-escape,\n        # ------------- non-standard for imgaug -------------\n        fixme,  # no warnings for TODOs\n        line-too-long,  # required for type definitions in docstrings\n        too-many-lines,  # currently unfulfillable\n        useless-object-inheritance,  # pylint complains that Foo(object) shouldn't be used anymore in py3+, but is required for py2.7\n        import-outside-toplevel,  # necessary e.g. for optional dependencies\n        too-many-arguments,\n        too-many-branches,\n        too-many-locals,\n        too-many-instance-attributes,\n        too-few-public-methods,\n        too-many-public-methods,\n        too-many-return-statements,\n        too-many-statements,\n        too-many-ancestors,\n        len-as-condition,  # more annoying than useful warning, suggestion doesn't even work with np arrays\n        unused-argument,  # without this pylint complains about almost every _augment_batch() implementation not using 'parents' and 'hooks'; due to inheritance we can't do anything about that\n        no-self-use,  # without this pylint complains about every get_parameters() implementation that returns only []; due to inheritance we can't do anything about that\n        protected-access,  # we use plenty of calls of functions that are only marked private to discourage calls of them from outside of the library, but not from within the library\n\n# Enable the message, report, category or checker with the given id(s). You can\n# either give multiple identifier separated by comma (,) or put this option\n# multiple time (only on the command line, not in the configuration file where\n# it should appear only once). See also the \"--disable\" option for examples.\nenable=c-extension-no-member\n\n\n[REPORTS]\n\n# Python expression which should return a score less than or equal to 10. You\n# have access to the variables 'error', 'warning', 'refactor', and 'convention'\n# which contain the number of messages in each category, as well as 'statement'\n# which is the total number of statements analyzed. This score is used by the\n# global evaluation report (RP0004).\nevaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)\n\n# Template used to display messages. This is a python new-style format string\n# used to format the message information. See doc for all details.\n#msg-template=\n\n# Set the output format. Available formats are text, parseable, colorized, json\n# and msvs (visual studio). You can also give a reporter class, e.g.\n# mypackage.mymodule.MyReporterClass.\noutput-format=text\n\n# Tells whether to display a full report or only the messages.\nreports=no\n\n# Activate the evaluation score.\nscore=yes\n\n\n[REFACTORING]\n\n# Maximum number of nested blocks for function / method body\nmax-nested-blocks=5\n\n# Complete name of functions that never returns. When checking for\n# inconsistent-return-statements if a never returning function is called then\n# it will be considered as an explicit return statement and no message will be\n# printed.\nnever-returning-functions=sys.exit\n\n\n[FORMAT]\n\n# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.\nexpected-line-ending-format=\n\n# Regexp for a line that is allowed to be longer than the limit.\nignore-long-lines=^\\s*(# )?<?https?://\\S+>?$\n\n# Number of spaces of indent required inside a hanging or continued line.\nindent-after-paren=4\n\n# String used as indentation unit. This is usually \"    \" (4 spaces) or \"\\t\" (1\n# tab).\nindent-string='    '\n\n# Maximum number of characters on a single line.\nmax-line-length=100\n\n# Maximum number of lines in a module.\nmax-module-lines=1000\n\n# List of optional constructs for which whitespace checking is disabled. `dict-\n# separator` is used to allow tabulation in dicts, etc.: {1  : 1,\\n222: 2}.\n# `trailing-comma` allows a space between comma and closing bracket: (a, ).\n# `empty-line` allows space-only lines.\nno-space-check=trailing-comma,\n               dict-separator\n\n# Allow the body of a class to be on the same line as the declaration if body\n# contains single statement.\nsingle-line-class-stmt=no\n\n# Allow the body of an if to be on the same line as the test if there is no\n# else.\nsingle-line-if-stmt=no\n\n\n[LOGGING]\n\n# Format style used to check logging format string. `old` means using %\n# formatting, `new` is for `{}` formatting,and `fstr` is for f-strings.\nlogging-format-style=old\n\n# Logging modules to check that the string format arguments are in logging\n# function parameter format.\nlogging-modules=logging\n\n\n[SIMILARITIES]\n\n# Ignore comments when computing similarities.\nignore-comments=yes\n\n# Ignore docstrings when computing similarities.\nignore-docstrings=yes\n\n# Ignore imports when computing similarities.\nignore-imports=no\n\n# Minimum lines number of a similarity.\nmin-similarity-lines=8\n\n\n[TYPECHECK]\n\n# List of decorators that produce context managers, such as\n# contextlib.contextmanager. Add to this list to register other decorators that\n# produce valid context managers.\ncontextmanager-decorators=contextlib.contextmanager\n\n# List of members which are set dynamically and missed by pylint inference\n# system, and so shouldn't trigger E1101 when accessed. Python regular\n# expressions are accepted.\ngenerated-members=\n\n# Tells whether missing members accessed in mixin class should be ignored. A\n# mixin class is detected if its name ends with \"mixin\" (case insensitive).\nignore-mixin-members=yes\n\n# Tells whether to warn about missing members when the owner of the attribute\n# is inferred to be None.\nignore-none=yes\n\n# This flag controls whether pylint should warn about no-member and similar\n# checks whenever an opaque object is returned when inferring. The inference\n# can return multiple potential results while evaluating a Python object, but\n# some branches might not be evaluated, which results in partial inference. In\n# that case, it might be useful to still emit no-member and other checks for\n# the rest of the inferred objects.\nignore-on-opaque-inference=yes\n\n# List of class names for which member attributes should not be checked (useful\n# for classes with dynamically set attributes). This supports the use of\n# qualified names.\nignored-classes=optparse.Values,thread._local,_thread._local\n\n# List of module names for which member attributes should not be checked\n# (useful for modules/projects where namespaces are manipulated during runtime\n# and thus existing member attributes cannot be deduced by static analysis). It\n# supports qualified module names, as well as Unix pattern matching.\nignored-modules=\n\n# Show a hint with possible names when a member name was not found. The aspect\n# of finding the hint is based on edit distance.\nmissing-member-hint=yes\n\n# The minimum edit distance a name should have in order to be considered a\n# similar match for a missing member name.\nmissing-member-hint-distance=1\n\n# The total number of similar names that should be taken in consideration when\n# showing a hint for a missing member.\nmissing-member-max-choices=1\n\n# List of decorators that change the signature of a decorated function.\nsignature-mutators=\n\n\n[BASIC]\n\n# Naming style matching correct argument names.\nargument-naming-style=snake_case\n\n# Regular expression matching correct argument names. Overrides argument-\n# naming-style.\n#argument-rgx=\n\n# Naming style matching correct attribute names.\nattr-naming-style=snake_case\n\n# Regular expression matching correct attribute names. Overrides attr-naming-\n# style.\n#attr-rgx=\n\n# Bad variable names which should always be refused, separated by a comma.\nbad-names=foo,\n          bar,\n          baz,\n          toto,\n          tutu,\n          tata\n\n# Naming style matching correct class attribute names.\nclass-attribute-naming-style=any\n\n# Regular expression matching correct class attribute names. Overrides class-\n# attribute-naming-style.\n#class-attribute-rgx=\n\n# Naming style matching correct class names.\nclass-naming-style=PascalCase\n\n# Regular expression matching correct class names. Overrides class-naming-\n# style.\n#class-rgx=\n\n# Naming style matching correct constant names.\nconst-naming-style=UPPER_CASE\n\n# Regular expression matching correct constant names. Overrides const-naming-\n# style.\n#const-rgx=\n\n# Minimum line length for functions/classes that require docstrings, shorter\n# ones are exempt.\ndocstring-min-length=-1\n\n# Naming style matching correct function names.\nfunction-naming-style=snake_case\n\n# Regular expression matching correct function names. Overrides function-\n# naming-style.\n#function-rgx=\n\n# Good variable names which should always be accepted, separated by a comma.\ngood-names=i,\n           j,\n           k,\n           ex,\n           Run,\n           _,\n           # ------- imgaug-specific settings -------\n           n,    # number\n           h,    # height\n           w,    # width\n           c,    # channel index\n           x,    # x-coordinate\n           y,    # y-coordinate\n           z,    # z-coordinate\n           xy,   # xy-coordinate pair\n           xx,   # x-coordinates\n           yy,   # y-coordinates\n           zz,   # z-coordinates\n           dx,   # some difference/shift in x\n           dy,\n           dz,\n           x1,   # bounding box top left x-coordinate\n           x2,   # bounding box bottom right x-coordinate\n           x3,\n           x4,\n           y1,   # bounding box top left y-coordinate\n           y2,   # bounding box bottom right x-coordinate\n           y3,\n           y4,\n           p,    # probability\n           bb,   # bounding box\n           bbs,  # bounding boxes\n           kp,   # keypoint\n           kps,  # keypoints\n           ls,   # line string\n           lss,  # line strings\n           rs,   # random state\n           ax,   # matplotlib axis\n           f     # file handle for 'open(...) as f:'\n\n# Include a hint for the correct naming format with invalid-name.\ninclude-naming-hint=no\n\n# Naming style matching correct inline iteration names.\ninlinevar-naming-style=any\n\n# Regular expression matching correct inline iteration names. Overrides\n# inlinevar-naming-style.\n#inlinevar-rgx=\n\n# Naming style matching correct method names.\nmethod-naming-style=snake_case\n\n# Regular expression matching correct method names. Overrides method-naming-\n# style.\n#method-rgx=\n\n# Naming style matching correct module names.\nmodule-naming-style=snake_case\n\n# Regular expression matching correct module names. Overrides module-naming-\n# style.\n#module-rgx=\n\n# Colon-delimited sets of names that determine each other's naming style when\n# the name regexes allow several styles.\nname-group=\n\n# Regular expression which should only match function or class names that do\n# not require a docstring.\nno-docstring-rgx=^_\n\n# List of decorators that produce properties, such as abc.abstractproperty. Add\n# to this list to register other decorators that produce valid properties.\n# These decorators are taken in consideration only for invalid-name.\nproperty-classes=abc.abstractproperty\n\n# Naming style matching correct variable names.\nvariable-naming-style=snake_case\n\n# Regular expression matching correct variable names. Overrides variable-\n# naming-style.\n#variable-rgx=\n\n\n[VARIABLES]\n\n# List of additional names supposed to be defined in builtins. Remember that\n# you should avoid defining new builtins when possible.\nadditional-builtins=\n\n# Tells whether unused global variables should be treated as a violation.\nallow-global-unused-variables=yes\n\n# List of strings which can identify a callback function by name. A callback\n# name must start or end with one of those strings.\ncallbacks=cb_,\n          _cb\n\n# A regular expression matching the name of dummy variables (i.e. expected to\n# not be used).\ndummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_\n\n# Argument names that match this expression will be ignored. Default to name\n# with leading underscore.\nignored-argument-names=_.*|^ignored_|^unused_\n\n# Tells whether we should check for unused import in __init__ files.\ninit-import=no\n\n# List of qualified module names which can have objects that can redefine\n# builtins.\nredefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io\n\n\n[MISCELLANEOUS]\n\n# List of note tags to take in consideration, separated by a comma.\nnotes=FIXME,\n      XXX,\n      TODO\n\n\n[SPELLING]\n\n# Limits count of emitted suggestions for spelling mistakes.\nmax-spelling-suggestions=4\n\n# Spelling dictionary name. Available dictionaries: none. To make it work,\n# install the python-enchant package.\nspelling-dict=\n\n# List of comma separated words that should not be checked.\nspelling-ignore-words=\n\n# A path to a file that contains the private dictionary; one word per line.\nspelling-private-dict-file=\n\n# Tells whether to store unknown words to the private dictionary (see the\n# --spelling-private-dict-file option) instead of raising a message.\nspelling-store-unknown-words=no\n\n\n[STRING]\n\n# This flag controls whether the implicit-str-concat-in-sequence should\n# generate a warning on implicit string concatenation in sequences defined over\n# several lines.\ncheck-str-concat-over-line-jumps=no\n\n\n[DESIGN]\n\n# Maximum number of arguments for function / method.\nmax-args=5\n\n# Maximum number of attributes for a class (see R0902).\nmax-attributes=7\n\n# Maximum number of boolean expressions in an if statement (see R0916).\nmax-bool-expr=5\n\n# Maximum number of branch for function / method body.\nmax-branches=12\n\n# Maximum number of locals for function / method body.\nmax-locals=15\n\n# Maximum number of parents for a class (see R0901).\nmax-parents=7\n\n# Maximum number of public methods for a class (see R0904).\nmax-public-methods=20\n\n# Maximum number of return / yield for function / method body.\nmax-returns=6\n\n# Maximum number of statements in function / method body.\nmax-statements=50\n\n# Minimum number of public methods for a class (see R0903).\nmin-public-methods=2\n\n\n[IMPORTS]\n\n# List of modules that can be imported at any level, not just the top level\n# one.\nallow-any-import-level=\n\n# Allow wildcard imports from modules that define __all__.\nallow-wildcard-with-all=no\n\n# Analyse import fallback blocks. This can be used to support both Python 2 and\n# 3 compatible code, which means that the block might have code that exists\n# only in one or another interpreter, leading to false positives when analysed.\nanalyse-fallback-blocks=no\n\n# Deprecated modules which should not be used, separated by a comma.\ndeprecated-modules=optparse,tkinter.tix\n\n# Create a graph of external dependencies in the given file (report RP0402 must\n# not be disabled).\next-import-graph=\n\n# Create a graph of every (i.e. internal and external) dependencies in the\n# given file (report RP0402 must not be disabled).\nimport-graph=\n\n# Create a graph of internal dependencies in the given file (report RP0402 must\n# not be disabled).\nint-import-graph=\n\n# Force import order to recognize a module as part of the standard\n# compatibility libraries.\nknown-standard-library=\n\n# Force import order to recognize a module as part of a third party library.\nknown-third-party=enchant\n\n# Couples of modules and preferred modules, separated by a comma.\npreferred-modules=\n\n\n[CLASSES]\n\n# List of method names used to declare (i.e. assign) instance attributes.\ndefining-attr-methods=__init__,\n                      __new__,\n                      setUp,\n                      __post_init__\n\n# List of member names, which should be excluded from the protected access\n# warning.\nexclude-protected=_asdict,\n                  _fields,\n                  _replace,\n                  _source,\n                  _make\n\n# List of valid names for the first argument in a class method.\nvalid-classmethod-first-arg=cls\n\n# List of valid names for the first argument in a metaclass class method.\nvalid-metaclass-classmethod-first-arg=cls\n\n\n[EXCEPTIONS]\n\n# Exceptions that will emit a warning when being caught. Defaults to\n# \"BaseException, Exception\".\novergeneral-exceptions=BaseException,\n                       Exception\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: required\n\ndist: trusty\n\nlanguage:\n  - python\n  - cpp\n\nenv:\n  global:\n    - CODACY_PROJECT_TOKEN=1370ce38e99e40af842d47a8dd721444\n\ncache:\n  directories:\n    - $HOME/.cache/pip\n\npython:\n  - \"2.7\"\n  # - \"3.2\"  # downloads np 1.17 on travis (?!), which doesn't support 3.2\n  # - \"3.3\"  # downloads np 1.17 on travis (?!), which doesn't support 3.3\n  - \"3.4\"\n  - \"3.5\"\n  - \"3.6\"\n  # - \"3.7\"  # python version cannot be installed on travis\n\nbefore_install:\n  - sudo apt-get update -qq\n  - sudo apt-get install -qq -y python-virtualenv\n  # otherwise imagecodecs fails to build on py3.6,\n  # see https://github.com/scikit-image/scikit-image/issues/4673\n  - pip install --upgrade pip\n\ninstall:\n# TODO why was this deactivated?\n#  - virtualenv venv\n#  - . venv/bin/activate\n  - pip install -r requirements.txt\n  # Added --upgrade, because at least pytest already came from some other\n  # install command and so version was never checked\n  - pip install --upgrade -r test/requirements.txt\n  - pip install coverage codecov pytest-cov codacy-coverage\n  - pip install .\n\nbefore_script:\n  - pip install flake8\n  # Stop the build if there are Python syntax errors or undefined names.\n  #\n  # We exclude poly_point_isect.py because it is incompatible with python2\n  # and poly_point_isect_py2py3.py is actually used instead. The incompatible\n  # file exists in the repo only for comparison. There are some other patterns\n  # added to --exclude, which are the default values for flake8's exclude\n  # option.\n  - flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics --exclude=\".svn,CVS,.bzr,.hg,.git,__pycache__,poly_point_isect.py\"\n  # exit-zero treats all errors as warnings.  The GitHub editor is 127 chars wide\n  # currently deactivated as style guidelines are not yet kept in the project\n  # TODO change this\n  #- flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics\n\nscript:\n  - python -m pytest --verbose --xdoctest-modules --ignore=\"test/run_all.py\" -s --durations=50 -Walways\n  - coverage run --source imgaug -m pytest --verbose --xdoctest-modules --ignore=\"test/run_all.py\" -Walways\n\n# some steps are now done in github action\nafter_success:\n#  - codecov -t feeff9b2-3750-4246-befb-8cde60dc28aa\n  - coverage xml\n  - python-codacy-coverage -r coverage.xml\n#  - coverage report\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "This file is no longer used.\nSee `changelogs/` for all current and previous changelogs.\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 aleju\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include setup.py\ninclude setup.cfg\ninclude LICENSE\ninclude MANIFEST.in\ninclude README.md\ninclude requirements.txt\nrecursive-include imgaug *.py *.jpg *.ttf *.png *.json\nprune test"
  },
  {
    "path": "README.md",
    "content": "# imgaug\n\nThis python library helps you with augmenting images for your machine learning projects.\nIt converts a set of input images into a new, much larger set of slightly altered images.\n\n[![Build Status](https://travis-ci.org/aleju/imgaug.svg?branch=master)](https://travis-ci.org/aleju/imgaug)\n[![codecov](https://codecov.io/gh/aleju/imgaug/branch/master/graph/badge.svg)](https://codecov.io/gh/aleju/imgaug)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/1370ce38e99e40af842d47a8dd721444)](https://www.codacy.com/app/aleju/imgaug?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=aleju/imgaug&amp;utm_campaign=Badge_Grade)\n\n<table>\n\n<tr>\n<th>&nbsp;</th>\n<th>Image</th>\n<th>Heatmaps</th>\n<th>Seg. Maps</th>\n<th>Keypoints</th>\n<th>Bounding Boxes,<br>Polygons</th>\n</tr>\n\n<!-- Line 1: Original Input -->\n<tr>\n<td><em>Original Input</em></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/noop_image.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"input images\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/noop_heatmap.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"input heatmaps\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/noop_segmap.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"input segmentation maps\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/noop_kps.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"input keypoints\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/noop_bbs.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"input bounding boxes\"></td>\n</tr>\n\n<!-- Line 2: Gauss. Noise + Contrast + Sharpen -->\n<tr>\n<td>Gauss. Noise<br>+&nbsp;Contrast<br>+&nbsp;Sharpen</td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/non_geometric_image.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"non geometric augmentations, applied to images\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/non_geometric_heatmap.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"non geometric augmentations, applied to heatmaps\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/non_geometric_segmap.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"non geometric augmentations, applied to segmentation maps\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/non_geometric_kps.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"non geometric augmentations, applied to keypoints\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/non_geometric_bbs.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"non geometric augmentations, applied to bounding boxes\"></td>\n</tr>\n\n<!-- Line 3: Affine -->\n<tr>\n<td>Affine</td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/affine_image.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"affine augmentations, applied to images\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/affine_heatmap.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"affine augmentations, applied to heatmaps\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/affine_segmap.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"affine augmentations, applied to segmentation maps\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/affine_kps.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"affine augmentations, applied to keypoints\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/affine_bbs.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"affine augmentations, applied to bounding boxes\"></td>\n</tr>\n\n<!-- Line 4: Crop + Pad -->\n<tr>\n<td>Crop<br>+&nbsp;Pad</td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/cropandpad_image.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"crop and pad augmentations, applied to images\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/cropandpad_heatmap.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"crop and pad augmentations, applied to heatmaps\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/cropandpad_segmap.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"crop and pad augmentations, applied to segmentation maps\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/cropandpad_kps.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"crop and pad augmentations, applied to keypoints\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/cropandpad_bbs.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"crop and pad augmentations, applied to bounding boxes\"></td>\n</tr>\n\n<!-- Line 5: Fliplr + Perspective -->\n<tr>\n<td>Fliplr<br>+&nbsp;Perspective</td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/fliplr_perspective_image.jpg\" height=\"83\" width=\"124\" alt=\"Horizontal flip and perspective transform augmentations, applied to images\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/fliplr_perspective_heatmap.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"Horizontal flip and perspective transform augmentations, applied to heatmaps\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/fliplr_perspective_segmap.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"Horizontal flip and perspective transform augmentations, applied to segmentation maps\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/fliplr_perspective_kps.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"Horizontal flip and perspective transform augmentations, applied to keypoints\"></td>\n<td><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/small_overview/fliplr_perspective_bbs.jpg?raw=true\" height=\"83\" width=\"124\" alt=\"Horizontal flip and perspective transform augmentations, applied to bounding boxes\"></td>\n</tr>\n\n</table>\n\n\n**More (strong) example augmentations of one input image:**\n\n![64 quokkas](https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/examples_grid.jpg?raw=true \"64 quokkas\")\n\n\n## Table of Contents\n\n1. [Features](#features)\n2. [Installation](#installation)\n3. [Documentation](#documentation)\n4. [Recent Changes](#recent_changes)\n5. [Example Images](#example_images)\n6. [Code Examples](#code_examples)\n7. [Citation](#citation)\n\n\n<a name=\"features\"/>\n\n## Features\n\n* Many augmentation techniques\n  * E.g. affine transformations, perspective transformations, contrast changes, gaussian noise, dropout of regions, hue/saturation changes, cropping/padding, blurring, ...\n  * Optimized for high performance\n  * Easy to apply augmentations only to some images\n  * Easy to apply augmentations in random order\n* Support for\n  * Images (full support for uint8, for other dtypes see [documentation](https://imgaug.readthedocs.io/en/latest/source/dtype_support.html))\n  * Heatmaps (float32), Segmentation Maps (int), Masks (bool)\n    * May be smaller/larger than their corresponding images. *No* extra lines of code needed for e.g. crop.\n  * Keypoints/Landmarks (int/float coordinates)\n  * Bounding Boxes (int/float coordinates)\n  * Polygons (int/float coordinates)\n  * Line Strings (int/float coordinates)\n* Automatic alignment of sampled random values\n  * Example: Rotate image and segmentation map on it by the same value sampled from `uniform(-10°, 45°)`. (0 extra lines of code.)\n* Probability distributions as parameters\n  * Example: Rotate images by values sampled from `uniform(-10°, 45°)`.\n  * Example: Rotate images by values sampled from `ABS(N(0, 20.0))*(1+B(1.0, 1.0))`\", where `ABS(.)` is the absolute function, `N(.)` the gaussian distribution and `B(.)` the beta distribution.\n* Many helper functions\n  * Example: Draw heatmaps, segmentation maps, keypoints, bounding boxes, ...\n  * Example: Scale segmentation maps, average/max pool of images/maps, pad images to aspect\n    ratios (e.g. to square them)\n  * Example: Convert keypoints to distance maps, extract pixels within bounding boxes from images, clip polygon to the image plane, ...\n* Support for augmentation on multiple CPU cores\n\n\n<a name=\"installation\"/>\n\n## Installation\n\nThe library supports python 2.7 and 3.4+.\n\n### Installation: Anaconda\n\nTo install the library in anaconda, perform the following commands:\n```bash\nconda config --add channels conda-forge\nconda install imgaug\n```\n\nYou can deinstall the library again via `conda remove imgaug`.\n\n### Installation: pip\n\nThen install imgaug either via pypi (can lag behind the github version):\n```bash\npip install imgaug\n```\n\nor install the latest version directly from github:\n```bash\npip install git+https://github.com/aleju/imgaug.git\n```\n\nFor more details, see the [install guide](https://imgaug.readthedocs.io/en/latest/source/installation.html)\n\nTo deinstall the library, just execute `pip uninstall imgaug`.\n\n\n<a name=\"documentation\"/>\n\n## Documentation\n\nExample jupyter notebooks:\n  * [Load and Augment an Image](https://nbviewer.jupyter.org/github/aleju/imgaug-doc/blob/master/notebooks/A01%20-%20Load%20and%20Augment%20an%20Image.ipynb)\n  * [Multicore Augmentation](https://nbviewer.jupyter.org/github/aleju/imgaug-doc/blob/master/notebooks/A03%20-%20Multicore%20Augmentation.ipynb)\n  * Augment and work with: [Keypoints/Landmarks](https://nbviewer.jupyter.org/github/aleju/imgaug-doc/blob/master/notebooks/B01%20-%20Augment%20Keypoints.ipynb),\n    [Bounding Boxes](https://nbviewer.jupyter.org/github/aleju/imgaug-doc/blob/master/notebooks/B02%20-%20Augment%20Bounding%20Boxes.ipynb),\n    [Polygons](https://nbviewer.jupyter.org/github/aleju/imgaug-doc/blob/master/notebooks/B03%20-%20Augment%20Polygons.ipynb),\n    [Line Strings](https://nbviewer.jupyter.org/github/aleju/imgaug-doc/blob/master/notebooks/B06%20-%20Augment%20Line%20Strings.ipynb),\n    [Heatmaps](https://nbviewer.jupyter.org/github/aleju/imgaug-doc/blob/master/notebooks/B04%20-%20Augment%20Heatmaps.ipynb),\n    [Segmentation Maps](https://nbviewer.jupyter.org/github/aleju/imgaug-doc/blob/master/notebooks/B05%20-%20Augment%20Segmentation%20Maps.ipynb) \n\nMore notebooks: [imgaug-doc/notebooks](https://github.com/aleju/imgaug-doc/tree/master/notebooks).\n\nExample ReadTheDocs pages:\n* [Quick example code on how to use the library](http://imgaug.readthedocs.io/en/latest/source/examples_basics.html)\n* [Overview of all Augmenters](https://imgaug.readthedocs.io/en/latest/source/overview_of_augmenters.html)\n* [API](http://imgaug.readthedocs.io/en/latest/source/api.html)\n\nMore RTD documentation: [imgaug.readthedocs.io](http://imgaug.readthedocs.io/en/latest/source/examples_basics.html).\n\nAll documentation related files of this project are hosted in the\nrepository [imgaug-doc](https://github.com/aleju/imgaug-doc).\n\n\n<a name=\"recent_changes\"/>\n\n## Recent Changes\n\n* **0.4.0**: Added new augmenters, changed backend to batchwise augmentation,\n  support for numpy 1.18 and python 3.8.\n* **0.3.0**: Reworked segmentation map augmentation, adapted to numpy 1.17+\n  random number sampling API, several new augmenters.\n* **0.2.9**: Added polygon augmentation, added line string augmentation,\n  simplified augmentation interface.\n* **0.2.8**: Improved performance, dtype support and multicore augmentation.\n\nSee [changelogs/](changelogs/) for more details.\n\n\n<a name=\"example_images\"/>\n\n## Example Images\n\nThe images below show examples for most augmentation techniques.\n\nValues written in the form `(a, b)` denote a uniform distribution,\ni.e. the value is randomly picked from the interval `[a, b]`.\nLine strings are supported by (almost) all augmenters, but are not explicitly\nvisualized here.\n\n<table>\n\n<tr><td colspan=\"5\"><strong>meta</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#identity\">Identity</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#channelshuffle\">ChannelShuffle</a></sub></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/meta/identity.gif\" height=\"148\" width=\"100\" alt=\"Identity\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/meta/channelshuffle.gif\" height=\"148\" width=\"100\" alt=\"ChannelShuffle\"></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#sequential\">Sequential</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#someof\">SomeOf</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#oneof\">OneOf</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#sometimes\">Sometimes</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#withchannels\">WithChannels</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#lambda\">Lambda</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#assertlambda\">AssertLambda</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#assertshape\">AssertShape</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#removecbasbyoutofimagefraction\">RemoveCBAsByOutOfImageFraction</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/meta.html#clipcbastoimageplanes\">ClipCBAsToImagePlanes</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>arithmetic</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#add\">Add</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#add\">Add</a><br/>(per_channel=True)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#additivegaussiannoise\">AdditiveGaussianNoise</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#additivegaussiannoise\">AdditiveGaussianNoise</a><br/>(per_channel=True)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#multiply\">Multiply</a></sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/add.gif\" height=\"148\" width=\"100\" alt=\"Add\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/add_per_channel_true.gif\" height=\"148\" width=\"100\" alt=\"Add per_channel=True\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/additivegaussiannoise.gif\" height=\"148\" width=\"100\" alt=\"AdditiveGaussianNoise\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/additivegaussiannoise_per_channel_true.gif\" height=\"148\" width=\"100\" alt=\"AdditiveGaussianNoise per_channel=True\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/multiply.gif\" height=\"148\" width=\"100\" alt=\"Multiply\"></td>\n</tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#cutout\">Cutout</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#dropout\">Dropout</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#coarsedropout\">CoarseDropout</a><br/>(p=0.2)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#coarsedropout\">CoarseDropout</a><br/>(p=0.2, per_channel=True)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#dropout2d\">Dropout2d</a></sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/cutout.gif\" height=\"148\" width=\"100\" alt=\"Cutout\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/dropout.gif\" height=\"148\" width=\"100\" alt=\"Dropout\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/coarsedropout_p_0_2.gif\" height=\"148\" width=\"100\" alt=\"CoarseDropout p=0.2\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/coarsedropout_p_0_2_per_channel_true.gif\" height=\"148\" width=\"100\" alt=\"CoarseDropout p=0.2, per_channel=True\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/dropout2d.gif\" height=\"148\" width=\"100\" alt=\"Dropout2d\"></td>\n</tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#saltandpepper\">SaltAndPepper</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#coarsesaltandpepper\">CoarseSaltAndPepper</a><br/>(p=0.2)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#invert\">Invert</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#solarize\">Solarize</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#jpegcompression\">JpegCompression</a></sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/saltandpepper.gif\" height=\"148\" width=\"100\" alt=\"SaltAndPepper\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/coarsesaltandpepper_p_0_2.gif\" height=\"148\" width=\"100\" alt=\"CoarseSaltAndPepper p=0.2\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/invert.gif\" height=\"148\" width=\"100\" alt=\"Invert\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/solarize.gif\" height=\"148\" width=\"100\" alt=\"Solarize\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/arithmetic/jpegcompression.gif\" height=\"148\" width=\"100\" alt=\"JpegCompression\"></td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#addelementwise\">AddElementwise</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#additivelaplacenoise\">AdditiveLaplaceNoise</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#additivepoissonnoise\">AdditivePoissonNoise</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#multiplyelementwise\">MultiplyElementwise</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#totaldropout\">TotalDropout</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#replaceelementwise\">ReplaceElementwise</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#impulsenoise\">ImpulseNoise</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#salt\">Salt</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#pepper\">Pepper</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#coarsesalt\">CoarseSalt</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#coarsepepper\">CoarsePepper</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/arithmetic.html#solarize\">Solarize</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>artistic</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/artistic.html#cartoon\">Cartoon</a></sub></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/artistic/cartoon.gif\" height=\"144\" width=\"128\" alt=\"Cartoon\"></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr><td colspan=\"5\"><strong>blend</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalpha\">BlendAlpha</a><br/>with EdgeDetect(1.0)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalphasimplexnoise\">BlendAlphaSimplexNoise</a><br/>with EdgeDetect(1.0)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalphafrequencynoise\">BlendAlphaFrequencyNoise</a><br/>with EdgeDetect(1.0)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalphasomecolors\">BlendAlphaSomeColors</a><br/>with RemoveSaturation(1.0)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalpharegulargrid\">BlendAlphaRegularGrid</a><br/>with Multiply((0.0, 0.5))</sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blend/blendalpha_with_edgedetect_1_0.gif\" height=\"148\" width=\"100\" alt=\"BlendAlpha with EdgeDetect1.0\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blend/blendalphasimplexnoise_with_edgedetect_1_0.gif\" height=\"148\" width=\"100\" alt=\"BlendAlphaSimplexNoise with EdgeDetect1.0\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blend/blendalphafrequencynoise_with_edgedetect_1_0.gif\" height=\"148\" width=\"100\" alt=\"BlendAlphaFrequencyNoise with EdgeDetect1.0\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blend/blendalphasomecolors_with_removesaturation_1_0.gif\" height=\"144\" width=\"128\" alt=\"BlendAlphaSomeColors with RemoveSaturation1.0\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blend/blendalpharegulargrid_with_multiply_0_0_0_5.gif\" height=\"148\" width=\"100\" alt=\"BlendAlphaRegularGrid with Multiply0.0, 0.5\"></td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalphamask\">BlendAlphaMask</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalphaelementwise\">BlendAlphaElementwise</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalphaverticallineargradient\">BlendAlphaVerticalLinearGradient</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalphahorizontallineargradient\">BlendAlphaHorizontalLinearGradient</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalphasegmapclassids\">BlendAlphaSegMapClassIds</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalphaboundingboxes\">BlendAlphaBoundingBoxes</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blend.html#blendalphacheckerboard\">BlendAlphaCheckerboard</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_blend.html#imgaug.augmenters.blend.SomeColorsMaskGen\">SomeColorsMaskGen</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_blend.html#imgaug.augmenters.blend.HorizontalLinearGradientMaskGen\">HorizontalLinearGradientMaskGen</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_blend.html#imgaug.augmenters.blend.VerticalLinearGradientMaskGen\">VerticalLinearGradientMaskGen</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_blend.html#imgaug.augmenters.blend.RegularGridMaskGen\">RegularGridMaskGen</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_blend.html#imgaug.augmenters.blend.CheckerboardMaskGen\">CheckerboardMaskGen</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_blend.html#imgaug.augmenters.blend.SegMapClassIdsMaskGen\">SegMapClassIdsMaskGen</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_blend.html#imgaug.augmenters.blend.BoundingBoxesMaskGen\">BoundingBoxesMaskGen</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_blend.html#imgaug.augmenters.blend.InvertMaskGen\">InvertMaskGen</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>blur</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blur.html#gaussianblur\">GaussianBlur</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blur.html#averageblur\">AverageBlur</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blur.html#medianblur\">MedianBlur</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blur.html#bilateralblur\">BilateralBlur</a><br/>(sigma_color=250,<br/>sigma_space=250)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blur.html#motionblur\">MotionBlur</a><br/>(angle=0)</sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blur/gaussianblur.gif\" height=\"148\" width=\"100\" alt=\"GaussianBlur\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blur/averageblur.gif\" height=\"148\" width=\"100\" alt=\"AverageBlur\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blur/medianblur.gif\" height=\"148\" width=\"100\" alt=\"MedianBlur\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blur/bilateralblur_sigma_color_250_sigma_space_250.gif\" height=\"148\" width=\"100\" alt=\"BilateralBlur sigma_color=250, sigma_space=250\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blur/motionblur_angle_0.gif\" height=\"148\" width=\"100\" alt=\"MotionBlur angle=0\"></td>\n</tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blur.html#motionblur\">MotionBlur</a><br/>(k=5)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/blur.html#meanshiftblur\">MeanShiftBlur</a></sub></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blur/motionblur_k_5.gif\" height=\"148\" width=\"100\" alt=\"MotionBlur k=5\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/blur/meanshiftblur.gif\" height=\"148\" width=\"100\" alt=\"MeanShiftBlur\"></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr><td colspan=\"5\"><strong>collections</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/collections.html#randaugment\">RandAugment</a></sub></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/collections/randaugment.gif\" height=\"148\" width=\"100\" alt=\"RandAugment\"></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr><td colspan=\"5\"><strong>color</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#multiplyandaddtobrightness\">MultiplyAndAddToBrightness</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#multiplyhueandsaturation\">MultiplyHueAndSaturation</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#multiplyhue\">MultiplyHue</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#multiplysaturation\">MultiplySaturation</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#addtohueandsaturation\">AddToHueAndSaturation</a></sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/color/multiplyandaddtobrightness.gif\" height=\"148\" width=\"100\" alt=\"MultiplyAndAddToBrightness\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/color/multiplyhueandsaturation.gif\" height=\"148\" width=\"100\" alt=\"MultiplyHueAndSaturation\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/color/multiplyhue.gif\" height=\"148\" width=\"100\" alt=\"MultiplyHue\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/color/multiplysaturation.gif\" height=\"148\" width=\"100\" alt=\"MultiplySaturation\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/color/addtohueandsaturation.gif\" height=\"148\" width=\"100\" alt=\"AddToHueAndSaturation\"></td>\n</tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#grayscale\">Grayscale</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#removesaturation\">RemoveSaturation</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#changecolortemperature\">ChangeColorTemperature</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#kmeanscolorquantization\">KMeansColorQuantization</a><br/>(to_colorspace=RGB)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#uniformcolorquantization\">UniformColorQuantization</a><br/>(to_colorspace=RGB)</sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/color/grayscale.gif\" height=\"148\" width=\"100\" alt=\"Grayscale\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/color/removesaturation.gif\" height=\"148\" width=\"100\" alt=\"RemoveSaturation\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/color/changecolortemperature.gif\" height=\"148\" width=\"100\" alt=\"ChangeColorTemperature\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/color/kmeanscolorquantization_to_colorspace_rgb.gif\" height=\"148\" width=\"100\" alt=\"KMeansColorQuantization to_colorspace=RGB\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/color/uniformcolorquantization_to_colorspace_rgb.gif\" height=\"148\" width=\"100\" alt=\"UniformColorQuantization to_colorspace=RGB\"></td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#withcolorspace\">WithColorspace</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#withbrightnesschannels\">WithBrightnessChannels</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#multiplybrightness\">MultiplyBrightness</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#addtobrightness\">AddToBrightness</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#withhueandsaturation\">WithHueAndSaturation</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#addtohue\">AddToHue</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#addtosaturation\">AddToSaturation</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#changecolorspace\">ChangeColorspace</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#posterize\">Posterize</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>contrast</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/contrast.html#gammacontrast\">GammaContrast</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/contrast.html#gammacontrast\">GammaContrast</a><br/>(per_channel=True)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/contrast.html#sigmoidcontrast\">SigmoidContrast</a><br/>(cutoff=0.5)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/contrast.html#sigmoidcontrast\">SigmoidContrast</a><br/>(gain=10)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/contrast.html#logcontrast\">LogContrast</a></sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/contrast/gammacontrast.gif\" height=\"148\" width=\"100\" alt=\"GammaContrast\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/contrast/gammacontrast_per_channel_true.gif\" height=\"148\" width=\"100\" alt=\"GammaContrast per_channel=True\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/contrast/sigmoidcontrast_cutoff_0_5.gif\" height=\"148\" width=\"100\" alt=\"SigmoidContrast cutoff=0.5\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/contrast/sigmoidcontrast_gain_10.gif\" height=\"148\" width=\"100\" alt=\"SigmoidContrast gain=10\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/contrast/logcontrast.gif\" height=\"148\" width=\"100\" alt=\"LogContrast\"></td>\n</tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/contrast.html#linearcontrast\">LinearContrast</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/contrast.html#allchannelshistogramequalization\">AllChannels-</a><br/>HistogramEqualization</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/contrast.html#histogramequalization\">HistogramEqualization</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/contrast.html#allchannelsclahe\">AllChannelsCLAHE</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/contrast.html#clahe\">CLAHE</a></sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/contrast/linearcontrast.gif\" height=\"148\" width=\"100\" alt=\"LinearContrast\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/contrast/allchannels_histogramequalization.gif\" height=\"148\" width=\"100\" alt=\"AllChannels- HistogramEqualization\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/contrast/histogramequalization.gif\" height=\"148\" width=\"100\" alt=\"HistogramEqualization\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/contrast/allchannelsclahe.gif\" height=\"148\" width=\"100\" alt=\"AllChannelsCLAHE\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/contrast/clahe.gif\" height=\"148\" width=\"100\" alt=\"CLAHE\"></td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/contrast.html#equalize\">Equalize</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>convolutional</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/convolutional.html#sharpen\">Sharpen</a><br/>(alpha=1)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/convolutional.html#emboss\">Emboss</a><br/>(alpha=1)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/convolutional.html#edgedetect\">EdgeDetect</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/convolutional.html#directededgedetect\">DirectedEdgeDetect</a><br/>(alpha=1)</sub></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/convolutional/sharpen_alpha_1.gif\" height=\"148\" width=\"100\" alt=\"Sharpen alpha=1\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/convolutional/emboss_alpha_1.gif\" height=\"148\" width=\"100\" alt=\"Emboss alpha=1\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/convolutional/edgedetect.gif\" height=\"148\" width=\"100\" alt=\"EdgeDetect\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/convolutional/directededgedetect_alpha_1.gif\" height=\"148\" width=\"100\" alt=\"DirectedEdgeDetect alpha=1\"></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/convolutional.html#convolve\">Convolve</a></td>\n</tr>\n<tr>\n<td colspan=\"5\"><strong>debug</strong></td></tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/debug.html#savedebugimageeverynbatches\">SaveDebugImageEveryNBatches</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>edges</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/edges.html#canny\">Canny</a></sub></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/edges/canny.gif\" height=\"148\" width=\"100\" alt=\"Canny\"></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr><td colspan=\"5\"><strong>flip</strong></td></tr>\n<tr>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/flip.html#fliplr\">Fliplr</a></sub></td>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/flip.html#flipud\">Flipud</a></sub></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/flip/fliplr.gif\" height=\"148\" width=\"300\" alt=\"Fliplr\"></td>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/flip/flipud.gif\" height=\"148\" width=\"300\" alt=\"Flipud\"></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#horizontalflip\">HorizontalFlip</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/color.html#verticalflip\">VerticalFlip</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>geometric</strong></td></tr>\n<tr>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#affine\">Affine</a></sub></td>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#affine\">Affine: Modes</a></sub></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/geometric/affine.gif\" height=\"148\" width=\"300\" alt=\"Affine\"></td>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/geometric/affine_modes.gif\" height=\"148\" width=\"300\" alt=\"Affine: Modes\"></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#affine\">Affine: cval</a></sub></td>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#piecewiseaffine\">PiecewiseAffine</a></sub></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/geometric/affine_cval.gif\" height=\"148\" width=\"300\" alt=\"Affine: cval\"></td>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/geometric/piecewiseaffine.gif\" height=\"148\" width=\"300\" alt=\"PiecewiseAffine\"></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#perspectivetransform\">PerspectiveTransform</a></sub></td>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#elastictransformation\">ElasticTransformation</a><br/>(sigma=1.0)</sub></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/geometric/perspectivetransform.gif\" height=\"148\" width=\"300\" alt=\"PerspectiveTransform\"></td>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/geometric/elastictransformation_sigma_1_0.gif\" height=\"148\" width=\"300\" alt=\"ElasticTransformation sigma=1.0\"></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#elastictransformation\">ElasticTransformation</a><br/>(sigma=4.0)</sub></td>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#rot90\">Rot90</a></sub></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/geometric/elastictransformation_sigma_4_0.gif\" height=\"148\" width=\"300\" alt=\"ElasticTransformation sigma=4.0\"></td>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/geometric/rot90.gif\" height=\"148\" width=\"300\" alt=\"Rot90\"></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#withpolarwarping\">WithPolarWarping</a><br/>+Affine</sub></td>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#jigsaw\">Jigsaw</a><br/>(5x5 grid)</sub></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/geometric/withpolarwarping_affine.gif\" height=\"148\" width=\"300\" alt=\"WithPolarWarping +Affine\"></td>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/geometric/jigsaw_5x5_grid.gif\" height=\"148\" width=\"300\" alt=\"Jigsaw 5x5 grid\"></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#scalex\">ScaleX</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#scaley\">ScaleY</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#translatex\">TranslateX</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#translatey\">TranslateY</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/geometric.html#rotate\">Rotate</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>imgcorruptlike</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#glassblur\">GlassBlur</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#defocusblur\">DefocusBlur</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#zoomblur\">ZoomBlur</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#snow\">Snow</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#spatter\">Spatter</a></sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/imgcorruptlike/glassblur.gif\" height=\"148\" width=\"100\" alt=\"GlassBlur\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/imgcorruptlike/defocusblur.gif\" height=\"148\" width=\"100\" alt=\"DefocusBlur\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/imgcorruptlike/zoomblur.gif\" height=\"148\" width=\"100\" alt=\"ZoomBlur\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/imgcorruptlike/snow.gif\" height=\"148\" width=\"100\" alt=\"Snow\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/imgcorruptlike/spatter.gif\" height=\"148\" width=\"100\" alt=\"Spatter\"></td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#gaussiannoise\">GaussianNoise</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#shotnoise\">ShotNoise</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#impulsenoise\">ImpulseNoise</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#specklenoise\">SpeckleNoise</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#gaussianblur\">GaussianBlur</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#motionblur\">MotionBlur</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#fog\">Fog</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#frost\">Frost</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#contrast\">Contrast</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#brightness\">Brightness</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#saturate\">Saturate</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#jpegcompression\">JpegCompression</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#pixelate\">Pixelate</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/imgcorruptlike.html#elastictransform\">ElasticTransform</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>pillike</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#autocontrast\">Autocontrast</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#enhancecolor\">EnhanceColor</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#enhancesharpness\">EnhanceSharpness</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#filteredgeenhancemore\">FilterEdgeEnhanceMore</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#filtercontour\">FilterContour</a></sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/pillike/autocontrast.gif\" height=\"148\" width=\"100\" alt=\"Autocontrast\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/pillike/enhancecolor.gif\" height=\"148\" width=\"100\" alt=\"EnhanceColor\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/pillike/enhancesharpness.gif\" height=\"148\" width=\"100\" alt=\"EnhanceSharpness\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/pillike/filteredgeenhancemore.gif\" height=\"148\" width=\"100\" alt=\"FilterEdgeEnhanceMore\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/pillike/filtercontour.gif\" height=\"148\" width=\"100\" alt=\"FilterContour\"></td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#solarize\">Solarize</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#posterize\">Posterize</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#equalize\">Equalize</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#enhancecontrast\">EnhanceContrast</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#enhancebrightness\">EnhanceBrightness</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#filterblur\">FilterBlur</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#filtersmooth\">FilterSmooth</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#filtersmoothmore\">FilterSmoothMore</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#filteredgeenhance\">FilterEdgeEnhance</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#filterfindedges\">FilterFindEdges</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#filteremboss\">FilterEmboss</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#filtersharpen\">FilterSharpen</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#filterdetail\">FilterDetail</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pillike.html#affine\">Affine</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>pooling</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pooling.html#averagepooling\">AveragePooling</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pooling.html#maxpooling\">MaxPooling</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pooling.html#minpooling\">MinPooling</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/pooling.html#medianpooling\">MedianPooling</a></sub></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/pooling/averagepooling.gif\" height=\"148\" width=\"100\" alt=\"AveragePooling\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/pooling/maxpooling.gif\" height=\"148\" width=\"100\" alt=\"MaxPooling\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/pooling/minpooling.gif\" height=\"148\" width=\"100\" alt=\"MinPooling\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/pooling/medianpooling.gif\" height=\"148\" width=\"100\" alt=\"MedianPooling\"></td>\n<td>&nbsp;</td>\n</tr>\n<tr><td colspan=\"5\"><strong>segmentation</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/segmentation.html#superpixels\">Superpixels</a><br/>(p_replace=1)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/segmentation.html#superpixels\">Superpixels</a><br/>(n_segments=100)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/segmentation.html#uniformvoronoi\">UniformVoronoi</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/segmentation.html#regulargridvoronoi\">RegularGridVoronoi: rows/cols</a><br/>(p_drop_points=0)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/segmentation.html#regulargridvoronoi\">RegularGridVoronoi: p_drop_points</a><br/>(n_rows=n_cols=30)</sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/segmentation/superpixels_p_replace_1.gif\" height=\"148\" width=\"100\" alt=\"Superpixels p_replace=1\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/segmentation/superpixels_n_segments_100.gif\" height=\"148\" width=\"100\" alt=\"Superpixels n_segments=100\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/segmentation/uniformvoronoi.gif\" height=\"148\" width=\"100\" alt=\"UniformVoronoi\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/segmentation/regulargridvoronoi_rows_cols_p_drop_points_0.gif\" height=\"148\" width=\"100\" alt=\"RegularGridVoronoi: rows/cols p_drop_points=0\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/segmentation/regulargridvoronoi_p_drop_points_n_rows_n_cols_30.gif\" height=\"148\" width=\"100\" alt=\"RegularGridVoronoi: p_drop_points n_rows=n_cols=30\"></td>\n</tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/segmentation.html#regulargridvoronoi\">RegularGridVoronoi: p_replace</a><br/>(n_rows=n_cols=16)</sub></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/segmentation/regulargridvoronoi_p_replace_n_rows_n_cols_16.gif\" height=\"148\" width=\"100\" alt=\"RegularGridVoronoi: p_replace n_rows=n_cols=16\"></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/segmentation.html#voronoi\">Voronoi</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/segmentation.html#relativeregulargridvoronoi\">RelativeRegularGridVoronoi</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_segmentation.html#imgaug.augmenters.segmentation.RegularGridPointsSampler\">RegularGridPointsSampler</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_segmentation.html#imgaug.augmenters.segmentation.RelativeRegularGridPointsSampler\">RelativeRegularGridPointsSampler</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_segmentation.html#imgaug.augmenters.segmentation.DropoutPointsSampler\">DropoutPointsSampler</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_segmentation.html#imgaug.augmenters.segmentation.UniformPointsSampler\">UniformPointsSampler</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/api_augmenters_segmentation.html#imgaug.augmenters.segmentation.SubsamplingPointsSampler\">SubsamplingPointsSampler</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>size</strong></td></tr>\n<tr>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#cropandpad\">CropAndPad</a></sub></td>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#crop\">Crop</a></sub></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/size/cropandpad.gif\" height=\"148\" width=\"300\" alt=\"CropAndPad\"></td>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/size/crop.gif\" height=\"148\" width=\"300\" alt=\"Crop\"></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#pad\">Pad</a></sub></td>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#padtofixedsize\">PadToFixedSize</a><br/>(height'=height+32,<br/>width'=width+32)</sub></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/size/pad.gif\" height=\"148\" width=\"300\" alt=\"Pad\"></td>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/size/padtofixedsize_height_height_32_width_width_32.gif\" height=\"148\" width=\"300\" alt=\"PadToFixedSize height'=height+32, width'=width+32\"></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#croptofixedsize\">CropToFixedSize</a><br/>(height'=height-32,<br/>width'=width-32)</sub></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td colspan=\"2\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/size/croptofixedsize_height_height_32_width_width_32.gif\" height=\"148\" width=\"300\" alt=\"CropToFixedSize height'=height-32, width'=width-32\"></td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#resize\">Resize</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#croptomultiplesof\">CropToMultiplesOf</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#padtomultiplesof\">PadToMultiplesOf</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#croptopowersof\">CropToPowersOf</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#padtopowersof\">PadToPowersOf</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#croptoaspectratio\">CropToAspectRatio</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#padtoaspectratio\">PadToAspectRatio</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#croptosquare\">CropToSquare</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#padtosquare\">PadToSquare</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#centercroptofixedsize\">CenterCropToFixedSize</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#centerpadtofixedsize\">CenterPadToFixedSize</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#centercroptomultiplesof\">CenterCropToMultiplesOf</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#centerpadtomultiplesof\">CenterPadToMultiplesOf</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#centercroptopowersof\">CenterCropToPowersOf</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#centerpadtopowersof\">CenterPadToPowersOf</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#centercroptoaspectratio\">CenterCropToAspectRatio</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#centerpadtoaspectratio\">CenterPadToAspectRatio</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#centercroptosquare\">CenterCropToSquare</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#centerpadtosquare\">CenterPadToSquare</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/size.html#keepsizebyresize\">KeepSizeByResize</a></td>\n</tr>\n<tr><td colspan=\"5\"><strong>weather</strong></td></tr>\n<tr>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/weather.html#fastsnowylandscape\">FastSnowyLandscape</a><br/>(lightness_multiplier=2.0)</sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/weather.html#clouds\">Clouds</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/weather.html#fog\">Fog</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/weather.html#snowflakes\">Snowflakes</a></sub></td>\n<td colspan=\"1\"><sub><a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/weather.html#rain\">Rain</a></sub></td>\n</tr>\n<tr>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/weather/fastsnowylandscape_lightness_multiplier_2_0.gif\" height=\"144\" width=\"128\" alt=\"FastSnowyLandscape lightness_multiplier=2.0\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/weather/clouds.gif\" height=\"144\" width=\"128\" alt=\"Clouds\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/weather/fog.gif\" height=\"144\" width=\"128\" alt=\"Fog\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/weather/snowflakes.gif\" height=\"144\" width=\"128\" alt=\"Snowflakes\"></td>\n<td colspan=\"1\"><img src=\"https://raw.githubusercontent.com/aleju/imgaug-doc/master/readme_images/augmenter_videos/weather/rain.gif\" height=\"144\" width=\"128\" alt=\"Rain\"></td>\n</tr>\n<tr>\n\n</tr>\n<tr>\n<td colspan=\"5\">See also: <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/weather.html#cloudlayer\">CloudLayer</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/weather.html#snowflakeslayer\">SnowflakesLayer</a>, <a href=\"https://imgaug.readthedocs.io/en/latest/source/overview/weather.html#rainlayer\">RainLayer</a></td>\n</tr>\n\n</table>\n\n\n\n<a name=\"code_examples\"/>\n\n\n## Code Examples\n\n### Example: Simple Training Setting\n\nA standard machine learning situation.\nTrain on batches of images and augment each batch via crop, horizontal\nflip (\"Fliplr\") and gaussian blur:\n```python\nimport numpy as np\nimport imgaug.augmenters as iaa\n\ndef load_batch(batch_idx):\n    # dummy function, implement this\n    # Return a numpy array of shape (N, height, width, #channels)\n    # or a list of (height, width, #channels) arrays (may have different image\n    # sizes).\n    # Images should be in RGB for colorspace augmentations.\n    # (cv2.imread() returns BGR!)\n    # Images should usually be in uint8 with values from 0-255.\n    return np.zeros((128, 32, 32, 3), dtype=np.uint8) + (batch_idx % 255)\n\ndef train_on_images(images):\n    # dummy function, implement this\n    pass\n\n# Pipeline:\n# (1) Crop images from each side by 1-16px, do not resize the results\n#     images back to the input size. Keep them at the cropped size.\n# (2) Horizontally flip 50% of the images.\n# (3) Blur images using a gaussian kernel with sigma between 0.0 and 3.0.\nseq = iaa.Sequential([\n    iaa.Crop(px=(1, 16), keep_size=False),\n    iaa.Fliplr(0.5),\n    iaa.GaussianBlur(sigma=(0, 3.0))\n])\n\nfor batch_idx in range(100):\n    images = load_batch(batch_idx)\n    images_aug = seq(images=images)  # done by the library\n    train_on_images(images_aug)\n```\n\n\n### Example: Very Complex Augmentation Pipeline\n\nApply a very heavy augmentation pipeline to images (used to create the image \nat the very top of this readme):\n```python\nimport numpy as np\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n# random example images\nimages = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\n\n# Sometimes(0.5, ...) applies the given augmenter in 50% of all cases,\n# e.g. Sometimes(0.5, GaussianBlur(0.3)) would blur roughly every second image.\nsometimes = lambda aug: iaa.Sometimes(0.5, aug)\n\n# Define our sequence of augmentation steps that will be applied to every image\n# All augmenters with per_channel=0.5 will sample one value _per image_\n# in 50% of all cases. In all other cases they will sample new values\n# _per channel_.\n\nseq = iaa.Sequential(\n    [\n        # apply the following augmenters to most images\n        iaa.Fliplr(0.5), # horizontally flip 50% of all images\n        iaa.Flipud(0.2), # vertically flip 20% of all images\n        # crop images by -5% to 10% of their height/width\n        sometimes(iaa.CropAndPad(\n            percent=(-0.05, 0.1),\n            pad_mode=ia.ALL,\n            pad_cval=(0, 255)\n        )),\n        sometimes(iaa.Affine(\n            scale={\"x\": (0.8, 1.2), \"y\": (0.8, 1.2)}, # scale images to 80-120% of their size, individually per axis\n            translate_percent={\"x\": (-0.2, 0.2), \"y\": (-0.2, 0.2)}, # translate by -20 to +20 percent (per axis)\n            rotate=(-45, 45), # rotate by -45 to +45 degrees\n            shear=(-16, 16), # shear by -16 to +16 degrees\n            order=[0, 1], # use nearest neighbour or bilinear interpolation (fast)\n            cval=(0, 255), # if mode is constant, use a cval between 0 and 255\n            mode=ia.ALL # use any of scikit-image's warping modes (see 2nd image from the top for examples)\n        )),\n        # execute 0 to 5 of the following (less important) augmenters per image\n        # don't execute all of them, as that would often be way too strong\n        iaa.SomeOf((0, 5),\n            [\n                sometimes(iaa.Superpixels(p_replace=(0, 1.0), n_segments=(20, 200))), # convert images into their superpixel representation\n                iaa.OneOf([\n                    iaa.GaussianBlur((0, 3.0)), # blur images with a sigma between 0 and 3.0\n                    iaa.AverageBlur(k=(2, 7)), # blur image using local means with kernel sizes between 2 and 7\n                    iaa.MedianBlur(k=(3, 11)), # blur image using local medians with kernel sizes between 2 and 7\n                ]),\n                iaa.Sharpen(alpha=(0, 1.0), lightness=(0.75, 1.5)), # sharpen images\n                iaa.Emboss(alpha=(0, 1.0), strength=(0, 2.0)), # emboss images\n                # search either for all edges or for directed edges,\n                # blend the result with the original image using a blobby mask\n                iaa.SimplexNoiseAlpha(iaa.OneOf([\n                    iaa.EdgeDetect(alpha=(0.5, 1.0)),\n                    iaa.DirectedEdgeDetect(alpha=(0.5, 1.0), direction=(0.0, 1.0)),\n                ])),\n                iaa.AdditiveGaussianNoise(loc=0, scale=(0.0, 0.05*255), per_channel=0.5), # add gaussian noise to images\n                iaa.OneOf([\n                    iaa.Dropout((0.01, 0.1), per_channel=0.5), # randomly remove up to 10% of the pixels\n                    iaa.CoarseDropout((0.03, 0.15), size_percent=(0.02, 0.05), per_channel=0.2),\n                ]),\n                iaa.Invert(0.05, per_channel=True), # invert color channels\n                iaa.Add((-10, 10), per_channel=0.5), # change brightness of images (by -10 to 10 of original value)\n                iaa.AddToHueAndSaturation((-20, 20)), # change hue and saturation\n                # either change the brightness of the whole image (sometimes\n                # per channel) or change the brightness of subareas\n                iaa.OneOf([\n                    iaa.Multiply((0.5, 1.5), per_channel=0.5),\n                    iaa.FrequencyNoiseAlpha(\n                        exponent=(-4, 0),\n                        first=iaa.Multiply((0.5, 1.5), per_channel=True),\n                        second=iaa.LinearContrast((0.5, 2.0))\n                    )\n                ]),\n                iaa.LinearContrast((0.5, 2.0), per_channel=0.5), # improve or worsen the contrast\n                iaa.Grayscale(alpha=(0.0, 1.0)),\n                sometimes(iaa.ElasticTransformation(alpha=(0.5, 3.5), sigma=0.25)), # move pixels locally around (with random strengths)\n                sometimes(iaa.PiecewiseAffine(scale=(0.01, 0.05))), # sometimes move parts of the image around\n                sometimes(iaa.PerspectiveTransform(scale=(0.01, 0.1)))\n            ],\n            random_order=True\n        )\n    ],\n    random_order=True\n)\nimages_aug = seq(images=images)\n```\n\n\n### Example: Augment Images and Keypoints\n\nAugment images and keypoints/landmarks on the same images:\n```python\nimport numpy as np\nimport imgaug.augmenters as iaa\n\nimages = np.zeros((2, 128, 128, 3), dtype=np.uint8)  # two example images\nimages[:, 64, 64, :] = 255\npoints = [\n    [(10.5, 20.5)],  # points on first image\n    [(50.5, 50.5), (60.5, 60.5), (70.5, 70.5)]  # points on second image\n]\n\nseq = iaa.Sequential([\n    iaa.AdditiveGaussianNoise(scale=0.05*255),\n    iaa.Affine(translate_px={\"x\": (1, 5)})\n])\n\n# augment keypoints and images\nimages_aug, points_aug = seq(images=images, keypoints=points)\n\nprint(\"Image 1 center\", np.argmax(images_aug[0, 64, 64:64+6, 0]))\nprint(\"Image 2 center\", np.argmax(images_aug[1, 64, 64:64+6, 0]))\nprint(\"Points 1\", points_aug[0])\nprint(\"Points 2\", points_aug[1])\n```\nNote that all coordinates in `imgaug` are subpixel-accurate, which is\nwhy `x=0.5, y=0.5` denotes the center of the top left pixel.\n\n\n### Example: Augment Images and Bounding Boxes\n\n```python\nimport numpy as np\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\nimages = np.zeros((2, 128, 128, 3), dtype=np.uint8)  # two example images\nimages[:, 64, 64, :] = 255\nbbs = [\n    [ia.BoundingBox(x1=10.5, y1=15.5, x2=30.5, y2=50.5)],\n    [ia.BoundingBox(x1=10.5, y1=20.5, x2=50.5, y2=50.5),\n     ia.BoundingBox(x1=40.5, y1=75.5, x2=70.5, y2=100.5)]\n]\n\nseq = iaa.Sequential([\n    iaa.AdditiveGaussianNoise(scale=0.05*255),\n    iaa.Affine(translate_px={\"x\": (1, 5)})\n])\n\nimages_aug, bbs_aug = seq(images=images, bounding_boxes=bbs)\n```\n\n\n### Example: Augment Images and Polygons\n\n```python\nimport numpy as np\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\nimages = np.zeros((2, 128, 128, 3), dtype=np.uint8)  # two example images\nimages[:, 64, 64, :] = 255\npolygons = [\n    [ia.Polygon([(10.5, 10.5), (50.5, 10.5), (50.5, 50.5)])],\n    [ia.Polygon([(0.0, 64.5), (64.5, 0.0), (128.0, 128.0), (64.5, 128.0)])]\n]\n\nseq = iaa.Sequential([\n    iaa.AdditiveGaussianNoise(scale=0.05*255),\n    iaa.Affine(translate_px={\"x\": (1, 5)})\n])\n\nimages_aug, polygons_aug = seq(images=images, polygons=polygons)\n```\n\n\n### Example: Augment Images and LineStrings\n\nLineStrings are similar to polygons, but are not closed, may intersect with\nthemselves and don't have an inner area.\n```python\nimport numpy as np\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\nimages = np.zeros((2, 128, 128, 3), dtype=np.uint8)  # two example images\nimages[:, 64, 64, :] = 255\nls = [\n    [ia.LineString([(10.5, 10.5), (50.5, 10.5), (50.5, 50.5)])],\n    [ia.LineString([(0.0, 64.5), (64.5, 0.0), (128.0, 128.0), (64.5, 128.0),\n                    (128.0, 0.0)])]\n]\n\nseq = iaa.Sequential([\n    iaa.AdditiveGaussianNoise(scale=0.05*255),\n    iaa.Affine(translate_px={\"x\": (1, 5)})\n])\n\nimages_aug, ls_aug = seq(images=images, line_strings=ls)\n```\n\n\n### Example: Augment Images and Heatmaps\n\nHeatmaps are dense float arrays with values between `0.0` and `1.0`.\nThey can be used e.g. when training models to predict facial landmark\nlocations. Note that the heatmaps here have lower height and width than the\nimages. `imgaug` handles that case automatically. The crop pixel amounts will\nbe halved for the heatmaps.\n\n```python\nimport numpy as np\nimport imgaug.augmenters as iaa\n\n# Standard scenario: You have N RGB-images and additionally 21 heatmaps per\n# image. You want to augment each image and its heatmaps identically.\nimages = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\nheatmaps = np.random.random(size=(16, 64, 64, 1)).astype(np.float32)\n\nseq = iaa.Sequential([\n    iaa.GaussianBlur((0, 3.0)),\n    iaa.Affine(translate_px={\"x\": (-40, 40)}),\n    iaa.Crop(px=(0, 10))\n])\n\nimages_aug, heatmaps_aug = seq(images=images, heatmaps=heatmaps)\n```\n\n\n### Example: Augment Images and Segmentation Maps\n\nThis is similar to heatmaps, but the dense arrays have dtype `int32`.\nOperations such as resizing will automatically use nearest neighbour\ninterpolation.\n\n```python\nimport numpy as np\nimport imgaug.augmenters as iaa\n\n# Standard scenario: You have N=16 RGB-images and additionally one segmentation\n# map per image. You want to augment each image and its heatmaps identically.\nimages = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\nsegmaps = np.random.randint(0, 10, size=(16, 64, 64, 1), dtype=np.int32)\n\nseq = iaa.Sequential([\n    iaa.GaussianBlur((0, 3.0)),\n    iaa.Affine(translate_px={\"x\": (-40, 40)}),\n    iaa.Crop(px=(0, 10))\n])\n\nimages_aug, segmaps_aug = seq(images=images, segmentation_maps=segmaps)\n```\n\n\n### Example: Visualize Augmented Images\n\nQuickly show example results of your augmentation sequence:\n```python\nimport numpy as np\nimport imgaug.augmenters as iaa\n\nimages = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\nseq = iaa.Sequential([iaa.Fliplr(0.5), iaa.GaussianBlur((0, 3.0))])\n\n# Show an image with 8*8 augmented versions of image 0 and 8*8 augmented\n# versions of image 1. Identical augmentations will be applied to\n# image 0 and 1.\nseq.show_grid([images[0], images[1]], cols=8, rows=8)\n```\n\n\n### Example: Visualize Augmented Non-Image Data\n\n`imgaug` contains many helper function, among these functions to quickly\nvisualize augmented non-image results, such as bounding boxes or heatmaps.\n\n```python\nimport numpy as np\nimport imgaug as ia\n\nimage = np.zeros((64, 64, 3), dtype=np.uint8)\n\n# points\nkps = [ia.Keypoint(x=10.5, y=20.5), ia.Keypoint(x=60.5, y=60.5)]\nkpsoi = ia.KeypointsOnImage(kps, shape=image.shape)\nimage_with_kps = kpsoi.draw_on_image(image, size=7, color=(0, 0, 255))\nia.imshow(image_with_kps)\n\n# bbs\nbbsoi = ia.BoundingBoxesOnImage([\n    ia.BoundingBox(x1=10.5, y1=20.5, x2=50.5, y2=30.5)\n], shape=image.shape)\nimage_with_bbs = bbsoi.draw_on_image(image)\nimage_with_bbs = ia.BoundingBox(\n    x1=50.5, y1=10.5, x2=100.5, y2=16.5\n).draw_on_image(image_with_bbs, color=(255, 0, 0), size=3)\nia.imshow(image_with_bbs)\n\n# polygons\npsoi = ia.PolygonsOnImage([\n    ia.Polygon([(10.5, 20.5), (50.5, 30.5), (10.5, 50.5)])\n], shape=image.shape)\nimage_with_polys = psoi.draw_on_image(\n    image, alpha_points=0, alpha_face=0.5, color_lines=(255, 0, 0))\nia.imshow(image_with_polys)\n\n# heatmaps\nhms = ia.HeatmapsOnImage(np.random.random(size=(32, 32, 1)).astype(np.float32),\n                         shape=image.shape)\nimage_with_hms = hms.draw_on_image(image)\nia.imshow(image_with_hms)\n```\n\nLineStrings and segmentation maps support similar methods as shown above.\n\n\n### Example: Using Augmenters Only Once \n\nWhile the interface is adapted towards re-using instances of augmenters\nmany times, you are also free to use them only once. The overhead to\ninstantiate the augmenters each time is usually negligible.\n\n```python\nfrom imgaug import augmenters as iaa\nimport numpy as np\n\nimages = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\n\n# always horizontally flip each input image\nimages_aug = iaa.Fliplr(1.0)(images=images)\n\n# vertically flip each input image with 90% probability\nimages_aug = iaa.Flipud(0.9)(images=images)\n\n# blur 50% of all images using a gaussian kernel with a sigma of 3.0\nimages_aug = iaa.Sometimes(0.5, iaa.GaussianBlur(3.0))(images=images)\n```\n\n\n### Example: Multicore Augmentation\n\nImages can be augmented in **background processes** using the\nmethod `augment_batches(batches, background=True)`, where `batches` is\na list/generator of\n[imgaug.augmentables.batches.UnnormalizedBatch](https://imgaug.readthedocs.io/en/latest/_modules/imgaug/augmentables/batches.html#UnnormalizedBatch)\nor\n[imgaug.augmentables.batches.Batch](https://imgaug.readthedocs.io/en/latest/source/api_augmentables_batches.html#imgaug.augmentables.batches.Batch).\nThe following example augments a list of image batches in the background:\n```python\nimport skimage.data\nimport imgaug as ia\nimport imgaug.augmenters as iaa\nfrom imgaug.augmentables.batches import UnnormalizedBatch\n\n# Number of batches and batch size for this example\nnb_batches = 10\nbatch_size = 32\n\n# Example augmentation sequence to run in the background\naugseq = iaa.Sequential([\n    iaa.Fliplr(0.5),\n    iaa.CoarseDropout(p=0.1, size_percent=0.1)\n])\n\n# For simplicity, we use the same image here many times\nastronaut = skimage.data.astronaut()\nastronaut = ia.imresize_single_image(astronaut, (64, 64))\n\n# Make batches out of the example image (here: 10 batches, each 32 times\n# the example image)\nbatches = []\nfor _ in range(nb_batches):\n    batches.append(UnnormalizedBatch(images=[astronaut] * batch_size))\n\n# Show the augmented images.\n# Note that augment_batches() returns a generator.\nfor images_aug in augseq.augment_batches(batches, background=True):\n    ia.imshow(ia.draw_grid(images_aug.images_aug, cols=8))\n```\n\nIf you need more control over the background augmentation, e.g. to set\nseeds, control the number of used CPU cores or constraint the memory usage,\nsee the corresponding\n[multicore augmentation notebook](https://nbviewer.jupyter.org/github/aleju/imgaug-doc/blob/master/notebooks/A03%20-%20Multicore%20Augmentation.ipynb)\nor the API about\n[Augmenter.pool()](https://imgaug.readthedocs.io/en/latest/source/api_augmenters_meta.html#imgaug.augmenters.meta.Augmenter.pool)\nand\n[imgaug.multicore.Pool](https://imgaug.readthedocs.io/en/latest/source/api_multicore.html#imgaug.multicore.Pool).\n\n\n### Example: Probability Distributions as Parameters\n\nMost augmenters support using tuples `(a, b)` as a shortcut to denote\n`uniform(a, b)` or lists `[a, b, c]` to denote a set of allowed values from\nwhich one will be picked randomly. If you require more complex probability\ndistributions (e.g. gaussians, truncated gaussians or poisson distributions)\nyou can use stochastic parameters from `imgaug.parameters`:\n\n```python\nimport numpy as np\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\n\nimages = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\n\n# Blur by a value sigma which is sampled from a uniform distribution\n# of range 10.1 <= x < 13.0.\n# The convenience shortcut for this is: GaussianBlur((10.1, 13.0))\nblurer = iaa.GaussianBlur(10 + iap.Uniform(0.1, 3.0))\nimages_aug = blurer(images=images)\n\n# Blur by a value sigma which is sampled from a gaussian distribution\n# N(1.0, 0.1), i.e. sample a value that is usually around 1.0.\n# Clip the resulting value so that it never gets below 0.1 or above 3.0.\nblurer = iaa.GaussianBlur(iap.Clip(iap.Normal(1.0, 0.1), 0.1, 3.0))\nimages_aug = blurer(images=images)\n```\n\nThere are many more probability distributions in the library, e.g. truncated\ngaussian distribution, poisson distribution or beta distribution.\n\n\n### Example: WithChannels\n\nApply an augmenter only to specific image channels:\n```python\nimport numpy as np\nimport imgaug.augmenters as iaa\n\n# fake RGB images\nimages = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\n\n# add a random value from the range (-30, 30) to the first two channels of\n# input images (e.g. to the R and G channels)\naug = iaa.WithChannels(\n  channels=[0, 1],\n  children=iaa.Add((-30, 30))\n)\n\nimages_aug = aug(images=images)\n```\n\n\n<a name=\"citation\"/>\n\n## Citation\n\n<!--\nNote: the table only lists people who have their real names (publicly)\nset in their github\n\nList of username-realname matching based on\nhttps://github.com/aleju/imgaug/graphs/contributors ordered by commits:\n\nwkentaro            Wada, Kentaro\nErotemic            Crall, Jon\nstnk20              Tanaka, Satoshi\njgraving            Graving, Jake\ncreinders           Reinders, Christoph     (lastname not public on github, guessed from username)\nSarthakYadav        Yadav, Sarthak\nnektor211           ?\njoybanerjee08       Banerjee, Joy\ngaborvecsei         Vecsei, Gábor\nadamwkraft          Kraft, Adam\nZhengRui            Rui, Zheng\nBorda               Borovec, Jirka\nvallentin           Vallentin, Christian\nss18                Zhydenko, Semen\nkilsenp             Pfeiffer, Kilian\nkacper1095          ?\nismaelfm            Fernández, Ismael\nfmder               De Rainville, François-Michel\nfchouteau           ?\nchi-hung            Weng, Chi-Hung\napatsekin           ?\nabnera              Ayala-Acevedo, Abner\nRephaelMeudec       Meudec, Raphael\nPetemir             Laporte, Matias\n\n-->\nIf this library has helped you during your research, feel free to cite it:\n```latex\n@misc{imgaug,\n  author = {Jung, Alexander B.\n            and Wada, Kentaro\n            and Crall, Jon\n            and Tanaka, Satoshi\n            and Graving, Jake\n            and Reinders, Christoph\n            and Yadav, Sarthak\n            and Banerjee, Joy\n            and Vecsei, Gábor\n            and Kraft, Adam\n            and Rui, Zheng\n            and Borovec, Jirka\n            and Vallentin, Christian\n            and Zhydenko, Semen\n            and Pfeiffer, Kilian\n            and Cook, Ben\n            and Fernández, Ismael\n            and De Rainville, François-Michel\n            and Weng, Chi-Hung\n            and Ayala-Acevedo, Abner\n            and Meudec, Raphael\n            and Laporte, Matias\n            and others},\n  title = {{imgaug}},\n  howpublished = {\\url{https://github.com/aleju/imgaug}},\n  year = {2020},\n  note = {Online; accessed 01-Feb-2020}\n}\n```\n"
  },
  {
    "path": "bandit.yml",
    "content": "skips: [\n  'B101',\n  'B301',  # \"Pickle and modules that wrap it can be unsafe when used to\n           #  deserialize untrusted data, possible security issue.\"\n  'B403'   # \"Consider possible security implications associated with pickle\n           #  module.\"\n]"
  },
  {
    "path": "changelogs/0.3.0/v0.3.0.cleaned.md",
    "content": "# 0.3.0 Cleaned Up Log Of Changes\n\n## Improved Segmentation Map Augmentation #302\n\nAugmentation of Segmentation Maps is now faster and more memory efficient.\nThis required some breaking changes to `SegmentationMapOnImage`.\nTo adapt to the new version, the following steps should be sufficient for most\nusers:\n\n* Rename all calls of `SegmentationMapOnImage` to `SegmentationMapsOnImage`\n  (Map -> Maps).\n* Rename all calls of `SegmentationMapsOnImage.get_arr_int()` to\n  `SegmentationMapsOnImage.get_arr()`.\n* Remove the argument `nb_classes` from all calls of `SegmentationMapsOnImage`.\n* Remove the arguments `background_id` and `background_threshold` from all\n  calls as these are no longer supported.\n* Ensure that the input array to `SegmentationMapsOnImage` is always an\n  int-like (int, uint or bool).\n  Float arrays are no longer accepted.\n* Adapt all calls `SegmentationMapsOnImage.draw()` and\n  `SegmentationMapsOnImage.draw_on_image()`, as both of these now return a\n  list of drawn images instead of a single array. (For a segmentation map\n  array of shape `(H,W,C)` they return `C` drawn images. In most cases `C=1`,\n  so simply call `draw()[0]` or `draw_on_image()[0]`.)\n* Ensure that if `SegmentationMapsOnImage.arr` is accessed anywhere, the\n  respective code can handle the new `int32` `(H,W,#maps)` array form.\n  Previously it was `float32` and the channel-axis had the same size as the\n  max class id (+1) that could appear in the map.\n\nChanges:\n\n- Changes to class `SegmentationMapOnImage`:\n    - Renamed `SegmentationMapOnImage` to plural `SegmentationMapsOnImage`\n      and deprecated the old name.\n      This was changed due to the input array now being allowed to contain\n      several channels, with each such channel containing one full segmentation\n      map.\n    - Changed `SegmentationMapsOnImage.__init__` to produce a deprecation\n      warning for float arrays as `arr` argument.\n    - **[breaking]** Changed `SegmentationMapsOnImage.__init__` to no longer\n      accept `uint32` and larger itemsizes as `arr` argument, only `uint16`\n      and below is accepted. For `int` the allowed maximum is `int32`.\n    - Changed `SegmentationMapsOnImage.__init__` to always accept `(H,W,C)`\n      `arr` arguments.\n    - **[breaking]** Changed  `SegmentationMapsOnImage.arr` to always be\n      `int32` `(H,W,#maps)` (previously: `float32` `(H,W,#nb_classes)`).\n    - Deprecated `nb_classes` argument in `SegmentationMapsOnImage.__init__`.\n      The argument is now ignored.\n    - Added `SegmentationMapsOnImage.get_arr()`, which always returns a\n      segmentation map array with similar dtype and number of dimensions as\n      was originally input when creating a class instance.\n    - Deprecated `SegmentationMapsOnImage.get_arr_int()`.\n      The method is now an alias for `get_arr()`.\n    - `SegmentationMapsOnImage.draw()`:\n        - **[breaking]** Removed argument `return_foreground_mask` and\n          corresponding optional output. To generate a foreground mask\n          for the `c`-th segmentation map on a given image (usually `c=0`),\n          use `segmentation_map.arr[:, :, c] != 0`, assuming that `0` is\n          the integer index of your background class. \n        - **[breaking]** Changed output of drawn image to be a list of arrays\n          instead of a single array (one per `C` in input array `(H,W,C)`).\n        - Refactored to be a wrapper around\n          `SegmentationMapsOnImage.draw_on_image()`.\n        - The `size` argument may now be any of: A single `None` (keep shape),\n          a single integer (use as height and width), a single float (relative\n          change to shape) or a tuple of these values. (\"shape\" here denotes\n          the value of the `.shape` attribute.)\n    - `SegmentationMapsOnImage.draw_on_image()`:\n        - **[breaking]** The argument `background_threshold` is now deprecated\n          and ignored. Providing it will lead to a deprecation warning.\n        - **[breaking]** Changed output of drawn image to be a list of arrays\n          instead of a single array (one per `C` in input array `(H,W,C)`).\n    - Changed `SegmentationMapsOnImage.resize()` to use nearest neighbour\n      interpolation by default.\n    - **[rarely breaking]** Changed `SegmentationMapsOnImage.copy()` to create\n      a shallow copy instead of being an alias for `deepcopy()`.\n    - Added optional arguments `arr` and `shape` to\n      `SegmentationMapsOnImage.copy()`.\n    - Added optional arguments `arr` and `shape` to\n      `SegmentationMapsOnImage.deepcopy()`.\n    - Refactored `SegmentationMapsOnImage.pad()`,\n      `SegmentationMapsOnImage.pad_to_aspect_ratio()` and\n      `SegmentationMapsOnImage.resize()` to generate new object instances via\n      `SegmentationMapsOnImage.deepcopy()`.\n    - **[rarely breaking]** Renamed `SegmentationMapsOnImage.input_was` to\n      `SegmentationMapsOnImage._input_was`.\n    - **[rarely breaking]** Changed `SegmentationMapsOnImage._input_was` to\n      always save `(input array dtype, input array ndim)` instead of mixtures\n      of strings/ints that varied by dtype kind.\n    - **[rarely breaking]** Restrict `shape` argument in\n      `SegmentationMapsOnImage.__init__` to tuples instead of accepting all\n      iterables.\n    - **[breaking]** Removed `SegmentationMapsOnImage.to_heatmaps()` as the\n      new segmentation map class is too different to sustain the old heatmap\n      conversion methods.\n    - **[breaking]** Removed `SegmentationMapsOnImage.from_heatmaps()` as the\n      new segmentation map class is too different to sustain the old heatmap\n      conversion methods.\n- Changes to class `Augmenter`:\n    - **[breaking]** Automatic segmentation map normalization from arrays or\n      lists of arrays now expects a single `(N,H,W,C)` array (before:\n      `(N,H,W)`) or a list of `(H,W,C)` arrays (before: `(H,W)`).\n      This affects valid segmentation map inputs for `Augmenter.augment()`\n      and its alias `Augmenter.__call__()`,\n      `imgaug.augmentables.batches.UnnormalizedBatch()` and\n      `imgaug.augmentables.normalization.normalize_segmentation_maps()`.\n    - Added `Augmenter._augment_segmentation_maps()`.\n    - Changed `Augmenter.augment_segmentation_maps()` to no longer be a\n    wrapper around `Augmenter.augment_heatmaps()` and instead call\n    `Augmenter._augment_segmentation_maps()`.\n- Added special segmentation map handling to all augmenters that modified\n  segmentation maps\n  (`Sequential`, `SomeOf`, `Sometimes`, `WithChannels`,\n   `Lambda`, `AssertLambda`, `AssertShape`,\n   `Alpha`, `AlphaElementwise`, `WithColorspace`, `Fliplr`, `Flipud`, `Affine`,\n   `AffineCv2`, `PiecewiseAffine`, `PerspectiveTransform`, `ElasticTransformation`,\n   `Rot90`, `Resize`, `CropAndPad`, `PadToFixedSize`, `CropToFixedSize`,\n   `KeepSizeByResize`).\n   - **[rarely breaking]** This changes the order of arguments in\n     `Lambda.__init__()`, `AssertLambda.__init__()`, `AssertShape.__init__()`\n     and hence breaks if one relied on that order.\n\n## New RNG handling #375\n\n* Adapted library to automatically use the new `numpy.random` classes of\n  numpy 1.17 -- if they are available. If they are not available (i.e. numpy\n  version is <=1.16), the library automatically falls back to the old\n  interface (i.e. `numpy.random.RandomState`).\n* Added module `imgaug.random`.\n  * Added class `imgaug.random.RNG`. This is now the preferred way to represent\n    RNG states (previously: `numpy.random.RandomState`). Instantiate it\n    via e.g. `RNG(1052912236)`, where `1052912236` is a seed.\n  * Added `imgaug.random.supports_new_rng_style()`.\n  * Added `imgaug.random.get_global_rng()`.\n  * Added `imgaug.random.seed()`.\n  * Added `imgaug.random.normalize_generator()`.\n  * Added `imgaug.random.normalize_generator_()`.\n  * Added `imgaug.random.convert_seed_to_generator()`.\n  * Added `imgaug.random.convert_seed_sequence_to_generator()`.\n  * Added `imgaug.random.create_pseudo_random_generator_()`.\n  * Added `imgaug.random.create_fully_random_generator()`.\n  * Added `imgaug.random.generate_seed_()`.\n  * Added `imgaug.random.generate_seeds_()`.\n  * Added `imgaug.random.copy_generator()`.\n  * Added `imgaug.random.copy_generator_unless_global_generator()`.\n  * Added `imgaug.random.reset_generator_cache_()`.\n  * Added `imgaug.random.derive_generator_()`.\n  * Added `imgaug.random.derive_generators_()`.\n  * Added `imgaug.random.get_generator_state()`.\n  * Added `imgaug.random.set_generator_state_()`.\n  * Added `imgaug.random.is_generator_equal_to()`.\n  * Added `imgaug.random.advance_generator_()`.\n  * Added `imgaug.random.polyfill_integers()`.\n  * Added `imgaug.random.polyfill_random()`.\n* Refactored all arguments related to random state handling to also accept\n  `imgaug.random.RNG`, as well as the new numpy random classes. This\n  particularly affects `imgaug.augmenters.meta.Augmenter` and\n  `imgaug.parameters.StochasticParameter` (argument `random_state` for both).\n* Marked old RNG related functions in `imgaug.imgaug` as deprecated.\n  They will now produce warnings and redirect towards corresponding functions\n  in `imgaug.random`. This does not yet affect `imgaug.imgaug.seed()`.\n  It does affect the functions listed below.\n  * `imgaug.imgaug.normalize_random_state()`.\n  * `imgaug.imgaug.current_random_state()`.\n  * `imgaug.imgaug.new_random_state()`.\n  * `imgaug.imgaug.dummy_random_state()`.\n  * `imgaug.imgaug.copy_random_state()`.\n  * `imgaug.imgaug.derive_random_state()`.\n  * `imgaug.imgaug.derive_random_states()`.\n  * `imgaug.imgaug.forward_random_state()`.\n* [rarely breaking] Removed `imgaug.imgaug.CURRENT_RANDOM_STATE`.\n  Use `imgaug.random.get_global_rng()` instead.\n* [rarely breaking] Removed `imgaug.imgaug.SEED_MIN_VALUE`.\n  Use `imgaug.random.SEED_MIN_VALUE` instead or sample seeds via\n  `imgaug.random.generate_seeds_()`.\n* [rarely breaking] Removed `imgaug.imgaug.SEED_MAX_VALUE`.\n  Use `imgaug.random.SEED_MAX_VALUE` instead or sample seeds via\n  `imgaug.random.generate_seeds_()`.\n* Optimized RNG handling throughout all augmenters to minimize the number of\n  RNG copies. RNGs are now re-used as often as possible. This improves\n  performance, but has the disadvantage that adding images to a batch will now\n  often affect the samples of the other images in the same batch. E.g.\n  previously for a batch of images `A,B,C` and seed `1`, the samples of `A,B,C`\n  would remain unchanged if the batch was changed to `A,B,C,D` (provided the\n  seed stayed the same). Now, if `D` is added the samples of `A,B,C` may\n  change.\n* [breaking] The above listed changes will lead to different values being\n  sampled for the same seeds (compared to past versions of the library).\n* [breaking] The seed for `imgaug`'s global random number generator is now\n  sampled from numpy's default random number generator. That means, that every\n  run of a program using `imgaug` will by default use a different seed and\n  hence result in different samples. Previously, a fixed seed was used,\n  resulting in the same samples for each run (unless the seed was manually\n  changed to a fixed one). It also means that seeding numpy will automatically\n  also seed imgaug (not guarantueed that this behaviour will be kept in\n  future releases). The change from fixed to random seed was done, because the\n  old (fixed) behaviour didn't match the common practice (and especially not\n  numpy's standard behaviour) and hence led to confusion. #408\n\n\n## Adaptations to numpy 1.17\n\n* [rarely breaking] Deactivated support for `int64` in\n  `imgaug.dtypes.clip_()`. This is due to numpy 1.17 turning `int64` to\n  `float64` in `numpy.clip()` (possible that this happened in some way\n  before 1.17 too). #302\n* [rarely breaking] Changed `imgaug.dtypes.clip()` to never clip `int32`\n  in-place, as `numpy.clip()` turns it into `float64` since 1.17 (possible\n  that this happend in some way before 1.17 too).\n* [rarely breaking] Deactivated support for `int64` in\n  `ReplaceElementwise`. See `clip` issue above. #302\n* [rarely breaking] Changed `parameters.DiscreteUniform` to always return\n  arrays of dtype `int32`. Previously it would automatically return\n  `int64`. #302\n* [rarely breaking] Changed `parameters.Deterministic` to always return\n  `int32` for integers and always `float32` for floats. #302\n* [rarely breaking] Changed `parameters.Choice` to limit integer\n  dtypes to `int32` or lower, uints to `uint32` or lower and floats\n  to `float32` or lower. #302\n* [rarely breaking] Changed `parameters.Binomial` and `parameters.Poisson`\n  to always return `int32`. #302\n* [rarely breaking] Changed `parameters.Normal`,\n  `parameters.TruncatedNormal`, `parameters.Laplace`,\n  `parameters.ChiSquare`, `parameters.Weibull`, `parameters.Uniform` and\n  `parameters.Beta` to always return `float32`. #302\n* [rarely breaking] Changed `augmenters.arithmetic.Add`,\n  `augmenters.arithmetic.AddElementwise`, `augmenters.arithmetic.Multiply`\n  and `augmenters.arithmetic.MultiplyElementwise` to no longer internally\n  increase itemsize of dtypes by a factor of 2 for\n  dtypes `uint16`, `int8` and `uint16`. For `Multiply*` this also\n  covers `float16` and `float32`. This protects against crashes due to\n  clipping `int64` or `uint64` data. In rare cases this can lead to\n  overflows if `image + random samples` or `image * random samples`\n  exceeds the value range of `int32` or `uint32`. This change may affect\n  various other augmenters that are wrappers around the mentioned ones,\n  e.g. `AdditiveGaussianNoise`. #302\n* [rarely breaking] Decreased support of dtypes `uint16`, `int8`,\n  `int16`, `float16`, `float32` and `bool` in `augmenters.arithmetic.Add`,\n  `AddElementwise`, `Multiply` and `MultiplyElementwise` from \"yes\" to\n  \"limited\". #302\n* [rarely breaking] Decreased support of dtype `int64` in\n  `augmenters.arithmetic.ReplaceElementwise` from \"yes\" to \"no\". This also\n  affects all `*Noise` augmenters (e.g. `AdditiveGaussianNoise`,\n  `ImpulseNoise`), all `Dropout` augmenters, all `Salt` augmenters and\n  all `Pepper` augmenters. #302\n* [rarely breaking] Changed `augmenters.contrast.adjust_contrast_log`\n  and thereby `LogContrast` to no longer support dtypes `uint32`, `uint64`,\n  `int32` and `int64`. #302\n\n\n## New Augmenters\n\n\n* Added `augmenters.edges.Canny`, which applies canny edge detection with alpha\n  blending and random coloring to images. #316\n* Added `augmenters.pooling.AveragePooling`. #317\n* Added `augmenters.pooling.MaxPooling`. #317\n* Added `augmenters.pooling.MinPooling`. #317\n* Added `augmenters.pooling.MedianPooling`. #317\n* Added `augmenters.color.AddToHue`, a shortcut for\n  `AddToHueAndSaturation(value_hue=...)`. #319\n* Added `augmenters.color.AddToSaturation`, a shortcut for\n  `AddToHueAndSaturation(value_saturation=...)`. #319\n* Added `augmenters.color.WithHueAndSaturation`. #319\n* Added `augmenters.color.MultiplyHueAndSaturation`. #319\n* Added `augmenters.color.MultiplyHue`. #319\n* Added `augmenters.color.MultiplySaturation`. #319\n* Added `augmenters.color.KMeansColorQuantization` and corresponding\n  `augmenters.color.quantize_colors_kmeans()`. Both deal with quantizing\n  similar colors using k-Means clustering. #347\n    * Added a check script for `KMeansColorQuantization` under\n      `checks/check_kmeans_color_quantization.py`. #347\n* Added `augmenters.color.UniformColorQuantization` and corresponding\n  `augmenters.color.quantize_colors_uniform()`. Both deal with quantizing\n  similar colors using k-Means clustering. #347\n* Added `imgaug.augmenters.segmentation.Voronoi`. An augmenter that converts\n  an image to a voronoi image.  #348\n* Added `imgaug.augmenters.segmentation.UniformVoronoi`, a shortcut for\n  `Voronoi(UniformPointsSamper)`. #348\n* Added `imgaug.augmenters.segmentation.RegularGridVoronoi`, a shortcut for\n  `Voronoi(DropoutPointsSampler(RegularGridPointsSampler))`. #348\n* Added `imgaug.augmenters.segmentation.RelativeRegularGridVoronoi`, a shortcut\n  for `Voronoi(DropoutPointsSampler(RelativeRegularGridPointsSampler))`. #348\n\n\n## New Modules\n\n* Added module `imgaug.augmenters.edges`. #316\n* Added module `imgaug.augmenters.pooling`. #317\n* Added module `imgaug.validation`. The module is intended for functions\n  related to the validation of input arguments. #413\n\n\n## output_buffer_size\n\n* Added argument `output_buffer_size` to `multicore.Pool.imap_batches()`\n  and `multicore.Pool.imap_batches_unordered()` to control the maximum number\n  of batches in the background augmentation pipeline (allows to limit\n  maximum RAM demands). #305\n* Changed default `output_buffer_size` in `Augmenter.augment_batches()` from\n  \"unlimited\" to `10*C`, where `C` is the number of logical CPU cores. #417\n\n\n## Other New Classes\n\n* Added interface `augmenters.edges.BinaryImageColorizerIf`, which\n  contains the interface for classes used to convert binary images to RGB\n  images. #316\n* Added `augmenters.pooling._AbstractPoolingBase`. #317\n* Added `augmenters.edges.RandomColorsBinaryImageColorizer`, which\n  converts binary images to RGB images by sampling uniformly RGB colors for\n  `True` and `False` values. #316\n* Added `augmenters.color._AbstractColorQuantization`. #347\n* Added `imgaug.augmenters.segmentation.PointsSamplerIf`. An interface for\n  classes used for sampling (usually random) coordinate arrays on images. #348\n* Added `imgaug.augmenters.segmentation.RegularGridPointsSampler`. A class\n  used to generate regular grids of `rows x columns` points on images. #348\n* Added `imgaug.augmenters.segmentation.RelativeRegularGridPointsSampler`.\n  Similar to `RegularGridPointsSampler`, but number of rows/columns is set\n  as fractions of image sizes, leading to more rows/columns for larger\n  images. #348\n* Added `imgaug.augmenters.segmentation.DropoutPointsSampler`. A class\n  used to randomly drop `p` percent of all coordinates sampled by another\n  another points sampler. #348\n* Added `imgaug.augmenters.segmentation.UniformPointsSampler`. A class used\n  to sample `N` points on each image with y-/x-coordinates uniformly sampled\n  using the corresponding image height/width. #348 \n* Added `imgaug.augmenters.segmentation.SubsamplingPointsSampler`. A class\n  that ensures that another points sampler does not produce more than\n  `N` points by subsampling a random subset of the produced points if `N`\n  is exceeded. #348\n* Added `imgaug.testutils.ArgCopyingMagicMock`. #413\n\n\n## Other New Functions\n\n* Added `imgaug.is_np_scalar()`, analogous to `imgaug.is_np_array()`. #366\n* Added `dtypes.normalize_dtypes()`. #366\n* Added `dtypes.normalize_dtype()`. #366\n* Added `dtypes.change_dtypes_()`. #366\n* Added `dtypes.change_dtype_()`. #366\n* Added `dtypes.increase_itemsize_of_dtype()`. #366\n* Added `imgaug.warn()` function. #367\n* Added `imgaug.min_pool()`. #369\n* Added `imgaug.median_pool()`. #369\n* Added `imgaug.compute_paddings_to_reach_multiples_of()`. #369\n* Added `imgaug.pad_to_multiples_of()`. #369\n* Added `imgaug.imgaug.normalize_random_state()`. #348\n* Added `imgaug.augmenters.segmentation._ensure_image_max_size()`. #348\n* Added `imgaug.augmenters.segmentation._verify_sample_points_images()`. #348\n* Added `imgaug.augmenters.segmentation.segment_voronoi()`, a function that\n  converts an image into a voronoi image, i.e. averages the colors within\n  voronoi cells placed on the image. #348\n* Added `_match_pixels_with_voronoi_cells()`. #348\n* Added `_generate_pixel_coords()`. #348\n* Added `_compute_avg_segment_colors()`. #348\n* Added `_render_segments()`. #348\n* Added `augmentables.utils.copy_augmentables`. #410\n* Added `augmenters.flip.fliplr()`. #385\n* Added `augmenters.flip.flipud()`. #385\n* Added `augmenters.color.change_colorspace_()`. #409\n* Added `augmenters.color.change_colorspace_batch_()`. #409\n* Added `augmenters.arithmetic.add_scalar()`. #411\n* Added `augmenters.arithmetic.add_elementwise()`. #411\n* Added `augmenters.arithmetic.replace_elementwise_()`. #411\n* Added `augmenters.arithmetic.compress_jpg()`. #411\n* Added `validation.convert_iterable_to_string_of_types()`. #413\n* Added `validation.is_iterable_of()`. #413\n* Added `validation.assert_is_iterable_of()`. #413\n\n\n## Other New Constants\n\n* Added to `imgaug.augmenters.color` the constants `CSPACE_RGB`, \n  `CSPACE_BGR`, `CSPACE_GRAY`, `CSPACE_CIE`, `CSPACE_YCrCb`, `CSPACE_HSV`,\n  `CSPACE_HLS`, `CSPACE_Lab`, `CSPACE_Luv`, `CSPACE_YUV`, `CSPACE_ALL`. #409\n\n\n## Other New Arguments\n\n* [rarely breaking] Added a `pad_mode` argument to `imgaug.pool()`,\n  `imgaug.avg_pool()`, `imgaug.max_pool()`, `imgaug.min_pool()` and\n  `imgaug.median_pool()`. This breaks code relying on the order of the\n  functions arguments. #369\n* Added to argument `size` of `Resize` the optional keys `short-side` and\n  `longer-side`. This adds the ability to resize the shorter and longer sides\n  of images (instead of only height/width). #349\n* [rarely breaking] Added `value_hue` and `value_saturation` arguments,\n  which allow to set individual parameters for hue and saturation\n  instead of having to use one parameter for both (they may not be set\n  if `value` is already set).\n  This changes the order of arguments of the augmenter and code that relied\n  on that order will now break.\n  This also changes the output of\n  `AddToHueAndSaturation.get_parameters()`. #319\n* [rarely breaking] Added argument `polygon_recoverer` to\n  `augmenters.geometric.PerspectiveTransform`. This changes the order of\n  arguments of the augmenter and code that relied on that order will now\n  break. #338\n* Added attribute `from_colorspace` to `AddToHueAndSaturation`. #409\n\n\n## Other Removed Concepts\n\n* [rarely breaking] Removed `dtypes.get_minimal_dtype_for_values()`. The\n  function was not used anywhere in the library. #366\n* [rarely breaking] Removed `dtypes.get_minimal_dtype_by_value_range()`. The\n  function was not used anywhere in the library. #366\n* [rarely breaking] Removed `Affine.VALID_DTYPES_CV2_ORDER_0`. #407\n* [rarely breaking] Removed `Affine.VALID_DTYPES_CV2_ORDER_NOT_0`. #407\n* [rarely breaking] Removed `Affine.order_map_skimage_cv2`. #407\n* [rarely breaking] Removed `Affine.mode_map_skimage_cv2`. #407\n* [rarely breaking] Removed attributes `colorspace_changer` and\n  `colorspace_changer_inv` from `AddToHueAndSaturation`. #409\n* [rarely breaking] Removed class constant `ALLOW_DTYPES_CUSTOM_MINMAX`\n  from `Invert`. #411\n* [rarely breaking] Removed attribute `dtype_kind_to_invert_func` from\n  `Invert`. #411\n* [rarely breaking] Removed attribute `maximum_quality` from\n  `JpegCompression`. #411\n* [rarely breaking] Removed attribute `minimum_quality` from\n  `JpegCompression`. #411\n* [rarely breaking] Removed argument `affects` from\n  `dtypes.promote_array_dtypes_()` as it was unnecessary and not used anywhere\n  in the library. #366\n* [rarely breaking] Removed method\n  `ElasticTransformation.generate_shift_maps()`. Use\n  `ElasticTransformation._generate_shift_maps()` instead. #413\n* [rarely breaking] Removed method `ElasticTransformation.map_coordinates()`.\n  Use `ElasticTransformation._map_coordinates()` instead. #413\n\n\n## Performance\n\n* Replaced all calls of `imgaug.do_assert` with ordinary `assert`\n  statements. This is a bit less secure, but should overall improve\n  performance. #387\n* Improved performance of `augmenters.flip.Fliplr`. #385\n* Improved performance of `augmenters.flip.Flipud`. #385\n\n\n## Deprecation\n\n* Marked the following functions as deprecated (#398):\n  * `imgaug.augmenters.meta.clip_augmented_image_`\n  * `imgaug.augmenters.meta.clip_augmented_image`\n  * `imgaug.augmenters.meta.clip_augmented_images_`\n  * `imgaug.augmenters.meta.clip_augmented_images`\n* Marked `imgaug.augmenters.arithmetic.ContrastNormalization` as deprecated.\n  Use `imgaug.augmenters.contrast.LinearContrast` instead. #396\n* Marked argument `X` of `imgaug.augmentables.kps.compute_geometric_median()`\n  as deprecated. Use argument `points` instead. #402\n* Marked `cval` in `imgaug.pool()`, `imgaug.avg_pool()` and `imgaug.max_pool()`\n  as deprecated. Use `pad_cval` instead. #369\n\n\n## Refactorings\n\n* Refactored `augmenters.arithmetic` to improve code quality and\n  docstrings. #328\n* Refactored `augmenters.segmentation` to improve code quality and\n  docstrings. #334\n* Refactored `augmenters/convolutional.py` to improve code quality and\n  docstrings. #335\n* Refactored `augmenters/weather.py` to improve code quality and\n  docstrings. #336\n* Refactored `multicore` to improve code quality and\n  docstrings. #367\n  * Improved error messages in `multicore`.\n* Refactored `imgaug.pool()` to use `imgaug.pad()` for image padding. #369\n* Refactored `augmenters.pooling.MedianPooling` to use\n  `imgaug.median_pool()`. #369\n* Refactored `augmenters.pooling.MinPooling` to use `imgaug.min_pool()`. #369\n* Improved code style and documentation of (#389, #402):\n    * `imgaug.augmentables.bbs`.\n    * `imgaug.augmentables.heatmaps`.\n    * `imgaug.augmentables.kps`.\n    * `imgaug.augmentables.lines`.\n    * `imgaug.augmentables.normalization`.\n    * `imgaug.augmentables.polys`.\n    * `imgaug.augmentables.segmaps`.\n    * `imgaug.augmentables.utils`.\n    * `imgaug.imgaug`.\n    * `imgaug.parameters`.\n    * `imgaug.augmenters.weather`.\n    * `imgaug.augmenters.size`.\n    * `imgaug.augmenters.segmentation`.\n    * `imgaug.augmenters.meta`.\n    * `imgaug.augmenters.geometric`.\n    * `imgaug.augmenters.flip`.\n    * `imgaug.augmenters.contrast`.\n    * `imgaug.augmenters.blur`.\n    * `imgaug.augmenters.blend`.\n    * `imgaug.augmenters.weather`.\n* Refactored all calls of `warnings.warn()` to use `imgaug.imgaug.warn()\n  instead. #401\n* [rarely breaking] Refactored most of the augmenters from functions to\n  classes. Previously, some augmenters were functions that returned an\n  instance of another augmenter (with adjusted hyperparameters) when being\n  called. Aside from a few corner cases, these have been switched to classes\n  inheriting from the augmenters that were previously returned. This should\n  make some outputs less confusing (e.g. `print(A())` does no longer lead to\n  class `B` being printed). All arguments stayed the same and this is not\n  expected to affect any user code negatively. The augmenters listed below are\n  affected by this change. #396\n    * `imgaug.augmenters.arithmetic.AdditiveGaussianNoise`\n    * `imgaug.augmenters.arithmetic.AdditiveLaplaceNoise`\n    * `imgaug.augmenters.arithmetic.AdditivePoissonNoise`\n    * `imgaug.augmenters.arithmetic.Dropout`\n    * `imgaug.augmenters.arithmetic.CoarseDropout`\n    * `imgaug.augmenters.arithmetic.ImpulseNoise`\n    * `imgaug.augmenters.arithmetic.SaltAndPepper`\n    * `imgaug.augmenters.arithmetic.CoarseSaltAndPepper`\n    * `imgaug.augmenters.arithmetic.Salt`\n    * `imgaug.augmenters.arithmetic.CoarseSalt`\n    * `imgaug.augmenters.arithmetic.Pepper`\n    * `imgaug.augmenters.arithmetic.CoarsePepper`\n    * `imgaug.augmenters.blend.SimplexNoiseAlpha`\n    * `imgaug.augmenters.blend.FrequencyNoiseAlpha`\n    * `imgaug.augmenters.blur.MotionBlur`\n    * `imgaug.augmenters.contrast.MultiplyHueAndSaturation`\n    * `imgaug.augmenters.contrast.MultiplyHue`\n    * `imgaug.augmenters.contrast.MultiplySaturation`\n    * `imgaug.augmenters.contrast.AddToHue`\n    * `imgaug.augmenters.contrast.AddToSaturation`\n    * `imgaug.augmenters.contrast.Grayscale`\n    * `imgaug.augmenters.contrast.GammaContrast`\n    * `imgaug.augmenters.contrast.SigmoidContrast`\n    * `imgaug.augmenters.contrast.LogContrast`\n    * `imgaug.augmenters.contrast.LinearContrast`\n    * `imgaug.augmenters.convolutional.Sharpen`\n    * `imgaug.augmenters.convolutional.Emboss`\n    * `imgaug.augmenters.convolutional.EdgeDetect`\n    * `imgaug.augmenters.convolutional.DirectedEdgeDetect`\n    * `imgaug.augmenters.meta.OneOf`\n    * `imgaug.augmenters.meta.AssertLambda`\n    * `imgaug.augmenters.meta.AssertShape`\n    * `imgaug.augmenters.size.Pad`\n    * `imgaug.augmenters.size.Crop`\n    * `imgaug.augmenters.weather.Clouds`\n    * `imgaug.augmenters.weather.Fog`\n    * `imgaug.augmenters.weather.Snowflakes`\n* Refactored `Affine` to improve code quality and minimize code\n  duplication. #407\n* Refactored `CropAndPad` to improve code quality and minimize code\n  duplication. #407\n* Refactored module `size` to decrease code duplication between different\n  augmenters. #407\n* Moved matrix generation logic of augmenters in module `convolutional`\n  to classes, one per augmenter (i.e. one per category of convolutional\n  matrix). This should avoid errors related to pickling of functions. #407\n* Refactored color augmenters to use `change_colorspace_()` and\n  `change_colorspace_batch_()`. #409\n* Refactored `Alpha` to decrease code duplication. #410\n* Refactored `AlphaElementwise` to decrease code duplication. #410\n* Refactored `Add` to use `imgaug.augmenters.arithmetic.add_scalar()`. #411\n* Refactored `AddElementwise` to use\n  `imgaug.augmenters.arithmetic.add_elementwise()`. #411\n* Refactored `ReplaceElementwise` to use\n  `imgaug.augmenters.arithmetic.replace_elementwise_()`. #411\n* Refactored `JpegCompression` to use\n  `imgaug.augmenters.arithmetic.compress_jpg()`. #411\n* Refactored `AddToHueAndSaturation` to decrease code duplication and improve\n  code quality. #319\n* Refactored `Affine` to improve code quality and decrease code\n  duplication. #413\n* Refactored `PiecewiseAffine` to improve code quality and decrease code\n  duplication. #413\n* Refactored `PerspectiveTransform` to improve code quality and decrease code\n  duplication. #413\n* Refactored `ElasticTransformation` to improve code quality and decrease code\n  duplication. #413\n* Refactored `Rot90` to improve code quality and decrease code\n  duplication. #413\n* Refactored `Augmenter.augment_images()`, `Augmenter.augment_heatmaps()`,\n  `Augmenter.augment_segmentation_maps()`, `Augmenter.augment_polygons()`,\n  `Augmenter.augment_line_strings()` and `Augmenter._augment_coord_augables()`\n  to improve code quality and remove redundancies. #413\n* Refactored `imgaug.imgaug.imresize_single_image()`. #413\n* Refactored `Sequential` to reduce code duplication. #413\n* Refactored `SomeOf` to improve code quality. #413\n* Refactored `Sometimes` to reduce code duplication. #413\n* Refactored `AssertShape` to reduce code duplication. #413\n* Refactored `ChannelShuffle` to improve code quality. #413\n* Refactored `dtypes.py` to improve code quality. #366\n* Refactored `dtypes.promote_array_dtypes_()` to use\n  `dtypes.change_dtypes_()`. #366\n* Refactored `dtypes.get_minimal_dtype()` to use\n  `dtypes.increase_itemsize_of_dtype()`. #366\n* Renamed `dtypes.restore_dtypes_()` to `dtypes.change_dtypes_()`. (Old name\n  still exists too and redirects to new name. Not yet marked as\n  deprecated.) #366\n\n\n## Other Changes\n\n* [rarely breaking] Changed colorspace transformations throughout the\n  library to fail if the input image does not have three channels. #409\n* Changed colorspace transformations throughout the library to also\n  support `YUV` colorspace. #409\n* [rarely breaking] Changed `AlphaElementwise` to verify for keypoint\n  and line string augmentation that the number of coordinates before/after \n  augmentation does not change. Previously this was allowed. This also\n  affects `SigmoidNoiseAlpha` and `FrequenceNoiseAlpha`. #410\n* [rarely breaking] Changed `AlphaElementwise` to use for keypoint,\n  line string and bounding box augmentation a pointwise approach, where\n  per coordinate a decision is made whether the new coordinate from the\n  first branch's (augmented) results or the second branch's (augmented)\n  results are used. The decision is based on the average alpha mask value\n  at the xy-location of the coordinate. For polygons, the old mode is\n  still used where either all coordinates from the first branch's results\n  or the second branch's results are used. This also affects \n  `SigmoidNoiseAlpha` and `FrequenceNoiseAlpha`. #410\n* Improved error messages of `dtypes.restore_dtypes_()`. #366 \n\n\n## Other Minor Changes\n\n* Increased `max_distance` thresholds for `almost_equals()`,\n  `exterior_almost_equals()` and `coords_almost_equals()` in `Polygon` and\n  `LineString` from `1e-6` to `1e-4`. This should fix false-negative problems\n  related to float inaccuracies.\n* Changed `_ConcavePolygonRecoverer` to not search for segment intersection\n  points in polygons with very large absolute coordinate values.\n  This prevents rare errors due to floating point inaccuracies. #338\n* Changed `_ConcavePolygonRecoverer` to raise warnings instead of throwing\n  exceptions when the underlying search for segment intersection points\n  crashes. #338\n* Added check to `dtypes.gate_dtypes()` verifying that arguments `allowed`\n  and `disallowed` have no intersection. #346\n* Changed `dtypes.get_value_range_of_dtype()` to return a float as the center\n  value of `uint` dtypes. #366\n* Changed `multicore.Pool` to produce a warning if it cannot find or call the\n  function `multiprocessing.cpu_count()` instead of silently failing.\n  (In both cases it falls back to a default value.) #367\n* Changed the default `pad_mode` of `avg_pool` from `constant` (`cval=128`)\n  to `reflect`. #369\n* Changed the default `pad_mode` of `max_pool` from `constant` (`cval=0`)\n  to `edge`. #369\n* Changed the default `pad_mode` of `min_pool` from `constant` (`cval=255`)\n  to `edge`. #369\n* Changed the default `pad_mode` of `median_pool` from `constant`\n  (`cval=128`) to `reflect`. #369\n* Changed `imgaug.imgaug.pad` to automatically clip the `cval` argument\n  to the value range of the array to be padded. #407\n* [rarely breaking] Changed `KeypointsOnImage.from_keypoints_image()` to\n  return `(x+0.5, y+0.5)` instead of `(x, y)` where `(x, y)` denotes the\n  coordinates of the pixel in which a maximum was found. This change matches\n  the standard that all pixels are given with subpixel accuracy and therefore\n  any whole pixel with a maximum should denote the coordinates of that\n  pixel's center. #413\n* Removed image-channel check for cv2-based warp in `Affine`. Images with any\n  channel number can now be warped using the cv2 backend (previously: only\n  `<=4`, others would be warped via skimage). #381\n* [rarely breaking] The `value` parameter in\n  `augmenters.color.AddToHueAndSaturation` is now interpreted by the\n  augmenter to return first the hue and then the saturation value to add,\n  instead of the other way round. (This isn't expected to affect\n  anybody.) #319\n* Added output `from_colorspace` to\n  `AddToHueAndSaturation.get_parameters()`. #409\n* Added dtype gating to `dtypes.clip_()`.\n* Changed all dtype-related functions to accept also dtypes given as\n  string names, numpy arrays, numpy scalars or dtype functions. #366\n* Changed `dtypes.restore_dtypes_()` so that if `images` is a list of length\n  `N` and `dtypes` is a list of length `M` and `N!=M`, the function now raises\n  an `AssertionError`. #366\n* Changed `dtypes.restore_dtypes_()` to ignore the argument `round` if the\n  input array does not have a float dtype. #366\n* Removed restrictions of `value` parameter in `AddElementwise`.\n  The value range is now no longer limited to `[-255, 255]` and floats\n  are now allowed. #411\n\n\n## New Scripts\n\n* Added a check script for `UniformColorQuantization` in\n  `checks/check_uniform_color_quantization.py`. #347\n* Added a check script for `Voronoi` in `checks/check_voronoi.py`. #348\n* Added a check script for flip performance measurments under\n  `checks/check_flip_performance.py`. #385\n\n\n## Other\n\n* Added the library to `conda-forge` so it can now be installed via\n  `conda install imgaug` (provided the conda-forge channel was added\n  before that). #320 #339\n* Changed dependency `opencv-python` to `opencv-python-headless`.\n  This should improve support for some system without GUIs.\n* Added dependency `pytest-subtests` for the library's unittests. #366\n* Improved the docstrings of most augmenters and added code examples. #302\n* Added error messages to `assert` statements throughout the library. #387\n* Removed the requirement to implement `_augment_keypoints()` and\n  `_augment_heatmaps()` in augmenters. The methods now default to doing\n  nothing. Also removed all such noop-implementations of these methods from\n  all augmenters. #380\n* Increased minimum version requirement for `scikit-image` to\n  `0.14.2`. #377, #399\n* Renamed `imgaug/external/poly_point_isect.py` to\n  `imgaug/external/poly_point_isect_py3.py.bak`.\n  The file is in the library only for completeness and contains python3 syntax.\n  `poly_point_isect_py2py3.py` is actually used.\n\n\n## Fixes\n \n* Fixed an issue with `Polygon.clip_out_of_image()`,\n  which would lead to exceptions if a polygon had overlap with an image,\n  but not a single one of its points was inside that image plane. \n* Fixed `multicore` methods falsely not accepting\n  `augmentables.batches.UnnormalizedBatch`.\n* `Rot90` now uses subpixel-based coordinate remapping.\n  I.e. any coordinate `(x, y)` will be mapped to `(H-y, x)` for a rotation by\n  90deg.\n  Previously, an integer-based remapping to `(H-y-1, x)` was used.\n  Coordinates are e.g. used by keypoints, bounding boxes or polygons.\n* `augmenters.arithmetic.Invert`\n    * [rarely breaking] If `min_value` and/or `max_value` arguments were\n      set, `uint64` is no longer a valid input array dtype for `Invert`.\n      This is due to a conversion to `float64` resulting in loss of resolution.\n    * Fixed `Invert` in rare cases restoring dtypes improperly.\n* Fixed `dtypes.gate_dtypes()` crashing if the input was one or more numpy\n  scalars instead of numpy arrays or dtypes.\n* Fixed `augmenters.geometric.PerspectiveTransform` producing invalid\n  polygons (more often with higher `scale` values). #338\n* Fixed errors caused by `external/poly_point_isect_py2py3.py` related to\n  floating point inaccuracies (changed an epsilon from `1e-10` to `1e-4`,\n  rounded some floats). #338\n* Fixed `Superpixels` breaking when a sampled `n_segments` was `<=0`.\n  `n_segments` is now treated as `1` in these cases.\n* Fixed `ReplaceElementwise` both allowing and disallowing dtype `int64`. #346\n* Fixed `BoundingBox.deepcopy()` creating only shallow copies of labels. #356\n* Fixed `dtypes.change_dtypes_()` #366\n    * Fixed argument `round` being ignored if input images were a list.\n    * Fixed failure if input images were a list and dtypes a single numpy\n      dtype function.\n* Fixed `dtypes.get_minimal_dtype()` failing if argument `arrays` contained\n  not *exactly* two items. #366\n* Fixed calls of `CloudLayer.get_parameters()` resulting in errors. #309\n* Fixed `SimplexNoiseAlpha` and `FrequencyNoiseAlpha` not handling\n  `sigmoid` argument correctly. #343\n* Fixed `SnowflakesLayer` crashing for grayscale images. #345\n* Fixed `Affine` heatmap augmentation crashing for arrays with more than\n  four channels and `order!=0`. #381\n* Fixed an outdated error message in `Affine`. #381\n* Fixed `Polygon.clip_out_of_image()` crashing if the intersection between\n  polygon and image plane was an edge or point. #382\n* Fixed `Polygon.clip_out_of_image()` potentially failing for polygons\n  containing two or fewer points. #382\n* Fixed `Polygon.is_out_of_image()` returning wrong values if the image plane\n  was fully contained inside the polygon with no intersection between the\n  image plane and the polygon edge. #382\n* Fixed  `Fliplr` and `Flipud` using for coordinate-based inputs and image-like\n  inputs slightly different conditions for when to actually apply\n  augmentations. #385\n* Fixed `Convolve` using an overly restrictive check when validating inputs\n  for `matrix` w.r.t. whether they are callables. The check should now also\n  support class methods (and possibly various other callables). #407\n* Fixed `CropAndPad`, `Pad` and `PadToFixedSize` still clipping `cval` samples\n  to the `uint8`. They now clip to the input array's dtype's value range. #407\n* Fixed `WithColorspace` not propagating polygons to child augmenters. #409\n* Fixed `WithHueAndSaturation` not propagating segmentation maps and polygons\n  to child augmenters. #409\n* Fixed `AlphaElementwise` to blend coordinates (for keypoints, polygons,\n  line strings) on a point-by-point basis following the image's average\n  alpha value in the sampled alpha mask of the point's coordinate.\n  Previously, the average over the whole mask was used and then either all\n  points of the first branch or all of the second branch were used as the\n  augmentation output. This also affects `SimplexNoiseAlpha` and\n  `FrequencyNoiseAlpha`. #410\n* Fixed many augmenters and helper functions producing errors if the height,\n  width and/or channels of input arrays were exactly `0` or the channels\n  were `>512`. #433\n* Fixed `Rot90` not supporting `imgaug.ALL`. #434\n* Fixed `PiecewiseAffine` possibly generating samples for non-image data\n  when using `absolute_scale=True` that were not well aligned with the\n  corresponding images. #437\n"
  },
  {
    "path": "changelogs/0.3.0/v0.3.0.uncleaned.md",
    "content": "# 0.3.0, Uncleaned Raw Log Of Changes\n\n* Added argument `output_buffer_size` to `multicore.Pool.imap_batches()`\n  and `multicore.Pool.imap_batches_unordered()` to control the maximum number\n  of batches in the background augmentation pipeline (allows to limit\n  maximum RAM demands).\n* Increased `max_distance` thresholds for `almost_equals()`,\n  `exterior_almost_equals()` and `coords_almost_equals()` in `Polygon` and\n  `LineString` from `1e-6` to `1e-4`.\n  This should fix false-negative problems related to float inaccuracies.\n* Added module `imgaug.augmenters.edges`.\n* Added interface `augmenters.edges.BinaryImageColorizerIf`, which\n  contains the interface for classes used to convert binary images to RGB\n  images.\n* Added `augmenters.edges.RandomColorsBinaryImageColorizer`, which\n  converts binary images to RGB images by sampling uniformly RGB colors for\n  `True` and `False` values.\n* Added `augmenters.edges.Canny`, which applies canny edge detection with alpha\n  blending and random coloring to images.\n* Renamed `imgaug/external/poly_point_isect.py` to\n  `imgaug/external/poly_point_isect_py3.py.bak`.\n  The file is in the library only for completeness and contains python3 syntax.\n  `poly_point_isect_py2py3.py` is actually used.\n* Added dtype gating to `dtypes.clip_()`.\n* Added module `augmenters.pooling`. #317\n    * Added `augmenters.pooling._AbstractPoolingBase`. #317\n    * Added `augmenters.pooling.AveragePooling`. #317\n    * Added `augmenters.pooling.MaxPooling`. #317\n    * Added `augmenters.pooling.MinPooling`. #317\n    * Added `augmenters.pooling.MedianPooling`. #317\n* `augmenters.color.AddToHueAndSaturation`\n    * [rarely breaking] Refactored `AddToHueAndSaturation` to clean it up.\n      Re-running old code with the same seeds will now produce different\n      images. #319\n    * [rarely breaking] The `value` parameter is now interpreted by the\n      augmenter to return first the hue and then the saturation value to add,\n      instead of the other way round.\n      (This shouldn't affect anybody.) #319\n    * [rarely breaking] Added `value_hue` and `value_saturation` arguments,\n      which allow to set individual parameters for hue and saturation\n      instead of having to use one parameter for both (they may not be set\n      if `value` is already set).\n      This changes the order of arguments of the augmenter and code that relied\n      on that order will now break.\n      This also changes the output of\n      `AddToHueAndSaturation.get_parameters()`. #319\n* Added `augmenters.color.AddToHue`, a shortcut for\n  `AddToHueAndSaturation(value_hue=...)`. #319\n* Added `augmenters.color.AddToSaturation`, a shortcut for\n  `AddToHueAndSaturation(value_saturation=...)`. #319\n* Added `augmenters.color.WithHueAndSaturation`. #319\n* Added `augmenters.color.MultiplyHueAndSaturation`. #319\n* Added `augmenters.color.MultiplyHue`. #319\n* Added `augmenters.color.MultiplySaturation`. #319\n* Refactored `augmenters/weather.py` (general code and docstring cleanup). #336\n* [rarely breaking] Refactored `augmenters/convolutional.py`\n  (general code and docstring cleanup).\n  This involved changing the random state handling.\n  Old seeds might now produce different result images for convolutional\n  augmenters (`Convolve`, `Sharpen`, `Emboss`, `EdgeDetect`,\n  `DirectedEdgeDetect`). #335\n* [rarely breaking] Added argument `polygon_recoverer` to\n  `augmenters.geometric.PerspectiveTransform`. This changes the order of\n  arguments of the augmenter and code that relied on that order will now\n  break. #338\n* Changed `_ConcavePolygonRecoverer` to not search for segment intersection\n  points in polygons with very large absolute coordinate values.\n  This prevents rare errors due to floating point inaccuracies. #338\n* Changed `_ConcavePolygonRecoverer` to raise warnings instead of throwing\n  exceptions when the underlying search for segment intersection points\n  crashes. #338\n* Added the library to `conda-forge` so it can now be installed via\n  `conda install imgaug` (provided the conda-forge channel was added\n  before that). #320 #339\n* Changed dependency `opencv-python` to `opencv-python-headless`.\n  This should improve support for some system without GUIs.\n* Refactored code in `augmenters.segmentation` (general code and docstring cleanup). #334\n* Refactored code in `augmenters.arithmetic` (general code and docstring cleanup). #328\n* Added check to `dtypes.gate_dtypes()` verifying that arguments `allowed`\n  and `disallowed` have no intersection. #346\n* Added dependency `pytest-subtests` for the library's unittests. #366\n* Added `imgaug.is_np_scalar()`, analogous to `imgaug.is_np_array()`. #366\n* Reworked and refactored code in `dtypes.py` (general code cleanup). #366\n  * Added `dtypes.normalize_dtypes()`.\n  * Added `dtypes.normaliz_dtypes()`.\n  * Refactored `dtypes.promote_array_dtypes_()` to use\n    `dtypes.change_dtypes_()`.\n  * Reworked dtype normalization. All functions in the module that required\n    dtype inputs accept now dtypes, dtype functions, dtype names, ndarrays\n    or numpy scalars.\n  * [rarely breaking] `dtypes.restore_dtypes_()`\n    * Improved error messages.\n    * Changed so that if `images` is a list of length `N` and `dtypes` is a\n      list of length `M` and `N!=M`, the function now raises an\n      `AssertionError`.\n    * The argument `round` is now ignored if the input array does not have\n      a float dtype.\n  * Renamed `dtypes.restore_dtypes_()` to `dtypes.change_dtypes_()` (old name\n    still exists too and redirects to new name).\n  * Added `dtypes.change_dtype_()`, analogous to `dtypes.change_dtypes_()`.\n  * Added `dtypes.increase_itemsize_of_dtype()`.\n      * Refactored `dtypes.get_minimal_dtype()` to use that new function.\n  * [rarely breaking] Removed `dtypes.get_minimal_dtype_for_values()`. The\n    function was not used anywhere in the library.\n  * [rarely breaking] Removed `dtypes.get_minimal_dtype_by_value_range()`. The\n    function was not used anywhere in the library.\n  * Changed `dtypes.get_value_range_of_dtype()` to return a float as the center\n    value of `uint` dtypes.\n  * [rarely breaking] Removed argument `affects` from\n    `dtypes.promote_array_dtypes_()` as it was unnecessary and not used anywhere\n    in the library. #366\n* Added `imgaug.warn()` function. #367\n* Changed `multicore.Pool` to produce a warning if it cannot find or call the\n  function `multiprocessing.cpu_count()` instead of silently failing.\n  (In both cases it falls back to a default value.) #367\n* Refactored code in `multicore` (general code and docstring cleanup). #367\n  * Improved error messages in `multicore`.\n* Added `imgaug.min_pool()`. #369\n  * Refactored `augmenters.pooling.MinPooling` to use `imgaug.min_pool()`.\n* Added `imgaug.median_pool()`. #369\n  * Refactored `augmenters.pooling.MedianPooling` to use\n    `imgaug.median_pool()`.\n* Added `imgaug.compute_paddings_to_reach_multiples_of()`. #369\n* Added `imgaug.pad_to_multiples_of()`. #369\n* Refactored `imgaug.pool()` to use `imgaug.pad()` for image padding. #369\n* [rarely breaking] Added a `pad_mode` argument to `imgaug.pool()`,\n  `imgaug.avg_pool()`, `imgaug.max_pool()`, `imgaug.min_pool()` and\n  `imgaug.median_pool()`. This breaks code relying on the order of the\n  functions arguments. #369\n  * Changed the default `pad_mode` of `avg_pool` from `constant` (`cval=128`)\n    to `reflect`.\n  * Changed the default `pad_mode` of `max_pool` from `constant` (`cval=0`)\n    to `edge`.\n  * Changed the default `pad_mode` of `min_pool` from `constant` (`cval=255`)\n    to `edge`.\n  * Changed the default `pad_mode` of `median_pool` from `constant`\n    (`cval=128`) to `reflect`.\n* Renamed argument `cval` to `pad_cval` in `imgaug.pool()`,\n  `imgaug.avg_pool()` and `imgaug.max_pool()`. The old name `cval` is now\n  deprecated. #369\n* Added `augmenters.color._AbstractColorQuantization`. #347\n* Added `augmenters.color.KMeansColorQuantization` and corresponding\n  `augmenters.color.quantize_colors_kmeans()`. Both deal with quantizing\n  similar colors using k-Means clustering. #347\n    * Added a check script for `KMeansColorQuantization` under\n      `checks/check_kmeans_color_quantization.py`. #347\n* Added `augmenters.color.UniformColorQuantization` and corresponding\n  `augmenters.color.quantize_colors_uniform()`. Both deal with quantizing\n  similar colors using k-Means clustering. #347\n    * Added a check script for `UniformColorQuantization` under\n      `checks/check_uniform_color_quantization.py`. #347\n* Added `imgaug.imgaug.normalize_random_state()`. #348\n* Added `imgaug.augmenters.segmentation._ensure_image_max_size()`. #348\n* Added `imgaug.augmenters.segmentation.PointsSamplerIf`. An interface for\n  classes used for sampling (usually random) coordinate arrays on images.\n* Added `imgaug.augmenters.segmentation._verify_sample_points_images()`. #348\n* Added `imgaug.augmenters.segmentation.RegularGridPointsSampler`. A class\n  used to generate regular grids of `rows x columns` points on images. #348\n* Added `imgaug.augmenters.segmentation.RelativeRegularGridPointsSampler`.\n  Similar to `RegularGridPointsSampler`, but number of rows/columns is set\n  as fractions of image sizes, leading to more rows/columns for larger\n  images. #348\n* Added `imgaug.augmenters.segmentation.DropoutPointsSampler`. A class\n  used to randomly drop `p` percent of all coordinates sampled by another\n  another points sampler. #348\n* Added `imgaug.augmenters.segmentation.UniformPointsSampler`. A class used\n  to sample `N` points on each image with y-/x-coordinates uniformly sampled\n  using the corresponding image height/width. #348 \n* Added `imgaug.augmenters.segmentation.SubsamplingPointsSampler`. A class\n  that ensures that another points sampler does not produce more than\n  `N` points by subsampling a random subset of the produced points if `N`\n  is exceeded. #348\n* Added `imgaug.augmenters.segmentation.segment_voronoi()`. A function that\n  converts an image into a voronoi image, i.e. averages the colors within\n  voronoi cells placed on the image. #348\n    * Also added in the same module the functions\n      `_match_pixels_with_voronoi_cells()`, `_generate_pixel_coords()`,\n      `_compute_avg_segment_colors()`, `_render_segments()`.\n* Added `imgaug.augmenters.segmentation.Voronoi`. An augmenter that converts\n  an image to a voronoi image.  #348\n    * Added a check script for `Voronoi` in `checks/check_voronoi.py`.\n* Added `imgaug.augmenters.segmentation.UniformVoronoi`, a shortcut for\n  `Voronoi(UniformPointsSamper)`. #348\n* Added `imgaug.augmenters.segmentation.RegularGridVoronoi`, a shortcut for\n  `Voronoi(DropoutPointsSampler(RegularGridPointsSampler))`. #348\n* Added `imgaug.augmenters.segmentation.RelativeRegularGridVoronoi`, a shortcut\n  for `Voronoi(DropoutPointsSampler(RelativeRegularGridPointsSampler))`. #348\n* Add to `Resize` the ability to resize the shorter and longer sides of\n  images (instead of only height/width). #349\n* Improved the docstrings of most augmenters and added code examples. #302\n* Changes to support numpy 1.17 #302\n    * [rarely breaking] Deactivated support for `int64` in\n      `imgaug.dtypes.clip_()`. This is due to numpy 1.17 turning `int64` to\n      `float64` in `numpy.clip()` (possible that this happened in some way\n      before 1.17 too).\n    * [rarely breaking] Changed `imgaug.dtypes.clip()` to never clip `int32`\n      in-place, as `numpy.clip()` turns it into `float64` since 1.17 (possible\n      that this happend in some way before 1.17 too).\n    * [rarely breaking] Deactivated support for `int64` in\n      `ReplaceElementwise`. See `clip` issue above.\n    * [rarely breaking] Changed `parameters.DiscreteUniform` to always return\n      arrays of dtype `int32`. Previously it would automatically return\n      `int64`.\n    * [rarely breaking] Changed `parameters.Deterministic` to always return\n      `int32` for integers and always `float32` for floats.\n    * [rarely breaking] Changed `parameters.Choice` to limit integer\n      dtypes to `int32` or lower, uints to `uint32` or lower and floats\n      to `float32` or lower. \n    * [rarely breaking] Changed `parameters.Binomial` and `parameters.Poisson`\n      to always return `int32`.\n    * [rarely breaking] Changed `parameters.Normal`,\n      `parameters.TruncatedNormal`, `parameters.Laplace`,\n      `parameters.ChiSquare`, `parameters.Weibull`, `parameters.Uniform` and\n      `parameters.Beta` to always return `float32`.\n    * [rarely breaking] Changed `augmenters.arithmetic.Add`,\n      `augmenters.arithmetic.AddElementwise`, `augmenters.arithmetic.Multiply`\n      and `augmenters.arithmetic.MultiplyElementwise` to no longer internally\n      increase itemsize of dtypes by a factor of 2 for\n      dtypes `uint16`, `int8` and `uint16`. For `Multiply*` this also\n      covers `float16` and `float32`. This protects against crashes due to\n      clipping `int64` or `uint64` data. In rare cases this can lead to\n      overflows if `image + random samples` or `image * random samples`\n      exceeds the value range of `int32` or `uint32`. This change may affect\n      various other augmenters that are wrappers around the mentioned ones,\n      e.g. `AdditiveGaussianNoise`.\n    * [rarely breaking] Decreased support of dtypes `uint16`, `int8`,\n      `int16`, `float16`, `float32` and `bool` in `augmenters.arithmetic.Add`,\n      `AddElementwise`, `Multiply` and `MultiplyElementwise` from \"yes\" to\n      \"limited\".\n    * [rarely breaking] Decreased support of dtype `int64` in\n      `augmenters.arithmetic.ReplaceElementwise` from \"yes\" to \"no\". This also\n      affects all `*Noise` augmenters (e.g. `AdditiveGaussianNoise`,\n      `ImpulseNoise`), all `Dropout` augmenters, all `Salt` augmenters and\n      all `Pepper` augmenters.\n    * [rarely breaking] Changed `augmenters.contrast.adjust_contrast_log`\n      and thereby `LogContrast` to no longer support dtypes `uint32`, `uint64`,\n      `int32` and `int64`.\n* Replaced all calls of `imgaug.imgaug.do_assert` by ordinary `assert`\n  statements. This is a bit less secure, but should overall improve\n  performance. #387\n* Added error messages to `assert` statements throughout the library. #387\n* Improved code style and documentation of (#389, #402):\n    * `imgaug.augmentables.bbs`.\n    * `imgaug.augmentables.heatmaps`.\n    * `imgaug.augmentables.kps`.\n    * `imgaug.augmentables.lines`.\n    * `imgaug.augmentables.normalization`.\n    * `imgaug.augmentables.polys`.\n    * `imgaug.augmentables.segmaps`.\n    * `imgaug.augmentables.utils`.\n    * `imgaug.imgaug`.\n    * `imgaug.parameters`.\n    * `imgaug.augmenters.weather`.\n    * `imgaug.augmenters.size`.\n    * `imgaug.augmenters.segmentation`.\n    * `imgaug.augmenters.meta`.\n    * `imgaug.augmenters.geometric`.\n    * `imgaug.augmenters.flip`.\n    * `imgaug.augmenters.contrast`.\n    * `imgaug.augmenters.blur`.\n    * `imgaug.augmenters.blend`.\n    * `imgaug.augmenters.weather`.\n* Removed image-channel check for cv2-based warp in `Affine`. Images with any\n  channel number can now be warped using the cv2 backend (previously: only\n  `<=4`, others would be warped via skimage). #381\n* Improved performance of `augmenters.flip.Fliplr`. #385\n* Improved performance of `augmenters.flip.Flipud`. #385\n* Added function `augmenters.flip.fliplr()`. #385\n* Added function `augmenters.flip.flipud()`. #385\n* Removed the requirement to implement `_augment_keypoints()` and\n  `_augment_heatmaps()` in augmenters. The methods now default to doing\n  nothing. Also removed all such noop-implementations of these methods from\n  all augmenters. #380\n* Increased minimum version requirement for `scikit-image` to\n  `0.14.2`. #377, #399\n* Marked the following functions as deprecated (#398):\n  * `imgaug.augmenters.meta.clip_augmented_image_`\n  * `imgaug.augmenters.meta.clip_augmented_image`\n  * `imgaug.augmenters.meta.clip_augmented_images_`\n  * `imgaug.augmenters.meta.clip_augmented_images`\n* Refactored all calls of `warnings.warn()` to use `imgaug.imgaug.warn()\n  instead. #401\n* [rarely breaking] Refactored most of the augmenters from functions to\n  classes. Previously, some augmenters were functions that returned an\n  instance of another augmenter (with adjusted hyperparameters) when being\n  called. Aside from a few corner cases, these have been switched to classes\n  inheriting from the augmenters that were previously returned. This should\n  make some outputs less confusing (ass `print(A())` does not lead to class\n  `B` being printed). All arguments stayed the same and this is not expected\n  to affect any user code negatively. The augmenters listed below are\n  affected by this change. #396\n    * `imgaug.augmenters.arithmetic.AdditiveGaussianNoise`\n    * `imgaug.augmenters.arithmetic.AdditiveLaplaceNoise`\n    * `imgaug.augmenters.arithmetic.AdditivePoissonNoise`\n    * `imgaug.augmenters.arithmetic.Dropout`\n    * `imgaug.augmenters.arithmetic.CoarseDropout`\n    * `imgaug.augmenters.arithmetic.ImpulseNoise`\n    * `imgaug.augmenters.arithmetic.SaltAndPepper`\n    * `imgaug.augmenters.arithmetic.CoarseSaltAndPepper`\n    * `imgaug.augmenters.arithmetic.Salt`\n    * `imgaug.augmenters.arithmetic.CoarseSalt`\n    * `imgaug.augmenters.arithmetic.Pepper`\n    * `imgaug.augmenters.arithmetic.CoarsePepper`\n    * `imgaug.augmenters.blend.SimplexNoiseAlpha`\n    * `imgaug.augmenters.blend.FrequencyNoiseAlpha`\n    * `imgaug.augmenters.blur.MotionBlur`\n    * `imgaug.augmenters.contrast.MultiplyHueAndSaturation`\n    * `imgaug.augmenters.contrast.MultiplyHue`\n    * `imgaug.augmenters.contrast.MultiplySaturation`\n    * `imgaug.augmenters.contrast.AddToHue`\n    * `imgaug.augmenters.contrast.AddToSaturation`\n    * `imgaug.augmenters.contrast.Grayscale`\n    * `imgaug.augmenters.contrast.GammaContrast`\n    * `imgaug.augmenters.contrast.SigmoidContrast`\n    * `imgaug.augmenters.contrast.LogContrast`\n    * `imgaug.augmenters.contrast.LinearContrast`\n    * `imgaug.augmenters.convolutional.Sharpen`\n    * `imgaug.augmenters.convolutional.Emboss`\n    * `imgaug.augmenters.convolutional.EdgeDetect`\n    * `imgaug.augmenters.convolutional.DirectedEdgeDetect`\n    * `imgaug.augmenters.meta.OneOf`\n    * `imgaug.augmenters.meta.AssertLambda`\n    * `imgaug.augmenters.meta.AssertShape`\n    * `imgaug.augmenters.size.Pad`\n    * `imgaug.augmenters.size.Crop`\n    * `imgaug.augmenters.weather.Clouds`\n    * `imgaug.augmenters.weather.Fog`\n    * `imgaug.augmenters.weather.Snowflakes`\n* Marked `imgaug.augmenters.arithmetic.ContrastNormalization` as deprecated.\n  Use `imgaug.augmenters.contrast.LinearContrast` instead. #396\n* Renamed argument `X` of `imgaug.augmentables.kps.compute_geometric_median()`\n  to `points`. The old argument is still accepted, but now deprecated. #402\n* Refactored `Affine` to improve code quality and minimize code\n  duplication. #407\n  * [rarely breaking] Removed `Affine.VALID_DTYPES_CV2_ORDER_0`.\n  * [rarely breaking] Removed `Affine.VALID_DTYPES_CV2_ORDER_NOT_0`.\n  * [rarely breaking] Removed `Affine.order_map_skimage_cv2`.\n  * [rarely breaking] Removed `Affine.mode_map_skimage_cv2`.\n* Refactored `CropAndPad` to improve code quality and minimize code\n  duplication. #407\n* Refactored module `size` to decrease code duplication between different\n  augmenters. #407\n* Changed `imgaug.imgaug.pad` to automatically clip the `cval` argument\n  to the value range of the array to be padded. #407\n* Moved matrix generation logic of augmenters in module `convolutional`\n  to classes, one per augmenter (i.e. one per category of convolutional\n  matrix). This should avoid errors related to pickling of functions. #407\n* Refactored `imgaug.augmenters.color` (#409):\n    * Added to `imgaug.augmenters.color` the constants `CSPACE_RGB`, \n      `CSPACE_BGR`, `CSPACE_GRAY`, `CSPACE_CIE`, `CSPACE_YCrCb`, `CSPACE_HSV`,\n      `CSPACE_HLS`, `CSPACE_Lab`, `CSPACE_Luv`, `CSPACE_YUV`, `CSPACE_ALL`.\n    * Added `imgaug.augmenters.color.change_colorspace_()`.\n    * Added `imgaug.augmenters.color.change_colorspace_batch_()`.\n    * Refactored color augmenters to use `change_colorspace_()` and\n      `change_colorspace_batch_()`. \n    * [rarely breaking] Removed attributes `colorspace_changer` and\n      `colorspace_changer_inv` from `AddToHueAndSaturation`.\n    * Added attribute `from_colorspace` to `AddToHueAndSaturation`. This also\n      affects `AddToHue` and `AddToSaturation`.\n    * Added output `from_colorspace` to\n      `AddToHueAndSaturation.get_parameters()`. This also affects `AddToHue`\n      and `AddToSaturation`.\n    * [rarely breaking] Changed colorspace transformations throughout the\n      library to fail if the input image does not have three channels.\n    * Changed colorspace transformations throughout the library to also\n      support `YUV` colorspace.\n* Added function `imgaug.augmentables.utils.copy_augmentables`. #410\n* Refactored `Alpha` to decrease code duplication. #410\n* Refactored `AlphaElementwise` to decrease code duplication. #410\n    * [rarely breaking] Changed `AlphaElementwise` to verify for keypoint\n      and line string augmentation that the number of coordinates before/after \n      augmentation does not change. Previously this was allowed. This also\n      affects `SigmoidNoiseAlpha` and `FrequenceNoiseAlpha`.\n    * [rarely breaking] Changed `AlphaElementwise` to use for keypoint,\n      line string and bounding box augmentation a pointwise approach, where\n      per coordinate a decision is made whether the new coordinate from the\n      first branch's (augmented) results or the second branch's (augmented)\n      results are used. The decision is based on the average alpha mask value\n      at the xy-location of the coordinate. For polygons, the old mode is\n      still used where either all coordinates from the first branch's results\n      or the second branch's results are used. This also affects \n      `SigmoidNoiseAlpha` and `FrequenceNoiseAlpha`.\n* Added function `imgaug.augmenters.arithmetic.add_scalar()`. #411\n    * Refactored `Add` to use that function.\n* Added function `imgaug.augmenters.arithmetic.add_elementwise()`. #411\n    * Refactored `AddElementwise` to use that function.\n    * Removed restrictions of `value` parameter in `AddElementwise`.\n      The value range is now no longer limited to `[-255, 255]` and floats\n      are now allowed.\n* Added function `imgaug.augmenters.arithmetic.replace_elementwise_()`. #411\n    * Refactored `ReplaceElementwise` to use that function.\n    * [rarely breaking] Removed class constant `ALLOW_DTYPES_CUSTOM_MINMAX`\n      from `Invert`.\n    * [rarely breaking] Removed attribute `dtype_kind_to_invert_func` from\n      `Invert`.\n* Added function `imgaug.augmenters.arithmetic.compress_jpg()`. #411\n    * Refactored `JpegCompression` to use that function.\n    * [rarely breaking] Removed attribute `maximum_quality` from\n      `JpegCompression`.\n    * [rarely breaking] Removed attribute `minimum_quality` from\n      `JpegCompression`.\n* Refactored `Affine` to improve code quality and decrease code\n  duplication. #413\n* Refactored `PiecewiseAffine` to improve code quality and decrease code\n  duplication. #413\n* Refactored `PerspectiveTransform` to improve code quality and decrease code\n  duplication. #413\n* Refactored `ElasticTransformation` to improve code quality and decrease code\n  duplication. #413\n  * [rarely breaking] Renamed `ElasticTransformation.generate_shift_maps()` to\n    `ElasticTransformation._generate_shift_maps()`.\n  * [rarely breaking] Renamed `ElasticTransformation.map_coordinates()` to\n    `ElasticTransformation._map_coordinates()`.\n* Refactored `Rot90` to improve code quality and decrease code\n  duplication. #413\n* Added `imgaug.testutils.ArgCopyingMagicMock`. #413\n* Refactored `Augmenter.augment_images()`, `Augmenter.augment_heatmaps()`,\n  `Augmenter.augment_segmentation_maps()`, `Augmenter.augment_polygons()`,\n  `Augmenter.augment_line_strings()` and `Augmenter._augment_coord_augables()`\n  to improve code quality and remove redundancies. #413\n* Refactored `imgaug.imgaug.imresize_single_image()`. #413\n* Added module `imgaug.validation`. #413\n  * Added `imgaug.validation.convert_iterable_to_string_of_types()`.\n  * Added `imgaug.validation.is_iterable_of()`.\n  * Added `imgaug.validation.assert_is_iterable_of()`.\n* Refactored `Sequential` to reduce code duplication. #413\n* Refactored `SomeOf` to improve code quality. #413\n* Refactored `Sometimes` to reduce code duplication. #413\n* Refactored `AssertShape` to reduce code duplication. #413\n* Refactored `ChannelShuffle` to improve code quality. #413\n* [rarely breaking] Changed `KeypointsOnImage.from_keypoints_image()` to\n  return `(x+0.5, y+0.5)` instead of `(x, y)` where `(x, y)` denotes the\n  coordinates of the pixel in which a maximum was found. This change matches\n  the standard that all pixels are given with subpixel accuracy and therefore\n  any whole pixel with a maximum should denote the coordinates of that\n  pixel's center. #413\n* Changed default `output_buffer_size` in `Augmenter.augment_batches()` from\n  \"unlimited\" to `10*C`, where `C` is the number of logical CPU cores. #417\n\n\n## Improved Segmentation Map Augmentation #302\n\nAugmentation of Segmentation Maps is now faster and more memory efficient.\nThis required some breaking changes to `SegmentationMapOnImage`.\nTo adapt to the new version, the following steps should be sufficient for most\nusers:\n\n* Rename all calls of `SegmentationMapOnImage` to `SegmentationMapsOnImage`\n  (Map -> Maps).\n* Rename all calls of `SegmentationMapsOnImage.get_arr_int()` to\n  `SegmentationMapsOnImage.get_arr()`.\n* Remove the argument `nb_classes` from all calls of `SegmentationMapsOnImage`.\n* Remove the arguments `background_id` and `background_threshold` from all\n  calls as these are no longer supported.\n* Ensure that the input array to `SegmentationMapsOnImage` is always an\n  int-like (int, uint or bool).\n  Float arrays are no longer accepted.\n* Adapt all calls `SegmentationMapsOnImage.draw()` and\n  `SegmentationMapsOnImage.draw_on_image()`, as both of these now return a\n  list of drawn images instead of a single array. (For a segmentation map\n  array of shape `(H,W,C)` they return `C` drawn images. In most cases `C=1`,\n  so simply call `draw()[0]` or `draw_on_image()[0]`.)\n* Ensure that if `SegmentationMapsOnImage.arr` is accessed anywhere, the\n  respective code can handle the new `int32` `(H,W,#maps)` array form.\n  Previously it was `float32` and the channel-axis had the same size as the\n  max class id (+1) that could appear in the map.\n\nChanges:\n\n- Changes to class `SegmentationMapOnImage`:\n    - Renamed `SegmentationMapOnImage` to plural `SegmentationMapsOnImage`\n      and deprecated the old name.\n      This was changed due to the input array now being allowed to contain\n      several channels, with each such channel containing one full segmentation\n      map.\n    - Changed `SegmentationMapsOnImage.__init__` to produce a deprecation\n      warning for float arrays as `arr` argument.\n    - **[breaking]** Changed `SegmentationMapsOnImage.__init__` to no longer\n      accept `uint32` and larger itemsizes as `arr` argument, only `uint16`\n      and below is accepted. For `int` the allowed maximum is `int32`.\n    - Changed `SegmentationMapsOnImage.__init__` to always accept `(H,W,C)`\n      `arr` arguments.\n    - **[breaking]** Changed  `SegmentationMapsOnImage.arr` to always be\n      `int32` `(H,W,#maps)` (previously: `float32` `(H,W,#nb_classes)`).\n    - Deprecated `nb_classes` argument in `SegmentationMapsOnImage.__init__`.\n      The argument is now ignored.\n    - Added `SegmentationMapsOnImage.get_arr()`, which always returns a\n      segmentation map array with similar dtype and number of dimensions as\n      was originally input when creating a class instance.\n    - Deprecated `SegmentationMapsOnImage.get_arr_int()`.\n      The method is now an alias for `get_arr()`.\n    - `SegmentationMapsOnImage.draw()`:\n        - **[breaking]** Removed argument `return_foreground_mask` and\n          corresponding optional output. To generate a foreground mask\n          for the `c`-th segmentation map on a given image (usually `c=0`),\n          use `segmentation_map.arr[:, :, c] != 0`, assuming that `0` is\n          the integer index of your background class. \n        - **[breaking]** Changed output of drawn image to be a list of arrays\n          instead of a single array (one per `C` in input array `(H,W,C)`).\n        - Refactored to be a wrapper around\n          `SegmentationMapsOnImage.draw_on_image()`.\n        - The `size` argument may now be any of: A single `None` (keep shape),\n          a single integer (use as height and width), a single float (relative\n          change to shape) or a tuple of these values. (\"shape\" here denotes\n          the value of the `.shape` attribute.)\n    - `SegmentationMapsOnImage.draw_on_image()`:\n        - **[breaking]** The argument `background_threshold` is now deprecated\n          and ignored. Providing it will lead to a deprecation warning.\n        - **[breaking]** Changed output of drawn image to be a list of arrays\n          instead of a single array (one per `C` in input array `(H,W,C)`).\n    - Changed `SegmentationMapsOnImage.resize()` to use nearest neighbour\n      interpolation by default.\n    - **[rarely breaking]** Changed `SegmentationMapsOnImage.copy()` to create\n      a shallow copy instead of being an alias for `deepcopy()`.\n    - Added optional arguments `arr` and `shape` to\n      `SegmentationMapsOnImage.copy()`.\n    - Added optional arguments `arr` and `shape` to\n      `SegmentationMapsOnImage.deepcopy()`.\n    - Refactored `SegmentationMapsOnImage.pad()`,\n      `SegmentationMapsOnImage.pad_to_aspect_ratio()` and\n      `SegmentationMapsOnImage.resize()` to generate new object instances via\n      `SegmentationMapsOnImage.deepcopy()`.\n    - **[rarely breaking]** Renamed `SegmentationMapsOnImage.input_was` to\n      `SegmentationMapsOnImage._input_was`.\n    - **[rarely breaking]** Changed `SegmentationMapsOnImage._input_was` to\n      always save `(input array dtype, input array ndim)` instead of mixtures\n      of strings/ints that varied by dtype kind.\n    - **[rarely breaking]** Restrict `shape` argument in\n      `SegmentationMapsOnImage.__init__` to tuples instead of accepting all\n      iterables.\n    - **[breaking]** Removed `SegmentationMapsOnImage.to_heatmaps()` as the\n      new segmentation map class is too different to sustain the old heatmap\n      conversion methods.\n    - **[breaking]** Removed `SegmentationMapsOnImage.from_heatmaps()` as the\n      new segmentation map class is too different to sustain the old heatmap\n      conversion methods.\n- Changes to class `Augmenter`:\n    - **[breaking]** Automatic segmentation map normalization from arrays or\n      lists of arrays now expects a single `(N,H,W,C)` array (before:\n      `(N,H,W)`) or a list of `(H,W,C)` arrays (before: `(H,W)`).\n      This affects valid segmentation map inputs for `Augmenter.augment()`\n      and its alias `Augmenter.__call__()`,\n      `imgaug.augmentables.batches.UnnormalizedBatch()` and\n      `imgaug.augmentables.normalization.normalize_segmentation_maps()`.\n    - Added `Augmenter._augment_segmentation_maps()`.\n    - Changed `Augmenter.augment_segmentation_maps()` to no longer be a\n    wrapper around `Augmenter.augment_heatmaps()` and instead call\n    `Augmenter._augment_segmentation_maps()`.\n- Added special segmentation map handling to all augmenters that modified\n  segmentation maps\n  (`Sequential`, `SomeOf`, `Sometimes`, `WithChannels`,\n   `Lambda`, `AssertLambda`, `AssertShape`,\n   `Alpha`, `AlphaElementwise`, `WithColorspace`, `Fliplr`, `Flipud`, `Affine`,\n   `AffineCv2`, `PiecewiseAffine`, `PerspectiveTransform`, `ElasticTransformation`,\n   `Rot90`, `Resize`, `CropAndPad`, `PadToFixedSize`, `CropToFixedSize`,\n   `KeepSizeByResize`).\n   - **[rarely breaking]** This changes the order of arguments in\n     `Lambda.__init__()`, `AssertLambda.__init__()`, `AssertShape.__init__()`\n     and hence breaks if one relied on that order.\n\n## New RNG handling #375\n\n* Adapted library to automatically use the new `numpy.random` classes of\n  numpy 1.17 -- if they are available. If they are not available (i.e. numpy\n  version is <=1.16), the library automatically falls back to the old\n  interface (i.e. `numpy.random.RandomState`).\n* Added module `imgaug.random`.\n  * Added class `imgaug.random.RNG`. This is now the preferred way to represent\n    RNG states (previously: `numpy.random.RandomState`). Instantiate it\n    via e.g. `RNG(1052912236)`, where `1052912236` is a seed.\n  * Added `imgaug.random.supports_new_rng_style()`.\n  * Added `imgaug.random.get_global_rng()`.\n  * Added `imgaug.random.seed()`.\n  * Added `imgaug.random.normalize_generator()`.\n  * Added `imgaug.random.normalize_generator_()`.\n  * Added `imgaug.random.convert_seed_to_generator()`.\n  * Added `imgaug.random.convert_seed_sequence_to_generator()`.\n  * Added `imgaug.random.create_pseudo_random_generator_()`.\n  * Added `imgaug.random.create_fully_random_generator()`.\n  * Added `imgaug.random.generate_seed_()`.\n  * Added `imgaug.random.generate_seeds_()`.\n  * Added `imgaug.random.copy_generator()`.\n  * Added `imgaug.random.copy_generator_unless_global_generator()`.\n  * Added `imgaug.random.reset_generator_cache_()`.\n  * Added `imgaug.random.derive_generator_()`.\n  * Added `imgaug.random.derive_generators_()`.\n  * Added `imgaug.random.get_generator_state()`.\n  * Added `imgaug.random.set_generator_state_()`.\n  * Added `imgaug.random.is_generator_equal_to()`.\n  * Added `imgaug.random.advance_generator_()`.\n  * Added `imgaug.random.polyfill_integers()`.\n  * Added `imgaug.random.polyfill_random()`.\n* Refactored all arguments related to random state handling to also accept\n  `imgaug.random.RNG`, as well as the new numpy random classes. This\n  particularly affects `imgaug.augmenters.meta.Augmenter` and\n  `imgaug.parameters.StochasticParameter` (argument `random_state` for both).\n* Marked old RNG related functions in `imgaug.imgaug` as deprecated.\n  They will now produce warnings and redirect towards corresponding functions\n  in `imgaug.random`. This does not yet affect `imgaug.imgaug.seed()`.\n  It does affect the functions listed below.\n  * `imgaug.imgaug.normalize_random_state()`.\n  * `imgaug.imgaug.current_random_state()`.\n  * `imgaug.imgaug.new_random_state()`.\n  * `imgaug.imgaug.dummy_random_state()`.\n  * `imgaug.imgaug.copy_random_state()`.\n  * `imgaug.imgaug.derive_random_state()`.\n  * `imgaug.imgaug.derive_random_states()`.\n  * `imgaug.imgaug.forward_random_state()`.\n* [rarely breaking] Removed `imgaug.imgaug.CURRENT_RANDOM_STATE`.\n  Use `imgaug.random.get_global_rng()` instead.\n* [rarely breaking] Removed `imgaug.imgaug.SEED_MIN_VALUE`.\n  Use `imgaug.random.SEED_MIN_VALUE` instead or sample seeds via\n  `imgaug.random.generate_seeds_()`.\n* [rarely breaking] Removed `imgaug.imgaug.SEED_MAX_VALUE`.\n  Use `imgaug.random.SEED_MAX_VALUE` instead or sample seeds via\n  `imgaug.random.generate_seeds_()`.\n* Optimized RNG handling throughout all augmenters to minimize the number of\n  RNG copies. RNGs are now re-used as often as possible. This improves\n  performance, but has the disadvantage that adding images to a batch will now\n  often affect the samples of the other images in the same batch. E.g.\n  previously for a batch of images `A,B,C` and seed `1`, the samples of `A,B,C`\n  would remain unchanged if the batch was changed to `A,B,C,D` (provided the\n  seed stayed the same). Now, if `D` is added the samples of `A,B,C` may\n  change.\n* [breaking] The above listed changes will lead to different values being\n  sampled for the same seeds (compared to past versions of the library).\n* [breaking] The seed for `imgaug`'s global random number generator is now\n  sampled from numpy's default random number generator. That means, that every\n  run of a program using `imgaug` will by default use a different seed and\n  hence result in different samples. Previously, a fixed seed was used,\n  resulting in the same samples for each run (unless the seed was manually\n  changed to a fixed one). It also means that seeding numpy will automatically\n  also seed imgaug (not guarantueed that this behaviour will be kept in\n  future releases). The change from fixed to random seed was done, because the\n  old (fixed) behaviour didn't match the common practice (and especially not\n  numpy's standard behaviour) and hence led to confusion. #408\n\n## Fixes\n \n* Fixed an issue with `Polygon.clip_out_of_image()`,\n  which would lead to exceptions if a polygon had overlap with an image,\n  but not a single one of its points was inside that image plane. \n* Fixed `multicore` methods falsely not accepting\n  `augmentables.batches.UnnormalizedBatch`.\n* `Rot90` now uses subpixel-based coordinate remapping.\n  I.e. any coordinate `(x, y)` will be mapped to `(H-y, x)` for a rotation by\n  90deg.\n  Previously, an integer-based remapping to `(H-y-1, x)` was used.\n  Coordinates are e.g. used by keypoints, bounding boxes or polygons.\n* `augmenters.arithmetic.Invert`\n    * [rarely breaking] If `min_value` and/or `max_value` arguments were\n      set, `uint64` is no longer a valid input array dtype for `Invert`.\n      This is due to a conversion to `float64` resulting in loss of resolution.\n    * Fixed `Invert` in rare cases restoring dtypes improperly.\n* Fixed `dtypes.gate_dtypes()` crashing if the input was one or more numpy\n  scalars instead of numpy arrays or dtypes.\n* Fixed `augmenters.geometric.PerspectiveTransform` producing invalid\n  polygons (more often with higher `scale` values). #338\n* Fixed errors caused by `external/poly_point_isect_py2py3.py` related to\n  floating point inaccuracies (changed an epsilon from `1e-10` to `1e-4`,\n  rounded some floats). #338\n* Fixed `Superpixels` breaking when a sampled `n_segments` was `<=0`.\n  `n_segments` is now treated as `1` in these cases.\n* Fixed `ReplaceElementwise` both allowing and disallowing dtype `int64`. #346\n* Fixed `BoundingBox.deepcopy()` creating only shallow copies of labels. #356\n* Fixed `dtypes.change_dtypes_()` #366\n    * Fixed argument `round` being ignored if input images were a list.\n    * Fixed failure if input images were a list and dtypes a single numpy\n      dtype function.\n* Fixed `dtypes.get_minimal_dtype()` failing if argument `arrays` contained\n  not *exactly* two items. #366\n* Fixed calls of `CloudLayer.get_parameters()` resulting in errors. #309\n* Fixed `SimplexNoiseAlpha` and `FrequencyNoiseAlpha` not handling\n  `sigmoid` argument correctly. #343\n* Fixed `SnowflakesLayer` crashing for grayscale images. #345\n* Fixed `Affine` heatmap augmentation crashing for arrays with more than\n  four channels and `order!=0`. #381\n* Fixed an outdated error message in `Affine`. #381\n* Fixed `Polygon.clip_out_of_image()` crashing if the intersection between\n  polygon and image plane was an edge or point. #382\n* Fixed `Polygon.clip_out_of_image()` potentially failing for polygons\n  containing two or fewer points. #382\n* Fixed `Polygon.is_out_of_image()` returning wrong values if the image plane\n  was fully contained inside the polygon with no intersection between the\n  image plane and the polygon edge. #382\n* Fixed  `Fliplr` and `Flipud` using for coordinate-based inputs and image-like\n  inputs slightly different conditions for when to actually apply\n  augmentations. #385\n* Fixed `Convolve` using an overly restrictive check when validating inputs\n  for `matrix` w.r.t. whether they are callables. The check should now also\n  support class methods (and possibly various other callables). #407\n* Fixed `CropAndPad`, `Pad` and `PadToFixedSize` still clipping `cval` samples\n  to the `uint8`. They now clip to the input array's dtype's value range. #407\n* Fixed `WithColorspace` not propagating polygons to child augmenters. #409\n* Fixed `WithHueAndSaturation` not propagating segmentation maps and polygons\n  to child augmenters. #409\n* Fixed `AlphaElementwise` to blend coordinates (for keypoints, polygons,\n  line strings) on a point-by-point basis following the image's average\n  alpha value in the sampled alpha mask of the point's coordinate.\n  Previously, the average over the whole mask was used and then either all\n  points of the first branch or all of the second branch were used as the\n  augmentation output. This also affects `SimplexNoiseAlpha` and\n  `FrequencyNoiseAlpha`. #410\n* Fixed many augmenters and helper functions producing errors if the height,\n  width and/or channels of input arrays were exactly `0` or the channels\n  were `>512`. #433\n* Fixed `Rot90` not supporting `imgaug.ALL`. #434\n* Fixed `PiecewiseAffine` possibly generating samples for non-image data\n  when using `absolute_scale=True` that were not well aligned with the\n  corresponding images. #437\n"
  },
  {
    "path": "changelogs/0.4.0/20191003_reworked_aug_methods.md",
    "content": "# Reworked Augmentation Methods #451 #566\n\n* Added method `to_normalized_batch()` to `imgaug.augmentables.batches.Batch`\n  to have the same interface in `Batch` and `UnnormalizedBatch`.\n* Added method `get_augmentable()` to\n  `imgaug.augmentables.batches.Batch` and\n  `imgaug.augmentables.batches.UnnormalizedBatch`.\n* Added method `get_augmentable_names()` to\n  `imgaug.augmentables.batches.Batch` and\n  `imgaug.augmentables.batches.UnnormalizedBatch`.\n* Added method `to_batch_in_augmentation()` to\n  `imgaug.augmentables.batches.Batch`.\n* Added method `fill_from_batch_in_augmentation_()` to\n  `imgaug.augmentables.batches.Batch`.\n* Added method `fill_from_augmented_normalized_batch_()` to\n  `imgaug.augmentables.batches.UnnormalizedBatch`.\n* Added class `imgaug.augmentables.batches._BatchInAugmentation`.\n* Added method `_augment_batch_()` in `imgaug.augmenters.meta.Augmenter`.\n  This method is now called from `augment_batch_()`.\n* Changed `augment_images()` in `imgaug.augmenters.meta.Augmenter` to be\n  a thin wrapper around `augment_batch_()`.\n* Changed `augment_heatmaps()` in `imgaug.augmenters.meta.Augmenter` to be\n  a thin wrapper around `augment_batch_()`.\n* Changed `augment_segmentation_maps()` in `imgaug.augmenters.meta.Augmenter`\n  to be a thin wrapper around `augment_batch_()`.\n* Changed `augment_keypoints()` in `imgaug.augmenters.meta.Augmenter` to be\n  a thin wrapper around `augment_batch_()`.\n* Changed `augment_bounding_boxes()` in `imgaug.augmenters.meta.Augmenter` to be\n  a thin wrapper around `augment_batch_()`.\n* Changed `augment_polygons()` in `imgaug.augmenters.meta.Augmenter` to be\n  a thin wrapper around `augment_batch_()`.\n* Changed `augment_line_strings()` in `imgaug.augmenters.meta.Augmenter` to be\n  a thin wrapper around `augment_batch_()`.\n* Changed `augment_image()`, `augment_images()`, `augment_heatmaps()`,\n  `augment_segmentation_maps()`, `augment_keypoints()`,\n  `augment_bounding_boxes()`, `augment_polygons()` and `augment_line_strings()`\n  to return `None` inputs without change. Previously they resulted in an\n  exception. This is more consistent with the behaviour in the other\n  `augment_*` methods.\n* Added method `imgaug.augmenters.meta.Augmenter.augment_batch_()`,\n  similar to `augment_batch()`, but explicitly works in-place and has a\n  `parent` parameter.\n* Deprecated `imgaug.augmenters.meta.Augmenter.augment_batch()`.\n  Use `.augment_batch_()` instead.\n* Changed `augment_images()` to no longer be abstract. It defaults\n  to not changing the input images.\n* Refactored `Sequential` to use single `_augment_batch_()` method.\n* Refactored `SomeOf` to use single `_augment_batch_()` method.\n* Refactored `Sometimes` to use single `_augment_batch_()` method.\n* Refactored `AveragePooling`, `MaxPooling`, `MinPooling`, `MedianPooling`\n  to use single `_augment_batch_()` method.\n* Refactored `ElasticTransformation` to use single `_augment_batch_()` method.\n* Refactored `Alpha` to use single `_augment_batch_()` method.\n* Refactored `AlphaElementwise` to use single `_augment_batch_()` method.\n* Refactored `WithColorspace` to use single `_augment_batch_()` method.\n* Refactored `WithHueAndSaturation` to use single `_augment_batch_()` method.\n* Refactored `Fliplr` to use single `_augment_batch_()` method.\n* Refactored `Flipud` to use single `_augment_batch_()` method.\n* Refactored `Affine` to use single `_augment_batch_()` method.\n* Refactored `Rot90` to use single `_augment_batch_()` method.\n* Refactored `Resize` to use single `_augment_batch_()` method.\n* Refactored `CropAndPad` to use single `_augment_batch_()` method.\n* Refactored `PadToFixedSize` to use single `_augment_batch_()` method.\n* Refactored `CropToFixedSize` to use single `_augment_batch_()` method.\n* Refactored `KeepSizeByResize` to use single `_augment_batch_()` method.\n* Refactored `PiecewiseAffine` to use single `_augment_batch_()` method.\n* Refactored `PerspectiveTransform` to use single `_augment_batch_()` method.\n* Refactored `WithChannels` to use single `_augment_batch_()` method.\n* Refactored `Add` to use single `_augment_batch_()` method.\n* Refactored `AddElementwise` to use single `_augment_batch_()` method.\n* Refactored `Multiply` to use single `_augment_batch_()` method.\n* Refactored `MultiplyElementwise` to use single `_augment_batch_()` method.\n* Refactored `ReplaceElementwise` to use single `_augment_batch_()` method.\n* Refactored `Invert` to use single `_augment_batch_()` method.\n* Refactored `JpegCompression` to use single `_augment_batch_()` method.\n* Refactored `GaussianBlur` to use single `_augment_batch_()` method.\n* Refactored `AverageBlur` to use single `_augment_batch_()` method.\n* Refactored `MedianBlur` to use single `_augment_batch_()` method.\n* Refactored `BilateralBlur` to use single `_augment_batch_()` method.\n* Refactored `AddToHueAndSaturation` to use single `_augment_batch_()` method.\n* Refactored `ChangeColorspace` to use single `_augment_batch_()` method.\n* Refactored `_AbstractColorQuantization` to use single `_augment_batch_()`\n  method.\n* Refactored `_ContrastFuncWrapper` to use single `_augment_batch_()` method.\n* Refactored `AllChannelsCLAHE` to use single `_augment_batch_()` method.\n* Refactored `CLAHE` to use single `_augment_batch_()` method.\n* Refactored `AllChannelsHistogramEqualization` to use single\n  `_augment_batch_()` method.\n* Refactored `HistogramEqualization` to use single `_augment_batch_()` method.\n* Refactored `Convolve` to use single `_augment_batch_()` method.\n* Refactored `Canny` to use single `_augment_batch_()` method.\n* Refactored `ChannelShuffle` to use single `_augment_batch_()` method.\n* Refactored `Superpixels` to use single `_augment_batch_()` method.\n* Refactored `Voronoi` to use single `_augment_batch_()` method.\n* Refactored `FastSnowyLandscape` to use single `_augment_batch_()` method.\n* Refactored `CloudLayer` to use single `_augment_batch_()` method.\n* Refactored `SnowflakesLayer` to use single `_augment_batch_()` method.\n* Added validation of input arguments to `KeypointsOnImage.from_xy_array()`.\n* Improved validation of input arguments to\n  `BoundingBoxesOnImage.from_xyxy_array()`.\n* Added method `BoundingBoxesOnImage.to_keypoints_on_image()`.\n* Added method `PolygonsOnImage.to_keypoints_on_image()`.\n* Added method `LineStringsOnImage.to_keypoints_on_image()`.\n* Added method `KeypointsOnImage.to_keypoints_on_image()`.\n* Added method `BoundingBoxesOnImage.invert_to_keypoints_on_image_()`.\n* Added method `PolygonsOnImage.invert_to_keypoints_on_image_()`.\n* Added method `LineStringsOnImage.invert_to_keypoints_on_image_()`.\n* Added method `KeypointsOnImage.invert_to_keypoints_on_image_()`.\n* Added method `imgaug.augmentables.polys.recover_psois_()`.\n* Added method `imgaug.augmentables.utils.convert_cbaois_to_kpsois()`.\n* Added method `imgaug.augmentables.utils.invert_convert_cbaois_to_kpsois_()`.\n* Added method `imgaug.augmentables.utils.deepcopy_fast()`.\n* Added method `imgaug.augmentables.kps.BoundingBoxesOnImage.to_xy_array()`.\n* Added method `imgaug.augmentables.kps.PolygonsOnImage.to_xy_array()`.\n* Added method `imgaug.augmentables.kps.LineStringsOnImage.to_xy_array()`.\n* Added method `imgaug.augmentables.kps.KeypointsOnImage.fill_from_xy_array_()`.\n* Added method `imgaug.augmentables.kps.BoundingBoxesOnImage.fill_from_xy_array_()`.\n* Added method `imgaug.augmentables.kps.PolygonsOnImage.fill_from_xy_array_()`.\n* Added method `imgaug.augmentables.kps.LineStringsOnImage.fill_from_xy_array_()`.\n* Added method `imgaug.augmentables.bbs.BoundingBoxesOnImage.fill_from_xyxy_array_()`.\n* Added method `imgaug.augmentables.bbs.BoundingBox.from_point_soup()`.\n* Added method `imgaug.augmentables.bbs.BoundingBoxesOnImages.from_point_soups()`.\n* Changed `imgaug.augmentables.BoundingBoxesOnImage.from_xyxy_array()` to also\n  accept `(N, 2, 2)` arrays instead of only `(N, 4)`.\n* Added context `imgaug.testutils.TemporaryDirectory`.\n"
  },
  {
    "path": "changelogs/0.4.0/20191016_pooling_affects_maps.md",
    "content": "# Pooling Augmenters now affects Maps #457\n\nPooling augmenters were previously implemented so that they did not pool\nthe arrays of maps (i.e. heatmap arrays, segmentation map arrays). Only\nthe image shape saved within `HeatmapsOnImage.shape` and\n`SegmentationMapsOnImage.shape` were updated. That was done because the library\ncan handle map arrays that are larger than the corresponding images and hence\nno pooling was necessary for the augmentation to work correctly. This was now\nchanged and pooling augmenters will also pool map arrays\n(if `keep_size=False`). The motiviation for this change is that the old\nbehaviour was unintuitive and inconsistent with other augmenters (e.g. `Crop`). \n"
  },
  {
    "path": "changelogs/0.4.0/20191026_reworked_quantization.md",
    "content": "# Reworked Quantization #467\n\n* Renamed `imgaug.augmenters.color.quantize_colors_uniform(image, n_colors)`\n  to `imgaug.augmenters.color.quantize_uniform(arr, nb_bins)`. The old name\n  is now deprecated.\n* Renamed `imgaug.augmenters.color.quantize_colors_kmeans(image, n_colors)`\n  to `imgaug.augmenters.color.quantize_kmeans(arr, nb_clusters)`. The old name\n  is now deprecated.\n* Improved performance of `quantize_uniform()` by roughly 10x (small images\n  around 64x64) to 100x (large images around 1024x1024). This also affects\n  `UniformColorQuantization`.\n* Improved performance of `UniformColorQuantization` by using more in-place\n  functions.\n* Added argument `to_bin_centers=True` to `quantize_uniform()`, controling\n  whether each bin `(a, b)` should be quantized to `a + (b-a)/2` or `a`.\n* Added function `imgaug.augmenters.color.quantize_uniform_()`, the in-place\n  version of `quantize_uniform()`.\n* Added function `imgaug.augmenters.color.quantize_uniform_to_n_bits()`.\n* Added function `imgaug.augmenters.color.quantize_uniform_to_n_bits_()`.\n* Added function `imgaug.augmenters.color.posterize()`, an alias of\n  `quantize_uniform_to_n_bits()` that produces the same outputs as\n  `PIL.ImageOps.posterize()`.\n* Added augmenter `UniformColorQuantizationToNBits`.\n* Added augmenter `Posterize` (alias of `UniformColorQuantizationToNBits`).\n* Fixed `quantize_uniform()` producing wrong outputs for non-contiguous arrays.\n"
  },
  {
    "path": "changelogs/0.4.0/20191027_improve_invert.md",
    "content": "# Improve Invert #469\n\n* Improved performance of `imgaug.augmenters.arithmetic.invert()` and\n  `imgaug.augmenters.arithmetic.Invert` for `uint8` images.\n* Added function `imgaug.augmenters.arithmetic.invert_()`, an in-place version\n  of `imgaug.augmenters.arithmetic.invert()`.\n* Added parameters `threshold` and `invert_above_threshold` to\n  `imgaug.augmenters.arithmetic.invert()`\n* Added parameters `threshold` and `invert_above_threshold` to\n  `imgaug.augmenters.arithmetic.Invert`.\n* Added function `imgaug.augmenters.arithmetic.solarize()`, a wrapper around\n  `solarize_()`.\n* Added function `imgaug.augmenters.arithmetic.solarize_()`, a wrapper around\n  `invert_()`.\n* Added augmenter `imgaug.augmenters.Solarize`, a wrapper around `Invert`.\n"
  },
  {
    "path": "changelogs/0.4.0/20191111_pickleable.md",
    "content": "# All Augmenters Pickle-able #493 #575\n\nEnsured that all augmenters can be pickled.\n\n* Added function `imgaug.testutils.runtest_pickleable_uint8_img()`.\n* Fixed `imgaug.augmenters.blur.MotionBlur` not being pickle-able.\n* Fixed `imgaug.augmenters.meta.AssertLambda` not being pickle-able.\n* Fixed `imgaug.augmenters.meta.AssertShape` not being pickle-able.\n* Fixed `imgaug.augmenters.color.MultiplyHueAndSaturation` not supporting\n  all standard RNG datatypes for `random_state`.\n"
  },
  {
    "path": "changelogs/0.4.0/20191113_iterable_augmentables.md",
    "content": "# Simplified Access to Coordinates and Items in Augmentables #495 #541\n\n* Added module `imgaug.augmentables.base`.\n* Added interface `imgaug.augmentables.base.IAugmentable`, implemented by\n  `HeatmapsOnImage`, `SegmentationMapsOnImage`, `KeypointsOnImage`,\n  `BoundingBoxesOnImage`, `PolygonsOnImage` and `LineStringsOnImage`.\n* Added ability to iterate over coordinate-based `*OnImage` instances\n  (keypoints, bounding boxes, polygons, line strings), e.g.\n  `bbsoi = BoundingBoxesOnImage(bbs, shape=...); for bb in bbsoi: ...`.\n  would iterate now over `bbs`.\n* Added implementations of `__len__` methods to coordinate-based `*OnImage`\n  instances, e.g.\n  `bbsoi = BoundingBoxesOnImage(bbs, shape=...); print(len(bbsoi))`\n  would now print the number of bounding boxes in `bbsoi`.\n* Added ability to iterate over coordinates of `BoundingBox` (top-left,\n  bottom-right), `Polygon` and `LineString` via `for xy in obj: ...`.\n* Added ability to access coordinates of `BoundingBox`, `Polygon` and\n  `LineString` using indices or slices, e.g. `line_string[1:]` to get an\n  array of all coordinates except the first one.\n* Added property `Keypoint.xy`.\n* Added property `Keypoint.xy_int`.\n"
  },
  {
    "path": "changelogs/0.4.0/20191610_crop_and_pad.md",
    "content": "# Changes to Crop and Pad augmenters #459\n\nThe following functions were moved. Their old names are now deprecated.\n* Moved `imgaug.imgaug.pad` to `imgaug.augmenters.size.pad`\n* Moved `imgaug.imgaug.pad_to_aspect_ratio` to\n  `imgaug.augmenters.size.pad_to_aspect_ratio`.\n* Moved `imgaug.imgaug.pad_to_multiples_of` to\n  `imgaug.augmenters.size.pad_to_multiples_of`.\n* Moved `imgaug.imgaug.compute_paddings_for_aspect_ratio` to\n  `imgaug.augmenters.size.compute_paddings_to_reach_aspect_ratio`.\n* Moved `imgaug.imgaug.compute_paddings_to_reach_multiples_of`\n  to `imgaug.augmenters.size.compute_paddings_to_reach_multiples_of`.\n\n\nThe following augmenters were added:\n* Added augmenter `CenterCropToFixedSize`.\n* Added augmenter `CenterPadToFixedSize`.\n* Added augmenter `CropToMultiplesOf`.\n* Added augmenter `CenterCropToMultiplesOf`.\n* Added augmenter `PadToMultiplesOf`.\n* Added augmenter `CenterPadToMultiplesOf`.\n* Added augmenter `CropToPowersOf`.\n* Added augmenter `CenterCropToPowersOf`.\n* Added augmenter `PadToPowersOf`.\n* Added augmenter `CenterPadToPowersOf`.\n* Added augmenter `CropToAspectRatio`.\n* Added augmenter `CenterCropToAspectRatio`.\n* Added augmenter `PadToAspectRatio`.\n* Added augmenter `CenterPadToAspectRatio`.\n* Added augmenter `PadToSquare`.\n* Added augmenter `CenterPadToSquare`.\n* Added augmenter `CropToSquare`.\n* Added augmenter `CenterCropToSquare`.\n\nAll `Center<name>` augmenters are wrappers around `<name>` with parameter\n`position=\"center\"`.\n\n\nAdded functions:\n* Added function\n  `imgaug.augmenters.size.compute_croppings_to_reach_aspect_ratio()`.\n* Added function\n  `imgaug.augmenters.size.compute_croppings_to_reach_multiples_of()`.\n* Added function\n  `imgaug.augmenters.size.compute_croppings_to_reach_powers_of()`.\n* Added function\n  `imgaug.augmenters.size.compute_paddings_to_reach_powers_of()`.\n\n\nOther changes:\n* Extended augmenter `CropToFixedSize` to support `height` and/or `width`\n  parameters to be `None`, in which case the respective axis is not changed.\n* Extended augmenter `PadToFixedSize` to support `height` and/or `width`\n  parameters to be `None`, in which case the respective axis is not changed.\n* [rarely breaking] Changed `CropToFixedSize.get_parameters()` to also\n  return the `height` and `width` values.\n* [rarely breaking] Changed `PadToFixedSize.get_parameters()` to also\n  return the `height` and `width` values.\n* [rarely breaking] Changed the order of parameters returned by\n  `PadToFixedSize.get_parameters()` to match the order in\n  `PadToFixedSize.__init__()`\n* Changed `PadToFixedSize` to prefer padding the right side over the left side\n  and the bottom side over the top side. E.g. if using a center pad and\n  `3` columns have to be padded, it will pad `1` on the left and `2` on the\n  right. Previously it was the other way round. This was changed to establish\n  more consistency with the various other pad and crop methods.\n* Changed the projection of pad/crop values between images and non-images\n  to make the behaviour slightly more accurate in fringe cases.\n* Improved behaviour of function\n  `imgaug.augmenters.size.compute_paddings_for_aspect_ratio()` for zero-sized\n  axes.\n* Changed function `imgaug.augmenters.size.compute_paddings_for_aspect_ratio()`\n  to also support shape tuples instead of only ndarrays.\n* Changed function\n  `imgaug.augmenters.size.compute_paddings_to_reach_multiples_of()`\n  to also support shape tuples instead of only ndarrays.\n\n\nFixes:\n* Fixed a formatting error in an error message of\n  `compute_paddings_to_reach_multiples_of()`.\n"
  },
  {
    "path": "changelogs/0.4.0/20191610_perspective_transform.md",
    "content": "# Changes to PerspectiveTransform #452 #456\n\n* [rarely breaking] PerspectiveTransform has now a `fit_output` parameter,\n  similar to `Affine`. This change may break code that relied on the order of\n  arguments to `__init__`.\n* The sampling code of `PerspectiveTransform` was reworked and should now\n  be faster.\n"
  },
  {
    "path": "changelogs/0.4.0/20200107_improved_blending.md",
    "content": "# More Choices for Image Blending #462 #556\n\nThe available augmenters for alpha-blending of images were\nsignificantly extended. There are now new blending\naugmenters available to alpha-blend acoording to:\n* Some randomly chosen colors. (`BlendAlphaSomeColors`)\n* Linear gradients. (`BlendAlphaHorizontalLinearGradient`,\n  `BlendAlphaVerticalLinearGradient`)\n* Regular grids and checkerboard patterns. (`BlendAlphaRegularGrid`,\n  `BlendAlphaCheckerboard`)\n* Only at locations that overlap with specific segmentation class\n  IDs (or the inverse of that). (`BlendAlphaSegMapClassIds`)\n* Only within bounding boxes with specific labels (or the inverse\n  of that). (`BlendAlphaBoundingBoxes`)\n\nThis allows to e.g. randomly remove some colors while leaving\nother colors unchanged (`BlendAlphaSomeColors(Grayscale(1.0))`),\nto change the color of some objects\n(`BlendAlphaSegMapClassIds(AddToHue((-256, 256)))`), to add\ncloud-patterns only to the top of images\n(`BlendAlphaVerticalLinearGradient(Clouds())`) or to apply\naugmenters in some coarse rectangular areas (e.g.\n`BlendAlphaRegularGrid(Multiply(0.0))` to achieve a similar\neffect to `CoarseDropout` or\n`BlendAlphaRegularGrid(AveragePooling(8))` to pool in equally\ncoarse image sub-regions).\n\nOther mask-based alpha blending techniques can be achieved by\nsubclassing `IBatchwiseMaskGenerator` and providing an\ninstance of such a class to `BlendAlphaMask`.\n\nThis patch also changes the naming of the blending augmenters\nas follows:\n* `Alpha` -> `BlendAlpha`\n* `AlphaElementwise` -> `BlendAlphaElementwise`\n* `SimplexNoiseAlpha` -> `BlendAlphaSimplexNoise`\n* `FrequencyNoiseAlpha` -> `BlendAlphaFrequencyNoise`\nThe old names are now deprecated.\nFurthermore, the parameters `first` and `second`, which were\nused by all blending augmenters, have now the names `foreground`\nand `background`.\n\nList of changes:\n* Added `imgaug.augmenters.blend.BlendAlphaMask`, which uses\n  a mask generator instance to generate per batch alpha masks and\n  then alpha-blends using these masks.\n* Added `imgaug.augmenters.blend.BlendAlphaSomeColors`.\n* Added `imgaug.augmenters.blend.BlendAlphaHorizontalLinearGradient`.\n* Added `imgaug.augmenters.blend.BlendAlphaVerticalLinearGradient`.\n* Added `imgaug.augmenters.blend.BlendAlphaRegularGrid`.\n* Added `imgaug.augmenters.blend.BlendAlphaCheckerboard`.\n* Added `imgaug.augmenters.blend.BlendAlphaSegMapClassIds`.\n* Added `imgaug.augmenters.blend.BlendAlphaBoundingBoxes`.\n* Added `imgaug.augmenters.blend.IBatchwiseMaskGenerator`,\n  an interface for classes generating masks on a batch-by-batch\n  basis.\n* Added `imgaug.augmenters.blend.StochasticParameterMaskGen`,\n  a helper to generate masks from `StochasticParameter` instances.\n* Added `imgaug.augmenters.blend.SomeColorsMaskGen`, a generator\n  that produces masks marking randomly chosen colors in images.\n* Added `imgaug.augmenters.blend.HorizontalLinearGradientMaskGen`,\n  a linear gradient mask generator.\n* Added `imgaug.augmenters.blend.VerticalLinearGradientMaskGen`,\n  a linear gradient mask generator.\n* Added `imgaug.augmenters.blend.RegularGridMaskGen`,\n  a checkerboard-like mask generator where every grid cell has\n  a random alpha value.\n* Added `imgaug.augmenters.blend.CheckerboardMaskGen`,\n  a checkerboard-like mask generator where every grid cell has\n  the opposite alpha value of its 4-neighbours.\n* Added `imgaug.augmenters.blend.SegMapClassIdsMaskGen`, a\n  segmentation map-based mask generator.\n* Added `imgaug.augmenters.blend.BoundingBoxesMaskGen`, a bounding\n  box-based mask generator.\n* Added `imgaug.augmenters.blend.InvertMaskGen`, an mask generator\n  that inverts masks produces by child generators.\n* Changed `imgaug.parameters.SimplexNoise` and\n  `imgaug.parameters.FrequencyNoise` to also accept `(H, W, C)`\n  sampling shapes, instead of only `(H, W)`.\n* Refactored `AlphaElementwise` to be a wrapper around\n  `BlendAlphaMask`.\n* Renamed `Alpha` to `BlendAlpha`.\n  `Alpha` is now deprecated.\n* Renamed `AlphaElementwise` to `BlendAlphaElementwise`.\n  `AlphaElementwise` is now deprecated.\n* Renamed `SimplexNoiseAlpha` to `BlendAlphaSimplexNoise`.\n  `SimplexNoiseAlpha` is now deprecated.\n* Renamed `FrequencyNoiseAlpha` to `BlendAlphaFrequencyNoise`.\n  `FrequencyNoiseAlpha` is now deprecated.\n* Renamed arguments `first` and `second` to `foreground` and `background`\n  in `BlendAlpha`, `BlendAlphaElementwise`, `BlendAlphaSimplexNoise` and\n  `BlendAlphaFrequencyNoise`.\n* Changed `imgaug.parameters.handle_categorical_string_param()` to allow\n  parameter `valid_values` to be `None`.\n* Fixed a wrong error message in\n  `imgaug.augmenters.color.change_colorspace_()`.\n"
  },
  {
    "path": "changelogs/0.4.0/20200126_python38.md",
    "content": "# Support for Python 3.8 #600\n\nThe library is now tested in python 3.8 and compatible with that\nversion. The latest version of `Shapely` is required for that,\nwhich can right now be installed via `pip install --pre Shapely`.\n(Skipping the `--pre` currently leads to an older shapely version,\nwhich causes an error during installation in python 3.8.)\n"
  },
  {
    "path": "changelogs/0.4.0/added/20190927_unwrapped_bb_aug.md",
    "content": "# Unwrapped Bounding Box Augmentation #446 #599\n\n* Added property `coords` to `BoundingBox`. The property returns an `(N,2)`\n  numpy array containing the coordinates of the top-left and bottom-right\n  bounding box corners.\n* Added method `BoundingBox.coords_almost_equals(other)`.\n* Added method `BoundingBox.almost_equals(other)`.\n* Changed method `Polygon.almost_equals(other)` to no longer verify the\n  datatype. It is assumed now that the input is a Polygon.\n* Added property `items` to `KeypointsOnImage`, `BoundingBoxesOnImage`,\n  `PolygonsOnImage`, `LineStringsOnImage`. The property returns the\n  keypoints/BBs/polygons/LineStrings contained by that instance.\n* Added method `Polygon.coords_almost_equals(other)`. Alias for\n  `Polygon.exterior_almost_equals(other)`.\n* Added property `Polygon.coords`. Alias for `Polygon.exterior`.\n* Added property `Keypoint.coords`.\n* Added method `Keypoint.coords_almost_equals(other)`.\n* Added method `Keypoint.almost_equals(other)`.\n* Added method `imgaug.testutils.assert_cbaois_equal()`.\n* Added internal `_augment_bounding_boxes()` methods to various augmenters.\n  This allows to individually control how bounding boxes are supposed to\n  be augmented. Previously, the bounding box augmentation was a wrapper around\n  keypoint augmentation that did not allow such control.\n* [breaking] Added parameter `parents` to `Augmenter.augment_bounding_boxes()`.\n  This breaks if `hooks` was used as a *positional* argument in connection with\n  that method.\n* [rarely breaking] Added parameter `func_bounding_boxes` to `Lambda`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names.\n* [rarely breaking] Added parameter `func_bounding_boxes` to `AssertLambda`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names.\n* [rarely breaking] Added parameter `check_bounding_boxes` to `AssertShape`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191002_unwrapped_ls_aug.md",
    "content": "# Unwrapped Line String Augmentation #450\n\n* Added internal `_augment_line_strings()` methods to various augmenters.\n  This allows to individually control how line strings are supposed to\n  be augmented. Previously, the line string augmentation was a wrapper around\n  keypoint augmentation that did not allow such control.\n* [rarely breaking] Added parameter `func_line_strings` to `Lambda`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names.\n* [rarely breaking] Added parameter `func_line_strings` to `AssertLambda`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names.\n* [rarely breaking] Added parameter `check_line_strings` to `AssertShape`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names."
  },
  {
    "path": "changelogs/0.4.0/added/20191013_change_color_temperature.md",
    "content": "# New Augmenter ChangeColorTemperature #454\n\n* Added augmenter `imgaug.augmenters.color.ChangeColorTemperature`.\n* Added function `imgaug.augmenters.color.change_color_temperatures_()`.\n* Added function `imgaug.augmenters.color.change_color_temperature_()`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191014_brightness_augmenters.md",
    "content": "# New brightness augmenters #455\n\n* Added augmenter `imgaug.augmenters.color.WithBrightnessChannels`.\n* Added augmenter `imgaug.augmenters.color.MultiplyAndAddToBrightness`.\n* Added augmenter `imgaug.augmenters.color.MultiplyBrightness`.\n* Added augmenter `imgaug.augmenters.color.AddToBrightness`.\n* Added method `imgaug.parameters.handle_categorical_string_param()`.\n* Changed `change_colorspaces_()` to accept any iterable of `str` for\n  argument `to_colorspaces`, not just `list`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191016_dropout2d.md",
    "content": "# New Dropout Augmenters #458\n\n* Added a new augmenter `Dropout2d`, which drops channels in images with\n  a defineable probability `p`. Dropped channels will be filled with zeros.\n  By default, the augmenter keeps at least one channel in each image\n  unaltered (i.e. not dropped).\n* Added new augmenter `TotalDropout`, which sets all components to zero\n  for `p` percent of all images. The augmenter should be used in connection\n  with e.g. blend augmenters.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191019_colorwise_grayscaling.md",
    "content": "# Added `RemoveSaturation` #462\n\n* Added `RemoveSaturation`, a shortcut for `MultiplySaturation((0.0, 1.0))`\n  with outputs similar to `Grayscale((0.0, 1.0))`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191020_cartoon.md",
    "content": "# New Augmenter `Cartoon` #463\n\n* Added module `imgaug.augmenters.artistic`.\n* Added function `imgaug.augmenters.artistic.stylize_cartoon(image)`.\n* Added augmenter `imgaug.augmenters.artistic.Cartoon`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191023_mean_shift_blur.md",
    "content": "# Added Augmenter `MeanShiftBlur` #466\n\n* Added function `imgaug.augmenters.blur.blur_mean_shift_(image)`.\n* Added augmenter `imgaug.augmenters.blur.MeanShiftBlur`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191027_jigsaw.md",
    "content": "# Jigsaw Augmenter #476 #577\n\n* Added function `imgaug.augmenters.geometric.apply_jigsaw()`.\n* Added function `imgaug.augmenters.geometric.apply_jigsaw_to_coords()`.\n* Added function `imgaug.augmenters.geometric.generate_jigsaw_destinations()`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191101_deterministic_list.md",
    "content": "# Added DeterministicList #475\n\n* Added `imgaug.parameters.DeterministicList`. Upon a request to generate\n  samples of shape `S`, this parameter will create a new array of shape `S`\n  and fill it by cycling over its list of values repeatedly."
  },
  {
    "path": "changelogs/0.4.0/added/20191102_autocontrast.md",
    "content": "# Autocontrast #479\n\n* Added `imgaug.augmenters.pillike.autocontrast()`, a function with identical\n  inputs and outputs to `PIL.ImageOps.autocontrast`.\n* Added `imgaug.augmenters.pillike.Autocontrast`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191103_affine_shear_y.md",
    "content": "# Affine Shear on the Y-Axis #482\n\n* [rarely breaking] Extended `Affine` to also support shearing on the\n  y-axis (previously, only x-axis was possible). This feature can be used\n  via e.g. ``Affine(shear={\"x\": (-30, 30), \"y\": (-10, 10)})``. If instead\n  a single number is used (e.g. ``Affine(shear=15)``), shearing will be done\n  only on the x-axis. If a single ``tuple``, ``list`` or\n  ``StochasticParameter`` is used, the generated samples will be used\n  identically for both the x-axis and y-axis (this is consistent with\n  translation and scaling). To get independent random samples per axis use\n  the dictionary form.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191103_equalize.md",
    "content": "# Equalize #480\n\n* Added `imgaug.augmenters.pillike.equalize`, similar to\n  `PIL.ImageOps.equalize`.\n* Added `imgaug.augmenters.pillike.equalize_`.\n* Added `imgaug.augmenters.pillike.Equalize`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191103_identity.md",
    "content": "# Added Identity #481\n\n* [rarely breaking] Added `imgaug.augmenters.meta.Identity`, an alias of\n  `Noop`. `Identity` is now the recommended augmenter for identity\n  transformations. This change can break code that explicitly relied on\n  exactly `Noop` being used, e.g. via `isinstance` checks.\n* Renamed parameter `noop_if_topmost` to `identity_if_topmost` in\n  method `imgaug.augmenters.meta.Augmenter.remove_augmenters()`. The old name\n  is now deprecated.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191105_affine_wrappers.md",
    "content": "# Added Wrappers around `Affine` #484\n\n* Added `imgaug.augmenters.geometric.ScaleX`.\n* Added `imgaug.augmenters.geometric.ScaleY`.\n* Added `imgaug.augmenters.geometric.TranslateX`.\n* Added `imgaug.augmenters.geometric.TranslateY`.\n* Added `imgaug.augmenters.geometric.Rotate`.\n* Added `imgaug.augmenters.geometric.ShearX`.\n* Added `imgaug.augmenters.geometric.ShearY`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191106_ooi_removal.md",
    "content": "# Removal of Coordinate-Based Augmentables Outside of the Image Plane #487\n\n* Added `Keypoint.is_out_of_image()`.\n\n* Added `BoundingBox.compute_out_of_image_area()`.\n* Added `Polygon.compute_out_of_image_area()`.\n\n* Added `Keypoint.compute_out_of_image_fraction()`\n* Added `BoundingBox.compute_out_of_image_fraction()`.\n* Added `Polygon.compute_out_of_image_fraction()`.\n* Added `LineString.compute_out_of_image_fraction()`.\n\n* Added `KeypointsOnImage.remove_out_of_image_fraction()`.\n* Added `BoundingBoxesOnImage.remove_out_of_image_fraction()`.\n* Added `PolygonsOnImage.remove_out_of_image_fraction()`.\n* Added `LineStringsOnImage.remove_out_of_image_fraction()`.\n\n* Added `KeypointsOnImage.clip_out_of_image()`.\n\n* Added `imgaug.augmenters.meta.RemoveCBAsByOutOfImageFraction`.\n  Removes coordinate-based augmentables (e.g. BBs) that have at least a\n  specified fraction of their area outside of the image plane.\n* Added `imgaug.augmenters.meta.ClipCBAsToImagePlanes`.\n  Clips off all parts from coordinate-based augmentables (e.g. BBs) that are\n  outside of the corresponding image.\n\n* Changed `Polygon.area` to return `0.0` if the polygon contains less than\n  three points (previously: exception).\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191110_bb_polygon_conversion.md",
    "content": "# Bounding Box to Polygon Conversion #489\n\n* Added method `imgaug.augmentables.bbs.BoundingBox.to_polygon()`.\n* Added method\n  `imgaug.augmentables.bbs.BoundingBoxesOnImage.to_polygons_on_image()`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191110_polygon_subdivision.md",
    "content": "# Added Polygon Subdivision #489\n\n* Added method `imgaug.augmentables.polys.Polygon.subdivide(N)`.\n  The method increases the polygon's corner point count by interpolating\n  `N` points on each edge with regular distance.\n* Added method `imgaug.augmentables.polys.PolygonsOnImage.subdivide(N)`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191110_withpolarwarping.md",
    "content": "# Added `WithPolarWarping` #489\n\n* Added augmenter `imgaug.augmenters.geometric.WithPolarWarping`, an\n  augmenter that applies child augmenters in a polar representation of the\n  image.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191117_debug_images.md",
    "content": "# Generate Debug Images #502\n\n* Added module `imgaug.augmenters.debug`.\n* Added function `imgaug.augmenters.debug.draw_debug_image()`. The function\n  draws an image containing debugging information for a provided set of\n  images and non-image data (e.g. segmentation maps, bounding boxes)\n  corresponding to a single batch. The debug image visualizes these\n  informations (e.g. bounding boxes drawn on images) and offers relevant\n  information (e.g. actual value ranges of images, labels of bounding\n  boxes and their counts, etc.).\n* Added augmenter `imgaug.augmenters.debug.SaveDebugImageEveryNBatches`.\n  Augmenter corresponding to `draw_debug_image()`. Saves an image at every\n  n-th batch into a provided folder.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191117_pad_multi_cval.md",
    "content": "# Multi-Channel cvals in `pad()` #502\n\nImproved `imgaug.augmenters.size.pad()` to support multi-channel values\nfor the `cval` parameter (e.g. RGB colors).\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191218_imagecorruptions.md",
    "content": "# Added Wrappers for `imagecorruptions` Package #530\n\nAdded wrappers around the functions from package\n[bethgelab/imagecorruptions](https://github.com/bethgelab/imagecorruptions).\nThe functions in that package were used in some recent papers and are added\nhere for convenience.\nThe wrappers produce arrays containing values identical to the output\narrays from the corresponding `imagecorruptions` functions when called\nvia the `imagecorruptions.corrupt()` (verified via unittests).\nThe interfaces of the wrapper functions are identical to the\n`imagecorruptions` functions, with the only difference of also supporting\n`seed` parameters.\n\n* Added module `imgaug.augmenters.imgcorruptlike`. The `like` signals that\n  the augmentation functions do not *have* to wrap `imagecorruptions`\n  internally. They merely have to produce the same outputs.\n* Added the following functions to module `imgaug.augmenters.imgcorruptlike`:\n    * `apply_gaussian_noise()`\n    * `apply_shot_noise()`\n    * `apply_impulse_noise()`\n    * `apply_speckle_noise()`\n    * `apply_gaussian_blur()`\n    * `apply_glass_blur()` (improved performance over original function)\n    * `apply_defocus_blur()`\n    * `apply_motion_blur()`\n    * `apply_zoom_blur()`\n    * `apply_fog()`\n    * `apply_snow()`\n    * `apply_spatter()`\n    * `apply_contrast()`\n    * `apply_brightness()`\n    * `apply_saturate()`\n    * `apply_jpeg_compression()`\n    * `apply_pixelate()`\n    * `apply_elastic_transform()`\n* Added function\n  `imgaug.augmenters.imgcorruptlike.get_corruption_names(subset)`.\n  Similar to `imagecorruptions.get_corruption_names(subset)`, but returns a\n  tuple\n  `(list of corruption method names, list of corruption method functions)`,\n  instead of only the names.\n* Added the following augmenters to module `imgaug.augmenters.imgcorruptlike`:\n    * `GaussianNoise`\n    * `ShotNoise`\n    * `ImpulseNoise`\n    * `SpeckleNoise`\n    * `GaussianBlur`\n    * `GlassBlur`\n    * `DefocusBlur`\n    * `MotionBlur`\n    * `ZoomBlur`\n    * `Fog`\n    * `Frost`\n    * `Snow`\n    * `Spatter`\n    * `Contrast`\n    * `Brightness`\n    * `Saturate`\n    * `JpegCompression`\n    * `Pixelate`\n    * `ElasticTransform`\n* Added context `imgaug.random.temporary_numpy_seed()`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191220_cutout.md",
    "content": "# Cutout Augmenter #531 #570\n\n* Added `imgaug.augmenters.arithmetic.apply_cutout_()`, which replaces\n  in-place a single rectangular area with a constant intensity value or a\n  constant color or gaussian noise.\n  See also the [paper](https://arxiv.org/abs/1708.04552) about Cutout.\n* Added `imgaug.augmenters.arithmetic.apply_cutout()`. Same as\n  `apply_cutout_()`, but copies the input images before applying cutout.\n* Added `imgaug.augmenters.arithmetic.Cutout`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191221_inplace_cba_methods.md",
    "content": "# Added in-place Methods for Coordinate-based Augmentables #532\n\n* Added `Keypoint.project_()`.\n* Added `Keypoint.shift_()`.    \n* Added `KeypointsOnImage.on_()`.\n* Added setter for `KeypontsOnImage.items`.\n* Added setter for `BoundingBoxesOnImage.items`.\n* Added setter for `LineStringsOnImage.items`.\n* Added setter for `PolygonsOnImage.items`.\n* Added `KeypointsOnImage.remove_out_of_image_fraction_()`.\n* Added `KeypointsOnImage.clip_out_of_image_fraction_()`.\n* Added `KeypointsOnImage.shift_()`.\n* Added `BoundingBox.project_()`.\n* Added `BoundingBox.extend_()`.\n* Added `BoundingBox.clip_out_of_image_()`.\n* Added `BoundingBox.shift_()`.\n* Added `BoundingBoxesOnImage.on_()`.\n* Added `BoundingBoxesOnImage.clip_out_of_image_()`.\n* Added `BoundingBoxesOnImage.remove_out_of_image_()`.\n* Added `BoundingBoxesOnImage.remove_out_of_image_fraction_()`.\n* Added `BoundingBoxesOnImage.shift_()`.\n* Added `imgaug.augmentables.utils.project_coords_()`.\n* Added `LineString.project_()`.\n* Added `LineString.shift_()`.\n* Added `LineStringsOnImage.on_()`.\n* Added `LineStringsOnImage.remove_out_of_image_()`.\n* Added `LineStringsOnImage.remove_out_of_image_fraction_()`.\n* Added `LineStringsOnImage.clip_out_of_image_()`.\n* Added `LineStringsOnImage.shift_()`.\n* Added `Polygon.project_()`.\n* Added `Polygon.shift_()`.\n* Added `Polygon.on_()`.\n* Added `Polygon.subdivide_()`.\n* Added `PolygonsOnImage.remove_out_of_image_()`.\n* Added `PolygonsOnImage.remove_out_of_image_fraction_()`.\n* Added `PolygonsOnImage.clip_out_of_image_()`.\n* Added `PolygonsOnImage.shift_()`.\n* Added `PolygonsOnImage.subdivide_()`.\n* Switched `BoundingBoxesOnImage.copy()` to a custom copy operation (away\n  from module `copy` module).\n* Added parameters `bounding_boxes` and `shape` to\n  BoundingBoxesOnImage.copy()`.\n* Added parameters `bounding_boxes` and `shape` to\n  BoundingBoxesOnImage.deepcopy()`.\n* Switched `KeypointsOnImage.copy()` to a custom copy operation (away\n  from module `copy` module).\n* Switched `PolygonsOnImage.copy()` to a custom copy operation (away\n  from module `copy` module).\n* Added parameters `polygons` and `shape` to\n  PolygonsOnImage.copy()`.\n* Added parameters `polygons` and `shape` to\n  PolygonsOnImage.deepcopy()`.\n* Switched augmenters to use in-place functions for keypoints,\n  bounding boxes, line strings and polygons.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191224_pil_module.md",
    "content": "# Added Module `imgaug.augmenters.pillike` #479 #480 #538\n\n* Added module `imgaug.augmenters.pillike`, which contains augmenters and\n  functions corresponding to commonly used PIL functions. Their outputs\n  are guaranteed to be identical to the PIL outputs.\n* Added the following functions to the module:\n  * `imgaug.augmenters.pillike.equalize`\n  * `imgaug.augmenters.pillike.equalize_`\n  * `imgaug.augmenters.pillike.autocontrast`\n  * `imgaug.augmenters.pillike.autocontrast_`\n  * `imgaug.augmenters.pillike.solarize`\n  * `imgaug.augmenters.pillike.solarize_`\n  * `imgaug.augmenters.pillike.posterize`\n  * `imgaug.augmenters.pillike.posterize_`\n  * `imgaug.augmenters.pillike.enhance_color`\n  * `imgaug.augmenters.pillike.enhance_contrast`\n  * `imgaug.augmenters.pillike.enhance_brightness`\n  * `imgaug.augmenters.pillike.enhance_sharpness`\n  * `imgaug.augmenters.pillike.filter_blur`\n  * `imgaug.augmenters.pillike.filter_smooth`\n  * `imgaug.augmenters.pillike.filter_smooth_more`\n  * `imgaug.augmenters.pillike.filter_edge_enhance`\n  * `imgaug.augmenters.pillike.filter_edge_enhance_more`\n  * `imgaug.augmenters.pillike.filter_find_edges`\n  * `imgaug.augmenters.pillike.filter_contour`\n  * `imgaug.augmenters.pillike.filter_emboss`\n  * `imgaug.augmenters.pillike.filter_sharpen`\n  * `imgaug.augmenters.pillike.filter_detail`\n  * `imgaug.augmenters.pillike.warp_affine`\n* Added the following augmenters to the module:\n  * `imgaug.augmenters.pillike.Solarize`\n  * `imgaug.augmenters.pillike.Posterize`.\n    (Currently alias for `imgaug.augmenters.color.Posterize`.)\n  * `imgaug.augmenters.pillike.Equalize`\n  * `imgaug.augmenters.pillike.Autocontrast`\n  * `imgaug.augmenters.pillike.EnhanceColor`\n  * `imgaug.augmenters.pillike.EnhanceContrast`\n  * `imgaug.augmenters.pillike.EnhanceBrightness`\n  * `imgaug.augmenters.pillike.EnhanceSharpness`\n  * `imgaug.augmenters.pillike.FilterBlur`\n  * `imgaug.augmenters.pillike.FilterSmooth`\n  * `imgaug.augmenters.pillike.FilterSmoothMore`\n  * `imgaug.augmenters.pillike.FilterEdgeEnhance`\n  * `imgaug.augmenters.pillike.FilterEdgeEnhanceMore`\n  * `imgaug.augmenters.pillike.FilterFindEdges`\n  * `imgaug.augmenters.pillike.FilterContour`\n  * `imgaug.augmenters.pillike.FilterEmboss`\n  * `imgaug.augmenters.pillike.FilterSharpen`\n  * `imgaug.augmenters.pillike.FilterDetail`\n  * `imgaug.augmenters.pillike.Affine`\n"
  },
  {
    "path": "changelogs/0.4.0/added/20191230_standardized_lut.md",
    "content": "# Standardized LUT Methods #542\n\n* Added `imgaug.imgaug.apply_lut()`, which applies a lookup table to an image.\n* Added `imgaug.imgaug.apply_lut_()`. In-place version of `apply_lut()`.\n* Refactored all augmenters to use these new LUT functions.\n  This likely fixed some so-far undiscovered bugs in augmenters using LUT\n  tables.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20200101_bb_label_drawing.md",
    "content": "# Drawing Bounding Box Labels #545\n\nWhen drawing bounding boxes on images via `BoundingBox.draw_on_image()`\nor `BoundingBoxesOnImage.draw_on_image()`, a box containing the label will now\nbe drawn over each bounding box's rectangle. If the bounding box's label is\nset to `None`, the label box will not be drawn. For more detailed control,\nuse `BoundingBox.draw_label_on_image()`.\n\n* Added method `imgaug.augmentables.BoundingBox.draw_label_on_image()`.\n* Added method `imgaug.augmentables.BoundingBox.draw_box_on_image()`.\n* Changed method `imgaug.augmentables.BoundingBox.draw_on_image()`\n  to automatically draw a bounding box's label.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20200102_cbasoi_getitem.md",
    "content": "# Index-based Access to Coordinate-based `*OnImage` Instances #547\n\nEnabled index-based access to coordinate-based `*OnImage` instances, i.e. to\n`KeypointsOnImage`, `BoundingBoxesOnImage`, `LineStringsOnImage` and\n`PolygonsOnImage`. This allows to do things like\n`bbsoi = BoundingBoxesOnImage(...); bbs = bbsoi[0:2];`.\n\n* Added `imgaug.augmentables.kps.KeypointsOnImage.__getitem__()`.\n* Added `imgaug.augmentables.bbs.BoundingBoxesOnImage.__getitem__()`.\n* Added `imgaug.augmentables.lines.LineStringsOnImage.__getitem__()`.\n* Added `imgaug.augmentables.polys.PolygonsOnImage.__getitem__()`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20200105_discretize_round.md",
    "content": "# Added `round` Parameter to `Discretize` #553\n\nAdded the parameter `round` to `imgaug.parameters.Discretize`. The parameter\ndefaults to `True`, i.e. the default behaviour of `Discretize` did not change.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20200106_rain.md",
    "content": "# Added Rain Augmenters #551\n\nAdded augmenter(s) to create fake rain effects. They currently seem to work\nbest at around medium-sized images (~224px).\n\n* Added `imgaug.augmenters.weather.Rain`.\n* Added `imgaug.augmenters.weather.RainLayer`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20200106_randaugment.md",
    "content": "# Add RandAugment #553\n\nAdded a RandAugment augmenter, similar to the one described in the paper\n\"RandAugment: Practical automated data augmentation with a reduced\nsearch space\".\n\n* Added module `imgaug.augmenters.collections`\n* Added augmenter `imgaug.augmenters.collections.RandAugment`.\n"
  },
  {
    "path": "changelogs/0.4.0/added/20200125_image_warnings.md",
    "content": "# Improved Warnings on Probably-Wrong Image Inputs #594\n\nImproved the errors and warnings on image augmentation calls.\n`augment_image()` will now produce a more self-explanatory error\nmessage when calling it as in `augment_image(list of images)`.\nCalls of single-image augmentation functions (e.g.\n`augment(image=...)`) with inputs that look like multiple images\nwill now produce warnings. This is the case for `(H, W, C)`\ninputs when `C>=32` (as that indicates that `(N, H, W)` was\nactually provided).\nCalls of multi-image augmentation functions (e.g.\n`augment(images=...)`) with inputs that look like single images\nwill now produce warnings. This is the case for `(N, H, W)`\ninputs when `W=1` or `W=3` (as that indicates that `(H, W, C)`\nwas actually provided.)\n\n* Added an assert in `augment_image()` to verify that inputs are\n  arrays.\n* Added warnings for probably-wrong image inputs in\n  `augment_image()`, `augment_images()`, `augment()` (and its\n  alias `__call__()`).\n* Added module `imgaug.augmenters.base`.\n* Added warning\n  `imgaug.augmenters.base.SuspiciousMultiImageShapeWarning`.\n* Added warning\n  `imgaug.augmenters.base.SuspiciousSingleImageShapeWarning`.\n* Added `imgaug.testutils.assertWarns`, similar to `unittest`'s\n  `assertWarns`, but available in python <3.2."
  },
  {
    "path": "changelogs/0.4.0/changed/20190929_rngs_polygon_recoverer.md",
    "content": "# Improved RNG Handling during Polygon Augmentation #447\n\n* Changed `Augmenter.augment_polygons()` to copy the augmenter's RNG\n  before starting concave polygon recovery. This is done for cleanliness and\n  should not have any effects for users.\n* Removed RNG copies in `_ConcavePolygonRecoverer` to improve performance.\n"
  },
  {
    "path": "changelogs/0.4.0/changed/20191110_affine_translation_precision.md",
    "content": "# Affine Translation Precision #489\n\n* Removed a rounding operation in `Affine` translation that would unnecessarily\n  round floats to integers. This should make coordinate augmentation overall\n  more accurate.\n"
  },
  {
    "path": "changelogs/0.4.0/changed/20191128_affine_translate.md",
    "content": "# `Affine.get_parameters()` and `translate_px`/`translate_percent` #508\n\n* Changed `Affine.get_parameters()` to always return a tuple `(x, y, mode)`\n  for translation, where `mode` is either `px` or `percent`,\n  and `x` and `y` are stochastic parameters. `y` may be `None` if the same\n  parameter (and hence samples) are used for both axes."
  },
  {
    "path": "changelogs/0.4.0/changed/20191230_dont_import_msgs.md",
    "content": "# Removed Outdated \"Don't Import from this Module\" Messages #539\n\nThe docstring of each module in ``imgaug.augmenters`` previously included a\nsuggestion to not directly import from that module, but instead use\n``imgaug.augmenters.<AugmenterName>``. That was due to the categorization\nstill being unstable. As the categorization has now been fairly stable\nfor a long time, the suggestion was removed from all modules. Calling\n``imgaug.augmenters.<AugmenterName>`` instead of\n``imgaug.augmenters.<ModuleName>.<AugmenterName>`` is however still the\npreferred way.\n"
  },
  {
    "path": "changelogs/0.4.0/changed/20200103_standardized_shift_interfaces.md",
    "content": "# Standardized `shift()` Interfaces of Coordinate-Based Augmentables #548\n\nThe interfaces for shift operations of all coordinate-based\naugmentables (Keypoints, BoundingBoxes, LineStrings, Polygons)\nwere standardized. All of these augmentables have now the same\ninterface for shift operations. Previously, Keypoints used\na different interface (using `x` and `y` arguments) than the\nother augmentables (using `top`, `right`, `bottom`, `left`\narguments). All augmentables use now the interface of Keypoints\nas that is simpler and less ambiguous. Old arguments are still\naccepted, but will produce deprecation warnings. Change the\narguments to `x` and `y` following `x=left-right` and\n`y=top-bottom`.\n\n**[breaking]** This breaks if one relied on calling `shift()` functions of\n`BoundingBox`, `LineString`, `Polygon`, `BoundingBoxesOnImage`,\n`LineStringsOnImage` or `PolygonsOnImage` without named arguments.\nE.g. `bb = BoundingBox(...); bb_shifted = bb.shift(1, 2, 3, 4);`\nwill produce unexpected outputs now (equivalent to\n`shift(x=1, y=2, top=3, right=4, bottom=0, left=0)`),\nwhile `bb_shifted = bb.shift(top=1, right=2, bottom=3, left=4)` will still\nwork as expected.\n\n* Added arguments `x`, `y` to `BoundingBox.shift()`, `LineString.shift()`\n  and `Polygon.shift()`.\n* Added arguments `x`, `y` to `BoundingBoxesOnImage.shift()`,\n  `LineStringsOnImage.shift()` and `PolygonsOnImage.shift()`.\n* Marked arguments `top`, `right`, `bottom`, `left` in\n  `BoundingBox.shift()`, `LineString.shift()` and `Polygon.shift()`\n  as deprecated. This also affects the corresponding `*OnImage`\n  classes.\n* Added function `testutils.wrap_shift_deprecation()`.\n"
  },
  {
    "path": "changelogs/0.4.0/changed/20200112_simplified_augmenter_args.md",
    "content": "# Simplified Standard Parameters of Augmenters #567 #595\n\nChanged the standard parameters shared by all augmenters to a\nreduced and more self-explanatory set. Previously, all augmenters\nshared the parameters `name`, `random_state` and `deterministic`.\nThe new parameters are `seed` and `name`.\n\n`deterministic` was removed as it was hardly ever used and because\nit caused frequently confusion with regards to its meaning. The\nparameter is still accepted but will now produce a deprecation\nwarning. Use `<augmenter>.to_deterministic()` instead.\n(Reminder: `to_deterministic()` is necessary if you want to get\nthe same samples in consecutive augmentation calls. It is *not*\nnecessary if you want your generated samples to be dependent on\nan initial seed or random state as that is *always* the case\nanyways. You only have to manually set the seed, either\naugmenter-specific via the `seed` parameter or global via\n`imgaug.random.seed()` (affects only augmenters without their\nown seed).)\n\n`random_state` was renamed to `seed` as providing a seed value\nis the more common use case compared to providing a random state.\nMany users also seemed to be unaware that `random_state` accepted\nseed values. The new name should make this more clear.\nThe old parameter `random_state` is still accepted, but will\nlikely be deprecated in the future.\n\n**[breaking]** This patch breaks if one relied on the order of\n`name`, `random_state` and `deterministic`. The new order is now\n`seed=..., name=..., random_state=..., deterministic=...` (with the\nlatter two parameters being outdated or deprecated)\nas opposed to previously\n`name=..., deterministic=..., random_state=...`.\n"
  },
  {
    "path": "changelogs/0.4.0/changed/20200115_changed_defaults.md",
    "content": "# Improved Default Values of Augmenters #582\n\n**[breaking]** Most augmenters had previously default values that\nmade them equivalent to identity functions. Users had to explicitly\nchange the defaults to proper values in order to \"activate\"\naugmentations. To simplify the usage of the library, the default\nvalues of most augmenters were changed to medium-strength\naugmentations. E.g.\n`Sequential([Affine(), UniformVoronoi(), CoarseDropout()])`\nshould now produce decent augmentations.\n\nA few augmenters were set to always-on, maximum-strength\naugmentations. This is the case for:\n\n* `Grayscale` (always fully grayscales images, use\n  `Grayscale((0.0, 1.0))` for random strengths)\n* `RemoveSaturation` (same as `Grayscale`)\n* `Fliplr` (always flips images, use `Fliplr(0.5)` for 50%\n  probability)\n* `Flipud` (same as `Fliplr`)\n* `TotalDropout` (always drops everything, use\n  `TotalDropout(0.1)` to drop everything for 10% of all images)\n* `Invert` (always inverts images, use `Invert(0.1)` to invert\n  10% of all images)\n* `Rot90` (always rotates exactly once clockwise by 90 degrees,\n  use `Rot90((0, 3))` for any rotation)\n\nThese settings seemed to better match user-expectations.\nSuch maximum-strength settings however were not chosen for all\naugmenters where one might expect them. The defaults are set to\nvarying strengths for, e.g.  `Superpixels` (replaces only some\nsuperpixels with cellwise average colors), `UniformVoronoi` (also\nonly replaces some cells), `Sharpen` (alpha-blends with variable\nstrength, the same is the case for `Emboss`, `EdgeDetect` and\n`DirectedEdgeDetect`) and `CLAHE` (variable clip limits).\n\n*Note*: Some of the new default values will cause issues with\nnon-`uint8` inputs.\n\n*Note*: The defaults for `per_channel` and `keep_size` were not\nadjusted. It is currently still the default behaviour of all\naugmenters to affect all channels in the same way and to resize\ntheir outputs back to the input sizes.\n\nThe exact changes to default values are listed below.\n\n**imgaug.arithmetic**\n  \n  * `Add`\n    * `value`: `0` -> `(-20, 20)`\n  * `AddElementwise`\n    * `value`: `0` -> `(-20, 20)`\n  * `AdditiveGaussianNoise`\n    * `scale`: `0` -> `(0, 15)`\n  * `AdditiveLaplaceNoise`\n    * `scale`: `0` -> `(0, 15)`\n  * `AdditivePoissonNoise`\n    * `scale`: `0` -> `(0, 15)`\n  * `Multiply`\n    * `mul`: `1.0` -> `(0.8, 1.2)`\n  * `MultiplyElementwise`:\n    * `mul`: `1.0` -> `(0.8, 1.2)`\n  * `Dropout`: \n    * `p`: `0.0` -> `(0.0, 0.05)`\n  * `CoarseDropout`:\n    * `p`: `0.0` -> `(0.02, 0.1)`\n    * `size_px`: `None` -> `(3, 8)`\n    * `min_size`: `4` -> `3`\n    * Default for `size_px` is only used if neither `size_percent`\n      nor `size_px` is provided by the user.\n  * `CoarseSaltAndPepper`:\n    * `p`: `0.0` -> `(0.02, 0.1)`\n    * `size_px`: `None` -> `(3, 8)`\n    * `min_size`: `4` -> `3`\n    * Default for `size_px` is only used if neither `size_percent`\n      nor `size_px` is provided by the user.\n  * `CoarseSalt`:\n    * `p`: `0.0` -> `(0.02, 0.1)`\n    * `size_px`: `None` -> `(3, 8)`\n    * `min_size`: `4` -> `3`\n    * Default for `size_px` is only used if neither `size_percent`\n      nor `size_px` is provided by the user.\n  * `CoarsePepper`:\n    * `p`: `0.0` -> `(0.02, 0.1)`\n    * `size_px`: `None` -> `(3, 8)`\n    * `min_size`: `4` -> `3`\n    * Default for `size_px` is only used if neither `size_percent`\n      nor `size_px` is provided by the user.\n  * `SaltAndPepper`:\n    * `p`: `0.0` -> `(0.0, 0.03)`\n  * `Salt`:\n    * `p`: `0.0` -> `(0.0, 0.03)`\n  * `Pepper`:\n    * `p`: `0.0` -> `(0.0, 0.05)`\n  * `ImpulseNoise`:\n    * `p`: `0.0` -> `(0.0, 0.03)`\n  * `Invert`: \n    * `p`: `0` -> `1`\n  * `JpegCompression`:\n    * `compression`: `50` -> `(0, 100)`\n\n**imgaug.blend**\n\n  * `BlendAlpha`\n    * `factor`: `0` -> `(0.0, 1.0)`\n  * `BlendAlphaElementwise`\n    * `factor`: `0` -> `(0.0, 1.0)`\n\n**imgaug.blur**\n\n  * `GaussianBlur`:\n    * `sigma`: `0` -> `(0.0, 3.0)`\n  * `AverageBlur`:\n    * `k`: `1` -> `(1, 7)`\n  * `MedianBlur`:\n    * `k`: `1` -> `(1, 7)`\n  * `BilateralBlur`:\n    * `d`: `1` -> `(1, 9)`\n  * `MotionBlur`:\n    * `k`: `5` -> `(3, 7)`\n\n**imgaug.color**\n\n  * `MultiplyHueAndSaturation`:\n    * `mul_hue`: `None` -> `(0.5, 1.5)`\n    * `mul_saturation`: `None` -> `(0.0, 1.7)`\n    * These defaults are only used if the user provided neither\n      `mul` nor `mul_hue` nor `mul_saturation`.\n  * `MultiplyHue`:\n    * `mul`: `(-1.0, 1.0)` -> `(-3.0, 3.0)`\n  * `AddToHueAndSaturation`:\n    * `value_hue`: `None` -> `(-40, 40)`\n    * `value_saturation`: `None` -> `(-40, 40)`\n    * These defaults are only used if the user provided neither\n      `value` nor `value_hue` nor `value_saturation`.\n  * `Grayscale`:\n    * `alpha`: `0` -> `1`\n\n**imgaug.contrast**\n\n  * `GammaContrast`:\n    * `gamma`: `1` -> `(0.7, 1.7)` \n  * `SigmoidContrast`:\n    * `gain`: `10` -> `(5, 6)`\n    * `cutoff`: `0.5` -> `(0.3, 0.6)`\n  * `LogContrast`:\n    * `gain`: `1` -> `(0.4, 1.6)`\n  * `LinearContrast`:\n    * `alpha`: `1` -> `(0.6, 1.4)`\n  * `AllChannelsCLAHE`:\n    * `clip_limit`: `40` -> `(0.1, 8)`\n    * `tile_grid_size_px`: `8` -> `(3, 12)`\n  * `CLAHE`: \n    * `clip_limit`: `40` -> `(0.1, 8)`\n    * `tile_grid_size_px`: `8` -> `(3, 12)`\n\n**convolutional**\n\n  * `Sharpen`:\n    * `alpha`: `0` -> `(0.0, 0.2)`\n    * `lightness`: `1` -> `(0.8, 1.2)`\n  * `Emboss`:\n    * `alpha`: `0` -> `(0.0, 1.0)`\n    * `strength`: `1` -> `(0.25, 1.0)`\n  * `EdgeDetect`:\n    * `alpha`: `0` -> `(0.0, 0.75)`\n  * `DirectedEdgeDetect`:\n    * `alpha`: `0` -> `(0.0, 0.75)`\n\n**imgaug.flip**\n\n  * `Fliplr`:\n    * `p`: `0` -> `1`\n  * `Flipud`:\n    * `p`: `0` -> `1`\n\n**imgaug.geometric**\n\n  * `Affine`:\n    * `scale`: `1` -> `{\"x\": (0.9, 1.1), \"y\": (0.9, 1.1)}`\n    * `translate_percent`: None -> `{\"x\": (-0.1, 0.1), \"y\": (-0.1, 0.1)}`\n    * `rotate`: `0` -> `(-15, 15)`\n    * `shear`: `0` -> `shear={\"x\": (-10, 10), \"y\": (-10, 10)}`\n    * These defaults are only used if no affine transformation\n      parameter was set by the user. Otherwise the not-set\n      parameters default again towards the identity function.\n  * `PiecewiseAffine`:\n    * `scale`: `0` -> `(0.0, 0.04)`\n    * `nb_rows`: `4` -> `(2, 4)`\n    * `nb_cols`: `4` -> `(2, 4)`\n  * `PerspectiveTransform`:\n    * `scale`: `0` -> `(0.0, 0.06)`\n  * `ElasticTransformation`:\n    * `alpha`: `0` -> `(0.0, 40.0)`\n    * `sigma`: `0` -> `(4.0, 8.0)`\n  * `Rot90`:\n    * `k`: `(no default)` -> `k=1`\n\n**imgaug.pooling**\n\n  * `AveragePooling`:\n    * `k`: `(no default)` -> `(1, 5)`\n  * `MaxPooling`:\n    * `k`: `(no default)` -> `(1, 5)`\n  * `MinPooling`:\n    * `k`: `(no default)` -> `(1, 5)`\n  * `MedianPooling`:\n    * `k`: `(no default)` -> `(1, 5)`\n\n**imgaug.segmentation**\n \n  * `Superpixels`:\n    * `p_replace`: `0.0` -> `(0.5, 1.0)`\n    * `n_segments`: `100` -> `(50, 120)`\n  * `UniformVoronoi`:\n    * `n_points`: `(no default)` -> `(50, 500)`\n    * `p_replace`: `1.0` -> `(0.5, 1.0)`.\n  * `RegularGridVoronoi`:\n    * `n_rows`: `(no default)` -> `(10, 30)`\n    * `n_cols`: `(no default)` -> `(10, 30)`\n    * `p_drop_points`: `0.4` -> `(0.0, 0.5)`\n    * `p_replace`: `1.0` -> `(0.5, 1.0)`\n  * `RelativeRegularGridVoronoi`: Changed defaults from\n    * `n_rows_frac`: `(no default)` -> `(0.05, 0.15)`\n    * `n_cols_frac`: `(no default)` -> `(0.05, 0.15)`\n    * `p_drop_points`: `0.4` -> `(0.0, 0.5)`\n    * `p_replace`: `1.0` -> `(0.5, 1.0)`\n\n**imgaug.size**\n\n  * `CropAndPad`:\n    * `percent`: `None` -> `(-0.1, 0.1)`\n    * This default is only used if the user has provided\n      neither `px` nor `percent`.\n  * `Pad`:\n    * `percent`: `None` -> `(0.0, 0.1)`\n    * This default is only used if the user has provided\n      neither `px` nor `percent`.\n  * `Crop`:\n    * `percent`: `None` -> `(0.0, 0.1)`\n    * This default is only used if the user has provided\n      neither `px` nor `percent`.\n"
  },
  {
    "path": "changelogs/0.4.0/changed/20200125_any_opencv_accepted.md",
    "content": "# `setup.py` Now Accepts Any `opencv-*` Installation #586\n\n`setup.py` was changed so that it now accepts `opencv-python`,\n`opencv-python-headless`, `opencv-contrib-python` and\n`opencv-contrib-python-headless` as valid OpenCV installations.\nPreviously, only `opencv-python-headless` was accepted, which\ncould easily cause conflicts when another one of the mentioned\nlibraries was already installed.\nIf none of the mentioned libraries is installed, `setup.py`\nwill default to adding `opencv-python` as a requirement.\n"
  },
  {
    "path": "changelogs/0.4.0/deprecated/20190926_rename_inplace.md",
    "content": "* Deprecated method `Augmenter.reseed()`.\n  Use `Augmenter.seed_()` instead. #444\n* Deprecated method `Augmenter.remove_augmenters_inplace()`.\n  Use `Augmenter.remove_augmenters_()` instead. #444"
  },
  {
    "path": "changelogs/0.4.0/deprecated/20191230_deprecate_affinecv2.md",
    "content": "# Deprecated AffineCv2 in Favor of Affine #540\n\nThe augmenter `imgaug.augmenters.geometric.AffineCv2` was not properly\nmaintained for quite a while and its functionality is already covered\nby `imgaug.augmenters.geometric.Affine` using parameter\n`backend='cv2'`. Hence, it was now deprecated. Use `Affine` instead.\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20190926_fixed_resize_dtype.md",
    "content": "* Fixed `Resize` always returning an `uint8` array during image augmentation\n  if the input was a single numpy array and all augmented images had the\n  same shape. #442 #443\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20190928_fixed_affine_coords_aug.md",
    "content": "* Fixed `Affine` coordinate-based augmentation applying wrong offset\n  when shifting images to/from top-left corner. This would lead to an error\n  of around 0.5 to 1.0 pixels. #446\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20190928_fixed_pwa_empty_kps_unaligned.md",
    "content": "* Fixed keypoint augmentation in `PiecewiseAffine` potentially being\n  unaligned if a `KeypointsOnImage` instance contained no keypoints. #446\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20190928_fixed_type_val.md",
    "content": "* Fixed `imgaug.validation.convert_iterable_to_string_of_types()` crashing due\n  to not converting types to strings before joining them. #446"
  },
  {
    "path": "changelogs/0.4.0/fixed/20190929_fixed_assert_is_iterable_of.md",
    "content": "* Fixed `imgaug.validation.assert_is_iterable_of()` producing a not\n  well-designed error if the input was not an iterable. #446\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191003_fixed_image_normalization.md",
    "content": "* Fixed image normalization crashing when an input ndarray of multiple images\n  was changed during augmentation to a list of multiple images with different\n  shapes *and* the original input ndarray represented a single image or\n  a collection of 2D `(H,W)` images. This problem affected `augment()`,\n  `augment_batch()` and `augment_batches()`.\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191003_fixed_typo.md",
    "content": "* Fixed typo in image normalization. #451\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191006_fixed_withchannels_alignment.md",
    "content": "* Fixed a problem in `WithChannels` that could lead random sampling in child\n  augmenters being unaligned between images and corresponding non-image\n  data. #451"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191106_fixed_random_state_funcs_missing.md",
    "content": "# Fixed Missing RandomState Methods #486\n\n* Added aliases to `imgaug.random.RNG` for some outdated numpy random number\n  sampling methods that existed in `numpy.random.RandomState` but not in\n  numpy's new RNG system (1.17+). These old methods are not used in `imgaug`,\n  but some custom augmenters and `Lambda` calls may require them when\n  interacting with the provided `random_state` instances. \n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191110_fixed_affine_map_aug.md",
    "content": "# Fixed Affine Translation of Map-Data #489\n\n* Fixed `Affine` producing unaligned augmentations between images and\n  segmentation maps or heatmaps when using `translate_px` and the segmentation\n  map or heatmap had a different height/width than corresponding image.\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191111_fixed_snowflakeslayer_crash.md",
    "content": "# Fixed `SnowflakesLayer` crash #471\n\n* Fixed a crash in `SnowflakesLayer` that could occur when using values\n  close to `1.0` for `flake_size`.\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191111_multiplyhueandsaturation_rng.md",
    "content": "# Fixed `MultiplyHueAndSaturation` RNG #493\n\n* Fixed `MultiplyHueAndSaturation` crashing if the RNG provided via\n  `random_state` was not `None` or `imgaug.random.RNG`.\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191124_fixed_cloud_layer_float.md",
    "content": "* Fixed `CloudLayer.draw_on_image()` producing tuples instead of arrays\n  as output for `float` input images. #540"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191128_fixed_affine_translate.md",
    "content": "# `Affine` Translate Type Falsely dependent on float/int Samples #508\n\n* Fixed `Affine` parameter `translate_px` behaving like `translate_percent`\n  if a continuous stochastic parameter was provided.\n  Analogously `translate_percent` would behave like `translate_px` if\n  a discrete stochastic parameter was provided."
  },
  {
    "path": "changelogs/0.4.0/fixed/20191128_fixed_hanging_nixos.md",
    "content": "# Fixed Hanging Code in NixOS #414 #510\n\n* Fixed code hanging indefinitely when using multicore augmentation\n  on NixOS."
  },
  {
    "path": "changelogs/0.4.0/fixed/20191217_collections_abc.md",
    "content": "# Fixed Abstract Base Classes Import #527\n\n* Fixed a deprecation warning and potential crash in python 3.8\n  related to the use of `collections` instead of `collections.abc`.\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191218_fixed_fromfunction_deprecated.md",
    "content": "# Fixed `scipy.fromfunction` Deprecated #529\n\n* Fixed deprecated `scipy.fromfunction()` being called.\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191222_fixed_numpy_1_18.md",
    "content": "# Fixed crashes in numpy 1.18 #534\n\n* Fixed `imgaug.random.normalize_generator()` crashing in numpy 1.18.\n  The function relied on `numpy.random.bit_generator.BitGenerator`, which\n  was moved in numpy 1.18 to `numpy.random.BitGenerator` without a\n  deprecation period for the old name.\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20191223_fixed_opencv_multicore_aug_hanging.md",
    "content": "# Fixed OpenCV Hanging in Multicore Augmentation #535\n\n* Fixed an issue that could lead to endlessly hanging programs on some OS\n  when using multicore augmentation (e.g. via pool) and augmenters using\n  OpenCV.\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20200110_fixed_seed.md",
    "content": "# Fixed `random.seed` not always seeding in-place #557\n\nFixed `imgaug.random.seed()` not seeding the global `RNG` in-place\nin numpy 1.17+. The (unfixed) function instead created a new\nglobal `RNG` with the given seed. This set the seed of augmenters\ncreated *after* the `seed()` call, but not of augmenters created\n*before* the `seed()` call as they would continue to use the old\nglobal RNG.\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20200111_fixed_elastic_transformation_cval.md",
    "content": "# Fixed `cval` in `ElasticTransformation` #561 #562\n\n* Fixed `cval` in `ElasticTransformation` resulting new pixels in RGB images\n  being filled with `(cval, 0, 0)` instead of `(cval, cval, cval)`.\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20200113_fixed_weather_randomness.md",
    "content": "* Fixed some augmenters in module `weather` not transferring seed values\n  or random states that were provided upon creation to child augmenters. #568\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20200118_perspt_inaccuracy.md",
    "content": "* Fixed an inaccuracy in `PerspectiveTransform` that could lead to slightly\n  misaligned transformations between images and coordinate-based\n  augmentables (e.g. bounding boxes). The problem was more significant the\n  smaller the images and larger the `scale` values were. It was also\n  worsened by using `fit_output`. #585\n"
  },
  {
    "path": "changelogs/0.4.0/fixed/20200122_fix_keepsizebyresize.md",
    "content": "* Fixed `KeepSizeByResize` potentially crashing if a single numpy array\n  was provided as the input for an iterable of images (as opposed to\n  a list of numpy arrays). #590"
  },
  {
    "path": "changelogs/0.4.0/fixed/20200126_shapely_17a2.md",
    "content": "* Fixed an issue in Shapely 1.7a2 (python 3.8) that could lead to\n  a crash in `LineString.find_intersections_with()` if there was\n  no intersection. #600\n"
  },
  {
    "path": "changelogs/0.4.0/refactored/20191124_pylint.md",
    "content": "# Refactored according to pylint requirements\n\n* Refactored all core library files to fulfill (most) pylint requirements.\n* [rarely breaking] Renamed\n  `imgaug.augmenters.size.KeepSizeByResize.get_shapes()` to `_get_shapes()`.\n* Added a project-specific pylint configuration.\n"
  },
  {
    "path": "changelogs/0.4.0/refactored/20200111_opencv_normalization.md",
    "content": "# Unified OpenCV Input Normalization #565\n\n* Refactored various augmenters to use the same normalization for OpenCV\n  inputs. This probably fixes some previously undiscovered bugs.\n"
  },
  {
    "path": "changelogs/0.4.0/renamed/20190926_rename_inplace.md",
    "content": "* Renamed `Augmenter.reseed()` to `Augmenter.seed_()`. #444\n* Renamed `Augmenter.remove_augmenters_inplace()` to\n  `Augmenter.remove_augmenters_()`. #444"
  },
  {
    "path": "changelogs/master/20200206_data_module.md",
    "content": "# Add New `data` Module #606\n\nThis patch moves the example data functions from `imgaug.imgaug` to\nthe new module `imgaug.data`.\n\nAdd Modules:\n* `imgaug.data`\n\nAdd Functions:\n* `imgaug.data.quokka()`\n* `imgaug.data.quokka_square()`\n* `imgaug.data.quokka_heatmap()`\n* `imgaug.data.quokka_segmentation_map()`\n* `imgaug.data.quokka_keypoints()`\n* `imgaug.data.quokka_bounding_boxes()`\n* `imgaug.data.quokka_polygons()`\n\nDeprecated Functions:\n* `imgaug.imgaug.quokka()`.\n  Use `imgaug.data.quokka()` instead.\n* `imgaug.imgaug.quokka_square()`.\n  Use `imgaug.data.quokka_square()` instead.\n* `imgaug.imgaug.quokka_heatmap()`.\n  Use `imgaug.data.quokka_heatmap()` instead.\n* `imgaug.imgaug.quokka_segmentation_map()`.\n  Use `imgaug.data.quokka_segmentation_map()` instead.\n* `imgaug.imgaug.quokka_keypoints()`.\n  Use `imgaug.data.quokka_keypoints()` instead.\n* `imgaug.imgaug.quokka_bounding_boxes()`.\n  Use `imgaug.data.quokka_bounding_boxes()` instead.\n* `imgaug.imgaug.quokka_polygons()`.\n  Use `imgaug.data.quokka_polygons()` instead.\n\nRemoved Constants:\n* [rarely breaking] `imgaug.imgaug.FILE_DIR`\n* [rarely breaking] `imgaug.imgaug.QUOKKA_FP`\n* [rarely breaking] `imgaug.imgaug.QUOKKA_ANNOTATIONS_FP`\n* [rarely breaking] `imgaug.imgaug.QUOKKA_DEPTH_MAP_HALFRES_FP`\n"
  },
  {
    "path": "changelogs/master/changed/20200222_shape_handling.md",
    "content": "# Stricter Shape Handling in Augmentables #623\n\nVarious methods of augmentables have so far accepted tuples\nof integers or numpy arrays for `shape` parameters. This was\nthe case for e.g. `BoundingBoxesOnImage.__init__(bbs, shape)`\nor `Polygon.clip_out_of_image(image)`. This tolerant handling\nof shapes conveys some risk that an input is actually a\nnumpy representation of a shape, i.e. the equivalent of\n`numpy.array(shape_tuple)`.\n\nTo decrease the risk of such an input leading to bugs, arrays\nare no longer recommended inputs for `shape` in\n`KeypointsOnImage.__init__`, `BoundingBoxesOnImage.__init__`,\n`LineStringsOnImage.__init__`, and `PolygonsOnImage.__init__`.\nTheir usage in these methods will now raise a deprecation warning.\n\nIn all other methods of augmentables that currently accept\nimage-like numpy arrays and shape tuples for parameters,\nonly arrays that are 2-dimensional or 3-dimensional are from\nnow on accepted. Other arrays (e.g. 1-dimensional ones)\nwill be rejected with an assertion error.\n\nAdd functions:\n* `imgaug.augmentables.utils.normalize_imglike_shape()`.\n\nList of deprecations:\n* `numpy.ndarray` as value of parameter `shape` in\n  `KeypointsOnImage.__init__`.\n* `numpy.ndarray` as value of parameter `shape` in\n  `BoundingBoxesOnImage.__init__`.\n* `numpy.ndarray` as value of parameter `shape` in\n  `LineStringsOnImage.__init__`.\n* `numpy.ndarray` as value of parameter `shape` in\n  `PolygonsOnImage.__init__`.\n"
  },
  {
    "path": "changelogs/master/changed/20200522_limit_dtype_support_alphablend.md",
    "content": "# Limit dtype Support in Alpha Blending in Windows #678\n\nThis patch marks the dtypes uint64, int64 and float64\nas 'only supported to a limited degree' in blend_alpha().\nThe dtypes require float128 for accurate output\ncomputations, which is not supported in Windows.\n\nAdditionally, a better error message is provided if one\nof these dtypes is used and float128 is not supported.\n"
  },
  {
    "path": "changelogs/master/fixed/20200217_legacy_kp_aug_fallback.md",
    "content": "* Fix legacy augmenters (i.e. no `_augment_batch_()`\n  implemented) not automatically falling back to\n  `_augment_keypoints()` for the augmentation of bounding\n  boxes, polygons and line strings. #617 #618"
  },
  {
    "path": "changelogs/master/fixed/20200225_fix_imageio.md",
    "content": "* Fixed a broken `imageio` dependency. The package no longer\n  supports python 3.4 and earlier and will fail to install in the\n  latest version. The dependency is now set to be more\n  restrictive for older python versions. #627 #628\n"
  },
  {
    "path": "changelogs/master/fixed/20200412_fix_change_color_temperature.md",
    "content": "* Fixed `change_color_temperatures_()` crashing on batches\n  that did not contain exactly `1` or `3` images. #646 #650\n"
  },
  {
    "path": "changelogs/master/fixed/20200521_fix_skimage_slic_warning.md",
    "content": "- Fixed an `skimage` deprecation warning in `Superpixels`. #672\n"
  },
  {
    "path": "changelogs/master/fixed/20200522_fix_blend_alpha_f128.md",
    "content": "- A problem was fixed that led to `blend_alpha()`\n  always producing assertion errors if the dtype\n  `float128` was not available on the given\n  system. #678\n"
  },
  {
    "path": "changelogs/master/fixed/20200522_fix_mac_multiprocessing.md",
    "content": "- Fixed an error on MacOS in python 3.7 that could appear\n  when using multicore augmentation. The library will now\n  use `spawn` mode in that situation. The error can thus\n  still appear when using a custom multiprocessing\n  implementation. It is recommended to use python 3.8 on\n  Mac. #673\n"
  },
  {
    "path": "changelogs/master/fixed/20200522_fix_pad_f128.md",
    "content": "- A problem was fixed that led to `pad()` always crashing\n  if the dtype `float128` was not available on the given\n  system. #678\n"
  },
  {
    "path": "changelogs/master/fixed/20200522_fix_permission_denied.md",
    "content": "- Fixed a `permission denied` error when calling\n  `StochasticParameter.draw_distribution_graph()` in\n  Windows. #678\n"
  },
  {
    "path": "changelogs/master/fixed/20200525_fix_affine_cval_float.md",
    "content": "- Fixed `Affine` casting float cvals to int, even when\n  the image had a float dtype, making it impossible to\n  properly use cvals for images with value\n  range `[0.0, 1.0]`. #669 #680\n"
  },
  {
    "path": "changelogs/master/fixed/20200601_fix_affine_skimage_order_0.md",
    "content": "- Fixed a deprecation warning in `Affine` that would\n  be caused when providing boolean images and\n  `order != 0`. #685\n"
  },
  {
    "path": "changelogs/master/improved/20200211_improve_performance_add.md",
    "content": "# Improve Performance of `Add` #608\n\nThis patch improves the performance of\n`imgaug.arithmetic.add_scalar()`\nand the corresponding augmenter `Add` for `uint8` inputs.\nThe expected performance improvement is 1.5x to 6x.\n(More for image arrays with higher widths/heights than\nsmaller ones. More for more channels. More for a single\nscalar added as opposed to channelwise values.)\n\nAdd functions:\n* `imgaug.augmenters.arithmetic.add_scalar_()`.\n"
  },
  {
    "path": "changelogs/master/improved/20200213_improved_blend_performance.md",
    "content": "# Improve Performance of Alpha-Blending #610\n\nThis patch reworks `imgaug.augmenters.blend.blend_alpha()` to\nimprove its performance. In the case of a scalar constant alpha\nvalue and both image inputs (foreground, background) being\n`uint8`, the improved method is roughly 10x faster. In the case\nof one constant alpha value per channel, the expected speedup\nis around 2x to 7x (more for larger images). In the case of\n`(H,W,[C])` alpha masks, the expected speedup is around 1.3x\nto 2.0x (`(H,W)` masks are faster for larger images,\n`(H,W,C)` the other way round).\n\nAdd functions:\n* `imgaug.augmenters.blend.blend_alpha_()`\n"
  },
  {
    "path": "changelogs/master/improved/20200216_add_elementwise_performance.md",
    "content": "# Improve Performance of Elementwise Addition #612\n\nThis patch improves the performance of\n`imgaug.augmenters.arithmetic.add_elementwise()` for `uint8`\nimages. The performance increase is expected to be between\nroughly 1.5x and 5x (more for very dense `value` matrices,\ni.e. for channelwise addition). This change affects\n`AddElementwise`, `AdditiveGaussianNoise`,\n`AdditiveLaplaceNoise` and `AdditivePoissonNoise`.\n"
  },
  {
    "path": "changelogs/master/improved/20200216_improve_multiply_scalar_perf.md",
    "content": "# Improve Performance of `multiply_scalar()` #614\n\nThis patch improves the performance of\n`imgaug.augmenters.arithmetic.multiply_scalar()` for\n`uint8` inputs. The function is now between 1.2x and 7x\nfaster (more for smaller images and channelwise\nmultipliers). This change affects `Multiply`.\n\nAdd functions:\n* `imgaug.augmenters.arithmetic.multiply_scalar_()`.\n"
  },
  {
    "path": "changelogs/master/improved/20200216_improved_mul_elementwise_perf.md",
    "content": "# Improve Performance of Elementwise Multiplication #615\n\nThis patch improves the performance of\n`imgaug.augmenters.arithmetic.multiply_elementwise()`. The\nperformance improvement is roughly between 1.5x and 10x.\nThe effect is stronger for smaller images and denser\nmatrices of multipliers (i.e. `(H,W,C)` instead of `(H,W)`).\nThis change affects `MultiplyElementwise`.\n\nAdd functions:\n* `imgaug.augmenters.arithmetic.multiply_elementwise_()`.\n"
  },
  {
    "path": "changelogs/master/improved/20200217_vectorize_cropandpad.md",
    "content": "# Vectorize `CropAndPad` #619\n\nThis patch vectorizes parts of `CropAndPad`, especially the\nsampling process, leading to an improved performance for large\nbatches.\n\nPreviously, cropping an image below a height and/or width of\n`1` would be prevented by `CropAndPad` *and* a warning was\nraised if it was tried. That warning was now removed, but\nheight/width of at least `1` is still ensured.\n"
  },
  {
    "path": "changelogs/master/improved/20200221_reworked_pooling.md",
    "content": "# Improved Performance of Pooling Operations #622\n\nThis patch improves the performance of pooling operations.\n\nFor `uint8` arrays, `max_pool()` and `min_pool()` are now\nbetween 3x and 8x faster. The improvements are more\nsignificant for larger images and smaller kernel sizes.\nIn-place versions of `max_pool()` and `min_pool()` are also\nadded. Both `MaxPooling` and `MinPooling` now use these\nfunctions.\n\nThe performance of `avg_pool()` for `uint8` is improved by\nroughly 4x to 15x. (More for larger images and smaller\nkernel sizes.)\n\nThe performance of `median_pool()` for `uint8` images is\nimproved by roughly 1.7x to 7x, if the kernel size is 3\nor 5 or if the kernel size is 7, 9, 11, 13 and the image\nsize is 32x32 or less. In both cases the kernel also has to be\nsymmetric.\nIn the case of a kernel size of 3, the performance improvement\nis most significant for larger images. In the case of 5, it\nis fairly even over all kernel sizes. In the case of 7 or higher,\nit is more significant for smaller images.\n\nAdd functions:\n* `imgaug.imgaug.min_pool_()`.\n* `imgaug.imgaug.max_pool_()`.\n"
  },
  {
    "path": "changelogs/master/improved/20200223_blur_avg.md",
    "content": "# Improved Average Bluring #625\n\nThis patch adds `imgaug.augmenters.blur.blur_avg_()`,\nwhich applies an averaging blur kernel to images. The method\nis slightly faster for single image inputs (factor of 1.01x to\n1.1x, more for medium-sized images around `128x128`) than\nthe one used in `AverageBlur`. The performance of `AverageBlur`\nhowever is likely not changed significantly due to input\nvalidation now being done per image instead of per batch.\n\nAdd functions:\n* `imgaug.augmenters.blur.blur_avg_()`\n"
  },
  {
    "path": "changelogs/master/improved/20200223_faster_elastic_tf.md",
    "content": "# Improved Performance of `ElasticTransformation` #624\n\nThis patch applies various performance-related changes to\n`ElasticTransformation`. These cover: (a) the re-use of\ngenerated random samples for multiple images in the same\nbatch (with some adjustments so that they are not identical),\n(b) the caching of generated and re-useable arrays,\n(c) a performance-optimized smoothing method for the\nunderlying displacement maps and (d) the use of nearest\nneighbour interpolation (`order=0`) instead of cubic\ninterpolation (`order=3`) as the new default parameter\nfor `order`.\n\nThese changes lead to a speedup of about 3x to 4x (more\nfor larger images) at a slight loss of visual\nquality (mainly from `order=0`) and variety (due to the\nre-use of random samples within each batch).\nThe new smoothing method leads to slightly stronger\ndisplacements for larger `sigma` values.\n"
  },
  {
    "path": "changelogs/master/improved/20200229_convolve.md",
    "content": "# Improved Convolution Filters #632\n\nThis patch reworks the backend of all convolutional\nfilters. It extracts the convolution logic out of\n`Convolve` and moves it into the new function\n`imgaug.augmenters.convolutional.convolve_()` (with\nnon-in-place version `convolve()`).\n\nThe logic is also reworked so that fewer convolution\nfunction calls and more in-place modification is\nused. This should lead to an improved performance.\n\nThese changes also affect `Sharpen`, `Emboss`,\n`EdgeDetect`, `DirectedEdgeDetect` and `MotionBlur`.\n\nAdd functions:\n* `imgaug.augmenters.convolutional.convolve_()`\n* `imgaug.augmenters.convolutional.convolve()`\n"
  },
  {
    "path": "changelogs/master/improved/20200229_faster_invert.md",
    "content": "# Improved Performance of `invert_()` #631\n\nThis patch improves the performance of\n`imgaug.augmenters.arithmetic.invert_()` for `uint8`\nimages. The update is expected to improve the\nperformance by a factor of 4.5x to 5.3x (more for\nsmaller images) if no threshold is provided and by\n1.5x to 2.7x (more for smaller images) if a threshold\nis provided.\n\nIn both cases these improvements are only realised\nif either no custom minimum and maximum for the\nvalue range is provided or only a custom maximum\nis provided. (This is expected to be the case for most\nusers.)\n\nThese improvements also affect `Invert` and `Solarize`.\n"
  },
  {
    "path": "changelogs/master/improved/20200308_prefetching.md",
    "content": "# Added Automatic Prefetching of Random Number Samples #634\n\nThis patch adds automatic prefetching of random samples,\nwhich performs a single large random sampling call instead\nof many smaller ones. This seems to improve the\nperformance of most augmenters by 5% to 40% for longer\naugmentation sessions (50+ consecutive batches of 128\nexamples each). A few augmenters seem to have gotten\nslightly slower, though these might be measuring errors.\n\nThe prefetching is done by adding a new parameter,\n`imgaug.parameters.AutoPrefetcher`, which prefetches\nsamples from a child parameter.\n\nThe change is expected to have for most augmenters a\nslight negative performance impact if the augmenters\nare used only once and not for multiple batches. For a\nfew augmenters there might be sizeable negative\npeformance impact (due to prefetching falsely being\nperformed). The negative impact can be avoided in\nthese cases by wrapping the augmentation calls in\n`with imgaug.parameters.no_prefetching(): ...`.\n\nThis patch also adds the property `prefetchable` to\n`StochasticParameter`, which defaults to `False` and\ndetermines whether the parameter's outputs may be\nprefetched.\n\nIt further adds to\n`handle_continuous_param()`, `handle_discrete_param()`.\n`handle_categorical_string_param()`,\n`handle_discrete_kernel_size_param()` and\n`handle_probability_param()` in `imgaug.parameters` the\nnew argument `prefetch`. If set to `True` (the default),\nthese functions may now partially or fully wrap their\nresults in `AutoPrefetcher`.\n\nAdd functions:\n* `imgaug.random.RNG.create_if_not_rng_()`\n* `imgaug.parameters.toggle_prefetching()`\n* `imgaug.testutils.is_parameter_instance()`\n* `imgaug.testutils.remove_prefetching()`\n\nAdd properties:\n* `imgaug.parameters.StochasticParameter.prefetchable`\n\nAdd classes:\n* `imgaug.parameters.toggled_prefetching()` (context)\n* `imgaug.parameters.no_prefetching()` (context)\n* `imgaug.parameters.AutoPrefetcher`\n"
  },
  {
    "path": "changelogs/master/improved/20200315_segment_replacement.md",
    "content": "# Improved Performance of Segment Replacement #640 #684\n\nThis patch improves the performance of segment\nreplacement (by average colors within the segments),\nused in `Superpixels` and `segment_voronoi()`.\nThe new method is in some cases (especially small\nimages) up to 100x faster now. For 224x224 images\nthe speed improvement is around 1.4x to 10x,\ndepending on how many segments have to be replaced.\n\nThis change is expected to have a moderate positive\nimpact on `Superpixels` and `segment_voronoi()` (i.e.\n`Voronoi`).\n\nAdded functions:\n* `imgaug.augmenters.segmentation.replace_segments_`\n\nAdded classes:\n* `imgaug.testutils.temporary_constants` (context)\n"
  },
  {
    "path": "changelogs/master/improved/20200413_frequency_noise.md",
    "content": "# Improve Performance of `FrequencyNoise` #651\n\nThis patch improves the performance of\n`imgaug.parameters.FrequencyNoise`, which is used in some\nweather augmenters. The parameter now samples `HxW` arrays\nabout 1.3x to 1.5x faster (more improvement for larger\nimages).\n"
  },
  {
    "path": "changelogs/master/improved/20200517_faster_dtype_checks.md",
    "content": "# Improved Performance of dtype checks #663\n\nThis patch improves the performance of dtype checks\nthroughout the library. The new method verifies input\narrays around 10x to 100x faster than the previous one.\n\nAdd functions:\n* `imgaug.dtypes.gate_dtypes_strs()`\n* `imgaug.dtypes.allow_only_uint8()`\n\nAdd decorators:\n* `imgaug.testutils.ensure_deprecation_warning`\n\nDeprecate functions:\n* `imgaug.dtypes.gate_dtypes()`\n"
  },
  {
    "path": "changelogs/master/improved/20200521_improved_cicd_testing.md",
    "content": "# Improved CI/CD Testing #670 #678\n\nThis patch improves the CI/CD environment by adding\ngithub actions. The library is now automatically tested\nin Ubuntu with python 2.7, 3.5, 3.6, 3.7 and 3.8,\nas well as MacOS and Windows with the same python\nversions (except for 2.7 in Windows).\nPreviously, only Ubuntu with python <=3.7 was\nautomatically tested in the CI/CD chain.\n\nAdditionally, the CI/CD pipeline now also generates\nwheel files (sdist, bdist) for every patch merged\ninto master.\n"
  },
  {
    "path": "changelogs/master/improved/20200522_tests_f128.md",
    "content": "# Removed Requirement of float128 Support from Tests #677\n\nThis patch modifies all tests so that they can be run on\nsystems that do not support the dtype `float128`, such\nas Windows.\n"
  },
  {
    "path": "changelogs/master/improved/20200530_glass_blur_perf.md",
    "content": "# Improved Performance of Glass Blur #683 #684\n\nThis patch improves the performance of\n`imgaug.augmenters.imgcorruptplike.apply_glass_blur()`\nand the corresponding augmenter in python 3.6+.\nThe improvement is around 14x to 45x, depending on\nthe image size (larger images have more speedup).\n\nAdded dependencies:\n* `numba` (requires python 3.6+)\n"
  },
  {
    "path": "changelogs/master/refactored/20200223_blur_gaussian.md",
    "content": "# Refactored `blur_gaussian_()` #626\n\nThis patch cleans up the code of\n`imgaug.augmenters.blur.blur_gaussian_()`. A very small\nperformance improvement (factor ~1.03x) is expected.\n"
  },
  {
    "path": "changelogs/master/refactored/20200314_affine.md",
    "content": "# Refactored Affine #639\n\nThis patch refactors affine to make the code more\nreadable and change the matrix generation routine to\na numpy-based one. It also merges the matrix\ngeneration of `Affine` and `pillike.Affine` and lays\nthe foundation for adding a `center` parameter to\n`Affine`.\n\nThis patch also changes the shear mechanic in\n`Affine`. When shearing on the x-axis, the points at\nthe top are now only moved to the left/right and no\nlonger up/down. Previously, they were also slightly\nmoved up/down. (Analogous for the y-axis.)\n"
  },
  {
    "path": "changelogs/v0.2.8.summary.md",
    "content": "# 0.2.8\n\nThis update focused on extending and documenting the library's dtype support, improving the performance and reworking multicore augmentation.\n\n\n## dtype support\n\nPrevious versions of `imgaug` were primarily geared towards `uint8`.\nIn this version, all augmenters and helper functions were refactored to be more tolerant towards non-uint8 dtypes.\nAdditionally all augmenters were tested with non-uint8 dtypes and an overview of the expected support-level\nis now listed in the documentation on page [dtype support](https://imgaug.readthedocs.io/en/latest/source/dtype_support.html).\nFurther details are listed in the docstrings of each individual augmenter or helper function.\n\n\n## Performance Improvements\n\nBelow are some numbers for the achieved performance improvements compared to 0.2.7.\nThe measurements were taken using realistic 224x224x3 uint8 images and batch size 128.\nThe percentage values denote the increase in bandwidth (i.e. mbyte/sec) of the respective\naugmenter given the described input. Improvements for smaller images, smaller batch sizes\nand non-uint8 dtypes may differ. Augmenters with less than roughly 10% improvement are not\nlisted. While the numbers here are exact, there is some measurement error involved as they\nwere calculated based on a rather low number of 100 repetitions.\n\n* Sequential (with 2x Noop as children) +184% to +276%\n* SomeOf (with 3x Noop as children) +24% to +49%\n* OneOf (with 3x Noop as children) +21%\n* Sometimes (with Noop as child) +23%\n* WithChannels +32%\n* Add +216%\n* AddElementwise +49%\n* AdditiveGaussianNoise +26%\n* AdditiveLaplaceNoise +20%\n* AdditivePoissonNoise +18%\n* Multiply +206%\n* MultiplyElementwise +74%\n* Dropout +154%\n* CoarseDropout +246%\n* ReplaceElementwise +119%\n* ImpulseNoise +333%\n* SaltAndPepper +184%\n* CoarseSaltAndPepper +227%\n* Salt +204%\n* CoarseSalt +260%\n* Pepper +208%\n* CoarsePepper +276%\n* Invert +1192%\n* GaussianBlur +885%\n* AddToHueAndSaturation +48%\n* GammaContrast +2988%\n* SigmoidContrast +519%\n* LogContrast +1048%\n* LinearContrast +448%\n* Convolve +47%\n* Sharpen +29%\n* Emboss +18%\n* EdgeDetect +41%\n* DirectedEdgeDetect +53%\n* Fliplr +75%\n* Flipud +25%\n* Affine +7% to +33%\n* ElasticTransformation +650 to +680%\n* CropAndPad +30% to +77% (from improved padding)\n* Pad +40 to +140%\n* PadToFixedSize +288%\n* KeepSizeByResize (with CropToFixedSize as child) +58%\n* Snowflakes +44%\n* SnowflakesLayer +42%\n\n\n## multicore augmentation\n\nThe implementation for multicore augmentation was completely rewritten and is now a wrapper around python's `multiprocessing.Pool`. Compared to the old version, it is by far less fragile and faster. It is also easier to use. Every augmenter now offers a simple `pool()` method, which can be used to quickly spawn a pool of child workers on multiple CPU cores. Example:\n```python\naug = iaa.PiecewiseAffine(0.2)\nwith aug.pool(processes=-1, seed=123) as pool:\n    batches_aug = pool.imap_batches(batches_generator, chunksize=32)\n    for batch_aug in batches_aug:\n        # do something\n```\nHere, `batches_generator` is a generator that yields instances of `imgaug.Batch`, e.g. something like `imgaug.Batch(images=<numpy array>, keypoints=[imgaug.KeypointsOnImage(...), imgaug.KeypointsOnImage(...), ...])`. The arguement `processes=-1` spawns `N-1` workers, where `N` is the number of CPU cores (includes hyperthreads).\n\nNote that `Augmenter.augment_batches(batches, background=True)` still works and now uses the above `pool()` method.\n\n\n## imgaug.imgaug\n\n* Added constants that control the min/max values for seed generation\n* Improved performance of `pad()`\n    * this change also improves the performance of:\n        * `imgaug.imgaug.pad_to_aspect_ratio()`,\n        * `imgaug.imgaug.HeatmapsOnImage.pad()`,\n        * `imgaug.imgaug.HeatmapsOnImage.pad_to_aspect_ratio()`,\n        * `imgaug.imgaug.SegmentationMapOnImage.pad()`,\n        * `imgaug.imgaug.SegmentationMapOnImage.pad_to_aspect_ratio()`,\n        * `imgaug.augmenters.size.PadToFixedSize`,\n        * `imgaug.augmenters.size.Pad`,\n        * `imgaug.augmenters.size.CropAndPad`\n* Changed `imshow()` to explicitly make the plot figure size dependent on the input image size.\n* Refactored `SegmentationMapOnImage` to have simplified dtype handling in `__init__`\n* Fixed an issue with `SEED_MAX_VALUE` exceeding the `int32` maximum on some systems, causing crashes related to\n  RandomState.\n* Moved BatchLoader to `multicore.py` and replaced the class with an alias pointing to `imgaug.multicore.BatchLoader`.\n* Moved BackgroundAugmenter to `multicore.py` and replaced the class with an alias pointing to `imgaug.multicore.BatchLoader`.\n* Renamed `HeatmapsOnImage.scale()` to `HeatmapsOnImage.resize()`.\n* Marked `HeatmapsOnImage.scale()` as deprecated.\n* Renamed `SegmentationMapOnImage.scale()` to `SegmentationMapOnImage.resize()`.\n* Marked `SegmentationMapOnImage.scale()` as deprecated.\n* Renamed `BoundingBox.cut_out_of_image()` to `BoundingBox.clip_out_of_image()`.\n* Marked `BoundingBox.cut_out_of_image()` as deprecated.\n* Renamed `BoundingBoxesOnImage.cut_out_of_image()` to `BoundingBoxesOnImage.clip_out_of_image()`.\n* Marked `BoundingBoxesOnImage.cut_out_of_image()` as deprecated.\n* Marked `Polygon.cut_out_of_image()` as deprecated. (The analogous clip function existed already.)\n* Renamed in `imgaug.Batch` the attributes storing input data `<attribute>_unaug`, e.g. `imgaug.Batch.images` to `imgaug.Batch.images_unaug` or `imgaug.Batch.keypoints` to `imgaug.Batch.keypoints_unaug`. The old attributes are still accessible, but will raise a DeprecatedWarning.\n\n\n## imgaug.multicore\n\n* Created this file.\n* Moved `BatchLoader` here from `imgaug.py`.\n* Moved `BackgroudAugmenter` here from `imgaug.py`.\n* Marked `BatchLoader` as deprecated.\n* Marked `BackgroundAugmenter` as deprecated.\n* Added class `Pool`. This is the new recommended way for multicore augmentation. `BatchLoader`/`BackgroundAugmenter` should not be used anymore. Example:\n  ```python\n  import imgaug as ia\n  from imgaug import augmenters as iaa\n  from imgaug import multicore\n  import numpy as np\n  aug = iaa.Add(1)\n  images = np.zeros((16, 128, 128, 3), dtype=np.uint8)\n  batches = [ia.Batch(images=np.copy(images)) for _ in range(100)]\n  with multicore.Pool(aug, processes=-1, seed=2) as pool:\n      batches_aug = pool.map_batches(batches, chunksize=8)\n  print(np.sum(batches_aug[0].images_aug[0]))\n  ```\n  The example starts a pool with N-1 workers (N=number of CPU cores) and augments 100 batches using these workers.\n  Use `imap_batches()` to feed in and get out a generator.\n\n\n## imgaug.parameters\n\n* Added `TruncatedNormal`\n* Added `handle_discrete_kernel_size_param()`\n* Improved dtype-related interplay of `FromLowerResolution` and `imresize_many_images()`\n* Improved performance for sampling from `Deterministic` by about 2x\n* Improved performance for sampling from `Uniform` with `a == b`\n* Improved performance for sampling from `DiscreteUniform` with `a == b`\n* Improved performance for sampling from `Laplace` with `scale=0`\n* Improved performance for sampling from `Normal` with `scale=0`\n* Improved performance of `Clip` and improved code style\n* Refactored `float check in force_np_float_dtype()`\n* Refactored `RandomSign`\n* Refactored various unittests to be more flexible with regards to returned dtypes\n* Refactored `StochasticParameter.draw_distribution_graph()` to use internally tempfile-based drawing. Should result in higher-quality outputs.\n* Refactored unittest for `draw_distributions_grid()` to improve performance\n* Fixed in `draw_distributions_grid()` a possible error from arrays with unequal shapes being combined to one array\n* Fixed a problem with `Sigmoid` not returning floats\n* Fixed noise produced by `SimplexNoise` having values below 0.0 or above 1.0\n* Fixed noise produced by `SimplexNoise` being more biased towards 0 than it should be\n\n\n## imgaug.dtypes\n\n* Added new file `imgaug/dtypes.py` and respective test file `test_dtypes.py`.\n* Added `clip_to_dtype_value_range_()`\n* Added `get_value_range_of_dtype()`\n* Added `promote_array_dtypes_()`\n* Added `get_minimal_dtype()`\n* Added `get_minimal_dtypes_for_values()`\n* Added `get_minimal_dtype_by_value_range()`\n* Added `restore_dtypes_()`\n* Added `gate_dtypes()`\n* Added `increase_array_resolutions()`\n* Added `copy_dtypes_for_restore()`\n\n\n## imgaug.augmenters.meta\n\n* Added `estimate_max_number_of_channels()`\n* Added `copy_arrays()`\n* Added an optional parameter `default` to `handle_children_lists()`\n* Enabled `None` as arguments for `Lambda` and made all arguments optional\n* Enabled `None` as arguments for `AssertLambda` and made all arguments optional\n* Improved dtype support of `AssertShape`\n* Improved dtype support of `AssertLambda`\n* Improved dtype support of `Lambda`\n* Improved dtype support of `ChannelShuffle`\n* Improved dtype support of `WithChannels`\n* Improved dtype support of `Sometimes`\n* Improved dtype support of `SomeOf`\n* Improved dtype support of `Sequential`\n* Improved dtype support of `Noop`\n* [breaking, mostly internal] Removed `restore_augmented_images_dtypes()`\n* [breaking, mostly internal] Removed `restore_augmented_images_dtypes_()`\n* [breaking, mostly internal] Removed `restore_augmented_images_dtype()`\n* [breaking, mostly internal] Removed `restore_augmented_images_dtype_()`\n* [breaking, mostly internal] Refactored `Augmenter.augment_images()` and `Augmenter._augment_images()` to default\n  `hooks` to `None`\n    * This will affect any custom implemented augmenters that try to access the hooks argument.\n* [breaking, mostly internal] Refactored `Augmenter.augment_heatmaps()` and `Augmenter._augment_heatmaps()` to default\n  `hooks` to `None`\n    * Same as above for images.\n* [breaking, mostly internal] Refactored `Augmenter.augment_keypoints()` and `Augmenter._augment_keypoints()` to default\n  `hooks` to None\n    * Same as above for images.\n* [breaking, mostly internal] Improved performance of image augmentation for augmenters with children\n    * For calls to `augment_image()`, the validation, normalization and copying steps are skipped if the call is\n      a child call (e.g. a `Sequential` calling `augment_images()` on a child `Add`). Hence, such child calls augment\n      now fully in-place (the top-most call still creates a copy though, so from the user perspective nothing\n      changes). Custom implemented augmenters that rely on child calls to `augment_images()` creating copies will\n      break from this change.\n      For an example `Sequential` containing two `Noop` augmenters, this change improves the performance by roughly 2x\n* [breaking, mostly internal] Improved performance of heatmap augmentation for augmenters with children\n    * Same as above for images.\n    * Speedup is around 2-3x for an exemplary `Sequential` containing two `Noop`s.\n    * This will similarly affect segmentation map augmentation too.\n* [breaking, mostly internal] Improved performance of keypoint augmentation for augmenters with children\n    * Same as above for images.\n    * Speedup is around 1.5-2x for an exemplary `Sequential` containing two Noops.\n    * This will similarly affect bounding box augmentation.\n* [critical] Fixed a bug in the augmentation of empty `KeypointsOnImage` instances that would lead image and keypoint\n  augmentation to be un-aligned within a batch after the first empty `KeypointsOnImage` instance. (#231)\n* Added `pool()` to `Augmenter`. This is a helper to start a `imgaug.multicore.Pool` via `with augmenter.pool() as pool: ...`.\n* Refactored `Augmenter.augment_batches(..., background=True)` to use `imgaug.multicore.Pool`.\n* Changed `to_deterministic()` in `Augmenter` and various child classes to derive its new random state from the augmenter's local random state instead of the global random state.\n* Enabled support for non-list `HeatmapsOnImage` inputs in `Augmenter.augment_heatmaps()`. (Before, only lists were supported.)\n* Enabled support for non-list `SegmentationMapOnImage` inputs in `Augmenter.augment_segmentation_maps()`. (Before, only lists were supported.)\n* Enabled support for non-list `KeypointsOnImage` inputs in `Augmenter.augment_keypoints()`. (Before, only lists were supported.)\n* Enabled support for non-list `BoundingBoxesOnImage` inputs in `Augmenter.augment_bounding_boxes()`. (Before, only lists were supported.)\n\n\n## imgaug.augmenters.arithmetic\n\n* `ContrastNormalization` is now an alias for `LinearContrast`\n* Restricted `JpegCompression` to uint8 inputs. Other dtypes will now produce errors early on.\n* Changed in `Add` the parameter `value` to be continuous and removed its `value_range`\n\n\n## imgaug.augmenters.blend\n\n* Renamed `imgaug.augmenters.overlay` to `imgaug.augmenters.blend`. Functions and classes in `imgaug.augmenters.overlay` are still accessible, but will now raise a DeprecatedWarning.\n* Added `blend_alpha()`.\n* Refactored `Alpha` to be simpler and use `blend_alpha()`.\n* Fixed `Alpha` not having its own `__str__` method.\n* Improved dtype support of `AlphaElementwise`.\n\n\n## imgaug.augmenters.blur\n\n* Added function `blur_gaussian()`\n\n\n## imgaug.augmenters.color\n\n* Added `Lab2RGB` and `Lab2BGR` to `ChangeColorspace`\n* Refactored the main loop in `AddToHueAndSaturation` to make it simpler and faster\n* Fixed `ChangeColorspace` not being able to convert from RGB/BGR to Lab/Luv\n\n\n## imgaug.augmenters.contrast\n\n* Added `AllChannelsCLAHE`\n* Added `CLAHE`\n* Added `AllChannelsHistogramEqualization`\n* Added `HistogramEqualization`\n* Added `_IntensityChannelBasedApplier`\n* Added function `adjust_contrast_gamma()`\n* Added function `adjust_contrast_sigmoid()`\n* Added function `adjust_contrast_log()`\n* Refactored random state handling in `_ContrastFuncWrapper`\n* [breaking, internal] Removed `_PreserveDtype`\n* [breaking, internal] Renamed `_adjust_linear` to `adjust_contrast_linear()`\n\n\n## imgaug.augmenters.convolutional\n\n* Refactored `AverageBlur` to have improved random state handling\n* Refactored `GaussianBlur` to only overwrite input images when that is necessary\n* Refactored `GaussianBlur` to have a simplified main loop\n* Refactored `AverageBlur` to have a simplified main loop\n* Refactored `MedianBlur` to have a simplified main loop\n* Refactored `BilateralBlur` to have a simplified main loop\n* Improved dtype support of `GaussianBlur`\n* Improved dtype support of `AverageBlur`\n* Improved dtype support of `Convolve`\n\n\n## imgaug.augmenters.flip\n\n* Improved dtype support of `Fliplr`\n* Improved dtype support of `Flipud`\n* Refactored `Fliplr` main loop to be more elegant and tolerant\n* Refactored `Flipud` main loop to be more elegant and tolerant\n* Added alias `HorizontalFlip` for `Fliplr`.\n* Added alias `VerticalFlip` for `Flipud`.\n\n\n## imgaug.augmenters.geometric\n\n* `ElasticTransformation`\n    * [breaking, mostly internal] `generate_indices()` now returns only the pixelwise shift as a tuple of x and y\n    * [breaking, mostly internal] `generate_indices()` has no longer a `reshape` argument\n    * [breaking, mostly internal] `renamed generate_indices()` to `generate_shift_maps()`\n    * [breaking, mostly internal] `map_coordinates()` now expects to get the pixelwise shift as its input, instead of\n      the target coordinates\n\n\n## imgaug.augmenters.segmentation\n\n* Improved dtype support of `Superpixels`\n\n\n## imgaug.augmenters.size\n\n* Removed the restriction to `uint8` in `Scale`. The augmenter now supports the same dtypes as `imresize_many_images()`.\n* Fixed missing pad mode `mean` in `Pad` and `CropAndPad`.\n* Improved error messages related to pad mode.\n* Improved and fixed docstrings of `CropAndPad`, `Crop`, `Pad`.\n* Renamed `Scale` to `Resize`.\n* Marked `Scale` as deprecated.\n\n\n## other\n\n* Improved descriptions of the library in `setup.py`\n* `matplotlib` is now an optional dependency of the library and loaded lazily when needed\n* `Shapely` is now an optional dependency of the library and loaded lazily when needed\n* `opencv-python` is now a dependency of the library\n* `setup.py` no longer enforces `cv2` to be installed (to allow installing libraries in random order)\n* Minimum required `numpy` version is now 1.15\n"
  },
  {
    "path": "changelogs/v0.2.9.summary.md",
    "content": "# 0.2.9\n\nThis update mainly covers the following topics:\n* Moved classes/methods related to augmentable data to their own modules.\n* Added polygon augmentation methods.\n* Added line strings and line string augmentation methods.\n* Added easier augmentation interface.\n\n## New 'augmentables' Modules\n\nFor the Polygon and Line String augmentation, new classes and methods had to be\nadded. The previous file for that was `imgaug/imgaug.py`, which however was\nalready fairly large. Therefore, all classes and methods related to augmentable\ndata were split off and moved to `imgaug/augmentables/<type>.py`. The new\nmodules and their main contents are:\n* `imgaug.augmentables.batches`: Contains `Batch`, `UnnormalizedBatch`.\n* `imgaug.augmentables.utils`: Contains utility functions.\n* `imgaug.augmentables.bbs`: Contains `BoundingBox`, `BoundingBoxesOnImage`.\n* `imgaug.augmentables.kps`: Contains `Keypoint`, `KeypointsOnImage`.\n* `imgaug.augmentables.polys`: Contains `Polygon`, `PolygonsOnImage`.\n* `imgaug.augmentables.lines`: Contains `LineString`, `LineStringsOnImage`.\n* `imgaug.augmentables.heatmaps`: Contains `HeatmapsOnImage`.\n* `imgaug.augmentables.segmaps`: Contains `SegmentationMapOnImage`.\n\nCurrently, all augmentable classes can still be created via `imgaug.<type>`,\ne.g. `imgaug.BoundingBox` still works.\n\nChanges related to the new modules:\n* Moved `Keypoint`, `KeypointsOnImage` and `imgaug.imgaug.compute_geometric_median` to `augmentables/kps.py`.\n* Moved `BoundingBox`, `BoundingBoxesOnImage` to `augmentables/bbs.py`.\n* Moved `Polygon`, `PolygonsOnImage` and related classes/functions to `augmentables/polys.py`.\n* Moved `HeatmapsOnImage` to `augmentables/heatmaps.py`.\n* Moved `SegmentationMapOnImage` to `augmentables/segmaps.py`.\n* Moved `Batch` to `augmentables/batches.py`.\n* Added module `imgaug.augmentables.utils`.\n    * Added function `normalize_shape()`.\n    * Added function `project_coords()`.\n* Moved line interpolation functions `_interpolate_points()`, `_interpolate_point_pair()` and `_interpolate_points_by_max_distance()` to `imgaug.augmentables.utils` and made them public functions.\n* Refactored `__init__()` of `PolygonsOnImage`, `BoundingBoxesOnImage`, `KeypointsOnImage` to make use of `imgaug.augmentables.utils.normalize_shape()`.\n* Refactored `KeypointsOnImage.on()` to use `imgaug.augmentables.utils.normalize_shape()`.\n* Refactored `Keypoint.project()` to use `imgaug.augmentables.utils.project_coords()`.\n\n## Polygon Augmentation\n\nPolygons were already part of `imgaug` for quite a while, but couldn't be\naugmented yet. This version adds methods to perform such augmentations.\nIt also makes some changes to the `Polygon` class, see the list of changes\nbelow.\n\nExample for polygon augmentation:\n```python\nimport imgaug as ia\nimport imgaug.augmenters as iaa\nfrom imgaug.augmentables.polys import Polygon, PolygonsOnImage\n\nimage = ia.quokka(size=0.2)\npsoi = PolygonsOnImage([\n    Polygon([(0, 0), (20, 0), (20, 20)])\n], shape=image.shape)\n\nimage_aug, psoi_aug = iaa.Affine(rotate=45).augment(\n    images=[image],\n    polygons=[psoi]\n)\n```\n\nSee [imgaug-doc/notebooks](https://github.com/aleju/imgaug-doc/tree/master/notebooks)\nfor a jupyter notebook with many more examples.\n\nChanges related to polygon augmentation:\n* Added `_ConcavePolygonRecoverer` to `imgaug.augmentables.polys`.\n* Added `PolygonsOnImage` to `imgaug.augmentables.polys`.\n* Added polygon augmentation methods:\n    * Added `augment_polygons()` to `Augmenter`.\n    * Added `_augment_polygons()` to `Augmenter`.\n    * Added `_augment_polygons_as_keypoints()` to `Augmenter`.\n    * Added argument `polygons` to `imgaug.augmentables.batches.Batch`.\n    * Added attributes `polygons_aug` and `polygons_unaug` to `imgaug.augmentables.batches.Batch`.\n    * Added polygon handling to `Augmenter.augment_batches()`.\n* Added property `Polygon.height`.\n* Added property `Polygon.width`.\n* Added method `Polygon.to_keypoints()`.\n* Added optional drawing of corner points to `Polygon.draw_on_image()` and `PolygonsOnImage.draw_on_image()`.\n* Added argument `raise_if_too_far_away=True` to `Polygon.change_first_point_by_coords()`.\n* Added `imgaug.quokka_polygons()` function to generate example polygon data.\n* [rarely breaking] `Polygon.draw_on_image()`, `PolygonsOnImage.draw_on_image()`\n    * Refactored to make partial use `LineString` methods.\n    * Added arguments `size` and `size_perimeter` to control polygon line thickness.\n    * Renamed arguments `alpha_perimeter` to `alpha_line`, `color_perimeter` to `color_line` to align with `LineStrings`.\n    * Renamed arguments `alpha_fill` to `alpha_face` and `color_fill` to `color_face`.\n* [rarely breaking] Changed the output of `Polygon.clip_out_of_image()` from `MultiPolygon` to `list` of `Polygon`.\n  This breaks for anybody who has already used `Polygon.clip_out_of_image()`.\n* Changed `Polygon.exterior_almost_equals()` to accept lists of tuples as argument `other_polygon`.\n* Changed arguments `color` and `alpha` in `Polygon.draw_on_image()` and `PolygonsOnImage.draw_on_image()` to represent\n  the general color and alpha of the polygon. The colors/alphas of the inner area, perimeter and points are derived from\n  `color` and `alpha` (unless `color_inner`, `color_perimeter` or `color_points` are set (analogous for alpha)).\n* Refactored `Polygon.project()` to use `LineString.project()`.\n* Refactored `Polygon.shift()` to use `LineString.shift()`.\n* [rarely breaking] `Polygon.exterior_almost_equals()`, `Polygon.almost_equals()`\n    * Refactored to make use of `LineString.coords_almost_equals()`.\n    * Renamed argument `interpolate` to `points_per_edge`.\n    * Renamed argument `other_polygon` to `other`.\n* Renamed `color_line` to `color_lines`, `alpha_line` to `alpha_lines` in `Polygon.draw_on_image()` and `PolygonsOnImage.draw_on_image()`.\n* Fixed `Polygon.clip_out_of_image(image)` not handling `image` being a tuple.\n* Fixed `Polygon.is_out_of_image()` falsely only checking the corner points of the polygon.\n\n## LineString Augmentation\n\nThis version adds Line String augmentation. Line Strings are simply lines made\nup of consecutive corner points that are connected by straight lines.\nLine strings have similarity with polygons, but do not have a filled inner area\nand are not closed (i.e. first and last coordinate differ).\n\nSimilar to other augmentables, line string are represented with the classes\n`LineString(<iterable of xy-coords>)` and\n`LineStringsOnImage(<iterable of LineString>, <shape of image>)`.\nThey are augmented e.g. via `Augmenter.augment_line_strings(<iterable of LineStringsOnImage>)`\nor `Augmenter.augment(images=..., line_strings=...)`.\n\nExample:\n```python\nimport imgaug as ia\nimport imgaug.augmenters as iaa\nfrom imgaug.augmentables.lines import LineString, LineStringsOnImage\n\nimage = ia.quokka(size=0.2)\nlsoi = LineStringsOnImage([\n    LineString([(0, 0), (20, 0), (20, 20)])\n], shape=image.shape)\n\nimage_aug, lsoi_aug = iaa.Affine(rotate=45).augment(\n    images=[image],\n    line_strings=[lsoi]\n)\n```\n\nSee [imgaug-doc/notebooks](https://github.com/aleju/imgaug-doc/tree/master/notebooks)\nfor a jupyter notebook with many more examples.\n\n## Simplified Augmentation Interface\n\nAugmentation of different data corresponding to the same image(s) has been\na bit convoluted in the past, as each data type had to be augmented on its own.\nE.g. to augment an image and its bounding boxes, one had to first switch the\naugmenters to deterministic mode, then augment the images, then the bounding\nboxes. This version adds methods that perform these steps in one call.\nSpecifically, `Augmenter.augment(...)` is used for that, which has the alias\n`Augmenter.__call__(...)`. One argument can be used for each augmentable,\ne.g. `bounding_boxes=<bounding box data>`.\nExample:\n```python\nimport imgaug as ia\nimport imgaug.augmenters as iaa\nfrom imgaug.augmentables.kps import Keypoint, KeypointsOnImage\n\nimage = ia.quokka(size=0.2)\nkpsoi = KeypointsOnImage([Keypoint(x=0, y=10), Keypoint(x=10, y=5)],\n                         shape=image.shape)\n\nimage_aug, kpsoi_aug = iaa.Affine(rotate=(-45, 45)).augment(\n    image=image,\n    keypoints=kpsoi\n)\n```\nThis will automatically make sure that image and keypoints are rotated by\ncorresponding amounts.\n\nNormalization methods have been added to that class, which allow it to\nprocess many more different inputs than just variations of `*OnImage`.\nExample:\n```python\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\nimage = ia.quokka(size=0.2)\nkps = [(0, 10), (10, 5)]\n\nimage_aug, kps_aug = iaa.Affine(rotate=(-45, 45)).augment(\n    image=image, keypoints=kps)\n```\nExamples for other inputs that are automatically handled by `augment()`:\n* Integer arrays as segmentation maps.\n* Float arrays for heatmaps.\n* `list([N,4] ndarray)` for bounding boxes. (One list for images,\n  then `N` bounding boxes in `(x1,y1,x2,y2)` form.)\n* `list(list(list(tuple)))` for line strings. (One list for images,\n  one list for line strings on the image, one list for coordinates within\n  the line string. Each tuple must contain two values for xy-coordinates.)\n* `list(list(imgaug.augmentables.polys.Polygon))` for polygons.\n  Note that this \"skips\" `imgaug.augmentables.polys.PolygonsOnImage`.\n\nIn **python <3.6**, `augment()` is limited to a maximum of two\ninputs/outputs *and* if two inputs/outputs are used, then one of them must be\nimage data *and* such (augmented) image data will always be returned first,\nindependent of the argument's order.\nE.g. `augment(line_strings=<data>, polygons=<data>)` would be invalid due to\nnot containing image data. `augment(polygons=<data>, images=<data>)` would\nstill return the images first, even though they are the second argument.\n\nIn **python >=3.6**, `augment()` may be called with more than two\narguments and will respect their order.\nExample:\n```python\nimport numpy as np\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\nimage = ia.quokka(size=0.2)\nkps = [(0, 10), (10, 5)]\nheatmap = np.zeros((image.shape[0], image.shape[1]), dtype=np.float32)\nrotate = iaa.Affine(rotate=(-45, 45))\n\nheatmaps_aug, images_aug, kps_aug = rotate(\n    heatmaps=[heatmap],\n    images=[image],\n    keypoints=[kps]\n)\n```\n\nTo use more than two inputs/outputs in python <3.6, add the argument `return_batch=True`,\nwhich will return an instance of `imgaug.augmentables.batches.UnnormalizedBatch`.\n\nChanges related to the augmentation interface:\n* Added `Augmenter.augment()` method.\n* Added `Augmenter.augment_batch()` method.\n    * This method is now called by `Augmenter.augment_batches()` and multicore routines.\n* Added `imgaug.augmentables.batches.UnnormalizedBatch`.\n* Added module `imgaug.augmentables.normalization` for data normalization routines.\n* Changed `augment_batches()`:\n  * Accepts now `UnnormalizedBatch` as input. It is automatically normalized before augmentation and unnormalized afterwards.\n    This allows to use `Batch` instances with non-standard datatypes.\n  * Accepts now single instances of `Batch` (and `UnnormalizedBatch`).\n  * The input may now also be a generator.\n  * The input may now be any iterable instead of just list (arrays or strings are not allowed).\n* Marked support for non-`Batch` (and non-`UnnormalizedBatch`) inputs to `augment_batches()` as deprecated.\n* Refactored `Batch.deepcopy()`\n    * Does no longer verify attribute datatypes.\n    * Allows now to directly change attributes of created copies, e.g. via `batch.deepcopy(images_aug=...)`.\n\n## Other Additions, Changes and Refactorings\n\nKeypoint augmentation\n* Added method `Keypoint.draw_on_image()`.\n* [mildly breaking] Added an `alpha` argument to `KeypointsOnImage.draw_on_image()`. This can break code that relied on\n  the order of arguments of the method (though will usually only have visual consequences).\n* `KeypointsOnImage` and `Keypoint` copying:\n    * Added optional arguments `keypoints` and `shape` to `KeypointsOnImage.deepcopy()`.\n    * Added optional arguments `keypoints` and `shape` to `KeypointsOnImage.copy()`.\n    * Added method `Keypoint.copy()`.\n    * Added method `Keypoint.deepcopy()`.\n        * Refactored methods in `Keypoint` to use `deepcopy()` to create copies of itself (instead of instantiating new instances via `Keypoint(...)`).\n    * `KeypointsOnImage.deepcopy()` now uses `Keypoint.deepcopy()` to create Keypoint copies, making it more flexible.\n    * Refactored `KeypointsOnImage` to use `KeypointsOnImage.deepcopy()` in as many methods as possible to create copies of itself.\n    * Refactored `Affine`, `AffineCv2`, `PiecewiseAffine`, `PerspectiveTransform`, `ElasticTransformation`, `Rot90` to use `KeypointsOnImage.deepcopy()` and `Keypoint.deepcopy()` during keypoint augmentation.\n* Changed `Keypoint.draw_on_image()` to draw a rectangle for the keypoint so long as *any* part of that rectangle is within the image plane.\n  (Previously, the rectangle was only drawn if the integer xy-coordinate of the point was inside the image plane.)\n* Changed `KeypointsOnImage.draw_on_image()` to raise an error if an input image has shape `(H,W)`.\n* Changed `KeypointsOnImage.draw_on_image()` to handle single-number inputs for `color`.\n* `KeypointsOnImage.from_coords_array()`\n    * Marked as deprecated.\n    * Renamed to `from_xy_array()`.\n    * Renamed arg `coords` to `xy`.\n    * Changed the method from `staticmethod` to `classmethod`.\n    * Refactored to make code simpler.\n* `KeypointsOnImage.get_coords_array()`\n    * Marked as deprecated.\n    * Renamed to `to_xy_array()`.\n* Refactored `KeypointsOnImage.draw_on_image()` to use `Keypoint.draw_on_image()`.\n\nHeatmap augmentation\n* Changed `Affine`, `PiecewiseAffine`, `ElasticTransformation` to always use `order=3` for heatmap augmentation.\n* Changed check in `HeatmapsOnImage` that validates whether the input array is within the desired value range `[min_value, max_value]`\n  from a hard exception to a soft warning (with clipping). Also improved the error message a bit.\n\nDeprecation warnings:\n* Added `imgaug.imgaug.DeprecationWarning`. The builtin python `DeprecationWarning` is silent since 2.7, which is why now a separate deprecation warning is used.\n* Added `imgaug.imgaug.warn_deprecated()`.\n    * Refactored deprecation warnings to use this function.\n* Added `imgaug.imgaug.deprecated` decorator.\n    * Refactored deprecation warnings to use this decorator.\n\nBounding Boxes:\n* Added to `BoundingBox.extract_from_image()` the arguments `pad` and `pad_max`.\n* Changed `BoundingBox.contains()` to also accept `Keypoint`.\n* Changed `BoundingBox.project(from, to)` to also accept images instead of shapes.\n* Renamed argument `thickness` in `BoundingBox.draw_on_image()` to `size` in order to match the name used for keypoints, polygons and line strings.\n  The argument `thickness` will still be accepted, but raises a deprecation warning.\n* Renamed argument `thickness` in `BoundingBoxesOnImage.draw_on_image()` to `size` in order to match the name used for keypoints, polygons and line strings.\n  The argument `thickness` will still be accepted, but raises a deprecation warning.\n* Refactored `BoundingBox` to reduce code repetition.\n* Refactored `BoundingBox.extract_from_image()`. Improved some code fragments that looked wrong.\n* Refactored `BoundingBoxesOnImage.draw_on_image()` to improve efficiency by evading unnecessary array copies.\n\nOther:\n* [rarely breaking] Added arguments `cval` and `mode` to `PerspectiveTransform` (PR #301).\n  This breaks code that relied on the order of the arguments and used `keep_size`, `name`, `deterministic` or `random_state` as positional arguments.\n* Added `dtypes.clip_()` function.\n* Added function `imgaug.imgaug.flatten()` that flattens nested lists/tuples.\n* Changed `PerspectiveTransform` to ensure minimum height and width of output images (by default `2x2`).\n  This prevents errors in polygon augmentation (possibly also in keypoint augmentation).\n* Refactored `imgaug.augmenters.blend.blend_alpha()` to no longer enforce a channel axis for foreground and background image.\n* Refactored `imgaug/parameters.py` to reorder classes within the file.\n* Re-allowed numpy 1.16 in `requirements.txt`.\n\n\n## Fixes\n\n* Fixed possible crash in `blend.blend_alpha()` if dtype numpy.float128 does not exist.\n* Fixed a crash in `ChangeColorspace` when `cv2.COLOR_Lab2RGB` was actually called `cv2.COLOR_LAB2RGB` in the local OpenCV installation (analogous for BGR). (PR #263)\n* Fixed `ReplaceElementwise` always sampling replacement per channel.\n* Fixed an error in `draw_text()` due to arrays that could not be set to writeable after drawing the text via PIL.\n* Fixed errors in docstring of `parameters.Subtract`.\n* Fixed a division by zero bug in `angle_between_vectors()`.\n* Augmentation of empty `KeypointsOnImage` instances\n    * Fixed `Rot90` not changing `KeypointsOnImage.shape` if `.keypoints` was empty.\n    * Fixed `Affine` not changing `KeypointsOnImage.shape` if `.keypoints` was empty.\n    * Fixed `PerspectiveTransform` not changing `KeypointsOnImage.shape` if `.keypoints` was empty.\n    * Fixed `Resize` not changing `KeypointsOnImage.shape` if `.keypoints` was empty.\n    * Fixed `CropAndPad` not changing `KeypointsOnImage.shape` if `.keypoints` was empty. (Same for `Crop`, `Pad`.)\n    * Fixed `PadToFixedSize` not changing `KeypointsOnImage.shape` if `.keypoints` was empty.\n    * Fixed `CropToFixedSize` not changing `KeypointsOnImage.shape` if `.keypoints` was empty.\n    * Fixed `KeepSizeByResize` not changing `KeypointsOnImage.shape` if `.keypoints` was empty.\n* Fixed `Affine` heatmap augmentation producing arrays with values outside the range `[0.0, 1.0]` when `order` was set to `3`.\n* Fixed `PiecewiseAffine` heatmap augmentation producing arrays with values outside the range `[0.0, 1.0]` when `order` was set to `3`.\n* Fixed assert in `SegmentationMapOnImage` falsely checking if max class index is `<= nb_classes` instead of `< nb_classes`.\n* Fixed an issue in `dtypes.clip_to_value_range_()` and `dtypes.restore_dtypes_()` causing errors when clip value range exceeded array dtype's value range.\n* Fixed an issue in `dtypes.clip_to_value_range_()` and `dtypes.restore_dtypes_()` when the input array was scalar, i.e. had shape `()`.\n* Fixed a Permission Denied error when using `JpegCompression` on windows (possibly also affected other systems). #297\n"
  },
  {
    "path": "changelogs/v0.3.0.summary.md",
    "content": "# 0.3.0 - Summary Of Changes\n\n## Improved Segmentation Map Augmentation (#302)\n\nThe segmentation map augmentation was previously previously a wrapper\naround heatmap augmentation. This patch introduces independent methods\nfor segmentation map augmentation. This makes the augmentation of such\ninputs faster and more memory efficient. The internal representation (int\ninstead of floats) also becomes more intuitive.\n\nThis improvement leads to some **breaking changes**. To adapt to the new\nversion, the following steps should be sufficient for most users:\n\n* Rename all calls of `SegmentationMapOnImage` to `SegmentationMapsOnImage`\n  (Map -> Maps).\n* Rename all calls of `SegmentationMapsOnImage.get_arr_int()` to\n  `SegmentationMapsOnImage.get_arr()`.\n* Remove the argument `nb_classes` from all calls of `SegmentationMapsOnImage`.\n* Remove the argument `background_threshold` from all\n  calls as it is no longer supported.\n* Remove the argument `draw_foreground_mask` from all calls of\n  `SegmentationMapsOnImage.draw_on_image()` as it is no longer supported.\n* Ensure that the input array to `SegmentationMapsOnImage` is always an\n  int-like (int, uint or bool). Float arrays are now deprecated.\n* Adapt all calls `SegmentationMapsOnImage.draw()` and\n  `SegmentationMapsOnImage.draw_on_image()`, as both of these now return a\n  list of drawn images instead of a single array. (For a segmentation map\n  array of shape `(H,W,C)` they return `C` drawn images. In most cases `C=1`,\n  so simply call `draw()[0]` or `draw_on_image()[0]`.)\n* Ensure that if `SegmentationMapsOnImage.arr` is accessed anywhere, the\n  respective code can handle the new `int32` `(H,W,#maps)` array form.\n  Previously, it was `float32` and the channel-axis had the same size as the\n  max class id (+1) that could appear in the map.\n* Ensure that calls of `<augmenter>.augment()` or `<augmenter>()` that\n  provide segmentation maps as numpy arrays (i.e. bypassing\n  `SegmentationMapsOnImage`) use the shape `(N,H,W,#maps)` as\n  `(N,H,W)` is no longer supported.\n\n\n## New RNG System (#375, #408)\n\nnumpy 1.17 introduces a new API for random number generation. This patch\nadapts `imgaug` to automatically use the new API if it is available and\nfall back to the old one otherwise. To achieve that, the module\n`imgaug.random` is introduced, containing the new standard random number\ngenerator `imgaug.random.RNG`. You can create a new RNG using a seed value\nvia `RNG(seed)` and it will take care of the rest. It supports all sampling\nfunctions that `numpy.random.RandomState` and `numpy.random.Generator`\nsupport. This new random number generator is now supposed to be used\nwherever previously `numpy.random.RandomState` would have been used.\n(For most users, this shouldn't change anything. Integer seeds are\nstill supported. If you used `RandomState` anywhere, that is also still\nsupported.) \n\n\n**Breaking changes** related to this patch:\n* imgaug now uses a different seed at each run of the library. Previously,\n  a fixed seed was used for each run, leading to the same agumentations. That\n  confused some users as it differed from numpy's behaviour.\n  The new \"dynamic\" seed is derived from numpy's seed and hence seeding numpy\n  will also lead to imgaug being seeded. (It is not recommended to rely on\n  that behaviour as it might be changed in the future. Use\n  `imgaug.random.seed()` to set a custom seed.)\n* The constants `imgaug.SEED_MIN_VALUE` and `imgaug.SEED_MAX_VALUE` were\n  removed. They are now in `imgaug.random`.\n* The constant `imgaug.CURRENT_RANDOM_STATE` was removed.\n  Use `imgaug.random.get_global_rng()` instead.\n\n\n## Other Changes Related to numpy 1.17 (#302)\n\nnumpy 1.17 uses a new implementation of `clip()`, which turns `int64` values\ninto `float64` values. As a result, it is no longer safe to use `int64` in\nmany augmenters and other functions/methods and hence these inputs are now\nrejected. This affects at least `ReplaceElementwise` and thereby `Dropout`,\n`CoarseDropout`, `Salt`, `Pepper`, `SaltAndPepper`, `CoarseSalt`,\n`CoarsePepper` and `CoarseSaltAndPepper`. See the ReadTheDocs documentation\npage about dtype support for more details.\n\nIn relation to this change, parameters in `imgaug.parameters` that previously\nreturned `int64` were modified to now return `int32` instead. Analogously,\n`float64` results were changed to `float32`.\n\n\n## New Augmenters\n\nThe following new augmenters were added to the library:\n\n**Canny edge detection** (#316):\n* `imgaug.augmenters.edges.Canny`. Performs canny edge detection and colorizes\n  the resulting binary image in random ways.\n\n**Pooling** (#317):\n* `imgaug.augmenters.edges.AveragePooling`. Performs average pooling using a\n  given kernel size. Very similar to `AverageBlur`.\n* `imgaug.augmenters.edges.MaxPooling`. Performs maximum pooling using a\n  given kernel size.\n* `imgaug.augmenters.edges.MinPooling`. Analogous.\n* `imgaug.augmenters.edges.MedianPooling`. Analogous.\n\n**Hue and Saturation** (#210, #319):\n* `imgaug.augmenters.color.WithHueAndSaturation`. Apply child augmenters to\n  images in `HSV` colorspace. Automatically accounts for the hue being in\n  angular representation.\n* `imgaug.augmenters.color.AddToHue`. Adds a defined value to the hue of each\n  pixel in input images.\n* `imgaug.augmenters.color.AddToSaturation`. Adds a defined value to the\n  saturation of each pixel in input images. \n* `imgaug.augmenters.color.MultiplyHueAndSaturation`. Multiplies the hue and/or\n  saturation of all pixels in input images.\n* `imgaug.augmenters.color.MultiplyHue`. Analogous, affects always only the hue.\n* `imgaug.augmenters.color.MultiplySaturation`. Analogous, affects always only\n  the saturation.\n\n**Color Quantization** (#347):\n* `imgaug.augmenters.color.UniformColorQuantization`. Uniformly splits all\n  possible colors into `N` different ones, then finds for each pixel in an\n  image among the `N` colors the most similar one and replaces that pixel's\n  color with the quantized color. \n* `imgaug.augmenters.color.KMeansColorQuantization`. Groups all colors in an\n  each into `N` different ones using k-Means clustering. Then replaces each\n  pixel'S color, analogously to `UniformColorQuantization`.\n\n**Voronoi** (#348):\n* `imgaug.augmenters.segmentation.Voronoi`. Queries a point sampler to\n  generate a large number of `(x,y)` coordinates on an image. Each such\n  coordinate becomes a voronoi cell. All pixels within the voronoi cell\n  are replaced by their average color. (Similar to `Superpixels`, this\n  augmenter also supports to only replace `p%` of all cells with their\n  average color.)\n* `imgaug.augmenters.segmentation.UniformVoronoi`. Shortcut to call `Voronoi`\n  with a uniform points sampler. That sampler places `N` points on an image\n  using uniform distributions (i.e. they are randomly spread over the image.)\n* `imgaug.augmenters.segmentation.RegularGridVoronoi`. Shortcut to call\n  `Voronoi` with a regular grid points sampler. That points sampler generates\n  coordinate on a regular grid with `H` rows and `W` cols. Some of these points\n  can be randomly dropped to generate a less regular pattern.\n* `imgaug.augmenters.segmentation.RelativeRegularGridVoronoi`. Same as\n  `RegularGridVoronoi`, but instead of using absolute numbers for `H` and `W`,\n  they are defined as relative amounts w.r.t. image shapes, leading to more\n  rows/cols on larger images.\n\n\n## New Augmentation Functions\n\nOne of the long term goals of the library is to move as much augmentation\nlogic as possible out of `Augmenter` instances and into functions. This\npatch therefore adds several new augmentation functions:\n* `imgaug.min_pool()`. #369\n* `imgaug.median_pool()`. #369\n* `augmenters.segmentation.segment_voronoi()`. #348\n* `augmenters.flip.fliplr()`. #385\n* `augmenters.flip.flipud()`. #385\n* `augmenters.color.change_colorspace_()`. #409\n* `augmenters.color.change_colorspace_batch_()`. #409\n* `augmenters.arithmetic.add_scalar()`. #411\n* `augmenters.arithmetic.add_elementwise()`. #411\n* `augmenters.arithmetic.replace_elementwise_()`. #411\n* `augmenters.arithmetic.compress_jpg()`. #411\n\n\n## Colorspace Changes (#409)\n\nThe color space naming within the library had become rather messy in the\npast as there were many colorspace-related augmenters, with some of them\nnot using constants for colorspace names/IDs and others defining their own\nones. This patch introduces a unified colorspace naming system for which the\nfollowing constants were added:\n* `imgaug.CSPACE_RGB` \n* `imgaug.CSPACE_BGR`\n* `imgaug.CSPACE_GRAY`\n* `imgaug.CSPACE_CIE`\n* `imgaug.CSPACE_YCrCb`\n* `imgaug.CSPACE_HSV`\n* `imgaug.CSPACE_HLS`\n* `imgaug.CSPACE_Lab`\n* `imgaug.CSPACE_Luv`\n* `imgaug.CSPACE_YUV`\n* `imgaug.CSPACE_ALL`\n\nAll colorspace-related augmenters should now support these constants.\n\nAdditionally, support for rarely used colorspaces -- mainly `CIE`, `YCrCb`,\n`Luv` and `YUV` -- was previously unverified or non-existent. These colorspaces\nare now tested for the underlying transformation functions and should be\nsupported by most colorspace-related augmenters. (Some augmenters may still\ndefine their own subset of actually sensible colorspaces and only accept\nthese.)\n\n\n## Setting limits on memory usage of background augmentation (#305, #417)\n\nThe methods `imap_batches()` and `imap_batches_unordered()` of\n`imgaug.multicore.Pool` have now the new argument `output_buffer_size`.\nThe argument set the maximum number of batches that may be handled anywhere\nin the augmentation pipeline at a given time (i.e. in the steps \"loaded and\nwaiting\", \"in augmentation\" or \"augmented and waiting\"). It denotes the\n*total* number of batches over *all* processes. Setting this argument to\nan integer value avoids situations where `Pool` eats up all the available\nmemory due to the data loading and augmentation running faster than the\ntraining.\n\n`Augmenter.augment_batches()` now uses a default value of `10*C`\nfor `output_buffer_size`, where `C` is the number of available logical CPU\ncores.\n\n\n## Performance Related Changes\n\nThe algorithms for `Fliplr` and `Flipud` were reworked to be as fast as\npossible. In practice this should have no noticeable effects as both augmenters\nwere already very fast. (#385)\n\nFurthermore, all assert statements within the library were changed from\n`do_assert()` to standard `assert` statements. This is a bit less secure\n(as `assert` statements can be optimized away), but should have a small\npositive impact on the performance. (#387)\n\nLarge parts of the library were also refactored to reduce code duplication\nand decrease the complexity of many functions. This should make future\nimprovements easier, but is expected to have a very small negative impact on\nthe performance due to an increased number of function calls.\nIt is also expected that numpy 1.17 can make some operations slower. This\nis because (a) creating and copying random number generaters has become slower\nand (b) `clip()` overall seems to be slower.\n\n\n## Improved Error Messages (#366, #367, #387)\n\nimgaug uses quite many `assert` statements and other checks on input data\nto fail early instead of late. This is supposed to improve usability, but that\ngoal was not always reached as many errors had no associated error\nmessages. This patch changes that. Now, all `assert` statements and other\nchecks have an associated error message. This should protect users from having\nto wade through the library's code in order to understand the root cause of\nerrors.\n\n\n## (Almost) All Augmenters Are Now Classes (#396) \n\nSome augmenters were previously defined as functions returning other\naugmenters with appropriate settings. This could lead to confusing effects,\nwhere seemingly instantiating an augmenters would lead to the instantiation\nof a completely different augmenter. Hence, most of these augmenters were\nswitched from functions to classes. (The classes are now inheriting from the\npreviously returned augmenters, i.e. `instanceof` checks should still work.)\nThis affects: `AdditiveGaussianNoise`, `AdditiveLaplaceNoise`,\n`AdditivePoissonNoise`, `Dropout`, `CoarseDropout`, `ImpulseNoise`,\n`SaltAndPepper`, `CoarseSaltAndPepper`, `Salt`, `CoarseSalt`, `Pepper`,\n`CoarsePepper`, `SimplexNoiseAlpha`, `FrequencyNoiseAlpha`, `MotionBlur`,\n`MultiplyHueAndSaturation`, `MultiplyHue`, `MultiplySaturation`, `AddToHue`,\n`AddToSaturation`, `Grayscale`, `GammaContrast`, `SigmoidContrast`,\n`LogContrast`, `LinearContrast`, `Sharpen`, `Emboss`, `EdgeDetect`,\n`DirectedEdgeDetect`, `OneOf`, `AssertLambda`, `AssertShape`, `Pad`, `Crop`,\n`Clouds`, `Fog` and `Snowflakes`.\n\nNot yet switched are: `InColorspace` (deprecated),\n`ContrastNormalization` (deprecated), `HorizontalFlip` (pure alias\nfor `Fliplr`), `VerticalFlip` (pure alias for `Flipud`)\nand `Scale` (deprecated).\n\n\n## Augmenters are now more robust towards unusual axis-sizes (#428, #433)\n\nFeeding images with height and/or width of `0` or a channel axis of size `0`\ninto augmenters would previously often result in crashes. This was also the\ncase for input arrays with more than `512` channels. Some of these errors\nalso included segmentation faults or endlessly hanging programs. Most\naugmenters and helper functions were modified to be more robust towards\nsuch unusual inputs and will no longer crash.\n\nIt is still good practice to avoid such inputs. Note e.g. that some helper\nfunctions -- like drawing routines -- may still crash. The unittests\ncorresponding to this change also only cover image data. Using other inputs,\ne.g. segmentation maps, might still induce problems.\n\n\n## Other New Functions\n\nThe following (public) functions were added to the library (not listing\nfunctions that were already mentioned above):\n* Added `imgaug.is_np_scalar()`. #366\n* Added `dtypes.normalize_dtypes()`. #366\n* Added `dtypes.normalize_dtype()`. #366\n* Added `dtypes.change_dtypes_()`. #366\n* Added `dtypes.change_dtype_()`. #366\n* Added `dtypes.increase_itemsize_of_dtype()`. #366\n* Added `imgaug.warn()` function. #367\n* Added `imgaug.compute_paddings_to_reach_multiples_of()`. #369\n* Added `imgaug.pad_to_multiples_of()`. #369\n* Added `augmentables.utils.copy_augmentables`. #410\n* Added `validation.convert_iterable_to_string_of_types()`. #413\n* Added `validation.is_iterable_of()`. #413\n* Added `validation.assert_is_iterable_of()`. #413\n* Added `random.supports_new_rng_style()`. #375\n* Added `random.get_global_rng()`. #375\n* Added `random.seed()`. #375\n* Added `random.normalize_generator()`. #375\n* Added `random.normalize_generator_()`. #375\n* Added `random.convert_seed_to_generator()`. #375\n* Added `random.convert_seed_sequence_to_generator()`. #375\n* Added `random.create_pseudo_random_generator_()`. #375\n* Added `random.create_fully_random_generator()`. #375\n* Added `random.generate_seed_()`. #375\n* Added `random.generate_seeds_()`. #375\n* Added `random.copy_generator()`. #375\n* Added `random.copy_generator_unless_global_generator()`. #375\n* Added `random.reset_generator_cache_()`. #375\n* Added `random.derive_generator_()`. #375\n* Added `random.derive_generators_()`. #375\n* Added `random.get_generator_state()`. #375\n* Added `random.set_generator_state_()`. #375\n* Added `random.is_generator_equal_to()`. #375\n* Added `random.advance_generator_()`. #375\n* Added `random.polyfill_integers()`. #375\n* Added `random.polyfill_random()`. #375\n\n\n## Other New Classes and Interfaces\n\nThe following (public) classes were added (not listing classes that were\nalready mentioned above):\n* Added `augmenters.edges.IBinaryImageColorizer`. #316\n* Added `augmenters.edges.RandomColorsBinaryImageColorizer`. #316\n* Added `augmenters.segmentation.IPointsSampler`. #348\n* Added `augmenters.segmentation.RegularGridPointsSampler`. #348\n* Added `augmenters.segmentation.RelativeRegularGridPointsSampler`. #348\n* Added `augmenters.segmentation.DropoutPointsSampler`. #348\n* Added `augmenters.segmentation.UniformPointsSampler`. #348 \n* Added `augmenters.segmentation.SubsamplingPointsSampler`. #348\n* Added `testutils.ArgCopyingMagicMock`. #413\n\nThe image colorization is used for `Canny` to turn binary images into color\nimages.\nThe points samplers are currently used within `Voronoi`.\n\n\n## Refactorings\n\nDue to fast growth of the library in the past, a significant amount of messy\ncode had accumulated. To fix that, a lot of time was spend to refactor the code\nthroughout the whole library to reduce code duplication and improve the\ngeneral quality. This also included a rewrite of many outdated docstrings.\nThere is still quite some mess remaining, but the current state should make\nit somewhat easier to add future improvements.\n\nAs part of the refactorings, a few humongously large unittests were also\nsplit up into many smaller tests. The library has now around 3000\nunique unittests (i.e. each unittest function is counted once, even it is\ncalled many times with different parameters). \n\nRelated PRs:\n * #302, #319, #328, #329, #330, #331, #332, #333, #334, #335, #336, #351,\n   #352, #353, #354, #355, #356, #359, #362, #366, #367, #368, #369, #389,\n   #397, #401, #402, #403, #407, #409, #410, #411, #413, #419\n\n\n## Deprecated\n\nThe following functions/classes/arguments are now deprecated:\n* Function `imgaug.augmenters.meta.clip_augmented_image_`.\n  Use `imgaug.dtypes.clip_()` or `numpy.clip()` instead. #398\n* Function `imgaug.augmenters.meta.clip_augmented_image`.\n  Use `imgaug.dtypes.clip_()` or `numpy.clip()` instead. #398\n* Function `imgaug.augmenters.meta.clip_augmented_images_`.\n  Use `imgaug.dtypes.clip_()` or `numpy.clip()` instead. #398\n* Function `imgaug.augmenters.meta.clip_augmented_images`.\n  Use `imgaug.dtypes.clip_()` or `numpy.clip()` instead. #398\n* Function `imgaug.normalize_random_state`.\n  Use `imgaug.random.normalize_generator` instead. #375\n* Function `imgaug.current_random_state`.\n  Use `imgaug.random.get_global_rng` instead. #375\n* Function `imgaug.new_random_state`.\n  Use class `imgaug.random.RNG` instead. #375\n* Function `imgaug.dummy_random_state`.\n  Use `imgaug.random.RNG(1)` instead. #375\n* Function `imgaug.copy_random_state`.\n  Use `imgaug.random.copy_generator` instead.\n* Function `imgaug.derive_random_state`.\n  Use `imgaug.random.derive_generator_` instead. #375\n* Function `imgaug.normalize_random_states`.\n  Use `imgaug.random.derive_generators_` instead. #375\n* Function `imgaug.forward_random_state`.\n  Use `imgaug.random.advance_generator_` instead. #375\n* Augmenter `imgaug.augmenters.arithmetic.ContrastNormalization`.\n  Use `imgaug.augmenters.contrast.LinearContrast` instead. #396\n* Argument `X` in `imgaug.augmentables.kps.compute_geometric_median()`.\n  Use argument `points` instead. #402\n* Argument `cval` in `imgaug.pool()`, `imgaug.avg_pool()` and\n  `imgaug.max_pool()`. Use `pad_cval` instead. #369\n\n\n## Dependencies\n\nThe following changes were made to the dependencies of the library:\n* Increased minimum version requirement for `scikit-image` to\n  `0.14.2`. #377, #399\n* Changed dependency `opencv-python` to `opencv-python-headless`.\n  This should improve support for some system without GUIs. #324\n* Added dependency `pytest-subtests` for the library's unittests. #366\n\n\n## conda-forge\n\nThe library was added to `conda-forge` so that it can now be installed via\n`conda install imgaug`. (The conda-forge channel must be added first,\nsee installation docs or README.) #320 #339\n\n\n## Fixes\n\n* Fixed an issue with `Polygon.clip_out_of_image()`,\n  which would lead to exceptions if a polygon had overlap with an image,\n  but not a single one of its points was inside that image plane. \n* Fixed `multicore` methods falsely not accepting\n  `augmentables.batches.UnnormalizedBatch`.\n* `Rot90` now uses subpixel-based coordinate remapping.\n  I.e. any coordinate `(x, y)` will be mapped to `(H-y, x)` for a rotation by\n  90deg.\n  Previously, an integer-based remapping to `(H-y-1, x)` was used.\n  Coordinates are e.g. used by keypoints, bounding boxes or polygons.\n* `augmenters.arithmetic.Invert`\n    * [rarely breaking] If `min_value` and/or `max_value` arguments were\n      set, `uint64` is no longer a valid input array dtype for `Invert`.\n      This is due to a conversion to `float64` resulting in loss of resolution.\n    * Fixed `Invert` in rare cases restoring dtypes improperly.\n* Fixed `dtypes.gate_dtypes()` crashing if the input was one or more numpy\n  scalars instead of numpy arrays or dtypes.\n* Fixed `augmenters.geometric.PerspectiveTransform` producing invalid\n  polygons (more often with higher `scale` values). #338\n* Fixed errors caused by `external/poly_point_isect_py2py3.py` related to\n  floating point inaccuracies (changed an epsilon from `1e-10` to `1e-4`,\n  rounded some floats). #338\n* Fixed `Superpixels` breaking when a sampled `n_segments` was `<=0`.\n  `n_segments` is now treated as `1` in these cases.\n* Fixed `ReplaceElementwise` both allowing and disallowing dtype `int64`. #346\n* Fixed `BoundingBox.deepcopy()` creating only shallow copies of labels. #356\n* Fixed `dtypes.change_dtypes_()` #366\n    * Fixed argument `round` being ignored if input images were a list.\n    * Fixed failure if input images were a list and dtypes a single numpy\n      dtype function.\n* Fixed `dtypes.get_minimal_dtype()` failing if argument `arrays` contained\n  not *exactly* two items. #366\n* Fixed calls of `CloudLayer.get_parameters()` resulting in errors. #309\n* Fixed `SimplexNoiseAlpha` and `FrequencyNoiseAlpha` not handling\n  `sigmoid` argument correctly. #343\n* Fixed `SnowflakesLayer` crashing for grayscale images. #345\n* Fixed `Affine` heatmap augmentation crashing for arrays with more than\n  four channels and `order!=0`. #381\n* Fixed an outdated error message in `Affine`. #381\n* Fixed `Polygon.clip_out_of_image()` crashing if the intersection between\n  polygon and image plane was an edge or point. #382\n* Fixed `Polygon.clip_out_of_image()` potentially failing for polygons\n  containing two or fewer points. #382\n* Fixed `Polygon.is_out_of_image()` returning wrong values if the image plane\n  was fully contained inside the polygon with no intersection between the\n  image plane and the polygon edge. #382\n* Fixed  `Fliplr` and `Flipud` using for coordinate-based inputs and image-like\n  inputs slightly different conditions for when to actually apply\n  augmentations. #385\n* Fixed `Convolve` using an overly restrictive check when validating inputs\n  for `matrix` w.r.t. whether they are callables. The check should now also\n  support class methods (and possibly various other callables). #407\n* Fixed `CropAndPad`, `Pad` and `PadToFixedSize` still clipping `cval` samples\n  to the `uint8`. They now clip to the input array's dtype's value range. #407\n* Fixed `WithColorspace` not propagating polygons to child augmenters. #409\n* Fixed `WithHueAndSaturation` not propagating segmentation maps and polygons\n  to child augmenters. #409\n* Fixed `AlphaElementwise` to blend coordinates (for keypoints, polygons,\n  line strings) on a point-by-point basis following the image's average\n  alpha value in the sampled alpha mask of the point's coordinate.\n  Previously, the average over the whole mask was used and then either all\n  points of the first branch or all of the second branch were used as the\n  augmentation output. This also affects `SimplexNoiseAlpha` and\n  `FrequencyNoiseAlpha`. #410\n* Fixed many augmenters and helper functions producing errors if the height,\n  width and/or channels of input arrays were exactly `0` or the channels\n  were `>512`. See further above for more details. #433\n* Fixed `Rot90` not supporting `imgaug.ALL`. #434\n* Fixed `PiecewiseAffine` possibly generating samples for non-image data\n  when using `absolute_scale=True` that were not well aligned with the\n  corresponding images. #437\n"
  },
  {
    "path": "changelogs/v0.4.0.summary.md",
    "content": "# Table of Contents\n\n1. [Overview](#overview)\n2. [Example Images](#example_images)\n3. [Mixed-Category Patches](#mixed_category_patches)\n4. [Added](#added)\n5. [Changed](#changed)\n6. [Refactored](#refactored)\n7. [Fixed](#fixed)\n\n\n<a name=\"overview\"/>\n\n# Overview\n\nRelease `0.4.0` focused mainly on adding new augmenters and improving\nthe internal augmentation \"backend\".\n\nThe following augmenters were added (see the\n[overview](https://imgaug.readthedocs.io/en/latest/source/overview_of_augmenters.html)\ndocs for more details):\n* `ChangeColorTemperature`: Gives images a red, orange or blue touch.\n* New Brightness augmenters: `WithBrightnessChannels`,\n  `MultiplyAndAddToBrightness`, `MultiplyBrightness`, `AddToBrightness`.\n* New Dropout augmenters: `Dropout2d`, `TotalDropout`.\n* `RemoveSaturation`: Decreases the saturation of colors. Effects are similar\n  to `Grayscale`.\n* `Cartoon`: Applies a cartoon-style to images (classical / not-learned).\n* `MeanShiftBlur`: Blurs images using a mean-shift clustering method.\n  (Note: Very slow.)\n* `Jigsaw`: Splits the image into rectangular cells and randomly switches\n  some pairs of neighbouring cells. (Note: Does not support bounding boxes,\n  polygons and line strings.)\n* `WithPolarWarping`: Transforms images to polar coordinate space and applies\n  child augmenters there.\n* `SaveDebugImageEveryNBatches`: Generates and saves at every `N`-th batch a\n  debug image visualizing all inputs within the batch. Useful to gauge\n  strength and effects of augmentations and quickly spot errors in ground truth\n  data (e.g. misaligned bounding boxes).\n* `Cutout`: Removes rectangular subregions of images. Has some similarity with\n  `CoarseDropout`.\n* `Rain` and `RainLayer`: Adds rain-like effects to images.\n* `RandAugment`: Combination of multiple augmenters. Similar to the paper\n  description. (Note: Can currently only augment images.)\n* `Identity`: Same as `Noop`. Does nothing.\n* `UniformColorQuantizationToNBits`: Quantizes each image array component down\n  to `N` bits. Similar to `UniformColorQuantization`. Has the alias\n  `Posterize`.\n* `Solarize`: Invert with threshold.\n* `RemoveCBAsByOutOfImageFraction`, `ClipCBAsToImagePlanes`: Augmenters to\n  remove or clip coordinate-based augmentables, e.g. bounding boxes\n* More blend augmenters:\n  * `BlendAlphaMask`: Uses batch-wise generated masks for alpha-blending.\n  * `BlendAlphaSomeColors`: Alpha-blends only within image regions having\n    specific randomly chosen colors.\n  * `BlendAlphaSegMapClassIds`: Alpha-blends only within image regions having\n    specific class ids in segmentation maps.\n  * `BlendAlphaBoundingBoxes`: Alpha-blends only within image regions covered\n    by bounding boxes having specific labels.\n  * `BlendAlphaHorizontalLinearGradient`: Alpha-blends using horizontal\n    linear gradients.\n  * `BlendAlphaVerticalLinearGradient`: Analogous.\n  * `BlendAlphaRegularGrid`: Places a regular grid on each image and\n    samples one alpha value per grid cell. Can be used e.g. to achieve\n    coarse dropout.\n  * `BlendAlphaCheckerboard`: Places also a regular grid on each image,\n    but neighbouring cells use alpha values that are inverse to each other.\n* Shortcuts for `Affine`: `ScaleX`, `ScaleY`, `TranslateX`, `TranslateY`,\n  `Rotate`, `ShearX`, `ShearY`.\n* Several new crop and pad augmenters: `CenterCropToFixedSize`,\n  `CenterPadToFixedSize`, `CropToMultiplesOf`, `CenterCropToMultiplesOf`,\n  `PadToMultiplesOf`, `CenterPadToMultiplesOf`, `CropToPowersOf`,\n  `CenterCropToPowersOf`, `PadToPowersOf`, `CenterPadToPowersOf`,\n  `CropToAspectRatio`, `CenterCropToAspectRatio`, `PadToAspectRatio`,\n  `CenterPadToAspectRatio`, `PadToSquare`, `CenterPadToSquare`,\n  `CropToSquare`, `CenterCropToSquare`.\n* Wrappers around `imagecorruptions` package (verified to have identical\n  outputs):\n  `GaussianNoise`, `ShotNoise`, `ImpulseNoise`, `SpeckleNoise`,\n  `GaussianBlur`, `GlassBlur`, `DefocusBlur`, `MotionBlur`, `ZoomBlur`,\n  `Fog`, `Frost`, `Snow`, `Spatter`, `Contrast`, `Brightness`, `Saturate`,\n  `JpegCompression`, `Pixelate`, `ElasticTransform`.\n  The augmenters are accessible via `iaa.imgcorruptlike.<AugmenterName>`.\n* Wrappers around `PIL` functions (verified to have identical outputs):\n  `Solarize`, `Posterize`, `Equalize`, `Autocontrast`, `EnhanceColor`,\n  `EnhanceContrast`, `EnhanceBrightness`, `EnhanceSharpness`, `FilterBlur`,\n  `FilterSmooth`, `FilterSmoothMore`, `FilterEdgeEnhance`,\n  `FilterEdgeEnhanceMore` `FilterFindEdges`, `FilterContour`,\n  `FilterEmboss`, `FilterSharpen`, `FilterDetail`, `Affine`.\n  The augmenters are accessible via `iaa.pillike.<AugmenterName>`.\n\nAside from these new augmenters, the following major changes were made:\n* Bounding boxes and line strings have now native augmentation methods.\n  Previously, they were converted to keypoints at the start of the\n  augmentation, which meant that child augmenters were unable to use\n  augmentation routines geared towards these two input types as they would\n  merely see a bunch of keypoints.\n* Augmentation happens now batchwise. Previously it was done\n  input type-wise. This change should improve performance for batches with\n  different types of inputs by re-using computation results for multiple\n  inputs. It also makes the library more flexible.\n* Improved the default parameters of augmenters. Most of them will now\n  produce medium-strength augmentations when instantiated without and\n  parameters. E.g. `CoarseDropout()` will now produce decent augmentations\n  instead of doing nothing. When using default parameters,\n  `Fliplr()` and `Flipud()` will always flip (p=100%).\n  `TotalDropout()` will always drop everything (p=100%).\n  `Grayscale()` and `RemoveSaturation()` will always fully grayscale/desaturate.\n  `Rot90()` will always rotate once (clockwise). `Invert()` will always\n  invert all components (p=100%).\n* Reworked the standard parameters shared by all augmenters.\n  `random_state` was renamed to `seed`, e.g. `Affine(..., seed=1)`\n  is now valid. The parameter `deterministic` is now deprecated. \n* Many methods were added to augmentables, e.g. `BoundingBoxesOnImage`\n  now supports index-based access (`bbs[0]` instead\n  of `bbs.bounding_boxes[0]`).\n* The bounding box drawing methods now also draw each BB's label.\n* All augmenters are now tested to be pickle-able without errors.\n* The library is now compatible with numpy 1.18 and python 3.8.\n* This release fixes two significant bugs in `Affine` that could lead\n  to unaligned outputs. It also fixes significant bugs related to\n  bounding box augmentation and various other issues. The update\n  is recommended. There are now around 5000 unique tests.\n\n\n<a name=\"example_images\"/>\n\n# Example Images\n\n## Brightness\n\nThree new brightness-related augmenters are introduced. The example below\nshows `AddToBrightness`. First image is the input, the others show\n`AddToBrightness(-100)` to `AddToBrightness(100)`.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/addtobrightness.jpg?raw=true\" width=\"800\" />\n\n\n## Cartoon\n\nA new cartoon style filter is introduced, shown below. Each row starts with\nthe input image.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/cartoon.jpg?raw=true\" width=\"800\" />\n\n\n## ChangeColorTemperature\n\nThe color temperature of images can now be modified. The example below\nshows `ChangeColorTemperature(kelvin=1000)` to\n`ChangeColorTemperature(kelvin=5000)`, with the first image being the input.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/changecolortemperature.jpg?raw=true\" width=\"800\" />\n\n\n## Cutout\n\nCutout is added to the library. The first row shows the hyperparameters that\nwere used in the corresponding paper. The second row shows two cutout\niterations per image, using intensity values, random RGB values and gaussian\nnoise to fill in the pixels. First image in each row is the input.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/cutout.jpg?raw=true\" width=\"800\" />\n\n\n## Dropout2d and TotalDropout\n\nTwo new dropout augmenters, `Dropout2d` and `TotalDropout`, are added.\nThe example below shows `Dropout2d`. First image is the input.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/dropout2d.jpg?raw=true\" width=\"800\" />\n\n\n## Jigsaw\n\nA jigsaw puzzle augmenter is added. The first row below shows its effects\nusing a grid size of `5x5`. The second row shows `10x10`.\nFirst image in each row is the input.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/jigsaw.jpg?raw=true\" width=\"800\" />\n\n\n## MeanShiftBlur\n\nA mean shift-based blur augmenter is added. First image below shows the input,\nfollowed by `MeanShiftBlur(5.0)` to `MeanShiftBlur(40.0)`.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/meanshiftblur.jpg?raw=true\" width=\"800\" />\n\n\n## Posterize\n\nThe example below shows `Posterize` (aka `UniformQuantizationToNBits`)\nwith `n_bits=8` to `n_bits=1`. First image is the input.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/posterize.jpg?raw=true\" width=\"800\" />\n\n\n## Solarize\n\nThe example below shows `Solarize`, which is the same as `Invert` with a\nthreshold. First image is the input.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/solarize.jpg?raw=true\" width=\"800\" />\n\n\n## Rain\n\nThe example below shows the new `Rain` augmenter. First image is the input.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/rain.jpg?raw=true\" width=\"800\" />\n\n\n## RandAugment\n\nThis release adds an implementation of `RandAugment` the following example\nshows `RandAugment(n=2, m=20)`. First image is the input.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/randaugment.jpg?raw=true\" width=\"800\" />\n\n\n## WithPolarWarping\n\nThe example below shows `WithPolarWarping(<children>)` in combination with\n`CropAndPad` (first row), `Affine` (second row) and\n`AveragePooling` (third row). First image in each row is the input.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/withpolarwarping.jpg?raw=true\" width=\"800\" />\n\nThe augmenter supports all input types, but bounding boxes and polygons\nshould be used with caution. (Bounding boxes, because they tend to produce\nunintuitive results in combination with rotation-like augmentations.\nPolygons, because they can become invalid under geometric augmentations\nand will have to be repaired, which can easily mess them up.)\n\n\n## imagecorruptions wrappers\n\nWrappers around the library `imagecorruptions` are added, which contains\naugmentation methods introduced by `Hendrycks and Dietterich - Benchmarking\nNeural Network Robustness to Common Corruptions and Surface Variations`.\nThe methods were used in some recent papers. The example below shows their\neffects, always with `severity=3`.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/imgcorruptlike.jpg?raw=true\" width=\"800\" />\n\n\n## PIL wrappers\n\nVarious wrappers around popular `PIL` methods are added.\nThe image below shows in the first row `Autocontrast`, in the second\n`EnhanceColor` (strength of `0.1` to `1.9`), the third\n`EnhanceSharpness` (strength of `0.1` to `1.9`), the fourth shows\nvarious convolution-based filters (`FilterBlur`, `FilterSmooth`,\n`FilterEdgeEnhance`, `FilterFindEdges`, `FilterContour`, `FilterSharpen`,\n`FilterDetail` -- in that order) and the fourth row shows `pillike.Affine`\nwith the top-left as the transformation origin.\nThe first image in each row is the input.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/pillike.jpg?raw=true\" width=\"800\" />\n\n\n## More Blending Augmenters\n\nVarious new (alpha-)blending augmenters are introduced in this patch.\n\nThe following example makes use of a segmentation map in which all cars\nare marked with a segmentation class id.\nIt uses roughly\n`BlendAlphaSegMapClassIds(BlendAlphaSomeColors(AddToHueAndSaturation(...)))`\nin order to modify some colors within the car classes.\nLeft is the input image, right is the output:\n\n<p float=\"left\">\n  <img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/annotated/cityscapes5.png\" width=\"380\" />\n  <img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/cityscapes5-car-lights-changed.jpg?raw=true\" width=\"380\" />\n</p>\n\nNote that `BlendAlphaSegMapClassIds` must be called with all inputs\nat the same time, e.g. via `augmenters(images=..., segmentation_maps...)`.\n\nThis example changes the train color using\n`BlendAlphaSegMapClassIds(AddToHueAndSaturation(...))`. The train has a\nseparate class in the segmentation map.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/cityscapes5-train-color.jpg?raw=true\" width=\"380\" />\n\nThe next example applies blending to some non color-based augmenters. It uses\nroughly `BlendAlphaSegMapClassIds(AdditiveGaussianNoise(...))` (left)\nand `BlendAlphaSegMapClassIds(Emboss(...))` (right). The street has a separate\nclass in the segmentation map.\n\n<p float=\"left\">\n  <img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/cityscapes5-street-gaussian-noise.jpg?raw=true\" width=\"380\" />\n  <img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/cityscapes5-street-embossed.jpg?raw=true\" width=\"380\" />\n</p>\n\nThis example shows how blending can be used to achieve dropout effects.\nIt uses roughly `BlendAlphaRegularGrid(Multiply(0.0))` (left) and\n`BlendAlphaCheckerboard(Multiply(0.0))` (right).\n\n<p float=\"left\">\n  <img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/cityscapes5-regular-grid-dropout.jpg?raw=true\" width=\"380\" />\n  <img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/cityscapes5-checkerboard-dropout.jpg?raw=true\" width=\"380\" />\n</p>\n\nThis example shows `BlendAlphaSomeColors(RemoveSaturation(1.0))`, applied\nto a more colorful image:\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/blendalphasomecolors_removesaturation.jpg?raw=true\" width=\"800\" />\n\nThis release also adds `BlendAlphaBoundingBoxes`,\n`BlendAlphaHorizontalLinearGradient` and\n`BlendAlphaVerticalLinearGradient`. These are not visualized here.\n\n\n## SaveDebugImageEveryNBatches\n\nA new debug helper -- `SaveDebugImageEveryNBatches` -- was added.\nThe example below shows one of its outputs for a batch containing images,\nsegmentation maps and bounding boxes.\n\n<img src=\"https://github.com/aleju/imgaug-doc/blob/master/images/changelogs/0.4.0/savedebugimageeverynbatches.jpg?raw=true\" width=\"512\" />\n\nNote that this augmenter must be called with all inputs at the same time,\ne.g. via `augmenters(images=..., segmentation_maps...)` for image + segmap\ninputs.\n\n\n<a name=\"mixed_category_patches\"/>\n\n# Mixed-Category Patches\n\n## Reworked Augmentation Methods [#451](https://github.com/aleju/imgaug/pull/451) [#566](https://github.com/aleju/imgaug/pull/566)\n\nThe internal backend of the library was changed so that augmentation now\nhappens batchwise instead of input-type-wise. Child augmenters still have\nthe option of using input-type-wise augmentation. All calls are now at some\npoint routed through `Augmenter.augment_batch_()` and child augmenters are\nexpected to implement `_augment_batch_()`. This change allows to re-use\ninformation between different input types within the same batch, which in\nturn improves performance and extends the space of possible augmentations.\n\nNote: It is now recommended to use a batch-wise augmentation call. I.e.\nuse `.augment_batch_()` or `.augment()` or `.__call__()`. These calls provide\nall inputs of a batch at the same time and several of the new augmenters now\nexplicitly require that (e.g. `BlendAlphaBoundingBoxes`). Example:\n```python\nimport numpy as np\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\nimages = [np.zeros((32, 32, 3), dtype=np.uint8),\n          np.zeros((64, 64, 3), dtype=np.uint8)]\nbbs = [\n    [ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)],\n    [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4),\n     ia.BoundingBox(x1=2, y1=3, x2=4, y2=5)],\n]\n\nbbsois = [ia.BoundingBoxesOnImage(bbs[0], shape=images[0]),\n          ia.BoundingBoxesOnImage(bbs[1], shape=images[1])]\n\naug = iaa.Affine(rotate=(-30, 30))\n\n# No longer recommended:\naug_det = aug.to_deterministic()\nimages_aug = aug_det.augment_images(images)\nbbsois_aug = aug_det.augment_bounding_boxes(bbsois)\n\n# Now recommended:\nimages_aug, bbs_aug = aug(images=images, bounding_boxes=bbs)\n\n```\n\n* Added methods:\n  * `augmentables.batches.Batch.to_normalized_batch()`.\n  * `augmentables.batches.Batch.get_augmentables()`.\n  * `augmentables.batches.UnnormalizedBatch.get_augmentables()`.\n  * `augmentables.batches.Batch.get_augmentable_names()`.\n  * `augmentables.batches.UnnormalizedBatch.get_augmentable_names()`.\n  * `augmentables.batches.Batch.to_batch_in_augmentation()`.\n  * `augmentables.batches.Batch.fill_from_batch_in_augmentation_()`.\n  * `augmentables.batches.UnnormalizedBatch.fill_from_augmented_normalized_batch()`.\n  * `augmenters.meta.Augmenter.augment_batch_()`, ,\n    similar to `augment_batch()`, but explicitly works in-place and has a\n    `parent` parameter.\n  * `augmenters.meta.Augmenter._augment_batch_()`.\n  * `augmentables.polys.recover_psois_()`.\n  * `augmentables.utils.convert_cbaois_to_kpsois()`.\n  * `augmentables.utils.invert_convert_cbaois_to_kpsois_()`.\n  * `augmentables.utils.deepcopy_fast()`.\n  * `augmentables.bbs.BoundingBox.from_point_soup()`.\n  * `augmentables.bbs.BoundingBoxesOnImages.from_point_soups()`.\n  * Added method `to_xy_array()` to:\n    * `augmentables.bbs.BoundingBoxesOnImage`.\n    * `augmentables.polys.PolygonsOnImage`.\n    * `augmentables.lines.LineStringsOnImage`.\n  * Added methods `to_keypoints_on_image()`,\n    `invert_to_keypoints_on_image_()` and `fill_from_xy_array_()` to:\n    * `augmentables.kps.KeypointsOnImage`.\n    * `augmentables.bbs.BoundingBoxesOnImage`.\n    * `augmentables.polys.PolygonsOnImage`.\n    * `augmentables.lines.LineStringsOnImage`.\n\n* Added classes:\n  * `testutils.TemporaryDirectory` (context)\n\n* Changed:\n  * Changed the following methods to be thin wrappers around\n    `augment_batch_()`:\n    * `augmenters.meta.Augmenter.augment_images()`\n    * `augmenters.meta.Augmenter.augment_heatmaps()`.\n    * `augmenters.meta.Augmenter.augment_segmentation_maps()`.\n    * `augmenters.meta.Augmenter.augment_keypoints()`.\n    * `augmenters.meta.Augmenter.augment_bounding_boxes()`.\n    * `augmenters.meta.Augmenter.augment_polygons()`.\n    * `augmenters.meta.Augmenter.augment_line_strings()`.\n  * Changed `augment_image()`, `augment_images()`, `augment_heatmaps()`,\n    `augment_segmentation_maps()`, `augment_keypoints()`,\n    `augment_bounding_boxes()`, `augment_polygons()` and\n    `augment_line_strings()` to return `None` inputs without change.\n    Previously they resulted in an exception. This is more consistent with\n    the behaviour in the other `augment_*` methods.\n  * Changed `augment_images()` to no longer be abstract. It defaults\n    to not changing the input images.\n  * Changed `imgaug.augmentables.BoundingBoxesOnImage.from_xyxy_array()`\n    to also accept `(N, 2, 2)` arrays instead of only `(N, 4)`.\n\nDeprecated:\n  * Deprecated `imgaug.augmenters.meta.Augmenter.augment_batch()`.\n    Use `.augment_batch_()` instead.\n\nRefactored:\n  * Refactored most augmenters to use single `_augment_batch_()` method.\n\nOther changes:\n  * Added validation of input arguments to `KeypointsOnImage.from_xy_array()`.\n  * Improved validation of input arguments to\n    `BoundingBoxesOnImage.from_xyxy_array()`.\n\n\n## Reworked Quantization [#467](https://github.com/aleju/imgaug/pull/467)\n\nThis patch reworked the quantization routines to also support quantization\nto `N` bits instead of `N` colors in a way that is similar to posterization\nin `PIL`. The patch added corresponding `UniformColorQuantizationToNBits`\nand `Posterize` augmenters, as well as a `quantize_uniform_to_n_bits()`\nfunction.\n\n* Added classes:\n  * `augmenters.color.UniformColorQuantizationToNBits`.\n  * `augmenters.color.Posterize` (alias of `UniformColorQuantizationToNBits`).\n\n* Added functions:\n  * `augmenters.color.quantize_uniform_()`, the in-place\n    version of `quantize_uniform()`.\n  * `augmenters.color.quantize_uniform_to_n_bits()`.\n  * `augmenters.color.quantize_uniform_to_n_bits_()`.\n  * `augmenters.color.posterize()`, an alias of\n    `quantize_uniform_to_n_bits()` that produces the same outputs as\n    `PIL.ImageOps.posterize()`.\n\n* Added parameters:\n  * Added `to_bin_centers=True` to `quantize_uniform()`, controling\n    whether each bin `(a, b)` should be quantized to `a + (b-a)/2` or `a`.\n\n* Deprecated:\n  * Renamed `imgaug.augmenters.color.quantize_colors_uniform(image, n_colors)`\n    to `imgaug.augmenters.color.quantize_uniform(arr, nb_bins)`. The old name\n    is now deprecated.\n  * Renamed `imgaug.augmenters.color.quantize_colors_kmeans(image, n_colors)`\n    to `imgaug.augmenters.color.quantize_kmeans(arr, nb_clusters)`. The old\n    name is now deprecated.\n\n* Other changes:\n  * Improved performance of `quantize_uniform()` by roughly 10x (small images\n    around 64x64) to 100x (large images around 1024x1024). This also affects\n    `UniformColorQuantization`.\n  * Improved performance of `UniformColorQuantization` by using more in-place\n    functions.\n\n* Fixed:\n  * Fixed `quantize_uniform()` producing wrong outputs for non-contiguous\n    arrays.\n\n\n## Improved Invert [#469](https://github.com/aleju/imgaug/pull/469)\n\nAdded thresholds to `Invert` and the corresponding functions. This enables\nsolarization (inversion with thresholds). The patch also added\na corresponding `Solarize` augmenter and two solarization functions.\n\n* Added augmenter `imgaug.augmenters.Solarize`, a wrapper around `Invert`.\n* Added function `imgaug.augmenters.arithmetic.solarize()`, a wrapper around\n  `solarize_()`.\n* Added function `imgaug.augmenters.arithmetic.solarize_()`, a wrapper around\n  `invert_()`.\n* Added function `imgaug.augmenters.arithmetic.invert_()`, an in-place version\n  of `imgaug.augmenters.arithmetic.invert()`.\n* Added parameters `threshold` and `invert_above_threshold` to\n  `imgaug.augmenters.arithmetic.invert()`\n* Added parameters `threshold` and `invert_above_threshold` to\n  `imgaug.augmenters.arithmetic.Invert`.\n* Improved performance of `imgaug.augmenters.arithmetic.invert()` and\n  `imgaug.augmenters.arithmetic.Invert` for `uint8` images.\n\n\n## All Augmenters are now Pickle-able [#493](https://github.com/aleju/imgaug/pull/493) [#575](https://github.com/aleju/imgaug/pull/575)\n\nEnsured that all augmenters can be pickled and un-pickled without errors.\n\n* Added function `imgaug.testutils.runtest_pickleable_uint8_img()`.\n* Fixed `imgaug.augmenters.blur.MotionBlur` not being pickle-able.\n* Fixed `imgaug.augmenters.meta.AssertLambda` not being pickle-able.\n* Fixed `imgaug.augmenters.meta.AssertShape` not being pickle-able.\n* Fixed `imgaug.augmenters.color.MultiplyHueAndSaturation` not supporting\n  all standard RNG datatypes for `random_state`.\n\n\n## Extended Cropping and Padding Augmenters [#459](https://github.com/aleju/imgaug/pull/459)\n\nThis patch extended the cropping and padding augmenters. It added\naugmenters that crop/pad towards multiples of values (e.g. crop the\nwidth until it is a multiple of `2`), towards powers of values (e.g.\ncrop the width until it is one of `1, 2, 4, 8, 16, ...`), towards\nan aspect ratio (crop the width or height until `width/height = 2.0`) or\ntowards a squared size (e.g. crop the width or height until they are equal).\nThese augmenters are wrappers around `CropToFixedSize` and\n`PadToFixedSize`. All `*FixedSize` augmenters also have now corresponding\n`Center*ToFixedSize` aliases, e.g. `CenterCropToPowersOf`. These\nare equivalent to using `position=\"center\"`, e.g. `CenterCropToPowersOf`\nis equivalent to `CropToPowersOf(..., position=\"center\")`. \n\nThe following functions were moved. Their old names are now deprecated.\n* Moved `imgaug.imgaug.pad` to `imgaug.augmenters.size.pad`\n* Moved `imgaug.imgaug.pad_to_aspect_ratio` to\n  `imgaug.augmenters.size.pad_to_aspect_ratio`.\n* Moved `imgaug.imgaug.pad_to_multiples_of` to\n  `imgaug.augmenters.size.pad_to_multiples_of`.\n* Moved `imgaug.imgaug.compute_paddings_for_aspect_ratio` to\n  `imgaug.augmenters.size.compute_paddings_to_reach_aspect_ratio`.\n* Moved `imgaug.imgaug.compute_paddings_to_reach_multiples_of`\n  to `imgaug.augmenters.size.compute_paddings_to_reach_multiples_of`.\n\n\nThe following augmenters were added:\n* Added augmenter `CenterCropToFixedSize`.\n* Added augmenter `CenterPadToFixedSize`.\n* Added augmenter `CropToMultiplesOf`.\n* Added augmenter `CenterCropToMultiplesOf`.\n* Added augmenter `PadToMultiplesOf`.\n* Added augmenter `CenterPadToMultiplesOf`.\n* Added augmenter `CropToPowersOf`.\n* Added augmenter `CenterCropToPowersOf`.\n* Added augmenter `PadToPowersOf`.\n* Added augmenter `CenterPadToPowersOf`.\n* Added augmenter `CropToAspectRatio`.\n* Added augmenter `CenterCropToAspectRatio`.\n* Added augmenter `PadToAspectRatio`.\n* Added augmenter `CenterPadToAspectRatio`.\n* Added augmenter `PadToSquare`.\n* Added augmenter `CenterPadToSquare`.\n* Added augmenter `CropToSquare`.\n* Added augmenter `CenterCropToSquare`.\n\nAll `Center<name>` augmenters are wrappers around `<name>` with parameter\n`position=\"center\"`.\n\n\nAdded functions:\n* Added function\n  `imgaug.augmenters.size.compute_croppings_to_reach_aspect_ratio()`.\n* Added function\n  `imgaug.augmenters.size.compute_croppings_to_reach_multiples_of()`.\n* Added function\n  `imgaug.augmenters.size.compute_croppings_to_reach_powers_of()`.\n* Added function\n  `imgaug.augmenters.size.compute_paddings_to_reach_powers_of()`.\n\n\nOther changes:\n* Extended augmenter `CropToFixedSize` to support `height` and/or `width`\n  parameters to be `None`, in which case the respective axis is not changed.\n* Extended augmenter `PadToFixedSize` to support `height` and/or `width`\n  parameters to be `None`, in which case the respective axis is not changed.\n* [rarely breaking] Changed `CropToFixedSize.get_parameters()` to also\n  return the `height` and `width` values.\n* [rarely breaking] Changed `PadToFixedSize.get_parameters()` to also\n  return the `height` and `width` values.\n* [rarely breaking] Changed the order of parameters returned by\n  `PadToFixedSize.get_parameters()` to match the order in\n  `PadToFixedSize.__init__()`\n* Changed `PadToFixedSize` to prefer padding the right side over the left side\n  and the bottom side over the top side. E.g. if using a center pad and\n  `3` columns have to be padded, it will pad `1` on the left and `2` on the\n  right. Previously it was the other way round. This was changed to establish\n  more consistency with the various other pad and crop methods.\n* Changed the projection of pad/crop values between images and non-images\n  to make the behaviour slightly more accurate in fringe cases.\n* Improved behaviour of function\n  `imgaug.augmenters.size.compute_paddings_for_aspect_ratio()` for zero-sized\n  axes.\n* Changed function `imgaug.augmenters.size.compute_paddings_for_aspect_ratio()`\n  to also support shape tuples instead of only ndarrays.\n* Changed function\n  `imgaug.augmenters.size.compute_paddings_to_reach_multiples_of()`\n  to also support shape tuples instead of only ndarrays.\n\n\nFixes:\n* Fixed a formatting error in an error message of\n  `compute_paddings_to_reach_multiples_of()`.\n\n\n## More Choices for Image Blending [#462](https://github.com/aleju/imgaug/pull/462) [#556](https://github.com/aleju/imgaug/pull/556)\n\nThe available augmenters for alpha-blending of images were\nsignificantly extended. There are now new blending\naugmenters available to alpha-blend acoording to:\n* Some randomly chosen colors. (`BlendAlphaSomeColors`)\n* Linear gradients. (`BlendAlphaHorizontalLinearGradient`,\n  `BlendAlphaVerticalLinearGradient`)\n* Regular grids and checkerboard patterns. (`BlendAlphaRegularGrid`,\n  `BlendAlphaCheckerboard`)\n* Only at locations that overlap with specific segmentation class\n  IDs (or the inverse of that). (`BlendAlphaSegMapClassIds`)\n* Only within bounding boxes with specific labels (or the inverse\n  of that). (`BlendAlphaBoundingBoxes`)\n\nThis allows to e.g. randomly remove some colors while leaving\nother colors unchanged (`BlendAlphaSomeColors(Grayscale(1.0))`),\nto change the color of some objects\n(`BlendAlphaSegMapClassIds(AddToHue((-256, 256)))`), to add\ncloud-patterns only to the top of images\n(`BlendAlphaVerticalLinearGradient(Clouds())`) or to apply\naugmenters in some coarse rectangular areas (e.g.\n`BlendAlphaRegularGrid(Multiply(0.0))` to achieve a similar\neffect to `CoarseDropout` or\n`BlendAlphaRegularGrid(AveragePooling(8))` to pool in equally\ncoarse image sub-regions).\n\nOther mask-based alpha blending techniques can be achieved by\nsubclassing `IBatchwiseMaskGenerator` and providing an\ninstance of such a class to `BlendAlphaMask`.\n\nThis patch also changes the naming of the blending augmenters\nas follows:\n* `Alpha` -> `BlendAlpha`\n* `AlphaElementwise` -> `BlendAlphaElementwise`\n* `SimplexNoiseAlpha` -> `BlendAlphaSimplexNoise`\n* `FrequencyNoiseAlpha` -> `BlendAlphaFrequencyNoise`\nThe old names are now deprecated.\nFurthermore, the parameters `first` and `second`, which were\nused by all blending augmenters, have now the names `foreground`\nand `background`.\n\nList of changes:\n* Added `imgaug.augmenters.blend.BlendAlphaMask`, which uses\n  a mask generator instance to generate per batch alpha masks and\n  then alpha-blends using these masks.\n* Added `imgaug.augmenters.blend.BlendAlphaSomeColors`.\n* Added `imgaug.augmenters.blend.BlendAlphaHorizontalLinearGradient`.\n* Added `imgaug.augmenters.blend.BlendAlphaVerticalLinearGradient`.\n* Added `imgaug.augmenters.blend.BlendAlphaRegularGrid`.\n* Added `imgaug.augmenters.blend.BlendAlphaCheckerboard`.\n* Added `imgaug.augmenters.blend.BlendAlphaSegMapClassIds`.\n* Added `imgaug.augmenters.blend.BlendAlphaBoundingBoxes`.\n* Added `imgaug.augmenters.blend.IBatchwiseMaskGenerator`,\n  an interface for classes generating masks on a batch-by-batch\n  basis.\n* Added `imgaug.augmenters.blend.StochasticParameterMaskGen`,\n  a helper to generate masks from `StochasticParameter` instances.\n* Added `imgaug.augmenters.blend.SomeColorsMaskGen`, a generator\n  that produces masks marking randomly chosen colors in images.\n* Added `imgaug.augmenters.blend.HorizontalLinearGradientMaskGen`,\n  a linear gradient mask generator.\n* Added `imgaug.augmenters.blend.VerticalLinearGradientMaskGen`,\n  a linear gradient mask generator.\n* Added `imgaug.augmenters.blend.RegularGridMaskGen`,\n  a checkerboard-like mask generator where every grid cell has\n  a random alpha value.\n* Added `imgaug.augmenters.blend.CheckerboardMaskGen`,\n  a checkerboard-like mask generator where every grid cell has\n  the opposite alpha value of its 4-neighbours.\n* Added `imgaug.augmenters.blend.SegMapClassIdsMaskGen`, a\n  segmentation map-based mask generator.\n* Added `imgaug.augmenters.blend.BoundingBoxesMaskGen`, a bounding\n  box-based mask generator.\n* Added `imgaug.augmenters.blend.InvertMaskGen`, an mask generator\n  that inverts masks produces by child generators.\n* Changed `imgaug.parameters.SimplexNoise` and\n  `imgaug.parameters.FrequencyNoise` to also accept `(H, W, C)`\n  sampling shapes, instead of only `(H, W)`.\n* Refactored `AlphaElementwise` to be a wrapper around\n  `BlendAlphaMask`.\n* Renamed `Alpha` to `BlendAlpha`.\n  `Alpha` is now deprecated.\n* Renamed `AlphaElementwise` to `BlendAlphaElementwise`.\n  `AlphaElementwise` is now deprecated.\n* Renamed `SimplexNoiseAlpha` to `BlendAlphaSimplexNoise`.\n  `SimplexNoiseAlpha` is now deprecated.\n* Renamed `FrequencyNoiseAlpha` to `BlendAlphaFrequencyNoise`.\n  `FrequencyNoiseAlpha` is now deprecated.\n* Renamed arguments `first` and `second` to `foreground` and `background`\n  in `BlendAlpha`, `BlendAlphaElementwise`, `BlendAlphaSimplexNoise` and\n  `BlendAlphaFrequencyNoise`.\n* Changed `imgaug.parameters.handle_categorical_string_param()` to allow\n  parameter `valid_values` to be `None`.\n* Fixed a wrong error message in\n  `imgaug.augmenters.color.change_colorspace_()`.\n\n\n<a name=\"added\"/>\n\n# Added\n\n## Unwrapped Bounding Box Augmentation [#446](https://github.com/aleju/imgaug/pull/446)\n\nThe bounding box augmentation was previously a wrapper around keypoint\naugmentation. Bounding Boxes were simply converted to keypoints at the\nstart of the augmentation and then augmented as keypoints by all called\naugmenters. This was now changed so that all augmenters receive\nbounding boxes and can then chose how to augment them. This enables\naugmentations specific to bounding boxes.\n\n* Added property `coords` to `BoundingBox`. The property returns an `(N,2)`\n  numpy array containing the coordinates of the top-left and bottom-right\n  bounding box corners.\n* Added method `BoundingBox.coords_almost_equals(other)`.\n* Added method `BoundingBox.almost_equals(other)`.\n* Changed method `Polygon.almost_equals(other)` to no longer verify the\n  datatype. It is assumed now that the input is a Polygon.\n* Added property `items` to `KeypointsOnImage`, `BoundingBoxesOnImage`,\n  `PolygonsOnImage`, `LineStringsOnImage`. The property returns the\n  keypoints/BBs/polygons/LineStrings contained by that instance.\n* Added method `Polygon.coords_almost_equals(other)`. Alias for\n  `Polygon.exterior_almost_equals(other)`.\n* Added property `Polygon.coords`. Alias for `Polygon.exterior`.\n* Added property `Keypoint.coords`.\n* Added method `Keypoint.coords_almost_equals(other)`.\n* Added method `Keypoint.almost_equals(other)`.\n* Added method `imgaug.testutils.assert_cbaois_equal()`.\n* Added method `imgaug.testutils.shift_cbaoi()`.\n* Added internal `_augment_bounding_boxes()` methods to various augmenters.\n  This allows to individually control how bounding boxes are supposed to\n  be augmented. Previously, the bounding box augmentation was a wrapper around\n  keypoint augmentation that did not allow such control.\n* **[breaking]** Added parameter `parents` to\n  `Augmenter.augment_bounding_boxes()`.\n  This breaks if `hooks` was used as a *positional* argument in connection with\n  that method.\n* [rarely breaking] Added parameter `func_bounding_boxes` to `Lambda`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names.\n* [rarely breaking] Added parameter `func_bounding_boxes` to `AssertLambda`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names.\n* [rarely breaking] Added parameter `check_bounding_boxes` to `AssertShape`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names.\n\n\n## Unwrapped Line String Augmentation [#450](https://github.com/aleju/imgaug/pull/450)\n\nThis patch is the same as the bounding box unwrapping above, only applied to\nline strings.\n\n* Added internal `_augment_line_strings()` methods to various augmenters.\n  This allows to individually control how line strings are supposed to\n  be augmented. Previously, the line string augmentation was a wrapper around\n  keypoint augmentation that did not allow such control.\n* [rarely breaking] Added parameter `func_line_strings` to `Lambda`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names.\n* [rarely breaking] Added parameter `func_line_strings` to `AssertLambda`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names.\n* [rarely breaking] Added parameter `check_line_strings` to `AssertShape`.\n  This breaks if one relied on the order of the augmenter's parameters instead\n  of their names.\n\n\n## Added fit_output to PerspectiveTransform [#452](https://github.com/aleju/imgaug/pull/452) [#456](https://github.com/aleju/imgaug/pull/456)\n\nThis patch added `fit_output` to `PerspectiveTransform`.\n\n* [rarely breaking] PerspectiveTransform has now a `fit_output` parameter,\n  similar to `Affine`. This change may break code that relied on the order of\n  arguments to `__init__`.\n* The sampling code of `PerspectiveTransform` was reworked and should now\n  be faster.\n\n\n# Added `ChangeColorTemperature` Augmenter [#454](https://github.com/aleju/imgaug/pull/454)\n\nThis patch added an augmenter and corresponding function to change the\ncolor temperature of images. This adds e.g. red, orange or blue tints.\n\n* Added augmenter `imgaug.augmenters.color.ChangeColorTemperature`.\n* Added function `imgaug.augmenters.color.change_color_temperatures_()`.\n* Added function `imgaug.augmenters.color.change_color_temperature_()`.\n\n\n## Added Brightness Augmenters [#455](https://github.com/aleju/imgaug/pull/455)\n\nThis patch added brightness-related augmenters. At the core is\n`WithBrightnessChannels`, which converts images to a choice of\ncolorspaces that have brightness-related channels, extracts these\nchannels and applies child augmenters to them. E.g. it might\ntransform to `L*a*b*` colorspace and extract `L`, then apply\na child augmenter and convert the modified `L*a*b*` back to `RGB`. \n\n* Added augmenter `imgaug.augmenters.color.WithBrightnessChannels`.\n* Added augmenter `imgaug.augmenters.color.MultiplyAndAddToBrightness`.\n* Added augmenter `imgaug.augmenters.color.MultiplyBrightness`.\n* Added augmenter `imgaug.augmenters.color.AddToBrightness`.\n* Added method `imgaug.parameters.handle_categorical_string_param()`.\n* Changed `change_colorspaces_()` to accept any iterable of `str` for\n  argument `to_colorspaces`, not just `list`.\n\n\n## Added More Dropout Augmenters [#458](https://github.com/aleju/imgaug/pull/458)\n\nThis patch added more dropout augmenters. `Dropout2d` randomly zeros\nwhole channels, while `TotalDropout` randomly zeros whole images.\nThe latter augmenter can sometimes be used in connection with\nblending operations. (Note though that in these cases it should not be\nused with coordinate-based input data, such as bounding boxes, because\nit removes that data from examples affected by total dropout. That\nbreaks the blending operation, which requires the number of coordinates\nto be unchanged.)\n\n* Added a new augmenter `Dropout2d`, which drops channels in images with\n  a defineable probability `p`. Dropped channels will be filled with zeros.\n  By default, the augmenter keeps at least one channel in each image\n  unaltered (i.e. not dropped).\n* Added new augmenter `TotalDropout`, which sets all components to zero\n  for `p` percent of all images. The augmenter should be used in connection\n  with e.g. blend augmenters.\n\n\n## Added `RemoveSaturation` [#462](https://github.com/aleju/imgaug/pull/462)\n\n* Added `RemoveSaturation`, a shortcut for `MultiplySaturation((0.0, 1.0))`\n  with outputs similar to `Grayscale((0.0, 1.0))`.\n\n\n## Added `Cartoon` Augmenter [#463](https://github.com/aleju/imgaug/pull/463)\n\nThis patch added a filter to change the style of images to one that looks\nmore cartoon-ish. The filter used classical methods. As such it works well\non some images and badly on others. It seems to work better on images\nthat already have rather saturated colors and pronounced edges. \n\n* Added module `imgaug.augmenters.artistic`.\n* Added function `imgaug.augmenters.artistic.stylize_cartoon(image)`.\n* Added augmenter `imgaug.augmenters.artistic.Cartoon`.\n\n\n## Added `MeanShiftBlur` Augmenter [#466](https://github.com/aleju/imgaug/pull/466)\n\nThis patch added a mean shift-based blur filter. Note that it is very\nslow when using the default parameters (high radius).\n\n* Added function `imgaug.augmenters.blur.blur_mean_shift_(image)`.\n* Added augmenter `imgaug.augmenters.blur.MeanShiftBlur`.\n\n\n## Added `DeterministicList` Parameter [#475](https://github.com/aleju/imgaug/pull/475)\n\nAdded `imgaug.parameters.DeterministicList`. Upon a request to generate\nsamples of shape `S`, this parameter will create a new array of shape `S`\nand fill it by cycling over its list of values repeatedly.\n\n\n## Added `Jigsaw` Augmenter [#476](https://github.com/aleju/imgaug/pull/476) [#577](https://github.com/aleju/imgaug/pull/577)\n\nThis patch added a jigsaw puzzle augmenter and corresponding functions.\nThe augmenter splits each image into a regular grid of cells, then\nrandomly picks some cells and switches them with one of their\n8-neighbours. The process is repeated for `N` steps per image.\n\nNote: The augmenter will reject batches containing bounding boxes,\npolygons or line strings.\n\n* Added function `imgaug.augmenters.geometric.apply_jigsaw()`.\n* Added function `imgaug.augmenters.geometric.apply_jigsaw_to_coords()`.\n* Added function `imgaug.augmenters.geometric.generate_jigsaw_destinations()`.\n\n\n## Added Wrappers around Package `PIL` [#479](https://github.com/aleju/imgaug/pull/479) [#480](https://github.com/aleju/imgaug/pull/480) [#538](https://github.com/aleju/imgaug/pull/538)\n\nThis patch added wrapper functions and augmenters around popular\n`PIL` functions. The outputs of these functions and augmenters are\ntested to be identical with the ones in `PIL`. They are intended\nfor research cases where papers have to be re-implemented as\naccurately as possible.\n\n* Added module `imgaug.augmenters.pillike`, which contains augmenters and\n  functions corresponding to commonly used PIL functions. Their outputs\n  are guaranteed to be identical to the PIL outputs.\n* Added the following functions to the module:\n  * `imgaug.augmenters.pillike.equalize`\n  * `imgaug.augmenters.pillike.equalize_`\n  * `imgaug.augmenters.pillike.autocontrast`\n  * `imgaug.augmenters.pillike.autocontrast_`\n  * `imgaug.augmenters.pillike.solarize`\n  * `imgaug.augmenters.pillike.solarize_`\n  * `imgaug.augmenters.pillike.posterize`\n  * `imgaug.augmenters.pillike.posterize_`\n  * `imgaug.augmenters.pillike.enhance_color`\n  * `imgaug.augmenters.pillike.enhance_contrast`\n  * `imgaug.augmenters.pillike.enhance_brightness`\n  * `imgaug.augmenters.pillike.enhance_sharpness`\n  * `imgaug.augmenters.pillike.filter_blur`\n  * `imgaug.augmenters.pillike.filter_smooth`\n  * `imgaug.augmenters.pillike.filter_smooth_more`\n  * `imgaug.augmenters.pillike.filter_edge_enhance`\n  * `imgaug.augmenters.pillike.filter_edge_enhance_more`\n  * `imgaug.augmenters.pillike.filter_find_edges`\n  * `imgaug.augmenters.pillike.filter_contour`\n  * `imgaug.augmenters.pillike.filter_emboss`\n  * `imgaug.augmenters.pillike.filter_sharpen`\n  * `imgaug.augmenters.pillike.filter_detail`\n  * `imgaug.augmenters.pillike.warp_affine`\n* Added the following augmenters to the module:\n  * `imgaug.augmenters.pillike.Solarize`\n  * `imgaug.augmenters.pillike.Posterize`.\n    (Currently alias for `imgaug.augmenters.color.Posterize`.)\n  * `imgaug.augmenters.pillike.Equalize`\n  * `imgaug.augmenters.pillike.Autocontrast`\n  * `imgaug.augmenters.pillike.EnhanceColor`\n  * `imgaug.augmenters.pillike.EnhanceContrast`\n  * `imgaug.augmenters.pillike.EnhanceBrightness`\n  * `imgaug.augmenters.pillike.EnhanceSharpness`\n  * `imgaug.augmenters.pillike.FilterBlur`\n  * `imgaug.augmenters.pillike.FilterSmooth`\n  * `imgaug.augmenters.pillike.FilterSmoothMore`\n  * `imgaug.augmenters.pillike.FilterEdgeEnhance`\n  * `imgaug.augmenters.pillike.FilterEdgeEnhanceMore`\n  * `imgaug.augmenters.pillike.FilterFindEdges`\n  * `imgaug.augmenters.pillike.FilterContour`\n  * `imgaug.augmenters.pillike.FilterEmboss`\n  * `imgaug.augmenters.pillike.FilterSharpen`\n  * `imgaug.augmenters.pillike.FilterDetail`\n  * `imgaug.augmenters.pillike.Affine`\n\n\n## Added `Identity` [#481](https://github.com/aleju/imgaug/pull/481)\n\nThis patch added an identity function augmenter (`Identity`), which is\nthe same as `Noop` and will replace the latter one in the long run.\n\n* [rarely breaking] Added `imgaug.augmenters.meta.Identity`, an alias of\n  `Noop`. `Identity` is now the recommended augmenter for identity\n  transformations. This change can break code that explicitly relied on\n  exactly `Noop` being used, e.g. via `isinstance` checks.\n* Renamed parameter `noop_if_topmost` to `identity_if_topmost` in\n  method `imgaug.augmenters.meta.Augmenter.remove_augmenters()`. The old name\n  is now deprecated.\n\n\n## Added Shearing on the Y-Axis to `Affine` [#482](https://github.com/aleju/imgaug/pull/482)\n\n`Affine` was changed to now also support shearing on the y-axis.\nPreviously, only the x-axis was supported. Use e.g.\n`Affine(shear={\"y\": (-20, 20))` now.\n\n* [rarely breaking] Extended `Affine` to also support shearing on the\n  y-axis (previously, only x-axis was possible). This feature can be used\n  via e.g. ``Affine(shear={\"x\": (-30, 30), \"y\": (-10, 10)})``. If instead\n  a single number is used (e.g. ``Affine(shear=15)``), shearing will be done\n  only on the x-axis. If a single ``tuple``, ``list`` or\n  ``StochasticParameter`` is used, the generated samples will be used\n  identically for both the x-axis and y-axis (this is consistent with\n  translation and scaling). To get independent random samples per axis use\n  the dictionary form.\n\n\n## Added Wrappers around `Affine` [#484](https://github.com/aleju/imgaug/pull/484)\n\nThis patch added a few convenience wrappers around `Affine`.\n\n* Added `imgaug.augmenters.geometric.ScaleX`.\n* Added `imgaug.augmenters.geometric.ScaleY`.\n* Added `imgaug.augmenters.geometric.TranslateX`.\n* Added `imgaug.augmenters.geometric.TranslateY`.\n* Added `imgaug.augmenters.geometric.Rotate`.\n* Added `imgaug.augmenters.geometric.ShearX`.\n* Added `imgaug.augmenters.geometric.ShearY`.\n\n\n## Added More Methods to Remove Out-of-Image Augmentables [#487](https://github.com/aleju/imgaug/pull/487)\n\nThis patch extended the methods to handle coordinate-based augmentables,\ne.g. bounding boxes, that are partially/fully outside of the image plane.\nThey can now more easily be dropped if more than `p%` of their areas is\noutside of the image plane.\n\nThe patch also adds augmenters to remove and clip coordinate-based\naugmentables that are outside of the image plane.\n\n* Added `Keypoint.is_out_of_image()`.\n\n* Added `BoundingBox.compute_out_of_image_area()`.\n* Added `Polygon.compute_out_of_image_area()`.\n\n* Added `Keypoint.compute_out_of_image_fraction()`\n* Added `BoundingBox.compute_out_of_image_fraction()`.\n* Added `Polygon.compute_out_of_image_fraction()`.\n* Added `LineString.compute_out_of_image_fraction()`.\n\n* Added `KeypointsOnImage.remove_out_of_image_fraction()`.\n* Added `BoundingBoxesOnImage.remove_out_of_image_fraction()`.\n* Added `PolygonsOnImage.remove_out_of_image_fraction()`.\n* Added `LineStringsOnImage.remove_out_of_image_fraction()`.\n\n* Added `KeypointsOnImage.clip_out_of_image()`.\n\n* Added `imgaug.augmenters.meta.RemoveCBAsByOutOfImageFraction`.\n  Removes coordinate-based augmentables (e.g. BBs) that have at least a\n  specified fraction of their area outside of the image plane.\n* Added `imgaug.augmenters.meta.ClipCBAsToImagePlanes`.\n  Clips off all parts from coordinate-based augmentables (e.g. BBs) that are\n  outside of the corresponding image.\n\n* Changed `Polygon.area` to return `0.0` if the polygon contains less than\n  three points (previously: exception).\n\n\n## Added Bounding Box to Polygon Conversion [#489](https://github.com/aleju/imgaug/pull/489)\n\n* Added method `imgaug.augmentables.bbs.BoundingBox.to_polygon()`.\n* Added method\n  `imgaug.augmentables.bbs.BoundingBoxesOnImage.to_polygons_on_image()`.\n\n\n## Added Polygon Subdivision [#489](https://github.com/aleju/imgaug/pull/489)\n\n* Added method `imgaug.augmentables.polys.Polygon.subdivide(N)`.\n  The method increases the polygon's corner point count by interpolating\n  `N` points on each edge with regular distance.\n* Added method `imgaug.augmentables.polys.PolygonsOnImage.subdivide(N)`.\n\n\n## Added `WithPolarWarping` Augmenter [#489](https://github.com/aleju/imgaug/pull/489)\n\nThis patch added an augmenter to transform images to polar coordinates\nand apply child augmenters within that space. This leads to interesting\neffects in combination with augmenters that affect pixel locations, such\nas cropping or affine transformations.\n\n* Added augmenter `imgaug.augmenters.geometric.WithPolarWarping`, an\n  augmenter that applies child augmenters in a polar representation of the\n  image.\n\n\n## Added Convenient Access Methods to Coordinate-Based Augmentables [#495](https://github.com/aleju/imgaug/pull/495) [#541](https://github.com/aleju/imgaug/pull/541)\n\nThis patch added various magic functions to coordinate-based\naugmentables that make their usage more convenient. Example:\n```python\nimport imgaug as ia\nbb1 = ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)\nbb2 = ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)\nbbsoi = ia.BoundingBoxesOnImage([bb1, bb2])\n\nprint(bbsoi[0])  # prints now str(bb1)\nprint(len(bbsoi))  # prints now 2\nfor bb in bbsoi:  # looping is now supported \n    print(bb)\n```\n\n* Added module `imgaug.augmentables.base`.\n* Added interface `imgaug.augmentables.base.IAugmentable`, implemented by\n  `HeatmapsOnImage`, `SegmentationMapsOnImage`, `KeypointsOnImage`,\n  `BoundingBoxesOnImage`, `PolygonsOnImage` and `LineStringsOnImage`.\n* Added ability to iterate over coordinate-based `*OnImage` instances\n  (keypoints, bounding boxes, polygons, line strings), e.g.\n  `bbsoi = BoundingBoxesOnImage(bbs, shape=...); for bb in bbsoi: ...`.\n  would iterate now over `bbs`.\n* Added implementations of `__len__` methods to coordinate-based `*OnImage`\n  instances, e.g.\n  `bbsoi = BoundingBoxesOnImage(bbs, shape=...); print(len(bbsoi))`\n  would now print the number of bounding boxes in `bbsoi`.\n* Added ability to iterate over coordinates of `BoundingBox` (top-left,\n  bottom-right), `Polygon` and `LineString` via `for xy in obj: ...`.\n* Added ability to access coordinates of `BoundingBox`, `Polygon` and\n  `LineString` using indices or slices, e.g. `line_string[1:]` to get an\n  array of all coordinates except the first one.\n* Added property `Keypoint.xy`.\n* Added property `Keypoint.xy_int`.\n\n\n## Added `SaveDebugImageEveryNBatches` Augmenter [#502](https://github.com/aleju/imgaug/pull/502)\n\nThis patch added a debug augmenter `SaveDebugImageEveryNBatches` that\nvisualizes a whole batch and saves the corresponding image to a directory.\nThe visualization happens at every `Nth` batch. The plot contains\na visualization of all images within the batch, as well as all additional\ninput data (e.g. segmentation maps or bounding boxes overlayed with\nimages). The plot also contains various additional information, such as\nobserved value ranges (min/max values) of images, observed labels of\nbounding boxes or observed segmentation classes.\n\nThe augmenter can be used during training to evaluate the strength of\naugmentations, whether all data is still aligned (e.g. bounding box\npositions match object positions) and whether the data statistics match\nthe expectations (e.g. no segmentation map classes missing).\n\nThe augmenter might be useful even if no augmentation is actually performed.\n\n* Added module `imgaug.augmenters.debug`.\n* Added function `imgaug.augmenters.debug.draw_debug_image()`. The function\n  draws an image containing debugging information for a provided set of\n  images and non-image data (e.g. segmentation maps, bounding boxes)\n  corresponding to a single batch. The debug image visualizes these\n  informations (e.g. bounding boxes drawn on images) and offers relevant\n  information (e.g. actual value ranges of images, labels of bounding\n  boxes and their counts, etc.).\n* Added augmenter `imgaug.augmenters.debug.SaveDebugImageEveryNBatches`.\n  Augmenter corresponding to `draw_debug_image()`. Saves an image at every\n  n-th batch into a provided folder.\n\n\n## Added Multi-Channel cvals in `pad()` [#502](https://github.com/aleju/imgaug/pull/502)\n\nImproved `imgaug.augmenters.size.pad()` to support multi-channel values\nfor the `cval` parameter (e.g. RGB colors).\n\n\n## Added Wrappers around Package `imagecorruptions` [#530](https://github.com/aleju/imgaug/pull/530)\n\nAdded wrappers around the functions from package\n[bethgelab/imagecorruptions](https://github.com/bethgelab/imagecorruptions).\nThe functions in that package were used in some recent papers and are added\nhere for convenience.\nThe wrappers produce arrays containing values identical to the output\narrays from the corresponding `imagecorruptions` functions when called\nvia the `imagecorruptions.corrupt()` (verified via unittests).\nThe interfaces of the wrapper functions are identical to the\n`imagecorruptions` functions, with the only difference of also supporting\n`seed` parameters.\n\n* Added module `imgaug.augmenters.imgcorruptlike`. The `like` signals that\n  the augmentation functions do not *have* to wrap `imagecorruptions`\n  internally. They merely have to produce the same outputs.\n* Added the following functions to module `imgaug.augmenters.imgcorruptlike`:\n    * `apply_gaussian_noise()`\n    * `apply_shot_noise()`\n    * `apply_impulse_noise()`\n    * `apply_speckle_noise()`\n    * `apply_gaussian_blur()`\n    * `apply_glass_blur()` (improved performance over original function)\n    * `apply_defocus_blur()`\n    * `apply_motion_blur()`\n    * `apply_zoom_blur()`\n    * `apply_fog()`\n    * `apply_snow()`\n    * `apply_spatter()`\n    * `apply_contrast()`\n    * `apply_brightness()`\n    * `apply_saturate()`\n    * `apply_jpeg_compression()`\n    * `apply_pixelate()`\n    * `apply_elastic_transform()`\n* Added function\n  `imgaug.augmenters.imgcorruptlike.get_corruption_names(subset)`.\n  Similar to `imagecorruptions.get_corruption_names(subset)`, but returns a\n  tuple\n  `(list of corruption method names, list of corruption method functions)`,\n  instead of only the names.\n* Added the following augmenters to module `imgaug.augmenters.imgcorruptlike`:\n    * `GaussianNoise`\n    * `ShotNoise`\n    * `ImpulseNoise`\n    * `SpeckleNoise`\n    * `GaussianBlur`\n    * `GlassBlur`\n    * `DefocusBlur`\n    * `MotionBlur`\n    * `ZoomBlur`\n    * `Fog`\n    * `Frost`\n    * `Snow`\n    * `Spatter`\n    * `Contrast`\n    * `Brightness`\n    * `Saturate`\n    * `JpegCompression`\n    * `Pixelate`\n    * `ElasticTransform`\n* Added context `imgaug.random.temporary_numpy_seed()`.\n\n\n## Added `Cutout` Augmenter [#531](https://github.com/aleju/imgaug/pull/531) [#570](https://github.com/aleju/imgaug/pull/570)\n\nThis patch added Cutout augmentation, similar to the paper proposal.\nThe augmetner has some similarity with `CoarseDropout`.\n\n* Added `imgaug.augmenters.arithmetic.apply_cutout_()`, which replaces\n  in-place a single rectangular area with a constant intensity value or a\n  constant color or gaussian noise.\n  See also the [paper](https://arxiv.org/abs/1708.04552) about Cutout.\n* Added `imgaug.augmenters.arithmetic.apply_cutout()`. Same as\n  `apply_cutout_()`, but copies the input images before applying cutout.\n* Added `imgaug.augmenters.arithmetic.Cutout`.\n\n\n## Added in-place Methods for Coordinate-based Augmentables [#532](https://github.com/aleju/imgaug/pull/532)\n\nThis patch added for many already existing methods corresponding in-place\nvariations. They are now used throughout the library, improving the\nperformance of augmentation in the case of e.g. bounding boxes.\n\n* Added `Keypoint.project_()`.\n* Added `Keypoint.shift_()`.    \n* Added `KeypointsOnImage.on_()`.\n* Added setter for `KeypontsOnImage.items`.\n* Added setter for `BoundingBoxesOnImage.items`.\n* Added setter for `LineStringsOnImage.items`.\n* Added setter for `PolygonsOnImage.items`.\n* Added `KeypointsOnImage.remove_out_of_image_fraction_()`.\n* Added `KeypointsOnImage.clip_out_of_image_fraction_()`.\n* Added `KeypointsOnImage.shift_()`.\n* Added `BoundingBox.project_()`.\n* Added `BoundingBox.extend_()`.\n* Added `BoundingBox.clip_out_of_image_()`.\n* Added `BoundingBox.shift_()`.\n* Added `BoundingBoxesOnImage.on_()`.\n* Added `BoundingBoxesOnImage.clip_out_of_image_()`.\n* Added `BoundingBoxesOnImage.remove_out_of_image_()`.\n* Added `BoundingBoxesOnImage.remove_out_of_image_fraction_()`.\n* Added `BoundingBoxesOnImage.shift_()`.\n* Added `imgaug.augmentables.utils.project_coords_()`.\n* Added `LineString.project_()`.\n* Added `LineString.shift_()`.\n* Added `LineStringsOnImage.on_()`.\n* Added `LineStringsOnImage.remove_out_of_image_()`.\n* Added `LineStringsOnImage.remove_out_of_image_fraction_()`.\n* Added `LineStringsOnImage.clip_out_of_image_()`.\n* Added `LineStringsOnImage.shift_()`.\n* Added `Polygon.project_()`.\n* Added `Polygon.shift_()`.\n* Added `Polygon.on_()`.\n* Added `Polygon.subdivide_()`.\n* Added `PolygonsOnImage.remove_out_of_image_()`.\n* Added `PolygonsOnImage.remove_out_of_image_fraction_()`.\n* Added `PolygonsOnImage.clip_out_of_image_()`.\n* Added `PolygonsOnImage.shift_()`.\n* Added `PolygonsOnImage.subdivide_()`.\n* Switched `BoundingBoxesOnImage.copy()` to a custom copy operation (away\n  from module `copy` module).\n* Added parameters `bounding_boxes` and `shape` to\n  BoundingBoxesOnImage.copy()`.\n* Added parameters `bounding_boxes` and `shape` to\n  BoundingBoxesOnImage.deepcopy()`.\n* Switched `KeypointsOnImage.copy()` to a custom copy operation (away\n  from module `copy` module).\n* Switched `PolygonsOnImage.copy()` to a custom copy operation (away\n  from module `copy` module).\n* Added parameters `polygons` and `shape` to\n  PolygonsOnImage.copy()`.\n* Added parameters `polygons` and `shape` to\n  PolygonsOnImage.deepcopy()`.\n* Switched augmenters to use in-place functions for keypoints,\n  bounding boxes, line strings and polygons.\n\n\n## Added Standardized LUT Methods [#542](https://github.com/aleju/imgaug/pull/542)\n\nThis patch standardized the handling of lookup tables throughout the library. \n\n* Added `imgaug.imgaug.apply_lut()`, which applies a lookup table to an image.\n* Added `imgaug.imgaug.apply_lut_()`. In-place version of `apply_lut()`.\n* Refactored all augmenters to use these new LUT functions.\n  This likely fixed some so-far undiscovered bugs in augmenters using LUT\n  tables.\n\n\n## Added Drawing of Bounding Box Labels [#545](https://github.com/aleju/imgaug/pull/545)\n\nWhen drawing bounding boxes on images via `BoundingBox.draw_on_image()`\nor `BoundingBoxesOnImage.draw_on_image()`, a box containing the label will now\nbe drawn over each bounding box's rectangle. If the bounding box's label is\nset to `None`, the label box will not be drawn. For more detailed control,\nuse `BoundingBox.draw_label_on_image()`.\n\n* Added method `imgaug.augmentables.BoundingBox.draw_label_on_image()`.\n* Added method `imgaug.augmentables.BoundingBox.draw_box_on_image()`.\n* Changed method `imgaug.augmentables.BoundingBox.draw_on_image()`\n  to automatically draw a bounding box's label.\n\n\n## Added Index-based Access to Coordinate-based `*OnImage` Instances [#547](https://github.com/aleju/imgaug/pull/547)\n\nEnabled index-based access to coordinate-based `*OnImage` instances, i.e. to\n`KeypointsOnImage`, `BoundingBoxesOnImage`, `LineStringsOnImage` and\n`PolygonsOnImage`. This allows to do things like\n`bbsoi = BoundingBoxesOnImage(...); bbs = bbsoi[0:2];`.\n\n* Added `imgaug.augmentables.kps.KeypointsOnImage.__getitem__()`.\n* Added `imgaug.augmentables.bbs.BoundingBoxesOnImage.__getitem__()`.\n* Added `imgaug.augmentables.lines.LineStringsOnImage.__getitem__()`.\n* Added `imgaug.augmentables.polys.PolygonsOnImage.__getitem__()`.\n\n\n## Added `Rain` and `RainLayer` Augmenters [#551](https://github.com/aleju/imgaug/pull/551)\n\nAdded augmenter(s) to create fake rain effects. They currently seem to work\nbest at around medium-sized images (~224px).\n\n* Added `imgaug.augmenters.weather.Rain`.\n* Added `imgaug.augmenters.weather.RainLayer`.\n\n\n## Added `round` Parameter to `Discretize` [#553](https://github.com/aleju/imgaug/pull/553)\n\nAdded the parameter `round` to `imgaug.parameters.Discretize`. The parameter\ndefaults to `True`, i.e. the default behaviour of `Discretize` did not change.\n\n\n## Added `RandAugment` Augmenter [#553](https://github.com/aleju/imgaug/pull/553)\n\nAdded a RandAugment augmenter, similar to the one described in the paper\n\"RandAugment: Practical automated data augmentation with a reduced\nsearch space\".\n\nNote: This implementation makes a best guess about some hyperparameters\nthat were neither in the paper nor in the code repsitory clearly\ndefined.\n\nNote: This augmenter differs from the paper implementation by applying\na fix to their color augmentations. The ones in the paper's implementation\nseemed to increase in strength as the magnitude was decreased below\na threshold.\n\nNote: This augmenter currently only accepts image inputs. Other input\ntypes (e.g. bounding boxes) will be rejected.\n\n* Added module `imgaug.augmenters.collections`\n* Added augmenter `imgaug.augmenters.collections.RandAugment`.\n\n\n## Added and Improved Warnings for Probably-Wrong Image Inputs [#594](https://github.com/aleju/imgaug/pull/594)\n\nImproved the errors and warnings on image augmentation calls.\n`augment_image()` will now produce a more self-explanatory error\nmessage when calling it as in `augment_image(list of images)`.\nCalls of single-image augmentation functions (e.g.\n`augment(image=...)`) with inputs that look like multiple images\nwill now produce warnings. This is the case for `(H, W, C)`\ninputs when `C>=32` (as that indicates that `(N, H, W)` was\nactually provided).\nCalls of multi-image augmentation functions (e.g.\n`augment(images=...)`) with inputs that look like single images\nwill now produce warnings. This is the case for `(N, H, W)`\ninputs when `W=1` or `W=3` (as that indicates that `(H, W, C)`\nwas actually provided.)\n\n* Added an assert in `augment_image()` to verify that inputs are\n  arrays.\n* Added warnings for probably-wrong image inputs in\n  `augment_image()`, `augment_images()`, `augment()` (and its\n  alias `__call__()`).\n* Added module `imgaug.augmenters.base`.\n* Added warning\n  `imgaug.augmenters.base.SuspiciousMultiImageShapeWarning`.\n* Added warning\n  `imgaug.augmenters.base.SuspiciousSingleImageShapeWarning`.\n* Added `imgaug.testutils.assertWarns`, similar to `unittest`'s\n  `assertWarns`, but available in python <3.2.\n\n\n<a name=\"changed\"/>\n\n# Changed\n\n## Improved RNG Handling during Polygon Augmentation [#447](https://github.com/aleju/imgaug/pull/447)\n\nChanged `Augmenter.augment_polygons()` to copy the augmenter's RNG\nbefore starting concave polygon recovery. This is done for cleanliness and\nshould not have any effects for users.\nAlso removed RNG copies in `_ConcavePolygonRecoverer` to improve performance.\n\n\n## Pooling Augmenters now Affect Maps [#457](https://github.com/aleju/imgaug/pull/457)\n\nPooling augmenters were previously implemented so that they did not pool\nthe arrays of maps (i.e. heatmap arrays, segmentation map arrays). Only\nthe image shape saved within `HeatmapsOnImage.shape` and\n`SegmentationMapsOnImage.shape` were updated. That was done because the library\ncan handle map arrays that are larger than the corresponding images and hence\nno pooling was necessary for the augmentation to work correctly. This was now\nchanged and pooling augmenters will also pool map arrays\n(if `keep_size=False`). The motiviation for this change is that the old\nbehaviour was unintuitive and inconsistent with other augmenters (e.g. `Crop`).\n\n\n## Affine Translation Precision [#489](https://github.com/aleju/imgaug/pull/489)\n\nRemoved a rounding operation in `Affine` translation that would unnecessarily\nround floats to integers. This should make coordinate augmentation overall\nmore accurate.\n\n\n## `Affine.get_parameters()` and `translate_px`/`translate_percent` [#508](https://github.com/aleju/imgaug/pull/508)\n\nChanged `Affine.get_parameters()` to always return a tuple `(x, y, mode)`\nfor translation, where `mode` is either `px` or `percent`,\nand `x` and `y` are stochastic parameters. `y` may be `None` if the same\nparameter (and hence samples) are used for both axes.\n\n\n## Removed Outdated \"Don't Import from this Module\" Messages [#539](https://github.com/aleju/imgaug/pull/539)\n\nThe docstring of each module in ``imgaug.augmenters`` previously included a\nsuggestion to not directly import from that module, but instead use\n``imgaug.augmenters.<AugmenterName>``. That was due to the categorization\nstill being unstable. As the categorization has now been fairly stable\nfor a long time, the suggestion was removed from all modules. Calling\n``imgaug.augmenters.<AugmenterName>`` instead of\n``imgaug.augmenters.<ModuleName>.<AugmenterName>`` is however still the\npreferred way.\n\n\n## Standardized `shift()` Interfaces of Coordinate-Based Augmentables [#548](https://github.com/aleju/imgaug/pull/548)\n\nThe interfaces for shift operations of all coordinate-based\naugmentables (Keypoints, BoundingBoxes, LineStrings, Polygons)\nwere standardized. All of these augmentables have now the same\ninterface for shift operations. Previously, Keypoints used\na different interface (using `x` and `y` arguments) than the\nother augmentables (using `top`, `right`, `bottom`, `left`\narguments). All augmentables use now the interface of Keypoints\nas that is simpler and less ambiguous. Old arguments are still\naccepted, but will produce deprecation warnings. Change the\narguments to `x` and `y` following `x=left-right` and\n`y=top-bottom`.\n\n**[breaking]** This breaks if one relied on calling `shift()` functions of\n`BoundingBox`, `LineString`, `Polygon`, `BoundingBoxesOnImage`,\n`LineStringsOnImage` or `PolygonsOnImage` without named arguments.\nE.g. `bb = BoundingBox(...); bb_shifted = bb.shift(1, 2, 3, 4);`\nwill produce unexpected outputs now (equivalent to\n`shift(x=1, y=2, top=3, right=4, bottom=0, left=0)`),\nwhile `bb_shifted = bb.shift(top=1, right=2, bottom=3, left=4)` will still\nwork as expected.\n\n* Added arguments `x`, `y` to `BoundingBox.shift()`, `LineString.shift()`\n  and `Polygon.shift()`.\n* Added arguments `x`, `y` to `BoundingBoxesOnImage.shift()`,\n  `LineStringsOnImage.shift()` and `PolygonsOnImage.shift()`.\n* Marked arguments `top`, `right`, `bottom`, `left` in\n  `BoundingBox.shift()`, `LineString.shift()` and `Polygon.shift()`\n  as deprecated. This also affects the corresponding `*OnImage`\n  classes.\n* Added function `testutils.wrap_shift_deprecation()`.\n\n\n## Simplified Standard Parameters of Augmenters [#567](https://github.com/aleju/imgaug/pull/567) [#595](https://github.com/aleju/imgaug/pull/595)\n\nThe patch changed the standard parameters shared by all augmenters to a\nreduced and more self-explanatory set. Previously, all augmenters\nshared the parameters `name`, `random_state` and `deterministic`.\nThe new parameters are `seed` and `name`.\n\n`deterministic` was removed as it was hardly ever used and because\nit caused frequently confusion with regards to its meaning. The\nparameter is still accepted but will now produce a deprecation\nwarning. Use `<augmenter>.to_deterministic()` instead.\n\nReminder: `to_deterministic()` is necessary if you want to get\nthe same samples in *consecutive* augmentation calls. It is *not*\nnecessary if you want your generated samples to be dependent on\nan initial seed or random state as that is *always* the case\nanyways. To use non-random initial seeds, use either\nthe `seed` parameter (augmenter-specific seeding) or\n`imgaug.random.seed()` (global seeding, affects only augmenters\nfor which the `seed` parameter was not explicitly provided).\n\n`random_state` was renamed to `seed` as providing a seed value\nis the more common use case compared to providing a random state.\nMany users also seemed to be unaware that `random_state` accepted\nseed values. The new name should make this more clear.\nThe old parameter `random_state` is still accepted, but will\nlikely be deprecated in the future.\n\n**[breaking]** This patch breaks if one relied on the order of\n`name`, `random_state` and `deterministic`. The new order is now\n`seed=..., name=..., random_state=..., deterministic=...` (with the\nlatter two parameters being outdated or deprecated)\nas opposed to previously\n`name=..., deterministic=..., random_state=...`.\n\n\n## Improved Default Values of Augmenters [#582](https://github.com/aleju/imgaug/pull/582)\n\n**[breaking]** Most augmenters had previously default values that\nmade them equivalent to identity functions. Users had to explicitly\nchange the defaults to proper values in order to \"activate\"\naugmentations. To simplify the usage of the library, the default\nvalues of most augmenters were changed to medium-strength\naugmentations. E.g.\n`Sequential([Affine(), UniformVoronoi(), CoarseDropout()])`\nshould now produce decent augmentations.\n\nA few augmenters were set to always-on, maximum-strength\naugmentations. This is the case for:\n\n* `Grayscale` (always fully grayscales images, use\n  `Grayscale((0.0, 1.0))` for random strengths)\n* `RemoveSaturation` (same as `Grayscale`)\n* `Fliplr` (always flips images, use `Fliplr(0.5)` for 50%\n  probability)\n* `Flipud` (same as `Fliplr`)\n* `TotalDropout` (always drops everything, use\n  `TotalDropout(0.1)` to drop everything for 10% of all images)\n* `Invert` (always inverts images, use `Invert(0.1)` to invert\n  10% of all images)\n* `Rot90` (always rotates exactly once clockwise by 90 degrees,\n  use `Rot90((0, 3))` for any rotation)\n\nThese settings seemed to better match user-expectations.\nSuch maximum-strength settings however were not chosen for all\naugmenters where one might expect them. The defaults are set to\nvarying strengths for, e.g.  `Superpixels` (replaces only some\nsuperpixels with cellwise average colors), `UniformVoronoi` (also\nonly replaces some cells), `Sharpen` (alpha-blends with variable\nstrength, the same is the case for `Emboss`, `EdgeDetect` and\n`DirectedEdgeDetect`) and `CLAHE` (variable clip limits).\n\n*Note*: Some of the new default values will cause issues with\nnon-`uint8` inputs.\n\n*Note*: The defaults for `per_channel` and `keep_size` were not\nadjusted. It is currently still the default behaviour of all\naugmenters to affect all channels in the same way and to resize\ntheir outputs back to the input sizes.\n\nThe exact changes to default values are listed below.\n\n**imgaug.arithmetic**\n  \n  * `Add`\n    * `value`: `0` -> `(-20, 20)`\n  * `AddElementwise`\n    * `value`: `0` -> `(-20, 20)`\n  * `AdditiveGaussianNoise`\n    * `scale`: `0` -> `(0, 15)`\n  * `AdditiveLaplaceNoise`\n    * `scale`: `0` -> `(0, 15)`\n  * `AdditivePoissonNoise`\n    * `scale`: `0` -> `(0, 15)`\n  * `Multiply`\n    * `mul`: `1.0` -> `(0.8, 1.2)`\n  * `MultiplyElementwise`:\n    * `mul`: `1.0` -> `(0.8, 1.2)`\n  * `Dropout`: \n    * `p`: `0.0` -> `(0.0, 0.05)`\n  * `CoarseDropout`:\n    * `p`: `0.0` -> `(0.02, 0.1)`\n    * `size_px`: `None` -> `(3, 8)`\n    * `min_size`: `4` -> `3`\n    * Default for `size_px` is only used if neither `size_percent`\n      nor `size_px` is provided by the user.\n  * `CoarseSaltAndPepper`:\n    * `p`: `0.0` -> `(0.02, 0.1)`\n    * `size_px`: `None` -> `(3, 8)`\n    * `min_size`: `4` -> `3`\n    * Default for `size_px` is only used if neither `size_percent`\n      nor `size_px` is provided by the user.\n  * `CoarseSalt`:\n    * `p`: `0.0` -> `(0.02, 0.1)`\n    * `size_px`: `None` -> `(3, 8)`\n    * `min_size`: `4` -> `3`\n    * Default for `size_px` is only used if neither `size_percent`\n      nor `size_px` is provided by the user.\n  * `CoarsePepper`:\n    * `p`: `0.0` -> `(0.02, 0.1)`\n    * `size_px`: `None` -> `(3, 8)`\n    * `min_size`: `4` -> `3`\n    * Default for `size_px` is only used if neither `size_percent`\n      nor `size_px` is provided by the user.\n  * `SaltAndPepper`:\n    * `p`: `0.0` -> `(0.0, 0.03)`\n  * `Salt`:\n    * `p`: `0.0` -> `(0.0, 0.03)`\n  * `Pepper`:\n    * `p`: `0.0` -> `(0.0, 0.05)`\n  * `ImpulseNoise`:\n    * `p`: `0.0` -> `(0.0, 0.03)`\n  * `Invert`: \n    * `p`: `0` -> `1`\n  * `JpegCompression`:\n    * `compression`: `50` -> `(0, 100)`\n\n**imgaug.blend**\n\n  * `BlendAlpha`\n    * `factor`: `0` -> `(0.0, 1.0)`\n  * `BlendAlphaElementwise`\n    * `factor`: `0` -> `(0.0, 1.0)`\n\n**imgaug.blur**\n\n  * `GaussianBlur`:\n    * `sigma`: `0` -> `(0.0, 3.0)`\n  * `AverageBlur`:\n    * `k`: `1` -> `(1, 7)`\n  * `MedianBlur`:\n    * `k`: `1` -> `(1, 7)`\n  * `BilateralBlur`:\n    * `d`: `1` -> `(1, 9)`\n  * `MotionBlur`:\n    * `k`: `5` -> `(3, 7)`\n\n**imgaug.color**\n\n  * `MultiplyHueAndSaturation`:\n    * `mul_hue`: `None` -> `(0.5, 1.5)`\n    * `mul_saturation`: `None` -> `(0.0, 1.7)`\n    * These defaults are only used if the user provided neither\n      `mul` nor `mul_hue` nor `mul_saturation`.\n  * `MultiplyHue`:\n    * `mul`: `(-1.0, 1.0)` -> `(-3.0, 3.0)`\n  * `AddToHueAndSaturation`:\n    * `value_hue`: `None` -> `(-40, 40)`\n    * `value_saturation`: `None` -> `(-40, 40)`\n    * These defaults are only used if the user provided neither\n      `value` nor `value_hue` nor `value_saturation`.\n  * `Grayscale`:\n    * `alpha`: `0` -> `1`\n\n**imgaug.contrast**\n\n  * `GammaContrast`:\n    * `gamma`: `1` -> `(0.7, 1.7)` \n  * `SigmoidContrast`:\n    * `gain`: `10` -> `(5, 6)`\n    * `cutoff`: `0.5` -> `(0.3, 0.6)`\n  * `LogContrast`:\n    * `gain`: `1` -> `(0.4, 1.6)`\n  * `LinearContrast`:\n    * `alpha`: `1` -> `(0.6, 1.4)`\n  * `AllChannelsCLAHE`:\n    * `clip_limit`: `40` -> `(0.1, 8)`\n    * `tile_grid_size_px`: `8` -> `(3, 12)`\n  * `CLAHE`: \n    * `clip_limit`: `40` -> `(0.1, 8)`\n    * `tile_grid_size_px`: `8` -> `(3, 12)`\n\n**convolutional**\n\n  * `Sharpen`:\n    * `alpha`: `0` -> `(0.0, 0.2)`\n    * `lightness`: `1` -> `(0.8, 1.2)`\n  * `Emboss`:\n    * `alpha`: `0` -> `(0.0, 1.0)`\n    * `strength`: `1` -> `(0.25, 1.0)`\n  * `EdgeDetect`:\n    * `alpha`: `0` -> `(0.0, 0.75)`\n  * `DirectedEdgeDetect`:\n    * `alpha`: `0` -> `(0.0, 0.75)`\n\n**imgaug.flip**\n\n  * `Fliplr`:\n    * `p`: `0` -> `1`\n  * `Flipud`:\n    * `p`: `0` -> `1`\n\n**imgaug.geometric**\n\n  * `Affine`:\n    * `scale`: `1` -> `{\"x\": (0.9, 1.1), \"y\": (0.9, 1.1)}`\n    * `translate_percent`: None -> `{\"x\": (-0.1, 0.1), \"y\": (-0.1, 0.1)}`\n    * `rotate`: `0` -> `(-15, 15)`\n    * `shear`: `0` -> `shear={\"x\": (-10, 10), \"y\": (-10, 10)}`\n    * These defaults are only used if no affine transformation\n      parameter was set by the user. Otherwise the not-set\n      parameters default again towards the identity function.\n  * `PiecewiseAffine`:\n    * `scale`: `0` -> `(0.0, 0.04)`\n    * `nb_rows`: `4` -> `(2, 4)`\n    * `nb_cols`: `4` -> `(2, 4)`\n  * `PerspectiveTransform`:\n    * `scale`: `0` -> `(0.0, 0.06)`\n  * `ElasticTransformation`:\n    * `alpha`: `0` -> `(0.0, 40.0)`\n    * `sigma`: `0` -> `(4.0, 8.0)`\n  * `Rot90`:\n    * `k`: `(no default)` -> `k=1`\n\n**imgaug.pooling**\n\n  * `AveragePooling`:\n    * `k`: `(no default)` -> `(1, 5)`\n  * `MaxPooling`:\n    * `k`: `(no default)` -> `(1, 5)`\n  * `MinPooling`:\n    * `k`: `(no default)` -> `(1, 5)`\n  * `MedianPooling`:\n    * `k`: `(no default)` -> `(1, 5)`\n\n**imgaug.segmentation**\n \n  * `Superpixels`:\n    * `p_replace`: `0.0` -> `(0.5, 1.0)`\n    * `n_segments`: `100` -> `(50, 120)`\n  * `UniformVoronoi`:\n    * `n_points`: `(no default)` -> `(50, 500)`\n    * `p_replace`: `1.0` -> `(0.5, 1.0)`.\n  * `RegularGridVoronoi`:\n    * `n_rows`: `(no default)` -> `(10, 30)`\n    * `n_cols`: `(no default)` -> `(10, 30)`\n    * `p_drop_points`: `0.4` -> `(0.0, 0.5)`\n    * `p_replace`: `1.0` -> `(0.5, 1.0)`\n  * `RelativeRegularGridVoronoi`: Changed defaults from\n    * `n_rows_frac`: `(no default)` -> `(0.05, 0.15)`\n    * `n_cols_frac`: `(no default)` -> `(0.05, 0.15)`\n    * `p_drop_points`: `0.4` -> `(0.0, 0.5)`\n    * `p_replace`: `1.0` -> `(0.5, 1.0)`\n\n**imgaug.size**\n\n  * `CropAndPad`:\n    * `percent`: `None` -> `(-0.1, 0.1)`\n    * This default is only used if the user has provided\n      neither `px` nor `percent`.\n  * `Pad`:\n    * `percent`: `None` -> `(0.0, 0.1)`\n    * This default is only used if the user has provided\n      neither `px` nor `percent`.\n  * `Crop`:\n    * `percent`: `None` -> `(0.0, 0.1)`\n    * This default is only used if the user has provided\n      neither `px` nor `percent`.\n\n\n## `setup.py` Now Accepts any `opencv-*` Installation [#586](https://github.com/aleju/imgaug/pull/586)\n\n`setup.py` was changed so that it now accepts `opencv-python`,\n`opencv-python-headless`, `opencv-contrib-python` and\n`opencv-contrib-python-headless` as valid OpenCV installations.\nPreviously, only `opencv-python-headless` was accepted, which\ncould easily cause conflicts when another one of the mentioned\nlibraries was already installed.\nIf none of the mentioned libraries is installed, `setup.py`\nwill default to adding `opencv-python` as a requirement.\n\nNote that this may still cause issues if a single installation\ncall installs multiple libraries and the order is random.\n`imgaug` will then currently request `opencv-python-headless`\nto be installed, which may differ from what a later installed\nlibrary requests. Try to ensure that the other library is installed\nfirst in these cases.\n\n\n## Unified OpenCV Input Normalization [#565](https://github.com/aleju/imgaug/pull/565)\n\nChanged various augmenters to use the same normalization for OpenCV\ninputs. This probably fixes some previously undiscovered bugs.\n\n\n## Renamed In-place Methods [#444](https://github.com/aleju/imgaug/pull/444)\n\n* Renamed `Augmenter.reseed()` to `Augmenter.seed_()`. The old name is\n  now deprecated.\n* Renamed `Augmenter.remove_augmenters_inplace()` to\n  `Augmenter.remove_augmenters_()`. The old name is now deprecated.\n\n\n## Deprecated `AffineCv2` [#540](https://github.com/aleju/imgaug/pull/540)\n\nDeprecated `imgaug.augmenters.geometric.AffineCv2`.\nUse `imgaug.augmenters.geometric.Affine` instead. [#540](https://github.com/aleju/imgaug/pull/540)\n\n\n<a name=\"refactored\"/>\n\n# Refactored\n\n## Refactored According to pylint Requirements [#504](https://github.com/aleju/imgaug/pull/504)\n\n* Refactored all core library files to fulfill (most) pylint requirements.\n* [rarely breaking] Renamed\n  `imgaug.augmenters.size.KeepSizeByResize.get_shapes()` to `_get_shapes()`.\n* Added a project-specific pylint configuration.\n\n\n<a name=\"fixed\"/>\n\n# Fixed\n\n* Fixed `Resize` always returning an `uint8` array during image augmentation\n  if the input was a single numpy array and all augmented images had the\n  same shape. [#442](https://github.com/aleju/imgaug/pull/442) [#443](https://github.com/aleju/imgaug/pull/443)\n* Fixed `Affine` coordinate-based augmentation applying wrong offset\n  when shifting images to/from top-left corner. This would lead to an error\n  of around 0.5 to 1.0 pixels. [#446](https://github.com/aleju/imgaug/pull/446)\n* Fixed keypoint augmentation in `PiecewiseAffine` potentially being\n  unaligned if a `KeypointsOnImage` instance contained no keypoints. [#446](https://github.com/aleju/imgaug/pull/446)\n* Fixed `imgaug.validation.convert_iterable_to_string_of_types()` crashing due\n  to not converting types to strings before joining them. [#446](https://github.com/aleju/imgaug/pull/446)\n* Fixed `imgaug.validation.assert_is_iterable_of()` producing a not\n  well-designed error if the input was not an iterable. [#446](https://github.com/aleju/imgaug/pull/446)\n* Fixed image normalization crashing when an input ndarray of multiple images\n  was changed during augmentation to a list of multiple images with different\n  shapes *and* the original input ndarray represented a single image or\n  a collection of 2D `(H,W)` images. This problem affected `augment()`,\n  `augment_batch()` and `augment_batches()`.\n* Fixed a typo in an image normalization error message. [#451](https://github.com/aleju/imgaug/pull/451)\n* Fixed a problem in `WithChannels` that could lead random sampling in child\n  augmenters being unaligned between images and corresponding non-image\n  data. [#451](https://github.com/aleju/imgaug/pull/451)\n* Added aliases to `imgaug.random.RNG` for some outdated numpy random number\n  sampling methods that existed in `numpy.random.RandomState` but not in\n  numpy's new RNG system (1.17+). These old methods are not used in `imgaug`,\n  but some custom augmenters and `Lambda` calls may require them when\n  interacting with the provided `random_state` instances. [#486](https://github.com/aleju/imgaug/pull/486) \n* Fixed `Affine` producing unaligned augmentations between images and\n  segmentation maps or heatmaps when using `translate_px` and the segmentation\n  map or heatmap had a different height/width than corresponding image. [#489](https://github.com/aleju/imgaug/pull/489)\n* Fixed a crash in `SnowflakesLayer` that could occur when using values\n  close to `1.0` for `flake_size`. [#471](https://github.com/aleju/imgaug/pull/471)\n* Fixed `MultiplyHueAndSaturation` crashing if the RNG provided via\n  `random_state` was not `None` or `imgaug.random.RNG`. [#493](https://github.com/aleju/imgaug/pull/493)\n* Fixed `CloudLayer.draw_on_image()` producing tuples instead of arrays\n  as output for `float` input images. [#540](https://github.com/aleju/imgaug/pull/540)\n* Fixed `Affine` parameter `translate_px` behaving like `translate_percent`\n  if a continuous stochastic parameter was provided.\n  Analogously `translate_percent` would behave like `translate_px` if\n  a discrete stochastic parameter was provided. [#508](https://github.com/aleju/imgaug/pull/508)\n* Fixed code hanging indefinitely when using multicore augmentation\n  on NixOS. [#414](https://github.com/aleju/imgaug/issues/414) [#510](https://github.com/aleju/imgaug/pull/510)\n* Fixed a deprecation warning and potential crash in python 3.8\n  related to the use of `collections` instead of `collections.abc`. [#527](https://github.com/aleju/imgaug/pull/527)\n* Fixed deprecated `scipy.fromfunction()` being called. [#529](https://github.com/aleju/imgaug/pull/529)\n* Fixed `imgaug.random.normalize_generator()` crashing in numpy 1.18.\n  The function relied on `numpy.random.bit_generator.BitGenerator`, which\n  was moved in numpy 1.18 to `numpy.random.BitGenerator` without a\n  deprecation period for the old name. [#534](https://github.com/aleju/imgaug/pull/534)\n* Fixed an issue that could lead to endlessly hanging programs on some OS\n  when using multicore augmentation (e.g. via pool) and augmenters using\n  OpenCV. [#535](https://github.com/aleju/imgaug/pull/535)\n* Fixed `imgaug.random.seed()` not seeding the global `RNG` in-place\n  in numpy 1.17+. The (unfixed) function instead created a new\n  global `RNG` with the given seed. This set the seed of augmenters\n  created *after* the `seed()` call, but not of augmenters created\n  *before* the `seed()` call as they would continue to use the old\n  global RNG. [#557](https://github.com/aleju/imgaug/pull/557)\n* Fixed `cval` in `ElasticTransformation` resulting in new pixels in RGB images\n  being filled with `(cval, 0, 0)` instead of `(cval, cval, cval)`. [#561](https://github.com/aleju/imgaug/pull/561) [#562](https://github.com/aleju/imgaug/pull/562)\n* Fixed some augmenters in module `weather` not transferring seed values\n  or random states that were provided upon creation to child augmenters. [#568](https://github.com/aleju/imgaug/pull/568)\n* Fixed an inaccuracy in `PerspectiveTransform` that could lead to slightly\n  misaligned transformations between images and coordinate-based\n  augmentables (e.g. bounding boxes). The problem was more significant the\n  smaller the images and larger the `scale` values were. It was also\n  worsened by using `fit_output`. [#585](https://github.com/aleju/imgaug/pull/585)\n* Fixed `KeepSizeByResize` potentially crashing if a single numpy array\n  was provided as the input for an iterable of images (as opposed to\n  a list of numpy arrays). [#590](https://github.com/aleju/imgaug/pull/590)\n"
  },
  {
    "path": "checks/README.md",
    "content": "These are checks for the library.\nIn contrast to tests they are expected to be executed by hand.\nTheir results have to be manually investigated (usually by looking at images).\n"
  },
  {
    "path": "checks/check_add_to_hue_and_saturation.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\nfrom skimage import data\nimport cv2\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\nVAL_PER_STEP = 1\nTIME_PER_STEP = 10\n\n\ndef main():\n    image = data.astronaut()\n\n    cv2.namedWindow(\"aug\", cv2.WINDOW_NORMAL)\n    cv2.imshow(\"aug\", image)\n    cv2.waitKey(TIME_PER_STEP)\n\n    # for value in cycle(np.arange(-255, 255, VAL_PER_STEP)):\n    for value in np.arange(-255, 255, VAL_PER_STEP):\n        aug = iaa.AddToHueAndSaturation(value=value)\n        img_aug = aug.augment_image(image)\n        img_aug = iaa.pad(img_aug, bottom=40)\n        img_aug = ia.draw_text(img_aug, x=0, y=img_aug.shape[0]-38, text=\"value=%d\" % (value,), size=30)\n\n        cv2.imshow(\"aug\", img_aug)\n        cv2.waitKey(TIME_PER_STEP)\n\n    images_aug = iaa.AddToHueAndSaturation(value=(-255, 255), per_channel=True).augment_images([image] * 64)\n    ia.imshow(ia.draw_grid(images_aug))\n\n    image = ia.quokka_square((128, 128))\n    images_aug = []\n    images_aug.extend(iaa.AddToHue().augment_images([image] * 10))\n    images_aug.extend(iaa.AddToSaturation().augment_images([image] * 10))\n    ia.imshow(ia.draw_grid(images_aug, rows=2))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_affine.py",
    "content": "from __future__ import print_function, division\n\nimport imageio\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\nNB_ROWS = 10\nNB_COLS = 10\nHEIGHT = 200\nWIDTH = 256\nBB_X1 = 64\nBB_X2 = WIDTH - 64\nBB_Y1 = 64\nBB_Y2 = HEIGHT - 64\n\n\ndef main():\n    image = data.astronaut()\n    image = ia.imresize_single_image(image, (HEIGHT, WIDTH))\n\n    # testing new shear on x-/y-axis\n    shear_x = [iaa.Affine(shear=shear)(image=image)\n               for shear in np.linspace(0, 45, 10)]\n    shear_y = [iaa.Affine(shear={\"y\": shear})(image=image)\n               for shear in np.linspace(0, 45, 10)]\n    ia.imshow(\n        ia.draw_grid(shear_x + shear_y, cols=10, rows=2)\n    )\n    ia.imshow(\n        ia.draw_grid(\n            iaa.Affine(shear=(-45, 45))(images=[image]*16)\n        )\n    )\n    ia.imshow(\n        ia.draw_grid(\n            iaa.Affine(shear=[-45, -20, 0, 20, 45])(images=[image]*16)\n        )\n    )\n    ia.imshow(\n        ia.draw_grid(\n            iaa.Affine(shear={\"y\": (-45, 45)})(images=[image]*16)\n        )\n    )\n\n    kps = []\n    for y in range(NB_ROWS):\n        ycoord = BB_Y1 + int(y * (BB_Y2 - BB_Y1) / (NB_COLS - 1))\n        for x in range(NB_COLS):\n            xcoord = BB_X1 + int(x * (BB_X2 - BB_X1) / (NB_ROWS - 1))\n            kp = (xcoord, ycoord)\n            kps.append(kp)\n    kps = set(kps)\n    kps = [ia.Keypoint(x=xcoord, y=ycoord) for (xcoord, ycoord) in kps]\n    kps = ia.KeypointsOnImage(kps, shape=image.shape)\n\n    bb = ia.BoundingBox(x1=BB_X1, x2=BB_X2, y1=BB_Y1, y2=BB_Y2)\n    bbs = ia.BoundingBoxesOnImage([bb], shape=image.shape)\n\n    pairs = []\n    params = [\n        {\"rotate\": 45},\n        {\"translate_px\": 20},\n        {\"translate_percent\": 0.1},\n        {\"scale\": 1.2},\n        {\"scale\": 0.8},\n        {\"shear\": 45},\n        {\"rotate\": 45, \"cval\": 255},\n        {\"translate_px\": 20, \"mode\": \"constant\"},\n        {\"translate_px\": 20, \"mode\": \"edge\"},\n        {\"translate_px\": 20, \"mode\": \"symmetric\"},\n        {\"translate_px\": 20, \"mode\": \"reflect\"},\n        {\"translate_px\": 20, \"mode\": \"wrap\"},\n        {\"scale\": 0.5, \"order\": 0},\n        {\"scale\": 0.5, \"order\": 1},\n        {\"scale\": 0.5, \"order\": 2},\n        {\"scale\": 0.5, \"order\": 3},\n        {\"scale\": 0.5, \"order\": 4},\n        {\"scale\": 0.5, \"order\": 5},\n        {\"rotate\": 45, \"translate_px\": 20, \"scale\": 1.2},\n        {\"rotate\": 45, \"translate_px\": 20, \"scale\": 0.8},\n        {\"rotate\": (-45, 45), \"translate_px\": (-20, 20), \"scale\": (0.8, 1.2), \"order\": ia.ALL,\n         \"mode\": ia.ALL, \"cval\": ia.ALL},\n        {\"rotate\": (-45, 45), \"translate_px\": (-20, 20), \"scale\": (0.8, 1.2), \"order\": ia.ALL,\n         \"mode\": ia.ALL, \"cval\": ia.ALL},\n        {\"rotate\": (-45, 45), \"translate_px\": (-20, 20), \"scale\": (0.8, 1.2), \"order\": ia.ALL,\n         \"mode\": ia.ALL, \"cval\": ia.ALL},\n        {\"rotate\": (-45, 45), \"translate_px\": (-20, 20), \"scale\": (0.8, 1.2), \"order\": ia.ALL,\n         \"mode\": ia.ALL, \"cval\": ia.ALL}\n    ]\n    seqs_skimage = [iaa.Affine(backend=\"skimage\", **p) for p in params]\n    seqs_cv2 = [iaa.Affine(backend=\"auto\", **p) for p in params]\n\n    for seq_skimage, seq_cv2 in zip(seqs_skimage, seqs_cv2):\n        seq_skimage_det = seq_skimage.to_deterministic()\n        seq_cv2_det = seq_cv2.to_deterministic()\n\n        seq_cv2_det.copy_random_state_(seq_skimage_det)\n\n        image_aug_skimage = seq_skimage_det.augment_image(image)\n        image_aug_cv2 = seq_cv2_det.augment_image(image)\n        kps_aug_skimage = seq_skimage_det.augment_keypoints([kps])[0]\n        kps_aug_cv2     = seq_cv2_det.augment_keypoints([kps])[0]\n        bbs_aug_skimage = seq_skimage_det.augment_bounding_boxes([bbs])[0]\n        bbs_aug_cv2     = seq_cv2_det.augment_bounding_boxes([bbs])[0]\n\n        image_before_skimage = np.copy(image)\n        image_before_cv2 = np.copy(image)\n        image_before_skimage = kps.draw_on_image(image_before_skimage)\n        image_before_cv2 = kps.draw_on_image(image_before_cv2)\n        image_before_skimage = bbs.draw_on_image(image_before_skimage)\n        image_before_cv2 = bbs.draw_on_image(image_before_cv2)\n\n        image_after_skimage = np.copy(image_aug_skimage)\n        image_after_cv2 = np.copy(image_aug_cv2)\n        image_after_skimage = kps_aug_skimage.draw_on_image(image_after_skimage)\n        image_after_cv2 = kps_aug_cv2.draw_on_image(image_after_cv2)\n        image_after_skimage = bbs_aug_skimage.draw_on_image(image_after_skimage)\n        image_after_cv2 = bbs_aug_cv2.draw_on_image(image_after_cv2)\n\n        pairs.append(np.hstack((image_before_skimage, image_after_skimage, image_after_cv2)))\n\n    ia.imshow(np.vstack(pairs))\n    imageio.imwrite(\"affine.jpg\", np.vstack(pairs))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_affinecv2.py",
    "content": "from __future__ import print_function, division\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nimport imageio\nimport numpy as np\nfrom skimage import data\nimport cv2\n\nNB_ROWS = 10\nNB_COLS = 10\nHEIGHT = 200\nWIDTH = 256\nBB_X1 = 64\nBB_X2 = WIDTH - 64\nBB_Y1 = 64\nBB_Y2 = HEIGHT - 64\n\ndef main():\n    image = data.astronaut()\n    image = ia.imresize_single_image(image, (HEIGHT, WIDTH))\n\n    kps = []\n    for y in range(NB_ROWS):\n        ycoord = BB_Y1 + int(y * (BB_Y2 - BB_Y1) / (NB_COLS - 1))\n        for x in range(NB_COLS):\n            xcoord = BB_X1 + int(x * (BB_X2 - BB_X1) / (NB_ROWS - 1))\n            kp = (xcoord, ycoord)\n            kps.append(kp)\n    kps = set(kps)\n    kps = [ia.Keypoint(x=xcoord, y=ycoord) for (xcoord, ycoord) in kps]\n    kps = ia.KeypointsOnImage(kps, shape=image.shape)\n\n    bb = ia.BoundingBox(x1=BB_X1, x2=BB_X2, y1=BB_Y1, y2=BB_Y2)\n    bbs = ia.BoundingBoxesOnImage([bb], shape=image.shape)\n\n    pairs = []\n    seqs = [\n        iaa.AffineCv2(rotate=45),\n        iaa.AffineCv2(translate_px=20),\n        iaa.AffineCv2(translate_percent=0.1),\n        iaa.AffineCv2(scale=1.2),\n        iaa.AffineCv2(scale=0.8),\n        iaa.AffineCv2(shear=45),\n        iaa.AffineCv2(rotate=45, cval=256),\n        iaa.AffineCv2(translate_px=20, mode=cv2.BORDER_CONSTANT),\n        iaa.AffineCv2(translate_px=20, mode=cv2.BORDER_REPLICATE),\n        iaa.AffineCv2(translate_px=20, mode=cv2.BORDER_REFLECT),\n        iaa.AffineCv2(translate_px=20, mode=cv2.BORDER_REFLECT_101),\n        iaa.AffineCv2(translate_px=20, mode=cv2.BORDER_WRAP),\n        iaa.AffineCv2(translate_px=20, mode=\"constant\"),\n        iaa.AffineCv2(translate_px=20, mode=\"replicate\"),\n        iaa.AffineCv2(translate_px=20, mode=\"reflect\"),\n        iaa.AffineCv2(translate_px=20, mode=\"reflect_101\"),\n        iaa.AffineCv2(translate_px=20, mode=\"wrap\"),\n        iaa.AffineCv2(scale=0.5, order=cv2.INTER_NEAREST),\n        iaa.AffineCv2(scale=0.5, order=cv2.INTER_LINEAR),\n        iaa.AffineCv2(scale=0.5, order=cv2.INTER_CUBIC),\n        iaa.AffineCv2(scale=0.5, order=cv2.INTER_LANCZOS4),\n        iaa.AffineCv2(scale=0.5, order=\"nearest\"),\n        iaa.AffineCv2(scale=0.5, order=\"linear\"),\n        iaa.AffineCv2(scale=0.5, order=\"cubic\"),\n        iaa.AffineCv2(scale=0.5, order=\"lanczos4\"),\n        iaa.AffineCv2(rotate=45, translate_px=20, scale=1.2),\n        iaa.AffineCv2(rotate=45, translate_px=20, scale=0.8),\n        iaa.AffineCv2(rotate=(-45, 45), translate_px=(-20, 20), scale=(0.8, 1.2), order=ia.ALL, mode=ia.ALL, cval=ia.ALL),\n        iaa.AffineCv2(rotate=(-45, 45), translate_px=(-20, 20), scale=(0.8, 1.2), order=ia.ALL, mode=ia.ALL, cval=ia.ALL),\n        iaa.AffineCv2(rotate=(-45, 45), translate_px=(-20, 20), scale=(0.8, 1.2), order=ia.ALL, mode=ia.ALL, cval=ia.ALL),\n        iaa.AffineCv2(rotate=(-45, 45), translate_px=(-20, 20), scale=(0.8, 1.2), order=ia.ALL, mode=ia.ALL, cval=ia.ALL)\n    ]\n\n    for seq in seqs:\n        seq_det = seq.to_deterministic()\n        image_aug = seq_det.augment_image(image)\n        #print(image_aug.dtype, np.min(image_aug), np.max(image_aug))\n        kps_aug = seq_det.augment_keypoints([kps])[0]\n        bbs_aug = seq_det.augment_bounding_boxes([bbs])[0]\n\n        image_before = np.copy(image)\n        image_before = kps.draw_on_image(image_before)\n        image_before = bbs.draw_on_image(image_before)\n\n        image_after = np.copy(image_aug)\n        image_after = kps_aug.draw_on_image(image_after)\n        image_after = bbs_aug.draw_on_image(image_after)\n\n        pairs.append(np.hstack((image_before, image_after)))\n\n    ia.imshow(np.vstack(pairs))\n    imageio.imwrite(\"affinecv2.jpg\", np.vstack(pairs))\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_average_blur.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\nfrom skimage import data\nimport cv2\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\nTIME_PER_STEP = 5000\nNB_AUGS_PER_IMAGE = 10\n\n\ndef main():\n    image = data.astronaut()\n    image = ia.imresize_single_image(image, (64, 64))\n    print(\"image shape:\", image.shape)\n    print(\"Press any key or wait %d ms to proceed to the next image.\" % (TIME_PER_STEP,))\n\n    k = [\n        1,\n        2,\n        4,\n        8,\n        16,\n        (8, 8),\n        (1, 8),\n        ((1, 1), (8, 8)),\n        ((1, 16), (1, 16)),\n        ((1, 16), 1)\n    ]\n\n    cv2.namedWindow(\"aug\", cv2.WINDOW_NORMAL)\n    cv2.resizeWindow(\"aug\", 64*NB_AUGS_PER_IMAGE, 64)\n\n    for ki in k:\n        aug = iaa.AverageBlur(k=ki)\n        img_aug = [aug.augment_image(image) for _ in range(NB_AUGS_PER_IMAGE)]\n        img_aug = np.hstack(img_aug)\n        print(\"dtype\", img_aug.dtype, \"averages\", np.average(img_aug, axis=tuple(range(0, img_aug.ndim-1))))\n\n        title = \"k=%s\" % (str(ki),)\n        img_aug = ia.draw_text(img_aug, x=5, y=5, text=title)\n\n        cv2.imshow(\"aug\", img_aug[..., ::-1])  # here with rgb2bgr\n        cv2.waitKey(TIME_PER_STEP)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_background_augmentation.py",
    "content": "from __future__ import print_function, division\n\nimport time\n\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nimport imgaug.multicore as multicore\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    augseq = iaa.Sequential([\n        iaa.Fliplr(0.5),\n        iaa.CoarseDropout(p=0.1, size_percent=0.1)\n    ])\n\n    def func_images(images, random_state, parents, hooks):\n        time.sleep(0.2)\n        return images\n\n    def func_heatmaps(heatmaps, random_state, parents, hooks):\n        return heatmaps\n\n    def func_keypoints(keypoints_on_images, random_state, parents, hooks):\n        return keypoints_on_images\n\n    augseq_slow = iaa.Sequential([\n        iaa.Fliplr(0.5),\n        iaa.Lambda(\n            func_images=func_images,\n            func_heatmaps=func_heatmaps,\n            func_keypoints=func_keypoints\n        )\n    ])\n\n    print(\"------------------\")\n    print(\"augseq.augment_batches(batches, background=True)\")\n    print(\"------------------\")\n    batches = list(load_images())\n    batches_aug = augseq.augment_batches(batches, background=True)\n    images_aug = []\n    keypoints_aug = []\n    for batch_aug in batches_aug:\n        images_aug.append(batch_aug.images_aug)\n        keypoints_aug.append(batch_aug.keypoints_aug)\n    ia.imshow(draw_grid(images_aug, keypoints_aug))\n\n    print(\"------------------\")\n    print(\"augseq.augment_batches(batches, background=True) -> only images\")\n    print(\"------------------\")\n    batches = list(load_images())\n    batches = [batch.images_unaug for batch in batches]\n    batches_aug = augseq.augment_batches(batches, background=True)\n    images_aug = []\n    keypoints_aug = None\n    for batch_aug in batches_aug:\n        images_aug.append(batch_aug)\n    ia.imshow(draw_grid(images_aug, keypoints_aug))\n\n    print(\"------------------\")\n    print(\"BackgroundAugmenter\")\n    print(\"------------------\")\n    batch_loader = multicore.BatchLoader(load_images)\n    bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq)\n    images_aug = []\n    keypoints_aug = []\n    while True:\n        print(\"Next batch...\")\n        batch = bg_augmenter.get_batch()\n        if batch is None:\n            print(\"Finished.\")\n            break\n        images_aug.append(batch.images_aug)\n        keypoints_aug.append(batch.keypoints_aug)\n    ia.imshow(draw_grid(images_aug, keypoints_aug))\n\n    print(\"------------------\")\n    print(\"BackgroundAugmenter with generator in BL\")\n    print(\"------------------\")\n    batch_loader = multicore.BatchLoader(load_images())\n    bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq)\n    images_aug = []\n    keypoints_aug = []\n    while True:\n        print(\"Next batch...\")\n        batch = bg_augmenter.get_batch()\n        if batch is None:\n            print(\"Finished.\")\n            break\n        images_aug.append(batch.images_aug)\n        keypoints_aug.append(batch.keypoints_aug)\n    ia.imshow(draw_grid(images_aug, keypoints_aug))\n\n    print(\"------------------\")\n    print(\"Long running BackgroundAugmenter at BL-queue_size=12\")\n    print(\"------------------\")\n    batch_loader = multicore.BatchLoader(load_images(n_batches=1000), queue_size=12)\n    bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq)\n    i = 0\n    while True:\n        if i % 100 == 0:\n            print(\"batch=%d...\" % (i,))\n        batch = bg_augmenter.get_batch()\n        if batch is None:\n            print(\"Finished.\")\n            break\n        i += 1\n\n    print(\"------------------\")\n    print(\"Long running BackgroundAugmenter at BL-queue_size=2\")\n    print(\"------------------\")\n    batch_loader = multicore.BatchLoader(load_images(n_batches=1000), queue_size=2)\n    bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq)\n    i = 0\n    while True:\n        if i % 100 == 0:\n            print(\"batch=%d...\" % (i,))\n        batch = bg_augmenter.get_batch()\n        if batch is None:\n            print(\"Finished.\")\n            break\n        i += 1\n\n    print(\"------------------\")\n    print(\"Long running BackgroundAugmenter (slow loading)\")\n    print(\"------------------\")\n    batch_loader = multicore.BatchLoader(load_images(n_batches=100, sleep=0.2))\n    bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq)\n    i = 0\n    while True:\n        if i % 10 == 0:\n            print(\"batch=%d...\" % (i,))\n        batch = bg_augmenter.get_batch()\n        if batch is None:\n            print(\"Finished.\")\n            break\n        i += 1\n\n    print(\"------------------\")\n    print(\"Long running BackgroundAugmenter (slow aug) at BL-queue_size=12\")\n    print(\"------------------\")\n    batch_loader = multicore.BatchLoader(load_images(n_batches=100), queue_size=12)\n    bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq_slow)\n    i = 0\n    while True:\n        if i % 10 == 0:\n            print(\"batch=%d...\" % (i,))\n        batch = bg_augmenter.get_batch()\n        if batch is None:\n            print(\"Finished.\")\n            break\n        i += 1\n\n    print(\"------------------\")\n    print(\"Long running BackgroundAugmenter (slow aug) at BL-queue_size=2\")\n    print(\"------------------\")\n    batch_loader = multicore.BatchLoader(load_images(n_batches=100), queue_size=2)\n    bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq_slow)\n    i = 0\n    while True:\n        if i % 10 == 0:\n            print(\"batch=%d...\" % (i,))\n        batch = bg_augmenter.get_batch()\n        if batch is None:\n            print(\"Finished.\")\n            break\n        i += 1\n\n    for augseq_i in [augseq, augseq_slow]:\n        print(\"------------------\")\n        print(\"Many very small runs (batches=1)\")\n        print(\"------------------\")\n        for i in range(100):\n            batch_loader = multicore.BatchLoader(load_images(n_batches=1), queue_size=100)\n            bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq_i)\n            while True:\n                batch = bg_augmenter.get_batch()\n                if batch is None:\n                    print(\"Finished (%d/%d).\" % (i+1, 100))\n                    break\n\n        print(\"------------------\")\n        print(\"Many very small runs (batches=2)\")\n        print(\"------------------\")\n        for i in range(100):\n            batch_loader = multicore.BatchLoader(load_images(n_batches=2), queue_size=100)\n            bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq_i)\n            while True:\n                batch = bg_augmenter.get_batch()\n                if batch is None:\n                    print(\"Finished (%d/%d).\" % (i+1, 100))\n                    break\n\n        print(\"------------------\")\n        print(\"Many very small runs, separate function (batches=1)\")\n        print(\"------------------\")\n\n        def _augment_small_1():\n            batch_loader = multicore.BatchLoader(load_images(n_batches=1), queue_size=100)\n            bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq_i)\n            i = 0\n            while True:\n                batch = bg_augmenter.get_batch()\n                if batch is None:\n                    break\n                i += 1\n\n        for i in range(100):\n            _augment_small_1()\n            print(\"Finished (%d/%d).\" % (i+1, 100))\n\n        print(\"------------------\")\n        print(\"Many very small runs, separate function (batches=2)\")\n        print(\"------------------\")\n\n        def _augment_small_2():\n            batch_loader = multicore.BatchLoader(load_images(n_batches=2), queue_size=100)\n            bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq_i)\n            i = 0\n            while True:\n                batch = bg_augmenter.get_batch()\n                if batch is None:\n                    break\n                i += 1\n\n        for i in range(100):\n            _augment_small_2()\n            print(\"Finished (%d/%d).\" % (i+1, 100))\n\n        print(\"------------------\")\n        print(\"Many very small runs, separate function, incomplete fetching (batches=2)\")\n        print(\"------------------\")\n\n        def _augment_small_3():\n            batch_loader = multicore.BatchLoader(load_images(n_batches=2), queue_size=100)\n            bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq_i)\n            batch = bg_augmenter.get_batch()\n\n        for i in range(100):\n            _augment_small_3()\n            print(\"Finished (%d/%d).\" % (i+1, 100))\n\n    #for augseq_i in [augseq, augseq_slow]:\n        print(\"------------------\")\n        print(\"Many very small runs, separate function, incomplete fetching (batches=10)\")\n        print(\"------------------\")\n\n        def _augment_small_4():\n            batch_loader = multicore.BatchLoader(load_images(n_batches=10), queue_size=100)\n            bg_augmenter = multicore.BackgroundAugmenter(batch_loader, augseq_i)\n            batch = bg_augmenter.get_batch()\n            #bg_augmenter.terminate()\n\n        for i in range(100):\n            _augment_small_4()\n            print(\"Finished (%d/%d).\" % (i+1, 100))\n\n\ndef load_images(n_batches=10, sleep=0.0):\n    batch_size = 4\n    astronaut = data.astronaut()\n    astronaut = ia.imresize_single_image(astronaut, (64, 64))\n    kps = ia.KeypointsOnImage([ia.Keypoint(x=15, y=25)], shape=astronaut.shape)\n    counter = 0\n    for i in range(n_batches):\n        batch_images = []\n        batch_kps = []\n        for b in range(batch_size):\n            astronaut_text = ia.draw_text(astronaut, x=0, y=0, text=\"%d\" % (counter,), color=[0, 255, 0], size=16)\n            batch_images.append(astronaut_text)\n            batch_kps.append(kps)\n            counter += 1\n        batch = ia.Batch(\n            images=np.array(batch_images, dtype=np.uint8),\n            keypoints=batch_kps\n        )\n        yield batch\n        if sleep > 0:\n            time.sleep(sleep)\n\n\ndef draw_grid(images_aug, keypoints_aug):\n    if keypoints_aug is None:\n        keypoints_aug = []\n        for bidx in range(len(images_aug)):\n            keypoints_aug.append([None for image in images_aug[bidx]])\n\n    images_kps_batches = []\n    for bidx in range(len(images_aug)):\n        images_kps_batch = []\n        for image, kps in zip(images_aug[bidx], keypoints_aug[bidx]):\n            if kps is None:\n                image_kps = image\n            else:\n                image_kps = kps.draw_on_image(image, size=5, color=[255, 0, 0])\n            images_kps_batch.append(image_kps)\n        images_kps_batches.extend(images_kps_batch)\n\n    grid = ia.draw_grid(images_kps_batches, cols=len(images_aug[0]))\n    return grid\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_bb_augmentation.py",
    "content": "from __future__ import print_function, division\n\nimport imageio\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\nNB_ROWS = 10\nNB_COLS = 10\nHEIGHT = 256\nWIDTH = 256\nBB_X1 = 64\nBB_X2 = WIDTH - 64\nBB_Y1 = 64\nBB_Y2 = HEIGHT - 64\n\n\ndef main():\n    image = data.astronaut()\n    image = ia.imresize_single_image(image, (HEIGHT, WIDTH))\n\n    kps = []\n    for y in range(NB_ROWS):\n        ycoord = BB_Y1 + int(y * (BB_Y2 - BB_Y1) / (NB_COLS - 1))\n        for x in range(NB_COLS):\n            xcoord = BB_X1 + int(x * (BB_X2 - BB_X1) / (NB_ROWS - 1))\n            kp = (xcoord, ycoord)\n            kps.append(kp)\n    kps = set(kps)\n    kps = [ia.Keypoint(x=xcoord, y=ycoord) for (xcoord, ycoord) in kps]\n    kps = ia.KeypointsOnImage(kps, shape=image.shape)\n\n    bb = ia.BoundingBox(x1=BB_X1, x2=BB_X2, y1=BB_Y1, y2=BB_Y2)\n    bbs = ia.BoundingBoxesOnImage([bb], shape=image.shape)\n\n    seq = iaa.Affine(rotate=45)\n    seq_det = seq.to_deterministic()\n    image_aug = seq_det.augment_image(image)\n    kps_aug = seq_det.augment_keypoints([kps])[0]\n    bbs_aug = seq_det.augment_bounding_boxes([bbs])[0]\n\n    image_before = np.copy(image)\n    image_before = kps.draw_on_image(image_before)\n    image_before = bbs.draw_on_image(image_before)\n\n    image_after = np.copy(image_aug)\n    image_after = kps_aug.draw_on_image(image_after)\n    image_after = bbs_aug.draw_on_image(image_after)\n\n    ia.imshow(np.hstack([image_before, image_after]))\n    imageio.imwrite(\"bb_aug.jpg\", np.hstack([image_before, image_after]))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_bilateral_blur.py",
    "content": "from __future__ import print_function, division\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nimport numpy as np\nfrom skimage import data\nimport cv2\n\nTIME_PER_STEP = 5000\nNB_AUGS_PER_IMAGE = 10\n\n\ndef main():\n    image = data.astronaut()\n    image = ia.imresize_single_image(image, (128, 128))\n    print(\"image shape:\", image.shape)\n    print(\"Press any key or wait %d ms to proceed to the next image.\" % (TIME_PER_STEP,))\n\n    configs = [\n        (1, 75, 75),\n        (3, 75, 75),\n        (5, 75, 75),\n        (10, 75, 75),\n        (10, 25, 25),\n        (10, 250, 150),\n        (15, 75, 75),\n        (15, 150, 150),\n        (15, 250, 150),\n        (20, 75, 75),\n        (40, 150, 150),\n        ((1, 5), 75, 75),\n        (5, (10, 250), 75),\n        (5, 75, (10, 250)),\n        (5, (10, 250), (10, 250)),\n        (10, (10, 250), (10, 250)),\n    ]\n\n    cv2.namedWindow(\"aug\", cv2.WINDOW_NORMAL)\n    cv2.resizeWindow(\"aug\", 128*NB_AUGS_PER_IMAGE, 128)\n\n    for (d, sigma_color, sigma_space) in configs:\n        aug = iaa.BilateralBlur(d=d, sigma_color=sigma_color, sigma_space=sigma_space)\n\n        img_aug = [aug.augment_image(image) for _ in range(NB_AUGS_PER_IMAGE)]\n        img_aug = np.hstack(img_aug)\n        print(\"dtype\", img_aug.dtype, \"averages\", np.average(img_aug, axis=tuple(range(0, img_aug.ndim-1))))\n\n        title = \"d=%s, sc=%s, ss=%s\" % (str(d), str(sigma_color), str(sigma_space))\n        img_aug = ia.draw_text(img_aug, x=5, y=5, text=title)\n\n        cv2.imshow(\"aug\", img_aug[..., ::-1]) # here with rgb2bgr\n        cv2.waitKey(TIME_PER_STEP)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_blendalphasegmapclassids.py",
    "content": "from __future__ import print_function, division, absolute_import\nimport imageio\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    aug = iaa.BlendAlphaMask(\n        iaa.SegMapClassIdsMaskGen(1),\n        iaa.OneOf([\n            iaa.TotalDropout(1.0),\n            iaa.AveragePooling(8)\n        ])\n    )\n\n    aug2 = iaa.BlendAlphaSegMapClassIds(\n        1, iaa.OneOf([\n            iaa.TotalDropout(1.0),\n            iaa.AveragePooling(8)\n        ])\n    )\n\n    image = ia.data.quokka(0.25)\n    segmap = ia.data.quokka_segmentation_map(0.25)\n\n    images_aug, segmaps_aug = aug(images=[image]*25,\n                                  segmentation_maps=[segmap]*25)\n    ia.imshow(ia.draw_grid(images_aug, cols=5, rows=5))\n\n    images_aug, segmaps_aug = aug2(images=[image]*25,\n                                  segmentation_maps=[segmap]*25)\n    ia.imshow(ia.draw_grid(images_aug, cols=5, rows=5))\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_blendalphasomecolors.py",
    "content": "from __future__ import print_function, division, absolute_import\nimport imageio\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    aug = iaa.BlendAlphaMask(\n        iaa.SomeColorsMaskGen(),\n        iaa.OneOf([\n            iaa.TotalDropout(1.0),\n            iaa.AveragePooling(8)\n        ])\n    )\n\n    aug2 = iaa.BlendAlphaSomeColors(iaa.OneOf([\n            iaa.TotalDropout(1.0),\n            iaa.AveragePooling(8)\n    ]))\n\n    urls = [\n        (\"https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/\"\n         \"Sarcophilus_harrisii_taranna.jpg/\"\n         \"320px-Sarcophilus_harrisii_taranna.jpg\"),\n        (\"https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/\"\n         \"Vincent_van_Gogh_-_Wheatfield_with_crows_-_Google_Art_Project.jpg/\"\n         \"320px-Vincent_van_Gogh_-_Wheatfield_with_crows_-_Google_Art_Project\"\n         \".jpg\"),\n        (\"https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/\"\n         \"Galerella_sanguinea_Zoo_Praha_2011-2.jpg/207px-Galerella_sanguinea_\"\n         \"Zoo_Praha_2011-2.jpg\"),\n        (\"https://upload.wikimedia.org/wikipedia/commons/thumb/9/96/\"\n         \"Ambrosius_Bosschaert_the_Elder_%28Dutch_-_Flower_Still_Life_-_\"\n         \"Google_Art_Project.jpg/307px-Ambrosius_Bosschaert_the_Elder_%28\"\n         \"Dutch_-_Flower_Still_Life_-_Google_Art_Project.jpg\")\n    ]\n\n    for url in urls:\n        img = imageio.imread(url)\n        ia.imshow(ia.draw_grid(aug(images=[img]*25), cols=5, rows=5))\n        ia.imshow(ia.draw_grid(aug2(images=[img]*25), cols=5, rows=5))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_brightness.py",
    "content": "from __future__ import print_function, division\nimport numpy as np\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    image = ia.quokka_square(size=(100, 100))\n\n    for cspace in iaa.WithBrightnessChannels._VALID_COLORSPACES:\n        print(cspace, \"add\")\n        images_aug = []\n        for add in np.linspace(-200, 200, 64):\n            aug = iaa.MultiplyAndAddToBrightness(add=add, mul=1.0,\n                                                 to_colorspace=cspace)\n            images_aug.append(aug(image=image))\n\n        ia.imshow(ia.draw_grid(images_aug))\n\n    for cspace in iaa.WithBrightnessChannels._VALID_COLORSPACES:\n        print(cspace, \"mul\")\n        images_aug = []\n        for mul in np.linspace(0.5, 1.5, 64):\n            aug = iaa.MultiplyAndAddToBrightness(add=0, mul=mul,\n                                                 to_colorspace=cspace)\n            images_aug.append(aug(image=image))\n\n        ia.imshow(ia.draw_grid(images_aug))\n\n    for cspace in iaa.WithBrightnessChannels._VALID_COLORSPACES:\n        print(cspace, \"defaults\")\n        aug = iaa.MultiplyAndAddToBrightness(to_colorspace=cspace)\n        images_aug = aug(images=[image] * 64)\n        ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_canny.py",
    "content": "from __future__ import print_function, division\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    black_and_white = iaa.RandomColorsBinaryImageColorizer(\n        color_true=255, color_false=0)\n\n    print(\"alpha=1.0, black and white\")\n    image = ia.quokka_square((128, 128))\n    aug = iaa.Canny(alpha=1.0, colorizer=black_and_white)\n    ia.imshow(ia.draw_grid(aug(images=[image] * (5*5))))\n\n    print(\"alpha=1.0, random color\")\n    image = ia.quokka_square((128, 128))\n    aug = iaa.Canny(alpha=1.0)\n    ia.imshow(ia.draw_grid(aug(images=[image] * (5*5))))\n\n    print(\"alpha=1.0, sobel ksize=[3, 13], black and white\")\n    image = ia.quokka_square((128, 128))\n    aug = iaa.Canny(alpha=1.0, sobel_kernel_size=[3, 7],\n                    colorizer=black_and_white)\n    ia.imshow(ia.draw_grid(aug(images=[image] * (5*5))))\n\n    print(\"alpha=1.0, sobel ksize=3, black and white\")\n    image = ia.quokka_square((128, 128))\n    aug = iaa.Canny(alpha=1.0, sobel_kernel_size=3,\n                    colorizer=black_and_white)\n    ia.imshow(ia.draw_grid(aug(images=[image] * (5*5))))\n\n    print(\"fully random\")\n    image = ia.quokka_square((128, 128))\n    aug = iaa.Canny()\n    ia.imshow(ia.draw_grid(aug(images=[image] * (5*5))))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_cartoon.py",
    "content": "import imgaug as ia\nimport imgaug.augmenters as iaa\nimport imageio\nimport cv2\nimport numpy as np\n\n\ndef main():\n    urls_small = [\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/\"\n        \"Physicist_Stephen_Hawking_in_Zero_Gravity_NASA.jpg/\"\n        \"320px-Physicist_Stephen_Hawking_in_Zero_Gravity_NASA.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/\"\n        \"Barack_Obama_family_portrait_2011.jpg/320px-Barack_Obama_\"\n        \"family_portrait_2011.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/\"\n        \"Pahalgam_Valley.jpg/320px-Pahalgam_Valley.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/\"\n        \"Iglesia_de_Nuestra_Se%C3%B1ora_de_La_Blanca%2C_Cardej%C3%B3n%2C_\"\n        \"Espa%C3%B1a%2C_2012-09-01%2C_DD_02.JPG/320px-Iglesia_de_Nuestra_\"\n        \"Se%C3%B1ora_de_La_Blanca%2C_Cardej%C3%B3n%2C_Espa%C3%B1a%2C_\"\n        \"2012-09-01%2C_DD_02.JPG\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/9/94/\"\n        \"Salad_platter.jpg/320px-Salad_platter.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/1/1c/\"\n        \"Squirrel_posing.jpg/287px-Squirrel_posing.jpg\"\n    ]\n    urls_medium = [\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/\"\n        \"Physicist_Stephen_Hawking_in_Zero_Gravity_NASA.jpg/\"\n        \"640px-Physicist_Stephen_Hawking_in_Zero_Gravity_NASA.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/\"\n        \"Barack_Obama_family_portrait_2011.jpg/640px-Barack_Obama_\"\n        \"family_portrait_2011.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/\"\n        \"Pahalgam_Valley.jpg/640px-Pahalgam_Valley.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/\"\n        \"Iglesia_de_Nuestra_Se%C3%B1ora_de_La_Blanca%2C_Cardej%C3%B3n%2C_\"\n        \"Espa%C3%B1a%2C_2012-09-01%2C_DD_02.JPG/640px-Iglesia_de_Nuestra_\"\n        \"Se%C3%B1ora_de_La_Blanca%2C_Cardej%C3%B3n%2C_Espa%C3%B1a%2C_\"\n        \"2012-09-01%2C_DD_02.JPG\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/9/94/\"\n        \"Salad_platter.jpg/640px-Salad_platter.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/1/1c/\"\n        \"Squirrel_posing.jpg/574px-Squirrel_posing.jpg\"\n    ]\n    urls_large = [\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/\"\n        \"Physicist_Stephen_Hawking_in_Zero_Gravity_NASA.jpg/\"\n        \"1024px-Physicist_Stephen_Hawking_in_Zero_Gravity_NASA.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/\"\n        \"Barack_Obama_family_portrait_2011.jpg/1024px-Barack_Obama_\"\n        \"family_portrait_2011.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/\"\n        \"Pahalgam_Valley.jpg/1024px-Pahalgam_Valley.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/\"\n        \"Iglesia_de_Nuestra_Se%C3%B1ora_de_La_Blanca%2C_Cardej%C3%B3n%2C_\"\n        \"Espa%C3%B1a%2C_2012-09-01%2C_DD_02.JPG/1024px-Iglesia_de_\"\n        \"Nuestra_Se%C3%B1ora_de_La_Blanca%2C_Cardej%C3%B3n%2C_\"\n        \"Espa%C3%B1a%2C_2012-09-01%2C_DD_02.JPG\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/9/94/\"\n        \"Salad_platter.jpg/1024px-Salad_platter.jpg\",\n        \"https://upload.wikimedia.org/wikipedia/commons/thumb/1/1c/\"\n        \"Squirrel_posing.jpg/574px-Squirrel_posing.jpg\"\n    ]\n\n    image = imageio.imread(urls_medium[1])\n\n    augs = [image] + iaa.Cartoon()(images=[image] * 15)\n    ia.imshow(ia.draw_grid(augs, 4, 4))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_channel_shuffle.py",
    "content": "from imgaug import augmenters as iaa\nimport imgaug as ia\nimport imgaug.random as iarandom\n\niarandom.seed(1)\n\n\ndef main():\n    img = ia.data.quokka(size=(128, 128), extract=\"square\")\n\n    aug = iaa.ChannelShuffle()\n    imgs_aug = aug.augment_images([img] * 64)\n    ia.imshow(ia.draw_grid(imgs_aug))\n\n    aug = iaa.ChannelShuffle(p=0.1)\n    imgs_aug = aug.augment_images([img] * 64)\n    ia.imshow(ia.draw_grid(imgs_aug))\n\n    aug = iaa.ChannelShuffle(p=1.0, channels=[0, 1])\n    imgs_aug = aug.augment_images([img] * 64)\n    ia.imshow(ia.draw_grid(imgs_aug))\n\n    aug = iaa.ChannelShuffle(p=1.0, channels=[1, 2])\n    imgs_aug = aug.augment_images([img] * 64)\n    ia.imshow(ia.draw_grid(imgs_aug))\n\n    aug = iaa.ChannelShuffle(p=1.0, channels=[1, 1, 2])\n    imgs_aug = aug.augment_images([img] * 64)\n    ia.imshow(ia.draw_grid(imgs_aug))\n\n    aug = iaa.ChannelShuffle(p=1.0, channels=ia.ALL)\n    imgs_aug = aug.augment_images([img] * 64)\n    ia.imshow(ia.draw_grid(imgs_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_clouds.py",
    "content": "from __future__ import print_function, division\n\nimport imageio\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    for size in [0.1, 0.2, 1.0]:\n        image = imageio.imread(\"https://upload.wikimedia.org/wikipedia/commons/8/89/Kukle%2CCzech_Republic..jpg\",\n                               format=\"jpg\")\n        image = ia.imresize_single_image(image, size, \"cubic\")\n        print(image.shape)\n        augs = [\n            (\"iaa.Clouds()\", iaa.Clouds())\n        ]\n\n        for descr, aug in augs:\n            print(descr)\n            images_aug = aug.augment_images([image] * 64)\n            ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_color_temperature.py",
    "content": "from __future__ import print_function, division\nimport numpy as np\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    image = ia.quokka_square()\n    images_aug = []\n    for kelvin in np.linspace(1000, 10000, 64):\n        images_aug.append(iaa.ChangeColorTemperature(kelvin)(image=image))\n\n    ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_contrast.py",
    "content": "from __future__ import print_function, division\nimport argparse\n\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Contrast check script\")\n    parser.add_argument(\"--per_channel\", dest=\"per_channel\", action=\"store_true\")\n    args = parser.parse_args()\n\n    augs = []\n    for p in [0.25, 0.5, 1.0, 2.0, (0.5, 1.5), [0.5, 1.0, 1.5]]:\n        augs.append((\"GammaContrast \" + str(p), iaa.GammaContrast(p, per_channel=args.per_channel)))\n\n    for cutoff in [0.25, 0.5, 0.75]:\n        for gain in [5, 10, 15, 20, 25]:\n            augs.append((\"SigmoidContrast \" + str(cutoff) + \" \" + str(gain), iaa.SigmoidContrast(gain, cutoff, per_channel=args.per_channel)))\n\n    for gain in [0.0, 0.25, 0.5, 1.0, 2.0, (0.5, 1.5), [0.5, 1.0, 1.5]]:\n        augs.append((\"LogContrast \" + str(gain), iaa.LogContrast(gain, per_channel=args.per_channel)))\n\n    for alpha in [-1.0, 0.5, 0, 0.5, 1.0, 2.0, (0.5, 1.5), [0.5, 1.0, 1.5]]:\n        augs.append((\"LinearContrast \" + str(alpha), iaa.LinearContrast(alpha, per_channel=args.per_channel)))\n\n    augs.append((\"AllChannelsHistogramEqualization\", iaa.AllChannelsHistogramEqualization()))\n    augs.append((\"HistogramEqualization (Lab)\", iaa.HistogramEqualization(to_colorspace=iaa.HistogramEqualization.Lab)))\n    augs.append((\"HistogramEqualization (HSV)\", iaa.HistogramEqualization(to_colorspace=iaa.HistogramEqualization.HSV)))\n    augs.append((\"HistogramEqualization (HLS)\", iaa.HistogramEqualization(to_colorspace=iaa.HistogramEqualization.HLS)))\n\n    for clip_limit in [0.1, 1, 5, 10]:\n        for tile_grid_size_px in [3, 7]:\n            augs.append((\"AllChannelsCLAHE %d %dx%d\" % (clip_limit, tile_grid_size_px, tile_grid_size_px),\n                         iaa.AllChannelsCLAHE(clip_limit=clip_limit, tile_grid_size_px=tile_grid_size_px,\n                                              per_channel=args.per_channel)))\n\n    for clip_limit in [1, 5, 10, 100, 200]:\n        for tile_grid_size_px in [3, 7, 15]:\n            augs.append((\"CLAHE %d %dx%d\" % (clip_limit, tile_grid_size_px, tile_grid_size_px),\n                         iaa.CLAHE(clip_limit=clip_limit, tile_grid_size_px=tile_grid_size_px)))\n\n    images = [data.astronaut()] * 16\n    images = ia.imresize_many_images(np.uint8(images), (128, 128))\n    for name, aug in augs:\n        print(\"-----------\")\n        print(name)\n        print(\"-----------\")\n        images_aug = aug.augment_images(images)\n        images_aug[0] = images[0]\n        grid = ia.draw_grid(images_aug, rows=4, cols=4)\n        ia.imshow(grid)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_crop_and_pad.py",
    "content": "from __future__ import print_function, division\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nimport numpy as np\n\n\ndef main():\n    image = ia.data.quokka(size=0.5)\n    kps = [ia.KeypointsOnImage(\n        [ia.Keypoint(x=245, y=203), ia.Keypoint(x=365, y=195), ia.Keypoint(x=313, y=269)],\n        shape=(image.shape[0]*2, image.shape[1]*2)\n    )]\n    kps[0] = kps[0].on(image.shape)\n    print(\"image shape:\", image.shape)\n\n    augs = [\n        iaa.CropAndPad(px=50, name=\"pad-by-50px\"),\n        iaa.CropAndPad(px=(10, 20, 30, 40), name=\"pad-by-10-20-30-40px\"),\n        iaa.CropAndPad(percent=0.1, name=\"pad-by-01percent\"),\n        iaa.CropAndPad(percent=(0.01, 0.02, 0.03, 0.04), name=\"pad-by-001-002-003-004percent\"),\n        iaa.CropAndPad(px=-20, name=\"crop-by-20px\"),\n        iaa.CropAndPad(px=(-10, -20, -30, -40), name=\"crop-by-10-20-30-40px\"),\n        iaa.CropAndPad(percent=-0.1, name=\"crop-by-01percent\"),\n        iaa.CropAndPad(percent=(-0.01, -0.02, -0.03, -0.04), name=\"crop-by-001-002-003-004percent\")\n    ]\n\n    augs_many = [\n        iaa.Crop(px=(0, 50), name=\"native-crop-0-to-50px\"),\n        iaa.Crop(px=iap.DiscreteUniform(0, 50), name=\"native-crop-0-to-50px-iap\"),\n        iaa.Pad(px=(0, 50), pad_mode=\"linear_ramp\", pad_cval=(0, 255), name=\"native-pad-0-to-50px-pad-modes\"),\n        iaa.CropAndPad(px=(0, 50), sample_independently=False, name=\"pad-by-0-to-50px-same\"),\n        iaa.CropAndPad(px=(0, 50), name=\"pad-by-0-to-50px\"),\n        iaa.CropAndPad(px=(0, 50), pad_mode=ia.ALL, pad_cval=(0, 255), name=\"pad-by-0-to-50px-random-pad-modes-cvals\"),\n        iaa.CropAndPad(px=((0, 50), (0, 50), (0, 50), (0, 50)), name=\"pad-by-0-to-50px-each\"),\n        iaa.CropAndPad(percent=(0, 0.1), sample_independently=False, name=\"pad-by-0-to-01percent-same\"),\n        iaa.CropAndPad(percent=(0, 0.1), name=\"pad-by-0-to-01percent\"),\n        iaa.CropAndPad(percent=(0, 0.1), pad_mode=ia.ALL, pad_cval=(0, 255),\n                       name=\"pad-by-0-to-01percent-random-pad-modes-cvals\"),\n        iaa.CropAndPad(percent=((0, 0.1), (0, 0.1), (0, 0.1), (0, 0.1)), name=\"pad-by-0-to-01percent-each\"),\n        iaa.CropAndPad(px=(-50, 0), name=\"crop-by-50-to-0px\"),\n        iaa.CropAndPad(px=((-50, 0), (-50, 0), (-50, 0), (-50, 0)), name=\"crop-by-50-to-0px-each\"),\n        iaa.CropAndPad(percent=(-0.1, 0), name=\"crop-by-01-to-0percent\"),\n        iaa.CropAndPad(percent=((-0.1, 0), (-0.1, 0), (-0.1, 0), (-0.1, 0)), name=\"crop-by-01-to-0percent-each\"),\n        iaa.CropAndPad(px=(-50, 50), name=\"pad-and-crop-by-50px\")\n    ]\n\n    print(\"original\", image.shape)\n    ia.imshow(kps[0].draw_on_image(image))\n\n    print(\"-----------------\")\n    print(\"Same aug per image\")\n    print(\"-----------------\")\n    for aug in augs:\n        img_aug = aug.augment_image(image)\n        kps_aug = aug.augment_keypoints(kps)[0]\n        img_aug_kps = kps_aug.draw_on_image(img_aug)\n        print(aug.name, img_aug_kps.shape, img_aug_kps.shape[1]/img_aug_kps.shape[0])\n        ia.imshow(img_aug_kps)\n\n    print(\"-----------------\")\n    print(\"Random aug per image\")\n    print(\"-----------------\")\n    for aug in augs_many:\n        images_aug = []\n        for _ in range(64):\n            aug_det = aug.to_deterministic()\n            img_aug = aug_det.augment_image(image)\n            kps_aug = aug_det.augment_keypoints(kps)[0]\n            img_aug_kps = kps_aug.draw_on_image(img_aug)\n            img_aug_kps = np.pad(img_aug_kps, ((1, 1), (1, 1), (0, 0)), mode=\"constant\", constant_values=255)\n            images_aug.append(img_aug_kps)\n        print(aug.name)\n        ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_cutout.py",
    "content": "from __future__ import print_function, division, absolute_import\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    aug = iaa.Cutout(fill_mode=[\"gaussian\", \"constant\"], cval=(0, 255),\n                     fill_per_channel=0.5)\n    image = ia.data.quokka()\n    images_aug = aug(images=[image] * 16)\n    ia.imshow(ia.draw_grid(images_aug, cols=4, rows=4))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_deprecation_warning.py",
    "content": "from __future__ import print_function, absolute_import, division\n\nimport imgaug as ia\n\n\nclass Dummy1(object):\n    @ia.deprecated(alt_func=\"Foo\")\n    def __init__(self):\n        pass\n\n\nclass Dummy2(object):\n    @ia.deprecated(alt_func=\"Foo\", comment=\"Some example comment.\")\n    def __init__(self):\n        pass\n\n\nclass Dummy3(object):\n    def __init__(self):\n        pass\n\n    @ia.deprecated(alt_func=\"bar()\",\n                   comment=\"Some example comment.\")\n    def foo(self):\n        pass\n\n\n@ia.deprecated(alt_func=\"bar()\", comment=\"Some example comment.\")\ndef foo():\n    pass\n\n\ndef main():\n    Dummy1()\n    Dummy2()\n    Dummy3()\n    foo()\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_directed_edge_detect.py",
    "content": "from __future__ import print_function, division\nfrom itertools import cycle\n\nimport numpy as np\nfrom skimage import data\nimport cv2\n\nfrom imgaug import augmenters as iaa\n\nPOINT_SIZE = 5\nDEG_PER_STEP = 1\nTIME_PER_STEP = 10\n\n\ndef main():\n    image = data.astronaut()\n\n    cv2.namedWindow(\"aug\", cv2.WINDOW_NORMAL)\n    cv2.imshow(\"aug\", image)\n    cv2.waitKey(TIME_PER_STEP)\n\n    height, width = image.shape[0], image.shape[1]\n    center_x = width // 2\n    center_y = height // 2\n    r = int(min(image.shape[0], image.shape[1]) / 3)\n\n    for deg in cycle(np.arange(0, 360, DEG_PER_STEP)):\n        rad = np.deg2rad(deg-90)\n        point_x = int(center_x + r * np.cos(rad))\n        point_y = int(center_y + r * np.sin(rad))\n\n        direction = deg / 360\n        aug = iaa.DirectedEdgeDetect(alpha=1.0, direction=direction)\n        img_aug = aug.augment_image(image)\n        img_aug[point_y-POINT_SIZE:point_y+POINT_SIZE+1, point_x-POINT_SIZE:point_x+POINT_SIZE+1, :] =\\\n            np.array([0, 255, 0])\n\n        cv2.imshow(\"aug\", img_aug)\n        cv2.waitKey(TIME_PER_STEP)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_elastic_transformation.py",
    "content": "from __future__ import print_function, division\n\nimport imageio\nimport numpy as np\nimport six.moves as sm\nfrom skimage import data\nfrom scipy import ndimage\nimport cv2\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug.augmenters import meta\nimport imgaug.random as iarandom\n\n\ndef main():\n    image = data.astronaut()\n    image = ia.imresize_single_image(image, (128, 128))\n\n    # check if scipy and cv2 remap similarly\n    rs = iarandom.RNG(1)\n    aug_scipy = ElasticTransformationScipy(alpha=30, sigma=3, random_state=rs, deterministic=True)\n    aug_cv2 = ElasticTransformationCv2(alpha=30, sigma=3, random_state=rs, deterministic=True)\n    augs_scipy = aug_scipy.augment_images([image] * 8)\n    augs_cv2 = aug_cv2.augment_images([image] * 8)\n    ia.imshow(ia.draw_grid(augs_scipy + augs_cv2, rows=2))\n\n    # check behaviour for multiple consecutive batches\n    aug = iaa.ElasticTransformation(alpha=(5, 100), sigma=(3, 5))\n    images1 = aug(images=[np.copy(image) for _ in range(10)])\n    images2 = aug(images=[np.copy(image) for _ in range(10)])\n    images3 = aug(images=[np.copy(image) for _ in range(10)])\n    ia.imshow(ia.draw_grid(images1 + images2 + images3, rows=3))\n\n    print(\"alpha=vary, sigma=0.25\")\n    augs = [iaa.ElasticTransformation(alpha=alpha, sigma=0.25) for alpha in np.arange(0.0, 50.0, 0.1)]\n    images_aug = [aug.augment_image(image) for aug in augs]\n    ia.imshow(ia.draw_grid(images_aug, cols=10))\n\n    print(\"alpha=vary, sigma=1.0\")\n    augs = [iaa.ElasticTransformation(alpha=alpha, sigma=1.0) for alpha in np.arange(0.0, 50.0, 0.1)]\n    images_aug = [aug.augment_image(image) for aug in augs]\n    ia.imshow(ia.draw_grid(images_aug, cols=10))\n\n    print(\"alpha=vary, sigma=3.0\")\n    augs = [iaa.ElasticTransformation(alpha=alpha, sigma=3.0) for alpha in np.arange(0.0, 50.0, 0.1)]\n    images_aug = [aug.augment_image(image) for aug in augs]\n    ia.imshow(ia.draw_grid(images_aug, cols=10))\n\n    print(\"alpha=vary, sigma=5.0\")\n    augs = [iaa.ElasticTransformation(alpha=alpha, sigma=5.0) for alpha in np.arange(0.0, 50.0, 0.1)]\n    images_aug = [aug.augment_image(image) for aug in augs]\n    ia.imshow(ia.draw_grid(images_aug, cols=10))\n\n    print(\"alpha=1.0, sigma=vary\")\n    augs = [iaa.ElasticTransformation(alpha=1.0, sigma=sigma) for sigma in np.arange(0.0, 50.0, 0.1)]\n    images_aug = [aug.augment_image(image) for aug in augs]\n    ia.imshow(ia.draw_grid(images_aug, cols=10))\n\n    print(\"alpha=10.0, sigma=vary\")\n    augs = [iaa.ElasticTransformation(alpha=10.0, sigma=sigma) for sigma in np.arange(0.0, 50.0, 0.1)]\n    images_aug = [aug.augment_image(image) for aug in augs]\n    ia.imshow(ia.draw_grid(images_aug, cols=10))\n\n    kps = ia.KeypointsOnImage(\n        [ia.Keypoint(x=1, y=1),\n         ia.Keypoint(x=50, y=24), ia.Keypoint(x=42, y=96), ia.Keypoint(x=88, y=106), ia.Keypoint(x=88, y=53),\n         ia.Keypoint(x=0, y=0), ia.Keypoint(x=128, y=128), ia.Keypoint(x=-20, y=30), ia.Keypoint(x=20, y=-30),\n         ia.Keypoint(x=-20, y=-30)],\n        shape=image.shape\n    )\n\n    images = []\n    params = [\n        (0.0, 0.0),\n        (0.2, 0.2),\n        (2.0, 0.25),\n        (0.25, 3.0),\n        (2.0, 3.0),\n        (6.0, 3.0),\n        (12.0, 3.0),\n        (50.0, 5.0),\n        (100.0, 5.0),\n        (100.0, 10.0)\n    ]\n\n    for (alpha, sigma) in params:\n        images_row = []\n        seqs_row = [\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"constant\", cval=0, order=0),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"constant\", cval=128, order=0),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"constant\", cval=255, order=0),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"constant\", cval=0, order=1),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"constant\", cval=128, order=1),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"constant\", cval=255, order=1),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"constant\", cval=0, order=3),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"constant\", cval=128, order=3),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"constant\", cval=255, order=3),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"nearest\", order=0),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"nearest\", order=1),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"nearest\", order=2),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"nearest\", order=3),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"reflect\", order=0),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"reflect\", order=1),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"reflect\", order=2),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"reflect\", order=3),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"wrap\", order=0),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"wrap\", order=1),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"wrap\", order=2),\n            iaa.ElasticTransformation(alpha=alpha, sigma=sigma, mode=\"wrap\", order=3)\n        ]\n\n        for seq in seqs_row:\n            seq_det = seq.to_deterministic()\n            image_aug = seq_det.augment_image(image)\n            kps_aug = seq_det.augment_keypoints([kps])[0]\n            image_aug_kp = np.copy(image_aug)\n            image_aug_kp = kps_aug.draw_on_image(image_aug_kp, size=3)\n            images_row.append(image_aug_kp)\n\n        images.append(np.hstack(images_row))\n\n    ia.imshow(np.vstack(images))\n    imageio.imwrite(\"elastic_transformations.jpg\", np.vstack(images))\n\n\n#\n# These classes are copies of ElasticTransformation and either always remap via scipy or always via cv2\n# This way, the remapping can be compared to make sure that scipy and cv2 lead to similar remapping behaviours\n# (important if two arrays with different dtypes are supposed to be remapped similarly)\n#\nclass ElasticTransformationScipy(iaa.ElasticTransformation):\n    @classmethod\n    def map_coordinates(cls, image, dx, dy, order=1, cval=0, mode=\"constant\"):\n        # small debug message to be sure that the right function is executed\n        print(\"map_coordinates() with scipy\")\n\n        if order == 0 and image.dtype.name in [\"uint64\", \"int64\"]:\n            raise Exception((\"dtypes uint64 and int64 are only supported in ElasticTransformation for order=0, \"\n                             + \"got order=%d with dtype=%s.\") % (order, image.dtype.name))\n\n        input_dtype = image.dtype\n        if image.dtype.name == \"bool\":\n            image = image.astype(np.float32)\n        elif order == 1 and image.dtype.name in [\"int8\", \"int16\", \"int32\"]:\n            image = image.astype(np.float64)\n        elif order >= 2 and image.dtype.name == \"int8\":\n            image = image.astype(np.int16)\n        elif order >= 2 and image.dtype.name == \"int32\":\n            image = image.astype(np.float64)\n\n        shrt_max = 32767\n        backend = \"cv2\"\n        if order == 0:\n            bad_dtype_cv2 = (image.dtype.name in [\"uint32\", \"uint64\", \"int64\", \"float128\", \"bool\"])\n        elif order == 1:\n            bad_dtype_cv2 = (image.dtype.name in [\"uint32\", \"uint64\", \"int8\", \"int16\", \"int32\", \"int64\", \"float128\",\n                                                  \"bool\"])\n        else:\n            bad_dtype_cv2 = (image.dtype.name in [\"uint32\", \"uint64\", \"int8\", \"int32\", \"int64\", \"float128\", \"bool\"])\n\n        bad_dx_shape_cv2 = (dx.shape[0] >= shrt_max or dx.shape[1] >= shrt_max)\n        bad_dy_shape_cv2 = (dy.shape[0] >= shrt_max or dy.shape[1] >= shrt_max)\n        if bad_dtype_cv2 or bad_dx_shape_cv2 or bad_dy_shape_cv2:\n            backend = \"scipy\"\n\n        ia.do_assert(image.ndim == 3)\n        result = np.copy(image)\n        height, width = image.shape[0:2]\n        # True was added here, only difference to usual code\n        if True or backend == \"scipy\":\n            h, w = image.shape[0:2]\n            y, x = np.meshgrid(np.arange(h).astype(np.float32), np.arange(w).astype(np.float32), indexing='ij')\n            x_shifted = x + (-1) * dx\n            y_shifted = y + (-1) * dy\n\n            for c in sm.xrange(image.shape[2]):\n                remapped_flat = ndimage.interpolation.map_coordinates(\n                    image[..., c],\n                    (y_shifted.flatten(), x_shifted.flatten()),\n                    order=order,\n                    cval=cval,\n                    mode=mode\n                )\n                remapped = remapped_flat.reshape((height, width))\n                result[..., c] = remapped\n        else:\n            h, w, nb_channels = image.shape\n\n            y, x = np.meshgrid(np.arange(h).astype(np.float32), np.arange(w).astype(np.float32), indexing='ij')\n            x_shifted = x + (-1) * dx\n            y_shifted = y + (-1) * dy\n\n            if image.dtype.kind == \"f\":\n                cval = float(cval)\n            else:\n                cval = int(cval)\n            border_mode = cls._MAPPING_MODE_SCIPY_CV2[mode]\n            interpolation = cls._MAPPING_ORDER_SCIPY_CV2[order]\n\n            is_nearest_neighbour = (interpolation == cv2.INTER_NEAREST)\n            map1, map2 = cv2.convertMaps(x_shifted, y_shifted, cv2.CV_16SC2, nninterpolation=is_nearest_neighbour)\n            # remap only supports up to 4 channels\n            if nb_channels <= 4:\n                result = cv2.remap(image, map1, map2, interpolation=interpolation, borderMode=border_mode, borderValue=cval)\n                if image.ndim == 3 and result.ndim == 2:\n                    result = result[..., np.newaxis]\n            else:\n                current_chan_idx = 0\n                result = []\n                while current_chan_idx < nb_channels:\n                    channels = image[..., current_chan_idx:current_chan_idx+4]\n                    result_c =  cv2.remap(channels, map1, map2, interpolation=interpolation, borderMode=border_mode,\n                                          borderValue=cval)\n                    if result_c.ndim == 2:\n                        result_c = result_c[..., np.newaxis]\n                    result.append(result_c)\n                    current_chan_idx += 4\n                result = np.concatenate(result, axis=2)\n\n        if result.dtype.name != input_dtype.name:\n            result = meta.restore_dtypes_(result, input_dtype)\n\n        return result\n\n\nclass ElasticTransformationCv2(iaa.ElasticTransformation):\n    @classmethod\n    def map_coordinates(cls, image, dx, dy, order=1, cval=0, mode=\"constant\"):\n        # small debug message to be sure that the right function is executed\n        print(\"map_coordinates() with cv2\")\n\n        if order == 0 and image.dtype.name in [\"uint64\", \"int64\"]:\n            raise Exception((\"dtypes uint64 and int64 are only supported in ElasticTransformation for order=0, \"\n                             + \"got order=%d with dtype=%s.\") % (order, image.dtype.name))\n\n        input_dtype = image.dtype\n        if image.dtype.name == \"bool\":\n            image = image.astype(np.float32)\n        elif order == 1 and image.dtype.name in [\"int8\", \"int16\", \"int32\"]:\n            image = image.astype(np.float64)\n        elif order >= 2 and image.dtype.name == \"int8\":\n            image = image.astype(np.int16)\n        elif order >= 2 and image.dtype.name == \"int32\":\n            image = image.astype(np.float64)\n\n        shrt_max = 32767\n        backend = \"cv2\"\n        if order == 0:\n            bad_dtype_cv2 = (image.dtype.name in [\"uint32\", \"uint64\", \"int64\", \"float128\", \"bool\"])\n        elif order == 1:\n            bad_dtype_cv2 = (image.dtype.name in [\"uint32\", \"uint64\", \"int8\", \"int16\", \"int32\", \"int64\", \"float128\",\n                                                  \"bool\"])\n        else:\n            bad_dtype_cv2 = (image.dtype.name in [\"uint32\", \"uint64\", \"int8\", \"int32\", \"int64\", \"float128\", \"bool\"])\n\n        bad_dx_shape_cv2 = (dx.shape[0] >= shrt_max or dx.shape[1] >= shrt_max)\n        bad_dy_shape_cv2 = (dy.shape[0] >= shrt_max or dy.shape[1] >= shrt_max)\n        if bad_dtype_cv2 or bad_dx_shape_cv2 or bad_dy_shape_cv2:\n            backend = \"scipy\"\n\n        ia.do_assert(image.ndim == 3)\n        result = np.copy(image)\n        height, width = image.shape[0:2]\n        # False was added here, only difference to usual code\n        if False or backend == \"scipy\":\n            h, w = image.shape[0:2]\n            y, x = np.meshgrid(np.arange(h).astype(np.float32), np.arange(w).astype(np.float32), indexing='ij')\n            x_shifted = x + (-1) * dx\n            y_shifted = y + (-1) * dy\n\n            for c in sm.xrange(image.shape[2]):\n                remapped_flat = ndimage.interpolation.map_coordinates(\n                    image[..., c],\n                    (x_shifted.flatten(), y_shifted.flatten()),\n                    order=order,\n                    cval=cval,\n                    mode=mode\n                )\n                remapped = remapped_flat.reshape((height, width))\n                result[..., c] = remapped\n        else:\n            h, w, nb_channels = image.shape\n\n            y, x = np.meshgrid(np.arange(h).astype(np.float32), np.arange(w).astype(np.float32), indexing='ij')\n            x_shifted = x + (-1) * dx\n            y_shifted = y + (-1) * dy\n\n            if image.dtype.kind == \"f\":\n                cval = float(cval)\n            else:\n                cval = int(cval)\n            border_mode = cls._MAPPING_MODE_SCIPY_CV2[mode]\n            interpolation = cls._MAPPING_ORDER_SCIPY_CV2[order]\n\n            is_nearest_neighbour = (interpolation == cv2.INTER_NEAREST)\n            map1, map2 = cv2.convertMaps(x_shifted, y_shifted, cv2.CV_16SC2, nninterpolation=is_nearest_neighbour)\n            # remap only supports up to 4 channels\n            if nb_channels <= 4:\n                result = cv2.remap(image, map1, map2, interpolation=interpolation, borderMode=border_mode, borderValue=cval)\n                if image.ndim == 3 and result.ndim == 2:\n                    result = result[..., np.newaxis]\n            else:\n                current_chan_idx = 0\n                result = []\n                while current_chan_idx < nb_channels:\n                    channels = image[..., current_chan_idx:current_chan_idx+4]\n                    result_c =  cv2.remap(channels, map1, map2, interpolation=interpolation, borderMode=border_mode,\n                                          borderValue=cval)\n                    if result_c.ndim == 2:\n                        result_c = result_c[..., np.newaxis]\n                    result.append(result_c)\n                    current_chan_idx += 4\n                result = np.concatenate(result, axis=2)\n\n        if result.dtype.name != input_dtype.name:\n            result = meta.restore_dtypes_(result, input_dtype)\n\n        return result\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_fast_snowy_landscape.py",
    "content": "from __future__ import print_function, division\n\nimport imageio\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    image = imageio.imread(\"https://upload.wikimedia.org/wikipedia/commons/8/89/Kukle%2CCzech_Republic..jpg\",\n                           format=\"jpg\")\n    augs = [\n        (\"iaa.FastSnowyLandscape(64, 1.5)\", iaa.FastSnowyLandscape(64, 1.5)),\n        (\"iaa.FastSnowyLandscape(128, 1.5)\", iaa.FastSnowyLandscape(128, 1.5)),\n        (\"iaa.FastSnowyLandscape(200, 1.5)\", iaa.FastSnowyLandscape(200, 1.5)),\n        (\"iaa.FastSnowyLandscape(64, 2.5)\", iaa.FastSnowyLandscape(64, 2.5)),\n        (\"iaa.FastSnowyLandscape(128, 2.5)\", iaa.FastSnowyLandscape(128, 2.5)),\n        (\"iaa.FastSnowyLandscape(200, 2.5)\", iaa.FastSnowyLandscape(200, 2.5)),\n        (\"iaa.FastSnowyLandscape(64, 3.5)\", iaa.FastSnowyLandscape(64, 3.5)),\n        (\"iaa.FastSnowyLandscape(128, 3.5)\", iaa.FastSnowyLandscape(128, 3.5)),\n        (\"iaa.FastSnowyLandscape(200, 3.5)\", iaa.FastSnowyLandscape(200, 3.5)),\n        (\"iaa.FastSnowyLandscape()\", iaa.FastSnowyLandscape())\n    ]\n\n    for descr, aug in augs:\n        print(descr)\n        images_aug = aug.augment_images([image] * 64)\n        ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_fixed_size.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    image = ia.data.quokka(size=0.5)\n    kps = [ia.KeypointsOnImage(\n        [ia.Keypoint(x=245, y=203), ia.Keypoint(x=365, y=195), ia.Keypoint(x=313, y=269)],\n        shape=(image.shape[0]*2, image.shape[1]*2)\n    )]\n    kps[0] = kps[0].on(image.shape)\n    print(\"image shape:\", image.shape)\n    \n    augs_many = [\n        iaa.PadToFixedSize(200, 200, name=\"pad-width200-height200\"),\n        iaa.PadToFixedSize(200, 322, name=\"pad-width200-height322\"),\n        iaa.PadToFixedSize(200, 400, name=\"pad-width200-height400\"),\n        iaa.PadToFixedSize(480, 200, name=\"pad-width480-height200\"),\n        iaa.PadToFixedSize(480, 322, name=\"pad-width480-height322\"),  # input size == output size\n        iaa.PadToFixedSize(480, 400, name=\"pad-width480-height400\"),\n        iaa.PadToFixedSize(600, 200, name=\"pad-width600-height200\"),\n        iaa.PadToFixedSize(600, 322, name=\"pad-width600-height322\"),\n        iaa.PadToFixedSize(600, 400, name=\"pad-width600-height400\"),\n\n        iaa.CropToFixedSize(200, 200, name=\"crop-width200-height200\"),\n        iaa.CropToFixedSize(200, 322, name=\"crop-width200-height322\"),\n        iaa.CropToFixedSize(200, 400, name=\"crop-width200-height400\"),\n        iaa.CropToFixedSize(480, 200, name=\"crop-width480-height200\"),\n        iaa.CropToFixedSize(480, 322, name=\"crop-width480-height322\"),  # input size == output size\n        iaa.CropToFixedSize(480, 400, name=\"crop-width480-height400\"),\n        iaa.CropToFixedSize(600, 200, name=\"crop-width600-height200\"),\n        iaa.CropToFixedSize(600, 322, name=\"crop-width600-height322\"),\n        iaa.CropToFixedSize(600, 400, name=\"crop-width600-height400\"),\n\n        iaa.Sequential([\n            iaa.PadToFixedSize(200, 200),\n            iaa.CropToFixedSize(200, 200)\n        ], name=\"pad-crop-width200-height200\"),\n        iaa.Sequential([\n            iaa.PadToFixedSize(400, 400),\n            iaa.CropToFixedSize(400, 400)\n        ], name=\"pad-crop-width400-height400\"),\n        iaa.Sequential([\n            iaa.PadToFixedSize(600, 600),\n            iaa.CropToFixedSize(600, 600)\n        ], name=\"pad-crop-width600-height600\"),\n\n        iaa.Sequential([\n            iaa.CropToFixedSize(200, 200),\n            iaa.PadToFixedSize(200, 200)\n        ], name=\"crop-pad-width200-height200\"),\n        iaa.Sequential([\n            iaa.CropToFixedSize(400, 400),\n            iaa.PadToFixedSize(400, 400)\n        ], name=\"crop-pad-width400-height400\"),\n        iaa.Sequential([\n            iaa.CropToFixedSize(600, 600),\n            iaa.PadToFixedSize(600, 600)\n        ], name=\"crop-pad-width600-height600\"),\n\n    ]\n\n    print(\"original\", image.shape)\n    ia.imshow(kps[0].draw_on_image(image))\n\n    print(\"-----------------\")\n    print(\"Random aug per image\")\n    print(\"-----------------\")\n    for aug in augs_many:\n        images_aug = []\n        for _ in range(36):\n            aug_det = aug.to_deterministic()\n            img_aug = aug_det.augment_image(image)\n            kps_aug = aug_det.augment_keypoints(kps)[0]\n            img_aug_kps = kps_aug.draw_on_image(img_aug)\n            img_aug_kps = np.pad(img_aug_kps, ((1, 1), (1, 1), (0, 0)), mode=\"constant\", constant_values=255)\n            images_aug.append(img_aug_kps)\n        print(aug.name)\n        ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_flip_performance.py",
    "content": "from __future__ import print_function, division\nimport timeit\nimport argparse\nimport numpy as np\n\nCOMMANDS_HORIZONTAL_FLIPS = [\n    (\"slice\",\n     \"arr2 = arr[:, ::-1, :]; \"),\n    (\"slice, contig\",\n     \"arr2 = arr[:, ::-1, :]; \"\n     \"arr2 = np.ascontiguousarray(arr2);\"),\n    (\"fliplr\",\n     \"arr2 = np.fliplr(arr); \"),\n    (\"fliplr contig\",\n     \"arr2 = np.fliplr(arr); \"\n     \"arr2 = np.ascontiguousarray(arr2);\"),\n    (\"cv2\",\n     \"arr2 = cv2.flip(arr, 1); \"\n     \"arr2 = arr2 if arr2.ndim == 3 else arr2[..., np.newaxis]; \"),\n    (\"cv2 contig\",\n     \"arr2 = cv2.flip(arr, 1); \"\n     \"arr2 = arr2 if arr2.ndim == 3 else arr2[..., np.newaxis]; \"\n     \"arr2 = np.ascontiguousarray(arr2); \"),\n    (\"fort cv2\",\n     \"arr = np.asfortranarray(arr); \"\n     \"arr2 = cv2.flip(arr, 1); \"\n     \"arr2 = arr2 if arr2.ndim == 3 else arr2[..., np.newaxis]; \"),\n    (\"fort cv2 contig\",\n     \"arr = np.asfortranarray(arr); \"\n     \"arr2 = cv2.flip(arr, 1); \"\n     \"arr2 = arr2 if arr2.ndim == 3 else arr2[..., np.newaxis]; \"\n     \"arr2 = np.ascontiguousarray(arr2); \"),\n    (\"cv2_\",\n     \"arr = cv2.flip(arr, 1, dst=arr); \"\n     \"arr = arr if arr.ndim == 3 else arr[..., np.newaxis]; \"),\n    (\"cv2_ contig\",\n     \"arr = cv2.flip(arr, 1, dst=arr); \"\n     \"arr = np.ascontiguousarray(arr); \"\n     \"arr = arr if arr.ndim == 3 else arr[..., np.newaxis]; \"),\n    (\"fort cv2_\",\n     \"arr = np.asfortranarray(arr); \"\n     \"arr = cv2.flip(arr, 1, dst=arr); \"\n     \"arr = arr if arr.ndim == 3 else arr[..., np.newaxis]; \"),\n    (\"fort cv2_ contig\",\n     \"arr = np.asfortranarray(arr); \"\n     \"arr = cv2.flip(arr, 1, dst=arr); \"\n     \"arr = np.ascontiguousarray(arr); \"\n     \"arr = arr if arr.ndim == 3 else arr[..., np.newaxis]; \"),\n    (\"cv2_ get\",\n     \"arr = cv2.flip(arr, 1, dst=arr); \"\n     \"arr = arr.get(); \"\n     \"arr = arr if arr.ndim == 3 else arr[..., np.newaxis]; \"),\n    (\"cv2_ get contig\",\n     \"arr = cv2.flip(arr, 1, dst=arr); \"\n     \"arr = arr.get(); \"\n     \"arr = arr if arr.ndim == 3 else arr[..., np.newaxis]; \"\n     \"arr = np.ascontiguousarray(arr); \"),\n    (\"fort cv2_ get\",\n     \"arr = np.asfortranarray(arr); \"\n     \"arr = cv2.flip(arr, 1, dst=arr); \"\n     \"arr = arr.get(); \"\n     \"arr = arr if arr.ndim == 3 else arr[..., np.newaxis]; \"),\n    (\"fort cv2_ get contig\",\n     \"arr = np.asfortranarray(arr); \"\n     \"arr = cv2.flip(arr, 1, dst=arr); \"\n     \"arr = arr.get(); \"\n     \"arr = arr if arr.ndim == 3 else arr[..., np.newaxis]; \"\n     \"arr = np.ascontiguousarray(arr); \")\n]\n\nCOMMANDS_VERTICAL_FLIPS = []\nfor name, command in COMMANDS_HORIZONTAL_FLIPS:\n    name = name.replace(\"fliplr\", \"flipud\")\n    command = command.replace(\"[:, ::-1, :]\", \"[::-1, :, :]\")\n    command = command.replace(\"fliplr\", \"flipud\")\n    command = command.replace(\"cv2.flip(arr, 1\", \"cv2.flip(arr, 0\")\n    COMMANDS_VERTICAL_FLIPS.append((name, command))\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description=\"Test performance of horizontal or vertical flip methods\")\n    parser.add_argument(\"--type\", help=\"horizontal|vertical\", required=True)\n    args = parser.parse_args()\n\n    assert args.type in [\"horizontal\", \"vertical\"]\n\n    commands = (\n        COMMANDS_HORIZONTAL_FLIPS\n        if args.type == \"horizontal\"\n        else COMMANDS_VERTICAL_FLIPS)\n\n    number = 10000\n    for dt in [\"bool\",\n               \"uint8\", \"uint16\", \"uint32\", \"uint64\",\n               \"int8\", \"int16\", \"int32\", \"int64\",\n               \"float16\", \"float32\", \"float64\", \"float128\"]:\n        print(\"\")\n        print(\"----------\")\n        print(dt)\n        print(\"----------\")\n\n        last_fliplr_time = None\n        for command_i_title, commands_i in commands:\n            try:\n                # verify that dtype does not change\n                arr_name = \"arr2\"\n                if \"arr2\" not in commands_i:\n                    arr_name = \"arr\"\n                _ = timeit.repeat(\n                    \"%s assert %s.dtype.name == '%s', ('Got dtype ' + %s.dtype.name)\" % (\n                        commands_i, arr_name, dt, arr_name),\n                    setup=\"import cv2; \"\n                          \"import numpy as np; \"\n                          \"arr = np.ones((224, 224, 3), dtype=np.%s)\" % (dt,),\n                    repeat=1, number=1)\n\n                times = timeit.repeat(\n                    commands_i,\n                    setup=\"import cv2; \"\n                          \"import numpy as np; \"\n                          \"arr = np.ones((224, 224, 3), dtype=np.%s)\" % (dt,),\n                    repeat=number, number=1)\n                time = np.average(times) * 1000\n                if command_i_title == \"slice, contig\":\n                    last_fliplr_time = time\n                if \"cv2\" not in command_i_title:\n                    print(\"{:>20s} {:.5f}ms\".format(command_i_title, time))\n                else:\n                    rel_time = last_fliplr_time / time\n                    print(\"{:>20s} {:.5f}ms ({:.2f}x)\".format(\n                        command_i_title, time, rel_time))\n            except (AssertionError, AttributeError, TypeError) as exc:\n                print(\"{:>20s} Error: {}\".format(command_i_title, str(exc)))\n                # import traceback\n                # traceback.print_exc()\n\n    augs = [\n        (\"Add\", \"iaa.Add(10)\"),\n        (\"Affine\", \"iaa.Affine(translate_px={'x': 10})\"),\n        (\"AverageBlur\", \"iaa.AverageBlur(3)\")\n    ]\n    for aug_name, aug_command in augs:\n        print(\"\")\n        print(\"==============================\")\n        print(\"flip method followed by %s\" % (aug_name,))\n        print(\"==============================\")\n\n        number = 5000\n        for command_i_title, commands_i in commands:\n            dt = \"uint8\"\n\n            try:\n                arr_name = \"arr\"\n                if \"arr2\" in commands_i:\n                    arr_name = \"arr2\"\n\n                _ = timeit.repeat(\n                        \"%s assert %s.dtype.name == '%s', ('Got dtype ' + %s.dtype.name)\" % (\n                            commands_i, arr_name, dt, arr_name),\n                        setup=\"import cv2; \"\n                              \"import numpy as np; \"\n                              \"arr = np.ones((224, 224, 3), dtype=np.%s)\" % (dt,),\n                        repeat=1, number=1)\n\n                times = timeit.repeat(\n                    \"%s _ = aug(image=%s);\" % (commands_i, arr_name),\n                    setup=\"import cv2; \"\n                          \"import numpy as np; \"\n                          \"import imgaug.augmenters as iaa; \"\n                          \"arr = np.ones((224, 224, 3), dtype=np.%s); \"\n                          \"aug = %s\" % (dt, aug_command),\n                    repeat=number, number=1)\n                time = np.average(times) * 1000\n                print(\"{:>20s} {:.5f}ms\".format(command_i_title, time))\n            except (AssertionError, AttributeError, TypeError) as exc:\n                print(\"{:>20s} Error: {}\".format(command_i_title, str(exc)))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_fog.py",
    "content": "from __future__ import print_function, division\n\nimport imageio\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    for size in [0.1, 0.2, 1.0]:\n        image = imageio.imread(\"https://upload.wikimedia.org/wikipedia/commons/8/89/Kukle%2CCzech_Republic..jpg\",\n                               format=\"jpg\")\n        image = ia.imresize_single_image(image, size, \"cubic\")\n        print(image.shape)\n        augs = [\n            (\"iaa.Fog()\", iaa.Fog())\n        ]\n\n        for descr, aug in augs:\n            print(descr)\n            images_aug = aug.augment_images([image] * 64)\n            ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_heatmaps.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    quokka = ia.data.quokka(size=0.5)\n    h, w = quokka.shape[0:2]\n    heatmap = np.zeros((h, w), dtype=np.float32)\n    heatmap[70:120, 90:150] = 0.1\n    heatmap[30:70, 50:65] = 0.5\n    heatmap[20:50, 55:85] = 1.0\n    heatmap[120:140, 0:20] = 0.75\n\n    heatmaps = ia.HeatmapsOnImage(heatmap[..., np.newaxis], quokka.shape)\n\n    print(\"Affine...\")\n    aug = iaa.Affine(translate_px={\"x\": 20}, mode=\"constant\", cval=128)\n    quokka_aug = aug.augment_image(quokka)\n    heatmaps_aug = aug.augment_heatmaps([heatmaps])[0]\n    heatmaps_drawn = heatmaps.draw_on_image(quokka)\n    heatmaps_aug_drawn = heatmaps_aug.draw_on_image(quokka_aug)\n\n    ia.imshow(\n        np.hstack([\n            heatmaps_drawn[0],\n            heatmaps_aug_drawn[0]\n        ])\n    )\n\n    print(\"Affine with mode=edge...\")\n    aug = iaa.Affine(translate_px={\"x\": 20}, mode=\"edge\")\n    quokka_aug = aug.augment_image(quokka)\n    heatmaps_aug = aug.augment_heatmaps([heatmaps])[0]\n    heatmaps_drawn = heatmaps.draw_on_image(quokka)\n    heatmaps_aug_drawn = heatmaps_aug.draw_on_image(quokka_aug)\n\n    ia.imshow(\n        np.hstack([\n            heatmaps_drawn[0],\n            heatmaps_aug_drawn[0]\n        ])\n    )\n\n    print(\"PiecewiseAffine...\")\n    aug = iaa.PiecewiseAffine(scale=0.04)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    heatmaps_aug = aug_det.augment_heatmaps([heatmaps])[0]\n    heatmaps_drawn = heatmaps.draw_on_image(quokka)\n    heatmaps_aug_drawn = heatmaps_aug.draw_on_image(quokka_aug)\n\n    ia.imshow(\n        np.hstack([\n            heatmaps_drawn[0],\n            heatmaps_aug_drawn[0]\n        ])\n    )\n\n    print(\"PerspectiveTransform...\")\n    aug = iaa.PerspectiveTransform(scale=0.04)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    heatmaps_aug = aug_det.augment_heatmaps([heatmaps])[0]\n    heatmaps_drawn = heatmaps.draw_on_image(quokka)\n    heatmaps_aug_drawn = heatmaps_aug.draw_on_image(quokka_aug)\n\n    ia.imshow(\n        np.hstack([\n            heatmaps_drawn[0],\n            heatmaps_aug_drawn[0]\n        ])\n    )\n\n    print(\"ElasticTransformation alpha=3, sig=0.5...\")\n    aug = iaa.ElasticTransformation(alpha=3.0, sigma=0.5)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    heatmaps_aug = aug_det.augment_heatmaps([heatmaps])[0]\n    heatmaps_drawn = heatmaps.draw_on_image(quokka)\n    heatmaps_aug_drawn = heatmaps_aug.draw_on_image(quokka_aug)\n\n    ia.imshow(\n        np.hstack([\n            heatmaps_drawn[0],\n            heatmaps_aug_drawn[0]\n        ])\n    )\n\n    print(\"ElasticTransformation alpha=10, sig=3...\")\n    aug = iaa.ElasticTransformation(alpha=10.0, sigma=3.0)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    heatmaps_aug = aug_det.augment_heatmaps([heatmaps])[0]\n    heatmaps_drawn = heatmaps.draw_on_image(quokka)\n    heatmaps_aug_drawn = heatmaps_aug.draw_on_image(quokka_aug)\n\n    ia.imshow(\n        np.hstack([\n            heatmaps_drawn[0],\n            heatmaps_aug_drawn[0]\n        ])\n    )\n\n    print(\"CopAndPad mode=constant...\")\n    aug = iaa.CropAndPad(px=(-10, 10, 15, -15), pad_mode=\"constant\", pad_cval=128)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    heatmaps_aug = aug_det.augment_heatmaps([heatmaps])[0]\n    heatmaps_drawn = heatmaps.draw_on_image(quokka)\n    heatmaps_aug_drawn = heatmaps_aug.draw_on_image(quokka_aug)\n\n    ia.imshow(\n        np.hstack([\n            heatmaps_drawn[0],\n            heatmaps_aug_drawn[0]\n        ])\n    )\n\n    print(\"CopAndPad mode=constant + percent...\")\n    aug = iaa.CropAndPad(percent=(-0.05, 0.05, 0.1, -0.1), pad_mode=\"constant\", pad_cval=128)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    heatmaps_aug = aug_det.augment_heatmaps([heatmaps])[0]\n    heatmaps_drawn = heatmaps.draw_on_image(quokka)\n    heatmaps_aug_drawn = heatmaps_aug.draw_on_image(quokka_aug)\n\n    ia.imshow(\n        np.hstack([\n            heatmaps_drawn[0],\n            heatmaps_aug_drawn[0]\n        ])\n    )\n\n    print(\"CropAndPad mode=edge...\")\n    aug = iaa.CropAndPad(px=(-10, 10, 15, -15), pad_mode=\"edge\")\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    heatmaps_aug = aug_det.augment_heatmaps([heatmaps])[0]\n    heatmaps_drawn = heatmaps.draw_on_image(quokka)\n    heatmaps_aug_drawn = heatmaps_aug.draw_on_image(quokka_aug)\n\n    ia.imshow(\n        np.hstack([\n            heatmaps_drawn[0],\n            heatmaps_aug_drawn[0]\n        ])\n    )\n\n    print(\"Resize...\")\n    aug = iaa.Resize(0.5, interpolation=\"nearest\")\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    heatmaps_aug = aug_det.augment_heatmaps([heatmaps])[0]\n    heatmaps_drawn = heatmaps.draw_on_image(quokka)\n    heatmaps_aug_drawn = heatmaps_aug.draw_on_image(quokka_aug)\n\n    ia.imshow(ia.draw_grid([heatmaps_drawn[0], heatmaps_aug_drawn[0]], cols=2))\n\n    print(\"Alpha...\")\n    aug = iaa.Alpha(0.7, iaa.Affine(rotate=20))\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    heatmaps_aug = aug_det.augment_heatmaps([heatmaps])[0]\n    heatmaps_drawn = heatmaps.draw_on_image(quokka)\n    heatmaps_aug_drawn = heatmaps_aug.draw_on_image(quokka_aug)\n\n    ia.imshow(\n        np.hstack([\n            heatmaps_drawn[0],\n            heatmaps_aug_drawn[0]\n        ])\n    )\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_impulse_noise.py",
    "content": "from __future__ import print_function, division\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    img = ia.data.quokka(0.5)\n    mul = 0.01\n    augs = [\n        (\"iaa.ImpulseNoise(p=0*mul)\", iaa.ImpulseNoise(p=0*mul)),\n        (\"iaa.ImpulseNoise(p=1*mul)\", iaa.ImpulseNoise(p=1*mul)),\n        (\"iaa.ImpulseNoise(p=2*mul)\", iaa.ImpulseNoise(p=2*mul)),\n        (\"iaa.ImpulseNoise(p=3*mul)\", iaa.ImpulseNoise(p=3*mul)),\n        (\"iaa.ImpulseNoise(p=(0*mul, 1*mul))\", iaa.ImpulseNoise(p=(0*mul, 1*mul))),\n        (\"iaa.ImpulseNoise(p=[0*mul, 1*mul, 2*mul])\", iaa.ImpulseNoise(p=[0*mul, 1*mul, 2*mul]))\n    ]\n    for descr, aug in augs:\n        print(descr)\n        imgs_aug = aug.augment_images([img] * 16)\n        ia.imshow(ia.draw_grid(imgs_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_imshow.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\n\nimport imgaug as ia\n\n\ndef main():\n    image = ia.data.quokka()\n    image_gray = np.average(image, axis=2).astype(np.uint8)\n    image_gray_3d = image_gray[:, :, np.newaxis]\n\n    ia.imshow(image)\n    ia.imshow(image / 255.0)\n    ia.imshow(image_gray)\n    ia.imshow(image_gray_3d)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_jigsaw.py",
    "content": "from __future__ import print_function, division, absolute_import\nimport imgaug as ia\nimport imgaug.augmenters as iaa\nimport timeit\n\n\ndef main():\n    image = ia.quokka_square((200, 200))\n    kpsoi = ia.quokka_keypoints((200, 200), extract=\"square\")\n    aug = iaa.Jigsaw(10, 10)\n\n    images_aug, kpsois_aug = aug(images=[image] * 16,\n                                 keypoints=[kpsoi] * 16)\n    images_show = [kpsoi_aug.draw_on_image(image_aug)\n                   for image_aug, kpsoi_aug in zip(images_aug, kpsois_aug)]\n    ia.imshow(ia.draw_grid(images_show))\n\n    gen_time = timeit.timeit(\n        \"iaa.generate_jigsaw_destinations(10, 10, 2, rng)\",\n        number=128,\n        setup=(\n            \"import imgaug.augmenters as iaa; \"\n            \"import imgaug.random as iarandom; \"\n            \"rng = iarandom.RNG(0)\"\n        )\n    )\n    print(\"Time to generate 128x dest:\", gen_time)\n\n    destinations = iaa.generate_jigsaw_destinations(10, 10, 1, seed=1)\n    image_jig = iaa.apply_jigsaw(image, destinations)\n    ia.imshow(image_jig)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_jpeg_compression.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    augs = [\n        (\"0\", iaa.JpegCompression(compression=0)),\n        (\"1\", iaa.JpegCompression(compression=1)),\n        (\"25\", iaa.JpegCompression(compression=25)),\n        (\"50\", iaa.JpegCompression(compression=50)),\n        (\"75\", iaa.JpegCompression(compression=75)),\n        (\"99\", iaa.JpegCompression(compression=99)),\n        (\"100\", iaa.JpegCompression(compression=100)),\n        (\"(0, 50)\", iaa.JpegCompression(compression=(0, 50))),\n        (\"(50, 100)\", iaa.JpegCompression(compression=(50, 100))),\n        (\"(0, 100)\", iaa.JpegCompression(compression=(0, 100))),\n    ]\n\n    image = ia.data.quokka(size=(256, 256), extract=\"square\")\n    images = np.uint8([image] * (5*5))\n\n    for i, (name, aug) in enumerate(augs):\n        print(i, name)\n        images_aug = aug.augment_images(images)\n        ia.imshow(ia.draw_grid(images_aug, cols=5, rows=5))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_kmeans_color_quantization.py",
    "content": "from __future__ import print_function, division\nimport numpy as np\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    image = ia.quokka_square((256, 256))\n    image_q2 = iaa.quantize_kmeans(image, 2)\n    image_q16 = iaa.quantize_kmeans(image, 16)\n    ia.imshow(np.hstack([image_q2, image_q16]))\n\n    from_cs = \"RGB\"\n    to_cs = [\"RGB\", \"Lab\"]\n    kwargs = {\"from_colorspace\": from_cs, \"to_colorspace\": to_cs}\n    augs = [\n        iaa.KMeansColorQuantization(2, **kwargs),\n        iaa.KMeansColorQuantization(4, **kwargs),\n        iaa.KMeansColorQuantization(8, **kwargs),\n        iaa.KMeansColorQuantization((2, 16), **kwargs),\n    ]\n\n    images_aug = []\n    for aug in augs:\n        images_aug.extend(aug(images=[image]*8))\n\n    ia.imshow(ia.draw_grid(images_aug, cols=8))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_laplace_noise.py",
    "content": "from __future__ import print_function, division\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    img = ia.data.quokka(0.5)\n    mul = 0.025\n    augs = [\n        (\"iaa.AdditiveLaplaceNoise(255*(1*mul))\", iaa.AdditiveLaplaceNoise(scale=255*(1*mul))),\n        (\"iaa.AdditiveLaplaceNoise(255*(2*mul))\", iaa.AdditiveLaplaceNoise(scale=255*(2*mul))),\n        (\"iaa.AdditiveLaplaceNoise(255*(3*mul))\", iaa.AdditiveLaplaceNoise(scale=255*(3*mul))),\n        (\"iaa.AdditiveLaplaceNoise(255*(4*mul))\", iaa.AdditiveLaplaceNoise(scale=255*(4*mul))),\n        (\"iaa.AdditiveLaplaceNoise((255*(0*mul), 255*(4*mul)))\",\n         iaa.AdditiveLaplaceNoise(scale=(255*(0*mul), 255*(4*mul)))),\n        (\"iaa.AdditiveLaplaceNoise([255*(1*mul), 255*(2*mul), 255*(3*mul)])\",\n         iaa.AdditiveLaplaceNoise(scale=[255*(1*mul), 255*(2*mul), 255*(3*mul)])),\n        (\"iaa.AdditiveLaplaceNoise(255*(2*mul), per_channel=True)\",\n         iaa.AdditiveLaplaceNoise(scale=255*(2*mul), per_channel=True)),\n    ]\n    for descr, aug in augs:\n        print(descr)\n        imgs_aug = aug.augment_images([img] * 16)\n        ia.imshow(ia.draw_grid(imgs_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_mean_shift_blur.py",
    "content": "from __future__ import print_function, division, absolute_import\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    image = ia.quokka_square((128, 128))\n    aug = iaa.MeanShiftBlur()\n    images_aug = aug(images=[image] * 16)\n    ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_median_blur.py",
    "content": "from __future__ import print_function, division\n\nimport cv2\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\nTIME_PER_STEP = 5000\nNB_AUGS_PER_IMAGE = 10\n\n\ndef main():\n    image = data.astronaut()\n    image = ia.imresize_single_image(image, (64, 64))\n    print(\"image shape:\", image.shape)\n    print(\"Press any key or wait %d ms to proceed to the next image.\" % (TIME_PER_STEP,))\n\n    k = [\n        1,\n        3,\n        5,\n        7,\n        (3, 3),\n        (1, 11)\n    ]\n\n    cv2.namedWindow(\"aug\", cv2.WINDOW_NORMAL)\n    cv2.resizeWindow(\"aug\", 64*NB_AUGS_PER_IMAGE, 64)\n\n    for ki in k:\n        aug = iaa.MedianBlur(k=ki)\n        img_aug = [aug.augment_image(image) for _ in range(NB_AUGS_PER_IMAGE)]\n        img_aug = np.hstack(img_aug)\n        print(\"dtype\", img_aug.dtype, \"averages\", np.average(img_aug, axis=tuple(range(0, img_aug.ndim-1))))\n\n        title = \"k=%s\" % (str(ki),)\n        img_aug = ia.draw_text(img_aug, x=5, y=5, text=title)\n\n        cv2.imshow(\"aug\", img_aug[..., ::-1]) # here with rgb2bgr\n        cv2.waitKey(TIME_PER_STEP)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_motion_blur.py",
    "content": "from __future__ import print_function, division\nfrom itertools import cycle\n\nimport numpy as np\nimport cv2\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\nPOINT_SIZE = 5\nDEG_PER_STEP = 2\nTIME_PER_STEP = 1\n\n\ndef main():\n    image = ia.data.quokka(0.5)\n    height, width = image.shape[0], image.shape[1]\n    center_x = width // 2\n    center_y = height // 2\n    r = int(min(image.shape[0], image.shape[1]) / 3)\n\n    cv2.namedWindow(\"aug\", cv2.WINDOW_NORMAL)\n    cv2.imshow(\"aug\", image[:, :, ::-1])\n    cv2.waitKey(TIME_PER_STEP)\n\n    for angle in cycle(np.arange(0, 360, DEG_PER_STEP)):\n        rad = np.deg2rad(angle-90)\n        point_x = int(center_x + r * np.cos(rad))\n        point_y = int(center_y + r * np.sin(rad))\n\n        aug = iaa.MotionBlur(k=35, angle=angle, direction=-1.0)\n        img_aug = aug.augment_image(image)\n        img_aug[\n            point_y-POINT_SIZE:point_y+POINT_SIZE+1,\n            point_x-POINT_SIZE:point_x+POINT_SIZE+1,\n            :] = np.array([0, 255, 0])\n\n        aug_inv = iaa.MotionBlur(k=35, angle=angle, direction=1.0)\n        img_aug_inv = aug_inv.augment_image(image)\n        img_aug_inv[\n            point_y - POINT_SIZE:point_y + POINT_SIZE + 1,\n            point_x - POINT_SIZE:point_x + POINT_SIZE + 1,\n            :] = np.array([0, 255, 0])\n\n        cv2.imshow(\"aug\", np.hstack([img_aug[:, :, ::-1], img_aug_inv[:, :, ::-1]]))\n        cv2.waitKey(TIME_PER_STEP)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_multicore_pool.py",
    "content": "from __future__ import print_function, division\n\nimport time\nimport multiprocessing\n\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nimport imgaug.multicore as multicore\nfrom imgaug import augmenters as iaa\n\n\nclass PoolWithMarkedWorker(multicore.Pool):\n    def __init__(self, *args, **kwargs):\n        super(PoolWithMarkedWorker, self).__init__(*args, **kwargs)\n\n    @classmethod\n    def _worker(cls, batch_idx, batch):\n        process_name = multiprocessing.current_process().name\n        # print(\"[_worker] called %s. images in batch: %d\" % (process_name, len(batch.images_unaug),))\n        if \"-1\" in process_name:\n            for image in batch.images_unaug:\n                image[::4, ::4, :] = [255, 255, 255]\n        return multicore.Pool._worker(batch_idx, batch)\n\n\ndef main():\n    augseq = iaa.Sequential([\n        iaa.Fliplr(0.5),\n        iaa.CoarseDropout(p=0.1, size_percent=0.1)\n    ])\n\n    def func_images(images, random_state, parents, hooks):\n        time.sleep(0.2)\n        return images\n\n    def func_heatmaps(heatmaps, random_state, parents, hooks):\n        return heatmaps\n\n    def func_keypoints(keypoints_on_images, random_state, parents, hooks):\n        return keypoints_on_images\n\n    augseq_slow = iaa.Sequential([\n        iaa.Fliplr(0.5),\n        iaa.Lambda(\n            func_images=func_images,\n            func_heatmaps=func_heatmaps,\n            func_keypoints=func_keypoints\n        )\n    ])\n\n    print(\"------------------\")\n    print(\".pool()\")\n    print(\"------------------\")\n    with augseq.pool() as pool:\n        time_start = time.time()\n        batches = list(load_images())\n        batches_aug = pool.map_batches(batches)\n        images_aug = []\n        keypoints_aug = []\n        for batch_aug in batches_aug:\n            images_aug.append(batch_aug.images_aug)\n            keypoints_aug.append(batch_aug.keypoints_aug)\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n    # ia.imshow(draw_grid(images_aug, keypoints_aug))\n\n    print(\"------------------\")\n    print(\"Pool.map_batches(batches)\")\n    print(\"------------------\")\n    with multicore.Pool(augseq) as pool:\n        time_start = time.time()\n        batches = list(load_images())\n        batches_aug = pool.map_batches(batches)\n        images_aug = []\n        keypoints_aug = []\n        for batch_aug in batches_aug:\n            images_aug.append(batch_aug.images_aug)\n            keypoints_aug.append(batch_aug.keypoints_aug)\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n    # ia.imshow(draw_grid(images_aug, keypoints_aug))\n\n    print(\"------------------\")\n    print(\"Pool.imap_batches(batches)\")\n    print(\"------------------\")\n    with multicore.Pool(augseq) as pool:\n        time_start = time.time()\n        batches_aug = pool.imap_batches(load_images())\n        images_aug = []\n        keypoints_aug = []\n        for batch in batches_aug:\n            images_aug.append(batch.images_aug)\n            keypoints_aug.append(batch.keypoints_aug)\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n    # ia.imshow(draw_grid(images_aug, keypoints_aug))\n\n    print(\"------------------\")\n    print(\"Pool.imap_batches(batches, chunksize=32)\")\n    print(\"------------------\")\n    with multicore.Pool(augseq) as pool:\n        time_start = time.time()\n        batches_aug = pool.imap_batches(load_images(n_batches=1000), chunksize=32)\n        count = 0\n        for batch in batches_aug:\n            count += 1\n        assert count == 1000\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n\n    print(\"------------------\")\n    print(\"Pool.imap_batches(batches, chunksize=2)\")\n    print(\"------------------\")\n    with multicore.Pool(augseq) as pool:\n        time_start = time.time()\n        batches_aug = pool.imap_batches(load_images(n_batches=1000), chunksize=2)\n        count = 0\n        for batch in batches_aug:\n            count += 1\n        assert count == 1000\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n\n    print(\"------------------\")\n    print(\"Pool.imap_batches(batches, chunksize=1)\")\n    print(\"------------------\")\n    with multicore.Pool(augseq) as pool:\n        time_start = time.time()\n        batches_aug = pool.imap_batches(load_images(n_batches=1000), chunksize=1)\n        count = 0\n        for batch in batches_aug:\n            count += 1\n        assert count == 1000\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n\n    print(\"------------------\")\n    print(\"Pool.map_batches(batches, chunksize=32)\")\n    print(\"------------------\")\n    with multicore.Pool(augseq) as pool:\n        time_start = time.time()\n        batches_aug = pool.map_batches(list(load_images(n_batches=1000)), chunksize=32)\n        assert len(batches_aug) == 1000\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n\n    print(\"------------------\")\n    print(\"Pool.map_batches chunksize with fast aug\")\n    print(\"------------------\")\n    def test_fast(processes, chunksize):\n        augseq = iaa.Dropout(0.1)\n        with multicore.Pool(augseq, processes=processes) as pool:\n            batches = list(load_images(n_batches=10000, draw_text=False))\n            time_start = time.time()\n            batches_aug = pool.map_batches(batches, chunksize=chunksize)\n            assert len(batches_aug) == 10000\n            print(\"chunksize=%d, worker=%s, time=%.4fs\" % (chunksize, processes, time.time() - time_start))\n\n    test_fast(-4, 1)\n    test_fast(1, 1)\n    test_fast(None, 1)\n    test_fast(1, 4)\n    test_fast(None, 4)\n    test_fast(1, 32)\n    test_fast(None, 32)\n\n    print(\"------------------\")\n    print(\"Pool.imap_batches chunksize with fast aug\")\n    print(\"------------------\")\n    def test_fast_imap(processes, chunksize):\n        augseq = iaa.Dropout(0.1)\n        with multicore.Pool(augseq, processes=processes) as pool:\n            time_start = time.time()\n            batches_aug = pool.imap_batches(load_images(n_batches=10000, draw_text=False), chunksize=chunksize)\n            batches_aug = list(batches_aug)\n            assert len(batches_aug) == 10000\n            print(\"chunksize=%d, worker=%s, time=%.4fs\" % (chunksize, processes, time.time() - time_start))\n\n    test_fast_imap(-4, 1)\n    test_fast_imap(1, 1)\n    test_fast_imap(None, 1)\n    test_fast_imap(1, 4)\n    test_fast_imap(None, 4)\n    test_fast_imap(1, 32)\n    test_fast_imap(None, 32)\n\n    print(\"------------------\")\n    print(\"Pool.map_batches with computationally expensive aug\")\n    print(\"------------------\")\n    def test_heavy(processes, chunksize):\n        augseq_heavy = iaa.PiecewiseAffine(scale=0.2, nb_cols=8, nb_rows=8)\n        with multicore.Pool(augseq_heavy, processes=processes) as pool:\n            batches = list(load_images(n_batches=500, draw_text=False))\n            time_start = time.time()\n            batches_aug = pool.map_batches(batches, chunksize=chunksize)\n            assert len(batches_aug) == 500\n            print(\"chunksize=%d, worker=%s, time=%.4fs\" % (chunksize, processes, time.time() - time_start))\n\n    test_heavy(-4, 1)\n    test_heavy(1, 1)\n    test_heavy(None, 1)\n    test_heavy(1, 4)\n    test_heavy(None, 4)\n    test_heavy(1, 32)\n    test_heavy(None, 32)\n\n    print(\"------------------\")\n    print(\"Pool.imap_batches(batches), slow loading\")\n    print(\"------------------\")\n    with multicore.Pool(augseq) as pool:\n        time_start = time.time()\n        batches_aug = pool.imap_batches(load_images(n_batches=100, sleep=0.2))\n        images_aug = []\n        keypoints_aug = []\n        for batch in batches_aug:\n            images_aug.append(batch.images_aug)\n            keypoints_aug.append(batch.keypoints_aug)\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n\n    print(\"------------------\")\n    print(\"Pool.imap_batches(batches), maxtasksperchild=4\")\n    print(\"------------------\")\n    with multicore.Pool(augseq, maxtasksperchild=4) as pool:\n        time_start = time.time()\n        batches_aug = pool.imap_batches(load_images(n_batches=100))\n        images_aug = []\n        keypoints_aug = []\n        for batch in batches_aug:\n            images_aug.append(batch.images_aug)\n            keypoints_aug.append(batch.keypoints_aug)\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n    ia.imshow(draw_grid(images_aug, keypoints_aug))\n\n    print(\"------------------\")\n    print(\"Pool.imap_batches(batches), seed=1\")\n    print(\"------------------\")\n    # we color here the images of the first worker to see in the grids which images belong to one worker\n    with PoolWithMarkedWorker(augseq, seed=1) as pool:\n        time_start = time.time()\n        batches_aug = pool.imap_batches(load_images(n_batches=4))\n        images_aug = []\n        keypoints_aug = []\n        for batch in batches_aug:\n            images_aug.append(batch.images_aug)\n            keypoints_aug.append(batch.keypoints_aug)\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n    grid_a = draw_grid(images_aug, keypoints_aug)\n\n    with multicore.Pool(augseq, seed=1) as pool:\n        time_start = time.time()\n        batches_aug = pool.imap_batches(load_images(n_batches=4))\n        images_aug = []\n        keypoints_aug = []\n        for batch in batches_aug:\n            images_aug.append(batch.images_aug)\n            keypoints_aug.append(batch.keypoints_aug)\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n    grid_b = draw_grid(images_aug, keypoints_aug)\n\n    grid_b[:, 0:2, 0] = 0\n    grid_b[:, 0:2, 1] = 255\n    grid_b[:, 0:2, 2] = 0\n    ia.imshow(np.hstack([grid_a, grid_b]))\n\n    print(\"------------------\")\n    print(\"Pool.imap_batches(batches), seed=None\")\n    print(\"------------------\")\n    with multicore.Pool(augseq, seed=None) as pool:\n        time_start = time.time()\n        batches_aug = pool.imap_batches(load_images(n_batches=4))\n        images_aug = []\n        keypoints_aug = []\n        for batch in batches_aug:\n            images_aug.append(batch.images_aug)\n            keypoints_aug.append(batch.keypoints_aug)\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n    grid_a = draw_grid(images_aug, keypoints_aug)\n\n    with multicore.Pool(augseq, seed=None) as pool:\n        time_start = time.time()\n        batches_aug = pool.imap_batches(load_images(n_batches=4))\n        images_aug = []\n        keypoints_aug = []\n        for batch in batches_aug:\n            images_aug.append(batch.images_aug)\n            keypoints_aug.append(batch.keypoints_aug)\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n    grid_b = draw_grid(images_aug, keypoints_aug)\n\n    ia.imshow(np.hstack([grid_a, grid_b]))\n\n    print(\"------------------\")\n    print(\"Pool.imap_batches(batches), maxtasksperchild=4, seed=1\")\n    print(\"------------------\")\n    with multicore.Pool(augseq, maxtasksperchild=4, seed=1) as pool:\n        time_start = time.time()\n        batches_aug = pool.imap_batches(load_images(n_batches=100))\n        images_aug = []\n        keypoints_aug = []\n        for batch in batches_aug:\n            images_aug.append(batch.images_aug)\n            keypoints_aug.append(batch.keypoints_aug)\n        print(\"Done in %.4fs\" % (time.time() - time_start,))\n    ia.imshow(draw_grid(images_aug, keypoints_aug))\n\n    for augseq_i in [augseq, augseq_slow]:\n        print(\"------------------\")\n        print(\"Many very small runs (batches=1)\")\n        print(\"------------------\")\n        with multicore.Pool(augseq_i) as pool:\n            time_start = time.time()\n            for i in range(100):\n                _ = pool.map_batches(list(load_images(n_batches=1)))\n            print(\"Done in %.4fs\" % (time.time() - time_start,))\n\n        print(\"------------------\")\n        print(\"Many very small runs (batches=2)\")\n        print(\"------------------\")\n        with multicore.Pool(augseq_i) as pool:\n            time_start = time.time()\n            for i in range(100):\n                _ = pool.map_batches(list(load_images(n_batches=2)))\n            print(\"Done in %.4fs\" % (time.time() - time_start,))\n\n\ndef load_images(n_batches=10, sleep=0.0, draw_text=True):\n    batch_size = 4\n    astronaut = data.astronaut()\n    astronaut = ia.imresize_single_image(astronaut, (64, 64))\n    kps = ia.KeypointsOnImage([ia.Keypoint(x=15, y=25)], shape=astronaut.shape)\n\n    counter = 0\n    for i in range(n_batches):\n        if draw_text:\n            batch_images = []\n            batch_kps = []\n            for b in range(batch_size):\n                astronaut_text = ia.draw_text(astronaut, x=0, y=0, text=\"%d\" % (counter,), color=[0, 255, 0], size=16)\n                batch_images.append(astronaut_text)\n                batch_kps.append(kps)\n                counter += 1\n            batch = ia.Batch(\n                images=np.array(batch_images, dtype=np.uint8),\n                keypoints=batch_kps\n            )\n        else:\n            if i == 0:\n                batch_images = np.array([np.copy(astronaut) for _ in range(batch_size)], dtype=np.uint8)\n\n            batch = ia.Batch(\n                images=np.copy(batch_images),\n                keypoints=[kps.deepcopy() for _ in range(batch_size)]\n            )\n        yield batch\n        if sleep > 0:\n            time.sleep(sleep)\n\n\ndef draw_grid(images_aug, keypoints_aug):\n    if keypoints_aug is None:\n        keypoints_aug = []\n        for bidx in range(len(images_aug)):\n            keypoints_aug.append([None for image in images_aug[bidx]])\n\n    images_kps_batches = []\n    for bidx in range(len(images_aug)):\n        images_kps_batch = []\n        for image, kps in zip(images_aug[bidx], keypoints_aug[bidx]):\n            if kps is None:\n                image_kps = image\n            else:\n                image_kps = kps.draw_on_image(image, size=5, color=[255, 0, 0])\n            images_kps_batch.append(image_kps)\n        images_kps_batches.extend(images_kps_batch)\n\n    grid = ia.draw_grid(images_kps_batches, cols=len(images_aug[0]))\n    return grid\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_multiply_hue_and_saturation.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    image = ia.quokka_square((128, 128))\n    images_aug = []\n\n    for mul in np.linspace(0.0, 2.0, 10):\n        aug = iaa.MultiplyHueAndSaturation(mul)\n        image_aug = aug.augment_image(image)\n        images_aug.append(image_aug)\n\n    for mul_hue in np.linspace(0.0, 5.0, 10):\n        aug = iaa.MultiplyHueAndSaturation(mul_hue=mul_hue)\n        image_aug = aug.augment_image(image)\n        images_aug.append(image_aug)\n\n    for mul_saturation in np.linspace(0.0, 5.0, 10):\n        aug = iaa.MultiplyHueAndSaturation(mul_saturation=mul_saturation)\n        image_aug = aug.augment_image(image)\n        images_aug.append(image_aug)\n\n    ia.imshow(ia.draw_grid(images_aug, rows=3))\n\n    images_aug = []\n    images_aug.extend(iaa.MultiplyHue().augment_images([image] * 10))\n    images_aug.extend(iaa.MultiplySaturation().augment_images([image] * 10))\n    ia.imshow(ia.draw_grid(images_aug, rows=2))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_noise.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\n\n\ndef main():\n    nb_rows = 8\n    nb_cols = 8\n    h, w = (128, 128)\n    sample_size = 128\n\n    noise_gens = [\n        iap.SimplexNoise(),\n        iap.FrequencyNoise(exponent=-4, size_px_max=sample_size, upscale_method=\"cubic\"),\n        iap.FrequencyNoise(exponent=-2, size_px_max=sample_size, upscale_method=\"cubic\"),\n        iap.FrequencyNoise(exponent=0, size_px_max=sample_size, upscale_method=\"cubic\"),\n        iap.FrequencyNoise(exponent=2, size_px_max=sample_size, upscale_method=\"cubic\"),\n        iap.FrequencyNoise(exponent=4, size_px_max=sample_size, upscale_method=\"cubic\"),\n        iap.FrequencyNoise(exponent=(-4, 4), size_px_max=(4, sample_size),\n                           upscale_method=[\"nearest\", \"linear\", \"cubic\"]),\n        iap.IterativeNoiseAggregator(\n            other_param=iap.FrequencyNoise(exponent=(-4, 4), size_px_max=(4, sample_size),\n                                           upscale_method=[\"nearest\", \"linear\", \"cubic\"]),\n            iterations=(1, 3),\n            aggregation_method=[\"max\", \"avg\"]\n        ),\n        iap.IterativeNoiseAggregator(\n            other_param=iap.Sigmoid(\n                iap.FrequencyNoise(exponent=(-4, 4), size_px_max=(4, sample_size),\n                                   upscale_method=[\"nearest\", \"linear\", \"cubic\"]),\n                threshold=(-10, 10),\n                activated=0.33,\n                mul=20,\n                add=-10\n            ),\n            iterations=(1, 3),\n            aggregation_method=[\"max\", \"avg\"]\n        )\n    ]\n\n    samples = [[] for _ in range(len(noise_gens))]\n    for _ in range(nb_rows * nb_cols):\n        for i, noise_gen in enumerate(noise_gens):\n            samples[i].append(noise_gen.draw_samples((h, w)))\n\n    rows = [np.hstack(row) for row in samples]\n    grid = np.vstack(rows)\n    ia.imshow((grid*255).astype(np.uint8))\n\n    images = [ia.quokka_square(size=(128, 128)) for _ in range(16)]\n    seqs = [\n        iaa.SimplexNoiseAlpha(first=iaa.EdgeDetect(1.0)),\n        iaa.SimplexNoiseAlpha(first=iaa.EdgeDetect(1.0), per_channel=True),\n        iaa.FrequencyNoiseAlpha(first=iaa.EdgeDetect(1.0)),\n        iaa.FrequencyNoiseAlpha(first=iaa.EdgeDetect(1.0), per_channel=True)\n    ]\n    images_aug = []\n\n    for seq in seqs:\n        images_aug.append(np.hstack(seq.augment_images(images)))\n    images_aug = np.vstack(images_aug)\n    ia.imshow(images_aug)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_parameters.py",
    "content": "from __future__ import print_function, division\nimport imgaug as ia\n# TODO ForceSign\nfrom imgaug.parameters import (\n    Binomial, Choice, DiscreteUniform, Poisson, Normal, Laplace, ChiSquare,\n    Weibull, Uniform, Beta, Deterministic, Clip, Discretize, Multiply, Add,\n    Divide, Power, Absolute, RandomSign, Positive, Negative,\n    SimplexNoise, FrequencyNoise, Sigmoid\n)\nimport numpy as np\n\n\ndef main():\n    params = [\n        (\"Binomial(0.1)\", Binomial(0.1)),\n        (\"Choice\", Choice([0, 1, 2])),\n        (\"Choice with p\", Choice([0, 1, 2], p=[0.1, 0.2, 0.7])),\n        (\"DiscreteUniform(0, 10)\", DiscreteUniform(0, 10)),\n        (\"Poisson(0)\", Poisson(0)),\n        (\"Poisson(5)\", Poisson(5)),\n        (\"Discretize(Poisson(5))\", Discretize(Poisson(5))),\n        (\"Normal(0, 1)\", Normal(0, 1)),\n        (\"Normal(1, 1)\", Normal(1, 1)),\n        (\"Normal(1, 2)\", Normal(0, 2)),\n        (\"Normal(Choice([-1, 1]), 2)\", Normal(Choice([-1, 1]), 2)),\n        (\"Discretize(Normal(0, 1.0))\", Discretize(Normal(0, 1.0))),\n        (\"Positive(Normal(0, 1.0))\", Positive(Normal(0, 1.0))),\n        (\"Positive(Normal(0, 1.0), mode='reroll')\", Positive(Normal(0, 1.0), mode=\"reroll\")),\n        (\"Negative(Normal(0, 1.0))\", Negative(Normal(0, 1.0))),\n        (\"Negative(Normal(0, 1.0), mode='reroll')\", Negative(Normal(0, 1.0), mode=\"reroll\")),\n        (\"Laplace(0, 1.0)\", Laplace(0, 1.0)),\n        (\"Laplace(1.0, 3.0)\", Laplace(1.0, 3.0)),\n        (\"Laplace([-1.0, 1.0], 1.0)\", Laplace([-1.0, 1.0], 1.0)),\n        (\"ChiSquare(1)\", ChiSquare(1)),\n        (\"ChiSquare([1, 6])\", ChiSquare([1, 6])),\n        (\"Weibull(0.5)\", Weibull(0.5)),\n        (\"Weibull((1.0, 3.0))\", Weibull((1.0, 3.0))),\n        (\"Uniform(0, 10)\", Uniform(0, 10)),\n        (\"Beta(0.5, 0.5)\", Beta(0.5, 0.5)),\n        (\"Deterministic(1)\", Deterministic(1)),\n        (\"Clip(Normal(0, 1), 0, None)\", Clip(Normal(0, 1), minval=0, maxval=None)),\n        (\"Multiply(Uniform(0, 10), 2)\", Multiply(Uniform(0, 10), 2)),\n        (\"Add(Uniform(0, 10), 5)\", Add(Uniform(0, 10), 5)),\n        (\"Absolute(Normal(0, 1))\", Absolute(Normal(0, 1))),\n        (\"RandomSign(Poisson(1))\", RandomSign(Poisson(1))),\n        (\"RandomSign(Poisson(1), 0.9)\", RandomSign(Poisson(1), 0.9))\n    ]\n\n    params_arithmetic = [\n        (\"Normal(0, 1.0)\", Normal(0.0, 1.0)),\n        (\"Normal(0, 1.0) + 5\", Normal(0.0, 1.0) + 5),\n        (\"5 + Normal(0, 1.0)\", 5 + Normal(0.0, 1.0)),\n        (\"5 + Normal(0, 1.0)\", Add(5, Normal(0.0, 1.0), elementwise=True)),\n        (\"Normal(0, 1.0) * 10\", Normal(0.0, 1.0) * 10),\n        (\"10 * Normal(0, 1.0)\", 10 * Normal(0.0, 1.0)),\n        (\"10 * Normal(0, 1.0)\", Multiply(10, Normal(0.0, 1.0), elementwise=True)),\n        (\"Normal(0, 1.0) / 10\", Normal(0.0, 1.0) / 10),\n        (\"10 / Normal(0, 1.0)\", 10 / Normal(0.0, 1.0)),\n        (\"10 / Normal(0, 1.0)\", Divide(10, Normal(0.0, 1.0), elementwise=True)),\n        (\"Normal(0, 1.0) ** 2\", Normal(0.0, 1.0) ** 2),\n        (\"2 ** Normal(0, 1.0)\", 2 ** Normal(0.0, 1.0)),\n        (\"2 ** Normal(0, 1.0)\", Power(2, Normal(0.0, 1.0), elementwise=True))\n    ]\n\n    params_noise = [\n        (\"SimplexNoise\", SimplexNoise()),\n        (\"Sigmoid(SimplexNoise)\", Sigmoid(SimplexNoise())),\n        (\"SimplexNoise(linear)\", SimplexNoise(upscale_method=\"linear\")),\n        (\"SimplexNoise(nearest)\", SimplexNoise(upscale_method=\"nearest\")),\n        (\"FrequencyNoise((-4, 4))\", FrequencyNoise(exponent=(-4, 4))),\n        (\"FrequencyNoise(-2)\", FrequencyNoise(exponent=-2)),\n        (\"FrequencyNoise(2)\", FrequencyNoise(exponent=2))\n    ]\n\n    images_params = [param.draw_distribution_graph() for (title, param) in params]\n    images_arithmetic = [param.draw_distribution_graph() for (title, param) in params_arithmetic]\n    images_noise = [param.draw_distribution_graph(size=(1000, 10, 10)) for (title, param) in params_noise]\n\n    ia.imshow(np.vstack(images_params))\n    ia.imshow(np.vstack(images_arithmetic))\n    ia.imshow(np.vstack(images_noise))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_performance.py",
    "content": "\"\"\"\nTests to measure the performance of each augmenter.\nRun these checks from the project directory (i.e. parent directory) via\n    python check_performance.py\n\"\"\"\nfrom __future__ import print_function, division\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nimport numpy as np\nimport time\nimport random\nimport six.moves as sm\n\n\"\"\"\n---------------------------\nKeypoints\n---------------------------\n[Augmenter: Identity]\n(4, 4, 3)            | SUM 0.01990s | PER ITER avg 0.00020s, min 0.00017s, max 0.00043s\n(32, 32, 3)          | SUM 0.01863s | PER ITER avg 0.00019s, min 0.00017s, max 0.00033s\n(256, 256, 3)        | SUM 0.01879s | PER ITER avg 0.00019s, min 0.00017s, max 0.00029s\n[Augmenter: Crop-px]\n(4, 4, 3)            | SUM 0.20215s | PER ITER avg 0.00202s, min 0.00168s, max 0.01908s\n(32, 32, 3)          | SUM 0.19844s | PER ITER avg 0.00198s, min 0.00164s, max 0.01933s\n(256, 256, 3)        | SUM 0.17918s | PER ITER avg 0.00179s, min 0.00166s, max 0.00214s\n[Augmenter: Crop-percent]\n(4, 4, 3)            | SUM 0.14201s | PER ITER avg 0.00142s, min 0.00114s, max 0.02041s\n(32, 32, 3)          | SUM 0.16912s | PER ITER avg 0.00169s, min 0.00137s, max 0.02023s\n(256, 256, 3)        | SUM 0.15548s | PER ITER avg 0.00155s, min 0.00142s, max 0.00193s\n[Augmenter: Fliplr]\n(4, 4, 3)            | SUM 0.02303s | PER ITER avg 0.00023s, min 0.00021s, max 0.00034s\n(32, 32, 3)          | SUM 0.02477s | PER ITER avg 0.00025s, min 0.00021s, max 0.00038s\n(256, 256, 3)        | SUM 0.02383s | PER ITER avg 0.00024s, min 0.00022s, max 0.00036s\n[Augmenter: Flipud]\n(4, 4, 3)            | SUM 0.02362s | PER ITER avg 0.00024s, min 0.00021s, max 0.00035s\n(32, 32, 3)          | SUM 0.02356s | PER ITER avg 0.00024s, min 0.00021s, max 0.00032s\n(256, 256, 3)        | SUM 0.02415s | PER ITER avg 0.00024s, min 0.00021s, max 0.00037s\n[Augmenter: Grayscale]\n(4, 4, 3)            | SUM 0.01908s | PER ITER avg 0.00019s, min 0.00017s, max 0.00030s\n(32, 32, 3)          | SUM 0.01903s | PER ITER avg 0.00019s, min 0.00017s, max 0.00030s\n(256, 256, 3)        | SUM 0.01876s | PER ITER avg 0.00019s, min 0.00017s, max 0.00027s\n[Augmenter: GaussianBlur]\n(4, 4, 3)            | SUM 0.01904s | PER ITER avg 0.00019s, min 0.00017s, max 0.00029s\n(32, 32, 3)          | SUM 0.01851s | PER ITER avg 0.00019s, min 0.00017s, max 0.00033s\n(256, 256, 3)        | SUM 0.01894s | PER ITER avg 0.00019s, min 0.00017s, max 0.00025s\n[Augmenter: AdditiveGaussianNoise]\n(4, 4, 3)            | SUM 0.01902s | PER ITER avg 0.00019s, min 0.00017s, max 0.00029s\n(32, 32, 3)          | SUM 0.01905s | PER ITER avg 0.00019s, min 0.00017s, max 0.00028s\n(256, 256, 3)        | SUM 0.01971s | PER ITER avg 0.00020s, min 0.00017s, max 0.00046s\n[Augmenter: Dropout]\n(4, 4, 3)            | SUM 0.01887s | PER ITER avg 0.00019s, min 0.00017s, max 0.00027s\n(32, 32, 3)          | SUM 0.01913s | PER ITER avg 0.00019s, min 0.00017s, max 0.00030s\n(256, 256, 3)        | SUM 0.01922s | PER ITER avg 0.00019s, min 0.00017s, max 0.00029s\n[Augmenter: Multiply]\n(4, 4, 3)            | SUM 0.01942s | PER ITER avg 0.00019s, min 0.00017s, max 0.00028s\n(32, 32, 3)          | SUM 0.01922s | PER ITER avg 0.00019s, min 0.00017s, max 0.00032s\n(256, 256, 3)        | SUM 0.01875s | PER ITER avg 0.00019s, min 0.00017s, max 0.00030s\n[Augmenter: ContrastNormalization]\n(4, 4, 3)            | SUM 0.01852s | PER ITER avg 0.00019s, min 0.00017s, max 0.00029s\n(32, 32, 3)          | SUM 0.01869s | PER ITER avg 0.00019s, min 0.00017s, max 0.00026s\n(256, 256, 3)        | SUM 0.01875s | PER ITER avg 0.00019s, min 0.00017s, max 0.00028s\n[Augmenter: Grayscale]\n(4, 4, 3)            | SUM 0.01919s | PER ITER avg 0.00019s, min 0.00017s, max 0.00030s\n(32, 32, 3)          | SUM 0.01923s | PER ITER avg 0.00019s, min 0.00017s, max 0.00033s\n(256, 256, 3)        | SUM 0.01888s | PER ITER avg 0.00019s, min 0.00017s, max 0.00028s\n[Augmenter: ElasticTransformation]\n(4, 4, 3)            | SUM 0.01882s | PER ITER avg 0.00019s, min 0.00017s, max 0.00024s\n(32, 32, 3)          | SUM 0.01883s | PER ITER avg 0.00019s, min 0.00017s, max 0.00029s\n(256, 256, 3)        | SUM 0.01869s | PER ITER avg 0.00019s, min 0.00017s, max 0.00029s\n[Augmenter: AffineOrder0ModeConstant]\n(4, 4, 3)            | SUM 0.28146s | PER ITER avg 0.00281s, min 0.00243s, max 0.02199s\n(32, 32, 3)          | SUM 0.28047s | PER ITER avg 0.00280s, min 0.00243s, max 0.02083s\n(256, 256, 3)        | SUM 0.28715s | PER ITER avg 0.00287s, min 0.00243s, max 0.02088s\n[Augmenter: AffineOrder0]\n(4, 4, 3)            | SUM 0.27242s | PER ITER avg 0.00272s, min 0.00246s, max 0.00362s\n(32, 32, 3)          | SUM 0.29675s | PER ITER avg 0.00297s, min 0.00247s, max 0.02220s\n(256, 256, 3)        | SUM 0.28988s | PER ITER avg 0.00290s, min 0.00247s, max 0.02128s\n[Augmenter: AffineOrder1]\n(4, 4, 3)            | SUM 0.26750s | PER ITER avg 0.00267s, min 0.00246s, max 0.00321s\n(32, 32, 3)          | SUM 0.28361s | PER ITER avg 0.00284s, min 0.00245s, max 0.02144s\n(256, 256, 3)        | SUM 0.28973s | PER ITER avg 0.00290s, min 0.00246s, max 0.02070s\n[Augmenter: AffineAll]\n(4, 4, 3)            | SUM 0.27070s | PER ITER avg 0.00271s, min 0.00246s, max 0.00367s\n(32, 32, 3)          | SUM 0.28405s | PER ITER avg 0.00284s, min 0.00247s, max 0.02120s\n(256, 256, 3)        | SUM 0.28895s | PER ITER avg 0.00289s, min 0.00247s, max 0.02144s\n---------------------------\nImages\n---------------------------\n[Augmenter: Identity]\n(16, 4, 4, 3)        | SUM 0.00135s | PER ITER avg 0.00001s, min 0.00001s, max 0.00008s\n(16, 32, 32, 3)      | SUM 0.00203s | PER ITER avg 0.00002s, min 0.00002s, max 0.00005s\n(16, 256, 256, 3)    | SUM 0.05284s | PER ITER avg 0.00053s, min 0.00044s, max 0.00194s\n[Augmenter: Crop-px]\n(16, 4, 4, 3)        | SUM 0.09324s | PER ITER avg 0.00093s, min 0.00084s, max 0.00315s\n(16, 32, 32, 3)      | SUM 0.10302s | PER ITER avg 0.00103s, min 0.00094s, max 0.00162s\n(16, 256, 256, 3)    | SUM 0.81943s | PER ITER avg 0.00819s, min 0.00767s, max 0.00934s\n[Augmenter: Crop-percent]\n(16, 4, 4, 3)        | SUM 0.06562s | PER ITER avg 0.00066s, min 0.00057s, max 0.00099s\n(16, 32, 32, 3)      | SUM 0.09784s | PER ITER avg 0.00098s, min 0.00089s, max 0.00131s\n(16, 256, 256, 3)    | SUM 0.80779s | PER ITER avg 0.00808s, min 0.00732s, max 0.01008s\n[Augmenter: Fliplr]\n(16, 4, 4, 3)        | SUM 0.00525s | PER ITER avg 0.00005s, min 0.00004s, max 0.00017s\n(16, 32, 32, 3)      | SUM 0.01025s | PER ITER avg 0.00010s, min 0.00007s, max 0.00015s\n(16, 256, 256, 3)    | SUM 0.36918s | PER ITER avg 0.00369s, min 0.00181s, max 0.00553s\n[Augmenter: Flipud]\n(16, 4, 4, 3)        | SUM 0.00512s | PER ITER avg 0.00005s, min 0.00004s, max 0.00009s\n(16, 32, 32, 3)      | SUM 0.00665s | PER ITER avg 0.00007s, min 0.00006s, max 0.00011s\n(16, 256, 256, 3)    | SUM 0.12664s | PER ITER avg 0.00127s, min 0.00092s, max 0.00167s\n[Augmenter: Grayscale]\n(16, 4, 4, 3)        | SUM 0.05943s | PER ITER avg 0.00059s, min 0.00050s, max 0.00125s\n(16, 32, 32, 3)      | SUM 0.12247s | PER ITER avg 0.00122s, min 0.00106s, max 0.00205s\n(16, 256, 256, 3)    | SUM 3.62785s | PER ITER avg 0.03628s, min 0.03508s, max 0.03963s\n[Augmenter: GaussianBlur]\n(16, 4, 4, 3)        | SUM 0.15514s | PER ITER avg 0.00155s, min 0.00136s, max 0.00188s\n(16, 32, 32, 3)      | SUM 0.25121s | PER ITER avg 0.00251s, min 0.00221s, max 0.00298s\n(16, 256, 256, 3)    | SUM 5.51685s | PER ITER avg 0.05517s, min 0.04923s, max 0.06026s\n[Augmenter: AdditiveGaussianNoise]\n(16, 4, 4, 3)        | SUM 0.09606s | PER ITER avg 0.00096s, min 0.00085s, max 0.00150s\n(16, 32, 32, 3)      | SUM 0.21302s | PER ITER avg 0.00213s, min 0.00196s, max 0.00254s\n(16, 256, 256, 3)    | SUM 7.22374s | PER ITER avg 0.07224s, min 0.07017s, max 0.07558s\n[Augmenter: Dropout]\n(16, 4, 4, 3)        | SUM 0.09362s | PER ITER avg 0.00094s, min 0.00084s, max 0.00118s\n(16, 32, 32, 3)      | SUM 0.17472s | PER ITER avg 0.00175s, min 0.00161s, max 0.00230s\n(16, 256, 256, 3)    | SUM 5.04969s | PER ITER avg 0.05050s, min 0.04839s, max 0.05631s\n[Augmenter: Multiply]\n(16, 4, 4, 3)        | SUM 0.05442s | PER ITER avg 0.00054s, min 0.00046s, max 0.00089s\n(16, 32, 32, 3)      | SUM 0.06895s | PER ITER avg 0.00069s, min 0.00060s, max 0.00109s\n(16, 256, 256, 3)    | SUM 0.87311s | PER ITER avg 0.00873s, min 0.00799s, max 0.00993s\n[Augmenter: ContrastNormalization]\n(16, 4, 4, 3)        | SUM 0.05746s | PER ITER avg 0.00057s, min 0.00050s, max 0.00094s\n(16, 32, 32, 3)      | SUM 0.08083s | PER ITER avg 0.00081s, min 0.00071s, max 0.00133s\n(16, 256, 256, 3)    | SUM 1.57577s | PER ITER avg 0.01576s, min 0.01443s, max 0.01831s\n[Augmenter: Grayscale]\n(16, 4, 4, 3)        | SUM 0.05464s | PER ITER avg 0.00055s, min 0.00049s, max 0.00069s\n(16, 32, 32, 3)      | SUM 0.12058s | PER ITER avg 0.00121s, min 0.00104s, max 0.00223s\n(16, 256, 256, 3)    | SUM 3.57037s | PER ITER avg 0.03570s, min 0.03461s, max 0.03780s\n[Augmenter: ElasticTransformation]\n(16, 4, 4, 3)        | SUM 0.29551s | PER ITER avg 0.00296s, min 0.00272s, max 0.00336s\n(16, 32, 32, 3)      | SUM 0.68591s | PER ITER avg 0.00686s, min 0.00642s, max 0.00764s\n(16, 256, 256, 3)    | SUM 26.30515s | PER ITER avg 0.26305s, min 0.25754s, max 0.26912s\n[Augmenter: AffineOrder0ModeConstant]\n(16, 4, 4, 3)        | SUM 0.35887s | PER ITER avg 0.00359s, min 0.00333s, max 0.00424s\n(16, 32, 32, 3)      | SUM 0.47889s | PER ITER avg 0.00479s, min 0.00451s, max 0.00535s\n(16, 256, 256, 3)    | SUM 9.83738s | PER ITER avg 0.09837s, min 0.09417s, max 0.10458s\n[Augmenter: AffineOrder0]\n(16, 4, 4, 3)        | SUM 0.37980s | PER ITER avg 0.00380s, min 0.00340s, max 0.00517s\n(16, 32, 32, 3)      | SUM 0.53106s | PER ITER avg 0.00531s, min 0.00472s, max 0.00630s\n(16, 256, 256, 3)    | SUM 10.69961s | PER ITER avg 0.10700s, min 0.10223s, max 0.11325s\n[Augmenter: AffineOrder1]\n(16, 4, 4, 3)        | SUM 0.39431s | PER ITER avg 0.00394s, min 0.00363s, max 0.00511s\n(16, 32, 32, 3)      | SUM 0.62730s | PER ITER avg 0.00627s, min 0.00576s, max 0.00711s\n(16, 256, 256, 3)    | SUM 14.50003s | PER ITER avg 0.14500s, min 0.13785s, max 0.15291s\n[Augmenter: AffineAll]\n(16, 4, 4, 3)        | SUM 0.58742s | PER ITER avg 0.00587s, min 0.00429s, max 0.00724s\n(16, 32, 32, 3)      | SUM 3.69956s | PER ITER avg 0.03700s, min 0.01358s, max 0.06233s\n(16, 256, 256, 3)    | SUM 212.91776s | PER ITER avg 2.12918s, min 0.57114s, max 3.95389s\n\"\"\"\n\n\ndef main():\n    augmenters = [\n        iaa.Identity(name=\"Identity\"),\n        iaa.Crop(px=(0, 8), name=\"Crop-px\"),\n        iaa.Crop(percent=(0, 0.1), name=\"Crop-percent\"),\n        iaa.Fliplr(0.5, name=\"Fliplr\"),\n        iaa.Flipud(0.5, name=\"Flipud\"),\n        iaa.Grayscale((0.0, 1.0), name=\"Grayscale\"),\n        iaa.GaussianBlur((0, 3.0), name=\"GaussianBlur\"),\n        iaa.AdditiveGaussianNoise(loc=0, scale=(0.0, 0.1), name=\"AdditiveGaussianNoise\"),\n        iaa.Dropout((0.0, 0.1), name=\"Dropout\"),\n        iaa.Multiply((0.5, 1.5), name=\"Multiply\"),\n        iaa.ContrastNormalization(alpha=(0.5, 2.0), name=\"ContrastNormalization\"),\n        iaa.Grayscale(alpha=(0.0, 1.0), name=\"Grayscale\"),\n        iaa.ElasticTransformation(alpha=(0.5, 8.0), sigma=1.0, name=\"ElasticTransformation\"),\n        iaa.Affine(\n            scale={\"x\": (0.8, 1.2), \"y\": (0.8, 1.2)},\n            translate_px={\"x\": (-16, 16), \"y\": (-16, 16)},\n            rotate=(-45, 45),\n            shear=(-16, 16),\n            order=0,\n            cval=(0, 255),\n            mode=\"constant\",\n            name=\"AffineOrder0ModeConstant\"\n        )\n    ]\n\n    for order in [0, 1]:\n        augmenters.append(\n            iaa.Affine(\n                scale={\"x\": (0.8, 1.2), \"y\": (0.8, 1.2)},\n                translate_px={\"x\": (-16, 16), \"y\": (-16, 16)},\n                rotate=(-45, 45),\n                shear=(-16, 16),\n                order=order,\n                cval=(0, 255),\n                mode=ia.ALL,\n                name=\"AffineOrder%d\" % (order,)\n            )\n        )\n\n    augmenters.append(\n        iaa.Affine(\n            scale={\"x\": (0.8, 1.2), \"y\": (0.8, 1.2)},\n            translate_px={\"x\": (-16, 16), \"y\": (-16, 16)},\n            rotate=(-45, 45),\n            shear=(-16, 16),\n            order=ia.ALL,\n            cval=(0, 255),\n            mode=ia.ALL,\n            name=\"AffineAll\"\n        )\n    )\n\n    kps = []\n    for _ in sm.xrange(20):\n        x = random.randint(0, 31)\n        y = random.randint(0, 31)\n        kps.append(ia.Keypoint(x=x, y=y))\n    kps = ia.KeypointsOnImage(kps, shape=(32, 32, 3))\n    small_keypoints_one = kps.on((4, 4, 3))\n    medium_keypoints_one = kps.on((32, 32, 3))\n    large_keypoints_one = kps.on((256, 256, 3))\n    small_keypoints = [small_keypoints_one.deepcopy() for _ in sm.xrange(16)]\n    medium_keypoints = [medium_keypoints_one.deepcopy() for _ in sm.xrange(16)]\n    large_keypoints = [large_keypoints_one.deepcopy() for _ in sm.xrange(16)]\n\n    small_images = np.random.randint(0, 255, (16, 4, 4, 3)).astype(np.uint8)\n    medium_images = np.random.randint(0, 255, (16, 32, 32, 3)).astype(np.uint8)\n    large_images = np.random.randint(0, 255, (16, 256, 256, 3)).astype(np.uint8)\n\n    print(\"---------------------------\")\n    print(\"Keypoints\")\n    print(\"---------------------------\")\n    for augmenter in augmenters:\n        print(\"[Augmenter: %s]\" % (augmenter.name,))\n        for keypoints in [small_keypoints, medium_keypoints, large_keypoints]:\n            times = []\n            for i in sm.xrange(100):\n                time_start = time.time()\n                _img_aug = augmenter.augment_keypoints(keypoints)\n                time_end = time.time()\n                times.append(time_end - time_start)\n            times = np.array(times)\n            img_str = \"{:20s}\".format(keypoints[0].shape)\n            print(\"%s | SUM %.5fs | PER ITER avg %.5fs, min %.5fs, max %.5fs\" % (\n                img_str, float(np.sum(times)), np.average(times), np.min(times), np.max(times)))\n\n    print(\"---------------------------\")\n    print(\"Images\")\n    print(\"---------------------------\")\n    for augmenter in augmenters:\n        print(\"[Augmenter: %s]\" % (augmenter.name,))\n        for images in [small_images, medium_images, large_images]:\n            times = []\n            for i in sm.xrange(100):\n                time_start = time.time()\n                _img_aug = augmenter.augment_images(images)\n                time_end = time.time()\n                times.append(time_end - time_start)\n            times = np.array(times)\n            img_str = \"{:20s}\".format(images.shape)\n            print(\"%s | SUM %.5fs | PER ITER avg %.5fs, min %.5fs, max %.5fs\" % (\n                img_str, float(np.sum(times)), np.average(times), np.min(times), np.max(times)))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_perspective_transform.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    image = ia.data.quokka(size=0.5)\n    kps = [ia.KeypointsOnImage(\n        [ia.Keypoint(x=245, y=203), ia.Keypoint(x=365, y=195), ia.Keypoint(x=313, y=269)],\n        shape=(image.shape[0]*2, image.shape[1]*2)\n    )]\n    kps[0] = kps[0].on(image.shape)\n    print(\"image shape:\", image.shape)\n\n    augs = [\n        iaa.PerspectiveTransform(scale=0.01, name=\"pt001\", keep_size=True),\n        iaa.PerspectiveTransform(scale=0.1, name=\"pt01\", keep_size=True),\n        iaa.PerspectiveTransform(scale=0.2, name=\"pt02\", keep_size=True),\n        iaa.PerspectiveTransform(scale=0.3, name=\"pt03\", keep_size=True),\n        iaa.PerspectiveTransform(scale=(0, 0.3), name=\"pt00to03\", keep_size=True)\n    ]\n\n    print(\"original\", image.shape)\n    ia.imshow(kps[0].draw_on_image(image))\n\n    print(\"-----------------\")\n    print(\"Random aug per image\")\n    print(\"-----------------\")\n    for aug in augs:\n        images_aug = []\n        for _ in range(16):\n            aug_det = aug.to_deterministic()\n            img_aug = aug_det.augment_image(image)\n            kps_aug = aug_det.augment_keypoints(kps)[0]\n            img_aug_kps = kps_aug.draw_on_image(img_aug)\n            img_aug_kps = np.pad(img_aug_kps, ((1, 1), (1, 1), (0, 0)), mode=\"constant\", constant_values=255)\n            images_aug.append(img_aug_kps)\n        print(aug.name)\n        ia.imshow(ia.draw_grid(images_aug))\n\n    print(\"----------------\")\n    print(\"6 channels\")\n    print(\"----------------\")\n    image6 = np.dstack([image, image])\n    image6_aug = augs[1].augment_image(image6)\n    ia.imshow(\n        np.hstack([image6_aug[..., 0:3], image6_aug[..., 3:6]])\n    )\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_piecewise_affine.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\n\nimport imgaug as ia\nimport imgaug.random as iarandom\nfrom imgaug import augmenters as iaa\n\niarandom.seed(3)\n\n\ndef main():\n    image = ia.data.quokka(size=0.5)\n    print(image.shape)\n    kps = [\n        ia.KeypointsOnImage(\n            [\n                ia.Keypoint(x=123, y=102),\n                ia.Keypoint(x=182, y=98),\n                ia.Keypoint(x=155, y=134),\n                ia.Keypoint(x=-20, y=20)\n            ],\n            shape=(image.shape[0], image.shape[1])\n        )\n    ]\n    print(\"image shape:\", image.shape)\n\n    augs = [\n        iaa.PiecewiseAffine(scale=0.05),\n        iaa.PiecewiseAffine(scale=0.1),\n        iaa.PiecewiseAffine(scale=0.2)\n    ]\n\n    ia.imshow(kps[0].draw_on_image(image))\n\n    print(\"-----------------\")\n    print(\"Random aug per image\")\n    print(\"-----------------\")\n    for aug in augs:\n        images_aug = []\n        for _ in range(16):\n            aug_det = aug.to_deterministic()\n            img_aug = aug_det.augment_image(image)\n            kps_aug = aug_det.augment_keypoints(kps)[0]\n            img_aug_kps = keypoints_draw_on_image(kps_aug, img_aug)\n            img_aug_kps = np.pad(img_aug_kps, ((1, 1), (1, 1), (0, 0)), mode=\"constant\", constant_values=255)\n            images_aug.append(img_aug_kps)\n        print(aug.name)\n        ia.imshow(ia.draw_grid(images_aug))\n\n\n# TODO why was this used here?\ndef keypoints_draw_on_image(kps, image, color=[0, 255, 0], size=3, copy=True, raise_if_out_of_image=False, border=50):\n    if copy:\n        image = np.copy(image)\n\n    image = np.pad(\n        image,\n        ((border, border), (border, border), (0, 0)),\n        mode=\"constant\",\n        constant_values=0\n    )\n\n    height, width = image.shape[0:2]\n\n    for keypoint in kps.keypoints:\n        y, x = keypoint.y + border, keypoint.x + border\n        if 0 <= y < height and 0 <= x < width:\n            x1 = max(x - size//2, 0)\n            x2 = min(x + 1 + size//2, width - 1)\n            y1 = max(y - size//2, 0)\n            y2 = min(y + 1 + size//2, height - 1)\n            image[y1:y2, x1:x2] = color\n        else:\n            if raise_if_out_of_image:\n                raise Exception(\"Cannot draw keypoint x=%d, y=%d on image with shape %s.\" % (y, x, image.shape))\n\n    return image\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_poisson_noise.py",
    "content": "from __future__ import print_function, division\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    img = ia.data.quokka(0.5)\n    augs = [\n        (\"iaa.AdditivePoissonNoise(0)\", iaa.AdditivePoissonNoise(0)),\n        (\"iaa.AdditivePoissonNoise(10.0)\", iaa.AdditivePoissonNoise(10.0)),\n        (\"iaa.AdditivePoissonNoise(20.0)\", iaa.AdditivePoissonNoise(20.0)),\n        (\"iaa.AdditivePoissonNoise(50.0)\", iaa.AdditivePoissonNoise(50.0)),\n        (\"iaa.AdditivePoissonNoise((10.0, 20))\", iaa.AdditivePoissonNoise((10.0, 20))),\n        (\"iaa.AdditivePoissonNoise([10.0, 20.0, 50])\", iaa.AdditivePoissonNoise([10.0, 20.0, 50])),\n        (\"iaa.AdditivePoissonNoise(20, per_channel=True)\", iaa.AdditivePoissonNoise(50, per_channel=True)),\n    ]\n    for descr, aug in augs:\n        print(descr)\n        imgs_aug = aug.augment_images([img] * 16)\n        ia.imshow(ia.draw_grid(imgs_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_polygons_stay_valid_during_augmentation.py",
    "content": "from __future__ import print_function, division\nimport numpy as np\n\nimport imgaug as ia\nimport imgaug.augmenters as iaa\nfrom imgaug.augmentables.polys import Polygon, PolygonsOnImage\n\n\ndef main():\n    nb_checked = 0\n\n    augs = iaa.SomeOf((1, None), [\n        iaa.Resize({\"height\": (1, 100), \"width\": (1, 100)}),\n        iaa.Affine(\n            scale=(0.01, 2.0),\n            rotate=(-360, 360),\n            shear=(-360, 360),\n            translate_px={\"x\": (-50, 50), \"y\": (-50, 50)}\n        ),\n        iaa.PerspectiveTransform((0.01, 0.2))\n    ])\n\n    height, width = 100, 200\n\n    while True:\n        poly = create_random_polygon(height, width, nb_checked)\n        psoi = PolygonsOnImage([poly], shape=(height, width, 3))\n        psoi_aug = augs.augment_polygons(psoi)\n\n        if not poly.is_valid or not psoi_aug.polygons[0].is_valid:\n            print(\"poly:     \", poly, poly.is_valid)\n            print(\"poly_aug: \", psoi_aug.polygons[0], psoi_aug.polygons[0].is_valid)\n\n        assert poly.is_valid\n        assert psoi_aug.polygons[0].is_valid\n\n        nb_checked += 1\n        if nb_checked % 100 == 0:\n            print(\"Checked %d...\" % (nb_checked,))\n        if nb_checked > 100000:\n            break\n\n\ndef create_random_polygon(height, width, seed):\n    rs = np.random.RandomState(seed)\n    nb_points = rs.randint(3, 50)\n    coords = rs.rand(nb_points, 2)\n    coords = (coords * 2 - 0.5)  # allow coords outside of the image plane\n    coords[:, 0] *= width\n    coords[:, 1] *= height\n    poly = Polygon(coords)\n    if poly.is_valid:\n        return poly\n\n    new_seed = rs.randint(ia.SEED_MIN_VALUE, ia.SEED_MAX_VALUE)\n    return create_random_polygon(height, width, new_seed)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_pooling.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\n\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    run(iaa.AveragePooling)\n    run(iaa.MaxPooling)\n    run(iaa.MinPooling)\n    run(iaa.MedianPooling)\n\n\ndef run(clazz):\n    image = ia.quokka_square((128, 128))\n    aug = clazz(2)\n    ia.imshow(\n        ia.draw_grid(aug.augment_images([image] * (5*5)))\n    )\n\n    aug = clazz(2, keep_size=False)\n    ia.imshow(\n        ia.draw_grid(aug.augment_images([image] * (5*5)))\n    )\n\n    aug_pool = clazz(((0, 10), (0, 10)))\n    aug_blur = clazz(((0, 10), (0, 10)))\n    ia.imshow(\n        np.hstack([\n            ia.draw_grid(aug_pool.augment_images([image] * (4*5)), cols=4),\n            ia.draw_grid(aug_blur.augment_images([image] * (4*5)), cols=4)\n        ])\n    )\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_quantize_uniform_to_n_bits.py",
    "content": "from __future__ import print_function, division, absolute_import\nimport imgaug.augmenters as iaa\nimport imgaug as ia\nimport timeit\n\n\ndef main():\n    for size in [64, 128, 256, 512, 1024]:\n        for nb_bits in [1, 2, 3, 4, 5, 6, 7, 8]:\n            time_iaa = timeit.timeit(\n                \"iaa.quantize_uniform_to_n_bits(image, %d)\" % (nb_bits,),\n                number=1000,\n                setup=(\n                    \"import imgaug as ia; \"\n                    \"import imgaug.augmenters as iaa; \"\n                    \"image = ia.quokka_square((%d, %d))\" % (size, size))\n            )\n            time_pil = timeit.timeit(\n                \"np.asarray(\"\n                \"PIL.ImageOps.posterize(PIL.Image.fromarray(image), %d)\"\n                \")\" % (nb_bits,),\n                number=1000,\n                setup=(\n                    \"import numpy as np; \"\n                    \"import PIL.Image; \"\n                    \"import PIL.ImageOps; \"\n                    \"import imgaug as ia; \"\n                    \"image = ia.quokka_square((%d, %d))\" % (size, size))\n            )\n            print(\"[size=%04d, bits=%d] iaa=%.4f pil=%.4f\" % (\n                size, nb_bits, time_iaa, time_pil))\n\n    image = ia.quokka_square((128, 128))\n    images_q = [iaa.quantize_uniform_to_n_bits(image, nb_bits)\n                for nb_bits\n                in [1, 2, 3, 4, 5, 6, 7, 8]]\n\n    ia.imshow(ia.draw_grid(images_q, cols=8, rows=1))\n\n\ndef posterize(arr, n_bits):\n    import numpy as np\n    import PIL.Image\n    import PIL.ImageOps\n    img = PIL.Image.fromarray(arr)\n    img_q = PIL.ImageOps.posterize(img, n_bits)\n    return np.asarray(img_q)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_rain.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport imageio\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    augs = [\n        iaa.Rain(speed=(0.1, 0.3)),\n        iaa.Rain(),\n        iaa.Rain(drop_size=(0.1, 0.2))\n    ]\n\n    image = imageio.imread(\n        (\"https://upload.wikimedia.org/wikipedia/commons/8/89/\"\n         \"Kukle%2CCzech_Republic..jpg\"),\n        format=\"jpg\")\n\n    for aug, size in zip(augs, [0.1, 0.2, 1.0]):\n        image_rs = ia.imresize_single_image(image, size, \"cubic\")\n        print(image_rs.shape)\n\n        images_aug = aug.augment_images([image_rs] * 64)\n        ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_randaugment.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport numpy as np\n\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    image = ia.data.quokka(0.25)\n\n    for N in [1, 2]:\n        print(\"N=%d\" % (N,))\n\n        images_aug = []\n        for M in [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]:\n            images_aug.extend(\n                iaa.RandAugment(n=N, m=M, random_state=1)(images=[image] * 10)\n            )\n        ia.imshow(ia.draw_grid(images_aug, cols=10))\n\n    for M in [0, 1, 2, 4, 8, 10]:\n        print(\"M=%d\" % (M,))\n        aug = iaa.RandAugment(m=M, random_state=1)\n\n        images_aug = []\n        for _ in np.arange(6):\n            images_aug.extend(aug(images=[image] * 16))\n\n        ia.imshow(ia.draw_grid(images_aug, cols=16, rows=6))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_readme_examples.py",
    "content": "\"\"\"\nScript to verify all examples in the readme.\nSimply execute\n    python test_readme_examples.py\n\n\nThe tests in this file are currently not unittests!\nThey do plot images.\n\nTODO move this to checks/ ?\n\n\"\"\"\nfrom __future__ import print_function, division\nimport functools\n\n\ndef main():\n    example_simple_training_setting()\n    example_very_complex_augmentation_pipeline()\n    example_augment_images_and_keypoints()\n    example_augment_images_and_bounding_boxes()\n    example_augment_images_and_polygons()\n    example_augment_images_and_linestrings()\n    example_augment_images_and_heatmaps()\n    example_augment_images_and_segmentation_maps()\n    example_visualize_augmented_images()\n    example_visualize_augmented_non_image_data()\n    example_using_augmenters_only_once()\n    example_multicore_augmentation()\n    example_probability_distributions_as_parameters()\n    example_withchannels()\n    example_hooks()\n\n\ndef seeded(func):\n    @functools.wraps(func)\n    def wrapper(*args, **kwargs):\n        import imgaug.random as iarandom\n        iarandom.seed(0)\n        func(*args, **kwargs)\n    return wrapper\n\n\n@seeded\ndef example_simple_training_setting():\n    print(\"Example: Simple Training Setting\")\n    import numpy as np\n    import imgaug.augmenters as iaa\n\n    def load_batch(batch_idx):\n        # dummy function, implement this\n        # Return a numpy array of shape (N, height, width, #channels)\n        # or a list of (height, width, #channels) arrays (may have different image\n        # sizes).\n        # Images should be in RGB for colorspace augmentations.\n        # (cv2.imread() returns BGR!)\n        # Images should usually be in uint8 with values from 0-255.\n        return np.zeros((128, 32, 32, 3), dtype=np.uint8) + (batch_idx % 255)\n\n    def train_on_images(images):\n        # dummy function, implement this\n        pass\n\n    # Pipeline:\n    # (1) Crop images from each side by 1-16px, do not resize the results\n    #     images back to the input size. Keep them at the cropped size.\n    # (2) Horizontally flip 50% of the images.\n    # (3) Blur images using a gaussian kernel with sigma between 0.0 and 3.0.\n    seq = iaa.Sequential([\n        iaa.Crop(px=(1, 16), keep_size=False),\n        iaa.Fliplr(0.5),\n        iaa.GaussianBlur(sigma=(0, 3.0))\n    ])\n\n    for batch_idx in range(100):\n        images = load_batch(batch_idx)\n        images_aug = seq(images=images)  # done by the library\n        train_on_images(images_aug)\n\n        # -----\n        # Make sure that the example really does something\n        if batch_idx == 0:\n            assert not np.array_equal(images, images_aug)\n\n\n@seeded\ndef example_very_complex_augmentation_pipeline():\n    print(\"Example: Very Complex Augmentation Pipeline\")\n    import numpy as np\n    import imgaug as ia\n    import imgaug.augmenters as iaa\n\n    # random example images\n    images = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\n\n    # Sometimes(0.5, ...) applies the given augmenter in 50% of all cases,\n    # e.g. Sometimes(0.5, GaussianBlur(0.3)) would blur roughly every second image.\n    sometimes = lambda aug: iaa.Sometimes(0.5, aug)\n\n    # Define our sequence of augmentation steps that will be applied to every image\n    # All augmenters with per_channel=0.5 will sample one value _per image_\n    # in 50% of all cases. In all other cases they will sample new values\n    # _per channel_.\n\n    seq = iaa.Sequential(\n        [\n            # apply the following augmenters to most images\n            iaa.Fliplr(0.5), # horizontally flip 50% of all images\n            iaa.Flipud(0.2), # vertically flip 20% of all images\n            # crop images by -5% to 10% of their height/width\n            sometimes(iaa.CropAndPad(\n                percent=(-0.05, 0.1),\n                pad_mode=ia.ALL,\n                pad_cval=(0, 255)\n            )),\n            sometimes(iaa.Affine(\n                scale={\"x\": (0.8, 1.2), \"y\": (0.8, 1.2)}, # scale images to 80-120% of their size, individually per axis\n                translate_percent={\"x\": (-0.2, 0.2), \"y\": (-0.2, 0.2)}, # translate by -20 to +20 percent (per axis)\n                rotate=(-45, 45), # rotate by -45 to +45 degrees\n                shear=(-16, 16), # shear by -16 to +16 degrees\n                order=[0, 1], # use nearest neighbour or bilinear interpolation (fast)\n                cval=(0, 255), # if mode is constant, use a cval between 0 and 255\n                mode=ia.ALL # use any of scikit-image's warping modes (see 2nd image from the top for examples)\n            )),\n            # execute 0 to 5 of the following (less important) augmenters per image\n            # don't execute all of them, as that would often be way too strong\n            iaa.SomeOf((0, 5),\n                [\n                    sometimes(iaa.Superpixels(p_replace=(0, 1.0), n_segments=(20, 200))), # convert images into their superpixel representation\n                    iaa.OneOf([\n                        iaa.GaussianBlur((0, 3.0)), # blur images with a sigma between 0 and 3.0\n                        iaa.AverageBlur(k=(2, 7)), # blur image using local means with kernel sizes between 2 and 7\n                        iaa.MedianBlur(k=(3, 11)), # blur image using local medians with kernel sizes between 2 and 7\n                    ]),\n                    iaa.Sharpen(alpha=(0, 1.0), lightness=(0.75, 1.5)), # sharpen images\n                    iaa.Emboss(alpha=(0, 1.0), strength=(0, 2.0)), # emboss images\n                    # search either for all edges or for directed edges,\n                    # blend the result with the original image using a blobby mask\n                    iaa.SimplexNoiseAlpha(iaa.OneOf([\n                        iaa.EdgeDetect(alpha=(0.5, 1.0)),\n                        iaa.DirectedEdgeDetect(alpha=(0.5, 1.0), direction=(0.0, 1.0)),\n                    ])),\n                    iaa.AdditiveGaussianNoise(loc=0, scale=(0.0, 0.05*255), per_channel=0.5), # add gaussian noise to images\n                    iaa.OneOf([\n                        iaa.Dropout((0.01, 0.1), per_channel=0.5), # randomly remove up to 10% of the pixels\n                        iaa.CoarseDropout((0.03, 0.15), size_percent=(0.02, 0.05), per_channel=0.2),\n                    ]),\n                    iaa.Invert(0.05, per_channel=True), # invert color channels\n                    iaa.Add((-10, 10), per_channel=0.5), # change brightness of images (by -10 to 10 of original value)\n                    iaa.AddToHueAndSaturation((-20, 20)), # change hue and saturation\n                    # either change the brightness of the whole image (sometimes\n                    # per channel) or change the brightness of subareas\n                    iaa.OneOf([\n                        iaa.Multiply((0.5, 1.5), per_channel=0.5),\n                        iaa.FrequencyNoiseAlpha(\n                            exponent=(-4, 0),\n                            first=iaa.Multiply((0.5, 1.5), per_channel=True),\n                            second=iaa.LinearContrast((0.5, 2.0))\n                        )\n                    ]),\n                    iaa.LinearContrast((0.5, 2.0), per_channel=0.5), # improve or worsen the contrast\n                    iaa.Grayscale(alpha=(0.0, 1.0)),\n                    sometimes(iaa.ElasticTransformation(alpha=(0.5, 3.5), sigma=0.25)), # move pixels locally around (with random strengths)\n                    sometimes(iaa.PiecewiseAffine(scale=(0.01, 0.05))), # sometimes move parts of the image around\n                    sometimes(iaa.PerspectiveTransform(scale=(0.01, 0.1)))\n                ],\n                random_order=True\n            )\n        ],\n        random_order=True\n    )\n    images_aug = seq(images=images)\n\n    # -----\n    # Make sure that the example really does something\n    assert not np.array_equal(images, images_aug)\n\n\n@seeded\ndef example_augment_images_and_keypoints():\n    print(\"Example: Augment Images and Keypoints\")\n    import numpy as np\n    import imgaug.augmenters as iaa\n\n    images = np.zeros((2, 128, 128, 3), dtype=np.uint8)  # two example images\n    images[:, 64, 64, :] = 255\n    points = [\n        [(10.5, 20.5)],  # points on first image\n        [(50.5, 50.5), (60.5, 60.5), (70.5, 70.5)]  # points on second image\n    ]\n\n    seq = iaa.Sequential([\n        iaa.AdditiveGaussianNoise(scale=0.05*255),\n        iaa.Affine(translate_px={\"x\": (1, 5)})\n    ])\n\n    # augment keypoints and images\n    images_aug, points_aug = seq(images=images, keypoints=points)\n\n    print(\"Image 1 center\", np.argmax(images_aug[0, 64, 64:64+6, 0]))\n    print(\"Image 2 center\", np.argmax(images_aug[1, 64, 64:64+6, 0]))\n    print(\"Points 1\", points_aug[0])\n    print(\"Points 2\", points_aug[1])\n\n\n@seeded\ndef example_augment_images_and_bounding_boxes():\n    print(\"Example: Augment Images and Bounding Boxes\")\n    import numpy as np\n    import imgaug as ia\n    import imgaug.augmenters as iaa\n\n    images = np.zeros((2, 128, 128, 3), dtype=np.uint8)  # two example images\n    images[:, 64, 64, :] = 255\n    bbs = [\n        [ia.BoundingBox(x1=10.5, y1=15.5, x2=30.5, y2=50.5)],\n        [ia.BoundingBox(x1=10.5, y1=20.5, x2=50.5, y2=50.5),\n         ia.BoundingBox(x1=40.5, y1=75.5, x2=70.5, y2=100.5)]\n    ]\n\n    seq = iaa.Sequential([\n        iaa.AdditiveGaussianNoise(scale=0.05*255),\n        iaa.Affine(translate_px={\"x\": (1, 5)})\n    ])\n\n    images_aug, bbs_aug = seq(images=images, bounding_boxes=bbs)\n\n\n@seeded\ndef example_augment_images_and_polygons():\n    print(\"Example: Augment Images and Polygons\")\n    import numpy as np\n    import imgaug as ia\n    import imgaug.augmenters as iaa\n\n    images = np.zeros((2, 128, 128, 3), dtype=np.uint8)  # two example images\n    images[:, 64, 64, :] = 255\n    polygons = [\n        [ia.Polygon([(10.5, 10.5), (50.5, 10.5), (50.5, 50.5)])],\n        [ia.Polygon([(0.0, 64.5), (64.5, 0.0), (128.0, 128.0), (64.5, 128.0)])]\n    ]\n\n    seq = iaa.Sequential([\n        iaa.AdditiveGaussianNoise(scale=0.05*255),\n        iaa.Affine(translate_px={\"x\": (1, 5)})\n    ])\n\n    images_aug, polygons_aug = seq(images=images, polygons=polygons)\n\n\n@seeded\ndef example_augment_images_and_linestrings():\n    print(\"Example: Augment Images and LineStrings\")\n    import numpy as np\n    import imgaug as ia\n    import imgaug.augmenters as iaa\n\n    images = np.zeros((2, 128, 128, 3), dtype=np.uint8)  # two example images\n    images[:, 64, 64, :] = 255\n    ls = [\n        [ia.LineString([(10.5, 10.5), (50.5, 10.5), (50.5, 50.5)])],\n        [ia.LineString([(0.0, 64.5), (64.5, 0.0), (128.0, 128.0), (64.5, 128.0),\n                        (128.0, 0.0)])]\n    ]\n\n    seq = iaa.Sequential([\n        iaa.AdditiveGaussianNoise(scale=0.05*255),\n        iaa.Affine(translate_px={\"x\": (1, 5)})\n    ])\n\n    images_aug, ls_aug = seq(images=images, line_strings=ls)\n\n\n@seeded\ndef example_augment_images_and_heatmaps():\n    print(\"Example: Augment Images and Heatmaps\")\n    import numpy as np\n    import imgaug.augmenters as iaa\n\n    # Standard scenario: You have N RGB-images and additionally 21 heatmaps per\n    # image. You want to augment each image and its heatmaps identically.\n    images = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\n    heatmaps = np.random.random(size=(16, 64, 64, 1)).astype(np.float32)\n\n    seq = iaa.Sequential([\n        iaa.GaussianBlur((0, 3.0)),\n        iaa.Affine(translate_px={\"x\": (-40, 40)}),\n        iaa.Crop(px=(0, 10))\n    ])\n\n    images_aug, heatmaps_aug = seq(images=images, heatmaps=heatmaps)\n\n\n@seeded\ndef example_augment_images_and_segmentation_maps():\n    print(\"Example: Augment Images and Segmentation Maps\")\n    import numpy as np\n    import imgaug.augmenters as iaa\n\n    # Standard scenario: You have N=16 RGB-images and additionally one segmentation\n    # map per image. You want to augment each image and its heatmaps identically.\n    images = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\n    segmaps = np.random.randint(0, 10, size=(16, 64, 64, 1), dtype=np.int32)\n\n    seq = iaa.Sequential([\n        iaa.GaussianBlur((0, 3.0)),\n        iaa.Affine(translate_px={\"x\": (-40, 40)}),\n        iaa.Crop(px=(0, 10))\n    ])\n\n    images_aug, segmaps_aug = seq(images=images, segmentation_maps=segmaps)\n\n\n@seeded\ndef example_visualize_augmented_images():\n    print(\"Example: Visualize Augmented Images\")\n    import numpy as np\n    import imgaug.augmenters as iaa\n\n    images = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\n    seq = iaa.Sequential([iaa.Fliplr(0.5), iaa.GaussianBlur((0, 3.0))])\n\n    # Show an image with 8*8 augmented versions of image 0 and 8*8 augmented\n    # versions of image 1. Identical augmentations will be applied to\n    # image 0 and 1.\n    seq.show_grid([images[0], images[1]], cols=8, rows=8)\n\n\n@seeded\ndef example_visualize_augmented_non_image_data():\n    print(\"Example: Visualize Augmented Non-Image Data\")\n    import numpy as np\n    import imgaug as ia\n\n    image = np.zeros((64, 64, 3), dtype=np.uint8)\n\n    # points\n    kps = [ia.Keypoint(x=10.5, y=20.5), ia.Keypoint(x=60.5, y=60.5)]\n    kpsoi = ia.KeypointsOnImage(kps, shape=image.shape)\n    image_with_kps = kpsoi.draw_on_image(image, size=7, color=(0, 0, 255))\n    ia.imshow(image_with_kps)\n\n    # bbs\n    bbsoi = ia.BoundingBoxesOnImage([\n        ia.BoundingBox(x1=10.5, y1=20.5, x2=50.5, y2=30.5)\n    ], shape=image.shape)\n    image_with_bbs = bbsoi.draw_on_image(image)\n    image_with_bbs = ia.BoundingBox(\n        x1=50.5, y1=10.5, x2=100.5, y2=16.5\n    ).draw_on_image(image_with_bbs, color=(255, 0, 0), size=3)\n    ia.imshow(image_with_bbs)\n\n    # polygons\n    psoi = ia.PolygonsOnImage([\n        ia.Polygon([(10.5, 20.5), (50.5, 30.5), (10.5, 50.5)])\n    ], shape=image.shape)\n    image_with_polys = psoi.draw_on_image(\n        image, alpha_points=0, alpha_face=0.5, color_lines=(255, 0, 0))\n    ia.imshow(image_with_polys)\n\n    # heatmaps\n    # pick first result via [0] here, because one image per heatmap channel\n    # is generated\n    hms = ia.HeatmapsOnImage(np.random.random(size=(32, 32, 1)).astype(np.float32),\n                             shape=image.shape)\n    image_with_hms = hms.draw_on_image(image)[0]\n    ia.imshow(image_with_hms)\n\n\n@seeded\ndef example_using_augmenters_only_once():\n    print(\"Example: Using Augmenters Only Once\")\n    from imgaug import augmenters as iaa\n    import numpy as np\n\n    images = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\n\n    # always horizontally flip each input image\n    images_aug = iaa.Fliplr(1.0)(images=images)\n\n    # vertically flip each input image with 90% probability\n    images_aug = iaa.Flipud(0.9)(images=images)\n\n    # blur 50% of all images using a gaussian kernel with a sigma of 3.0\n    images_aug = iaa.Sometimes(0.5, iaa.GaussianBlur(3.0))(images=images)\n\n\n@seeded\ndef example_multicore_augmentation():\n    print(\"Example: Multicore Augmentation\")\n    import skimage.data\n    import imgaug as ia\n    import imgaug.augmenters as iaa\n    from imgaug.augmentables.batches import UnnormalizedBatch\n\n    # Number of batches and batch size for this example\n    nb_batches = 10\n    batch_size = 32\n\n    # Example augmentation sequence to run in the background\n    augseq = iaa.Sequential([\n        iaa.Fliplr(0.5),\n        iaa.CoarseDropout(p=0.1, size_percent=0.1)\n    ])\n\n    # For simplicity, we use the same image here many times\n    astronaut = skimage.data.astronaut()\n    astronaut = ia.imresize_single_image(astronaut, (64, 64))\n\n    # Make batches out of the example image (here: 10 batches, each 32 times\n    # the example image)\n    batches = []\n    for _ in range(nb_batches):\n        batches.append(UnnormalizedBatch(images=[astronaut] * batch_size))\n\n    # Show the augmented images.\n    # Note that augment_batches() returns a generator.\n    for images_aug in augseq.augment_batches(batches, background=True):\n        ia.imshow(ia.draw_grid(images_aug.images_aug, cols=8))\n\n\n@seeded\ndef example_probability_distributions_as_parameters():\n    print(\"Example: Probability Distributions as Parameters\")\n    import numpy as np\n    from imgaug import augmenters as iaa\n    from imgaug import parameters as iap\n\n    images = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\n\n    # Blur by a value sigma which is sampled from a uniform distribution\n    # of range 10.1 <= x < 13.0.\n    # The convenience shortcut for this is: GaussianBlur((10.1, 13.0))\n    blurer = iaa.GaussianBlur(10 + iap.Uniform(0.1, 3.0))\n    images_aug = blurer(images=images)\n\n    # Blur by a value sigma which is sampled from a gaussian distribution\n    # N(1.0, 0.1), i.e. sample a value that is usually around 1.0.\n    # Clip the resulting value so that it never gets below 0.1 or above 3.0.\n    blurer = iaa.GaussianBlur(iap.Clip(iap.Normal(1.0, 0.1), 0.1, 3.0))\n    images_aug = blurer(images=images)\n\n\n@seeded\ndef example_withchannels():\n    print(\"Example: WithChannels\")\n    import numpy as np\n    import imgaug.augmenters as iaa\n\n    # fake RGB images\n    images = np.random.randint(0, 255, (16, 128, 128, 3), dtype=np.uint8)\n\n    # add a random value from the range (-30, 30) to the first two channels of\n    # input images (e.g. to the R and G channels)\n    aug = iaa.WithChannels(\n      channels=[0, 1],\n      children=iaa.Add((-30, 30))\n    )\n\n    images_aug = aug(images=images)\n\n\n@seeded\ndef example_hooks():\n    print(\"Example: Hooks\")\n    import numpy as np\n    import imgaug as ia\n    import imgaug.augmenters as iaa\n\n    # Images and heatmaps, just arrays filled with value 30.\n    # We define the heatmaps here as uint8 arrays as we are going to feed them\n    # through the pipeline similar to normal images. In that way, every\n    # augmenter is applied to them.\n    images = np.full((16, 128, 128, 3), 30, dtype=np.uint8)\n    heatmaps = np.full((16, 128, 128, 21), 30, dtype=np.uint8)\n\n    # add vertical lines to see the effect of flip\n    images[:, 16:128-16, 120:124, :] = 120\n    heatmaps[:, 16:128-16, 120:124, :] = 120\n\n    seq = iaa.Sequential([\n      iaa.Fliplr(0.5, name=\"Flipper\"),\n      iaa.GaussianBlur((0, 3.0), name=\"GaussianBlur\"),\n      iaa.Dropout(0.02, name=\"Dropout\"),\n      iaa.AdditiveGaussianNoise(scale=0.01*255, name=\"MyLittleNoise\"),\n      iaa.AdditiveGaussianNoise(loc=32, scale=0.0001*255, name=\"SomeOtherNoise\"),\n      iaa.Affine(translate_px={\"x\": (-40, 40)}, name=\"Affine\")\n    ])\n\n    # change the activated augmenters for heatmaps,\n    # we only want to execute horizontal flip, affine transformation and one of\n    # the gaussian noises\n    def activator_heatmaps(images, augmenter, parents, default):\n        if augmenter.name in [\"GaussianBlur\", \"Dropout\", \"MyLittleNoise\"]:\n            return False\n        else:\n            # default value for all other augmenters\n            return default\n    hooks_heatmaps = ia.HooksImages(activator=activator_heatmaps)\n\n    # call to_deterministic() once per batch, NOT only once at the start\n    seq_det = seq.to_deterministic()\n    images_aug = seq_det(images=images)\n    heatmaps_aug = seq_det(images=heatmaps, hooks=hooks_heatmaps)\n\n    # -----------\n    ia.show_grid(images_aug)\n    ia.show_grid(heatmaps_aug[..., 0:3])\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_remove_saturation.py",
    "content": "import imgaug as ia\nimport imgaug.augmenters as iaa\nimport imageio\n\n\ndef main():\n    urls = [\n        (\"https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/\"\n         \"Sarcophilus_harrisii_taranna.jpg/\"\n         \"320px-Sarcophilus_harrisii_taranna.jpg\"),\n        (\"https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/\"\n         \"Vincent_van_Gogh_-_Wheatfield_with_crows_-_Google_Art_Project.jpg/\"\n         \"320px-Vincent_van_Gogh_-_Wheatfield_with_crows_-_Google_Art_Project\"\n         \".jpg\"),\n        (\"https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/\"\n         \"Galerella_sanguinea_Zoo_Praha_2011-2.jpg/207px-Galerella_sanguinea_\"\n         \"Zoo_Praha_2011-2.jpg\"),\n        (\"https://upload.wikimedia.org/wikipedia/commons/thumb/9/96/\"\n         \"Ambrosius_Bosschaert_the_Elder_%28Dutch_-_Flower_Still_Life_-_\"\n         \"Google_Art_Project.jpg/307px-Ambrosius_Bosschaert_the_Elder_%28\"\n         \"Dutch_-_Flower_Still_Life_-_Google_Art_Project.jpg\")\n    ]\n\n    image = imageio.imread(urls[3])\n\n    aug = iaa.RemoveSaturation()\n    images_aug = aug(images=[image] * (5*5))\n\n    ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_resize.py",
    "content": "from __future__ import print_function, division\n\nimport cv2\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    # test 2d image\n    ia.imshow(iaa.Resize(64).augment_image(data.camera()))\n\n    # test many images\n    images = [ia.data.quokka(size=0.5), ia.data.quokka(size=0.5)]\n    images_aug = iaa.Resize(64).augment_images(images)\n    ia.imshow(np.hstack(images_aug))\n\n    image = ia.data.quokka(size=0.5)\n    kps = [ia.KeypointsOnImage(\n        [ia.Keypoint(x=245, y=203), ia.Keypoint(x=365, y=195), ia.Keypoint(x=313, y=269)],\n        shape=(image.shape[0]*2, image.shape[1]*2)\n    )]\n    kps[0] = kps[0].on(image.shape)\n    print(\"image shape:\", image.shape)\n\n    augs = [\n        iaa.Resize(\"keep\", name=\"keep\"),\n        iaa.Resize(32, name=\"i32\"),\n        iaa.Resize(0.5, name=\"f05\"),\n\n        iaa.Resize({\"height\": 32}, name=\"height32\"),\n        iaa.Resize({\"width\": 32}, name=\"width32\"),\n        iaa.Resize({\"height\": \"keep\", \"width\": 32}, name=\"keep-width32\"),\n        iaa.Resize({\"height\": 32, \"width\": \"keep\"}, name=\"height32-keep\"),\n        iaa.Resize({\"height\": \"keep\", \"width\": \"keep\"}, name=\"keep-keep\"),\n        iaa.Resize({\"height\": 32, \"width\": 64}, name=\"height32width64\"),\n        iaa.Resize({\"height\": 64, \"width\": \"keep-aspect-ratio\"}, name=\"height64width-kar\"),\n        iaa.Resize({\"height\": \"keep-aspect-ratio\", \"width\": 64}, name=\"height-kar_width64\")\n    ]\n\n    augs_many = [\n        iaa.Resize((32, 128), name=\"tuple-32-128\"),\n        iaa.Resize([32, 64, 128], name=\"list-32-64-128\"),\n        iaa.Resize({\"height\": (32, 128), \"width\": \"keep\"}, name=\"height-32-64_width-keep\"),\n        iaa.Resize({\"height\": (32, 128), \"width\": \"keep-aspect-ratio\"}, name=\"height-32-128_width-kar\"),\n        iaa.Resize({\"height\": (32, 128), \"width\": (32, 128)}, name=\"height-32-128_width-32-128\")\n    ]\n\n    print(\"original\", image.shape)\n    ia.imshow(kps[0].draw_on_image(image))\n\n    print(\"-----------------\")\n    print(\"Same size per image\")\n    print(\"-----------------\")\n    for aug in augs:\n        img_aug = aug.augment_image(image)\n        kps_aug = aug.augment_keypoints(kps)[0]\n        img_aug_kps = kps_aug.draw_on_image(img_aug)\n        print(aug.name, img_aug_kps.shape, img_aug_kps.shape[1]/img_aug_kps.shape[0])\n        ia.imshow(img_aug_kps)\n\n    print(\"-----------------\")\n    print(\"Random per image\")\n    print(\"-----------------\")\n    for aug in augs_many:\n        images_aug = []\n        for _ in range(64):\n            aug_det = aug.to_deterministic()\n            img_aug = aug_det.augment_image(image)\n            kps_aug = aug_det.augment_keypoints(kps)[0]\n            img_aug_kps = kps_aug.draw_on_image(img_aug)\n            images_aug.append(img_aug_kps)\n        print(aug.name)\n        ia.imshow(ia.draw_grid(images_aug))\n\n    print(\"nearest/cv2.INTER_NEAREST/cubic\")\n    ia.imshow(np.hstack([\n        iaa.Resize(64, interpolation=\"nearest\").augment_image(image),\n        iaa.Resize(64, interpolation=cv2.INTER_NEAREST).augment_image(image),\n        iaa.Resize(64, interpolation=\"cubic\").augment_image(image)\n    ]))\n\n    print(\"random nearest/cubic\")\n    iaa.Resize(64, interpolation=[\"nearest\", \"cubic\"]).show_grid([image], 8, 8)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_rot90.py",
    "content": "from __future__ import print_function, division\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    augs = [\n        (\"iaa.Rot90(-1, keep_size=False)\", iaa.Rot90(-1, keep_size=False)),\n        (\"iaa.Rot90(0, keep_size=False)\", iaa.Rot90(0, keep_size=False)),\n        (\"iaa.Rot90(1, keep_size=False)\", iaa.Rot90(1, keep_size=False)),\n        (\"iaa.Rot90(2, keep_size=False)\", iaa.Rot90(2, keep_size=False)),\n        (\"iaa.Rot90(3, keep_size=False)\", iaa.Rot90(3, keep_size=False)),\n        (\"iaa.Rot90(4, keep_size=False)\", iaa.Rot90(4, keep_size=False)),\n        (\"iaa.Rot90(-1, keep_size=True)\", iaa.Rot90(-1, keep_size=True)),\n        (\"iaa.Rot90(0, keep_size=True)\", iaa.Rot90(0, keep_size=True)),\n        (\"iaa.Rot90(1, keep_size=True)\", iaa.Rot90(1, keep_size=True)),\n        (\"iaa.Rot90(2, keep_size=True)\", iaa.Rot90(2, keep_size=True)),\n        (\"iaa.Rot90(3, keep_size=True)\", iaa.Rot90(3, keep_size=True)),\n        (\"iaa.Rot90(4, keep_size=True)\", iaa.Rot90(4, keep_size=True)),\n        (\"iaa.Rot90([0, 1, 2, 3, 4], keep_size=False)\", iaa.Rot90([0, 1, 2, 3, 4], keep_size=False)),\n        (\"iaa.Rot90([0, 1, 2, 3, 4], keep_size=True)\", iaa.Rot90([0, 1, 2, 3, 4], keep_size=True)),\n        (\"iaa.Rot90((0, 4), keep_size=False)\", iaa.Rot90((0, 4), keep_size=False)),\n        (\"iaa.Rot90((0, 4), keep_size=True)\", iaa.Rot90((0, 4), keep_size=True)),\n        (\"iaa.Rot90((1, 3), keep_size=False)\", iaa.Rot90((1, 3), keep_size=False)),\n        (\"iaa.Rot90((1, 3), keep_size=True)\", iaa.Rot90((1, 3), keep_size=True))\n    ]\n\n    image = ia.data.quokka(0.25)\n\n    print(\"--------\")\n    print(\"Image + Keypoints\")\n    print(\"--------\")\n    kps = ia.quokka_keypoints(0.25)\n    for name, aug in augs:\n        print(name, \"...\")\n        aug_det = aug.to_deterministic()\n        images_aug = aug_det.augment_images([image] * 16)\n        kps_aug = aug_det.augment_keypoints([kps] * 16)\n        images_aug = [kps_aug_i.draw_on_image(image_aug_i, size=5)\n                      for image_aug_i, kps_aug_i in zip(images_aug, kps_aug)]\n        ia.imshow(ia.draw_grid(images_aug))\n\n    print(\"--------\")\n    print(\"Image + Heatmaps (low res)\")\n    print(\"--------\")\n    hms = ia.quokka_heatmap(0.10)\n    for name, aug in augs:\n        print(name, \"...\")\n        aug_det = aug.to_deterministic()\n        images_aug = aug_det.augment_images([image] * 16)\n        hms_aug = aug_det.augment_heatmaps([hms] * 16)\n        images_aug = [hms_aug_i.draw_on_image(image_aug_i)[0]\n                      for image_aug_i, hms_aug_i in zip(images_aug, hms_aug)]\n        ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_seed.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nimport imgaug.random as iarandom\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    img = data.astronaut()\n    img = ia.imresize_single_image(img, (64, 64))\n    aug = iaa.Fliplr(0.5)\n    unseeded1 = aug.draw_grid(img, cols=8, rows=1)\n    unseeded2 = aug.draw_grid(img, cols=8, rows=1)\n\n    iarandom.seed(1000)\n    seeded1 = aug.draw_grid(img, cols=8, rows=1)\n    seeded2 = aug.draw_grid(img, cols=8, rows=1)\n\n    iarandom.seed(1000)\n    reseeded1 = aug.draw_grid(img, cols=8, rows=1)\n    reseeded2 = aug.draw_grid(img, cols=8, rows=1)\n\n    iarandom.seed(1001)\n    reseeded3 = aug.draw_grid(img, cols=8, rows=1)\n    reseeded4 = aug.draw_grid(img, cols=8, rows=1)\n\n    all_rows = np.vstack([unseeded1, unseeded2, seeded1, seeded2, reseeded1, reseeded2, reseeded3, reseeded4])\n    ia.imshow(all_rows)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_segmentation_maps.py",
    "content": "from __future__ import print_function\n\nimport numpy as np\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    quokka = ia.data.quokka(size=0.5)\n    h, w = quokka.shape[0:2]\n    c = 1\n    segmap = np.zeros((h, w, c), dtype=np.int32)\n    segmap[70:120, 90:150, 0] = 1\n    segmap[30:70, 50:65, 0] = 2\n    segmap[20:50, 55:85, 0] = 3\n    segmap[120:140, 0:20, 0] = 4\n\n    segmap = ia.SegmentationMapsOnImage(segmap, quokka.shape)\n\n    print(\"Affine...\")\n    aug = iaa.Affine(translate_px={\"x\": 20}, mode=\"constant\", cval=128)\n    quokka_aug = aug.augment_image(quokka)\n    segmaps_aug = aug.augment_segmentation_maps([segmap])[0]\n    segmaps_drawn = segmap.draw_on_image(quokka)[0]\n    segmaps_aug_drawn = segmaps_aug.draw_on_image(quokka_aug)[0]\n\n    ia.imshow(\n        np.hstack([\n            segmaps_drawn,\n            segmaps_aug_drawn\n        ])\n    )\n\n    print(\"Affine with mode=edge...\")\n    aug = iaa.Affine(translate_px={\"x\": 20}, mode=\"edge\")\n    quokka_aug = aug.augment_image(quokka)\n    segmaps_aug = aug.augment_segmentation_maps(segmap)\n    segmaps_drawn = segmap.draw_on_image(quokka)[0]\n    segmaps_aug_drawn = segmaps_aug.draw_on_image(quokka_aug)[0]\n\n    ia.imshow(\n        np.hstack([\n            segmaps_drawn,\n            segmaps_aug_drawn\n        ])\n    )\n\n    print(\"PiecewiseAffine...\")\n    aug = iaa.PiecewiseAffine(scale=0.04)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    segmaps_aug = aug_det.augment_segmentation_maps(segmap)\n    segmaps_drawn = segmap.draw_on_image(quokka)[0]\n    segmaps_aug_drawn = segmaps_aug.draw_on_image(quokka_aug)[0]\n\n    ia.imshow(\n        np.hstack([\n            segmaps_drawn,\n            segmaps_aug_drawn\n        ])\n    )\n\n    print(\"PerspectiveTransform...\")\n    aug = iaa.PerspectiveTransform(scale=0.04)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    segmaps_aug = aug_det.augment_segmentation_maps(segmap)\n    segmaps_drawn = segmap.draw_on_image(quokka)[0]\n    segmaps_aug_drawn = segmaps_aug.draw_on_image(quokka_aug)[0]\n\n    ia.imshow(\n        np.hstack([\n            segmaps_drawn,\n            segmaps_aug_drawn\n        ])\n    )\n\n    print(\"ElasticTransformation alpha=3, sig=0.5...\")\n    aug = iaa.ElasticTransformation(alpha=3.0, sigma=0.5)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    segmaps_aug = aug_det.augment_segmentation_maps(segmap)\n    segmaps_drawn = segmap.draw_on_image(quokka)[0]\n    segmaps_aug_drawn = segmaps_aug.draw_on_image(quokka_aug)[0]\n\n    ia.imshow(\n        np.hstack([\n            segmaps_drawn,\n            segmaps_aug_drawn\n        ])\n    )\n\n    print(\"ElasticTransformation alpha=10, sig=3...\")\n    aug = iaa.ElasticTransformation(alpha=10.0, sigma=3.0)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    segmaps_aug = aug_det.augment_segmentation_maps(segmap)\n    segmaps_drawn = segmap.draw_on_image(quokka)[0]\n    segmaps_aug_drawn = segmaps_aug.draw_on_image(quokka_aug)[0]\n\n    ia.imshow(\n        np.hstack([\n            segmaps_drawn,\n            segmaps_aug_drawn\n        ])\n    )\n\n    print(\"ElasticTransformation alpha=200, sig=20...\")\n    aug = iaa.ElasticTransformation(alpha=200.0, sigma=20.0)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    segmaps_aug = aug_det.augment_segmentation_maps(segmap)\n    segmaps_drawn = segmap.draw_on_image(quokka)[0]\n    segmaps_aug_drawn = segmaps_aug.draw_on_image(quokka_aug)[0]\n\n    ia.imshow(\n        np.hstack([\n            segmaps_drawn,\n            segmaps_aug_drawn\n        ])\n    )\n\n    print(\"CopAndPad mode=constant...\")\n    aug = iaa.CropAndPad(px=(-10, 10, 15, -15), pad_mode=\"constant\", pad_cval=128)\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    segmaps_aug = aug_det.augment_segmentation_maps(segmap)\n    segmaps_drawn = segmap.draw_on_image(quokka)[0]\n    segmaps_aug_drawn = segmaps_aug.draw_on_image(quokka_aug)[0]\n\n    ia.imshow(\n        np.hstack([\n            segmaps_drawn,\n            segmaps_aug_drawn\n        ])\n    )\n\n    print(\"CropAndPad mode=edge...\")\n    aug = iaa.CropAndPad(px=(-10, 10, 15, -15), pad_mode=\"edge\")\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    segmaps_aug = aug_det.augment_segmentation_maps(segmap)\n    segmaps_drawn = segmap.draw_on_image(quokka)[0]\n    segmaps_aug_drawn = segmaps_aug.draw_on_image(quokka_aug)[0]\n\n    ia.imshow(\n        np.hstack([\n            segmaps_drawn,\n            segmaps_aug_drawn\n        ])\n    )\n\n    print(\"Resize...\")\n    aug = iaa.Resize(0.5, interpolation=\"nearest\")\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    segmaps_aug = aug_det.augment_segmentation_maps(segmap)\n    segmaps_drawn = segmap.draw_on_image(quokka)[0]\n    segmaps_aug_drawn = segmaps_aug.draw_on_image(quokka_aug)[0]\n\n    ia.imshow(ia.draw_grid([segmaps_drawn, segmaps_aug_drawn], cols=2))\n\n    print(\"Alpha...\")\n    aug = iaa.Alpha(0.7, iaa.Affine(rotate=20))\n    aug_det = aug.to_deterministic()\n    quokka_aug = aug_det.augment_image(quokka)\n    segmaps_aug = aug_det.augment_segmentation_maps(segmap)\n    segmaps_drawn = segmap.draw_on_image(quokka)[0]\n    segmaps_aug_drawn = segmaps_aug.draw_on_image(quokka_aug)[0]\n\n    ia.imshow(\n        np.hstack([\n            segmaps_drawn,\n            segmaps_aug_drawn\n        ])\n    )\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_single_image_warning.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\n\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    def z(shape):\n        return np.zeros(shape, dtype=np.uint8)\n\n    seq = iaa.Identity()\n\n    print(\"This should generate NO warning:\")\n    _image_aug = seq.augment_images(z((1, 16, 16, 3)))\n\n    print(\"This should generate NO warning:\")\n    _image_aug = seq.augment_images(z((16, 16, 8)))\n\n    print(\"This should generate NO warning:\")\n    _image_aug = seq.augment_images([z((16, 16, 3))])\n\n    print(\"This should generate NO warning:\")\n    _image_aug = seq.augment_images([z((16, 16))])\n\n    print(\"This should generate a warning:\")\n    _image_aug = seq.augment_images(z((16, 16, 3)))\n\n    print(\"This should generate a warning:\")\n    for _ in range(2):\n        _image_aug = seq.augment_images(z((16, 16, 1)))\n\n    print(\"This should fail:\")\n    _image_aug = seq.augment_images(z((16, 16)))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_snowflakes.py",
    "content": "from __future__ import print_function, division\n\nimport imageio\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    for size in [0.1, 0.2, 1.0]:\n        image = imageio.imread(\"https://upload.wikimedia.org/wikipedia/commons/8/89/Kukle%2CCzech_Republic..jpg\",\n                               format=\"jpg\")\n        image = ia.imresize_single_image(image, size, \"cubic\")\n        print(image.shape)\n        augs = [\n            (\"iaa.Snowflakes()\", iaa.Snowflakes())\n        ]\n\n        for descr, aug in augs:\n            print(descr)\n            images_aug = aug.augment_images([image] * 64)\n            ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_snowflakes_layer.py",
    "content": "from __future__ import print_function, division\n\nimport imageio\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    image = imageio.imread(\"https://upload.wikimedia.org/wikipedia/commons/8/89/Kukle%2CCzech_Republic..jpg\",\n                           format=\"jpg\")\n    augs = [\n        (\"iaa.SnowflakesLayer()\", iaa.SnowflakesLayer(\n            density=0.05, density_uniformity=0.5, flake_size=0.9, flake_size_uniformity=0.5,\n            angle=(-45, 45), speed=(0.001, 0.04), blur_sigma_fraction=(0.75*0.0001, 0.75*0.001))\n         )\n    ]\n\n    for descr, aug in augs:\n        print(descr)\n        images_aug = aug.augment_images([image] * 64)\n        ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_solarize.py",
    "content": "from __future__ import print_function, division, absolute_import\nimport imgaug as ia\nimport imgaug.augmenters as iaa\nimport timeit\n\n\ndef main():\n    for size in [64, 128, 256, 512, 1024]:\n        for threshold in [64, 128, 192]:\n            time_iaa = timeit.timeit(\n                \"iaa.solarize(image, %d)\" % (threshold,),\n                number=1000,\n                setup=(\n                    \"import imgaug as ia; \"\n                    \"import imgaug.augmenters as iaa; \"\n                    \"image = ia.quokka_square((%d, %d))\" % (size, size))\n            )\n            time_pil = timeit.timeit(\n                \"np.asarray(\"\n                \"PIL.ImageOps.solarize(PIL.Image.fromarray(image), %d)\"\n                \")\" % (threshold,),\n                number=1000,\n                setup=(\n                    \"import numpy as np; \"\n                    \"import PIL.Image; \"\n                    \"import PIL.ImageOps; \"\n                    \"import imgaug as ia; \"\n                    \"image = ia.quokka_square((%d, %d))\" % (size, size))\n            )\n            print(\"[size=%04d, thresh=%03d] iaa=%.4f pil=%.4f\" % (\n                size, threshold, time_iaa, time_pil))\n\n    image = ia.quokka_square((128, 128))\n    images_aug = iaa.Solarize(1.0)(images=[image] * (5*5))\n    ia.imshow(ia.draw_grid(images_aug))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_some_of.py",
    "content": "from __future__ import print_function, division\n\nimport cv2\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\nTIME_PER_STEP = 20000\nNB_AUGS_PER_IMAGE = 10\n\n\ndef main():\n    image = data.astronaut()\n    image = ia.imresize_single_image(image, (64, 64))\n    keypoints_on_image = ia.KeypointsOnImage([ia.Keypoint(x=10, y=10)], shape=image.shape)\n    images_arr = np.array([image for _ in range(NB_AUGS_PER_IMAGE)])\n    images_list = [image for _ in range(NB_AUGS_PER_IMAGE)]\n    keypoints_on_images = [keypoints_on_image.deepcopy() for _ in range(NB_AUGS_PER_IMAGE)]\n    print(\"image shape:\", image.shape)\n    print(\"Press ENTER or wait %d ms to proceed to the next image.\" % (TIME_PER_STEP,))\n\n    children = [\n        iaa.CoarseDropout(p=0.5, size_percent=0.05),\n        iaa.AdditiveGaussianNoise(scale=0.1*255)\n    ]\n\n    n = [\n        None,\n        0,\n        1,\n        len(children),\n        len(children)+1,\n        (1, 1),\n        (1, len(children)),\n        (1, len(children)+1),\n        (1, None)\n    ]\n\n    cv2.namedWindow(\"aug\", cv2.WINDOW_NORMAL)\n    cv2.resizeWindow(\"aug\", 64*NB_AUGS_PER_IMAGE, (2+4+4)*64)\n\n    rows = []\n    for ni in n:\n        aug = iaa.SomeOf(ni, children, random_order=False)\n        rows.append(aug.augment_images(images_arr))\n    grid = to_grid(rows, None)\n    cv2.imshow(\"aug\", grid[..., ::-1])  # here with rgb2bgr\n    cv2.waitKey(TIME_PER_STEP)\n\n    for ni in n:\n        print(\"------------------------\")\n        print(\"-- %s\" % (str(ni),))\n        print(\"------------------------\")\n        aug = iaa.SomeOf(ni, children, random_order=False)\n        aug_ro = iaa.SomeOf(ni, children, random_order=True)\n\n        aug_det = aug.to_deterministic()\n        aug_ro_det = aug_ro.to_deterministic()\n\n        aug_kps = []\n        aug_kps.extend([aug_det.augment_keypoints(keypoints_on_images)] * 4)\n        aug_kps.extend([aug_ro_det.augment_keypoints(keypoints_on_images)] * 4)\n\n        aug_rows = []\n        aug_rows.append(images_arr)\n        aug_rows.append(images_list)\n\n        aug_rows.append(aug_det.augment_images(images_arr))\n        aug_rows.append(aug_det.augment_images(images_arr))\n        aug_rows.append(aug_det.augment_images(images_list))\n        aug_rows.append(aug_det.augment_images(images_list))\n\n        aug_rows.append(aug_ro_det.augment_images(images_arr))\n        aug_rows.append(aug_ro_det.augment_images(images_arr))\n        aug_rows.append(aug_ro_det.augment_images(images_list))\n        aug_rows.append(aug_ro_det.augment_images(images_list))\n\n        grid = to_grid(aug_rows, aug_kps)\n\n        title = \"n=%s\" % (str(ni),)\n        grid = ia.draw_text(grid, x=5, y=5, text=title)\n\n        cv2.imshow(\"aug\", grid[..., ::-1]) # here with rgb2bgr\n        cv2.waitKey(TIME_PER_STEP)\n\n\n# TODO could be replaced by imgaug.draw_grid()?\ndef to_grid(rows, rows_kps):\n    if rows_kps is None:\n        rows = [np.hstack(list(row)) for row in rows]\n        return np.vstack(rows)\n    else:\n        rows_rendered = []\n        for row, row_kps in zip(rows, rows_kps):\n            row_with_kps = []\n            for i in range(len(row)):\n                img = row[i]\n                img_kps = row_kps[i].draw_on_image(img)\n                row_with_kps.append(img_kps)\n            rows_rendered.append(np.hstack(row_with_kps))\n\n        return np.vstack(rows_rendered)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_superpixels.py",
    "content": "from __future__ import print_function, division\n\nimport time\nfrom itertools import cycle\n\nimport cv2\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\nPOINT_SIZE = 5\nSEGMENTS_PER_STEP = 1\nTIME_PER_STEP = 10\n\n\ndef main():\n    image = data.astronaut()[..., ::-1]  # rgb2bgr\n    print(image.shape)\n\n    cv2.namedWindow(\"aug\", cv2.WINDOW_NORMAL)\n    cv2.imshow(\"aug\", image)\n    cv2.waitKey(TIME_PER_STEP)\n\n    for n_segments in cycle(reversed(np.arange(1, 200, SEGMENTS_PER_STEP))):\n        aug = iaa.Superpixels(p_replace=0.75, n_segments=n_segments)\n        time_start = time.time()\n        img_aug = aug.augment_image(image)\n        print(\"augmented %d in %.4fs\" % (n_segments, time.time() - time_start))\n        img_aug = ia.draw_text(img_aug, x=5, y=5, text=\"%d\" % (n_segments,))\n\n        cv2.imshow(\"aug\", img_aug)\n        cv2.waitKey(TIME_PER_STEP)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_uniform_color_quantization.py",
    "content": "from __future__ import print_function, division\nimport numpy as np\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    image = ia.quokka_square((256, 256))\n    ia.imshow(\n        ia.draw_grid([\n            iaa.quantize_uniform(image, 2),\n            iaa.quantize_uniform(image, 4),\n            iaa.quantize_uniform(image, 8),\n            iaa.quantize_uniform(image, 16),\n            iaa.quantize_uniform(image, 32),\n            iaa.quantize_uniform(image, 64)\n        ], cols=6)\n    )\n\n    aug = iaa.UniformColorQuantization((2, 16))\n    ia.imshow(ia.draw_grid(aug(images=[image] * 16)))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_visually.py",
    "content": "\"\"\"\nTests to visually inspect the results of the library's functionality.\nRun checks via\n    python check_visually.py\n\"\"\"\nfrom __future__ import print_function, division\n\nimport argparse\n\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Check augmenters visually.\")\n    parser.add_argument(\n        \"--only\", default=None,\n        help=\"If this is set, then only the results of an augmenter with this name will be shown. \"\n             \"Optionally, comma-separated list.\",\n        required=False)\n    args = parser.parse_args()\n\n    images = [\n        ia.quokka_square(size=(128, 128)),\n        ia.imresize_single_image(data.astronaut(), (128, 128))\n    ]\n\n    keypoints = [\n        ia.KeypointsOnImage([\n                ia.Keypoint(x=50, y=40),\n                ia.Keypoint(x=70, y=38),\n                ia.Keypoint(x=62, y=52)\n            ],\n            shape=images[0].shape\n        ),\n        ia.KeypointsOnImage([\n                ia.Keypoint(x=55, y=32),\n                ia.Keypoint(x=42, y=95),\n                ia.Keypoint(x=75, y=89)\n            ],\n            shape=images[1].shape\n        )\n    ]\n\n    bounding_boxes = [\n        ia.BoundingBoxesOnImage([\n                ia.BoundingBox(x1=10, y1=10, x2=20, y2=20),\n                ia.BoundingBox(x1=40, y1=50, x2=70, y2=60)\n            ],\n            shape=images[0].shape\n        ),\n        ia.BoundingBoxesOnImage([\n                ia.BoundingBox(x1=10, y1=10, x2=20, y2=20),\n                ia.BoundingBox(x1=40, y1=50, x2=70, y2=60)\n            ],\n            shape=images[1].shape\n        )\n    ]\n\n    augmenters = [\n        iaa.Sequential([\n            iaa.CoarseDropout(p=0.5, size_percent=0.05),\n            iaa.AdditiveGaussianNoise(scale=0.1*255),\n            iaa.Crop(percent=0.1)\n        ], name=\"Sequential\"),\n        iaa.SomeOf(2, children=[\n            iaa.CoarseDropout(p=0.5, size_percent=0.05),\n            iaa.AdditiveGaussianNoise(scale=0.1*255),\n            iaa.Crop(percent=0.1)\n        ], name=\"SomeOf\"),\n        iaa.OneOf(children=[\n            iaa.CoarseDropout(p=0.5, size_percent=0.05),\n            iaa.AdditiveGaussianNoise(scale=0.1*255),\n            iaa.Crop(percent=0.1)\n        ], name=\"OneOf\"),\n        iaa.Sometimes(0.5, iaa.AdditiveGaussianNoise(scale=0.1*255), name=\"Sometimes\"),\n        iaa.WithColorspace(\"HSV\", children=[iaa.Add(20)], name=\"WithColorspace\"),\n        iaa.WithChannels([0], children=[iaa.Add(20)], name=\"WithChannels\"),\n        iaa.AddToHueAndSaturation((-20, 20), per_channel=True, name=\"AddToHueAndSaturation\"),\n        iaa.Identity(name=\"Identity\"),\n        iaa.Resize({\"width\": 64, \"height\": 64}, name=\"Resize\"),\n        iaa.CropAndPad(px=(-8, 8), name=\"CropAndPad-px\"),\n        iaa.Pad(px=(0, 8), name=\"Pad-px\"),\n        iaa.Crop(px=(0, 8), name=\"Crop-px\"),\n        iaa.Crop(percent=(0, 0.1), name=\"Crop-percent\"),\n        iaa.Fliplr(0.5, name=\"Fliplr\"),\n        iaa.Flipud(0.5, name=\"Flipud\"),\n        iaa.Superpixels(p_replace=0.75, n_segments=50, name=\"Superpixels\"),\n        iaa.Grayscale(0.5, name=\"Grayscale0.5\"),\n        iaa.Grayscale(1.0, name=\"Grayscale1.0\"),\n        iaa.GaussianBlur((0, 3.0), name=\"GaussianBlur\"),\n        iaa.AverageBlur(k=(3, 11), name=\"AverageBlur\"),\n        iaa.MedianBlur(k=(3, 11), name=\"MedianBlur\"),\n        iaa.BilateralBlur(d=10, name=\"BilateralBlur\"),\n        iaa.Sharpen(alpha=(0.1, 1.0), lightness=(0, 2.0), name=\"Sharpen\"),\n        iaa.Emboss(alpha=(0.1, 1.0), strength=(0, 2.0), name=\"Emboss\"),\n        iaa.EdgeDetect(alpha=(0.1, 1.0), name=\"EdgeDetect\"),\n        iaa.DirectedEdgeDetect(alpha=(0.1, 1.0), direction=(0, 1.0), name=\"DirectedEdgeDetect\"),\n        iaa.Add((-50, 50), name=\"Add\"),\n        iaa.Add((-50, 50), per_channel=True, name=\"AddPerChannel\"),\n        iaa.AddElementwise((-50, 50), name=\"AddElementwise\"),\n        iaa.AdditiveGaussianNoise(loc=0, scale=(0.0, 0.1*255), name=\"AdditiveGaussianNoise\"),\n        iaa.Multiply((0.5, 1.5), name=\"Multiply\"),\n        iaa.Multiply((0.5, 1.5), per_channel=True, name=\"MultiplyPerChannel\"),\n        iaa.MultiplyElementwise((0.5, 1.5), name=\"MultiplyElementwise\"),\n        iaa.Dropout((0.0, 0.1), name=\"Dropout\"),\n        iaa.CoarseDropout(p=0.05, size_percent=(0.05, 0.5), name=\"CoarseDropout\"),\n        iaa.Invert(p=0.5, name=\"Invert\"),\n        iaa.Invert(p=0.5, per_channel=True, name=\"InvertPerChannel\"),\n        iaa.ContrastNormalization(alpha=(0.5, 2.0), name=\"ContrastNormalization\"),\n        iaa.SaltAndPepper(p=0.05, name=\"SaltAndPepper\"),\n        iaa.Salt(p=0.05, name=\"Salt\"),\n        iaa.Pepper(p=0.05, name=\"Pepper\"),\n        iaa.CoarseSaltAndPepper(p=0.05, size_percent=(0.01, 0.1), name=\"CoarseSaltAndPepper\"),\n        iaa.CoarseSalt(p=0.05, size_percent=(0.01, 0.1), name=\"CoarseSalt\"),\n        iaa.CoarsePepper(p=0.05, size_percent=(0.01, 0.1), name=\"CoarsePepper\"),\n        iaa.Affine(\n            scale={\"x\": (0.8, 1.2), \"y\": (0.8, 1.2)},\n            translate_px={\"x\": (-16, 16), \"y\": (-16, 16)},\n            rotate=(-45, 45),\n            shear=(-16, 16),\n            order=ia.ALL,\n            cval=(0, 255),\n            mode=ia.ALL,\n            name=\"Affine\"\n        ),\n        iaa.PiecewiseAffine(scale=0.03, nb_rows=(2, 6), nb_cols=(2, 6), name=\"PiecewiseAffine\"),\n        iaa.PerspectiveTransform(scale=0.1, name=\"PerspectiveTransform\"),\n        iaa.ElasticTransformation(alpha=(0.5, 8.0), sigma=1.0, name=\"ElasticTransformation\"),\n        iaa.Alpha(\n            factor=(0.0, 1.0),\n            first=iaa.Add(100),\n            second=iaa.Dropout(0.5),\n            per_channel=False,\n            name=\"Alpha\"\n        ),\n        iaa.Alpha(\n            factor=(0.0, 1.0),\n            first=iaa.Add(100),\n            second=iaa.Dropout(0.5),\n            per_channel=True,\n            name=\"AlphaPerChannel\"\n        ),\n        iaa.Alpha(\n            factor=(0.0, 1.0),\n            first=iaa.Affine(rotate=(-45, 45)),\n            per_channel=True,\n            name=\"AlphaAffine\"\n        ),\n        iaa.AlphaElementwise(\n            factor=(0.0, 1.0),\n            first=iaa.Add(50),\n            second=iaa.ContrastNormalization(2.0),\n            per_channel=False,\n            name=\"AlphaElementwise\"\n        ),\n        iaa.AlphaElementwise(\n            factor=(0.0, 1.0),\n            first=iaa.Add(50),\n            second=iaa.ContrastNormalization(2.0),\n            per_channel=True,\n            name=\"AlphaElementwisePerChannel\"\n        ),\n        iaa.AlphaElementwise(\n            factor=(0.0, 1.0),\n            first=iaa.Affine(rotate=(-45, 45)),\n            per_channel=True,\n            name=\"AlphaElementwiseAffine\"\n        ),\n        iaa.SimplexNoiseAlpha(\n            first=iaa.EdgeDetect(1.0),\n            per_channel=False,\n            name=\"SimplexNoiseAlpha\"\n        ),\n        iaa.FrequencyNoiseAlpha(\n            first=iaa.EdgeDetect(1.0),\n            per_channel=False,\n            name=\"FrequencyNoiseAlpha\"\n        )\n    ]\n\n    augmenters.append(iaa.Sequential([iaa.Sometimes(0.2, aug.copy()) for aug in augmenters], name=\"Sequential\"))\n    augmenters.append(iaa.Sometimes(0.5, [aug.copy() for aug in augmenters], name=\"Sometimes\"))\n\n    for augmenter in augmenters:\n        if args.only is None or augmenter.name in [v.strip() for v in args.only.split(\",\")]:\n            print(\"Augmenter: %s\" % (augmenter.name,))\n            grid = []\n            for image, kps, bbs in zip(images, keypoints, bounding_boxes):\n                aug_det = augmenter.to_deterministic()\n                imgs_aug = aug_det.augment_images(np.tile(image[np.newaxis, ...], (16, 1, 1, 1)))\n                kps_aug = aug_det.augment_keypoints([kps] * 16)\n                bbs_aug = aug_det.augment_bounding_boxes([bbs] * 16)\n                imgs_aug_drawn = [kps_aug_one.draw_on_image(img_aug) for img_aug, kps_aug_one in zip(imgs_aug, kps_aug)]\n                imgs_aug_drawn = [bbs_aug_one.draw_on_image(img_aug) for img_aug, bbs_aug_one in zip(imgs_aug_drawn, bbs_aug)]\n                grid.append(np.hstack(imgs_aug_drawn))\n            ia.imshow(np.vstack(grid))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_voronoi.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\n\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    image = ia.quokka_square((256, 256))\n\n    reggrid_sampler = iaa.DropoutPointsSampler(\n        iaa.RegularGridPointsSampler(n_rows=50, n_cols=50),\n        0.5\n    )\n    uniform_sampler = iaa.UniformPointsSampler(50*50)\n\n    for p_replace in [1.0, 0.5, 0.1, 0.0]:\n        augs = [\n            iaa.Voronoi(points_sampler=reggrid_sampler, p_replace=p_replace,\n                        max_size=128),\n            iaa.Voronoi(points_sampler=uniform_sampler, p_replace=p_replace,\n                        max_size=128),\n            iaa.UniformVoronoi(50*50, p_replace=p_replace, max_size=128),\n            iaa.RegularGridVoronoi(50, 50, p_drop_points=0.4,\n                                   p_replace=p_replace, max_size=128),\n            iaa.RelativeRegularGridVoronoi(p_replace=p_replace, max_size=128)\n        ]\n\n        images = [aug(image=image) for aug in augs]\n\n        ia.imshow(np.hstack(images))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_with_hue_and_saturation.py",
    "content": "from __future__ import print_function, division\nimport imgaug as ia\nimport imgaug.augmenters as iaa\n\n\ndef main():\n    image = ia.quokka_square(size=(128, 128))\n    images = []\n\n    for i in range(15):\n        aug = iaa.WithHueAndSaturation(iaa.WithChannels(0, iaa.Add(i*20)))\n        images.append(aug.augment_image(image))\n\n    for i in range(15):\n        aug = iaa.WithHueAndSaturation(iaa.WithChannels(1, iaa.Add(i*20)))\n        images.append(aug.augment_image(image))\n\n    ia.imshow(ia.draw_grid(images, rows=2))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_withchannels.py",
    "content": "from __future__ import print_function, division\n\nimport cv2\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\nTIME_PER_STEP = 10000\n\n\ndef main():\n    image = data.astronaut()\n    print(\"image shape:\", image.shape)\n    print(\"Press ENTER or wait %d ms to proceed to the next image.\" % (TIME_PER_STEP,))\n\n    children_all = [\n        (\"hflip\", iaa.Fliplr(1)),\n        (\"add\", iaa.Add(50)),\n        (\"dropout\", iaa.Dropout(0.2)),\n        (\"affine\", iaa.Affine(rotate=35))\n    ]\n\n    channels_all = [\n        None,\n        0,\n        [],\n        [0],\n        [0, 1],\n        [1, 2],\n        [0, 1, 2]\n    ]\n\n    cv2.namedWindow(\"aug\", cv2.WINDOW_NORMAL)\n    cv2.imshow(\"aug\", image[..., ::-1])\n    cv2.waitKey(TIME_PER_STEP)\n\n    for children_title, children in children_all:\n        for channels in channels_all:\n            aug = iaa.WithChannels(channels=channels, children=children)\n            img_aug = aug.augment_image(image)\n            print(\"dtype\", img_aug.dtype, \"averages\", np.average(img_aug, axis=tuple(range(0, img_aug.ndim-1))))\n\n            title = \"children=%s | channels=%s\" % (children_title, channels)\n            img_aug = ia.draw_text(img_aug, x=5, y=5, text=title)\n\n            cv2.imshow(\"aug\", img_aug[..., ::-1])  # here with rgb2bgr\n            cv2.waitKey(TIME_PER_STEP)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "checks/check_withcolorspace.py",
    "content": "from __future__ import print_function, division\n\nimport numpy as np\nfrom skimage import data\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n\n\ndef main():\n    image = data.astronaut()\n    print(\"image shape:\", image.shape)\n\n    aug = iaa.WithColorspace(\n        from_colorspace=\"RGB\",\n        to_colorspace=\"HSV\",\n        children=iaa.WithChannels(0, iaa.Add(50))\n    )\n\n    aug_no_colorspace = iaa.WithChannels(0, iaa.Add(50))\n\n    img_show = np.hstack([\n        image,\n        aug.augment_image(image),\n        aug_no_colorspace.augment_image(image)\n    ])\n    ia.imshow(img_show)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "codecov.yml",
    "content": "#see https://github.com/codecov/support/wiki/Codecov-Yaml\ncodecov:\n  notify:\n    require_ci_to_pass: yes\n\ncoverage:\n  precision: 2  # 2 = xx.xx%, 0 = xx%\n  round: nearest # how coverage is rounded: down/up/nearest\n  range: 10...90 # custom range of coverage colors from red -> yellow -> green\n  status:\n    # https://codecov.readme.io/v1.0/docs/commit-status\n    project:\n      default:\n        against: auto\n        target: 40% # specify the target coverage for each commit status\n        threshold: 20% # allow this little decrease on project\n        # https://github.com/codecov/support/wiki/Filtering-Branches\n        # branches: master\n        if_ci_failed: error\n    # https://github.com/codecov/support/wiki/Patch-Status\n    patch:\n      default:\n        against: parent\n        target: 30% # specify the target \"X%\" coverage to hit\n        # threshold: 50% # allow this much decrease on patch\n    changes: false\n\nparsers:\n  gcov:\n    branch_detection:\n      conditional: true\n      loop: true\n      macro: false\n      method: false\n  javascript:\n    enable_partials: false\n\ncomment:\n  layout: header, diff\n  require_changes: false\n  behavior: default  # update if exists else create new\n  branches: *\n\nignore:\n  imgaug/external/*.py\n"
  },
  {
    "path": "imgaug/__init__.py",
    "content": "\"\"\"Imports for package imgaug.\"\"\"\nfrom __future__ import absolute_import\n\n# this contains some deprecated classes/functions pointing to the new\n# classes/functions, hence always place the other imports below this so that\n# the deprecated stuff gets overwritten as much as possible\nfrom imgaug.imgaug import *  # pylint: disable=redefined-builtin\n\nimport imgaug.augmentables as augmentables\nfrom imgaug.augmentables import *\nimport imgaug.augmenters as augmenters\nimport imgaug.parameters as parameters\nimport imgaug.dtypes as dtypes\nimport imgaug.data as data\n\n__version__ = '0.4.0'\n"
  },
  {
    "path": "imgaug/augmentables/__init__.py",
    "content": "\"\"\"Combination of all augmentable classes and related functions.\"\"\"\nfrom __future__ import absolute_import\nfrom imgaug.augmentables.kps import *\nfrom imgaug.augmentables.bbs import *\nfrom imgaug.augmentables.polys import *\nfrom imgaug.augmentables.lines import *\nfrom imgaug.augmentables.heatmaps import *\nfrom imgaug.augmentables.segmaps import *\nfrom imgaug.augmentables.batches import *\n"
  },
  {
    "path": "imgaug/augmentables/base.py",
    "content": "\"\"\"Interfaces used by augmentable objects.\n\nAdded in 0.4.0.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\n\nclass IAugmentable(object):\n    \"\"\"Interface of augmentable objects.\n\n    This interface is right now only used to \"mark\" augmentable objects.\n    It does not enforce any methods yet (but will probably in the future).\n\n    Currently, only ``*OnImage`` clases are marked as augmentable.\n    Non-OnImage objects are normalized to OnImage-objects.\n    Batches are not yet marked as augmentable, but might be in the future.\n\n    Added in 0.4.0.\n\n    \"\"\"\n"
  },
  {
    "path": "imgaug/augmentables/batches.py",
    "content": "\"\"\"Classes representing batches of normalized or unnormalized data.\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport collections\n\nimport numpy as np\n\nfrom .. import imgaug as ia\nfrom . import normalization as nlib\nfrom . import utils\n\nDEFAULT = \"DEFAULT\"\n\n_AUGMENTABLE_NAMES = [\n    \"images\", \"heatmaps\", \"segmentation_maps\", \"keypoints\",\n    \"bounding_boxes\", \"polygons\", \"line_strings\"]\n\n_AugmentableColumn = collections.namedtuple(\n    \"_AugmentableColumn\",\n    [\"name\", \"value\", \"attr_name\"])\n\n\ndef _get_column_names(batch, postfix):\n    return [column.name\n            for column\n            in _get_columns(batch, postfix)]\n\n\ndef _get_columns(batch, postfix):\n    result = []\n    for name in _AUGMENTABLE_NAMES:\n        attr_name = name + postfix\n        value = getattr(batch, name + postfix)\n        # Every data item is either an array or a list. If there are no\n        # items in the array/list, there are also no shapes to change\n        # as shape-changes are imagewise. Hence, we can afford to check\n        # len() here.\n        if value is not None and len(value) > 0:\n            result.append(_AugmentableColumn(name, value, attr_name))\n    return result\n\n\n# TODO also support (H,W,C) for heatmaps of len(images) == 1\n# TODO also support (H,W) for segmaps of len(images) == 1\nclass UnnormalizedBatch(object):\n    \"\"\"\n    Class for batches of unnormalized data before and after augmentation.\n\n    Parameters\n    ----------\n    images : None or (N,H,W,C) ndarray or (N,H,W) ndarray or iterable of (H,W,C) ndarray or iterable of (H,W) ndarray\n        The images to augment.\n\n    heatmaps : None or (N,H,W,C) ndarray or imgaug.augmentables.heatmaps.HeatmapsOnImage or iterable of (H,W,C) ndarray or iterable of imgaug.augmentables.heatmaps.HeatmapsOnImage\n        The heatmaps to augment.\n        If anything else than ``HeatmapsOnImage``, then the number of heatmaps\n        must match the number of images provided via parameter `images`.\n        The number is contained either in ``N`` or the first iterable's size.\n\n    segmentation_maps : None or (N,H,W) ndarray or imgaug.augmentables.segmaps.SegmentationMapsOnImage or iterable of (H,W) ndarray or iterable of imgaug.augmentables.segmaps.SegmentationMapsOnImage\n        The segmentation maps to augment.\n        If anything else than ``SegmentationMapsOnImage``, then the number of\n        segmaps must match the number of images provided via parameter\n        `images`. The number is contained either in ``N`` or the first\n        iterable's size.\n\n    keypoints : None or list of (N,K,2) ndarray or tuple of number or imgaug.augmentables.kps.Keypoint or iterable of (K,2) ndarray or iterable of tuple of number or iterable of imgaug.augmentables.kps.Keypoint or iterable of imgaug.augmentables.kps.KeypointOnImage or iterable of iterable of tuple of number or iterable of iterable of imgaug.augmentables.kps.Keypoint\n        The keypoints to augment.\n        If a tuple (or iterable(s) of tuple), then iterpreted as (x,y)\n        coordinates and must hence contain two numbers.\n        A single tuple represents a single coordinate on one image, an\n        iterable of tuples the coordinates on one image and an iterable of\n        iterable of tuples the coordinates on several images. Analogous if\n        ``Keypoint`` objects are used instead of tuples.\n        If an ndarray, then ``N`` denotes the number of images and ``K`` the\n        number of keypoints on each image.\n        If anything else than ``KeypointsOnImage`` is provided, then the\n        number of keypoint groups must match the number of images provided\n        via parameter `images`. The number is contained e.g. in ``N`` or\n        in case of \"iterable of iterable of tuples\" in the first iterable's\n        size.\n\n    bounding_boxes : None or (N,B,4) ndarray or tuple of number or imgaug.augmentables.bbs.BoundingBox or imgaug.augmentables.bbs.BoundingBoxesOnImage or iterable of (B,4) ndarray or iterable of tuple of number or iterable of imgaug.augmentables.bbs.BoundingBox or iterable of imgaug.augmentables.bbs.BoundingBoxesOnImage or iterable of iterable of tuple of number or iterable of iterable imgaug.augmentables.bbs.BoundingBox\n        The bounding boxes to augment.\n        This is analogous to the `keypoints` parameter. However, each\n        tuple -- and also the last index in case of arrays -- has size 4,\n        denoting the bounding box coordinates ``x1``, ``y1``, ``x2`` and ``y2``.\n\n    polygons : None  or (N,#polys,#points,2) ndarray or imgaug.augmentables.polys.Polygon or imgaug.augmentables.polys.PolygonsOnImage or iterable of (#polys,#points,2) ndarray or iterable of tuple of number or iterable of imgaug.augmentables.kps.Keypoint or iterable of imgaug.augmentables.polys.Polygon or iterable of imgaug.augmentables.polys.PolygonsOnImage or iterable of iterable of (#points,2) ndarray or iterable of iterable of tuple of number or iterable of iterable of imgaug.augmentables.kps.Keypoint or iterable of iterable of imgaug.augmentables.polys.Polygon or iterable of iterable of iterable of tuple of number or iterable of iterable of iterable of tuple of imgaug.augmentables.kps.Keypoint\n        The polygons to augment.\n        This is similar to the `keypoints` parameter. However, each polygon\n        may be made up of several ``(x,y)`` coordinates (three or more are\n        required for valid polygons).\n        The following datatypes will be interpreted as a single polygon on a\n        single image:\n\n          * ``imgaug.augmentables.polys.Polygon``\n          * ``iterable of tuple of number``\n          * ``iterable of imgaug.augmentables.kps.Keypoint``\n\n        The following datatypes will be interpreted as multiple polygons on a\n        single image:\n\n          * ``imgaug.augmentables.polys.PolygonsOnImage``\n          * ``iterable of imgaug.augmentables.polys.Polygon``\n          * ``iterable of iterable of tuple of number``\n          * ``iterable of iterable of imgaug.augmentables.kps.Keypoint``\n          * ``iterable of iterable of imgaug.augmentables.polys.Polygon``\n\n        The following datatypes will be interpreted as multiple polygons on\n        multiple images:\n\n          * ``(N,#polys,#points,2) ndarray``\n          * ``iterable of (#polys,#points,2) ndarray``\n          * ``iterable of iterable of (#points,2) ndarray``\n          * ``iterable of iterable of iterable of tuple of number``\n          * ``iterable of iterable of iterable of tuple of imgaug.augmentables.kps.Keypoint``\n\n    line_strings : None or (N,#lines,#points,2) ndarray or imgaug.augmentables.lines.LineString or imgaug.augmentables.lines.LineStringOnImage or iterable of (#lines,#points,2) ndarray or iterable of tuple of number or iterable of imgaug.augmentables.kps.Keypoint or iterable of imgaug.augmentables.lines.LineString or iterable of imgaug.augmentables.lines.LineStringOnImage or iterable of iterable of (#points,2) ndarray or iterable of iterable of tuple of number or iterable of iterable of imgaug.augmentables.kps.Keypoint or iterable of iterable of imgaug.augmentables.polys.LineString or iterable of iterable of iterable of tuple of number or iterable of iterable of iterable of tuple of imgaug.augmentables.kps.Keypoint\n        The line strings to augment.\n        See `polygons` for more details as polygons follow a similar\n        structure to line strings.\n\n    data\n        Additional data that is saved in the batch and may be read out\n        after augmentation. This could e.g. contain filepaths to each image\n        in `images`. As this object is usually used for background\n        augmentation with multiple processes, the augmented Batch objects might\n        not be returned in the original order, making this information useful.\n\n    \"\"\"\n\n    def __init__(self, images=None, heatmaps=None, segmentation_maps=None,\n                 keypoints=None, bounding_boxes=None, polygons=None,\n                 line_strings=None, data=None):\n        \"\"\"Construct a new :class:`UnnormalizedBatch` instance.\"\"\"\n        self.images_unaug = images\n        self.images_aug = None\n        self.heatmaps_unaug = heatmaps\n        self.heatmaps_aug = None\n        self.segmentation_maps_unaug = segmentation_maps\n        self.segmentation_maps_aug = None\n        self.keypoints_unaug = keypoints\n        self.keypoints_aug = None\n        self.bounding_boxes_unaug = bounding_boxes\n        self.bounding_boxes_aug = None\n        self.polygons_unaug = polygons\n        self.polygons_aug = None\n        self.line_strings_unaug = line_strings\n        self.line_strings_aug = None\n        self.data = data\n\n    def get_column_names(self):\n        \"\"\"Get the names of types of augmentables that contain data.\n\n        This method is intended for situations where one wants to know which\n        data is contained in the batch that has to be augmented, visualized\n        or something similar.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of str\n            Names of types of augmentables. E.g. ``[\"images\", \"polygons\"]``.\n\n        \"\"\"\n        return _get_column_names(self, \"_unaug\")\n\n    def to_normalized_batch(self):\n        \"\"\"Convert this unnormalized batch to an instance of Batch.\n\n        As this method is intended to be called before augmentation, it\n        assumes that none of the ``*_aug`` attributes is yet set.\n        It will produce an AssertionError otherwise.\n\n        The newly created Batch's ``*_unaug`` attributes will match the ones\n        in this batch, just in normalized form.\n\n        Returns\n        -------\n        imgaug.augmentables.batches.Batch\n            The batch, with ``*_unaug`` attributes being normalized.\n\n        \"\"\"\n        contains_no_augmented_data_yet = all([\n            attr is None\n            for attr_name, attr\n            in self.__dict__.items()\n            if attr_name.endswith(\"_aug\")])\n        assert contains_no_augmented_data_yet, (\n            \"Expected UnnormalizedBatch to not contain any augmented data \"\n            \"before normalization, but at least one '*_aug' attribute was \"\n            \"already set.\")\n\n        images_unaug = nlib.normalize_images(self.images_unaug)\n        shapes = None\n        if images_unaug is not None:\n            shapes = [image.shape for image in images_unaug]\n\n        return Batch(\n            images=images_unaug,\n            heatmaps=nlib.normalize_heatmaps(\n                self.heatmaps_unaug, shapes),\n            segmentation_maps=nlib.normalize_segmentation_maps(\n                self.segmentation_maps_unaug, shapes),\n            keypoints=nlib.normalize_keypoints(\n                self.keypoints_unaug, shapes),\n            bounding_boxes=nlib.normalize_bounding_boxes(\n                self.bounding_boxes_unaug, shapes),\n            polygons=nlib.normalize_polygons(\n                self.polygons_unaug, shapes),\n            line_strings=nlib.normalize_line_strings(\n                self.line_strings_unaug, shapes),\n            data=self.data\n        )\n\n    def fill_from_augmented_normalized_batch_(self, batch_aug_norm):\n        \"\"\"\n        Fill this batch with (normalized) augmentation results in-place.\n\n        This method receives a (normalized) Batch instance, takes all\n        ``*_aug`` attributes out if it and assigns them to this\n        batch *in unnormalized form*. Hence, the datatypes of all ``*_aug``\n        attributes will match the datatypes of the ``*_unaug`` attributes.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        batch_aug_norm: imgaug.augmentables.batches.Batch\n            Batch after normalization and augmentation.\n\n        Returns\n        -------\n        imgaug.augmentables.batches.UnnormalizedBatch\n            This instance itself.\n            All ``*_unaug`` attributes are unchanged.\n            All ``*_aug`` attributes are taken from `batch_normalized`,\n            converted to unnormalized form.\n\n        \"\"\"\n        self.images_aug = nlib.invert_normalize_images(\n            batch_aug_norm.images_aug, self.images_unaug)\n        self.heatmaps_aug = nlib.invert_normalize_heatmaps(\n            batch_aug_norm.heatmaps_aug, self.heatmaps_unaug)\n        self.segmentation_maps_aug = nlib.invert_normalize_segmentation_maps(\n            batch_aug_norm.segmentation_maps_aug, self.segmentation_maps_unaug)\n        self.keypoints_aug = nlib.invert_normalize_keypoints(\n            batch_aug_norm.keypoints_aug, self.keypoints_unaug)\n        self.bounding_boxes_aug = nlib.invert_normalize_bounding_boxes(\n            batch_aug_norm.bounding_boxes_aug, self.bounding_boxes_unaug)\n        self.polygons_aug = nlib.invert_normalize_polygons(\n            batch_aug_norm.polygons_aug, self.polygons_unaug)\n        self.line_strings_aug = nlib.invert_normalize_line_strings(\n            batch_aug_norm.line_strings_aug, self.line_strings_unaug)\n        return self\n\n    def fill_from_augmented_normalized_batch(self, batch_aug_norm):\n        \"\"\"\n        Fill this batch with (normalized) augmentation results.\n\n        This method receives a (normalized) Batch instance, takes all\n        ``*_aug`` attributes out if it and assigns them to this\n        batch *in unnormalized form*. Hence, the datatypes of all ``*_aug``\n        attributes will match the datatypes of the ``*_unaug`` attributes.\n\n        Parameters\n        ----------\n        batch_aug_norm: imgaug.augmentables.batches.Batch\n            Batch after normalization and augmentation.\n\n        Returns\n        -------\n        imgaug.augmentables.batches.UnnormalizedBatch\n            New UnnormalizedBatch instance. All ``*_unaug`` attributes are\n            taken from the old UnnormalizedBatch (without deepcopying them)\n            and all ``*_aug`` attributes are taken from `batch_normalized`,\n            converted to unnormalized form.\n\n        \"\"\"\n        # we take here the .data from the normalized batch instead of from\n        # self for the rare case where one has decided to somehow change it\n        # during augmentation\n        batch = UnnormalizedBatch(\n            images=self.images_unaug,\n            heatmaps=self.heatmaps_unaug,\n            segmentation_maps=self.segmentation_maps_unaug,\n            keypoints=self.keypoints_unaug,\n            bounding_boxes=self.bounding_boxes_unaug,\n            polygons=self.polygons_unaug,\n            line_strings=self.line_strings_unaug,\n            data=batch_aug_norm.data\n        )\n\n        batch.images_aug = nlib.invert_normalize_images(\n            batch_aug_norm.images_aug, self.images_unaug)\n        batch.heatmaps_aug = nlib.invert_normalize_heatmaps(\n            batch_aug_norm.heatmaps_aug, self.heatmaps_unaug)\n        batch.segmentation_maps_aug = nlib.invert_normalize_segmentation_maps(\n            batch_aug_norm.segmentation_maps_aug, self.segmentation_maps_unaug)\n        batch.keypoints_aug = nlib.invert_normalize_keypoints(\n            batch_aug_norm.keypoints_aug, self.keypoints_unaug)\n        batch.bounding_boxes_aug = nlib.invert_normalize_bounding_boxes(\n            batch_aug_norm.bounding_boxes_aug, self.bounding_boxes_unaug)\n        batch.polygons_aug = nlib.invert_normalize_polygons(\n            batch_aug_norm.polygons_aug, self.polygons_unaug)\n        batch.line_strings_aug = nlib.invert_normalize_line_strings(\n            batch_aug_norm.line_strings_aug, self.line_strings_unaug)\n\n        return batch\n\n\nclass Batch(object):\n    \"\"\"\n    Class encapsulating a batch before and after augmentation.\n\n    Parameters\n    ----------\n    images : None or (N,H,W,C) ndarray or list of (H,W,C) ndarray\n        The images to augment.\n\n    heatmaps : None or list of imgaug.augmentables.heatmaps.HeatmapsOnImage\n        The heatmaps to augment.\n\n    segmentation_maps : None or list of imgaug.augmentables.segmaps.SegmentationMapsOnImage\n        The segmentation maps to augment.\n\n    keypoints : None or list of imgaug.augmentables.kps.KeypointOnImage\n        The keypoints to augment.\n\n    bounding_boxes : None or list of imgaug.augmentables.bbs.BoundingBoxesOnImage\n        The bounding boxes to augment.\n\n    polygons : None or list of imgaug.augmentables.polys.PolygonsOnImage\n        The polygons to augment.\n\n    line_strings : None or list of imgaug.augmentables.lines.LineStringsOnImage\n        The line strings to augment.\n\n    data\n        Additional data that is saved in the batch and may be read out\n        after augmentation. This could e.g. contain filepaths to each image\n        in `images`. As this object is usually used for background\n        augmentation with multiple processes, the augmented Batch objects might\n        not be returned in the original order, making this information useful.\n\n    \"\"\"\n\n    def __init__(self, images=None, heatmaps=None, segmentation_maps=None,\n                 keypoints=None, bounding_boxes=None, polygons=None,\n                 line_strings=None, data=None):\n        \"\"\"Construct a new :class:`Batch` instance.\"\"\"\n        self.images_unaug = images\n        self.images_aug = None\n        self.heatmaps_unaug = heatmaps\n        self.heatmaps_aug = None\n        self.segmentation_maps_unaug = segmentation_maps\n        self.segmentation_maps_aug = None\n        self.keypoints_unaug = keypoints\n        self.keypoints_aug = None\n        self.bounding_boxes_unaug = bounding_boxes\n        self.bounding_boxes_aug = None\n        self.polygons_unaug = polygons\n        self.polygons_aug = None\n        self.line_strings_unaug = line_strings\n        self.line_strings_aug = None\n        self.data = data\n\n    @property\n    @ia.deprecated(\"Batch.images_unaug\")\n    def images(self):\n        \"\"\"Get unaugmented images.\"\"\"\n        return self.images_unaug\n\n    @property\n    @ia.deprecated(\"Batch.heatmaps_unaug\")\n    def heatmaps(self):\n        \"\"\"Get unaugmented heatmaps.\"\"\"\n        return self.heatmaps_unaug\n\n    @property\n    @ia.deprecated(\"Batch.segmentation_maps_unaug\")\n    def segmentation_maps(self):\n        \"\"\"Get unaugmented segmentation maps.\"\"\"\n        return self.segmentation_maps_unaug\n\n    @property\n    @ia.deprecated(\"Batch.keypoints_unaug\")\n    def keypoints(self):\n        \"\"\"Get unaugmented keypoints.\"\"\"\n        return self.keypoints_unaug\n\n    @property\n    @ia.deprecated(\"Batch.bounding_boxes_unaug\")\n    def bounding_boxes(self):\n        \"\"\"Get unaugmented bounding boxes.\"\"\"\n        return self.bounding_boxes_unaug\n\n    def get_column_names(self):\n        \"\"\"Get the names of types of augmentables that contain data.\n\n        This method is intended for situations where one wants to know which\n        data is contained in the batch that has to be augmented, visualized\n        or something similar.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of str\n            Names of types of augmentables. E.g. ``[\"images\", \"polygons\"]``.\n\n        \"\"\"\n        return _get_column_names(self, \"_unaug\")\n\n    def to_normalized_batch(self):\n        \"\"\"Return this batch.\n\n        This method does nothing and only exists to simplify interfaces\n        that accept both :class:`UnnormalizedBatch` and :class:`Batch`.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.batches.Batch\n            This batch (not copied).\n\n        \"\"\"\n        return self\n\n    def to_batch_in_augmentation(self):\n        \"\"\"Convert this batch to a :class:`_BatchInAugmentation` instance.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.batches._BatchInAugmentation\n            The converted batch.\n\n        \"\"\"\n        def _copy(var):\n            # TODO first check here if _aug is set and if it is then use that?\n            if var is not None:\n                return utils.copy_augmentables(var)\n            return var\n\n        return _BatchInAugmentation(\n            images=_copy(self.images_unaug),\n            heatmaps=_copy(self.heatmaps_unaug),\n            segmentation_maps=_copy(self.segmentation_maps_unaug),\n            keypoints=_copy(self.keypoints_unaug),\n            bounding_boxes=_copy(self.bounding_boxes_unaug),\n            polygons=_copy(self.polygons_unaug),\n            line_strings=_copy(self.line_strings_unaug)\n        )\n\n    def fill_from_batch_in_augmentation_(self, batch_in_augmentation):\n        \"\"\"Set the columns in this batch to the column values of another batch.\n\n        This method works in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        batch_in_augmentation : _BatchInAugmentation\n            Batch of which to use the column values.\n            The values are *not* copied. Only their references are used.\n\n        Returns\n        -------\n        Batch\n            The updated batch. (Modified in-place.)\n\n        \"\"\"\n        self.images_aug = batch_in_augmentation.images\n        self.heatmaps_aug = batch_in_augmentation.heatmaps\n        self.segmentation_maps_aug = batch_in_augmentation.segmentation_maps\n        self.keypoints_aug = batch_in_augmentation.keypoints\n        self.bounding_boxes_aug = batch_in_augmentation.bounding_boxes\n        self.polygons_aug = batch_in_augmentation.polygons\n        self.line_strings_aug = batch_in_augmentation.line_strings\n        return self\n\n    def deepcopy(self,\n                 images_unaug=DEFAULT,\n                 images_aug=DEFAULT,\n                 heatmaps_unaug=DEFAULT,\n                 heatmaps_aug=DEFAULT,\n                 segmentation_maps_unaug=DEFAULT,\n                 segmentation_maps_aug=DEFAULT,\n                 keypoints_unaug=DEFAULT,\n                 keypoints_aug=DEFAULT,\n                 bounding_boxes_unaug=DEFAULT,\n                 bounding_boxes_aug=DEFAULT,\n                 polygons_unaug=DEFAULT,\n                 polygons_aug=DEFAULT,\n                 line_strings_unaug=DEFAULT,\n                 line_strings_aug=DEFAULT):\n        \"\"\"Copy this batch and all of its column values.\n\n        Parameters\n        ----------\n        images_unaug : imgaug.augmentables.batches.DEFAULT or None or (N,H,W,C) ndarray or list of (H,W,C) ndarray\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        images_aug : imgaug.augmentables.batches.DEFAULT or None or (N,H,W,C) ndarray or list of (H,W,C) ndarray\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        heatmaps_unaug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        heatmaps_aug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        segmentation_maps_unaug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        segmentation_maps_aug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        keypoints_unaug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.kps.KeypointOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        keypoints_aug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.kps.KeypointOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        bounding_boxes_unaug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        bounding_boxes_aug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        polygons_unaug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.polys.PolygonsOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        polygons_aug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.polys.PolygonsOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        line_strings_unaug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.lines.LineStringsOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        line_strings_aug : imgaug.augmentables.batches.DEFAULT or None or list of imgaug.augmentables.lines.LineStringsOnImage\n            Copies the current attribute value without changes if set to\n            ``imgaug.augmentables.batches.DEFAULT``.\n            Otherwise same as in :func:`Batch.__init__`.\n\n        Returns\n        -------\n        Batch\n            Deep copy of the batch, optionally with new attributes.\n\n        \"\"\"\n        def _copy_optional(self_attr, arg):\n            return utils.deepcopy_fast(arg if arg is not DEFAULT else self_attr)\n\n        batch = Batch(\n            images=_copy_optional(self.images_unaug, images_unaug),\n            heatmaps=_copy_optional(self.heatmaps_unaug, heatmaps_unaug),\n            segmentation_maps=_copy_optional(self.segmentation_maps_unaug,\n                                             segmentation_maps_unaug),\n            keypoints=_copy_optional(self.keypoints_unaug, keypoints_unaug),\n            bounding_boxes=_copy_optional(self.bounding_boxes_unaug,\n                                          bounding_boxes_unaug),\n            polygons=_copy_optional(self.polygons_unaug, polygons_unaug),\n            line_strings=_copy_optional(self.line_strings_unaug,\n                                        line_strings_unaug),\n            data=utils.deepcopy_fast(self.data)\n        )\n        batch.images_aug = _copy_optional(self.images_aug, images_aug)\n        batch.heatmaps_aug = _copy_optional(self.heatmaps_aug, heatmaps_aug)\n        batch.segmentation_maps_aug = _copy_optional(self.segmentation_maps_aug,\n                                                     segmentation_maps_aug)\n        batch.keypoints_aug = _copy_optional(self.keypoints_aug, keypoints_aug)\n        batch.bounding_boxes_aug = _copy_optional(self.bounding_boxes_aug,\n                                                  bounding_boxes_aug)\n        batch.polygons_aug = _copy_optional(self.polygons_aug, polygons_aug)\n        batch.line_strings_aug = _copy_optional(self.line_strings_aug,\n                                                line_strings_aug)\n\n        return batch\n\n\n# Added in 0.4.0.\nclass _BatchInAugmentationPropagationContext(object):\n    def __init__(self, batch, augmenter, hooks, parents):\n        self.batch = batch\n        self.augmenter = augmenter\n        self.hooks = hooks\n        self.parents = parents\n        self.noned_info = None\n\n    def __enter__(self):\n        if self.hooks is not None:\n            self.noned_info = self.batch.apply_propagation_hooks_(\n                self.augmenter, self.hooks, self.parents)\n        return self.batch\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        if self.noned_info is not None:\n            self.batch = \\\n                self.batch.invert_apply_propagation_hooks_(self.noned_info)\n\n\nclass _BatchInAugmentation(object):\n    \"\"\"\n    Class encapsulating a batch during the augmentation process.\n\n    Data within the batch is already verified and normalized, similar to\n    :class:`Batch`. Data within the batch may be changed in-place. No initial\n    copy is needed.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    images : None or (N,H,W,C) ndarray or list of (H,W,C) ndarray\n        The images to augment.\n\n    heatmaps : None or list of imgaug.augmentables.heatmaps.HeatmapsOnImage\n        The heatmaps to augment.\n\n    segmentation_maps : None or list of imgaug.augmentables.segmaps.SegmentationMapsOnImage\n        The segmentation maps to augment.\n\n    keypoints : None or list of imgaug.augmentables.kps.KeypointOnImage\n        The keypoints to augment.\n\n    bounding_boxes : None or list of imgaug.augmentables.bbs.BoundingBoxesOnImage\n        The bounding boxes to augment.\n\n    polygons : None or list of imgaug.augmentables.polys.PolygonsOnImage\n        The polygons to augment.\n\n    line_strings : None or list of imgaug.augmentables.lines.LineStringsOnImage\n        The line strings to augment.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, images=None, heatmaps=None, segmentation_maps=None,\n                 keypoints=None, bounding_boxes=None, polygons=None,\n                 line_strings=None, data=None):\n        \"\"\"Create a new :class:`_BatchInAugmentation` instance.\"\"\"\n        self.images = images\n        self.heatmaps = heatmaps\n        self.segmentation_maps = segmentation_maps\n        self.keypoints = keypoints\n        self.bounding_boxes = bounding_boxes\n        self.polygons = polygons\n        self.line_strings = line_strings\n        self.data = data\n\n    @property\n    def empty(self):\n        \"\"\"Estimate whether this batch is empty, i.e. contains no data.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        bool\n            ``True`` if the batch contains no data to augment.\n            ``False`` otherwise.\n\n        \"\"\"\n        return self.nb_rows == 0\n\n    @property\n    def nb_rows(self):\n        \"\"\"Get the number of rows (i.e. examples) in this batch.\n\n        Note that this method assumes that all columns have the same number\n        of rows.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        int\n            Number of rows or ``0`` if there is no data in the batch.\n\n        \"\"\"\n        for augm_name in _AUGMENTABLE_NAMES:\n            value = getattr(self, augm_name)\n            if value is not None:\n                return len(value)\n        return 0\n\n    @property\n    def columns(self):\n        \"\"\"Get the columns of data to augment.\n\n        Each column represents one datatype and its corresponding data,\n        e.g. images or polygons.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of _AugmentableColumn\n            The columns to augment within this batch.\n\n        \"\"\"\n        return _get_columns(self, \"\")\n\n    def get_column_names(self):\n        \"\"\"Get the names of types of augmentables that contain data.\n\n        This method is intended for situations where one wants to know which\n        data is contained in the batch that has to be augmented, visualized\n        or something similar.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of str\n            Names of types of augmentables. E.g. ``[\"images\", \"polygons\"]``.\n\n        \"\"\"\n        return _get_column_names(self, \"\")\n\n    def get_rowwise_shapes(self):\n        \"\"\"Get the shape of each row within this batch.\n\n        Each row denotes the data of different types (e.g. image array,\n        polygons) corresponding to a single example in the batch.\n\n        This method assumes that all ``.shape`` attributes contain the same\n        shape and that it is identical to the image's shape.\n        It also assumes that there are no columns containing only ``None`` s.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of tuple of int\n            The shapes of each row.\n\n        \"\"\"\n        nb_rows = self.nb_rows\n        columns = self.columns\n        shapes = [None] * nb_rows\n        found = np.zeros((nb_rows,), dtype=bool)\n        for column in columns:\n            if column.name == \"images\" and ia.is_np_array(column.value):\n                shapes = [column.value.shape[1:]] * nb_rows\n            else:\n                for i, item in enumerate(column.value):\n                    if item is not None:\n                        shapes[i] = item.shape\n                        found[i] = True\n            if np.all(found):\n                return shapes\n        return shapes\n\n    def subselect_rows_by_indices(self, indices):\n        \"\"\"Reduce this batch to a subset of rows based on their row indices.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        indices : iterable of int\n            Row indices to select.\n\n        Returns\n        -------\n        _BatchInAugmentation\n            Batch containing only a subselection of rows.\n\n        \"\"\"\n        kwargs = {\"data\": self.data}\n        for augm_name in _AUGMENTABLE_NAMES:\n            rows = getattr(self, augm_name)\n            if rows is not None:\n                if augm_name == \"images\" and ia.is_np_array(rows):\n                    rows = rows[indices]  # pylint: disable=unsubscriptable-object\n                else:\n                    rows = [rows[index] for index in indices]\n\n                if len(rows) == 0:\n                    rows = None\n            kwargs[augm_name] = rows\n\n        return _BatchInAugmentation(**kwargs)\n\n    def invert_subselect_rows_by_indices_(self, indices, batch_subselected):\n        \"\"\"Reverse the subselection of rows in-place.\n\n        This is the inverse of\n        :func:`_BatchInAugmentation.subselect_rows_by_indices`.\n\n        This method has to be executed on the batch *before* subselection.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        indices : iterable of int\n            Row indices that were selected. (This is the input to\n\n        batch_subselected : _BatchInAugmentation\n            The batch after\n            :func:`_BatchInAugmentation.subselect_rows_by_indices` was called.\n\n        Returns\n        -------\n        _BatchInAugmentation\n            The updated batch. (Modified in-place.)\n\n        Examples\n        --------\n        >>> import numpy as np\n        >>> from imgaug.augmentables.batches import _BatchInAugmentation\n        >>> images = np.zeros((2, 10, 20, 3), dtype=np.uint8)\n        >>> batch = _BatchInAugmentation(images=images)\n        >>> batch_sub = batch.subselect_rows_by_indices([0])\n        >>> batch_sub.images += 1\n        >>> batch = batch.invert_subselect_rows_by_indices_([0], batch_sub)\n\n        \"\"\"\n        for augm_name in _AUGMENTABLE_NAMES:\n            column = getattr(self, augm_name)\n            if column is not None:\n                column_sub = getattr(batch_subselected, augm_name)\n                if column_sub is None:\n                    # list of indices was empty, resulting in the columns\n                    # in the subselected batch being empty and replaced\n                    # by Nones. We can just re-use the columns before\n                    # subselection.\n                    pass\n                elif augm_name == \"images\" and ia.is_np_array(column):\n                    # An array does not have to stay an array after\n                    # augmentation. The shapes and/or dtypes of rows may\n                    # change, turning the array into a list.\n                    if ia.is_np_array(column_sub):\n                        shapes = {column.shape[1:], column_sub.shape[1:]}\n                        dtypes = {column.dtype.name, column_sub.dtype.name}\n                    else:\n                        shapes = set(\n                            [column.shape[1:]]\n                            + [image.shape for image in column_sub])\n                        dtypes = set(\n                            [column.dtype.name]\n                            + [image.dtype.name for image in column_sub])\n\n                    if len(shapes) == 1 and len(dtypes) == 1:\n                        column[indices] = column_sub  # pylint: disable=unsupported-assignment-operation\n                    else:\n                        self.images = list(column)\n                        for ith_index, index in enumerate(indices):\n                            self.images[index] = column_sub[ith_index]\n                else:\n                    for ith_index, index in enumerate(indices):\n                        column[index] = column_sub[ith_index]  # pylint: disable=unsupported-assignment-operation\n\n        return self\n\n    def propagation_hooks_ctx(self, augmenter, hooks, parents):\n        \"\"\"Start a context in which propagation hooks are applied.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        augmenter : imgaug.augmenters.meta.Augmenter\n            Augmenter to provide to the propagation hook function.\n\n        hooks : imgaug.imgaug.HooksImages or imgaug.imgaug.HooksKeypoints\n            The hooks that might contain a propagation hook function.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            The list of parents to provide to the propagation hook function.\n\n        Returns\n        -------\n        _BatchInAugmentationPropagationContext\n            The progagation hook context.\n\n        \"\"\"\n        return _BatchInAugmentationPropagationContext(\n            self, augmenter=augmenter, hooks=hooks, parents=parents)\n\n    def apply_propagation_hooks_(self, augmenter, hooks, parents):\n        \"\"\"Set columns in this batch to ``None`` based on a propagation hook.\n\n        This method works in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        augmenter : imgaug.augmenters.meta.Augmenter\n            Augmenter to provide to the propagation hook function.\n\n        hooks : imgaug.imgaug.HooksImages or imgaug.imgaug.HooksKeypoints\n            The hooks that might contain a propagation hook function.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            The list of parents to provide to the propagation hook function.\n\n        Returns\n        -------\n        list of tuple of str\n            Information about which columns were set to ``None``.\n            Each tuple contains\n            ``(column attribute name, column value before setting it to None)``.\n            This information is required when calling\n            :func:`_BatchInAugmentation.invert_apply_propagation_hooks_`.\n\n        \"\"\"\n        if hooks is None:\n            return None\n\n        noned_info = []\n        for column in self.columns:\n            is_prop = hooks.is_propagating(\n                column.value, augmenter=augmenter, parents=parents,\n                default=True)\n            if not is_prop:\n                setattr(self, column.attr_name, None)\n                noned_info.append((column.attr_name, column.value))\n        return noned_info\n\n    def invert_apply_propagation_hooks_(self, noned_info):\n        \"\"\"Set columns from ``None`` back to their original values.\n\n        This is the inverse of\n        :func:`_BatchInAugmentation.apply_propagation_hooks_`.\n\n        This method works in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        noned_info : list of tuple of str\n            Information about which columns were set to ``None`` and their\n            original values. This is the output of\n            :func:`_BatchInAugmentation.apply_propagation_hooks_`.\n\n        Returns\n        -------\n        _BatchInAugmentation\n            The updated batch. (Modified in-place.)\n\n        \"\"\"\n        for attr_name, value in noned_info:\n            setattr(self, attr_name, value)\n        return self\n\n    def to_batch_in_augmentation(self):\n        \"\"\"Convert this batch to a :class:`_BatchInAugmentation` instance.\n\n        This method simply returns the batch itself. It exists for consistency\n        with the other batch classes.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.batches._BatchInAugmentation\n            The batch itself. (Not copied.)\n\n        \"\"\"\n        return self\n\n    def fill_from_batch_in_augmentation_(self, batch_in_augmentation):\n        \"\"\"Set the columns in this batch to the column values of another batch.\n\n        This method works in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        batch_in_augmentation : _BatchInAugmentation\n            Batch of which to use the column values.\n            The values are *not* copied. Only their references are used.\n\n        Returns\n        -------\n        _BatchInAugmentation\n            The updated batch. (Modified in-place.)\n\n        \"\"\"\n        if batch_in_augmentation is self:\n            return self\n\n        self.images = batch_in_augmentation.images\n        self.heatmaps = batch_in_augmentation.heatmaps\n        self.segmentation_maps = batch_in_augmentation.segmentation_maps\n        self.keypoints = batch_in_augmentation.keypoints\n        self.bounding_boxes = batch_in_augmentation.bounding_boxes\n        self.polygons = batch_in_augmentation.polygons\n        self.line_strings = batch_in_augmentation.line_strings\n\n        return self\n\n    def to_batch(self, batch_before_aug):\n        \"\"\"Convert this batch into a :class:`Batch` instance.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        batch_before_aug : imgaug.augmentables.batches.Batch\n            The batch before augmentation. It is required to set the input\n            data of the :class:`Batch` instance, e.g. ``images_unaug``\n            or ``data``.\n\n        Returns\n        -------\n        imgaug.augmentables.batches.Batch\n            Batch, with original unaugmented inputs from `batch_before_aug`\n            and augmented outputs from this :class:`_BatchInAugmentation`\n            instance.\n\n        \"\"\"\n        batch = Batch(\n            images=batch_before_aug.images_unaug,\n            heatmaps=batch_before_aug.heatmaps_unaug,\n            segmentation_maps=batch_before_aug.segmentation_maps_unaug,\n            keypoints=batch_before_aug.keypoints_unaug,\n            bounding_boxes=batch_before_aug.bounding_boxes_unaug,\n            polygons=batch_before_aug.polygons_unaug,\n            line_strings=batch_before_aug.line_strings_unaug,\n            data=batch_before_aug.data\n        )\n        batch.images_aug = self.images\n        batch.heatmaps_aug = self.heatmaps\n        batch.segmentation_maps_aug = self.segmentation_maps\n        batch.keypoints_aug = self.keypoints\n        batch.bounding_boxes_aug = self.bounding_boxes\n        batch.polygons_aug = self.polygons\n        batch.line_strings_aug = self.line_strings\n        return batch\n\n    def deepcopy(self):\n        \"\"\"Copy this batch and all of its column values.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        _BatchInAugmentation\n            Deep copy of this batch.\n\n        \"\"\"\n        batch = _BatchInAugmentation(data=utils.deepcopy_fast(self.data))\n\n        for augm_name in _AUGMENTABLE_NAMES:\n            value = getattr(self, augm_name)\n            if value is not None:\n                setattr(batch, augm_name, utils.copy_augmentables(value))\n\n        return batch\n"
  },
  {
    "path": "imgaug/augmentables/bbs.py",
    "content": "\"\"\"Classes representing bounding boxes.\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport copy\n\nimport numpy as np\nimport skimage.draw\nimport skimage.measure\n\nfrom .. import imgaug as ia\nfrom .base import IAugmentable\nfrom .utils import (\n    normalize_imglike_shape,\n    project_coords,\n    _remove_out_of_image_fraction_,\n    _normalize_shift_args,\n    _handle_on_image_shape\n)\n\n\n# TODO functions: square(), to_aspect_ratio(), contains_point()\nclass BoundingBox(object):\n    \"\"\"Class representing bounding boxes.\n\n    Each bounding box is parameterized by its top left and bottom right\n    corners. Both are given as x and y-coordinates. The corners are intended\n    to lie inside the bounding box area. As a result, a bounding box that lies\n    completely inside the image but has maximum extensions would have\n    coordinates ``(0.0, 0.0)`` and ``(W - epsilon, H - epsilon)``. Note that\n    coordinates are saved internally as floats.\n\n    Parameters\n    ----------\n    x1 : number\n        X-coordinate of the top left of the bounding box.\n\n    y1 : number\n        Y-coordinate of the top left of the bounding box.\n\n    x2 : number\n        X-coordinate of the bottom right of the bounding box.\n\n    y2 : number\n        Y-coordinate of the bottom right of the bounding box.\n\n    label : None or str, optional\n        Label of the bounding box, e.g. a string representing the class.\n\n    \"\"\"\n\n    def __init__(self, x1, y1, x2, y2, label=None):\n        \"\"\"Create a new BoundingBox instance.\"\"\"\n        if x1 > x2:\n            x2, x1 = x1, x2\n        if y1 > y2:\n            y2, y1 = y1, y2\n\n        self.x1 = x1\n        self.y1 = y1\n        self.x2 = x2\n        self.y2 = y2\n        self.label = label\n\n    @property\n    def coords(self):\n        \"\"\"Get the top-left and bottom-right coordinates as one array.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        ndarray\n            A ``(N, 2)`` numpy array with ``N=2`` containing the top-left\n            and bottom-right coordinates.\n\n        \"\"\"\n        arr = np.empty((2, 2), dtype=np.float32)\n        arr[0, :] = (self.x1, self.y1)\n        arr[1, :] = (self.x2, self.y2)\n        return arr\n\n    @property\n    def x1_int(self):\n        \"\"\"Get the x-coordinate of the top left corner as an integer.\n\n        Returns\n        -------\n        int\n            X-coordinate of the top left corner, rounded to the closest\n            integer.\n\n        \"\"\"\n        # use numpy's round to have consistent behaviour between python\n        # versions\n        return int(np.round(self.x1))\n\n    @property\n    def y1_int(self):\n        \"\"\"Get the y-coordinate of the top left corner as an integer.\n\n        Returns\n        -------\n        int\n            Y-coordinate of the top left corner, rounded to the closest\n            integer.\n\n        \"\"\"\n        # use numpy's round to have consistent behaviour between python\n        # versions\n        return int(np.round(self.y1))\n\n    @property\n    def x2_int(self):\n        \"\"\"Get the x-coordinate of the bottom left corner as an integer.\n\n        Returns\n        -------\n        int\n            X-coordinate of the bottom left corner, rounded to the closest\n            integer.\n\n        \"\"\"\n        # use numpy's round to have consistent behaviour between python\n        # versions\n        return int(np.round(self.x2))\n\n    @property\n    def y2_int(self):\n        \"\"\"Get the y-coordinate of the bottom left corner as an integer.\n\n        Returns\n        -------\n        int\n            Y-coordinate of the bottom left corner, rounded to the closest\n            integer.\n\n        \"\"\"\n        # use numpy's round to have consistent behaviour between python\n        # versions\n        return int(np.round(self.y2))\n\n    @property\n    def height(self):\n        \"\"\"Estimate the height of the bounding box.\n\n        Returns\n        -------\n        number\n            Height of the bounding box.\n\n        \"\"\"\n        return self.y2 - self.y1\n\n    @property\n    def width(self):\n        \"\"\"Estimate the width of the bounding box.\n\n        Returns\n        -------\n        number\n            Width of the bounding box.\n\n        \"\"\"\n        return self.x2 - self.x1\n\n    @property\n    def center_x(self):\n        \"\"\"Estimate the x-coordinate of the center point of the bounding box.\n\n        Returns\n        -------\n        number\n            X-coordinate of the center point of the bounding box.\n\n        \"\"\"\n        return self.x1 + self.width/2\n\n    @property\n    def center_y(self):\n        \"\"\"Estimate the y-coordinate of the center point of the bounding box.\n\n        Returns\n        -------\n        number\n            Y-coordinate of the center point of the bounding box.\n\n        \"\"\"\n        return self.y1 + self.height/2\n\n    @property\n    def area(self):\n        \"\"\"Estimate the area of the bounding box.\n\n        Returns\n        -------\n        number\n            Area of the bounding box, i.e. ``height * width``.\n\n        \"\"\"\n        return self.height * self.width\n\n    # TODO add test for tuple of number\n    def contains(self, other):\n        \"\"\"Estimate whether the bounding box contains a given point.\n\n        Parameters\n        ----------\n        other : tuple of number or imgaug.augmentables.kps.Keypoint\n            Point to check for.\n\n        Returns\n        -------\n        bool\n            ``True`` if the point is contained in the bounding box,\n            ``False`` otherwise.\n\n        \"\"\"\n        if isinstance(other, tuple):\n            x, y = other\n        else:\n            x, y = other.x, other.y\n        return self.x1 <= x <= self.x2 and self.y1 <= y <= self.y2\n\n    def project_(self, from_shape, to_shape):\n        \"\"\"Project the bounding box onto a differently shaped image in-place.\n\n        E.g. if the bounding box is on its original image at\n        ``x1=(10 of 100 pixels)`` and ``y1=(20 of 100 pixels)`` and is\n        projected onto a new image with size ``(width=200, height=200)``,\n        its new position will be ``(x1=20, y1=40)``.\n        (Analogous for ``x2``/``y2``.)\n\n        This is intended for cases where the original image is resized.\n        It cannot be used for more complex changes (e.g. padding, cropping).\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        from_shape : tuple of int or ndarray\n            Shape of the original image. (Before resize.)\n\n        to_shape : tuple of int or ndarray\n            Shape of the new image. (After resize.)\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox\n            ``BoundingBox`` instance with new coordinates.\n            The object may have been modified in-place.\n\n        \"\"\"\n        (self.x1, self.y1), (self.x2, self.y2) = project_coords(\n            [(self.x1, self.y1), (self.x2, self.y2)],\n            from_shape,\n            to_shape)\n        return self\n\n    # TODO add tests for ndarray inputs\n    def project(self, from_shape, to_shape):\n        \"\"\"Project the bounding box onto a differently shaped image.\n\n        E.g. if the bounding box is on its original image at\n        ``x1=(10 of 100 pixels)`` and ``y1=(20 of 100 pixels)`` and is\n        projected onto a new image with size ``(width=200, height=200)``,\n        its new position will be ``(x1=20, y1=40)``.\n        (Analogous for ``x2``/``y2``.)\n\n        This is intended for cases where the original image is resized.\n        It cannot be used for more complex changes (e.g. padding, cropping).\n\n        Parameters\n        ----------\n        from_shape : tuple of int or ndarray\n            Shape of the original image. (Before resize.)\n\n        to_shape : tuple of int or ndarray\n            Shape of the new image. (After resize.)\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox\n            ``BoundingBox`` instance with new coordinates.\n\n        \"\"\"\n        return self.deepcopy().project_(from_shape, to_shape)\n\n    def extend_(self, all_sides=0, top=0, right=0, bottom=0, left=0):\n        \"\"\"Extend the size of the bounding box along its sides in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        all_sides : number, optional\n            Value by which to extend the bounding box size along all\n            sides.\n\n        top : number, optional\n            Value by which to extend the bounding box size along its top\n            side.\n\n        right : number, optional\n            Value by which to extend the bounding box size along its right\n            side.\n\n        bottom : number, optional\n            Value by which to extend the bounding box size along its bottom\n            side.\n\n        left : number, optional\n            Value by which to extend the bounding box size along its left\n            side.\n\n        Returns\n        -------\n        imgaug.BoundingBox\n            Extended bounding box.\n            The object may have been modified in-place.\n\n        \"\"\"\n        self.x1 = self.x1 - all_sides - left\n        self.x2 = self.x2 + all_sides + right\n        self.y1 = self.y1 - all_sides - top\n        self.y2 = self.y2 + all_sides + bottom\n        return self\n\n    def extend(self, all_sides=0, top=0, right=0, bottom=0, left=0):\n        \"\"\"Extend the size of the bounding box along its sides.\n\n        Parameters\n        ----------\n        all_sides : number, optional\n            Value by which to extend the bounding box size along all\n            sides.\n\n        top : number, optional\n            Value by which to extend the bounding box size along its top\n            side.\n\n        right : number, optional\n            Value by which to extend the bounding box size along its right\n            side.\n\n        bottom : number, optional\n            Value by which to extend the bounding box size along its bottom\n            side.\n\n        left : number, optional\n            Value by which to extend the bounding box size along its left\n            side.\n\n        Returns\n        -------\n        imgaug.BoundingBox\n            Extended bounding box.\n\n        \"\"\"\n        return self.deepcopy().extend_(all_sides, top, right, bottom, left)\n\n    def intersection(self, other, default=None):\n        \"\"\"Compute the intersection BB between this BB and another BB.\n\n        Note that in extreme cases, the intersection can be a single point.\n        In that case the intersection bounding box exists and it will be\n        returned, but it will have a height and width of zero.\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.bbs.BoundingBox\n            Other bounding box with which to generate the intersection.\n\n        default : any, optional\n            Default value to return if there is no intersection.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox or any\n            Intersection bounding box of the two bounding boxes if there is\n            an intersection.\n            If there is no intersection, the default value will be returned,\n            which can by anything.\n\n        \"\"\"\n        x1_i = max(self.x1, other.x1)\n        y1_i = max(self.y1, other.y1)\n        x2_i = min(self.x2, other.x2)\n        y2_i = min(self.y2, other.y2)\n        if x1_i > x2_i or y1_i > y2_i:\n            return default\n        return BoundingBox(x1=x1_i, y1=y1_i, x2=x2_i, y2=y2_i)\n\n    def union(self, other):\n        \"\"\"Compute the union BB between this BB and another BB.\n\n        This is equivalent to drawing a bounding box around all corner points\n        of both bounding boxes.\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.bbs.BoundingBox\n            Other bounding box with which to generate the union.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox\n            Union bounding box of the two bounding boxes.\n\n        \"\"\"\n        return BoundingBox(\n            x1=min(self.x1, other.x1),\n            y1=min(self.y1, other.y1),\n            x2=max(self.x2, other.x2),\n            y2=max(self.y2, other.y2),\n        )\n\n    def iou(self, other):\n        \"\"\"Compute the IoU between this bounding box and another one.\n\n        IoU is the intersection over union, defined as::\n\n            ``area(intersection(A, B)) / area(union(A, B))``\n            ``= area(intersection(A, B))\n                / (area(A) + area(B) - area(intersection(A, B)))``\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.bbs.BoundingBox\n            Other bounding box with which to compare.\n\n        Returns\n        -------\n        float\n            IoU between the two bounding boxes.\n\n        \"\"\"\n        inters = self.intersection(other)\n        if inters is None:\n            return 0.0\n        area_union = self.area + other.area - inters.area\n        return inters.area / area_union if area_union > 0 else 0.0\n\n    def compute_out_of_image_area(self, image):\n        \"\"\"Compute the area of the BB that is outside of the image plane.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape\n            and must contain at least two integers.\n\n        Returns\n        -------\n        float\n            Total area of the bounding box that is outside of the image plane.\n            Can be ``0.0``.\n\n        \"\"\"\n        shape = normalize_imglike_shape(image)\n        height, width = shape[0:2]\n        bb_image = BoundingBox(x1=0, y1=0, x2=width, y2=height)\n        inter = self.intersection(bb_image, default=None)\n        area = self.area\n        return area if inter is None else area - inter.area\n\n    def compute_out_of_image_fraction(self, image):\n        \"\"\"Compute fraction of BB area outside of the image plane.\n\n        This estimates ``f = A_ooi / A``, where ``A_ooi`` is the area of the\n        bounding box that is outside of the image plane, while ``A`` is the\n        total area of the bounding box.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape\n            and must contain at least two integers.\n\n        Returns\n        -------\n        float\n            Fraction of the bounding box area that is outside of the image\n            plane. Returns ``0.0`` if the bounding box is fully inside of\n            the image plane. If the bounding box has an area of zero, the\n            result is ``1.0`` if its coordinates are outside of the image\n            plane, otherwise ``0.0``.\n\n        \"\"\"\n        area = self.area\n        if area == 0:\n            shape = normalize_imglike_shape(image)\n            height, width = shape[0:2]\n            y1_outside = self.y1 < 0 or self.y1 >= height\n            x1_outside = self.x1 < 0 or self.x1 >= width\n            is_outside = (y1_outside or x1_outside)\n            return 1.0 if is_outside else 0.0\n        return self.compute_out_of_image_area(image) / area\n\n    def is_fully_within_image(self, image):\n        \"\"\"Estimate whether the bounding box is fully inside the image area.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape\n            and must contain at least two integers.\n\n        Returns\n        -------\n        bool\n            ``True`` if the bounding box is fully inside the image area.\n            ``False`` otherwise.\n\n        \"\"\"\n        shape = normalize_imglike_shape(image)\n        height, width = shape[0:2]\n        return (\n            self.x1 >= 0\n            and self.x2 < width\n            and self.y1 >= 0\n            and self.y2 < height)\n\n    def is_partly_within_image(self, image):\n        \"\"\"Estimate whether the BB is at least partially inside the image area.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape\n            and must contain at least two integers.\n\n        Returns\n        -------\n        bool\n            ``True`` if the bounding box is at least partially inside the\n            image area.\n            ``False`` otherwise.\n\n        \"\"\"\n        shape = normalize_imglike_shape(image)\n        height, width = shape[0:2]\n        eps = np.finfo(np.float32).eps\n        img_bb = BoundingBox(x1=0, x2=width-eps, y1=0, y2=height-eps)\n        return self.intersection(img_bb) is not None\n\n    def is_out_of_image(self, image, fully=True, partly=False):\n        \"\"\"Estimate whether the BB is partially/fully outside of the image area.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape and\n            must contain at least two integers.\n\n        fully : bool, optional\n            Whether to return ``True`` if the bounding box is fully outside\n            of the image area.\n\n        partly : bool, optional\n            Whether to return ``True`` if the bounding box is at least\n            partially outside fo the image area.\n\n        Returns\n        -------\n        bool\n            ``True`` if the bounding box is partially/fully outside of the\n            image area, depending on defined parameters.\n            ``False`` otherwise.\n\n        \"\"\"\n        if self.is_fully_within_image(image):\n            return False\n        if self.is_partly_within_image(image):\n            return partly\n        return fully\n\n    @ia.deprecated(alt_func=\"BoundingBox.clip_out_of_image()\",\n                   comment=\"clip_out_of_image() has the exactly same \"\n                           \"interface.\")\n    def cut_out_of_image(self, *args, **kwargs):\n        \"\"\"Clip off all parts of the BB box that are outside of the image.\"\"\"\n        return self.clip_out_of_image(*args, **kwargs)\n\n    def clip_out_of_image_(self, image):\n        \"\"\"Clip off parts of the BB box that are outside of the image in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use for the clipping of the bounding box.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape and\n            must contain at least two integers.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox\n            Bounding box, clipped to fall within the image dimensions.\n            The object may have been modified in-place.\n\n        \"\"\"\n        shape = normalize_imglike_shape(image)\n\n        height, width = shape[0:2]\n        assert height > 0, (\n            \"Expected image with height>0, got shape %s.\" % (image.shape,))\n        assert width > 0, (\n            \"Expected image with width>0, got shape %s.\" % (image.shape,))\n\n        eps = np.finfo(np.float32).eps\n        self.x1 = np.clip(self.x1, 0, width - eps)\n        self.x2 = np.clip(self.x2, 0, width - eps)\n        self.y1 = np.clip(self.y1, 0, height - eps)\n        self.y2 = np.clip(self.y2, 0, height - eps)\n\n        return self\n\n    def clip_out_of_image(self, image):\n        \"\"\"Clip off all parts of the BB box that are outside of the image.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use for the clipping of the bounding box.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape and\n            must contain at least two integers.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox\n            Bounding box, clipped to fall within the image dimensions.\n\n        \"\"\"\n        return self.deepcopy().clip_out_of_image_(image)\n\n    def shift_(self, x=0, y=0):\n        \"\"\"Move this bounding box along the x/y-axis in-place.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox\n            Shifted bounding box.\n            The object may have been modified in-place.\n\n        \"\"\"\n        self.x1 += x\n        self.x2 += x\n        self.y1 += y\n        self.y2 += y\n        return self\n\n    def shift(self, x=0, y=0, top=None, right=None, bottom=None, left=None):\n        \"\"\"Move this bounding box along the x/y-axis.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        top : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            top (towards the bottom).\n\n        right : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            right (towards the left).\n\n        bottom : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            bottom (towards the top).\n\n        left : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            left (towards the right).\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox\n            Shifted bounding box.\n\n        \"\"\"\n        # pylint: disable=redefined-outer-name\n        x, y = _normalize_shift_args(\n            x, y, top=top, right=right, bottom=bottom, left=left)\n        return self.deepcopy().shift_(x, y)\n\n    def draw_label_on_image(self, image, color=(0, 255, 0),\n                            color_text=None, color_bg=None, alpha=1.0, size=1,\n                            size_text=20, height=30,\n                            copy=True, raise_if_out_of_image=False):\n        \"\"\"Draw a box showing the BB's label.\n\n        The box is placed right above the BB's rectangle.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : (H,W,C) ndarray\n            The image onto which to draw the label.\n            Currently expected to be ``uint8``.\n\n        color : None or iterable of int, optional\n            The color to use, corresponding to the channel layout of the\n            image. Usually RGB. Text and background colors will be derived\n            from this.\n\n        color_text : None or iterable of int, optional\n            The text color to use.\n            If ``None``, derived from `color_bg`.\n\n        color_bg : None or iterable of int, optional\n            The background color of the label box.\n            If ``None``, derived from `color`.\n\n        alpha : float, optional\n            The transparency of the drawn bounding box, where ``1.0`` denotes\n            no transparency and ``0.0`` is invisible.\n\n        size : int, optional\n            The thickness of the bounding box in pixels. If the value is\n            larger than ``1``, then additional pixels will be added around\n            the bounding box (i.e. extension towards the outside).\n\n        size_text : int, optional\n            Font size to use.\n\n        height : int, optional\n            Height of the label box in pixels.\n\n        copy : bool, optional\n            Whether to copy the input image or change it in-place.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the bounding box is fully outside of\n            the image. If set to ``False``, no error will be raised and only\n            the parts inside the image will be drawn.\n\n        Returns\n        -------\n        (H,W,C) ndarray(uint8)\n            Image with bounding box drawn on it.\n\n        \"\"\"\n        # pylint: disable=redefined-outer-name\n        drawer = _LabelOnImageDrawer(\n            color=color,\n            color_text=color_text,\n            color_bg=color_bg,\n            size=size,\n            alpha=alpha,\n            raise_if_out_of_image=raise_if_out_of_image,\n            height=height,\n            size_text=size_text)\n        if copy:\n            return drawer.draw_on_image(image, self)\n        return drawer.draw_on_image_(image, self)\n\n    def draw_box_on_image(self, image, color=(0, 255, 0), alpha=1.0,\n                          size=1, copy=True, raise_if_out_of_image=False,\n                          thickness=None):\n        \"\"\"Draw the rectangle of the bounding box on an image.\n\n        This method does not draw the label.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : (H,W,C) ndarray\n            The image onto which to draw the bounding box rectangle.\n            Currently expected to be ``uint8``.\n\n        color : iterable of int, optional\n            The color to use, corresponding to the channel layout of the\n            image. Usually RGB.\n\n        alpha : float, optional\n            The transparency of the drawn bounding box, where ``1.0`` denotes\n            no transparency and ``0.0`` is invisible.\n\n        size : int, optional\n            The thickness of the bounding box in pixels. If the value is\n            larger than ``1``, then additional pixels will be added around\n            the bounding box (i.e. extension towards the outside).\n\n        copy : bool, optional\n            Whether to copy the input image or change it in-place.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the bounding box is fully outside of\n            the image. If set to ``False``, no error will be raised and only\n            the parts inside the image will be drawn.\n\n        thickness : None or int, optional\n            Deprecated.\n\n        Returns\n        -------\n        (H,W,C) ndarray(uint8)\n            Image with bounding box drawn on it.\n\n        \"\"\"\n        # pylint: disable=invalid-name, redefined-outer-name\n        if thickness is not None:\n            ia.warn_deprecated(\n                \"Usage of argument 'thickness' in BoundingBox.draw_on_image() \"\n                \"is deprecated. The argument was renamed to 'size'.\")\n            size = thickness\n\n        if raise_if_out_of_image and self.is_out_of_image(image):\n            raise Exception(\n                \"Cannot draw bounding box x1=%.8f, y1=%.8f, x2=%.8f, y2=%.8f \"\n                \"on image with shape %s.\" % (\n                    self.x1, self.y1, self.x2, self.y2, image.shape))\n\n        result = np.copy(image) if copy else image\n\n        if isinstance(color, (tuple, list)):\n            color = np.uint8(color)\n\n        for i in range(size):\n            y1, y2, x1, x2 = self.y1_int, self.y2_int, self.x1_int, self.x2_int\n\n            # When y values get into the range (H-0.5, H), the *_int functions\n            # round them to H. That is technically sensible, but in the case\n            # of drawing means that the border lies just barely outside of\n            # the image, making the border disappear, even though the BB is\n            # fully inside the image. Here we correct for that because of\n            # beauty reasons. Same is the case for x coordinates.\n            if self.is_fully_within_image(image):\n                y1 = np.clip(y1, 0, image.shape[0]-1)\n                y2 = np.clip(y2, 0, image.shape[0]-1)\n                x1 = np.clip(x1, 0, image.shape[1]-1)\n                x2 = np.clip(x2, 0, image.shape[1]-1)\n\n            y = [y1-i, y1-i, y2+i, y2+i]\n            x = [x1-i, x2+i, x2+i, x1-i]\n            rr, cc = skimage.draw.polygon_perimeter(y, x, shape=result.shape)\n            if alpha >= 0.99:\n                result[rr, cc, :] = color\n            else:\n                if ia.is_float_array(result):\n                    # TODO use blend_alpha here\n                    result[rr, cc, :] = (\n                        (1 - alpha) * result[rr, cc, :]\n                        + alpha * color)\n                    result = np.clip(result, 0, 255)\n                else:\n                    input_dtype = result.dtype\n                    result = result.astype(np.float32)\n                    result[rr, cc, :] = (\n                        (1 - alpha) * result[rr, cc, :]\n                        + alpha * color)\n                    result = np.clip(result, 0, 255).astype(input_dtype)\n\n        return result\n\n    # TODO add explicit test for zero-sized BBs (worked when tested by hand)\n    def draw_on_image(self, image, color=(0, 255, 0), alpha=1.0, size=1,\n                      copy=True, raise_if_out_of_image=False, thickness=None):\n        \"\"\"Draw the bounding box on an image.\n\n        This will automatically also draw the label, unless it is ``None``.\n        To only draw the box rectangle use\n        :func:`~imgaug.augmentables.bbs.BoundingBox.draw_box_on_image`.\n        To draw the label even if it is ``None`` or to configure e.g. its\n        color, use\n        :func:`~imgaug.augmentables.bbs.BoundingBox.draw_label_on_image`.\n\n        Parameters\n        ----------\n        image : (H,W,C) ndarray\n            The image onto which to draw the bounding box.\n            Currently expected to be ``uint8``.\n\n        color : iterable of int, optional\n            The color to use, corresponding to the channel layout of the\n            image. Usually RGB.\n\n        alpha : float, optional\n            The transparency of the drawn bounding box, where ``1.0`` denotes\n            no transparency and ``0.0`` is invisible.\n\n        size : int, optional\n            The thickness of the bounding box in pixels. If the value is\n            larger than ``1``, then additional pixels will be added around\n            the bounding box (i.e. extension towards the outside).\n\n        copy : bool, optional\n            Whether to copy the input image or change it in-place.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the bounding box is fully outside of\n            the image. If set to ``False``, no error will be raised and only\n            the parts inside the image will be drawn.\n\n        thickness : None or int, optional\n            Deprecated.\n\n        Returns\n        -------\n        (H,W,C) ndarray(uint8)\n            Image with bounding box drawn on it.\n\n        \"\"\"\n        # pylint: disable=redefined-outer-name\n        image_drawn = self.draw_box_on_image(\n            image, color=color, alpha=alpha, size=size,\n            copy=copy, raise_if_out_of_image=raise_if_out_of_image,\n            thickness=thickness\n        )\n        if self.label is not None:\n            image_drawn = self.draw_label_on_image(\n                image_drawn, color=color, alpha=alpha,\n                size=size if thickness is None else thickness,\n                copy=False, raise_if_out_of_image=raise_if_out_of_image\n            )\n        return image_drawn\n\n    # TODO add tests for pad and pad_max\n    def extract_from_image(self, image, pad=True, pad_max=None,\n                           prevent_zero_size=True):\n        \"\"\"Extract the image pixels within the bounding box.\n\n        This function will zero-pad the image if the bounding box is\n        partially/fully outside of the image.\n\n        Parameters\n        ----------\n        image : (H,W) ndarray or (H,W,C) ndarray\n            The image from which to extract the pixels within the bounding box.\n\n        pad : bool, optional\n            Whether to zero-pad the image if the object is partially/fully\n            outside of it.\n\n        pad_max : None or int, optional\n            The maximum number of pixels that may be zero-paded on any side,\n            i.e. if this has value ``N`` the total maximum of added pixels\n            is ``4*N``.\n            This option exists to prevent extremely large images as a result of\n            single points being moved very far away during augmentation.\n\n        prevent_zero_size : bool, optional\n            Whether to prevent the height or width of the extracted image from\n            becoming zero.\n            If this is set to ``True`` and the height or width of the bounding\n            box is below ``1``, the height/width will be increased to ``1``.\n            This can be useful to prevent problems, e.g. with image saving or\n            plotting.\n            If it is set to ``False``, images will be returned as ``(H', W')``\n            or ``(H', W', 3)`` with ``H`` or ``W`` potentially being 0.\n\n        Returns\n        -------\n        (H',W') ndarray or (H',W',C) ndarray\n            Pixels within the bounding box. Zero-padded if the bounding box\n            is partially/fully outside of the image.\n            If `prevent_zero_size` is activated, it is guarantueed that\n            ``H'>0`` and ``W'>0``, otherwise only ``H'>=0`` and ``W'>=0``.\n\n        \"\"\"\n        # pylint: disable=no-else-return, too-many-statements\n        height, width = image.shape[0], image.shape[1]\n        x1, x2, y1, y2 = self.x1_int, self.x2_int, self.y1_int, self.y2_int\n\n        # When y values get into the range (H-0.5, H), the *_int functions\n        # round them to H. That is technically sensible, but in the case of\n        # extraction leads to a black border, which is both ugly and\n        # unexpected after calling cut_out_of_image(). Here we correct for\n        # that because of beauty reasons. Same is the case for x coordinates.\n        fully_within = self.is_fully_within_image(image)\n        if fully_within:\n            y1, y2 = np.clip([y1, y2], 0, height-1)\n            x1, x2 = np.clip([x1, x2], 0, width-1)\n\n        # TODO add test\n        if prevent_zero_size:\n            if abs(x2 - x1) < 1:\n                x2 = x1 + 1\n            if abs(y2 - y1) < 1:\n                y2 = y1 + 1\n\n        if pad:\n            # if the bb is outside of the image area, the following pads the\n            # image first with black pixels until the bb is inside the image\n            # and only then extracts the image area\n            # TODO probably more efficient to initialize an array of zeros\n            #      and copy only the portions of the bb into that array that\n            #      are natively inside the image area\n            from ..augmenters import size as iasize\n\n            pad_top = 0\n            pad_right = 0\n            pad_bottom = 0\n            pad_left = 0\n\n            if x1 < 0:\n                pad_left = abs(x1)\n                x2 = x2 + pad_left\n                width = width + pad_left\n                x1 = 0\n            if y1 < 0:\n                pad_top = abs(y1)\n                y2 = y2 + pad_top\n                height = height + pad_top\n                y1 = 0\n            if x2 >= width:\n                pad_right = x2 - width\n            if y2 >= height:\n                pad_bottom = y2 - height\n\n            paddings = [pad_top, pad_right, pad_bottom, pad_left]\n            any_padded = any([val > 0 for val in paddings])\n            if any_padded:\n                if pad_max is None:\n                    pad_max = max(paddings)\n\n                image = iasize.pad(\n                    image,\n                    top=min(pad_top, pad_max),\n                    right=min(pad_right, pad_max),\n                    bottom=min(pad_bottom, pad_max),\n                    left=min(pad_left, pad_max)\n                )\n            return image[y1:y2, x1:x2]\n        else:\n            within_image = (\n                (0, 0, 0, 0)\n                <= (x1, y1, x2, y2)\n                < (width, height, width, height)\n            )\n            out_height, out_width = (y2 - y1), (x2 - x1)\n            nonzero_height = (out_height > 0)\n            nonzero_width = (out_width > 0)\n            if within_image and nonzero_height and nonzero_width:\n                return image[y1:y2, x1:x2]\n            if prevent_zero_size:\n                out_height = 1\n                out_width = 1\n            else:\n                out_height = 0\n                out_width = 0\n            if image.ndim == 2:\n                return np.zeros((out_height, out_width), dtype=image.dtype)\n            return np.zeros((out_height, out_width, image.shape[-1]),\n                            dtype=image.dtype)\n\n    # TODO also add to_heatmap\n    # TODO add this to BoundingBoxesOnImage\n    # TODO add label to keypoints?\n    def to_keypoints(self):\n        \"\"\"Convert the BB's corners to keypoints (clockwise, from top left).\n\n        Returns\n        -------\n        list of imgaug.augmentables.kps.Keypoint\n            Corners of the bounding box as keypoints.\n\n        \"\"\"\n        # TODO get rid of this deferred import\n        from imgaug.augmentables.kps import Keypoint\n\n        return [\n            Keypoint(x=self.x1, y=self.y1),\n            Keypoint(x=self.x2, y=self.y1),\n            Keypoint(x=self.x2, y=self.y2),\n            Keypoint(x=self.x1, y=self.y2)\n        ]\n\n    def to_polygon(self):\n        \"\"\"Convert this bounding box to a polygon covering the same area.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            The bounding box converted to a polygon.\n\n        \"\"\"\n        # TODO get rid of this deferred import\n        from imgaug.augmentables.polys import Polygon\n\n        return Polygon([\n            (self.x1, self.y1),\n            (self.x2, self.y1),\n            (self.x2, self.y2),\n            (self.x1, self.y2)\n        ], label=self.label)\n\n    # TODO also introduce similar area_almost_equals()\n    def coords_almost_equals(self, other, max_distance=1e-4):\n        \"\"\"Estimate if this and another BB have almost identical coordinates.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.bbs.BoundingBox or iterable\n            The other bounding box with which to compare this one.\n            If this is an ``iterable``, it is assumed to represent the top-left\n            and bottom-right coordinates of that bounding box, given as e.g.\n            an ``(2,2)`` ndarray or an ``(4,)`` ndarray or as a similar list.\n\n        max_distance : number, optional\n            The maximum euclidean distance between a corner on one bounding\n            box and the closest corner on the other bounding box. If the\n            distance is exceeded for any such pair, the two BBs are not\n            viewed as equal.\n\n        Returns\n        -------\n        bool\n            Whether the two bounding boxes have almost identical corner\n            coordinates.\n\n        \"\"\"\n        if isinstance(other, BoundingBox):\n            coords_b = other.coords.flat\n        elif ia.is_np_array(other):\n            # we use flat here in case other is (N,2) instead of (4,)\n            coords_b = other.flat\n        elif ia.is_iterable(other):\n            coords_b = list(ia.flatten(other))\n        else:\n            raise ValueError(\n                \"Expected 'other' to be an iterable containing two \"\n                \"(x,y)-coordinate pairs or a BoundingBox. \"\n                \"Got type %s.\" % (type(other),))\n\n        coords_a = self.coords\n\n        return np.allclose(coords_a.flat, coords_b, atol=max_distance, rtol=0)\n\n    def almost_equals(self, other, max_distance=1e-4):\n        \"\"\"Compare this and another BB's label and coordinates.\n\n        This is the same as\n        :func:`~imgaug.augmentables.bbs.BoundingBox.coords_almost_equals` but\n        additionally compares the labels.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.bbs.BoundingBox or iterable\n            The other object to compare against. Expected to be a\n            ``BoundingBox``.\n\n        max_distance : number, optional\n            See\n            :func:`~imgaug.augmentables.bbs.BoundingBox.coords_almost_equals`.\n\n        Returns\n        -------\n        bool\n            ``True`` if the coordinates are almost equal and additionally\n            the labels are equal. Otherwise ``False``.\n\n        \"\"\"\n        if self.label != other.label:\n            return False\n        return self.coords_almost_equals(other, max_distance=max_distance)\n\n    @classmethod\n    def from_point_soup(cls, xy):\n        \"\"\"Convert a ``(2P,) or (P,2) ndarray`` to a BB instance.\n\n        This is the inverse of\n        :func:`~imgaug.BoundingBoxesOnImage.to_xyxy_array`.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        xy : (2P,) ndarray or (P, 2) array or iterable of number or iterable of iterable of number\n            Array containing ``P`` points in xy-form denoting a soup of\n            points around which to place a bounding box.\n            The array should usually be of dtype ``float32``.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox\n            Bounding box around the points.\n\n        \"\"\"\n        # pylint: disable=unsubscriptable-object\n        xy = np.array(xy, dtype=np.float32)\n\n        assert len(xy) > 0, (\n            \"Expected to get at least one point to place a bounding box \"\n            \"around, got shape %s.\" % (xy.shape,))\n\n        assert xy.ndim == 1 or (xy.ndim == 2 and xy.shape[-1] == 2), (\n            \"Expected input array of shape (P,) or (P, 2), \"\n            \"got shape %s.\" % (xy.shape,))\n\n        if xy.ndim == 1:\n            xy = xy.reshape((-1, 2))\n\n        x1, y1 = np.min(xy, axis=0)\n        x2, y2 = np.max(xy, axis=0)\n\n        return cls(x1=x1, y1=y1, x2=x2, y2=y2)\n\n    def copy(self, x1=None, y1=None, x2=None, y2=None, label=None):\n        \"\"\"Create a shallow copy of this BoundingBox instance.\n\n        Parameters\n        ----------\n        x1 : None or number\n            If not ``None``, then the ``x1`` coordinate of the copied object\n            will be set to this value.\n\n        y1 : None or number\n            If not ``None``, then the ``y1`` coordinate of the copied object\n            will be set to this value.\n\n        x2 : None or number\n            If not ``None``, then the ``x2`` coordinate of the copied object\n            will be set to this value.\n\n        y2 : None or number\n            If not ``None``, then the ``y2`` coordinate of the copied object\n            will be set to this value.\n\n        label : None or string\n            If not ``None``, then the ``label`` of the copied object\n            will be set to this value.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox\n            Shallow copy.\n\n        \"\"\"\n        return BoundingBox(\n            x1=self.x1 if x1 is None else x1,\n            x2=self.x2 if x2 is None else x2,\n            y1=self.y1 if y1 is None else y1,\n            y2=self.y2 if y2 is None else y2,\n            label=copy.deepcopy(self.label) if label is None else label\n        )\n\n    def deepcopy(self, x1=None, y1=None, x2=None, y2=None, label=None):\n        \"\"\"\n        Create a deep copy of the BoundingBox object.\n\n        Parameters\n        ----------\n        x1 : None or number\n            If not ``None``, then the ``x1`` coordinate of the copied object\n            will be set to this value.\n\n        y1 : None or number\n            If not ``None``, then the ``y1`` coordinate of the copied object\n            will be set to this value.\n\n        x2 : None or number\n            If not ``None``, then the ``x2`` coordinate of the copied object\n            will be set to this value.\n\n        y2 : None or number\n            If not ``None``, then the ``y2`` coordinate of the copied object\n            will be set to this value.\n\n        label : None or string\n            If not ``None``, then the ``label`` of the copied object\n            will be set to this value.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox\n            Deep copy.\n\n        \"\"\"\n        # TODO write specific copy routine with deepcopy for label and remove\n        #      the deepcopy from copy()\n        return self.copy(x1=x1, y1=y1, x2=x2, y2=y2, label=label)\n\n    def __getitem__(self, indices):\n        \"\"\"Get the coordinate(s) with given indices.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        ndarray\n            xy-coordinate(s) as ``ndarray``.\n\n        \"\"\"\n        return self.coords[indices]\n\n    def __iter__(self):\n        \"\"\"Iterate over the coordinates of this instance.\n\n        Added in 0.4.0.\n\n        Yields\n        ------\n        ndarray\n            An ``(2,)`` ``ndarray`` denoting an xy-coordinate pair.\n\n        \"\"\"\n        return iter(self.coords)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"BoundingBox(x1=%.4f, y1=%.4f, x2=%.4f, y2=%.4f, label=%s)\" % (\n            self.x1, self.y1, self.x2, self.y2, self.label)\n\n\nclass BoundingBoxesOnImage(IAugmentable):\n    \"\"\"Container for the list of all bounding boxes on a single image.\n\n    Parameters\n    ----------\n    bounding_boxes : list of imgaug.augmentables.bbs.BoundingBox\n        List of bounding boxes on the image.\n\n    shape : tuple of int\n        The shape of the image on which the objects are placed, i.e. the\n        result of ``image.shape``.\n        Should include the number of channels, not only height and width.\n\n    Examples\n    --------\n    >>> import numpy as np\n    >>> from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage\n    >>>\n    >>> image = np.zeros((100, 100))\n    >>> bbs = [\n    >>>     BoundingBox(x1=10, y1=20, x2=20, y2=30),\n    >>>     BoundingBox(x1=25, y1=50, x2=30, y2=70)\n    >>> ]\n    >>> bbs_oi = BoundingBoxesOnImage(bbs, shape=image.shape)\n\n    \"\"\"\n    def __init__(self, bounding_boxes, shape):\n        self.bounding_boxes = bounding_boxes\n        self.shape = _handle_on_image_shape(shape, self)\n\n    @property\n    def items(self):\n        \"\"\"Get the bounding boxes in this container.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of BoundingBox\n            Bounding boxes within this container.\n\n        \"\"\"\n        return self.bounding_boxes\n\n    @items.setter\n    def items(self, value):\n        \"\"\"Set the bounding boxes in this container.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        value : list of BoundingBox\n            Bounding boxes within this container.\n\n        \"\"\"\n        self.bounding_boxes = value\n\n    # TODO remove this? here it is image height, but in BoundingBox it is\n    #      bounding box height\n    @property\n    def height(self):\n        \"\"\"Get the height of the image on which the bounding boxes fall.\n\n        Returns\n        -------\n        int\n            Image height.\n\n        \"\"\"\n        return self.shape[0]\n\n    # TODO remove this? here it is image width, but in BoundingBox it is\n    #      bounding box width\n    @property\n    def width(self):\n        \"\"\"Get the width of the image on which the bounding boxes fall.\n\n        Returns\n        -------\n        int\n            Image width.\n\n        \"\"\"\n        return self.shape[1]\n\n    @property\n    def empty(self):\n        \"\"\"Determine whether this instance contains zero bounding boxes.\n\n        Returns\n        -------\n        bool\n            True if this object contains zero bounding boxes.\n\n        \"\"\"\n        return len(self.bounding_boxes) == 0\n\n    def on_(self, image):\n        \"\"\"Project BBs from one image (shape) to a another one in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            New image onto which the bounding boxes are to be projected.\n            May also simply be that new image's shape tuple.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Object containing the same bounding boxes after projection to\n            the new image shape.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        # pylint: disable=invalid-name\n        on_shape = normalize_imglike_shape(image)\n        if on_shape[0:2] == self.shape[0:2]:\n            self.shape = on_shape  # channels may differ\n            return self\n\n        for i, item in enumerate(self.items):\n            self.bounding_boxes[i] = item.project_(self.shape, on_shape)\n        self.shape = on_shape\n        return self\n\n    def on(self, image):\n        \"\"\"Project bounding boxes from one image (shape) to a another one.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            New image onto which the bounding boxes are to be projected.\n            May also simply be that new image's shape tuple.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Object containing the same bounding boxes after projection to\n            the new image shape.\n\n        \"\"\"\n        # pylint: disable=invalid-name\n        return self.deepcopy().on_(image)\n\n    @classmethod\n    def from_xyxy_array(cls, xyxy, shape):\n        \"\"\"Convert an ``(N, 4) or (N, 2, 2) ndarray`` to a BBsOI instance.\n\n        This is the inverse of\n        :func:`~imgaug.BoundingBoxesOnImage.to_xyxy_array`.\n\n        Parameters\n        ----------\n        xyxy : (N, 4) ndarray or (N, 2, 2) array\n            Array containing the corner coordinates of ``N`` bounding boxes.\n            Each bounding box is represented by its top-left and bottom-right\n            coordinates.\n            The array should usually be of dtype ``float32``.\n\n        shape : tuple of int\n            Shape of the image on which the bounding boxes are placed.\n            Should usually be ``(H, W, C)`` or ``(H, W)``.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Object containing a list of :class:`BoundingBox` instances\n            derived from the provided corner coordinates.\n\n        \"\"\"\n        # pylint: disable=unsubscriptable-object\n        xyxy = np.array(xyxy, dtype=np.float32)\n\n        # note that np.array([]) is (0,), not (0, 2)\n        if xyxy.shape[0] == 0:\n            return BoundingBoxesOnImage([], shape)\n\n        assert (\n            (xyxy.ndim == 2 and xyxy.shape[-1] == 4)\n            or (xyxy.ndim == 3 and xyxy.shape[1:3] == (2, 2))), (\n                \"Expected input array of shape (N, 4) or (N, 2, 2), \"\n                \"got shape %s.\" % (xyxy.shape,))\n\n        xyxy = xyxy.reshape((-1, 2, 2))\n        boxes = [BoundingBox.from_point_soup(row) for row in xyxy]\n\n        return cls(boxes, shape)\n\n    @classmethod\n    def from_point_soups(cls, xy, shape):\n        \"\"\"Convert an ``(N, 2P) or (N, P, 2) ndarray`` to a BBsOI instance.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        xy : (N, 2P) ndarray or (N, P, 2) array or iterable of iterable of number or iterable of iterable of iterable of number\n            Array containing the corner coordinates of ``N`` bounding boxes.\n            Each bounding box is represented by a soup of ``P`` points.\n            If ``(N, P)`` then the second axis is expected to be in\n            xy-form (e.g. ``x1``, ``y1``, ``x2``, ``y2``, ...).\n            The final bounding box coordinates will be derived using ``min``\n            and ``max`` operations on the xy-values.\n            The array should usually be of dtype ``float32``.\n\n        shape : tuple of int\n            Shape of the image on which the bounding boxes are placed.\n            Should usually be ``(H, W, C)`` or ``(H, W)``.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Object containing a list of :class:`BoundingBox` instances\n            derived from the provided point soups.\n\n        \"\"\"\n        xy = np.array(xy, dtype=np.float32)\n\n        # from_xy_array() already checks the ndim/shape, so we don't have to\n        # do it here\n        boxes = [BoundingBox.from_point_soup(row) for row in xy]\n\n        return cls(boxes, shape)\n\n    def to_xyxy_array(self, dtype=np.float32):\n        \"\"\"Convert the ``BoundingBoxesOnImage`` object to an ``(N,4) ndarray``.\n\n        This is the inverse of\n        :func:`~imgaug.BoundingBoxesOnImage.from_xyxy_array`.\n\n        Parameters\n        ----------\n        dtype : numpy.dtype, optional\n            Desired output datatype of the ndarray.\n\n        Returns\n        -------\n        ndarray\n            ``(N,4) ndarray``, where ``N`` denotes the number of bounding\n            boxes and ``4`` denotes the top-left and bottom-right bounding\n            box corner coordinates in form ``(x1, y1, x2, y2)``.\n\n        \"\"\"\n        xyxy_array = np.zeros((len(self.bounding_boxes), 4), dtype=np.float32)\n\n        for i, box in enumerate(self.bounding_boxes):\n            xyxy_array[i] = [box.x1, box.y1, box.x2, box.y2]\n\n        return xyxy_array.astype(dtype)\n\n    def to_xy_array(self):\n        \"\"\"Convert the ``BoundingBoxesOnImage`` object to an ``(N,2) ndarray``.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        ndarray\n            ``(2*B,2) ndarray`` of xy-coordinates, where ``B`` denotes the\n            number of bounding boxes.\n\n        \"\"\"\n        return self.to_xyxy_array().reshape((-1, 2))\n\n    def fill_from_xyxy_array_(self, xyxy):\n        \"\"\"Modify the BB coordinates of this instance in-place.\n\n        .. note::\n\n            This currently expects exactly one entry in `xyxy` per bounding\n            in this instance. (I.e. two corner coordinates per instance.)\n            Otherwise, an ``AssertionError`` will be raised.\n\n        .. note::\n\n            This method will automatically flip x-coordinates if ``x1>x2``\n            for a bounding box. (Analogous for y-coordinates.)\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        xyxy : (N, 4) ndarray or iterable of iterable of number\n            Coordinates of ``N`` bounding boxes on an image, given as\n            a ``(N,4)`` array of two corner xy-coordinates per bounding box.\n            ``N`` must match the number of bounding boxes in this instance.\n\n        Returns\n        -------\n        BoundingBoxesOnImage\n            This instance itself, with updated bounding box coordinates.\n            Note that the instance was modified in-place.\n\n        \"\"\"\n        xyxy = np.array(xyxy, dtype=np.float32)\n\n        # note that np.array([]) is (0,), not (0, 4)\n        assert xyxy.shape[0] == 0 or (xyxy.ndim == 2 and xyxy.shape[-1] == 4), (  # pylint: disable=unsubscriptable-object\n            \"Expected input array to have shape (N,4), \"\n            \"got shape %s.\" % (xyxy.shape,))\n\n        assert len(xyxy) == len(self.bounding_boxes), (\n            \"Expected to receive an array with as many rows there are \"\n            \"bounding boxes in this instance. Got %d rows, expected %d.\" % (\n                len(xyxy), len(self.bounding_boxes)))\n\n        for bb, (x1, y1, x2, y2) in zip(self.bounding_boxes, xyxy):\n            bb.x1 = min([x1, x2])\n            bb.y1 = min([y1, y2])\n            bb.x2 = max([x1, x2])\n            bb.y2 = max([y1, y2])\n\n        return self\n\n    def fill_from_xy_array_(self, xy):\n        \"\"\"Modify the BB coordinates of this instance in-place.\n\n        See\n        :func:`~imgaug.augmentables.bbs.BoundingBoxesOnImage.fill_from_xyxy_array_`.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        xy : (2*B, 2) ndarray or iterable of iterable of number\n            Coordinates of ``B`` bounding boxes on an image, given as\n            a ``(2*B,2)`` array of two corner xy-coordinates per bounding box.\n            ``B`` must match the number of bounding boxes in this instance.\n\n        Returns\n        -------\n        BoundingBoxesOnImage\n            This instance itself, with updated bounding box coordinates.\n            Note that the instance was modified in-place.\n\n        \"\"\"\n        xy = np.array(xy, dtype=np.float32)\n        return self.fill_from_xyxy_array_(xy.reshape((-1, 4)))\n\n    def draw_on_image(self, image, color=(0, 255, 0), alpha=1.0, size=1,\n                      copy=True, raise_if_out_of_image=False, thickness=None):\n        \"\"\"Draw all bounding boxes onto a given image.\n\n        Parameters\n        ----------\n        image : (H,W,3) ndarray\n            The image onto which to draw the bounding boxes.\n            This image should usually have the same shape as set in\n            ``BoundingBoxesOnImage.shape``.\n\n        color : int or list of int or tuple of int or (3,) ndarray, optional\n            The RGB color of all bounding boxes.\n            If a single ``int`` ``C``, then that is equivalent to ``(C,C,C)``.\n\n        alpha : float, optional\n            Alpha/transparency of the bounding box.\n\n        size : int, optional\n            Thickness in pixels.\n\n        copy : bool, optional\n            Whether to copy the image before drawing the bounding boxes.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an exception if any bounding box is outside of the\n            image.\n\n        thickness : None or int, optional\n            Deprecated.\n\n        Returns\n        -------\n        (H,W,3) ndarray\n            Image with drawn bounding boxes.\n\n        \"\"\"\n        # pylint: disable=redefined-outer-name\n        image = np.copy(image) if copy else image\n\n        for bb in self.bounding_boxes:\n            image = bb.draw_on_image(\n                image,\n                color=color,\n                alpha=alpha,\n                size=size,\n                copy=False,\n                raise_if_out_of_image=raise_if_out_of_image,\n                thickness=thickness\n            )\n\n        return image\n\n    def remove_out_of_image_(self, fully=True, partly=False):\n        \"\"\"Remove in-place all BBs that are fully/partially outside of the image.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        fully : bool, optional\n            Whether to remove bounding boxes that are fully outside of the\n            image.\n\n        partly : bool, optional\n            Whether to remove bounding boxes that are partially outside of\n            the image.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Reduced set of bounding boxes, with those that were\n            fully/partially outside of the image being removed.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        self.bounding_boxes = [\n            bb\n            for bb\n            in self.bounding_boxes\n            if not bb.is_out_of_image(self.shape, fully=fully, partly=partly)]\n        return self\n\n    def remove_out_of_image(self, fully=True, partly=False):\n        \"\"\"Remove all BBs that are fully/partially outside of the image.\n\n        Parameters\n        ----------\n        fully : bool, optional\n            Whether to remove bounding boxes that are fully outside of the\n            image.\n\n        partly : bool, optional\n            Whether to remove bounding boxes that are partially outside of\n            the image.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Reduced set of bounding boxes, with those that were\n            fully/partially outside of the image being removed.\n\n        \"\"\"\n        return self.copy().remove_out_of_image_(fully=fully, partly=partly)\n\n    def remove_out_of_image_fraction_(self, fraction):\n        \"\"\"Remove in-place all BBs with an OOI fraction of at least `fraction`.\n\n        'OOI' is the abbreviation for 'out of image'.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        fraction : number\n            Minimum out of image fraction that a bounding box has to have in\n            order to be removed. A fraction of ``1.0`` removes only bounding\n            boxes that are ``100%`` outside of the image. A fraction of ``0.0``\n            removes all bounding boxes.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Reduced set of bounding boxes, with those that had an out of image\n            fraction greater or equal the given one removed.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        return _remove_out_of_image_fraction_(self, fraction)\n\n    def remove_out_of_image_fraction(self, fraction):\n        \"\"\"Remove all BBs with an out of image fraction of at least `fraction`.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        fraction : number\n            Minimum out of image fraction that a bounding box has to have in\n            order to be removed. A fraction of ``1.0`` removes only bounding\n            boxes that are ``100%`` outside of the image. A fraction of ``0.0``\n            removes all bounding boxes.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Reduced set of bounding boxes, with those that had an out of image\n            fraction greater or equal the given one removed.\n\n        \"\"\"\n        return self.copy().remove_out_of_image_fraction_(fraction)\n\n    @ia.deprecated(alt_func=\"BoundingBoxesOnImage.clip_out_of_image()\",\n                   comment=\"clip_out_of_image() has the exactly same \"\n                           \"interface.\")\n    def cut_out_of_image(self):\n        \"\"\"Clip off all parts from all BBs that are outside of the image.\"\"\"\n        return self.clip_out_of_image()\n\n    def clip_out_of_image_(self):\n        \"\"\"\n        Clip off in-place all parts from all BBs that are outside of the image.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Bounding boxes, clipped to fall within the image dimensions.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        # remove bbs that are not at least partially inside the image plane\n        self.bounding_boxes = [bb for bb in self.bounding_boxes\n                               if bb.is_partly_within_image(self.shape)]\n\n        for i, bb in enumerate(self.bounding_boxes):\n            self.bounding_boxes[i] = bb.clip_out_of_image(self.shape)\n\n        return self\n\n    def clip_out_of_image(self):\n        \"\"\"Clip off all parts from all BBs that are outside of the image.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Bounding boxes, clipped to fall within the image dimensions.\n\n        \"\"\"\n        return self.deepcopy().clip_out_of_image_()\n\n    def shift_(self, x=0, y=0):\n        \"\"\"Move all BBs along the x/y-axis in-place.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Shifted bounding boxes.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        for i, bb in enumerate(self.bounding_boxes):\n            self.bounding_boxes[i] = bb.shift_(x=x, y=y)\n        return self\n\n    def shift(self, x=0, y=0, top=None, right=None, bottom=None, left=None):\n        \"\"\"Move all BBs along the x/y-axis.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        top : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            top (towards the bottom).\n\n        right : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            right (towads the left).\n\n        bottom : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            bottom (towards the top).\n\n        left : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            left (towards the right).\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Shifted bounding boxes.\n\n        \"\"\"\n        x, y = _normalize_shift_args(\n            x, y, top=top, right=right, bottom=bottom, left=left)\n        return self.deepcopy().shift_(x=x, y=y)\n\n    def to_keypoints_on_image(self):\n        \"\"\"Convert the bounding boxes to one ``KeypointsOnImage`` instance.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            A keypoints instance containing ``N*4`` coordinates for ``N``\n            bounding boxes. Order matches the order in ``bounding_boxes``.\n\n        \"\"\"\n        from .kps import KeypointsOnImage\n\n        # This currently uses 4 points instead of 2 points as the method\n        # is primarily used during augmentation and 4 points are overall\n        # the better choice there.\n        arr = np.zeros((len(self.bounding_boxes), 2*4), dtype=np.float32)\n\n        for i, box in enumerate(self.bounding_boxes):\n            arr[i] = [\n                box.x1, box.y1,\n                box.x2, box.y1,\n                box.x2, box.y2,\n                box.x1, box.y2\n            ]\n\n        return KeypointsOnImage.from_xy_array(\n            arr.reshape((-1, 2)),\n            shape=self.shape\n        )\n\n    def invert_to_keypoints_on_image_(self, kpsoi):\n        \"\"\"Invert the output of ``to_keypoints_on_image()`` in-place.\n\n        This function writes in-place into this ``BoundingBoxesOnImage``\n        instance.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        kpsoi : imgaug.augmentables.kps.KeypointsOnImages\n            Keypoints to convert back to bounding boxes, i.e. the outputs\n            of ``to_keypoints_on_image()``.\n\n        Returns\n        -------\n        BoundingBoxesOnImage\n            Bounding boxes container with updated coordinates.\n            Note that the instance is also updated in-place.\n\n        \"\"\"\n        assert len(kpsoi.keypoints) == len(self.bounding_boxes) * 4, (\n            \"Expected %d coordinates, got %d.\" % (\n                len(self.bounding_boxes) * 2, len(kpsoi.keypoints)))\n        for i, bb in enumerate(self.bounding_boxes):\n            xx = [kpsoi.keypoints[4*i+0].x, kpsoi.keypoints[4*i+1].x,\n                  kpsoi.keypoints[4*i+2].x, kpsoi.keypoints[4*i+3].x]\n            yy = [kpsoi.keypoints[4*i+0].y, kpsoi.keypoints[4*i+1].y,\n                  kpsoi.keypoints[4*i+2].y, kpsoi.keypoints[4*i+3].y]\n            bb.x1 = min(xx)\n            bb.y1 = min(yy)\n            bb.x2 = max(xx)\n            bb.y2 = max(yy)\n        self.shape = kpsoi.shape\n        return self\n\n    def to_polygons_on_image(self):\n        \"\"\"Convert the bounding boxes to one ``PolygonsOnImage`` instance.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            A ``PolygonsOnImage`` containing polygons. Each polygon covers\n            the same area as the corresponding bounding box.\n\n        \"\"\"\n        from .polys import PolygonsOnImage\n\n        polygons = [bb.to_polygon() for bb in self.bounding_boxes]\n        return PolygonsOnImage(polygons, shape=self.shape)\n\n    def copy(self, bounding_boxes=None, shape=None):\n        \"\"\"Create a shallow copy of the ``BoundingBoxesOnImage`` instance.\n\n        Parameters\n        ----------\n        bounding_boxes : None or list of imgaug.augmntables.bbs.BoundingBox, optional\n            List of bounding boxes on the image.\n            If ``None``, the instance's bounding boxes will be copied.\n\n        shape : tuple of int, optional\n            The shape of the image on which the bounding boxes are placed.\n            If ``None``, the instance's shape will be copied.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Shallow copy.\n\n        \"\"\"\n        if bounding_boxes is None:\n            bounding_boxes = self.bounding_boxes[:]\n        if shape is None:\n            # use tuple() here in case the shape was provided as a list\n            shape = tuple(self.shape)\n\n        return BoundingBoxesOnImage(bounding_boxes, shape)\n\n    def deepcopy(self, bounding_boxes=None, shape=None):\n        \"\"\"Create a deep copy of the ``BoundingBoxesOnImage`` object.\n\n        Parameters\n        ----------\n        bounding_boxes : None or list of imgaug.augmntables.bbs.BoundingBox, optional\n            List of bounding boxes on the image.\n            If ``None``, the instance's bounding boxes will be copied.\n\n        shape : tuple of int, optional\n            The shape of the image on which the bounding boxes are placed.\n            If ``None``, the instance's shape will be copied.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Deep copy.\n\n        \"\"\"\n        # Manual copy is far faster than deepcopy, so use manual copy here.\n        if bounding_boxes is None:\n            bounding_boxes = [bb.deepcopy() for bb in self.bounding_boxes]\n        if shape is None:\n            # use tuple() here in case the shape was provided as a list\n            shape = tuple(self.shape)\n\n        return BoundingBoxesOnImage(bounding_boxes, shape)\n\n    def __getitem__(self, indices):\n        \"\"\"Get the bounding box(es) with given indices.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of imgaug.augmentables.bbs.BoundingBoxes\n            Bounding box(es) with given indices.\n\n        \"\"\"\n        return self.bounding_boxes[indices]\n\n    def __iter__(self):\n        \"\"\"Iterate over the bounding boxes in this container.\n\n        Added in 0.4.0.\n\n        Yields\n        ------\n        BoundingBox\n            A bounding box in this container.\n            The order is identical to the order in the bounding box list\n            provided upon class initialization.\n\n        \"\"\"\n        return iter(self.bounding_boxes)\n\n    def __len__(self):\n        \"\"\"Get the number of items in this instance.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        int\n            Number of items in this instance.\n\n        \"\"\"\n        return len(self.items)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return (\n            \"BoundingBoxesOnImage(%s, shape=%s)\"\n            % (str(self.bounding_boxes), self.shape))\n\n\nclass _LabelOnImageDrawer(object):\n    # size refers to the thickness of the BB\n    # height is the height of the label rectangle, not the whole BB\n    def __init__(self, color=(0, 255, 0), color_text=None, color_bg=None,\n                 size=1, alpha=1.0, raise_if_out_of_image=False,\n                 height=30, size_text=20):\n        self.color = color\n        self.color_text = color_text\n        self.color_bg = color_bg\n        self.size = size\n        self.alpha = alpha\n        self.raise_if_out_of_image = raise_if_out_of_image\n        self.height = height\n        self.size_text = size_text\n\n    def draw_on_image_(self, image, bounding_box):\n        # pylint: disable=invalid-name, redefined-outer-name\n        if self.raise_if_out_of_image:\n            self._do_raise_if_out_of_image(image, bounding_box)\n        color_text, color_bg = self._preprocess_colors()\n        x1, y1, x2, y2 = self._compute_bg_corner_coords(image, bounding_box)\n\n        # cant draw anything if OOI\n        if x2 <= x1 or y2 <= y1:\n            return image\n\n        # can currently only draw on images with shape (H,W,C), not (H,W)\n        label_arr = self._draw_label_arr(bounding_box.label,\n                                         y2 - y1, x2 - x1, image.shape[-1],\n                                         image.dtype,\n                                         color_text, color_bg,\n                                         self.size_text)\n\n        image = self._blend_label_arr_with_image_(image, label_arr,\n                                                  x1, y1, x2, y2)\n        return image\n\n    def draw_on_image(self, image, bounding_box):\n        return self.draw_on_image_(np.copy(image), bounding_box)\n\n    @classmethod\n    def _do_raise_if_out_of_image(cls, image, bounding_box):\n        if bounding_box.is_out_of_image(image):\n            raise Exception(\n                \"Cannot draw bounding box x1=%.8f, y1=%.8f, x2=%.8f, y2=%.8f \"\n                \"on image with shape %s.\" % (\n                    bounding_box.x1, bounding_box.y1,\n                    bounding_box.x2, bounding_box.y2,\n                    image.shape))\n\n    def _preprocess_colors(self):\n        color = np.uint8(self.color) if self.color is not None else None\n\n        color_bg = self.color_bg\n        if self.color_bg is not None:\n            color_bg = np.uint8(color_bg)\n        else:\n            assert color is not None, (\n                \"Expected `color` to be set when `color_bg` is not set, \"\n                \"but it was None.\")\n            color_bg = color\n\n        color_text = self.color_text\n        if self.color_text is not None:\n            color_text = np.uint8(color_text)\n        else:\n            # we follow the approach from https://stackoverflow.com/a/1855903\n            # here\n            gray = (0.299 * color_bg[0]\n                    + 0.587 * color_bg[1]\n                    + 0.114 * color_bg[2])\n            color_text = np.full((3,),\n                                 0 if gray > 128 else 255,\n                                 dtype=np.uint8)\n\n        return color_text, color_bg\n\n    def _compute_bg_corner_coords(self, image, bounding_box):\n        bb = bounding_box\n        offset = self.size\n        height, width = image.shape[0:2]\n\n        y1, x1, x2 = bb.y1_int, bb.x1_int, bb.x2_int\n\n        # dont use bb.y2 here! we want the label to be above the BB\n        y1 = y1 - 1 - self.height\n        y2 = y1 + self.height\n\n        x1 = x1 - offset + 1\n        x2 = x2 + offset\n\n        y1, y2 = np.clip([y1, y2], 0, height-1)\n        x1, x2 = np.clip([x1, x2], 0, width-1)\n\n        return x1, y1, x2, y2\n\n    @classmethod\n    def _draw_label_arr(cls, label, height, width, nb_channels, dtype,\n                        color_text, color_bg, size_text):\n        label_arr = np.zeros((height, width, nb_channels), dtype=dtype)\n        label_arr[...] = color_bg.reshape((1, 1, -1))\n        label_arr = ia.draw_text(label_arr,\n                                 x=2, y=2,\n                                 text=str(label),\n                                 color=color_text,\n                                 size=size_text)\n        return label_arr\n\n    def _blend_label_arr_with_image_(self, image, label_arr, x1, y1, x2, y2):\n        alpha = self.alpha\n        if alpha >= 0.99:\n            image[y1:y2, x1:x2, :] = label_arr\n        else:\n            input_dtype = image.dtype\n            foreground = label_arr.astype(np.float64)\n            background = image[y1:y2, x1:x2, :].astype(np.float64)\n            blend = (1 - alpha) * background + alpha * foreground\n            blend = np.clip(blend, 0, 255).astype(input_dtype)\n            image[y1:y2, x1:x2, :] = blend\n        return image\n"
  },
  {
    "path": "imgaug/augmentables/heatmaps.py",
    "content": "\"\"\"Classes to represent heatmaps, i.e. float arrays of ``[0.0, 1.0]``.\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport numpy as np\nimport six.moves as sm\n\nfrom .. import imgaug as ia\nfrom .base import IAugmentable\n\n\nclass HeatmapsOnImage(IAugmentable):\n    \"\"\"Object representing heatmaps on a single image.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Array representing the heatmap(s) on a single image.\n        Multiple heatmaps may be provided, in which case ``C`` is expected to\n        denote the heatmap index.\n        The array must be of dtype ``float32``.\n\n    shape : tuple of int\n        Shape of the image on which the heatmap(s) is/are placed.\n        **Not** the shape of the heatmap(s) array, unless it is identical\n        to the image shape (note the likely difference between the arrays\n        in the number of channels).\n        This is expected to be ``(H, W)`` or ``(H, W, C)`` with ``C`` usually\n        being ``3``.\n        If there is no corresponding image, use ``(H_arr, W_arr)`` instead,\n        where ``H_arr`` is the height of the heatmap(s) array\n        (analogous ``W_arr``).\n\n    min_value : float, optional\n        Minimum value for the heatmaps that `arr` represents. This will\n        usually be ``0.0``.\n\n    max_value : float, optional\n        Maximum value for the heatmaps that `arr` represents. This will\n        usually be ``1.0``.\n\n    \"\"\"\n\n    def __init__(self, arr, shape, min_value=0.0, max_value=1.0):\n        \"\"\"Construct a new HeatmapsOnImage object.\"\"\"\n        assert ia.is_np_array(arr), (\n            \"Expected numpy array as heatmap input array, \"\n            \"got type %s\" % (type(arr),))\n        # TODO maybe allow 0-sized heatmaps? in that case the min() and max()\n        #      must be adjusted\n        assert arr.shape[0] > 0 and arr.shape[1] > 0, (\n            \"Expected numpy array as heatmap with height and width greater \"\n            \"than 0, got shape %s.\" % (arr.shape,))\n        assert arr.dtype.name in [\"float32\"], (\n            \"Heatmap input array expected to be of dtype float32, \"\n            \"got dtype %s.\" % (arr.dtype,))\n        assert arr.ndim in [2, 3], (\n            \"Heatmap input array must be 2d or 3d, got shape %s.\" % (\n                arr.shape,))\n        assert len(shape) in [2, 3], (\n            \"Argument 'shape' in HeatmapsOnImage expected to be 2d or 3d, \"\n            \"got shape %s.\" % (shape,))\n        assert min_value < max_value, (\n            \"Expected min_value to be lower than max_value, \"\n            \"got %.4f and %.4f\" % (min_value, max_value))\n\n        eps = np.finfo(arr.dtype).eps\n        components = arr.flat[0:50]\n        beyond_min = np.min(components) < min_value - eps\n        beyond_max = np.max(components) > max_value + eps\n        if beyond_min or beyond_max:\n            ia.warn(\n                \"Value range of heatmap was chosen to be (%.8f, %.8f), but \"\n                \"found actual min/max of (%.8f, %.8f). Array will be \"\n                \"clipped to chosen value range.\" % (\n                    min_value, max_value, np.min(arr), np.max(arr)))\n            arr = np.clip(arr, min_value, max_value)\n\n        if arr.ndim == 2:\n            arr = arr[..., np.newaxis]\n            self.arr_was_2d = True\n        else:\n            self.arr_was_2d = False\n\n        min_is_zero = 0.0 - eps < min_value < 0.0 + eps\n        max_is_one = 1.0 - eps < max_value < 1.0 + eps\n        if min_is_zero and max_is_one:\n            self.arr_0to1 = arr\n        else:\n            self.arr_0to1 = (arr - min_value) / (max_value - min_value)\n\n        self.shape = shape\n\n        self.min_value = min_value\n        self.max_value = max_value\n\n    def get_arr(self):\n        \"\"\"Get the heatmap's array in value range provided to ``__init__()``.\n\n        The :class:`HeatmapsOnImage` object saves heatmaps internally in the\n        value range ``[0.0, 1.0]``. This function converts the internal\n        representation to ``[min, max]``, where ``min`` and ``max`` are\n        provided to :func:`HeatmapsOnImage.__init__` upon instantiation of\n        the object.\n\n        Returns\n        -------\n        (H,W) ndarray or (H,W,C) ndarray\n            Heatmap array of dtype ``float32``.\n\n        \"\"\"\n        if self.arr_was_2d and self.arr_0to1.shape[2] == 1:\n            arr = self.arr_0to1[:, :, 0]\n        else:\n            arr = self.arr_0to1\n\n        eps = np.finfo(np.float32).eps\n        min_is_zero = 0.0 - eps < self.min_value < 0.0 + eps\n        max_is_one = 1.0 - eps < self.max_value < 1.0 + eps\n        if min_is_zero and max_is_one:\n            return np.copy(arr)\n\n        diff = self.max_value - self.min_value\n        return self.min_value + diff * arr\n\n    # TODO\n    # def find_global_maxima(self):\n    #    raise NotImplementedError()\n\n    def draw(self, size=None, cmap=\"jet\"):\n        \"\"\"Render the heatmaps as RGB images.\n\n        Parameters\n        ----------\n        size : None or float or iterable of int or iterable of float, optional\n            Size of the rendered RGB image as ``(height, width)``.\n            See :func:`~imgaug.imgaug.imresize_single_image` for details.\n            If set to ``None``, no resizing is performed and the size of the\n            heatmaps array is used.\n\n        cmap : str or None, optional\n            Name of the ``matplotlib`` color map to use when convert the\n            heatmaps to RGB images.\n            If set to ``None``, no color map will be used and the heatmaps\n            will be converted to simple intensity maps.\n\n        Returns\n        -------\n        list of (H,W,3) ndarray\n            Rendered heatmaps as ``uint8`` arrays.\n            Always a **list** containing one RGB image per heatmap array\n            channel.\n\n        \"\"\"\n        heatmaps_uint8 = self.to_uint8()\n        heatmaps_drawn = []\n\n        for c in sm.xrange(heatmaps_uint8.shape[2]):\n            # We use c:c+1 here to get a (H,W,1) array. Otherwise imresize\n            # would have to re-attach an axis.\n            heatmap_c = heatmaps_uint8[..., c:c+1]\n\n            if size is not None:\n                heatmap_c_rs = ia.imresize_single_image(\n                    heatmap_c, size, interpolation=\"nearest\")\n            else:\n                heatmap_c_rs = heatmap_c\n            heatmap_c_rs = np.squeeze(heatmap_c_rs).astype(np.float32) / 255.0\n\n            if cmap is not None:\n                # import only when necessary (faster startup; optional\n                # dependency; less fragile -- see issue #225)\n                import matplotlib.pyplot as plt\n\n                cmap_func = plt.get_cmap(cmap)\n                heatmap_cmapped = cmap_func(heatmap_c_rs)\n                heatmap_cmapped = np.delete(heatmap_cmapped, 3, 2)\n            else:\n                heatmap_cmapped = np.tile(\n                    heatmap_c_rs[..., np.newaxis], (1, 1, 3))\n\n            heatmap_cmapped = np.clip(\n                heatmap_cmapped * 255, 0, 255).astype(np.uint8)\n\n            heatmaps_drawn.append(heatmap_cmapped)\n        return heatmaps_drawn\n\n    def draw_on_image(self, image, alpha=0.75, cmap=\"jet\", resize=\"heatmaps\"):\n        \"\"\"Draw the heatmaps as overlays over an image.\n\n        Parameters\n        ----------\n        image : (H,W,3) ndarray\n            Image onto which to draw the heatmaps.\n            Expected to be of dtype ``uint8``.\n\n        alpha : float, optional\n            Alpha/opacity value to use for the mixing of image and heatmaps.\n            Larger values mean that the heatmaps will be more visible and the\n            image less visible.\n\n        cmap : str or None, optional\n            Name of the ``matplotlib`` color map to use.\n            See :func:`HeatmapsOnImage.draw` for details.\n\n        resize : {'heatmaps', 'image'}, optional\n            In case of size differences between the image and heatmaps,\n            either the image or the heatmaps can be resized. This parameter\n            controls which of the two will be resized to the other's size.\n\n        Returns\n        -------\n        list of (H,W,3) ndarray\n            Rendered overlays as ``uint8`` arrays.\n            Always a **list** containing one RGB image per heatmap array\n            channel.\n\n        \"\"\"\n        # assert RGB image\n        assert image.ndim == 3, (\n            \"Expected to draw on three-dimensional image, \"\n            \"got %d dimensions with shape %s instead.\" % (\n                image.ndim, image.shape))\n        assert image.shape[2] == 3, (\n            \"Expected RGB image, got %d channels instead.\" % (image.shape[2],))\n        assert image.dtype.name == \"uint8\", (\n            \"Expected uint8 image, got dtype %s.\" % (image.dtype.name,))\n\n        assert 0 - 1e-8 <= alpha <= 1.0 + 1e-8, (\n            \"Expected 'alpha' to be in the interval [0.0, 1.0], got %.4f\" % (\n                alpha))\n        assert resize in [\"heatmaps\", \"image\"], (\n            \"Expected resize to be \\\"heatmaps\\\" or \\\"image\\\", \"\n            \"got %s instead.\" % (resize,))\n\n        if resize == \"image\":\n            image = ia.imresize_single_image(\n                image, self.arr_0to1.shape[0:2], interpolation=\"cubic\")\n\n        heatmaps_drawn = self.draw(\n            size=image.shape[0:2] if resize == \"heatmaps\" else None,\n            cmap=cmap)\n\n        # TODO use blend_alpha here\n        mix = [\n            np.clip(\n                (1-alpha) * image + alpha * heatmap_i,\n                0, 255\n            ).astype(np.uint8)\n            for heatmap_i\n            in heatmaps_drawn]\n\n        return mix\n\n    def invert(self):\n        \"\"\"Invert each component in the heatmap.\n\n        This shifts low values towards high values and vice versa.\n\n        This changes each value to::\n\n            v' = max - (v - min)\n\n        where ``v`` is the value at a spatial location, ``min`` is the\n        minimum value in the heatmap and ``max`` is the maximum value.\n        As the heatmap uses internally a ``0.0`` to ``1.0`` representation,\n        this simply becomes ``v' = 1.0 - v``.\n\n        This function can be useful e.g. when working with depth maps, where\n        algorithms might have an easier time representing the furthest away\n        points with zeros, requiring an inverted depth map.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Inverted heatmap.\n\n        \"\"\"\n        arr_inv = HeatmapsOnImage.from_0to1(\n            1 - self.arr_0to1,\n            shape=self.shape,\n            min_value=self.min_value,\n            max_value=self.max_value)\n        arr_inv.arr_was_2d = self.arr_was_2d\n        return arr_inv\n\n    def pad(self, top=0, right=0, bottom=0, left=0, mode=\"constant\", cval=0.0):\n        \"\"\"Pad the heatmaps at their top/right/bottom/left side.\n\n        Parameters\n        ----------\n        top : int, optional\n            Amount of pixels to add at the top side of the heatmaps.\n            Must be ``0`` or greater.\n\n        right : int, optional\n            Amount of pixels to add at the right side of the heatmaps.\n            Must be ``0`` or greater.\n\n        bottom : int, optional\n            Amount of pixels to add at the bottom side of the heatmaps.\n            Must be ``0`` or greater.\n\n        left : int, optional\n            Amount of pixels to add at the left side of the heatmaps.\n            Must be ``0`` or greater.\n\n        mode : string, optional\n            Padding mode to use. See :func:`~imgaug.imgaug.pad` for details.\n\n        cval : number, optional\n            Value to use for padding `mode` is ``constant``.\n            See :func:`~imgaug.imgaug.pad` for details.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Padded heatmaps of height ``H'=H+top+bottom`` and\n            width ``W'=W+left+right``.\n\n        \"\"\"\n        from ..augmenters import size as iasize\n        arr_0to1_padded = iasize.pad(\n            self.arr_0to1,\n            top=top,\n            right=right,\n            bottom=bottom,\n            left=left,\n            mode=mode,\n            cval=cval)\n        # TODO change to deepcopy()\n        return HeatmapsOnImage.from_0to1(\n            arr_0to1_padded,\n            shape=self.shape,\n            min_value=self.min_value,\n            max_value=self.max_value)\n\n    def pad_to_aspect_ratio(self, aspect_ratio, mode=\"constant\", cval=0.0,\n                            return_pad_amounts=False):\n        \"\"\"Pad the heatmaps until they match a target aspect ratio.\n\n        Depending on which dimension is smaller (height or width), only the\n        corresponding sides (left/right or top/bottom) will be padded. In\n        each case, both of the sides will be padded equally.\n\n        Parameters\n        ----------\n        aspect_ratio : float\n            Target aspect ratio, given as width/height. E.g. ``2.0`` denotes\n            the image having twice as much width as height.\n\n        mode : str, optional\n            Padding mode to use.\n            See :func:`~imgaug.imgaug.pad` for details.\n\n        cval : number, optional\n            Value to use for padding if `mode` is ``constant``.\n            See :func:`~imgaug.imgaug.pad` for details.\n\n        return_pad_amounts : bool, optional\n            If ``False``, then only the padded instance will be returned.\n            If ``True``, a tuple with two entries will be returned, where\n            the first entry is the padded instance and the second entry are\n            the amounts by which each array side was padded. These amounts are\n            again a tuple of the form ``(top, right, bottom, left)``, with\n            each value being an integer.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Padded heatmaps as :class:`HeatmapsOnImage` instance.\n\n        tuple of int\n            Amounts by which the instance's array was padded on each side,\n            given as a tuple ``(top, right, bottom, left)``.\n            This tuple is only returned if `return_pad_amounts` was set to\n            ``True``.\n\n        \"\"\"\n        from ..augmenters import size as iasize\n        arr_0to1_padded, pad_amounts = iasize.pad_to_aspect_ratio(\n            self.arr_0to1,\n            aspect_ratio=aspect_ratio,\n            mode=mode,\n            cval=cval,\n            return_pad_amounts=True)\n        # TODO change to deepcopy()\n        heatmaps = HeatmapsOnImage.from_0to1(\n            arr_0to1_padded,\n            shape=self.shape,\n            min_value=self.min_value,\n            max_value=self.max_value)\n        if return_pad_amounts:\n            return heatmaps, pad_amounts\n        return heatmaps\n\n    def avg_pool(self, block_size):\n        \"\"\"Average-pool the heatmap(s) array using a given block/kernel size.\n\n        Parameters\n        ----------\n        block_size : int or tuple of int\n            Size of each block of values to pool, aka kernel size.\n            See :func:`~imgaug.imgaug.pool` for details.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Heatmaps after average pooling.\n\n        \"\"\"\n        arr_0to1_reduced = ia.avg_pool(self.arr_0to1, block_size, pad_cval=0.0)\n        return HeatmapsOnImage.from_0to1(\n            arr_0to1_reduced,\n            shape=self.shape,\n            min_value=self.min_value,\n            max_value=self.max_value)\n\n    def max_pool(self, block_size):\n        \"\"\"Max-pool the heatmap(s) array using a given block/kernel size.\n\n        Parameters\n        ----------\n        block_size : int or tuple of int\n            Size of each block of values to pool, aka kernel size.\n            See :func:`~imgaug.imgaug.pool` for details.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Heatmaps after max-pooling.\n\n        \"\"\"\n        arr_0to1_reduced = ia.max_pool(self.arr_0to1, block_size)\n        return HeatmapsOnImage.from_0to1(\n            arr_0to1_reduced,\n            shape=self.shape,\n            min_value=self.min_value,\n            max_value=self.max_value)\n\n    @ia.deprecated(alt_func=\"HeatmapsOnImage.resize()\",\n                   comment=\"resize() has the exactly same interface.\")\n    def scale(self, *args, **kwargs):\n        \"\"\"Resize the heatmap(s) array given a target size and interpolation.\"\"\"\n        return self.resize(*args, **kwargs)\n\n    def resize(self, sizes, interpolation=\"cubic\"):\n        \"\"\"Resize the heatmap(s) array given a target size and interpolation.\n\n        Parameters\n        ----------\n        sizes : float or iterable of int or iterable of float\n            New size of the array in ``(height, width)``.\n            See :func:`~imgaug.imgaug.imresize_single_image` for details.\n\n        interpolation : None or str or int, optional\n            The interpolation to use during resize.\n            See :func:`~imgaug.imgaug.imresize_single_image` for details.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Resized heatmaps object.\n\n        \"\"\"\n        arr_0to1_resized = ia.imresize_single_image(\n            self.arr_0to1, sizes, interpolation=interpolation)\n\n        # cubic interpolation can lead to values outside of [0.0, 1.0],\n        # see https://github.com/opencv/opencv/issues/7195\n        # TODO area interpolation too?\n        arr_0to1_resized = np.clip(arr_0to1_resized, 0.0, 1.0)\n\n        return HeatmapsOnImage.from_0to1(\n            arr_0to1_resized,\n            shape=self.shape,\n            min_value=self.min_value,\n            max_value=self.max_value)\n\n    def to_uint8(self):\n        \"\"\"Convert this heatmaps object to an ``uint8`` array.\n\n        Returns\n        -------\n        (H,W,C) ndarray\n            Heatmap as an ``uint8`` array, i.e. with the discrete value\n            range ``[0, 255]``.\n\n        \"\"\"\n        # TODO this always returns (H,W,C), even if input ndarray was\n        #      originally (H,W). Does it make sense here to also return\n        #      (H,W) if self.arr_was_2d?\n        arr_0to255 = np.clip(np.round(self.arr_0to1 * 255), 0, 255)\n        arr_uint8 = arr_0to255.astype(np.uint8)\n        return arr_uint8\n\n    @staticmethod\n    def from_uint8(arr_uint8, shape, min_value=0.0, max_value=1.0):\n        \"\"\"Create a ``float``-based heatmaps object from an ``uint8`` array.\n\n        Parameters\n        ----------\n        arr_uint8 : (H,W) ndarray or (H,W,C) ndarray\n            Heatmap(s) array, where ``H`` is height, ``W`` is width\n            and ``C`` is the number of heatmap channels.\n            Expected dtype is ``uint8``.\n\n        shape : tuple of int\n            Shape of the image on which the heatmap(s) is/are placed.\n            **Not** the shape of the heatmap(s) array, unless it is identical\n            to the image shape (note the likely difference between the arrays\n            in the number of channels).\n            If there is not a corresponding image, use the shape of the\n            heatmaps array.\n\n        min_value : float, optional\n            Minimum value of the float heatmaps that the input array\n            represents. This will usually be 0.0. In most other cases it will\n            be close to the interval ``[0.0, 1.0]``.\n            Calling :func:`~imgaug.HeatmapsOnImage.get_arr`, will automatically\n            convert the interval ``[0.0, 1.0]`` float array to this\n            ``[min, max]`` interval.\n\n        max_value : float, optional\n            Minimum value of the float heatmaps that the input array\n            represents. This will usually be 1.0.\n            See parameter `min_value` for details.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Heatmaps object.\n\n        \"\"\"\n        arr_0to1 = arr_uint8.astype(np.float32) / 255.0\n        return HeatmapsOnImage.from_0to1(\n            arr_0to1, shape,\n            min_value=min_value,\n            max_value=max_value)\n\n    @staticmethod\n    def from_0to1(arr_0to1, shape, min_value=0.0, max_value=1.0):\n        \"\"\"Create a heatmaps object from a ``[0.0, 1.0]`` float array.\n\n        Parameters\n        ----------\n        arr_0to1 : (H,W) or (H,W,C) ndarray\n            Heatmap(s) array, where ``H`` is the height, ``W`` is the width\n            and ``C`` is the number of heatmap channels.\n            Expected dtype is ``float32``.\n\n        shape : tuple of ints\n            Shape of the image on which the heatmap(s) is/are placed.\n            **Not** the shape of the heatmap(s) array, unless it is identical\n            to the image shape (note the likely difference between the arrays\n            in the number of channels).\n            If there is not a corresponding image, use the shape of the\n            heatmaps array.\n\n        min_value : float, optional\n            Minimum value of the float heatmaps that the input array\n            represents. This will usually be 0.0. In most other cases it will\n            be close to the interval ``[0.0, 1.0]``.\n            Calling :func:`~imgaug.HeatmapsOnImage.get_arr`, will automatically\n            convert the interval ``[0.0, 1.0]`` float array to this\n            ``[min, max]`` interval.\n\n        max_value : float, optional\n            Minimum value of the float heatmaps that the input array\n            represents. This will usually be 1.0.\n            See parameter `min_value` for details.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Heatmaps object.\n\n        \"\"\"\n        heatmaps = HeatmapsOnImage(arr_0to1, shape,\n                                   min_value=0.0, max_value=1.0)\n        heatmaps.min_value = min_value\n        heatmaps.max_value = max_value\n        return heatmaps\n\n    # TODO change name to change_value_range()?\n    @classmethod\n    def change_normalization(cls, arr, source, target):\n        \"\"\"Change the value range of a heatmap array.\n\n        E.g. the value range may be changed from the interval ``[0.0, 1.0]``\n        to ``[-1.0, 1.0]``.\n\n        Parameters\n        ----------\n        arr : ndarray\n            Heatmap array to modify.\n\n        source : tuple of float\n            Current value range of the input array, given as a\n            tuple ``(min, max)``, where both are ``float`` values.\n\n        target : tuple of float\n            Desired output value range of the array, given as a\n            tuple ``(min, max)``, where both are ``float`` values.\n\n        Returns\n        -------\n        ndarray\n            Input array, with value range projected to the desired target\n            value range.\n\n        \"\"\"\n        assert ia.is_np_array(arr), (\n            \"Expected 'arr' to be an ndarray, got type %s.\" % (type(arr),))\n\n        def _validate_tuple(arg_name, arg_value):\n            assert isinstance(arg_value, tuple), (\n                \"'%s' was not a HeatmapsOnImage instance, \"\n                \"expected type tuple then. Got type %s.\" % (\n                    arg_name, type(arg_value),))\n            assert len(arg_value) == 2, (\n                \"Expected tuple '%s' to contain exactly two entries, \"\n                \"got %d.\" % (arg_name, len(arg_value),))\n            assert arg_value[0] < arg_value[1], (\n                \"Expected tuple '%s' to have two entries with \"\n                \"entry 1 < entry 2, got values %.4f and %.4f.\" % (\n                    arg_name, arg_value[0], arg_value[1]))\n\n        if isinstance(source, HeatmapsOnImage):\n            source = (source.min_value, source.max_value)\n        else:\n            _validate_tuple(\"source\", source)\n\n        if isinstance(target, HeatmapsOnImage):\n            target = (target.min_value, target.max_value)\n        else:\n            _validate_tuple(\"target\", target)\n\n        # Check if source and target are the same (with a tiny bit of\n        # tolerance) if so, evade compuation and just copy the array instead.\n        # This is reasonable, as source and target will often both\n        # be (0.0, 1.0).\n        eps = np.finfo(arr.dtype).eps\n        mins_same = source[0] - 10*eps < target[0] < source[0] + 10*eps\n        maxs_same = source[1] - 10*eps < target[1] < source[1] + 10*eps\n        if mins_same and maxs_same:\n            return np.copy(arr)\n\n        min_source, max_source = source\n        min_target, max_target = target\n\n        diff_source = max_source - min_source\n        diff_target = max_target - min_target\n\n        arr_0to1 = (arr - min_source) / diff_source\n        arr_target = min_target + arr_0to1 * diff_target\n\n        return arr_target\n\n    # TODO make this a proper shallow-copy\n    def copy(self):\n        \"\"\"Create a shallow copy of the heatmaps object.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Shallow copy.\n\n        \"\"\"\n        return self.deepcopy()\n\n    def deepcopy(self):\n        \"\"\"Create a deep copy of the heatmaps object.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Deep copy.\n\n        \"\"\"\n        return HeatmapsOnImage(\n            self.get_arr(),\n            shape=self.shape,\n            min_value=self.min_value,\n            max_value=self.max_value)\n"
  },
  {
    "path": "imgaug/augmentables/kps.py",
    "content": "\"\"\"Classes to represent keypoints, i.e. points given as xy-coordinates.\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport numpy as np\nimport scipy.spatial.distance\nimport six.moves as sm\n\nfrom .. import imgaug as ia\nfrom .base import IAugmentable\nfrom .utils import (\n    normalize_imglike_shape,\n    project_coords,\n    _remove_out_of_image_fraction_,\n    _handle_on_image_shape\n)\n\n\ndef compute_geometric_median(points=None, eps=1e-5, X=None):\n    \"\"\"Estimate the geometric median of points in 2D.\n\n    Code from https://stackoverflow.com/a/30305181\n\n    Parameters\n    ----------\n    points : (N,2) ndarray\n        Points in 2D. Second axis must be given in xy-form.\n\n    eps : float, optional\n        Distance threshold when to return the median.\n\n    X : None or (N,2) ndarray, optional\n        Deprecated.\n\n    Returns\n    -------\n    (2,) ndarray\n        Geometric median as xy-coordinate.\n\n    \"\"\"\n    # pylint: disable=invalid-name\n    if X is not None:\n        assert points is None\n        ia.warn_deprecated(\"Using 'X' is deprecated, use 'points' instead.\")\n        points = X\n\n    y = np.mean(points, 0)\n\n    while True:\n        dist = scipy.spatial.distance.cdist(points, [y])\n        nonzeros = (dist != 0)[:, 0]\n\n        dist_inv = 1 / dist[nonzeros]\n        dist_inv_sum = np.sum(dist_inv)\n        dist_inv_norm = dist_inv / dist_inv_sum\n        T = np.sum(dist_inv_norm * points[nonzeros], 0)\n\n        num_zeros = len(points) - np.sum(nonzeros)\n        if num_zeros == 0:\n            y1 = T\n        elif num_zeros == len(points):\n            return y\n        else:\n            R = (T - y) * dist_inv_sum\n            r = np.linalg.norm(R)\n            rinv = 0 if r == 0 else num_zeros/r\n            y1 = max(0, 1-rinv)*T + min(1, rinv)*y\n\n        if scipy.spatial.distance.euclidean(y, y1) < eps:\n            return y1\n\n        y = y1\n\n\nclass Keypoint(object):\n    \"\"\"A single keypoint (aka landmark) on an image.\n\n    Parameters\n    ----------\n    x : number\n        Coordinate of the keypoint on the x axis.\n\n    y : number\n        Coordinate of the keypoint on the y axis.\n\n    \"\"\"\n\n    def __init__(self, x, y):\n        self.x = x\n        self.y = y\n\n    @property\n    def coords(self):\n        \"\"\"Get the xy-coordinates as an ``(N,2)`` ndarray.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        ndarray\n            An ``(N, 2)`` ``float32`` ndarray with ``N=1`` containing the\n            coordinates of this keypoints.\n\n        \"\"\"\n        arr = np.empty((1, 2), dtype=np.float32)\n        arr[0, :] = [self.x, self.y]\n        return arr\n\n    @property\n    def x_int(self):\n        \"\"\"Get the keypoint's x-coordinate, rounded to the closest integer.\n\n        Returns\n        -------\n        result : int\n            Keypoint's x-coordinate, rounded to the closest integer.\n\n        \"\"\"\n        return int(np.round(self.x))\n\n    @property\n    def y_int(self):\n        \"\"\"Get the keypoint's y-coordinate, rounded to the closest integer.\n\n        Returns\n        -------\n        result : int\n            Keypoint's y-coordinate, rounded to the closest integer.\n\n        \"\"\"\n        return int(np.round(self.y))\n\n    @property\n    def xy(self):\n        \"\"\"Get the keypoint's x- and y-coordinate as a single array.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        ndarray\n            A ``(2,)`` ``ndarray`` denoting the xy-coordinate pair.\n\n        \"\"\"\n        return self.coords[0, :]\n\n    @property\n    def xy_int(self):\n        \"\"\"Get the keypoint's xy-coord, rounded to closest integer.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        ndarray\n            A ``(2,)`` ``ndarray`` denoting the xy-coordinate pair.\n\n        \"\"\"\n        return np.round(self.xy).astype(np.int32)\n\n    def project_(self, from_shape, to_shape):\n        \"\"\"Project in-place the keypoint onto a new position on a new image.\n\n        E.g. if the keypoint is on its original image\n        at ``x=(10 of 100 pixels)`` and ``y=(20 of 100 pixels)`` and is\n        projected onto a new image with size ``(width=200, height=200)``, its\n        new position will be ``(20, 40)``.\n\n        This is intended for cases where the original image is resized.\n        It cannot be used for more complex changes (e.g. padding, cropping).\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        from_shape : tuple of int\n            Shape of the original image. (Before resize.)\n\n        to_shape : tuple of int\n            Shape of the new image. (After resize.)\n\n        Returns\n        -------\n        imgaug.augmentables.kps.Keypoint\n            Keypoint object with new coordinates.\n            The instance of the keypoint may have been modified in-place.\n\n        \"\"\"\n        xy_proj = project_coords([(self.x, self.y)], from_shape, to_shape)\n        self.x, self.y = xy_proj[0]\n        return self\n\n    def project(self, from_shape, to_shape):\n        \"\"\"Project the keypoint onto a new position on a new image.\n\n        E.g. if the keypoint is on its original image\n        at ``x=(10 of 100 pixels)`` and ``y=(20 of 100 pixels)`` and is\n        projected onto a new image with size ``(width=200, height=200)``, its\n        new position will be ``(20, 40)``.\n\n        This is intended for cases where the original image is resized.\n        It cannot be used for more complex changes (e.g. padding, cropping).\n\n        Parameters\n        ----------\n        from_shape : tuple of int\n            Shape of the original image. (Before resize.)\n\n        to_shape : tuple of int\n            Shape of the new image. (After resize.)\n\n        Returns\n        -------\n        imgaug.augmentables.kps.Keypoint\n            Keypoint object with new coordinates.\n\n        \"\"\"\n        return self.deepcopy().project_(from_shape, to_shape)\n\n    def is_out_of_image(self, image):\n        \"\"\"Estimate whether this point is outside of the given image plane.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape\n            and must contain at least two integers.\n\n        Returns\n        -------\n        bool\n            ``True`` is the point is inside the image plane, ``False``\n            otherwise.\n\n        \"\"\"\n        shape = normalize_imglike_shape(image)\n        height, width = shape[0:2]\n        y_inside = (0 <= self.y < height)\n        x_inside = (0 <= self.x < width)\n        return not y_inside or not x_inside\n\n    def compute_out_of_image_fraction(self, image):\n        \"\"\"Compute fraction of the keypoint that is out of the image plane.\n\n        The fraction is always either ``1.0`` (point is outside of the image\n        plane) or ``0.0`` (point is inside the image plane). This method\n        exists for consistency with other augmentables, e.g. bounding boxes.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape\n            and must contain at least two integers.\n\n        Returns\n        -------\n        float\n            Either ``1.0`` (point is outside of the image plane) or\n            ``0.0`` (point is inside of it).\n\n        \"\"\"\n        return float(self.is_out_of_image(image))\n\n    def shift_(self, x=0, y=0):\n        \"\"\"Move the keypoint around on an image in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        x : number, optional\n            Move by this value on the x axis.\n\n        y : number, optional\n            Move by this value on the y axis.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.Keypoint\n            Keypoint object with new coordinates.\n            The instance of the keypoint may have been modified in-place.\n\n        \"\"\"\n        self.x += x\n        self.y += y\n        return self\n\n    def shift(self, x=0, y=0):\n        \"\"\"Move the keypoint around on an image.\n\n        Parameters\n        ----------\n        x : number, optional\n            Move by this value on the x axis.\n\n        y : number, optional\n            Move by this value on the y axis.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.Keypoint\n            Keypoint object with new coordinates.\n\n        \"\"\"\n        return self.deepcopy().shift_(x, y)\n\n    def draw_on_image(self, image, color=(0, 255, 0), alpha=1.0, size=3,\n                      copy=True, raise_if_out_of_image=False):\n        \"\"\"Draw the keypoint onto a given image.\n\n        The keypoint is drawn as a square.\n\n        Parameters\n        ----------\n        image : (H,W,3) ndarray\n            The image onto which to draw the keypoint.\n\n        color : int or list of int or tuple of int or (3,) ndarray, optional\n            The RGB color of the keypoint.\n            If a single ``int`` ``C``, then that is equivalent to ``(C,C,C)``.\n\n        alpha : float, optional\n            The opacity of the drawn keypoint, where ``1.0`` denotes a fully\n            visible keypoint and ``0.0`` an invisible one.\n\n        size : int, optional\n            The size of the keypoint. If set to ``S``, each square will have\n            size ``S x S``.\n\n        copy : bool, optional\n            Whether to copy the image before drawing the keypoint.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an exception if the keypoint is outside of the\n            image.\n\n        Returns\n        -------\n        image : (H,W,3) ndarray\n            Image with drawn keypoint.\n\n        \"\"\"\n        # pylint: disable=redefined-outer-name\n        if copy:\n            image = np.copy(image)\n\n        if image.ndim == 2:\n            assert ia.is_single_number(color), (\n                \"Got a 2D image. Expected then 'color' to be a single number, \"\n                \"but got %s.\" % (str(color),))\n        elif image.ndim == 3 and ia.is_single_number(color):\n            color = [color] * image.shape[-1]\n\n        input_dtype = image.dtype\n        alpha_color = color\n        if alpha < 0.01:\n            # keypoint invisible, nothing to do\n            return image\n\n        if alpha > 0.99:\n            alpha = 1\n        else:\n            image = image.astype(np.float32, copy=False)\n            alpha_color = alpha * np.array(color)\n\n        height, width = image.shape[0:2]\n\n        y, x = self.y_int, self.x_int\n\n        x1 = max(x - size//2, 0)\n        x2 = min(x + 1 + size//2, width)\n        y1 = max(y - size//2, 0)\n        y2 = min(y + 1 + size//2, height)\n\n        x1_clipped, x2_clipped = np.clip([x1, x2], 0, width)\n        y1_clipped, y2_clipped = np.clip([y1, y2], 0, height)\n\n        x1_clipped_ooi = (x1_clipped < 0 or x1_clipped >= width)\n        x2_clipped_ooi = (x2_clipped < 0 or x2_clipped >= width+1)\n        y1_clipped_ooi = (y1_clipped < 0 or y1_clipped >= height)\n        y2_clipped_ooi = (y2_clipped < 0 or y2_clipped >= height+1)\n        x_ooi = (x1_clipped_ooi and x2_clipped_ooi)\n        y_ooi = (y1_clipped_ooi and y2_clipped_ooi)\n        x_zero_size = (x2_clipped - x1_clipped) < 1  # min size is 1px\n        y_zero_size = (y2_clipped - y1_clipped) < 1\n        if not x_ooi and not y_ooi and not x_zero_size and not y_zero_size:\n            if alpha == 1:\n                image[y1_clipped:y2_clipped, x1_clipped:x2_clipped] = color\n            else:\n                image[y1_clipped:y2_clipped, x1_clipped:x2_clipped] = (\n                    (1 - alpha)\n                    * image[y1_clipped:y2_clipped, x1_clipped:x2_clipped]\n                    + alpha_color\n                )\n        else:\n            if raise_if_out_of_image:\n                raise Exception(\n                    \"Cannot draw keypoint x=%.8f, y=%.8f on image with \"\n                    \"shape %s.\" % (y, x, image.shape))\n\n        if image.dtype.name != input_dtype.name:\n            if input_dtype.name == \"uint8\":\n                image = np.clip(image, 0, 255, out=image)\n            image = image.astype(input_dtype, copy=False)\n        return image\n\n    def generate_similar_points_manhattan(self, nb_steps, step_size,\n                                          return_array=False):\n        \"\"\"Generate nearby points based on manhattan distance.\n\n        To generate the first neighbouring points, a distance of ``S`` (step\n        size) is moved from the center point (this keypoint) to the top,\n        right, bottom and left, resulting in four new points. From these new\n        points, the pattern is repeated. Overlapping points are ignored.\n\n        The resulting points have a shape similar to a square rotated\n        by ``45`` degrees.\n\n        Parameters\n        ----------\n        nb_steps : int\n            The number of steps to move from the center point.\n            ``nb_steps=1`` results in a total of ``5`` output points (one\n            center point + four neighbours).\n\n        step_size : number\n            The step size to move from every point to its neighbours.\n\n        return_array : bool, optional\n            Whether to return the generated points as a list of\n            :class:`Keypoint` or an array of shape ``(N,2)``, where ``N`` is\n            the number of generated points and the second axis contains the\n            x-/y-coordinates.\n\n        Returns\n        -------\n        list of imgaug.augmentables.kps.Keypoint or (N,2) ndarray\n            If `return_array` was ``False``, then a list of :class:`Keypoint`.\n            Otherwise a numpy array of shape ``(N,2)``, where ``N`` is the\n            number of generated points and the second axis contains\n            the x-/y-coordinates. The center keypoint (the one on which this\n            function was called) is always included.\n\n        \"\"\"\n        # TODO add test\n        # Points generates in manhattan style with S steps have a shape\n        # similar to a 45deg rotated square. The center line with the origin\n        # point has S+1+S = 1+2*S points (S to the left, S to the right).\n        # The lines above contain (S+1+S)-2 + (S+1+S)-2-2 + ... + 1 points.\n        # E.g. for S=2 it would be 3+1=4 and for S=3 it would be 5+3+1=9.\n        # Same for the lines below the center. Hence the total number of\n        # points is S+1+S + 2*(S^2).\n        nb_points = nb_steps + 1 + nb_steps + 2*(nb_steps**2)\n        points = np.zeros((nb_points, 2), dtype=np.float32)\n\n        # we start at the bottom-most line and move towards the top-most line\n        yy = np.linspace(\n            self.y - nb_steps * step_size,\n            self.y + nb_steps * step_size,\n            nb_steps + 1 + nb_steps)\n\n        # bottom-most line contains only one point\n        width = 1\n\n        nth_point = 0\n        for i_y, y in enumerate(yy):\n            if width == 1:\n                xx = [self.x]\n            else:\n                xx = np.linspace(\n                    self.x - (width-1)//2 * step_size,\n                    self.x + (width-1)//2 * step_size,\n                    width)\n            for x in xx:\n                points[nth_point] = [x, y]\n                nth_point += 1\n            if i_y < nb_steps:\n                width += 2\n            else:\n                width -= 2\n\n        if return_array:\n            return points\n        return [self.deepcopy(x=point[0], y=point[1]) for point in points]\n\n    def coords_almost_equals(self, other, max_distance=1e-4):\n        \"\"\"Estimate if this and another KP have almost identical coordinates.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.kps.Keypoint or iterable\n            The other keypoint with which to compare this one.\n            If this is an ``iterable``, it is assumed to contain the\n            xy-coordinates of a keypoint.\n\n        max_distance : number, optional\n            The maximum euclidean distance between a this keypoint and the\n            other one. If the distance is exceeded, the two keypoints are not\n            viewed as equal.\n\n        Returns\n        -------\n        bool\n            Whether the two keypoints have almost identical coordinates.\n\n        \"\"\"\n        if ia.is_np_array(other):\n            # we use flat here in case other is (N,2) instead of (4,)\n            coords_b = other.flat\n        elif ia.is_iterable(other):\n            coords_b = list(ia.flatten(other))\n        else:\n            assert isinstance(other, Keypoint), (\n                \"Expected 'other' to be an iterable containing one \"\n                \"(x,y)-coordinate pair or a Keypoint. \"\n                \"Got type %s.\" % (type(other),))\n            coords_b = other.coords.flat\n\n        coords_a = self.coords\n\n        return np.allclose(coords_a.flat, coords_b, atol=max_distance, rtol=0)\n\n    def almost_equals(self, other, max_distance=1e-4):\n        \"\"\"Compare this and another KP's coordinates.\n\n        .. note::\n\n            This method is currently identical to ``coords_almost_equals``.\n            It exists for consistency with ``BoundingBox`` and ``Polygons``.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.kps.Keypoint or iterable\n            The other object to compare against. Expected to be a\n            ``Keypoint``.\n\n        max_distance : number, optional\n            See\n            :func:`~imgaug.augmentables.kps.Keypoint.coords_almost_equals`.\n\n        Returns\n        -------\n        bool\n            ``True`` if the coordinates are almost equal. Otherwise ``False``.\n\n        \"\"\"\n        return self.coords_almost_equals(other, max_distance=max_distance)\n\n    def copy(self, x=None, y=None):\n        \"\"\"Create a shallow copy of the keypoint instance.\n\n        Parameters\n        ----------\n        x : None or number, optional\n            Coordinate of the keypoint on the x axis.\n            If ``None``, the instance's value will be copied.\n\n        y : None or number, optional\n            Coordinate of the keypoint on the y axis.\n            If ``None``, the instance's value will be copied.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.Keypoint\n            Shallow copy.\n\n        \"\"\"\n        return self.deepcopy(x=x, y=y)\n\n    def deepcopy(self, x=None, y=None):\n        \"\"\"Create a deep copy of the keypoint instance.\n\n        Parameters\n        ----------\n        x : None or number, optional\n            Coordinate of the keypoint on the x axis.\n            If ``None``, the instance's value will be copied.\n\n        y : None or number, optional\n            Coordinate of the keypoint on the y axis.\n            If ``None``, the instance's value will be copied.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.Keypoint\n            Deep copy.\n\n        \"\"\"\n        x = self.x if x is None else x\n        y = self.y if y is None else y\n        return Keypoint(x=x, y=y)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Keypoint(x=%.8f, y=%.8f)\" % (self.x, self.y)\n\n\nclass KeypointsOnImage(IAugmentable):\n    \"\"\"Container for all keypoints on a single image.\n\n    Parameters\n    ----------\n    keypoints : list of imgaug.augmentables.kps.Keypoint\n        List of keypoints on the image.\n\n    shape : tuple of int\n        The shape of the image on which the objects are placed, i.e. the\n        result of ``image.shape``.\n        Should include the number of channels, not only height and width.\n\n    Examples\n    --------\n    >>> import numpy as np\n    >>> from imgaug.augmentables.kps import Keypoint, KeypointsOnImage\n    >>>\n    >>> image = np.zeros((70, 70))\n    >>> kps = [Keypoint(x=10, y=20), Keypoint(x=34, y=60)]\n    >>> kps_oi = KeypointsOnImage(kps, shape=image.shape)\n\n    \"\"\"\n\n    def __init__(self, keypoints, shape):\n        self.keypoints = keypoints\n        self.shape = _handle_on_image_shape(shape, self)\n\n    @property\n    def items(self):\n        \"\"\"Get the keypoints in this container.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of Keypoint\n            Keypoints within this container.\n\n        \"\"\"\n        return self.keypoints\n\n    @items.setter\n    def items(self, value):\n        \"\"\"Set the keypoints in this container.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        value : list of Keypoint\n            Keypoints within this container.\n\n        \"\"\"\n        self.keypoints = value\n\n    @property\n    def height(self):\n        \"\"\"Get the image height.\n\n        Returns\n        -------\n        int\n            Image height.\n\n        \"\"\"\n        return self.shape[0]\n\n    @property\n    def width(self):\n        \"\"\"Get the image width.\n\n        Returns\n        -------\n        int\n            Image width.\n\n        \"\"\"\n        return self.shape[1]\n\n    @property\n    def empty(self):\n        \"\"\"Determine whether this object contains zero keypoints.\n\n        Returns\n        -------\n        bool\n            ``True`` if this object contains zero keypoints.\n\n        \"\"\"\n        return len(self.keypoints) == 0\n\n    def on_(self, image):\n        \"\"\"Project all keypoints from one image shape to a new one in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            New image onto which the keypoints are to be projected.\n            May also simply be that new image's shape tuple.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            Object containing all projected keypoints.\n            The object may have been modified in-place.\n\n        \"\"\"\n        # pylint: disable=invalid-name\n        on_shape = normalize_imglike_shape(image)\n        if on_shape[0:2] == self.shape[0:2]:\n            self.shape = on_shape  # channels may differ\n            return self\n\n        for i, kp in enumerate(self.keypoints):\n            self.keypoints[i] = kp.project_(self.shape, on_shape)\n        self.shape = on_shape\n        return self\n\n    def on(self, image):\n        \"\"\"Project all keypoints from one image shape to a new one.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            New image onto which the keypoints are to be projected.\n            May also simply be that new image's shape tuple.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            Object containing all projected keypoints.\n\n        \"\"\"\n        # pylint: disable=invalid-name\n        return self.deepcopy().on_(image)\n\n    def draw_on_image(self, image, color=(0, 255, 0), alpha=1.0, size=3,\n                      copy=True, raise_if_out_of_image=False):\n        \"\"\"Draw all keypoints onto a given image.\n\n        Each keypoint is drawn as a square of provided color and size.\n\n        Parameters\n        ----------\n        image : (H,W,3) ndarray\n            The image onto which to draw the keypoints.\n            This image should usually have the same shape as\n            set in ``KeypointsOnImage.shape``.\n\n        color : int or list of int or tuple of int or (3,) ndarray, optional\n            The RGB color of all keypoints.\n            If a single ``int`` ``C``, then that is equivalent to ``(C,C,C)``.\n\n        alpha : float, optional\n            The opacity of the drawn keypoint, where ``1.0`` denotes a fully\n            visible keypoint and ``0.0`` an invisible one.\n\n        size : int, optional\n            The size of each point. If set to ``C``, each square will have\n            size ``C x C``.\n\n        copy : bool, optional\n            Whether to copy the image before drawing the points.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an exception if any keypoint is outside of the\n            image.\n\n        Returns\n        -------\n        (H,W,3) ndarray\n            Image with drawn keypoints.\n\n        \"\"\"\n        # pylint: disable=redefined-outer-name\n        image = np.copy(image) if copy else image\n        for keypoint in self.keypoints:\n            image = keypoint.draw_on_image(\n                image, color=color, alpha=alpha, size=size, copy=False,\n                raise_if_out_of_image=raise_if_out_of_image)\n        return image\n\n    def remove_out_of_image_fraction_(self, fraction):\n        \"\"\"Remove all KPs with an OOI fraction of at least `fraction` in-place.\n\n        'OOI' is the abbreviation for 'out of image'.\n\n        This method exists for consistency with other augmentables, e.g.\n        bounding boxes.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        fraction : number\n            Minimum out of image fraction that a keypoint has to have in\n            order to be removed. Note that any keypoint can only have a\n            fraction of either ``1.0`` (is outside) or ``0.0`` (is inside).\n            Set this to ``0.0+eps`` to remove all points that are outside of\n            the image. Setting this to ``0.0`` will remove all points.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            Reduced set of keypoints, with those thathad an out of image\n            fraction greater or equal the given one removed.\n            The object may have been modified in-place.\n\n        \"\"\"\n        return _remove_out_of_image_fraction_(self, fraction)\n\n    def remove_out_of_image_fraction(self, fraction):\n        \"\"\"Remove all KPs with an out of image fraction of at least `fraction`.\n\n        This method exists for consistency with other augmentables, e.g.\n        bounding boxes.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        fraction : number\n            Minimum out of image fraction that a keypoint has to have in\n            order to be removed. Note that any keypoint can only have a\n            fraction of either ``1.0`` (is outside) or ``0.0`` (is inside).\n            Set this to ``0.0+eps`` to remove all points that are outside of\n            the image. Setting this to ``0.0`` will remove all points.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            Reduced set of keypoints, with those thathad an out of image\n            fraction greater or equal the given one removed.\n\n        \"\"\"\n        return self.deepcopy().remove_out_of_image_fraction_(fraction)\n\n    def clip_out_of_image_(self):\n        \"\"\"Remove all KPs that are outside of the image plane.\n\n        This method exists for consistency with other augmentables, e.g.\n        bounding boxes.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            Keypoints that are inside the image plane.\n            The object may have been modified in-place.\n\n        \"\"\"\n        # we could use anything >0 here as the fraction\n        return self.remove_out_of_image_fraction_(0.5)\n\n    def clip_out_of_image(self):\n        \"\"\"Remove all KPs that are outside of the image plane.\n\n        This method exists for consistency with other augmentables, e.g.\n        bounding boxes.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            Keypoints that are inside the image plane.\n\n        \"\"\"\n        return self.deepcopy().clip_out_of_image_()\n\n    def shift_(self, x=0, y=0):\n        \"\"\"Move the keypoints on the x/y-axis in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        x : number, optional\n            Move each keypoint by this value on the x axis.\n\n        y : number, optional\n            Move each keypoint by this value on the y axis.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            Keypoints after moving them.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        for i, keypoint in enumerate(self.keypoints):\n            self.keypoints[i] = keypoint.shift_(x=x, y=y)\n        return self\n\n    def shift(self, x=0, y=0):\n        \"\"\"Move the keypoints on the x/y-axis.\n\n        Parameters\n        ----------\n        x : number, optional\n            Move each keypoint by this value on the x axis.\n\n        y : number, optional\n            Move each keypoint by this value on the y axis.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            Keypoints after moving them.\n\n        \"\"\"\n        return self.deepcopy().shift_(x=x, y=y)\n\n    @ia.deprecated(alt_func=\"KeypointsOnImage.to_xy_array()\")\n    def get_coords_array(self):\n        \"\"\"Convert all keypoint coordinates to an array of shape ``(N,2)``.\n\n        Returns\n        -------\n        (N, 2) ndarray\n            Array containing the coordinates of all keypoints.\n            ``N`` denotes the number of keypoints. The second axis denotes\n            the x/y-coordinates.\n\n        \"\"\"\n        return self.to_xy_array()\n\n    def to_xy_array(self):\n        \"\"\"Convert all keypoint coordinates to an array of shape ``(N,2)``.\n\n        Returns\n        -------\n        (N, 2) ndarray\n            Array containing the coordinates of all keypoints.\n            ``N`` denotes the number of keypoints. The second axis denotes\n            the x/y-coordinates.\n\n        \"\"\"\n        result = np.zeros((len(self.keypoints), 2), dtype=np.float32)\n        for i, keypoint in enumerate(self.keypoints):\n            result[i, 0] = keypoint.x\n            result[i, 1] = keypoint.y\n        return result\n\n    @staticmethod\n    @ia.deprecated(alt_func=\"KeypointsOnImage.from_xy_array()\")\n    def from_coords_array(coords, shape):\n        \"\"\"Convert an ``(N,2)`` array to a ``KeypointsOnImage`` object.\n\n        Parameters\n        ----------\n        coords : (N, 2) ndarray\n            Coordinates of ``N`` keypoints on an image, given as a ``(N,2)``\n            array of xy-coordinates.\n\n        shape : tuple\n            The shape of the image on which the keypoints are placed.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            :class:`KeypointsOnImage` object containing the array's keypoints.\n\n        \"\"\"\n        return KeypointsOnImage.from_xy_array(coords, shape)\n\n    @classmethod\n    def from_xy_array(cls, xy, shape):\n        \"\"\"Convert an ``(N,2)`` array to a ``KeypointsOnImage`` object.\n\n        Parameters\n        ----------\n        xy : (N, 2) ndarray or iterable of iterable of number\n            Coordinates of ``N`` keypoints on an image, given as a ``(N,2)``\n            array of xy-coordinates.\n\n        shape : tuple of int or ndarray\n            The shape of the image on which the keypoints are placed.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            :class:`KeypointsOnImage` object containing the array's keypoints.\n\n        \"\"\"\n        xy = np.array(xy, dtype=np.float32)\n\n        # note that np.array([]) is (0,), not (0, 2)\n        if xy.shape[0] == 0:  # pylint: disable=unsubscriptable-object\n            return KeypointsOnImage([], shape)\n\n        assert xy.ndim == 2 and xy.shape[-1] == 2, (  # pylint: disable=unsubscriptable-object\n            \"Expected input array to have shape (N,2), \"\n            \"got shape %s.\" % (xy.shape,))\n        keypoints = [Keypoint(x=coord[0], y=coord[1]) for coord in xy]\n        return KeypointsOnImage(keypoints, shape)\n\n    def fill_from_xy_array_(self, xy):\n        \"\"\"Modify the keypoint coordinates of this instance in-place.\n\n        .. note::\n\n            This currently expects that `xy` contains exactly as many\n            coordinates as there are keypoints in this instance. Otherwise,\n            an ``AssertionError`` will be raised.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        xy : (N, 2) ndarray or iterable of iterable of number\n            Coordinates of ``N`` keypoints on an image, given as a ``(N,2)``\n            array of xy-coordinates. ``N`` must match the number of keypoints\n            in this instance.\n\n        Returns\n        -------\n        KeypointsOnImage\n            This instance itself, with updated keypoint coordinates.\n            Note that the instance was modified in-place.\n\n        \"\"\"\n        xy = np.array(xy, dtype=np.float32)\n\n        # note that np.array([]) is (0,), not (0, 2)\n        assert xy.shape[0] == 0 or (xy.ndim == 2 and xy.shape[-1] == 2), (  # pylint: disable=unsubscriptable-object\n            \"Expected input array to have shape (N,2), \"\n            \"got shape %s.\" % (xy.shape,))\n\n        assert len(xy) == len(self.keypoints), (\n            \"Expected to receive as many keypoint coordinates as there are \"\n            \"currently keypoints in this instance. Got %d, expected %d.\" % (\n                len(xy), len(self.keypoints)))\n\n        for kp, (x, y) in zip(self.keypoints, xy):\n            kp.x = x\n            kp.y = y\n\n        return self\n\n    # TODO add to_gaussian_heatmaps(), from_gaussian_heatmaps()\n    def to_keypoint_image(self, size=1):\n        \"\"\"Create an ``(H,W,N)`` image with keypoint coordinates set to ``255``.\n\n        This method generates a new ``uint8`` array of shape ``(H,W,N)``,\n        where ``H`` is the ``.shape`` height, ``W`` the ``.shape`` width and\n        ``N`` is the number of keypoints. The array is filled with zeros.\n        The coordinate of the ``n``-th keypoint is set to ``255`` in the\n        ``n``-th channel.\n\n        This function can be used as a helper when augmenting keypoints with\n        a method that only supports the augmentation of images.\n\n        Parameters\n        -------\n        size : int\n            Size of each (squared) point.\n\n        Returns\n        -------\n        (H,W,N) ndarray\n            Image in which the keypoints are marked. ``H`` is the height,\n            defined in ``KeypointsOnImage.shape[0]`` (analogous ``W``).\n            ``N`` is the number of keypoints.\n\n        \"\"\"\n        height, width = self.shape[0:2]\n        image = np.zeros((height, width, len(self.keypoints)), dtype=np.uint8)\n        assert size % 2 != 0, (\n            \"Expected 'size' to have an odd value, got %d instead.\" % (size,))\n        sizeh = max(0, (size-1)//2)\n        for i, keypoint in enumerate(self.keypoints):\n            # TODO for float values spread activation over several cells\n            # here and do voting at the end\n            y = keypoint.y_int\n            x = keypoint.x_int\n\n            x1 = np.clip(x - sizeh, 0, width-1)\n            x2 = np.clip(x + sizeh + 1, 0, width)\n            y1 = np.clip(y - sizeh, 0, height-1)\n            y2 = np.clip(y + sizeh + 1, 0, height)\n\n            if x1 < x2 and y1 < y2:\n                image[y1:y2, x1:x2, i] = 128\n            if 0 <= y < height and 0 <= x < width:\n                image[y, x, i] = 255\n        return image\n\n    @staticmethod\n    def from_keypoint_image(image, if_not_found_coords={\"x\": -1, \"y\": -1},\n                            threshold=1, nb_channels=None):\n        \"\"\"Convert ``to_keypoint_image()`` outputs to ``KeypointsOnImage``.\n\n        This is the inverse of :func:`KeypointsOnImage.to_keypoint_image`.\n\n        Parameters\n        ----------\n        image : (H,W,N) ndarray\n            The keypoints image. N is the number of keypoints.\n\n        if_not_found_coords : tuple or list or dict or None, optional\n            Coordinates to use for keypoints that cannot be found in `image`.\n\n            * If this is a ``list``/``tuple``, it must contain two ``int``\n              values.\n            * If it is a ``dict``, it must contain the keys ``x`` and\n              ``y`` with each containing one ``int`` value.\n            * If this is ``None``, then the keypoint will not be added to the\n              final :class:`KeypointsOnImage` object.\n\n        threshold : int, optional\n            The search for keypoints works by searching for the argmax in\n            each channel. This parameters contains the minimum value that\n            the max must have in order to be viewed as a keypoint.\n\n        nb_channels : None or int, optional\n            Number of channels of the image on which the keypoints are placed.\n            Some keypoint augmenters require that information.\n            If set to ``None``, the keypoint's shape will be set\n            to ``(height, width)``, otherwise ``(height, width, nb_channels)``.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            The extracted keypoints.\n\n        \"\"\"\n        # pylint: disable=dangerous-default-value\n        assert image.ndim == 3, (\n            \"Expected 'image' to have three dimensions, \"\n            \"got %d with shape %s instead.\" % (image.ndim, image.shape))\n        height, width, nb_keypoints = image.shape\n\n        drop_if_not_found = False\n        if if_not_found_coords is None:\n            drop_if_not_found = True\n            if_not_found_x = -1\n            if_not_found_y = -1\n        elif isinstance(if_not_found_coords, (tuple, list)):\n            assert len(if_not_found_coords) == 2, (\n                \"Expected tuple 'if_not_found_coords' to contain exactly two \"\n                \"values, got %d values.\" % (len(if_not_found_coords),))\n            if_not_found_x = if_not_found_coords[0]\n            if_not_found_y = if_not_found_coords[1]\n        elif isinstance(if_not_found_coords, dict):\n            if_not_found_x = if_not_found_coords[\"x\"]\n            if_not_found_y = if_not_found_coords[\"y\"]\n        else:\n            raise Exception(\n                \"Expected if_not_found_coords to be None or tuple or list \"\n                \"or dict, got %s.\" % (type(if_not_found_coords),))\n\n        keypoints = []\n        for i in sm.xrange(nb_keypoints):\n            maxidx_flat = np.argmax(image[..., i])\n            maxidx_ndim = np.unravel_index(maxidx_flat, (height, width))\n\n            found = (image[maxidx_ndim[0], maxidx_ndim[1], i] >= threshold)\n            if found:\n                x = maxidx_ndim[1] + 0.5\n                y = maxidx_ndim[0] + 0.5\n                keypoints.append(Keypoint(x=x, y=y))\n            else:\n                if drop_if_not_found:\n                    # dont add the keypoint to the result list, i.e. drop it\n                    pass\n                else:\n                    keypoints.append(Keypoint(x=if_not_found_x,\n                                              y=if_not_found_y))\n\n        out_shape = (height, width)\n        if nb_channels is not None:\n            out_shape += (nb_channels,)\n        return KeypointsOnImage(keypoints, shape=out_shape)\n\n    def to_distance_maps(self, inverted=False):\n        \"\"\"Generate a ``(H,W,N)`` array of distance maps for ``N`` keypoints.\n\n        The ``n``-th distance map contains at every location ``(y, x)`` the\n        euclidean distance to the ``n``-th keypoint.\n\n        This function can be used as a helper when augmenting keypoints with a\n        method that only supports the augmentation of images.\n\n        Parameters\n        -------\n        inverted : bool, optional\n            If ``True``, inverted distance maps are returned where each\n            distance value d is replaced by ``d/(d+1)``, i.e. the distance\n            maps have values in the range ``(0.0, 1.0]`` with ``1.0`` denoting\n            exactly the position of the respective keypoint.\n\n        Returns\n        -------\n        (H,W,N) ndarray\n            A ``float32`` array containing ``N`` distance maps for ``N``\n            keypoints. Each location ``(y, x, n)`` in the array denotes the\n            euclidean distance at ``(y, x)`` to the ``n``-th keypoint.\n            If `inverted` is ``True``, the distance ``d`` is replaced\n            by ``d/(d+1)``. The height and width of the array match the\n            height and width in ``KeypointsOnImage.shape``.\n\n        \"\"\"\n        height, width = self.shape[0:2]\n        distance_maps = np.zeros((height, width, len(self.keypoints)),\n                                 dtype=np.float32)\n\n        yy = np.arange(0, height)\n        xx = np.arange(0, width)\n        grid_xx, grid_yy = np.meshgrid(xx, yy)\n\n        for i, keypoint in enumerate(self.keypoints):\n            y, x = keypoint.y, keypoint.x\n            distance_maps[:, :, i] = (grid_xx - x) ** 2 + (grid_yy - y) ** 2\n        distance_maps = np.sqrt(distance_maps)\n        if inverted:\n            return 1/(distance_maps+1)\n        return distance_maps\n\n    # TODO add option to if_not_found_coords to reuse old keypoint coords\n    @staticmethod\n    def from_distance_maps(distance_maps, inverted=False,\n                           if_not_found_coords={\"x\": -1, \"y\": -1},\n                           threshold=None, nb_channels=None):\n        \"\"\"Convert outputs of ``to_distance_maps()`` to ``KeypointsOnImage``.\n\n        This is the inverse of :func:`KeypointsOnImage.to_distance_maps`.\n\n        Parameters\n        ----------\n        distance_maps : (H,W,N) ndarray\n            The distance maps. ``N`` is the number of keypoints.\n\n        inverted : bool, optional\n            Whether the given distance maps were generated in inverted mode\n            (i.e. :func:`KeypointsOnImage.to_distance_maps` was called with\n            ``inverted=True``) or in non-inverted mode.\n\n        if_not_found_coords : tuple or list or dict or None, optional\n            Coordinates to use for keypoints that cannot be found\n            in `distance_maps`.\n\n            * If this is a ``list``/``tuple``, it must contain two ``int``\n              values.\n            * If it is a ``dict``, it must contain the keys ``x`` and\n              ``y`` with each containing one ``int`` value.\n            * If this is ``None``, then the keypoint will not be added to the\n              final :class:`KeypointsOnImage` object.\n\n        threshold : float, optional\n            The search for keypoints works by searching for the\n            argmin (non-inverted) or argmax (inverted) in each channel. This\n            parameters contains the maximum (non-inverted) or\n            minimum (inverted) value to accept in order to view a hit as a\n            keypoint. Use ``None`` to use no min/max.\n\n        nb_channels : None or int, optional\n            Number of channels of the image on which the keypoints are placed.\n            Some keypoint augmenters require that information.\n            If set to ``None``, the keypoint's shape will be set\n            to ``(height, width)``, otherwise ``(height, width, nb_channels)``.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            The extracted keypoints.\n\n        \"\"\"\n        # pylint: disable=dangerous-default-value\n        assert distance_maps.ndim == 3, (\n            \"Expected three-dimensional input, got %d dimensions and \"\n            \"shape %s.\" % (distance_maps.ndim, distance_maps.shape))\n        height, width, nb_keypoints = distance_maps.shape\n\n        drop_if_not_found = False\n        if if_not_found_coords is None:\n            drop_if_not_found = True\n            if_not_found_x = -1\n            if_not_found_y = -1\n        elif isinstance(if_not_found_coords, (tuple, list)):\n            assert len(if_not_found_coords) == 2, (\n                \"Expected tuple/list 'if_not_found_coords' to contain \"\n                \"exactly two entries, got %d.\" % (len(if_not_found_coords),))\n            if_not_found_x = if_not_found_coords[0]\n            if_not_found_y = if_not_found_coords[1]\n        elif isinstance(if_not_found_coords, dict):\n            if_not_found_x = if_not_found_coords[\"x\"]\n            if_not_found_y = if_not_found_coords[\"y\"]\n        else:\n            raise Exception(\n                \"Expected if_not_found_coords to be None or tuple or list or \"\n                \"dict, got %s.\" % (type(if_not_found_coords),))\n\n        keypoints = []\n        for i in sm.xrange(nb_keypoints):\n            # TODO introduce voting here among all distance values that have\n            #      min/max values\n            if inverted:\n                hitidx_flat = np.argmax(distance_maps[..., i])\n            else:\n                hitidx_flat = np.argmin(distance_maps[..., i])\n            hitidx_ndim = np.unravel_index(hitidx_flat, (height, width))\n            if not inverted and threshold is not None:\n                found = (distance_maps[hitidx_ndim[0], hitidx_ndim[1], i]\n                         < threshold)\n            elif inverted and threshold is not None:\n                found = (distance_maps[hitidx_ndim[0], hitidx_ndim[1], i]\n                         >= threshold)\n            else:\n                found = True\n            if found:\n                keypoints.append(Keypoint(x=hitidx_ndim[1], y=hitidx_ndim[0]))\n            else:\n                if drop_if_not_found:\n                    # dont add the keypoint to the result list, i.e. drop it\n                    pass\n                else:\n                    keypoints.append(Keypoint(x=if_not_found_x,\n                                              y=if_not_found_y))\n\n        out_shape = (height, width)\n        if nb_channels is not None:\n            out_shape += (nb_channels,)\n        return KeypointsOnImage(keypoints, shape=out_shape)\n\n    # TODO add to_keypoints_on_image_() and call that wherever possible\n    def to_keypoints_on_image(self):\n        \"\"\"Convert the keypoints to one ``KeypointsOnImage`` instance.\n\n        This method exists for consistency with ``BoundingBoxesOnImage``,\n        ``PolygonsOnImage`` and ``LineStringsOnImage``.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            Copy of this keypoints instance.\n\n        \"\"\"\n        return self.deepcopy()\n\n    def invert_to_keypoints_on_image_(self, kpsoi):\n        \"\"\"Invert the output of ``to_keypoints_on_image()`` in-place.\n\n        This function writes in-place into this ``KeypointsOnImage``\n        instance.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        kpsoi : imgaug.augmentables.kps.KeypointsOnImages\n            Keypoints to copy data from, i.e. the outputs of\n            ``to_keypoints_on_image()``.\n\n        Returns\n        -------\n        KeypointsOnImage\n            Keypoints container with updated coordinates.\n            Note that the instance is also updated in-place.\n\n        \"\"\"\n        nb_points_exp = len(self.keypoints)\n        assert len(kpsoi.keypoints) == nb_points_exp, (\n            \"Expected %d coordinates, got %d.\" % (\n                nb_points_exp, len(kpsoi.keypoints)))\n\n        for kp_target, kp_source in zip(self.keypoints, kpsoi.keypoints):\n            kp_target.x = kp_source.x\n            kp_target.y = kp_source.y\n\n        self.shape = kpsoi.shape\n        return self\n\n    def copy(self, keypoints=None, shape=None):\n        \"\"\"Create a shallow copy of the ``KeypointsOnImage`` object.\n\n        Parameters\n        ----------\n        keypoints : None or list of imgaug.Keypoint, optional\n            List of keypoints on the image.\n            If ``None``, the instance's keypoints will be copied.\n\n        shape : tuple of int, optional\n            The shape of the image on which the keypoints are placed.\n            If ``None``, the instance's shape will be copied.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            Shallow copy.\n\n        \"\"\"\n        if keypoints is None:\n            keypoints = self.keypoints[:]\n        if shape is None:\n            # use tuple() here in case the shape was provided as a list\n            shape = tuple(self.shape)\n\n        return KeypointsOnImage(keypoints, shape)\n\n    def deepcopy(self, keypoints=None, shape=None):\n        \"\"\"Create a deep copy of the ``KeypointsOnImage`` object.\n\n        Parameters\n        ----------\n        keypoints : None or list of imgaug.Keypoint, optional\n            List of keypoints on the image.\n            If ``None``, the instance's keypoints will be copied.\n\n        shape : tuple of int, optional\n            The shape of the image on which the keypoints are placed.\n            If ``None``, the instance's shape will be copied.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            Deep copy.\n\n        \"\"\"\n        # Manual copy is far faster than deepcopy, so use manual copy here.\n        if keypoints is None:\n            keypoints = [kp.deepcopy() for kp in self.keypoints]\n        if shape is None:\n            # use tuple() here in case the shape was provided as a list\n            shape = tuple(self.shape)\n\n        return KeypointsOnImage(keypoints, shape)\n\n    def __getitem__(self, indices):\n        \"\"\"Get the keypoint(s) with given indices.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of imgaug.augmentables.kps.Keypoint\n            Keypoint(s) with given indices.\n\n        \"\"\"\n        return self.keypoints[indices]\n\n    def __iter__(self):\n        \"\"\"Iterate over the keypoints in this container.\n\n        Added in 0.4.0.\n\n        Yields\n        ------\n        Keypoint\n            A keypoint in this container.\n            The order is identical to the order in the keypoint list\n            provided upon class initialization.\n\n        \"\"\"\n        return iter(self.items)\n\n    def __len__(self):\n        \"\"\"Get the number of items in this instance.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        int\n            Number of items in this instance.\n\n        \"\"\"\n        return len(self.items)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"KeypointsOnImage(%s, shape=%s)\" % (\n            str(self.keypoints), self.shape)\n"
  },
  {
    "path": "imgaug/augmentables/lines.py",
    "content": "\"\"\"Classes representing lines.\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport copy as copylib\n\nimport numpy as np\nimport skimage.draw\nimport skimage.measure\nimport cv2\n\nfrom .. import imgaug as ia\nfrom .base import IAugmentable\nfrom .utils import (\n    normalize_imglike_shape,\n    project_coords_,\n    interpolate_points,\n    _remove_out_of_image_fraction_,\n    _normalize_shift_args,\n    _handle_on_image_shape\n)\n\n\n# TODO Add Line class and make LineString a list of Line elements\n# TODO add to_distance_maps(), compute_hausdorff_distance(), intersects(),\n#      find_self_intersections(), is_self_intersecting(),\n#      remove_self_intersections()\nclass LineString(object):\n    \"\"\"Class representing line strings.\n\n    A line string is a collection of connected line segments, each\n    having a start and end point. Each point is given as its ``(x, y)``\n    absolute (sub-)pixel coordinates. The end point of each segment is\n    also the start point of the next segment.\n\n    The line string is not closed, i.e. start and end point are expected to\n    differ and will not be connected in drawings.\n\n    Parameters\n    ----------\n    coords : iterable of tuple of number or ndarray\n        The points of the line string.\n\n    label : None or str, optional\n        The label of the line string.\n\n    \"\"\"\n\n    def __init__(self, coords, label=None):\n        \"\"\"Create a new LineString instance.\"\"\"\n        # use the conditions here to avoid unnecessary copies of ndarray inputs\n        if ia.is_np_array(coords):\n            if coords.dtype.name != \"float32\":\n                coords = coords.astype(np.float32)\n        elif len(coords) == 0:\n            coords = np.zeros((0, 2), dtype=np.float32)\n        else:\n            assert ia.is_iterable(coords), (\n                \"Expected 'coords' to be an iterable, \"\n                \"got type %s.\" % (type(coords),))\n            assert all([len(coords_i) == 2 for coords_i in coords]), (\n                \"Expected 'coords' to contain (x,y) tuples, \"\n                \"got %s.\" % (str(coords),))\n            coords = np.float32(coords)\n\n        assert coords.ndim == 2 and coords.shape[-1] == 2, (\n            \"Expected 'coords' to have shape (N, 2), got shape %s.\" % (\n                coords.shape,))\n\n        self.coords = coords\n        self.label = label\n\n    @property\n    def length(self):\n        \"\"\"Compute the total euclidean length of the line string.\n\n        Returns\n        -------\n        float\n            The length based on euclidean distance, i.e. the sum of the\n            lengths of each line segment.\n\n        \"\"\"\n        if len(self.coords) == 0:\n            return 0\n        return np.sum(self.compute_neighbour_distances())\n\n    @property\n    def xx(self):\n        \"\"\"Get an array of x-coordinates of all points of the line string.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` x-coordinates of the line string points.\n\n        \"\"\"\n        return self.coords[:, 0]\n\n    @property\n    def yy(self):\n        \"\"\"Get an array of y-coordinates of all points of the line string.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` y-coordinates of the line string points.\n\n        \"\"\"\n        return self.coords[:, 1]\n\n    @property\n    def xx_int(self):\n        \"\"\"Get an array of discrete x-coordinates of all points.\n\n        The conversion from ``float32`` coordinates to ``int32`` is done\n        by first rounding the coordinates to the closest integer and then\n        removing everything after the decimal point.\n\n        Returns\n        -------\n        ndarray\n            ``int32`` x-coordinates of the line string points.\n\n        \"\"\"\n        return np.round(self.xx).astype(np.int32)\n\n    @property\n    def yy_int(self):\n        \"\"\"Get an array of discrete y-coordinates of all points.\n\n        The conversion from ``float32`` coordinates to ``int32`` is done\n        by first rounding the coordinates to the closest integer and then\n        removing everything after the decimal point.\n\n        Returns\n        -------\n        ndarray\n            ``int32`` y-coordinates of the line string points.\n\n        \"\"\"\n        return np.round(self.yy).astype(np.int32)\n\n    @property\n    def height(self):\n        \"\"\"Compute the height of a bounding box encapsulating the line.\n\n        The height is computed based on the two points with lowest and\n        largest y-coordinates.\n\n        Returns\n        -------\n        float\n            The height of the line string.\n\n        \"\"\"\n        if len(self.coords) <= 1:\n            return 0\n        return np.max(self.yy) - np.min(self.yy)\n\n    @property\n    def width(self):\n        \"\"\"Compute the width of a bounding box encapsulating the line.\n\n        The width is computed based on the two points with lowest and\n        largest x-coordinates.\n\n        Returns\n        -------\n        float\n            The width of the line string.\n\n        \"\"\"\n        if len(self.coords) <= 1:\n            return 0\n        return np.max(self.xx) - np.min(self.xx)\n\n    def get_pointwise_inside_image_mask(self, image):\n        \"\"\"Determine per point whether it is inside of a given image plane.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            Either an image with shape ``(H,W,[C])`` or a ``tuple`` denoting\n            such an image shape.\n\n        Returns\n        -------\n        ndarray\n            ``(N,) ``bool`` array with one value for each of the ``N`` points\n            indicating whether it is inside of the provided image\n            plane (``True``) or not (``False``).\n\n        \"\"\"\n        # pylint: disable=misplaced-comparison-constant\n        if len(self.coords) == 0:\n            return np.zeros((0,), dtype=bool)\n        shape = normalize_imglike_shape(image)\n        height, width = shape[0:2]\n        x_within = np.logical_and(0 <= self.xx, self.xx < width)\n        y_within = np.logical_and(0 <= self.yy, self.yy < height)\n        return np.logical_and(x_within, y_within)\n\n    # TODO add closed=False/True?\n    def compute_neighbour_distances(self):\n        \"\"\"Compute the euclidean distance between each two consecutive points.\n\n        Returns\n        -------\n        ndarray\n            ``(N-1,)`` ``float32`` array of euclidean distances between point\n            pairs. Same order as in `coords`.\n\n        \"\"\"\n        if len(self.coords) <= 1:\n            return np.zeros((0,), dtype=np.float32)\n        return np.sqrt(\n            np.sum(\n                (self.coords[:-1, :] - self.coords[1:, :]) ** 2,\n                axis=1\n            )\n        )\n\n    # TODO change output to array\n    def compute_pointwise_distances(self, other, default=None):\n        \"\"\"Compute min distances between points of this and another line string.\n\n        Parameters\n        ----------\n        other : tuple of number or imgaug.augmentables.kps.Keypoint or imgaug.augmentables.LineString\n            Other object to which to compute the distances.\n\n        default : any\n            Value to return if `other` contains no points.\n\n        Returns\n        -------\n        list of float or any\n            For each coordinate of this line string, the distance to any\n            closest location on `other`.\n            `default` if no distance could be computed.\n\n        \"\"\"\n        import shapely.geometry\n        from .kps import Keypoint\n\n        if isinstance(other, Keypoint):\n            other = shapely.geometry.Point((other.x, other.y))\n        elif isinstance(other, LineString):\n            if len(other.coords) == 0:\n                return default\n            if len(other.coords) == 1:\n                other = shapely.geometry.Point(other.coords[0, :])\n            else:\n                other = shapely.geometry.LineString(other.coords)\n        elif isinstance(other, tuple):\n            assert len(other) == 2, (\n                \"Expected tuple 'other' to contain exactly two entries, \"\n                \"got %d.\" % (len(other),))\n            other = shapely.geometry.Point(other)\n        else:\n            raise ValueError(\n                \"Expected Keypoint or LineString or tuple (x,y), \"\n                \"got type %s.\" % (type(other),))\n\n        return [shapely.geometry.Point(point).distance(other)\n                for point in self.coords]\n\n    def compute_distance(self, other, default=None):\n        \"\"\"Compute the minimal distance between the line string and `other`.\n\n        Parameters\n        ----------\n        other : tuple of number or imgaug.augmentables.kps.Keypoint or imgaug.augmentables.LineString\n            Other object to which to compute the distance.\n\n        default : any\n            Value to return if this line string or `other` contain no points.\n\n        Returns\n        -------\n        float or any\n            Minimal distance to `other` or `default` if no distance could be\n            computed.\n\n        \"\"\"\n        # FIXME this computes distance pointwise, does not have to be identical\n        #       with the actual min distance (e.g. edge center to other's point)\n        distances = self.compute_pointwise_distances(other, default=[])\n        if len(distances) == 0:\n            return default\n        return min(distances)\n\n    # TODO update BB's contains(), which can only accept Keypoint currently\n    def contains(self, other, max_distance=1e-4):\n        \"\"\"Estimate whether a point is on this line string.\n\n        This method uses a maximum distance to estimate whether a point is\n        on a line string.\n\n        Parameters\n        ----------\n        other : tuple of number or imgaug.augmentables.kps.Keypoint\n            Point to check for.\n\n        max_distance : float\n            Maximum allowed euclidean distance between the point and the\n            closest point on the line. If the threshold is exceeded, the point\n            is not considered to fall on the line.\n\n        Returns\n        -------\n        bool\n            ``True`` if the point is on the line string, ``False`` otherwise.\n\n        \"\"\"\n        return self.compute_distance(other, default=np.inf) < max_distance\n\n    def project_(self, from_shape, to_shape):\n        \"\"\"Project the line string onto a differently shaped image in-place.\n\n        E.g. if a point of the line string is on its original image at\n        ``x=(10 of 100 pixels)`` and ``y=(20 of 100 pixels)`` and is projected\n        onto a new image with size ``(width=200, height=200)``, its new\n        position will be ``(x=20, y=40)``.\n\n        This is intended for cases where the original image is resized.\n        It cannot be used for more complex changes (e.g. padding, cropping).\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        from_shape : tuple of int or ndarray\n            Shape of the original image. (Before resize.)\n\n        to_shape : tuple of int or ndarray\n            Shape of the new image. (After resize.)\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineString\n            Line string with new coordinates.\n            The object may have been modified in-place.\n\n        \"\"\"\n        self.coords = project_coords_(self.coords, from_shape, to_shape)\n        return self\n\n    def project(self, from_shape, to_shape):\n        \"\"\"Project the line string onto a differently shaped image.\n\n        E.g. if a point of the line string is on its original image at\n        ``x=(10 of 100 pixels)`` and ``y=(20 of 100 pixels)`` and is projected\n        onto a new image with size ``(width=200, height=200)``, its new\n        position will be ``(x=20, y=40)``.\n\n        This is intended for cases where the original image is resized.\n        It cannot be used for more complex changes (e.g. padding, cropping).\n\n        Parameters\n        ----------\n        from_shape : tuple of int or ndarray\n            Shape of the original image. (Before resize.)\n\n        to_shape : tuple of int or ndarray\n            Shape of the new image. (After resize.)\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineString\n            Line string with new coordinates.\n\n        \"\"\"\n        return self.deepcopy().project_(from_shape, to_shape)\n\n    def compute_out_of_image_fraction(self, image):\n        \"\"\"Compute fraction of polygon area outside of the image plane.\n\n        This estimates ``f = A_ooi / A``, where ``A_ooi`` is the area of the\n        polygon that is outside of the image plane, while ``A`` is the\n        total area of the bounding box.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape\n            and must contain at least two integers.\n\n        Returns\n        -------\n        float\n            Fraction of the polygon area that is outside of the image\n            plane. Returns ``0.0`` if the polygon is fully inside of\n            the image plane. If the polygon has an area of zero, the polygon\n            is treated similarly to a :class:`LineString`, i.e. the fraction\n            of the line that is inside the image plane is returned.\n\n        \"\"\"\n        length = self.length\n        if length == 0:\n            if len(self.coords) == 0:\n                return 0.0\n            points_ooi = ~self.get_pointwise_inside_image_mask(image)\n            return 1.0 if np.all(points_ooi) else 0.0\n        lss_clipped = self.clip_out_of_image(image)\n        length_after_clip = sum([ls.length for ls in lss_clipped])\n        inside_image_factor = length_after_clip / length\n        return 1.0 - inside_image_factor\n\n    def is_fully_within_image(self, image, default=False):\n        \"\"\"Estimate whether the line string is fully inside an image plane.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            Either an image with shape ``(H,W,[C])`` or a ``tuple`` denoting\n            such an image shape.\n\n        default : any\n            Default value to return if the line string contains no points.\n\n        Returns\n        -------\n        bool or any\n            ``True`` if the line string is fully inside the image area.\n            ``False`` otherwise.\n            Will return `default` if this line string contains no points.\n\n        \"\"\"\n        if len(self.coords) == 0:\n            return default\n        return np.all(self.get_pointwise_inside_image_mask(image))\n\n    def is_partly_within_image(self, image, default=False):\n        \"\"\"\n        Estimate whether the line string is at least partially inside the image.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            Either an image with shape ``(H,W,[C])`` or a ``tuple`` denoting\n            such an image shape.\n\n        default : any\n            Default value to return if the line string contains no points.\n\n        Returns\n        -------\n        bool or any\n            ``True`` if the line string is at least partially inside the image\n            area. ``False`` otherwise.\n            Will return `default` if this line string contains no points.\n\n        \"\"\"\n        if len(self.coords) == 0:\n            return default\n        # check mask first to avoid costly computation of intersection points\n        # whenever possible\n        mask = self.get_pointwise_inside_image_mask(image)\n        if np.any(mask):\n            return True\n        return len(self.clip_out_of_image(image)) > 0\n\n    def is_out_of_image(self, image, fully=True, partly=False, default=True):\n        \"\"\"\n        Estimate whether the line is partially/fully outside of the image area.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            Either an image with shape ``(H,W,[C])`` or a tuple denoting\n            such an image shape.\n\n        fully : bool, optional\n            Whether to return ``True`` if the line string is fully outside\n            of the image area.\n\n        partly : bool, optional\n            Whether to return ``True`` if the line string is at least partially\n            outside fo the image area.\n\n        default : any\n            Default value to return if the line string contains no points.\n\n        Returns\n        -------\n        bool or any\n            ``True`` if the line string is partially/fully outside of the image\n            area, depending on defined parameters.\n            ``False`` otherwise.\n            Will return `default` if this line string contains no points.\n\n        \"\"\"\n        if len(self.coords) == 0:\n            return default\n\n        if self.is_fully_within_image(image):\n            return False\n        if self.is_partly_within_image(image):\n            return partly\n        return fully\n\n    def clip_out_of_image(self, image):\n        \"\"\"Clip off all parts of the line string that are outside of the image.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            Either an image with shape ``(H,W,[C])`` or a ``tuple`` denoting\n            such an image shape.\n\n        Returns\n        -------\n        list of imgaug.augmentables.lines.LineString\n            Line strings, clipped to the image shape.\n            The result may contain any number of line strins, including zero.\n\n        \"\"\"\n        if len(self.coords) == 0:\n            return []\n\n        inside_image_mask = self.get_pointwise_inside_image_mask(image)\n        ooi_mask = ~inside_image_mask\n\n        if len(self.coords) == 1:\n            if not np.any(inside_image_mask):\n                return []\n            return [self.copy()]\n\n        if np.all(inside_image_mask):\n            return [self.copy()]\n\n        # top, right, bottom, left image edges\n        # we subtract eps here, because intersection() works inclusively,\n        # i.e. not subtracting eps would be equivalent to 0<=x<=C for C being\n        # height or width\n        # don't set the eps too low, otherwise points at height/width seem\n        # to get rounded to height/width by shapely, which can cause problems\n        # when first clipping and then calling is_fully_within_image()\n        # returning false\n        height, width = normalize_imglike_shape(image)[0:2]\n        eps = 1e-3\n        edges = [\n            LineString([(0.0, 0.0), (width - eps, 0.0)]),\n            LineString([(width - eps, 0.0), (width - eps, height - eps)]),\n            LineString([(width - eps, height - eps), (0.0, height - eps)]),\n            LineString([(0.0, height - eps), (0.0, 0.0)])\n        ]\n        intersections = self.find_intersections_with(edges)\n\n        points = []\n        gen = enumerate(zip(self.coords[:-1], self.coords[1:],\n                            ooi_mask[:-1], ooi_mask[1:],\n                            intersections))\n        for i, (line_start, line_end, ooi_start, ooi_end, inter_line) in gen:\n            points.append((line_start, False, ooi_start))\n            for p_inter in inter_line:\n                points.append((p_inter, True, False))\n\n            is_last = (i == len(self.coords) - 2)\n            if is_last and not ooi_end:\n                points.append((line_end, False, ooi_end))\n\n        lines = []\n        line = []\n        for i, (coord, was_added, ooi) in enumerate(points):\n            # remove any point that is outside of the image,\n            # also start a new line once such a point is detected\n            if ooi:\n                if len(line) > 0:\n                    lines.append(line)\n                    line = []\n                continue\n\n            if not was_added:\n                # add all points that were part of the original line string\n                # AND that are inside the image plane\n                line.append(coord)\n            else:\n                is_last_point = (i == len(points)-1)\n                # ooi is a numpy.bool_, hence the bool(.)\n                is_next_ooi = (not is_last_point\n                               and bool(points[i+1][2]) is True)\n\n                # Add all points that were new (i.e. intersections), so\n                # long that they aren't essentially identical to other point.\n                # This prevents adding overlapping intersections multiple times.\n                # (E.g. when a line intersects with a corner of the image plane\n                # and also with one of its edges.)\n                p_prev = line[-1] if len(line) > 0 else None\n                # ignore next point if end reached or next point is out of image\n                p_next = None\n                if not is_last_point and not is_next_ooi:\n                    p_next = points[i+1][0]\n                dist_prev = None\n                dist_next = None\n                if p_prev is not None:\n                    dist_prev = np.linalg.norm(\n                        np.float32(coord) - np.float32(p_prev))\n                if p_next is not None:\n                    dist_next = np.linalg.norm(\n                        np.float32(coord) - np.float32(p_next))\n\n                dist_prev_ok = (dist_prev is None or dist_prev > 1e-2)\n                dist_next_ok = (dist_next is None or dist_next > 1e-2)\n                if dist_prev_ok and dist_next_ok:\n                    line.append(coord)\n\n        if len(line) > 0:\n            lines.append(line)\n\n        lines = [line for line in lines if len(line) > 0]\n        return [self.deepcopy(coords=line) for line in lines]\n\n    # TODO add tests for this\n    # TODO extend this to non line string geometries\n    def find_intersections_with(self, other):\n        \"\"\"Find all intersection points between this line string and `other`.\n\n        Parameters\n        ----------\n        other : tuple of number or list of tuple of number or list of LineString or LineString\n            The other geometry to use during intersection tests.\n\n        Returns\n        -------\n        list of list of tuple of number\n            All intersection points. One list per pair of consecutive start\n            and end point, i.e. `N-1` lists of `N` points. Each list may\n            be empty or may contain multiple points.\n\n        \"\"\"\n        import shapely.geometry\n\n        geom = _convert_var_to_shapely_geometry(other)\n\n        result = []\n        for p_start, p_end in zip(self.coords[:-1], self.coords[1:]):\n            ls = shapely.geometry.LineString([p_start, p_end])\n            intersections = ls.intersection(geom)\n            intersections = list(_flatten_shapely_collection(intersections))\n\n            intersections_points = []\n            for inter in intersections:\n                if isinstance(inter, shapely.geometry.linestring.LineString):\n                    # Since shapely 1.7a2 (tested in python 3.8),\n                    # .intersection() apprently can return LINE STRING EMPTY\n                    # (i.e. .coords is an empty list). Before that, the result\n                    # of .intersection() was just []. Hence, we first check\n                    # the length here.\n                    if len(inter.coords) > 0:\n                        inter_start = (inter.coords[0][0], inter.coords[0][1])\n                        inter_end = (inter.coords[-1][0], inter.coords[-1][1])\n                        intersections_points.extend([inter_start, inter_end])\n                else:\n                    assert isinstance(inter, shapely.geometry.point.Point), (\n                        \"Expected to find shapely.geometry.point.Point or \"\n                        \"shapely.geometry.linestring.LineString intersection, \"\n                        \"actually found %s.\" % (type(inter),))\n                    intersections_points.append((inter.x, inter.y))\n\n            # sort by distance to start point, this makes it later on easier\n            # to remove duplicate points\n            inter_sorted = sorted(\n                intersections_points,\n                key=lambda p, ps=p_start: np.linalg.norm(np.float32(p) - ps)\n            )\n\n            result.append(inter_sorted)\n        return result\n\n    def shift_(self, x=0, y=0):\n        \"\"\"Move this line string along the x/y-axis in-place.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        Returns\n        -------\n        result : imgaug.augmentables.lines.LineString\n            Shifted line string.\n            The object may have been modified in-place.\n\n        \"\"\"\n        self.coords[:, 0] += x\n        self.coords[:, 1] += y\n        return self\n\n    def shift(self, x=0, y=0, top=None, right=None, bottom=None, left=None):\n        \"\"\"Move this line string along the x/y-axis.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        top : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            top (towards the bottom).\n\n        right : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            right (towards the left).\n\n        bottom : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            bottom (towards the top).\n\n        left : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            left (towards the right).\n\n        Returns\n        -------\n        result : imgaug.augmentables.lines.LineString\n            Shifted line string.\n\n        \"\"\"\n        x, y = _normalize_shift_args(\n            x, y, top=top, right=right, bottom=bottom, left=left)\n        return self.deepcopy().shift_(x=x, y=y)\n\n    def draw_mask(self, image_shape, size_lines=1, size_points=0,\n                  raise_if_out_of_image=False):\n        \"\"\"Draw this line segment as a binary image mask.\n\n        Parameters\n        ----------\n        image_shape : tuple of int\n            The shape of the image onto which to draw the line mask.\n\n        size_lines : int, optional\n            Thickness of the line segments.\n\n        size_points : int, optional\n            Size of the points in pixels.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the line string is fully\n            outside of the image. If set to ``False``, no error will be\n            raised and only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        ndarray\n            Boolean line mask of shape `image_shape` (no channel axis).\n\n        \"\"\"\n        heatmap = self.draw_heatmap_array(\n            image_shape,\n            alpha_lines=1.0, alpha_points=1.0,\n            size_lines=size_lines, size_points=size_points,\n            antialiased=False,\n            raise_if_out_of_image=raise_if_out_of_image)\n        return heatmap > 0.5\n\n    def draw_lines_heatmap_array(self, image_shape, alpha=1.0,\n                                 size=1, antialiased=True,\n                                 raise_if_out_of_image=False):\n        \"\"\"Draw the line segments of this line string as a heatmap array.\n\n        Parameters\n        ----------\n        image_shape : tuple of int\n            The shape of the image onto which to draw the line mask.\n\n        alpha : float, optional\n            Opacity of the line string. Higher values denote a more visible\n            line string.\n\n        size : int, optional\n            Thickness of the line segments.\n\n        antialiased : bool, optional\n            Whether to draw the line with anti-aliasing activated.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the line string is fully\n            outside of the image. If set to ``False``, no error will be\n            raised and only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` array of shape `image_shape` (no channel axis) with\n            drawn line string. All values are in the interval ``[0.0, 1.0]``.\n\n        \"\"\"\n        assert len(image_shape) == 2 or (\n            len(image_shape) == 3 and image_shape[-1] == 1), (\n                \"Expected (H,W) or (H,W,1) as image_shape, got %s.\" % (\n                    image_shape,))\n\n        arr = self.draw_lines_on_image(\n            np.zeros(image_shape, dtype=np.uint8),\n            color=255, alpha=alpha, size=size,\n            antialiased=antialiased,\n            raise_if_out_of_image=raise_if_out_of_image\n        )\n        return arr.astype(np.float32) / 255.0\n\n    def draw_points_heatmap_array(self, image_shape, alpha=1.0,\n                                  size=1, raise_if_out_of_image=False):\n        \"\"\"Draw the points of this line string as a heatmap array.\n\n        Parameters\n        ----------\n        image_shape : tuple of int\n            The shape of the image onto which to draw the point mask.\n\n        alpha : float, optional\n            Opacity of the line string points. Higher values denote a more\n            visible points.\n\n        size : int, optional\n            Size of the points in pixels.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the line string is fully\n            outside of the image. If set to ``False``, no error will be\n            raised and only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` array of shape `image_shape` (no channel axis) with\n            drawn line string points. All values are in the\n            interval ``[0.0, 1.0]``.\n\n        \"\"\"\n        assert len(image_shape) == 2 or (\n            len(image_shape) == 3 and image_shape[-1] == 1), (\n                \"Expected (H,W) or (H,W,1) as image_shape, got %s.\" % (\n                    image_shape,))\n\n        arr = self.draw_points_on_image(\n            np.zeros(image_shape, dtype=np.uint8),\n            color=255, alpha=alpha, size=size,\n            raise_if_out_of_image=raise_if_out_of_image\n        )\n        return arr.astype(np.float32) / 255.0\n\n    def draw_heatmap_array(self, image_shape, alpha_lines=1.0, alpha_points=1.0,\n                           size_lines=1, size_points=0, antialiased=True,\n                           raise_if_out_of_image=False):\n        \"\"\"\n        Draw the line segments and points of the line string as a heatmap array.\n\n        Parameters\n        ----------\n        image_shape : tuple of int\n            The shape of the image onto which to draw the line mask.\n\n        alpha_lines : float, optional\n            Opacity of the line string. Higher values denote a more visible\n            line string.\n\n        alpha_points : float, optional\n            Opacity of the line string points. Higher values denote a more\n            visible points.\n\n        size_lines : int, optional\n            Thickness of the line segments.\n\n        size_points : int, optional\n            Size of the points in pixels.\n\n        antialiased : bool, optional\n            Whether to draw the line with anti-aliasing activated.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the line string is fully\n            outside of the image. If set to ``False``, no error will be\n            raised and only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` array of shape `image_shape` (no channel axis) with\n            drawn line segments and points. All values are in the\n            interval ``[0.0, 1.0]``.\n\n        \"\"\"\n        heatmap_lines = self.draw_lines_heatmap_array(\n            image_shape,\n            alpha=alpha_lines,\n            size=size_lines,\n            antialiased=antialiased,\n            raise_if_out_of_image=raise_if_out_of_image)\n        if size_points <= 0:\n            return heatmap_lines\n\n        heatmap_points = self.draw_points_heatmap_array(\n            image_shape,\n            alpha=alpha_points,\n            size=size_points,\n            raise_if_out_of_image=raise_if_out_of_image)\n\n        heatmap = np.dstack([heatmap_lines, heatmap_points])\n        return np.max(heatmap, axis=2)\n\n    # TODO only draw line on image of size BB around line, then paste into full\n    #      sized image\n    def draw_lines_on_image(self, image, color=(0, 255, 0),\n                            alpha=1.0, size=3,\n                            antialiased=True,\n                            raise_if_out_of_image=False):\n        \"\"\"Draw the line segments of this line string on a given image.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            The image onto which to draw.\n            Expected to be ``uint8`` and of shape ``(H, W, C)`` with ``C``\n            usually being ``3`` (other values are not tested).\n            If a tuple, expected to be ``(H, W, C)`` and will lead to a new\n            ``uint8`` array of zeros being created.\n\n        color : int or iterable of int\n            Color to use as RGB, i.e. three values.\n\n        alpha : float, optional\n            Opacity of the line string. Higher values denote a more visible\n            line string.\n\n        size : int, optional\n            Thickness of the line segments.\n\n        antialiased : bool, optional\n            Whether to draw the line with anti-aliasing activated.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the line string is fully\n            outside of the image. If set to ``False``, no error will be\n            raised and only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        ndarray\n            `image` with line drawn on it.\n\n        \"\"\"\n        # pylint: disable=invalid-name, misplaced-comparison-constant\n        from .. import dtypes as iadt\n        from ..augmenters import blend as blendlib\n\n        image_was_empty = False\n        if isinstance(image, tuple):\n            image_was_empty = True\n            image = np.zeros(image, dtype=np.uint8)\n        assert image.ndim in [2, 3], (\n            \"Expected image or shape of form (H,W) or (H,W,C), \"\n            \"got shape %s.\" % (image.shape,))\n\n        if len(self.coords) <= 1 or alpha < 0 + 1e-4 or size < 1:\n            return np.copy(image)\n\n        if raise_if_out_of_image \\\n                and self.is_out_of_image(image, partly=False, fully=True):\n            raise Exception(\n                \"Cannot draw line string '%s' on image with shape %s, because \"\n                \"it would be out of bounds.\" % (\n                    self.__str__(), image.shape))\n\n        if image.ndim == 2:\n            assert ia.is_single_number(color), (\n                \"Got a 2D image. Expected then 'color' to be a single number, \"\n                \"but got %s.\" % (str(color),))\n            color = [color]\n        elif image.ndim == 3 and ia.is_single_number(color):\n            color = [color] * image.shape[-1]\n\n        image = image.astype(np.float32)\n        height, width = image.shape[0:2]\n\n        # We can't trivially exclude lines outside of the image here, because\n        # even if start and end point are outside, there can still be parts of\n        # the line inside the image.\n        # TODO Do this with edge-wise intersection tests\n        lines = []\n        for line_start, line_end in zip(self.coords[:-1], self.coords[1:]):\n            # note that line() expects order (y1, x1, y2, x2), hence ([1], [0])\n            lines.append((line_start[1], line_start[0],\n                          line_end[1], line_end[0]))\n\n        # skimage.draw.line can only handle integers\n        lines = np.round(np.float32(lines)).astype(np.int32)\n\n        # size == 0 is already covered above\n        # Note here that we have to be careful not to draw lines two times\n        # at their intersection points, e.g. for (p0, p1), (p1, 2) we could\n        # end up drawing at p1 twice, leading to higher values if alpha is\n        # used.\n        color = np.float32(color)\n        heatmap = np.zeros(image.shape[0:2], dtype=np.float32)\n        for line in lines:\n            if antialiased:\n                rr, cc, val = skimage.draw.line_aa(*line)\n            else:\n                rr, cc = skimage.draw.line(*line)\n                val = 1.0\n\n            # mask check here, because line() can generate coordinates\n            # outside of the image plane\n            rr_mask = np.logical_and(0 <= rr, rr < height)\n            cc_mask = np.logical_and(0 <= cc, cc < width)\n            mask = np.logical_and(rr_mask, cc_mask)\n\n            if np.any(mask):\n                rr = rr[mask]\n                cc = cc[mask]\n                val = val[mask] if not ia.is_single_number(val) else val\n                heatmap[rr, cc] = val * alpha\n\n        if size > 1:\n            kernel = np.ones((size, size), dtype=np.uint8)\n            heatmap = cv2.dilate(heatmap, kernel)\n\n        if image_was_empty:\n            image_blend = image + heatmap * color\n        else:\n            image_color_shape = image.shape[0:2]\n            if image.ndim == 3:\n                image_color_shape = image_color_shape + (1,)\n            image_color = np.tile(color, image_color_shape)\n            image_blend = blendlib.blend_alpha(image_color, image, heatmap)\n\n        image_blend = iadt.restore_dtypes_(image_blend, np.uint8)\n        return image_blend\n\n    def draw_points_on_image(self, image, color=(0, 128, 0),\n                             alpha=1.0, size=3,\n                             copy=True, raise_if_out_of_image=False):\n        \"\"\"Draw the points of this line string onto a given image.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            The image onto which to draw.\n            Expected to be ``uint8`` and of shape ``(H, W, C)`` with ``C``\n            usually being ``3`` (other values are not tested).\n            If a tuple, expected to be ``(H, W, C)`` and will lead to a new\n            ``uint8`` array of zeros being created.\n\n        color : iterable of int\n            Color to use as RGB, i.e. three values.\n\n        alpha : float, optional\n            Opacity of the line string points. Higher values denote a more\n            visible points.\n\n        size : int, optional\n            Size of the points in pixels.\n\n        copy : bool, optional\n            Whether it is allowed to draw directly in the input\n            array (``False``) or it has to be copied (``True``).\n            The routine may still have to copy, even if ``copy=False`` was\n            used. Always use the return value.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the line string is fully\n            outside of the image. If set to ``False``, no error will be\n            raised and only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` array of shape `image_shape` (no channel axis) with\n            drawn line string points. All values are in the\n            interval ``[0.0, 1.0]``.\n\n        \"\"\"\n        from .kps import KeypointsOnImage\n        kpsoi = KeypointsOnImage.from_xy_array(self.coords, shape=image.shape)\n        image = kpsoi.draw_on_image(\n            image, color=color, alpha=alpha,\n            size=size, copy=copy,\n            raise_if_out_of_image=raise_if_out_of_image)\n\n        return image\n\n    def draw_on_image(self, image,\n                      color=(0, 255, 0), color_lines=None, color_points=None,\n                      alpha=1.0, alpha_lines=None, alpha_points=None,\n                      size=1, size_lines=None, size_points=None,\n                      antialiased=True,\n                      raise_if_out_of_image=False):\n        \"\"\"Draw this line string onto an image.\n\n        Parameters\n        ----------\n        image : ndarray\n            The `(H,W,C)` `uint8` image onto which to draw the line string.\n\n        color : iterable of int, optional\n            Color to use as RGB, i.e. three values.\n            The color of the line and points are derived from this value,\n            unless they are set.\n\n        color_lines : None or iterable of int\n            Color to use for the line segments as RGB, i.e. three values.\n            If ``None``, this value is derived from `color`.\n\n        color_points : None or iterable of int\n            Color to use for the points as RGB, i.e. three values.\n            If ``None``, this value is derived from ``0.5 * color``.\n\n        alpha : float, optional\n            Opacity of the line string. Higher values denote more visible\n            points.\n            The alphas of the line and points are derived from this value,\n            unless they are set.\n\n        alpha_lines : None or float, optional\n            Opacity of the line string. Higher values denote more visible\n            line string.\n            If ``None``, this value is derived from `alpha`.\n\n        alpha_points : None or float, optional\n            Opacity of the line string points. Higher values denote more\n            visible points.\n            If ``None``, this value is derived from `alpha`.\n\n        size : int, optional\n            Size of the line string.\n            The sizes of the line and points are derived from this value,\n            unless they are set.\n\n        size_lines : None or int, optional\n            Thickness of the line segments.\n            If ``None``, this value is derived from `size`.\n\n        size_points : None or int, optional\n            Size of the points in pixels.\n            If ``None``, this value is derived from ``3 * size``.\n\n        antialiased : bool, optional\n            Whether to draw the line with anti-aliasing activated.\n            This does currently not affect the point drawing.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the line string is fully\n            outside of the image. If set to ``False``, no error will be\n            raised and only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        ndarray\n            Image with line string drawn on it.\n\n        \"\"\"\n        def _assert_not_none(arg_name, arg_value):\n            assert arg_value is not None, (\n                \"Expected '%s' to not be None, got type %s.\" % (\n                    arg_name, type(arg_value),))\n\n        _assert_not_none(\"color\", color)\n        _assert_not_none(\"alpha\", alpha)\n        _assert_not_none(\"size\", size)\n\n        color_lines = color_lines if color_lines is not None \\\n            else np.float32(color)\n        color_points = color_points if color_points is not None \\\n            else np.float32(color) * 0.5\n\n        alpha_lines = alpha_lines if alpha_lines is not None \\\n            else np.float32(alpha)\n        alpha_points = alpha_points if alpha_points is not None \\\n            else np.float32(alpha)\n\n        size_lines = size_lines if size_lines is not None else size\n        size_points = size_points if size_points is not None else size * 3\n\n        image = self.draw_lines_on_image(\n            image, color=np.array(color_lines).astype(np.uint8),\n            alpha=alpha_lines, size=size_lines,\n            antialiased=antialiased,\n            raise_if_out_of_image=raise_if_out_of_image)\n\n        image = self.draw_points_on_image(\n            image, color=np.array(color_points).astype(np.uint8),\n            alpha=alpha_points, size=size_points,\n            copy=False,\n            raise_if_out_of_image=raise_if_out_of_image)\n\n        return image\n\n    def extract_from_image(self, image, size=1, pad=True, pad_max=None,\n                           antialiased=True, prevent_zero_size=True):\n        \"\"\"Extract all image pixels covered by the line string.\n\n        This will only extract pixels overlapping with the line string.\n        As a rectangular image array has to be returned, non-overlapping\n        pixels will be set to zero.\n\n        This function will by default zero-pad the image if the line string is\n        partially/fully outside of the image. This is for consistency with\n        the same methods for bounding boxes and polygons.\n\n        Parameters\n        ----------\n        image : ndarray\n            The image of shape `(H,W,[C])` from which to extract the pixels\n            within the line string.\n\n        size : int, optional\n            Thickness of the line.\n\n        pad : bool, optional\n            Whether to zero-pad the image if the object is partially/fully\n            outside of it.\n\n        pad_max : None or int, optional\n            The maximum number of pixels that may be zero-paded on any side,\n            i.e. if this has value ``N`` the total maximum of added pixels\n            is ``4*N``.\n            This option exists to prevent extremely large images as a result of\n            single points being moved very far away during augmentation.\n\n        antialiased : bool, optional\n            Whether to apply anti-aliasing to the line string.\n\n        prevent_zero_size : bool, optional\n            Whether to prevent height or width of the extracted image from\n            becoming zero. If this is set to ``True`` and height or width of\n            the line string is below ``1``, the height/width will be increased\n            to ``1``. This can be useful to prevent problems, e.g. with image\n            saving or plotting. If it is set to ``False``, images will be\n            returned as ``(H', W')`` or ``(H', W', 3)`` with ``H`` or ``W``\n            potentially being ``0``.\n\n        Returns\n        -------\n        (H',W') ndarray or (H',W',C) ndarray\n            Pixels overlapping with the line string. Zero-padded if the\n            line string is partially/fully outside of the image and\n            ``pad=True``. If `prevent_zero_size` is activated, it is\n            guarantueed that ``H'>0`` and ``W'>0``, otherwise only\n            ``H'>=0`` and ``W'>=0``.\n\n        \"\"\"\n        from .bbs import BoundingBox\n\n        assert image.ndim in [2, 3], (\n            \"Expected image of shape (H,W,[C]), got shape %s.\" % (\n                image.shape,))\n\n        if len(self.coords) == 0 or size <= 0:\n            if prevent_zero_size:\n                return np.zeros((1, 1) + image.shape[2:], dtype=image.dtype)\n            return np.zeros((0, 0) + image.shape[2:], dtype=image.dtype)\n\n        xx = self.xx_int\n        yy = self.yy_int\n\n        # this would probably work if drawing was subpixel-accurate\n        # x1 = np.min(self.coords[:, 0]) - (size / 2)\n        # y1 = np.min(self.coords[:, 1]) - (size / 2)\n        # x2 = np.max(self.coords[:, 0]) + (size / 2)\n        # y2 = np.max(self.coords[:, 1]) + (size / 2)\n\n        # this works currently with non-subpixel-accurate drawing\n        sizeh = (size - 1) / 2\n        x1 = np.min(xx) - sizeh\n        y1 = np.min(yy) - sizeh\n        x2 = np.max(xx) + 1 + sizeh\n        y2 = np.max(yy) + 1 + sizeh\n        bb = BoundingBox(x1=x1, y1=y1, x2=x2, y2=y2)\n\n        if len(self.coords) == 1:\n            return bb.extract_from_image(image, pad=pad, pad_max=pad_max,\n                                         prevent_zero_size=prevent_zero_size)\n\n        heatmap = self.draw_lines_heatmap_array(\n            image.shape[0:2], alpha=1.0, size=size, antialiased=antialiased)\n        if image.ndim == 3:\n            heatmap = np.atleast_3d(heatmap)\n        image_masked = image.astype(np.float32) * heatmap\n        extract = bb.extract_from_image(image_masked, pad=pad, pad_max=pad_max,\n                                        prevent_zero_size=prevent_zero_size)\n        return np.clip(np.round(extract), 0, 255).astype(np.uint8)\n\n    def concatenate(self, other):\n        \"\"\"Concatenate this line string with another one.\n\n        This will add a line segment between the end point of this line string\n        and the start point of `other`.\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.lines.LineString or ndarray or iterable of tuple of number\n            The points to add to this line string.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineString\n            New line string with concatenated points.\n            The `label` of this line string will be kept.\n\n        \"\"\"\n        if not isinstance(other, LineString):\n            other = LineString(other)\n        return self.deepcopy(\n            coords=np.concatenate([self.coords, other.coords], axis=0))\n\n    # TODO add tests\n    def subdivide(self, points_per_edge):\n        \"\"\"Derive a new line string with ``N`` interpolated points per edge.\n\n        The interpolated points have (per edge) regular distances to each\n        other.\n\n        For each edge between points ``A`` and ``B`` this adds points\n        at ``A + (i/(1+N)) * (B - A)``, where ``i`` is the index of the added\n        point and ``N`` is the number of points to add per edge.\n\n        Calling this method two times will split each edge at its center\n        and then again split each newly created edge at their center.\n        It is equivalent to calling `subdivide(3)`.\n\n        Parameters\n        ----------\n        points_per_edge : int\n            Number of points to interpolate on each edge.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineString\n            Line string with subdivided edges.\n\n        \"\"\"\n        if len(self.coords) <= 1 or points_per_edge < 1:\n            return self.deepcopy()\n        coords = interpolate_points(self.coords, nb_steps=points_per_edge,\n                                    closed=False)\n        return self.deepcopy(coords=coords)\n\n    def to_keypoints(self):\n        \"\"\"Convert the line string points to keypoints.\n\n        Returns\n        -------\n        list of imgaug.augmentables.kps.Keypoint\n            Points of the line string as keypoints.\n\n        \"\"\"\n        # TODO get rid of this deferred import\n        from imgaug.augmentables.kps import Keypoint\n        return [Keypoint(x=x, y=y) for (x, y) in self.coords]\n\n    def to_bounding_box(self):\n        \"\"\"Generate a bounding box encapsulating the line string.\n\n        Returns\n        -------\n        None or imgaug.augmentables.bbs.BoundingBox\n            Bounding box encapsulating the line string.\n            ``None`` if the line string contained no points.\n\n        \"\"\"\n        from .bbs import BoundingBox\n        # we don't have to mind the case of len(.) == 1 here, because\n        # zero-sized BBs are considered valid\n        if len(self.coords) == 0:\n            return None\n        return BoundingBox(x1=np.min(self.xx), y1=np.min(self.yy),\n                           x2=np.max(self.xx), y2=np.max(self.yy),\n                           label=self.label)\n\n    def to_polygon(self):\n        \"\"\"Generate a polygon from the line string points.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            Polygon with the same corner points as the line string.\n            Note that the polygon might be invalid, e.g. contain less\n            than ``3`` points or have self-intersections.\n\n        \"\"\"\n        from .polys import Polygon\n        return Polygon(self.coords, label=self.label)\n\n    def to_heatmap(self, image_shape, size_lines=1, size_points=0,\n                   antialiased=True, raise_if_out_of_image=False):\n        \"\"\"Generate a heatmap object from the line string.\n\n        This is similar to\n        :func:`~imgaug.augmentables.lines.LineString.draw_lines_heatmap_array`,\n        executed with ``alpha=1.0``. The result is wrapped in a\n        :class:`~imgaug.augmentables.heatmaps.HeatmapsOnImage` object instead\n        of just an array. No points are drawn.\n\n        Parameters\n        ----------\n        image_shape : tuple of int\n            The shape of the image onto which to draw the line mask.\n\n        size_lines : int, optional\n            Thickness of the line.\n\n        size_points : int, optional\n            Size of the points in pixels.\n\n        antialiased : bool, optional\n            Whether to draw the line with anti-aliasing activated.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the line string is fully\n            outside of the image. If set to ``False``, no error will be\n            raised and only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Heatmap object containing drawn line string.\n\n        \"\"\"\n        from .heatmaps import HeatmapsOnImage\n        return HeatmapsOnImage(\n            self.draw_heatmap_array(\n                image_shape, size_lines=size_lines, size_points=size_points,\n                antialiased=antialiased,\n                raise_if_out_of_image=raise_if_out_of_image),\n            shape=image_shape\n        )\n\n    def to_segmentation_map(self, image_shape, size_lines=1, size_points=0,\n                            raise_if_out_of_image=False):\n        \"\"\"Generate a segmentation map object from the line string.\n\n        This is similar to\n        :func:`~imgaug.augmentables.lines.LineString.draw_mask`.\n        The result is wrapped in a ``SegmentationMapsOnImage`` object\n        instead of just an array.\n\n        Parameters\n        ----------\n        image_shape : tuple of int\n            The shape of the image onto which to draw the line mask.\n\n        size_lines : int, optional\n            Thickness of the line.\n\n        size_points : int, optional\n            Size of the points in pixels.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the line string is fully\n            outside of the image. If set to ``False``, no error will be\n            raised and only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            Segmentation map object containing drawn line string.\n\n        \"\"\"\n        from .segmaps import SegmentationMapsOnImage\n        return SegmentationMapsOnImage(\n            self.draw_mask(\n                image_shape, size_lines=size_lines, size_points=size_points,\n                raise_if_out_of_image=raise_if_out_of_image),\n            shape=image_shape\n        )\n\n    # TODO make this non-approximate\n    def coords_almost_equals(self, other, max_distance=1e-4, points_per_edge=8):\n        \"\"\"Compare this and another LineString's coordinates.\n\n        This is an approximate method based on pointwise distances and can\n        in rare corner cases produce wrong outputs.\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.lines.LineString or tuple of number or ndarray or list of ndarray or list of tuple of number\n            The other line string or its coordinates.\n\n        max_distance : float, optional\n            Max distance of any point from the other line string before\n            the two line strings are evaluated to be unequal.\n\n        points_per_edge : int, optional\n            How many points to interpolate on each edge.\n\n        Returns\n        -------\n        bool\n            Whether the two LineString's coordinates are almost identical,\n            i.e. the max distance is below the threshold.\n            If both have no coordinates, ``True`` is returned.\n            If only one has no coordinates, ``False`` is returned.\n            Beyond that, the number of points is not evaluated.\n\n        \"\"\"\n        if isinstance(other, LineString):\n            pass\n        elif isinstance(other, tuple):\n            other = LineString([other])\n        else:\n            other = LineString(other)\n\n        if len(self.coords) == 0 and len(other.coords) == 0:\n            return True\n        if 0 in [len(self.coords), len(other.coords)]:\n            # only one of the two line strings has no coords\n            return False\n\n        self_subd = self.subdivide(points_per_edge)\n        other_subd = other.subdivide(points_per_edge)\n\n        dist_self2other = self_subd.compute_pointwise_distances(other_subd)\n        dist_other2self = other_subd.compute_pointwise_distances(self_subd)\n        dist = max(np.max(dist_self2other), np.max(dist_other2self))\n        return dist < max_distance\n\n    def almost_equals(self, other, max_distance=1e-4, points_per_edge=8):\n        \"\"\"Compare this and another line string.\n\n        Parameters\n        ----------\n        other: imgaug.augmentables.lines.LineString\n            The other object to compare against. Expected to be a\n            ``LineString``.\n\n        max_distance : float, optional\n            See :func:`~imgaug.augmentables.lines.LineString.coords_almost_equals`.\n\n        points_per_edge : int, optional\n            See :func:`~imgaug.augmentables.lines.LineString.coords_almost_equals`.\n\n        Returns\n        -------\n        bool\n            ``True`` if the coordinates are almost equal and additionally\n            the labels are equal. Otherwise ``False``.\n\n        \"\"\"\n        if self.label != other.label:\n            return False\n        return self.coords_almost_equals(\n            other, max_distance=max_distance, points_per_edge=points_per_edge)\n\n    def copy(self, coords=None, label=None):\n        \"\"\"Create a shallow copy of this line string.\n\n        Parameters\n        ----------\n        coords : None or iterable of tuple of number or ndarray\n            If not ``None``, then the coords of the copied object will be set\n            to this value.\n\n        label : None or str\n            If not ``None``, then the label of the copied object will be set to\n            this value.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineString\n            Shallow copy.\n\n        \"\"\"\n        return LineString(coords=self.coords if coords is None else coords,\n                          label=self.label if label is None else label)\n\n    def deepcopy(self, coords=None, label=None):\n        \"\"\"Create a deep copy of this line string.\n\n        Parameters\n        ----------\n        coords : None or iterable of tuple of number or ndarray\n            If not ``None``, then the coords of the copied object will be set\n            to this value.\n\n        label : None or str\n            If not ``None``, then the label of the copied object will be set to\n            this value.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineString\n            Deep copy.\n\n        \"\"\"\n        return LineString(\n            coords=np.copy(self.coords) if coords is None else coords,\n            label=copylib.deepcopy(self.label) if label is None else label)\n\n    def __getitem__(self, indices):\n        \"\"\"Get the coordinate(s) with given indices.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        ndarray\n            xy-coordinate(s) as ``ndarray``.\n\n        \"\"\"\n        return self.coords[indices]\n\n    def __iter__(self):\n        \"\"\"Iterate over the coordinates of this instance.\n\n        Added in 0.4.0.\n\n        Yields\n        ------\n        ndarray\n            An ``(2,)`` ``ndarray`` denoting an xy-coordinate pair.\n\n        \"\"\"\n        return iter(self.coords)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        points_str = \", \".join(\n            [\"(%.2f, %.2f)\" % (x, y) for x, y in self.coords])\n        return \"LineString([%s], label=%s)\" % (points_str, self.label)\n\n\n# TODO\n# distance\n# hausdorff_distance\n# is_fully_within_image()\n# is_partly_within_image()\n# is_out_of_image()\n# draw()\n# draw_mask()\n# extract_from_image()\n# to_keypoints()\n# intersects(other)\n# concat(other)\n# is_self_intersecting()\n# remove_self_intersections()\nclass LineStringsOnImage(IAugmentable):\n    \"\"\"Object that represents all line strings on a single image.\n\n    Parameters\n    ----------\n    line_strings : list of imgaug.augmentables.lines.LineString\n        List of line strings on the image.\n\n    shape : tuple of int or ndarray\n        The shape of the image on which the objects are placed.\n        Either an image with shape ``(H,W,[C])`` or a ``tuple`` denoting\n        such an image shape.\n\n    Examples\n    --------\n    >>> import numpy as np\n    >>> from imgaug.augmentables.lines import LineString, LineStringsOnImage\n    >>>\n    >>> image = np.zeros((100, 100))\n    >>> lss = [\n    >>>     LineString([(0, 0), (10, 0)]),\n    >>>     LineString([(10, 20), (30, 30), (50, 70)])\n    >>> ]\n    >>> lsoi = LineStringsOnImage(lss, shape=image.shape)\n\n    \"\"\"\n\n    def __init__(self, line_strings, shape):\n        assert ia.is_iterable(line_strings), (\n            \"Expected 'line_strings' to be an iterable, got type '%s'.\" % (\n                type(line_strings),))\n        assert all([isinstance(v, LineString) for v in line_strings]), (\n            \"Expected iterable of LineString, got types: %s.\" % (\n                \", \".join([str(type(v)) for v in line_strings])\n            ))\n        self.line_strings = line_strings\n        self.shape = _handle_on_image_shape(shape, self)\n\n    @property\n    def items(self):\n        \"\"\"Get the line strings in this container.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of LineString\n            Line strings within this container.\n\n        \"\"\"\n        return self.line_strings\n\n    @items.setter\n    def items(self, value):\n        \"\"\"Set the line strings in this container.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        value : list of LineString\n            Line strings within this container.\n\n        \"\"\"\n        self.line_strings = value\n\n    @property\n    def empty(self):\n        \"\"\"Estimate whether this object contains zero line strings.\n\n        Returns\n        -------\n        bool\n            ``True`` if this object contains zero line strings.\n\n        \"\"\"\n        return len(self.line_strings) == 0\n\n    def on_(self, image):\n        \"\"\"Project the line strings from one image shape to a new one in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            The new image onto which to project.\n            Either an image with shape ``(H,W,[C])`` or a tuple denoting\n            such an image shape.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStrings\n            Object containing all projected line strings.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        # pylint: disable=invalid-name\n        on_shape = normalize_imglike_shape(image)\n        if on_shape[0:2] == self.shape[0:2]:\n            self.shape = on_shape  # channels may differ\n            return self\n\n        for i, item in enumerate(self.items):\n            self.line_strings[i] = item.project_(self.shape, on_shape)\n        self.shape = on_shape\n        return self\n\n    def on(self, image):\n        \"\"\"Project the line strings from one image shape to a new one.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            The new image onto which to project.\n            Either an image with shape ``(H,W,[C])`` or a tuple denoting\n            such an image shape.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStrings\n            Object containing all projected line strings.\n\n        \"\"\"\n        # pylint: disable=invalid-name\n        return self.deepcopy().on_(image)\n\n    @classmethod\n    def from_xy_arrays(cls, xy, shape):\n        \"\"\"Convert an ``(N,M,2)`` ndarray to a ``LineStringsOnImage`` object.\n\n        This is the inverse of\n        :func:`~imgaug.augmentables.lines.LineStringsOnImage.to_xy_array`.\n\n        Parameters\n        ----------\n        xy : (N,M,2) ndarray or iterable of (M,2) ndarray\n            Array containing the point coordinates ``N`` line strings\n            with each ``M`` points given as ``(x,y)`` coordinates.\n            ``M`` may differ if an iterable of arrays is used.\n            Each array should usually be of dtype ``float32``.\n\n        shape : tuple of int\n            ``(H,W,[C])`` shape of the image on which the line strings are\n            placed.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage\n            Object containing a list of ``LineString`` objects following the\n            provided point coordinates.\n\n        \"\"\"\n        lss = []\n        for xy_ls in xy:\n            lss.append(LineString(xy_ls))\n        return cls(lss, shape)\n\n    def to_xy_arrays(self, dtype=np.float32):\n        \"\"\"Convert this object to an iterable of ``(M,2)`` arrays of points.\n\n        This is the inverse of\n        :func:`~imgaug.augmentables.lines.LineStringsOnImage.from_xy_array`.\n\n        Parameters\n        ----------\n        dtype : numpy.dtype, optional\n            Desired output datatype of the ndarray.\n\n        Returns\n        -------\n        list of ndarray\n            The arrays of point coordinates, each given as ``(M,2)``.\n\n        \"\"\"\n        from .. import dtypes as iadt\n        return [iadt.restore_dtypes_(np.copy(ls.coords), dtype)\n                for ls in self.line_strings]\n\n    def draw_on_image(self, image,\n                      color=(0, 255, 0), color_lines=None, color_points=None,\n                      alpha=1.0, alpha_lines=None, alpha_points=None,\n                      size=1, size_lines=None, size_points=None,\n                      antialiased=True,\n                      raise_if_out_of_image=False):\n        \"\"\"Draw all line strings onto a given image.\n\n        Parameters\n        ----------\n        image : ndarray\n            The ``(H,W,C)`` ``uint8`` image onto which to draw the line\n            strings.\n\n        color : iterable of int, optional\n            Color to use as RGB, i.e. three values.\n            The color of the lines and points are derived from this value,\n            unless they are set.\n\n        color_lines : None or iterable of int\n            Color to use for the line segments as RGB, i.e. three values.\n            If ``None``, this value is derived from `color`.\n\n        color_points : None or iterable of int\n            Color to use for the points as RGB, i.e. three values.\n            If ``None``, this value is derived from ``0.5 * color``.\n\n        alpha : float, optional\n            Opacity of the line strings. Higher values denote more visible\n            points.\n            The alphas of the line and points are derived from this value,\n            unless they are set.\n\n        alpha_lines : None or float, optional\n            Opacity of the line strings. Higher values denote more visible\n            line string.\n            If ``None``, this value is derived from `alpha`.\n\n        alpha_points : None or float, optional\n            Opacity of the line string points. Higher values denote more\n            visible points.\n            If ``None``, this value is derived from `alpha`.\n\n        size : int, optional\n            Size of the line strings.\n            The sizes of the line and points are derived from this value,\n            unless they are set.\n\n        size_lines : None or int, optional\n            Thickness of the line segments.\n            If ``None``, this value is derived from `size`.\n\n        size_points : None or int, optional\n            Size of the points in pixels.\n            If ``None``, this value is derived from ``3 * size``.\n\n        antialiased : bool, optional\n            Whether to draw the lines with anti-aliasing activated.\n            This does currently not affect the point drawing.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if a line string is fully\n            outside of the image. If set to ``False``, no error will be\n            raised and only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        ndarray\n            Image with line strings drawn on it.\n\n        \"\"\"\n        # TODO improve efficiency here by copying only once\n        for ls in self.line_strings:\n            image = ls.draw_on_image(\n                image,\n                color=color, color_lines=color_lines, color_points=color_points,\n                alpha=alpha, alpha_lines=alpha_lines, alpha_points=alpha_points,\n                size=size, size_lines=size_lines, size_points=size_points,\n                antialiased=antialiased,\n                raise_if_out_of_image=raise_if_out_of_image\n            )\n\n        return image\n\n    def remove_out_of_image_(self, fully=True, partly=False):\n        \"\"\"\n        Remove all LS that are fully/partially outside of an image in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        fully : bool, optional\n            Whether to remove line strings that are fully outside of the image.\n\n        partly : bool, optional\n            Whether to remove line strings that are partially outside of the\n            image.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage\n            Reduced set of line strings. Those that are fully/partially\n            outside of the given image plane are removed.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        self.line_strings = [\n            ls for ls in self.line_strings\n            if not ls.is_out_of_image(self.shape, fully=fully, partly=partly)]\n        return self\n\n    def remove_out_of_image(self, fully=True, partly=False):\n        \"\"\"\n        Remove all line strings that are fully/partially outside of an image.\n\n        Parameters\n        ----------\n        fully : bool, optional\n            Whether to remove line strings that are fully outside of the image.\n\n        partly : bool, optional\n            Whether to remove line strings that are partially outside of the\n            image.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage\n            Reduced set of line strings. Those that are fully/partially\n            outside of the given image plane are removed.\n\n        \"\"\"\n        return self.copy().remove_out_of_image_(fully=fully, partly=partly)\n\n    def remove_out_of_image_fraction_(self, fraction):\n        \"\"\"Remove all LS with an OOI fraction of at least `fraction` in-place.\n\n        'OOI' is the abbreviation for 'out of image'.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        fraction : number\n            Minimum out of image fraction that a line string has to have in\n            order to be removed. A fraction of ``1.0`` removes only line\n            strings that are ``100%`` outside of the image. A fraction of\n            ``0.0`` removes all line strings.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage\n            Reduced set of line strings, with those that had an out of image\n            fraction greater or equal the given one removed.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        return _remove_out_of_image_fraction_(self, fraction)\n\n    def remove_out_of_image_fraction(self, fraction):\n        \"\"\"Remove all LS with an out of image fraction of at least `fraction`.\n\n        Parameters\n        ----------\n        fraction : number\n            Minimum out of image fraction that a line string has to have in\n            order to be removed. A fraction of ``1.0`` removes only line\n            strings that are ``100%`` outside of the image. A fraction of\n            ``0.0`` removes all line strings.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage\n            Reduced set of line strings, with those that had an out of image\n            fraction greater or equal the given one removed.\n\n        \"\"\"\n        return self.copy().remove_out_of_image_fraction_(fraction)\n\n    def clip_out_of_image_(self):\n        \"\"\"\n        Clip off all parts of the LSs that are outside of an image in-place.\n\n        .. note::\n\n            The result can contain fewer line strings than the input did. That\n            happens when a polygon is fully outside of the image plane.\n\n        .. note::\n\n            The result can also contain *more* line strings than the input\n            did. That happens when distinct parts of a line string are only\n            connected by line segments that are outside of the image plane and\n            hence will be clipped off, resulting in two or more unconnected\n            line string parts that are left in the image plane.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage\n            Line strings, clipped to fall within the image dimensions.\n            The count of output line strings may differ from the input count.\n\n        \"\"\"\n        self.line_strings = [\n            ls_clipped\n            for ls in self.line_strings\n            for ls_clipped in ls.clip_out_of_image(self.shape)]\n        return self\n\n    def clip_out_of_image(self):\n        \"\"\"\n        Clip off all parts of the line strings that are outside of an image.\n\n        .. note::\n\n            The result can contain fewer line strings than the input did. That\n            happens when a polygon is fully outside of the image plane.\n\n        .. note::\n\n            The result can also contain *more* line strings than the input\n            did. That happens when distinct parts of a line string are only\n            connected by line segments that are outside of the image plane and\n            hence will be clipped off, resulting in two or more unconnected\n            line string parts that are left in the image plane.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage\n            Line strings, clipped to fall within the image dimensions.\n            The count of output line strings may differ from the input count.\n\n        \"\"\"\n        return self.copy().clip_out_of_image_()\n\n    def shift_(self, x=0, y=0):\n        \"\"\"Move the line strings along the x/y-axis in-place.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage\n            Shifted line strings.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        for i, ls in enumerate(self.line_strings):\n            self.line_strings[i] = ls.shift_(x=x, y=y)\n        return self\n\n    def shift(self, x=0, y=0, top=None, right=None, bottom=None, left=None):\n        \"\"\"Move the line strings along the x/y-axis.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        top : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            top (towards the bottom).\n\n        right : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            right (towads the left).\n\n        bottom : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            bottom (towards the top).\n\n        left : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            left (towards the right).\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage\n            Shifted line strings.\n\n        \"\"\"\n        x, y = _normalize_shift_args(\n            x, y, top=top, right=right, bottom=bottom, left=left)\n        return self.deepcopy().shift_(x=x, y=y)\n\n    def to_xy_array(self):\n        \"\"\"Convert all line string coordinates to one array of shape ``(N,2)``.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        (N, 2) ndarray\n            Array containing all xy-coordinates of all line strings within this\n            instance.\n\n        \"\"\"\n        if self.empty:\n            return np.zeros((0, 2), dtype=np.float32)\n        return np.concatenate([ls.coords for ls in self.line_strings])\n\n    def fill_from_xy_array_(self, xy):\n        \"\"\"Modify the corner coordinates of all line strings in-place.\n\n        .. note::\n\n            This currently expects that `xy` contains exactly as many\n            coordinates as the line strings within this instance have corner\n            points. Otherwise, an ``AssertionError`` will be raised.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        xy : (N, 2) ndarray or iterable of iterable of number\n            XY-Coordinates of ``N`` corner points. ``N`` must match the\n            number of corner points in all line strings within this instance.\n\n        Returns\n        -------\n        LineStringsOnImage\n            This instance itself, with updated coordinates.\n            Note that the instance was modified in-place.\n\n        \"\"\"\n        xy = np.array(xy, dtype=np.float32)\n\n        # note that np.array([]) is (0,), not (0, 2)\n        assert xy.shape[0] == 0 or (xy.ndim == 2 and xy.shape[-1] == 2), (  # pylint: disable=unsubscriptable-object\n            \"Expected input array to have shape (N,2), \"\n            \"got shape %s.\" % (xy.shape,))\n\n        counter = 0\n        for ls in self.line_strings:\n            nb_points = len(ls.coords)\n            assert counter + nb_points <= len(xy), (\n                \"Received fewer points than there are corner points in \"\n                \"all line strings. Got %d points, expected %d.\" % (\n                    len(xy),\n                    sum([len(ls_.coords) for ls_ in self.line_strings])))\n\n            ls.coords[:, ...] = xy[counter:counter+nb_points]\n            counter += nb_points\n\n        assert counter == len(xy), (\n            \"Expected to get exactly as many xy-coordinates as there are \"\n            \"points in all line strings polygons within this instance. \"\n            \"Got %d points, could only assign %d points.\" % (\n                len(xy), counter,))\n\n        return self\n\n    def to_keypoints_on_image(self):\n        \"\"\"Convert the line strings to one ``KeypointsOnImage`` instance.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            A keypoints instance containing ``N`` coordinates for a total\n            of ``N`` points in the ``coords`` attributes of all line strings.\n            Order matches the order in ``line_strings`` and ``coords``\n            attributes.\n\n        \"\"\"\n        from . import KeypointsOnImage\n        if self.empty:\n            return KeypointsOnImage([], shape=self.shape)\n        coords = np.concatenate(\n            [ls.coords for ls in self.line_strings],\n            axis=0)\n        return KeypointsOnImage.from_xy_array(coords,\n                                              shape=self.shape)\n\n    def invert_to_keypoints_on_image_(self, kpsoi):\n        \"\"\"Invert the output of ``to_keypoints_on_image()`` in-place.\n\n        This function writes in-place into this ``LineStringsOnImage``\n        instance.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        kpsoi : imgaug.augmentables.kps.KeypointsOnImages\n            Keypoints to convert back to line strings, i.e. the outputs\n            of ``to_keypoints_on_image()``.\n\n        Returns\n        -------\n        LineStringsOnImage\n            Line strings container with updated coordinates.\n            Note that the instance is also updated in-place.\n\n        \"\"\"\n        lss = self.line_strings\n        coordss = [ls.coords for ls in lss]\n        nb_points_exp = sum([len(coords) for coords in coordss])\n        assert len(kpsoi.keypoints) == nb_points_exp, (\n            \"Expected %d coordinates, got %d.\" % (\n                nb_points_exp, len(kpsoi.keypoints)))\n\n        xy_arr = kpsoi.to_xy_array()\n\n        counter = 0\n        for ls in lss:\n            coords = ls.coords\n            coords[:, :] = xy_arr[counter:counter+len(coords), :]\n            counter += len(coords)\n        self.shape = kpsoi.shape\n        return self\n\n    def copy(self, line_strings=None, shape=None):\n        \"\"\"Create a shallow copy of this object.\n\n        Parameters\n        ----------\n        line_strings : None or list of imgaug.augmentables.lines.LineString, optional\n            List of line strings on the image.\n            If not ``None``, then the ``line_strings`` attribute of the copied\n            object will be set to this value.\n\n        shape : None or tuple of int or ndarray, optional\n            The shape of the image on which the objects are placed.\n            Either an image with shape ``(H,W,[C])`` or a tuple denoting\n            such an image shape.\n            If not ``None``, then the ``shape`` attribute of the copied object\n            will be set to this value.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage\n            Shallow copy.\n\n        \"\"\"\n        if line_strings is None:\n            line_strings = self.line_strings[:]\n        if shape is None:\n            # use tuple() here in case the shape was provided as a list\n            shape = tuple(self.shape)\n\n        return LineStringsOnImage(line_strings, shape)\n\n    def deepcopy(self, line_strings=None, shape=None):\n        \"\"\"Create a deep copy of the object.\n\n        Parameters\n        ----------\n        line_strings : None or list of imgaug.augmentables.lines.LineString, optional\n            List of line strings on the image.\n            If not ``None``, then the ``line_strings`` attribute of the copied\n            object will be set to this value.\n\n        shape : None or tuple of int or ndarray, optional\n            The shape of the image on which the objects are placed.\n            Either an image with shape ``(H,W,[C])`` or a tuple denoting\n            such an image shape.\n            If not ``None``, then the ``shape`` attribute of the copied object\n            will be set to this value.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage\n            Deep copy.\n\n        \"\"\"\n        # Manual copy is far faster than deepcopy, so use manual copy here.\n        if line_strings is None:\n            line_strings = [ls.deepcopy() for ls in self.line_strings]\n        if shape is None:\n            # use tuple() here in case the shape was provided as a list\n            shape = tuple(self.shape)\n\n        return LineStringsOnImage(line_strings, shape)\n\n    def __getitem__(self, indices):\n        \"\"\"Get the line string(s) with given indices.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of imgaug.augmentables.lines.LineString\n            Line string(s) with given indices.\n\n        \"\"\"\n        return self.line_strings[indices]\n\n    def __iter__(self):\n        \"\"\"Iterate over the line strings in this container.\n\n        Added in 0.4.0.\n\n        Yields\n        ------\n        LineString\n            A line string in this container.\n            The order is identical to the order in the line string list\n            provided upon class initialization.\n\n        \"\"\"\n        return iter(self.line_strings)\n\n    def __len__(self):\n        \"\"\"Get the number of items in this instance.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        int\n            Number of items in this instance.\n\n        \"\"\"\n        return len(self.items)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"LineStringsOnImage(%s, shape=%s)\" % (\n            str(self.line_strings), self.shape)\n\n\ndef _is_point_on_line(line_start, line_end, point, eps=1e-4):\n    dist_s2e = np.linalg.norm(np.float32(line_start) - np.float32(line_end))\n    dist_s2p2e = (\n        np.linalg.norm(np.float32(line_start) - np.float32(point))\n        + np.linalg.norm(np.float32(point) - np.float32(line_end))\n    )\n    return -eps < (dist_s2p2e - dist_s2e) < eps\n\n\ndef _flatten_shapely_collection(collection):\n    import shapely.geometry\n    if not isinstance(collection, list):\n        collection = [collection]\n    for item in collection:\n        if hasattr(item, \"geoms\"):\n            for subitem in _flatten_shapely_collection(item.geoms):\n                # MultiPoint.geoms actually returns a GeometrySequence\n                if isinstance(subitem, shapely.geometry.base.GeometrySequence):\n                    for subsubel in subitem:\n                        yield subsubel\n                else:\n                    yield _flatten_shapely_collection(subitem)\n        else:\n            yield item\n\n\ndef _convert_var_to_shapely_geometry(var):\n    import shapely.geometry\n    if isinstance(var, tuple):\n        geom = shapely.geometry.Point(var[0], var[1])\n    elif isinstance(var, list):\n        assert len(var) > 0, (\n            \"Expected list to contain at least one coordinate, \"\n            \"got %d coordinates.\" % (len(var),))\n        if isinstance(var[0], tuple):\n            geom = shapely.geometry.LineString(var)\n        elif all([isinstance(v, LineString) for v in var]):\n            geom = shapely.geometry.MultiLineString([\n                shapely.geometry.LineString(ls.coords) for ls in var\n            ])\n        else:\n            raise ValueError(\n                \"Could not convert list-input to shapely geometry. Invalid \"\n                \"datatype. List elements had datatypes: %s.\" % (\n                    \", \".join([str(type(v)) for v in var]),))\n    elif isinstance(var, LineString):\n        geom = shapely.geometry.LineString(var.coords)\n    else:\n        raise ValueError(\n            \"Could not convert input to shapely geometry. Invalid datatype. \"\n            \"Got: %s\" % (type(var),))\n    return geom\n"
  },
  {
    "path": "imgaug/augmentables/normalization.py",
    "content": "\"\"\"Functions dealing with normalization of user input data to imgaug classes.\"\"\"\nfrom __future__ import print_function, division, absolute_import\nimport functools\n\nimport numpy as np\n\nfrom .. import imgaug as ia\nfrom .. import dtypes as iadt\nfrom .base import IAugmentable\n\n\ndef _preprocess_shapes(shapes):\n    if shapes is None:\n        return None\n    if ia.is_np_array(shapes):\n        assert shapes.ndim in [3, 4], (\n            \"Expected array 'shapes' to be 3- or 4-dimensional, got %d \"\n            \"dimensions and shape %s instead.\" % (shapes.ndim, shapes.shape))\n        return [image.shape for image in shapes]\n\n    assert isinstance(shapes, list), (\n        \"Expected 'shapes' to be None or ndarray or list, got type %s \"\n        \"instead.\" % (type(shapes),))\n    result = []\n    for shape_i in shapes:\n        if isinstance(shape_i, tuple):\n            result.append(shape_i)\n        else:\n            assert ia.is_np_array(shape_i), (\n                \"Expected each entry in list 'shapes' to be either a \"\n                \"tuple or an ndarray, got type %s.\" % (type(shape_i),))\n            result.append(shape_i.shape)\n    return result\n\n\ndef _assert_exactly_n_shapes(shapes, n, from_ntype, to_ntype):\n    if shapes is None:\n        raise ValueError(\n            \"Tried to convert data of form '%s' to '%s'. This required %d \"\n            \"corresponding image shapes, but argument 'shapes' was set to \"\n            \"None. This can happen e.g. if no images were provided in a \"\n            \"Batch, as these would usually be used to automatically derive \"\n            \"image shapes.\" % (from_ntype, to_ntype, n))\n\n    if len(shapes) != n:\n        raise ValueError(\n            \"Tried to convert data of form '%s' to '%s'. This required \"\n            \"exactly %d corresponding image shapes, but instead %d were \"\n            \"provided. This can happen e.g. if more images were provided \"\n            \"than corresponding augmentables, e.g. 10 images but only 5 \"\n            \"segmentation maps. It can also happen if there was a \"\n            \"misunderstanding about how an augmentable input would be \"\n            \"parsed. E.g. if a list of N (x,y)-tuples was provided as \"\n            \"keypoints and the expectation was that this would be parsed \"\n            \"as one keypoint per image for N images, but instead it was \"\n            \"parsed as N keypoints on 1 image (i.e. 'shapes' would have to \"\n            \"contain 1 shape, but N would be provided). To avoid this, it \"\n            \"is recommended to provide imgaug standard classes, e.g. \"\n            \"KeypointsOnImage for keypoints instead of lists of \"\n            \"tuples.\" % (from_ntype, to_ntype, n, len(shapes)))\n\n\ndef _assert_single_array_ndim(arr, ndim, shape_str, to_ntype):\n    if arr.ndim != ndim:\n        raise ValueError(\n            \"Tried to convert an array to list of %s. Expected \"\n            \"that array to be of shape %s, i.e. %d-dimensional, but \"\n            \"got %d dimensions instead.\" % (\n                to_ntype, shape_str, ndim, arr.ndim,))\n\n\ndef _assert_many_arrays_ndim(arrs, ndim, shape_str, to_ntype):\n    # For polygons, this can be a list of lists of arrays, hence we must\n    # flatten the lists here.\n    # itertools.chain.from_iterable() seems to flatten the arrays too, so it\n    # cannot be used here.\n    iterable_type_str = \"iterable\"\n    if len(arrs) == 0:\n        arrs_flat = []\n    elif ia.is_np_array(arrs[0]):\n        arrs_flat = arrs\n    else:\n        iterable_type_str = \"iterable of iterable\"\n        arrs_flat = [arr for arrs_sublist in arrs for arr in arrs_sublist]\n\n    if any([arr.ndim != ndim for arr in arrs_flat]):\n        raise ValueError(\n            \"Tried to convert an %s of arrays to a list of \"\n            \"%s. Expected each array to be of shape %s, \"\n            \"i.e. to be %d-dimensional, but got dimensions %s \"\n            \"instead (array shapes: %s).\" % (\n                iterable_type_str, to_ntype, shape_str, ndim,\n                \", \".join([str(arr.ndim) for arr in arrs_flat]),\n                \", \".join([str(arr.shape) for arr in arrs_flat])))\n\n\ndef _assert_single_array_last_dim_exactly(arr, size, to_ntype):\n    if arr.shape[-1] != size:\n        raise ValueError(\n            \"Tried to convert an array to a list of %s. Expected the array's \"\n            \"last dimension to have size %d, but got %d instead (array \"\n            \"shape: %s).\" % (\n                to_ntype, size, arr.shape[-1], str(arr.shape)))\n\n\ndef _assert_many_arrays_last_dim_exactly(arrs, size, to_ntype):\n    # For polygons, this can be a list of lists of arrays, hence we must\n    # flatten the lists here.\n    # itertools.chain.from_iterable() seems to flatten the arrays too, so it\n    # cannot be used here.\n    iterable_type_str = \"iterable\"\n    if len(arrs) == 0:\n        arrs_flat = []\n    elif ia.is_np_array(arrs[0]):\n        arrs_flat = arrs\n    else:\n        iterable_type_str = \"iterable of iterable\"\n        arrs_flat = [arr for arrs_sublist in arrs for arr in arrs_sublist]\n\n    if any([arr.shape[-1] != size for arr in arrs_flat]):\n        raise ValueError(\n            \"Tried to convert an %s of array to a list of %s. Expected the \"\n            \"arrays' last dimensions to have size %d, but got %s instead \"\n            \"(array shapes: %s).\" % (\n                iterable_type_str, to_ntype, size,\n                \", \".join([str(arr.shape[-1]) for arr in arrs_flat]),\n                \", \".join([str(arr.shape) for arr in arrs_flat])))\n\n\ndef normalize_images(images):\n    if images is None:\n        return None\n    if ia.is_np_array(images):\n        if images.ndim == 2:\n            return images[np.newaxis, ..., np.newaxis]\n        if images.ndim == 3:\n            return images[..., np.newaxis]\n        return images\n    if ia.is_iterable(images):\n        result = []\n        for image in images:\n            assert image.ndim in [2, 3], (\n                \"Got a list of arrays as argument 'images'. Expected each \"\n                \"array in that list to have 2 or 3 dimensions, i.e. shape \"\n                \"(H,W) or (H,W,C). Got %d dimensions \"\n                \"instead.\" % (image.ndim,))\n\n            if image.ndim == 2:\n                result.append(image[..., np.newaxis])\n            else:\n                result.append(image)\n        return result\n    raise ValueError(\n        \"Expected argument 'images' to be any of the following: \"\n        \"None or array or iterable of array. Got type: %s.\" % (\n            type(images),))\n\n\ndef normalize_heatmaps(inputs, shapes=None):\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.heatmaps import HeatmapsOnImage\n\n    shapes = _preprocess_shapes(shapes)\n    ntype = estimate_heatmaps_norm_type(inputs)\n    _assert_exactly_n_shapes_partial = functools.partial(\n        _assert_exactly_n_shapes,\n        from_ntype=ntype, to_ntype=\"List[HeatmapsOnImage]\", shapes=shapes)\n\n    if ntype == \"None\":\n        return None\n    if ntype == \"array[float]\":\n        _assert_single_array_ndim(inputs, 4, \"(N,H,W,C)\", \"HeatmapsOnImage\")\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [HeatmapsOnImage(attr_i, shape=shape_i)\n                for attr_i, shape_i in zip(inputs, shapes)]\n    if ntype == \"HeatmapsOnImage\":\n        return [inputs]\n    if ntype == \"iterable[empty]\":\n        return None\n    if ntype == \"iterable-array[float]\":\n        _assert_many_arrays_ndim(inputs, 3, \"(H,W,C)\", \"HeatmapsOnImage\")\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [HeatmapsOnImage(attr_i, shape=shape_i)\n                for attr_i, shape_i in zip(inputs, shapes)]\n    assert ntype == \"iterable-HeatmapsOnImage\", (\n        \"Got unknown normalization type '%s'.\" % (ntype,))\n    return inputs  # len allowed to differ from len of images\n\n\ndef normalize_segmentation_maps(inputs, shapes=None):\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.segmaps import SegmentationMapsOnImage\n\n    shapes = _preprocess_shapes(shapes)\n    ntype = estimate_segmaps_norm_type(inputs)\n    _assert_exactly_n_shapes_partial = functools.partial(\n        _assert_exactly_n_shapes,\n        from_ntype=ntype, to_ntype=\"List[SegmentationMapsOnImage]\",\n        shapes=shapes)\n\n    if ntype == \"None\":\n        return None\n    if ntype in [\"array[int]\", \"array[uint]\", \"array[bool]\"]:\n        _assert_single_array_ndim(inputs, 4, \"(N,H,W,#SegmapsPerImage)\",\n                                  \"SegmentationMapsOnImage\")\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        if ntype == \"array[bool]\":\n            return [SegmentationMapsOnImage(attr_i, shape=shape)\n                    for attr_i, shape in zip(inputs, shapes)]\n        return [SegmentationMapsOnImage(attr_i, shape=shape)\n                for attr_i, shape in zip(inputs, shapes)]\n    if ntype == \"SegmentationMapsOnImage\":\n        return [inputs]\n    if ntype == \"iterable[empty]\":\n        return None\n    if ntype in [\"iterable-array[int]\",\n                 \"iterable-array[uint]\",\n                 \"iterable-array[bool]\"]:\n        _assert_many_arrays_ndim(inputs, 3, \"(H,W,#SegmapsPerImage)\",\n                                 \"SegmentationMapsOnImage\")\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        if ntype == \"iterable-array[bool]\":\n            return [SegmentationMapsOnImage(attr_i, shape=shape)\n                    for attr_i, shape in zip(inputs, shapes)]\n        return [SegmentationMapsOnImage(attr_i, shape=shape)\n                for attr_i, shape in zip(inputs, shapes)]\n\n    assert ntype == \"iterable-SegmentationMapsOnImage\", (\n        \"Got unknown normalization type '%s'.\" % (ntype,))\n    return inputs  # len allowed to differ from len of images\n\n\ndef normalize_keypoints(inputs, shapes=None):\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.kps import Keypoint, KeypointsOnImage\n\n    shapes = _preprocess_shapes(shapes)\n    ntype = estimate_keypoints_norm_type(inputs)\n    _assert_exactly_n_shapes_partial = functools.partial(\n        _assert_exactly_n_shapes,\n        from_ntype=ntype, to_ntype=\"List[KeypointsOnImage]\",\n        shapes=shapes)\n\n    if ntype == \"None\":\n        return inputs\n    if ntype in [\"array[float]\", \"array[int]\", \"array[uint]\"]:\n        _assert_single_array_ndim(inputs, 3, \"(N,K,2)\", \"KeypointsOnImage\")\n        _assert_single_array_last_dim_exactly(inputs, 2, \"KeypointsOnImage\")\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [\n            KeypointsOnImage.from_xy_array(attr_i, shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)\n        ]\n    if ntype == \"tuple[number,size=2]\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [KeypointsOnImage([Keypoint(x=inputs[0], y=inputs[1])],\n                                 shape=shapes[0])]\n    if ntype == \"Keypoint\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [KeypointsOnImage([inputs], shape=shapes[0])]\n    if ntype == \"KeypointsOnImage\":\n        return [inputs]\n    if ntype == \"iterable[empty]\":\n        return None\n    if ntype in [\"iterable-array[float]\",\n                 \"iterable-array[int]\",\n                 \"iterable-array[uint]\"]:\n        _assert_many_arrays_ndim(inputs, 2, \"(K,2)\", \"KeypointsOnImage\")\n        _assert_many_arrays_last_dim_exactly(inputs, 2, \"KeypointsOnImage\")\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [\n            KeypointsOnImage.from_xy_array(attr_i, shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)\n        ]\n    if ntype == \"iterable-tuple[number,size=2]\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [KeypointsOnImage([Keypoint(x=x, y=y) for x, y in inputs],\n                                 shape=shapes[0])]\n    if ntype == \"iterable-Keypoint\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [KeypointsOnImage(inputs, shape=shapes[0])]\n    if ntype == \"iterable-KeypointsOnImage\":\n        return inputs\n    if ntype == \"iterable-iterable[empty]\":\n        return None\n    if ntype == \"iterable-iterable-tuple[number,size=2]\":\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [\n            KeypointsOnImage.from_xy_array(\n                np.array(attr_i, dtype=np.float32),\n                shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)\n        ]\n\n    assert ntype == \"iterable-iterable-Keypoint\", (\n        \"Got unknown normalization type '%s'.\" % (ntype,))\n    _assert_exactly_n_shapes_partial(n=len(inputs))\n    return [KeypointsOnImage(attr_i, shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)]\n\n\ndef normalize_bounding_boxes(inputs, shapes=None):\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage\n\n    shapes = _preprocess_shapes(shapes)\n    ntype = estimate_bounding_boxes_norm_type(inputs)\n    _assert_exactly_n_shapes_partial = functools.partial(\n        _assert_exactly_n_shapes,\n        from_ntype=ntype, to_ntype=\"List[BoundingBoxesOnImage]\",\n        shapes=shapes)\n\n    if ntype == \"None\":\n        return None\n    if ntype in [\"array[float]\", \"array[int]\", \"array[uint]\"]:\n        _assert_single_array_ndim(inputs, 3, \"(N,B,4)\", \"BoundingBoxesOnImage\")\n        _assert_single_array_last_dim_exactly(\n            inputs, 4, \"BoundingBoxesOnImage\")\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [\n            BoundingBoxesOnImage.from_xyxy_array(attr_i, shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)\n        ]\n    if ntype == \"tuple[number,size=4]\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [\n            BoundingBoxesOnImage(\n                [BoundingBox(\n                    x1=inputs[0], y1=inputs[1],\n                    x2=inputs[2], y2=inputs[3])],\n                shape=shapes[0])\n        ]\n    if ntype == \"BoundingBox\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [BoundingBoxesOnImage([inputs], shape=shapes[0])]\n    if ntype == \"BoundingBoxesOnImage\":\n        return [inputs]\n    if ntype == \"iterable[empty]\":\n        return None\n    if ntype in [\"iterable-array[float]\",\n                 \"iterable-array[int]\",\n                 \"iterable-array[uint]\"]:\n        _assert_many_arrays_ndim(inputs, 2, \"(B,4)\", \"BoundingBoxesOnImage\")\n        _assert_many_arrays_last_dim_exactly(inputs, 4, \"BoundingBoxesOnImage\")\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [\n            BoundingBoxesOnImage.from_xyxy_array(attr_i, shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)\n        ]\n    if ntype == \"iterable-tuple[number,size=4]\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [\n            BoundingBoxesOnImage(\n                [BoundingBox(x1=x1, y1=y1, x2=x2, y2=y2)\n                 for x1, y1, x2, y2 in inputs],\n                shape=shapes[0])\n        ]\n    if ntype == \"iterable-BoundingBox\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [BoundingBoxesOnImage(inputs, shape=shapes[0])]\n    if ntype == \"iterable-BoundingBoxesOnImage\":\n        return inputs\n    if ntype == \"iterable-iterable[empty]\":\n        return None\n    if ntype == \"iterable-iterable-tuple[number,size=4]\":\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [\n            BoundingBoxesOnImage.from_xyxy_array(\n                np.array(attr_i, dtype=np.float32),\n                shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)\n        ]\n\n    assert ntype == \"iterable-iterable-BoundingBox\", (\n        \"Got unknown normalization type '%s'.\" % (ntype,))\n    _assert_exactly_n_shapes_partial(n=len(inputs))\n    return [BoundingBoxesOnImage(attr_i, shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)]\n\n\ndef normalize_polygons(inputs, shapes=None):\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.polys import Polygon, PolygonsOnImage\n\n    return _normalize_polygons_and_line_strings(\n        cls_single=Polygon,\n        cls_oi=PolygonsOnImage,\n        axis_names=[\"#polys\", \"#points\"],\n        estimate_ntype_func=estimate_polygons_norm_type,\n        inputs=inputs, shapes=shapes\n    )\n\n\ndef normalize_line_strings(inputs, shapes=None):\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.lines import LineString, LineStringsOnImage\n\n    return _normalize_polygons_and_line_strings(\n        cls_single=LineString,\n        cls_oi=LineStringsOnImage,\n        axis_names=[\"#lines\", \"#points\"],\n        estimate_ntype_func=estimate_line_strings_norm_type,\n        inputs=inputs, shapes=shapes\n    )\n\n\ndef _normalize_polygons_and_line_strings(cls_single, cls_oi, axis_names,\n                                         estimate_ntype_func,\n                                         inputs, shapes=None):\n    cls_single_name = cls_single.__name__\n    cls_oi_name = cls_oi.__name__\n    axis_names_4_str = \"(N,%s,%s,2)\" % (axis_names[0], axis_names[1])\n    axis_names_3_str = \"(%s,%s,2)\" % (axis_names[0], axis_names[1])\n    axis_names_2_str = \"(%s,2)\" % (axis_names[1],)\n\n    shapes = _preprocess_shapes(shapes)\n    ntype = estimate_ntype_func(inputs)\n    _assert_exactly_n_shapes_partial = functools.partial(\n        _assert_exactly_n_shapes,\n        from_ntype=ntype, to_ntype=(\"List[%s]\" % (cls_oi_name,)),\n        shapes=shapes)\n\n    if ntype == \"None\":\n        return None\n    if ntype in [\"array[float]\", \"array[int]\", \"array[uint]\"]:\n        _assert_single_array_ndim(inputs, 4, axis_names_4_str,\n                                  cls_oi_name)\n        _assert_single_array_last_dim_exactly(inputs, 2, cls_oi_name)\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [\n            cls_oi(\n                [cls_single(points) for points in attr_i],\n                shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)\n        ]\n    if ntype == cls_single_name:\n        _assert_exactly_n_shapes_partial(n=1)\n        return [cls_oi([inputs], shape=shapes[0])]\n    if ntype == cls_oi_name:\n        return [inputs]\n    if ntype == \"iterable[empty]\":\n        return None\n    if ntype in [\"iterable-array[float]\",\n                 \"iterable-array[int]\",\n                 \"iterable-array[uint]\"]:\n        _assert_many_arrays_ndim(inputs, 3, axis_names_3_str,\n                                 cls_oi_name)\n        _assert_many_arrays_last_dim_exactly(inputs, 2, cls_oi_name)\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [\n            cls_oi([cls_single(points) for points in attr_i], shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)\n        ]\n    if ntype == \"iterable-tuple[number,size=2]\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [cls_oi([cls_single(inputs)], shape=shapes[0])]\n    if ntype == \"iterable-Keypoint\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [cls_oi([cls_single(inputs)], shape=shapes[0])]\n    if ntype == (\"iterable-%s\" % (cls_single_name,)):\n        _assert_exactly_n_shapes_partial(n=1)\n        return [cls_oi(inputs, shape=shapes[0])]\n    if ntype == (\"iterable-%s\" % (cls_oi_name,)):\n        return inputs\n    if ntype == \"iterable-iterable[empty]\":\n        return None\n    if ntype in [\"iterable-iterable-array[float]\",\n                 \"iterable-iterable-array[int]\",\n                 \"iterable-iterable-array[uint]\"]:\n        _assert_many_arrays_ndim(inputs, 2, axis_names_2_str, cls_oi_name)\n        _assert_many_arrays_last_dim_exactly(inputs, 2, cls_oi_name)\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [\n            cls_oi(\n                [cls_single(points) for points in attr_i],\n                shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)\n        ]\n    if ntype == \"iterable-iterable-tuple[number,size=2]\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [\n            cls_oi([cls_single(attr_i) for attr_i in inputs],\n                   shape=shapes[0])\n        ]\n    if ntype == \"iterable-iterable-Keypoint\":\n        _assert_exactly_n_shapes_partial(n=1)\n        return [\n            cls_oi([cls_single(attr_i) for attr_i in inputs],\n                   shape=shapes[0])\n        ]\n    if ntype == (\"iterable-iterable-%s\" % (cls_single_name,)):\n        _assert_exactly_n_shapes_partial(n=len(inputs))\n        return [\n            cls_oi(attr_i, shape=shape)\n            for attr_i, shape\n            in zip(inputs, shapes)\n        ]\n    if ntype == \"iterable-iterable-iterable[empty]\":\n        return None\n\n    assert ntype in [\"iterable-iterable-iterable-tuple[number,size=2]\",\n                     \"iterable-iterable-iterable-Keypoint\"], (\n                         \"Got unknown normalization type '%s'.\" % (ntype,))\n    _assert_exactly_n_shapes_partial(n=len(inputs))\n    return [\n        cls_oi(\n            [cls_single(points) for points in attr_i],\n            shape=shape)\n        for attr_i, shape\n        in zip(inputs, shapes)\n    ]\n\n\ndef invert_normalize_images(images, images_old):\n    if images_old is None:\n        assert images is None, (\n            \"Expected (normalized) 'images' to be None due to (unnormalized) \"\n            \"'images_old' being None. Got type %s instead.\" % (type(images),))\n        return None\n    if ia.is_np_array(images_old):\n        if not ia.is_np_array(images):\n            # Images were turned from array to list during augmentation.\n            # This can happen for e.g. crop operations.\n            # We will proceed as if the old images were a list.\n            # One could also generate an array-output if all shapes and dtypes\n            # in `images` are the same. This was not done here, because\n            # (a) that would incur a performance penalty and (b) it would\n            # lead to less consistent outputs.\n            if images_old.ndim == 2:\n                # dont interpret first axis as N if `images_old` was a single\n                # image\n                return invert_normalize_images(images, [images_old])\n            return invert_normalize_images(images, list(images_old))\n\n        if images_old.ndim == 2:\n            assert images.shape[0] == 1, (\n                \"Expected normalized images of shape (N,H,W,C) to have \"\n                \"N=1 due to the unnormalized images being a single 2D \"\n                \"image. Got instead N=%d and shape %s.\" % (\n                    images.shape[0], images.shape))\n            assert images.shape[3] == 1, (\n                \"Expected normalized images of shape (N,H,W,C) to have \"\n                \"C=1 due to the unnormalized images being a single 2D \"\n                \"image. Got instead C=%d and shape %s.\" % (\n                    images.shape[3], images.shape))\n            return images[0, ..., 0]\n        if images_old.ndim == 3:\n            assert images.shape[3] == 1, (\n                \"Expected normalized images of shape (N,H,W,C) to have \"\n                \"C=1 due to unnormalized images being a single 3D image. \"\n                \"Got instead C=%d and shape %s\" % (\n                    images.shape[3], images.shape))\n            return images[..., 0]\n        return images\n    if ia.is_iterable(images_old):\n        result = []\n        for image, image_old in zip(images, images_old):\n            if image_old.ndim == 2:\n                assert image.shape[2] == 1, (\n                    \"Expected each image of shape (H,W,C) to have C=1 due to \"\n                    \"the corresponding unnormalized image being a 2D image. \"\n                    \"Got instead C=%d and shape %s.\" % (\n                        image.shape[2], image.shape))\n                result.append(image[:, :, 0])\n            else:\n                assert image_old.ndim == 3, (\n                    \"Expected 'image_old' to be three-dimensional, got %d \"\n                    \"dimensions and shape %s.\" % (\n                        image_old.ndim, image_old.shape))\n                result.append(image)\n        return result\n    raise ValueError(\n        \"Expected argument 'images_old' to be any of the following: \"\n        \"None or array or iterable of array. Got type: %s.\" % (\n            type(images_old),))\n\n\ndef invert_normalize_heatmaps(heatmaps, heatmaps_old):\n    ntype = estimate_heatmaps_norm_type(heatmaps_old)\n    if ntype == \"None\":\n        assert heatmaps is None, (\n            \"Expected (normalized) 'heatmaps' to be None due (unnormalized) \"\n            \"'heatmaps_old' being None. Got type %s instead.\" % (\n                type(heatmaps),))\n        return heatmaps\n    if ntype == \"array[float]\":\n        assert len(heatmaps) == heatmaps_old.shape[0], (\n            \"Expected as many heatmaps after normalization as before \"\n            \"normalization. Got %d (after) and %d (before).\" % (\n                len(heatmaps), heatmaps_old.shape[0]))\n        input_dtype = heatmaps_old.dtype\n        return restore_dtype_and_merge(\n            [hm_i.arr_0to1 for hm_i in heatmaps],\n            input_dtype)\n    if ntype == \"HeatmapsOnImage\":\n        assert len(heatmaps) == 1, (\n            \"Expected as many heatmaps after normalization as before \"\n            \"normalization. Got %d (after) and %d (before).\" % (\n                len(heatmaps), 1))\n        return heatmaps[0]\n    if ntype == \"iterable[empty]\":\n        assert heatmaps is None, (\n            \"Expected heatmaps after normalization to be None, due to the \"\n            \"heatmaps before normalization being an empty iterable. \"\n            \"Got type %s instead.\" % (type(heatmaps),))\n        return []\n    if ntype == \"iterable-array[float]\":\n        nonempty, _, _ = find_first_nonempty(heatmaps_old)\n        input_dtype = nonempty.dtype\n        return [restore_dtype_and_merge(hm_i.arr_0to1, input_dtype)\n                for hm_i in heatmaps]\n\n    assert ntype == \"iterable-HeatmapsOnImage\", (\n        \"Got unknown normalization type '%s'.\" % (ntype,))\n    return heatmaps\n\n\ndef invert_normalize_segmentation_maps(segmentation_maps,\n                                       segmentation_maps_old):\n    ntype = estimate_segmaps_norm_type(segmentation_maps_old)\n    if ntype == \"None\":\n        assert segmentation_maps is None, (\n            \"Expected (normalized) 'segmentation_maps' to be None due \"\n            \"(unnormalized) 'segmentation_maps_old' being None. Got type %s \"\n            \"instead.\" % (type(segmentation_maps),))\n        return segmentation_maps\n    if ntype in [\"array[int]\", \"array[uint]\", \"array[bool]\"]:\n        assert len(segmentation_maps) == segmentation_maps_old.shape[0], (\n            \"Expected as many segmentation maps after normalization as before \"\n            \"normalization. Got %d (after) and %d (before).\" % (\n                len(segmentation_maps), segmentation_maps_old.shape[0]))\n        input_dtype = segmentation_maps_old.dtype\n        return restore_dtype_and_merge(\n            [segmap_i.get_arr() for segmap_i in segmentation_maps],\n            input_dtype)\n    if ntype == \"SegmentationMapsOnImage\":\n        assert len(segmentation_maps) == 1, (\n            \"Expected as many segmentation maps after normalization as before \"\n            \"normalization. Got %d (after) and %d (before).\" % (\n                len(segmentation_maps), 1))\n        return segmentation_maps[0]\n    if ntype == \"iterable[empty]\":\n        assert segmentation_maps is None, (\n            \"Expected segmentation maps after normalization to be None, due \"\n            \"to the segmentation maps before normalization being an empty \"\n            \"iterable. Got type %s instead.\" % (type(segmentation_maps),))\n        return []\n    if ntype in [\"iterable-array[int]\",\n                 \"iterable-array[uint]\",\n                 \"iterable-array[bool]\"]:\n        nonempty, _, _ = find_first_nonempty(segmentation_maps_old)\n        input_dtype = nonempty.dtype\n        return [restore_dtype_and_merge(segmap_i.get_arr(), input_dtype)\n                for segmap_i in segmentation_maps]\n\n    assert ntype == \"iterable-SegmentationMapsOnImage\", (\n        \"Got unknown normalization type '%s'.\" % (ntype,))\n    return segmentation_maps\n\n\ndef invert_normalize_keypoints(keypoints, keypoints_old):\n    ntype = estimate_keypoints_norm_type(keypoints_old)\n    if ntype == \"None\":\n        assert keypoints is None, (\n            \"Expected (normalized) 'keypoints' to be None due (unnormalized) \"\n            \"'keypoints_old' being None. Got type %s instead.\" % (\n                type(keypoints),))\n        return keypoints\n    if ntype in [\"array[float]\", \"array[int]\", \"array[uint]\"]:\n        assert len(keypoints) == 1, (\n            \"Expected a single KeypointsOnImage instance after normalization \"\n            \"due to getting a single ndarray before normalization. \"\n            \"Got %d instances instead.\" % (len(keypoints),))\n        input_dtype = keypoints_old.dtype\n        return restore_dtype_and_merge(\n            [kpsoi.to_xy_array() for kpsoi in keypoints],\n            input_dtype)\n    if ntype == \"tuple[number,size=2]\":\n        assert len(keypoints) == 1, (\n            \"Expected a single KeypointsOnImage instance after normalization \"\n            \"due to getting a single (x,y) tuple before normalization. \"\n            \"Got %d instances instead.\" % (len(keypoints),))\n        assert len(keypoints[0].keypoints) == 1, (\n            \"Expected a KeypointsOnImage instance containing a single \"\n            \"Keypoint after normalization due to getting a single (x,y) tuple \"\n            \"before normalization. Got %d keypoints instead.\" % (\n                len(keypoints[0].keypoints)\n            ))\n        return (keypoints[0].keypoints[0].x,\n                keypoints[0].keypoints[0].y)\n    if ntype == \"Keypoint\":\n        assert len(keypoints) == 1, (\n            \"Expected a single KeypointsOnImage instance after normalization \"\n            \"due to getting a single Keypoint before normalization. \"\n            \"Got %d instances instead.\" % (len(keypoints),))\n        assert len(keypoints[0].keypoints) == 1, (\n            \"Expected a KeypointsOnImage instance containing a single \"\n            \"Keypoint after normalization due to getting a single Keypoint \"\n            \"before normalization. Got %d keypoints instead.\" % (\n                len(keypoints[0].keypoints)\n            ))\n        return keypoints[0].keypoints[0]\n    if ntype == \"KeypointsOnImage\":\n        assert len(keypoints) == 1, (\n            \"Expected a single KeypointsOnImage instance after normalization \"\n            \"due to getting a single KeypointsOnImage before normalization. \"\n            \"Got %d instances instead.\" % (len(keypoints),))\n        return keypoints[0]\n    if ntype == \"iterable[empty]\":\n        assert keypoints is None, (\n            \"Expected keypoints after normalization to be None, due \"\n            \"to the keypoints before normalization being an empty \"\n            \"iterable. Got type %s instead.\" % (type(keypoints),))\n        return []\n    if ntype in [\"iterable-array[float]\",\n                 \"iterable-array[int]\",\n                 \"iterable-array[uint]\"]:\n        nonempty, _, _ = find_first_nonempty(keypoints_old)\n        input_dtype = nonempty.dtype\n        return [\n            restore_dtype_and_merge(kps_i.to_xy_array(), input_dtype)\n            for kps_i in keypoints]\n    if ntype == \"iterable-tuple[number,size=2]\":\n        assert len(keypoints) == 1, (\n            \"Expected a single KeypointsOnImage instance after normalization \"\n            \"due to getting an iterable of (x,y) tuples before \"\n            \"normalization. Got %d instances instead.\" % (len(keypoints),))\n        return [\n            (kp.x, kp.y) for kp in keypoints[0].keypoints]\n    if ntype == \"iterable-Keypoint\":\n        assert len(keypoints) == 1, (\n            \"Expected a single KeypointsOnImage instance after normalization \"\n            \"due to getting an iterable of Keypoint before \"\n            \"normalization. Got %d instances instead.\" % (len(keypoints),))\n        return keypoints[0].keypoints\n    if ntype == \"iterable-KeypointsOnImage\":\n        return keypoints\n    if ntype == \"iterable-iterable[empty]\":\n        assert keypoints is None, (\n            \"Expected keypoints after normalization to be None, due \"\n            \"to the keypoints before normalization being an empty \"\n            \"iterable of iterables. Got type %s instead.\" % (type(keypoints),))\n        return keypoints_old[:]\n    if ntype == \"iterable-iterable-tuple[number,size=2]\":\n        return [\n            [(kp.x, kp.y) for kp in kpsoi.keypoints]\n            for kpsoi in keypoints]\n\n    assert ntype == \"iterable-iterable-Keypoint\", (\n        \"Got unknown normalization type '%s'.\" % (ntype,))\n    return [kpsoi.keypoints[:] for kpsoi in keypoints]\n\n\ndef invert_normalize_bounding_boxes(bounding_boxes, bounding_boxes_old):\n    ntype = estimate_normalization_type(bounding_boxes_old)\n    if ntype == \"None\":\n        assert bounding_boxes is None, (\n            \"Expected (normalized) 'bounding_boxes' to be None due \"\n            \"(unnormalized) 'bounding_boxes_old' being None. Got type %s \"\n            \"instead.\" % (type(bounding_boxes),))\n        return bounding_boxes\n    if ntype in [\"array[float]\", \"array[int]\", \"array[uint]\"]:\n        assert len(bounding_boxes) == 1, (\n            \"Expected a single BoundingBoxesOnImage instance after \"\n            \"normalization due to getting a single ndarray before \"\n            \"normalization. Got %d instances instead.\" % (\n                len(bounding_boxes),))\n        input_dtype = bounding_boxes_old.dtype\n        return restore_dtype_and_merge([\n            bbsoi.to_xyxy_array() for bbsoi in bounding_boxes\n        ], input_dtype)\n    if ntype == \"tuple[number,size=4]\":\n        assert len(bounding_boxes) == 1, (\n            \"Expected a single BoundingBoxesOnImage instance after \"\n            \"normalization due to getting a single (x1,y1,x2,y2) tuple before \"\n            \"normalization. Got %d instances instead.\" % (\n                len(bounding_boxes),))\n        assert len(bounding_boxes[0].bounding_boxes) == 1, (\n            \"Expected a BoundingBoxesOnImage instance containing a single \"\n            \"BoundingBox after normalization due to getting a single \"\n            \"(x1,y1,x2,y2) tuple before normalization. Got %d bounding boxes \"\n            \"instead.\" % (len(bounding_boxes[0].bounding_boxes)))\n        bb = bounding_boxes[0].bounding_boxes[0]\n        return bb.x1, bb.y1, bb.x2, bb.y2\n    if ntype == \"BoundingBox\":\n        assert len(bounding_boxes) == 1, (\n            \"Expected a single BoundingBoxesOnImage instance after \"\n            \"normalization due to getting a single BoundingBox before \"\n            \"normalization. Got %d instances instead.\" % (\n                len(bounding_boxes),))\n        assert len(bounding_boxes[0].bounding_boxes) == 1, (\n            \"Expected a BoundingBoxesOnImage instance containing a single \"\n            \"BoundingBox after normalization due to getting a single \"\n            \"BoundingBox before normalization. Got %d bounding boxes \"\n            \"instead.\" % (len(bounding_boxes[0].bounding_boxes)))\n        return bounding_boxes[0].bounding_boxes[0]\n    if ntype == \"BoundingBoxesOnImage\":\n        assert len(bounding_boxes) == 1, (\n            \"Expected a single BoundingBoxesOnImage instance after \"\n            \"normalization due to getting a single BoundingBoxesOnImage \"\n            \"before normalization. Got %d instances instead.\" % (\n                len(bounding_boxes),))\n        return bounding_boxes[0]\n    if ntype == \"iterable[empty]\":\n        assert bounding_boxes is None, (\n            \"Expected bounding boxes after normalization to be None, due \"\n            \"to the bounding boxes before normalization being an empty \"\n            \"iterable. Got type %s instead.\" % (type(bounding_boxes),))\n        return []\n    if ntype in [\"iterable-array[float]\",\n                 \"iterable-array[int]\",\n                 \"iterable-array[uint]\"]:\n        nonempty, _, _ = find_first_nonempty(bounding_boxes_old)\n        input_dtype = nonempty.dtype\n        return [\n            restore_dtype_and_merge(bbsoi.to_xyxy_array(), input_dtype)\n            for bbsoi in bounding_boxes]\n    if ntype == \"iterable-tuple[number,size=4]\":\n        assert len(bounding_boxes) == 1, (\n            \"Expected a single BoundingBoxesOnImage instance after \"\n            \"normalization due to getting a an iterable of (x1,y1,x2,y2) \"\n            \"tuples before normalization. Got %d instances instead.\" % (\n                len(bounding_boxes),))\n        return [\n            (bb.x1, bb.y1, bb.x2, bb.y2)\n            for bb in bounding_boxes[0].bounding_boxes]\n    if ntype == \"iterable-BoundingBox\":\n        assert len(bounding_boxes) == 1, (\n            \"Expected a single BoundingBoxesOnImage instance after \"\n            \"normalization due to getting an iterable of BoundingBox before \"\n            \"normalization. Got %d instances instead.\" % (\n                len(bounding_boxes),))\n        return bounding_boxes[0].bounding_boxes\n    if ntype == \"iterable-BoundingBoxesOnImage\":\n        return bounding_boxes\n    if ntype == \"iterable-iterable[empty]\":\n        assert bounding_boxes is None, (\n            \"Expected bounding boxes after normalization to be None, due \"\n            \"to the bounding boxes before normalization being an empty \"\n            \"iterable of iterables. Got type %s instead.\" % (\n                type(bounding_boxes),))\n        return bounding_boxes_old[:]\n    if ntype == \"iterable-iterable-tuple[number,size=4]\":\n        return [\n            [(bb.x1, bb.y1, bb.x2, bb.y2) for bb in bbsoi.bounding_boxes]\n            for bbsoi in bounding_boxes]\n\n    assert ntype == \"iterable-iterable-BoundingBox\", (\n        \"Got unknown normalization type '%s'.\" % (ntype,))\n    return [bbsoi.bounding_boxes[:] for bbsoi in bounding_boxes]\n\n\ndef invert_normalize_polygons(polygons, polygons_old):\n    return _invert_normalize_polygons_and_line_strings(\n        polygons, polygons_old, estimate_polygons_norm_type,\n        \"Polygon\",\n        \"PolygonsOnImage\",\n        lambda psoi: psoi.polygons,\n        lambda poly: poly.exterior)\n\n\ndef invert_normalize_line_strings(line_strings, line_strings_old):\n    return _invert_normalize_polygons_and_line_strings(\n        line_strings, line_strings_old, estimate_line_strings_norm_type,\n        \"LineString\",\n        \"LineStringsOnImage\",\n        lambda lsoi: lsoi.line_strings,\n        lambda ls: ls.coords)\n\n\ndef _invert_normalize_polygons_and_line_strings(inputs, inputs_old,\n                                                estimate_ntype_func,\n                                                cls_single_name,\n                                                cls_oi_name,\n                                                get_entities_func,\n                                                get_points_func):\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.kps import Keypoint\n\n    ntype = estimate_ntype_func(inputs_old)\n    if ntype == \"None\":\n        assert inputs is None, (\n            \"Expected (normalized) polygons/line strings to be None due \"\n            \"(unnormalized) polygons/line strings being None. Got type %s \"\n            \"instead.\" % (type(inputs),))\n        return inputs\n    if ntype in [\"array[float]\", \"array[int]\", \"array[uint]\"]:\n        input_dtype = inputs_old.dtype\n        return restore_dtype_and_merge([\n            [get_points_func(entity) for entity in get_entities_func(oi)]\n            for oi in inputs\n        ], input_dtype)\n    if ntype == cls_single_name:\n        assert len(inputs) == 1, (\n            \"Expected a single %s instance after normalization \"\n            \"due to getting a single %s before normalization. \"\n            \"Got %d instances instead.\" % (\n                cls_oi_name, cls_single_name, len(inputs),))\n        assert len(get_entities_func(inputs[0])) == 1, (\n            \"Expected a %s instance containing a single \"\n            \"%s after normalization due to getting a single %s \"\n            \"before normalization. Got %d instances instead.\" % (\n                cls_oi_name, cls_single_name, cls_single_name,\n                len(get_entities_func(inputs[0]))))\n        return get_entities_func(inputs[0])[0]\n    if ntype == cls_oi_name:\n        assert len(inputs) == 1, (\n            \"Expected a single %s instance after normalization \"\n            \"due to getting a single %s before normalization. \"\n            \"Got %d instances instead.\" % (\n                cls_oi_name, cls_oi_name, len(inputs),))\n        return inputs[0]\n    if ntype == \"iterable[empty]\":\n        assert inputs is None, (\n            \"Expected polygons/line strings after normalization to be None, \"\n            \"due to the polygons/line strings before normalization being an \"\n            \"empty iterable. Got type %s instead.\" % (type(inputs),))\n        return []\n    if ntype in [\"iterable-array[float]\",\n                 \"iterable-array[int]\",\n                 \"iterable-array[uint]\"]:\n        nonempty, _, _ = find_first_nonempty(inputs_old)\n        input_dtype = nonempty.dtype\n        return [\n            restore_dtype_and_merge(\n                [get_points_func(entity) for entity in get_entities_func(oi)],\n                input_dtype)\n            for oi in inputs\n        ]\n    if ntype == \"iterable-tuple[number,size=2]\":\n        assert len(inputs) == 1, (\n            \"Expected a single %s instance after normalization \"\n            \"due to getting an iterable of (x,y) tuples before \"\n            \"normalization. Got %d instances instead.\" % (\n                cls_oi_name, len(inputs),))\n        assert len(get_entities_func(inputs[0])) == 1, (\n            \"Expected a %s instance after normalization \"\n            \"containing a single %s instance due to getting an iterable \"\n            \"of (x,y) tuples before normalization. \"\n            \"Got a %s with %d %s instances instead.\" % (\n                cls_oi_name, cls_single_name, cls_oi_name, cls_single_name,\n                len(inputs),))\n        return [(point[0], point[1])\n                for point in get_points_func(get_entities_func(inputs[0])[0])]\n    if ntype == \"iterable-Keypoint\":\n        assert len(inputs) == 1, (\n            \"Expected a single %s instance after normalization \"\n            \"due to getting an iterable of Keypoint before \"\n            \"normalization. Got %d instances instead.\" % (\n                cls_oi_name, len(inputs),))\n        assert len(get_entities_func(inputs[0])) == 1, (\n            \"Expected a %s instance after normalization \"\n            \"containing a single %s instance due to getting an iterable \"\n            \"of Keypoint before normalization. \"\n            \"Got a %s with %d %s instances instead.\" % (\n                cls_oi_name, cls_single_name, cls_oi_name, cls_single_name,\n                len(inputs),))\n        return [Keypoint(x=point[0], y=point[1])\n                for point in get_points_func(get_entities_func(inputs[0])[0])]\n    if ntype == (\"iterable-%s\" % (cls_single_name,)):\n        assert len(inputs) == 1, (\n            \"Expected a single %s instance after normalization \"\n            \"due to getting an iterable of %s before \"\n            \"normalization. Got %d instances instead.\" % (\n                cls_oi_name, cls_single_name, len(inputs),))\n        assert len(get_entities_func(inputs[0])) == len(inputs_old), (\n            \"Expected a %s instance after normalization \"\n            \"containing a single %s instance due to getting an iterable \"\n            \"of %s before normalization. \"\n            \"Got a %s with %d %s instances instead.\" % (\n                cls_oi_name, cls_single_name, cls_single_name, cls_oi_name,\n                cls_single_name, len(inputs),))\n        return get_entities_func(inputs[0])\n    if ntype == (\"iterable-%s\" % (cls_oi_name,)):\n        return inputs\n    if ntype == \"iterable-iterable[empty]\":\n        assert inputs is None, (\n            \"Expected polygons/line strings after normalization to be None, \"\n            \"due to the polygons/line strings before normalization being an \"\n            \"empty iterable of iterables. Got type %s instead.\" % (\n                type(inputs),))\n        return inputs_old[:]\n    if ntype in [\"iterable-iterable-array[float]\",\n                 \"iterable-iterable-array[int]\",\n                 \"iterable-iterable-array[uint]\"]:\n        nonempty, _, _ = find_first_nonempty(inputs_old)\n        input_dtype = nonempty.dtype\n        return [\n            [restore_dtype_and_merge(get_points_func(entity), input_dtype)\n             for entity in get_entities_func(oi)]\n            for oi in inputs\n        ]\n    if ntype == \"iterable-iterable-tuple[number,size=2]\":\n        assert len(inputs) == 1, (\n            \"Expected a single %s instance after normalization \"\n            \"due to getting an iterable of iterables of (x,y) tuples before \"\n            \"normalization. Got %d instances instead.\" % (\n                cls_oi_name, len(inputs),))\n        return [\n            [(point[0], point[1]) for point in get_points_func(entity)]\n            for entity in get_entities_func(inputs[0])]\n    if ntype == \"iterable-iterable-Keypoint\":\n        assert len(inputs) == 1, (\n            \"Expected a single %s instance after normalization \"\n            \"due to getting an iterable of iterables of Keypoint before \"\n            \"normalization. Got %d instances instead.\" % (\n                cls_oi_name, len(inputs),))\n        return [\n            [Keypoint(x=point[0], y=point[1])\n             for point in get_points_func(entity)]\n            for entity in get_entities_func(inputs[0])]\n    if ntype == (\"iterable-iterable-%s\" % (cls_single_name,)):\n        return [get_entities_func(oi) for oi in inputs]\n    if ntype == \"iterable-iterable-iterable[empty]\":\n        return inputs_old[:]\n    if ntype == \"iterable-iterable-iterable-tuple[number,size=2]\":\n        return [\n            [\n                [\n                    (point[0], point[1])\n                    for point in get_points_func(entity)\n                ]\n                for entity in get_entities_func(oi)\n            ]\n            for oi in inputs]\n\n    assert ntype == \"iterable-iterable-iterable-Keypoint\", (\n        \"Got unknown normalization type '%s'.\" % (ntype,))\n    return [\n        [\n            [\n                Keypoint(x=point[0], y=point[1])\n                for point in get_points_func(entity)\n            ]\n            for entity in get_entities_func(oi)\n        ]\n        for oi in inputs]\n\n\ndef _assert_is_of_norm_type(type_str, valid_type_strs, arg_name):\n    assert type_str in valid_type_strs, (\n        \"Got an unknown datatype for argument '%s'. \"\n        \"Expected datatypes were: %s. Got: %s.\" % (\n            arg_name, \", \".join(valid_type_strs), type_str))\n\n\ndef estimate_heatmaps_norm_type(heatmaps):\n    type_str = estimate_normalization_type(heatmaps)\n    valid_type_strs = [\n        \"None\",\n        \"array[float]\",\n        \"HeatmapsOnImage\",\n        \"iterable[empty]\",\n        \"iterable-array[float]\",\n        \"iterable-HeatmapsOnImage\"\n    ]\n    _assert_is_of_norm_type(type_str, valid_type_strs, \"heatmaps\")\n    return type_str\n\n\ndef estimate_segmaps_norm_type(segmentation_maps):\n    type_str = estimate_normalization_type(segmentation_maps)\n    valid_type_strs = [\n        \"None\",\n        \"array[int]\",\n        \"array[uint]\",\n        \"array[bool]\",\n        \"SegmentationMapsOnImage\",\n        \"iterable[empty]\",\n        \"iterable-array[int]\",\n        \"iterable-array[uint]\",\n        \"iterable-array[bool]\",\n        \"iterable-SegmentationMapsOnImage\"\n    ]\n    _assert_is_of_norm_type(\n        type_str, valid_type_strs, \"segmentation_maps\")\n    return type_str\n\n\ndef estimate_keypoints_norm_type(keypoints):\n    type_str = estimate_normalization_type(keypoints)\n    valid_type_strs = [\n        \"None\",\n        \"array[float]\",\n        \"array[int]\",\n        \"array[uint]\",\n        \"tuple[number,size=2]\",\n        \"Keypoint\",\n        \"KeypointsOnImage\",\n        \"iterable[empty]\",\n        \"iterable-array[float]\",\n        \"iterable-array[int]\",\n        \"iterable-array[uint]\",\n        \"iterable-tuple[number,size=2]\",\n        \"iterable-Keypoint\",\n        \"iterable-KeypointsOnImage\",\n        \"iterable-iterable[empty]\",\n        \"iterable-iterable-tuple[number,size=2]\",\n        \"iterable-iterable-Keypoint\"\n    ]\n    _assert_is_of_norm_type(type_str, valid_type_strs, \"keypoints\")\n    return type_str\n\n\ndef estimate_bounding_boxes_norm_type(bounding_boxes):\n    type_str = estimate_normalization_type(bounding_boxes)\n    valid_type_strs = [\n        \"None\",\n        \"array[float]\",\n        \"array[int]\",\n        \"array[uint]\",\n        \"tuple[number,size=4]\",\n        \"BoundingBox\",\n        \"BoundingBoxesOnImage\",\n        \"iterable[empty]\",\n        \"iterable-array[float]\",\n        \"iterable-array[int]\",\n        \"iterable-array[uint]\",\n        \"iterable-tuple[number,size=4]\",\n        \"iterable-BoundingBox\",\n        \"iterable-BoundingBoxesOnImage\",\n        \"iterable-iterable[empty]\",\n        \"iterable-iterable-tuple[number,size=4]\",\n        \"iterable-iterable-BoundingBox\"\n    ]\n    _assert_is_of_norm_type(\n        type_str, valid_type_strs, \"bounding_boxes\")\n    return type_str\n\n\ndef estimate_polygons_norm_type(polygons):\n    return _estimate_polygons_and_line_segments_norm_type(\n        polygons, \"Polygon\", \"PolygonsOnImage\", \"polygons\")\n\n\ndef estimate_line_strings_norm_type(line_strings):\n    return _estimate_polygons_and_line_segments_norm_type(\n        line_strings, \"LineString\", \"LineStringsOnImage\", \"line_strings\")\n\n\ndef _estimate_polygons_and_line_segments_norm_type(inputs, cls_single_name,\n                                                   cls_oi_name,\n                                                   augmentable_name):\n    type_str = estimate_normalization_type(inputs)\n    valid_type_strs = [\n        \"None\",\n        \"array[float]\",\n        \"array[int]\",\n        \"array[uint]\",\n        cls_single_name,\n        cls_oi_name,\n        \"iterable[empty]\",\n        \"iterable-array[float]\",\n        \"iterable-array[int]\",\n        \"iterable-array[uint]\",\n        \"iterable-tuple[number,size=2]\",\n        \"iterable-Keypoint\",\n        \"iterable-%s\" % (cls_single_name,),\n        \"iterable-%s\" % (cls_oi_name,),\n        \"iterable-iterable[empty]\",\n        \"iterable-iterable-array[float]\",\n        \"iterable-iterable-array[int]\",\n        \"iterable-iterable-array[uint]\",\n        \"iterable-iterable-tuple[number,size=2]\",\n        \"iterable-iterable-Keypoint\",\n        \"iterable-iterable-%s\" % (cls_single_name,),\n        \"iterable-iterable-iterable[empty]\",\n        \"iterable-iterable-iterable-tuple[number,size=2]\",\n        \"iterable-iterable-iterable-Keypoint\"\n    ]\n    _assert_is_of_norm_type(type_str, valid_type_strs, augmentable_name)\n    return type_str\n\n\ndef estimate_normalization_type(inputs):\n    nonempty, success, parents = find_first_nonempty(inputs)\n    type_str = _nonempty_info_to_type_str(nonempty, success, parents)\n    return type_str\n\n\ndef restore_dtype_and_merge(arr, input_dtype):\n    if isinstance(arr, list):\n        arr = [restore_dtype_and_merge(arr_i, input_dtype)\n               for arr_i in arr]\n        shapes = [arr_i.shape for arr_i in arr]\n        if len(set(shapes)) == 1:\n            arr = np.array(arr)\n\n    if ia.is_np_array(arr):\n        arr = iadt.restore_dtypes_(arr, input_dtype)\n    return arr\n\n\ndef _is_iterable(obj):\n    return (\n        ia.is_iterable(obj)\n        and not isinstance(obj, IAugmentable)  # not e.g. KeypointsOnImage\n        and not hasattr(obj, \"coords\")  # not BBs, Polys, LS\n        and not ia.is_string(obj)\n    )\n\n\ndef find_first_nonempty(attr, parents=None):\n    if parents is None:\n        parents = []\n\n    if attr is None or ia.is_np_array(attr):\n        return attr, True, parents\n    # we exclude strings here, as otherwise we would get the first\n    # character, while we want to get the whole string\n    if _is_iterable(attr):\n        if len(attr) == 0:\n            return None, False, parents\n\n        # this prevents the loop below from becoming infinite if the\n        # element in the iterable is identical with the iterable,\n        # as is the case for e.g. strings\n        if attr[0] is attr:\n            return attr, True, parents\n\n        # Usually in case of empty lists, all lists should have similar\n        # depth. We are a bit more tolerant here and pick the deepest one.\n        # Only parents would really need to be tracked here, we could\n        # ignore nonempty and success as they will always have the same\n        # values (if only empty lists exist).\n        nonempty_deepest = None\n        success_deepest = False\n        parents_deepest = parents\n        for attr_i in attr:\n            nonempty, success, parents_found = find_first_nonempty(\n                attr_i, parents=parents+[attr])\n            if success:\n                # on any nonempty hit we return immediately as we assume\n                # that the datatypes do not change between child branches\n                return nonempty, success, parents_found\n            if len(parents_found) > len(parents_deepest):\n                nonempty_deepest = nonempty\n                success_deepest = success\n                parents_deepest = parents_found\n\n        return nonempty_deepest, success_deepest, parents_deepest\n\n    return attr, True, parents\n\n\ndef _nonempty_info_to_type_str(nonempty, success, parents):\n    assert len(parents) <= 4, \"Expected 'parents' to be <=4, got %d.\" % (\n        len(parents),)\n    parent_iters = \"\"\n    if len(parents) > 0:\n        parent_iters = \"%s-\" % (\"-\".join([\"iterable\"] * len(parents)),)\n\n    if not success:\n        return \"%siterable[empty]\" % (parent_iters,)\n\n    is_parent_tuple = (\n        len(parents) >= 1\n        and isinstance(parents[-1], tuple)\n    )\n\n    if is_parent_tuple:\n        is_only_numbers_in_tuple = (\n            len(parents[-1]) > 0\n            and all([ia.is_single_number(val) for val in parents[-1]])\n        )\n\n        if is_only_numbers_in_tuple:\n            parent_iters = \"-\".join([\"iterable\"] * (len(parents)-1))\n            tpl_name = \"tuple[number,size=%d]\" % (len(parents[-1]),)\n            return \"-\".join([parent_iters, tpl_name]).lstrip(\"-\")\n\n    if nonempty is None:\n        return \"None\"\n    if ia.is_np_array(nonempty):\n        kind = nonempty.dtype.kind\n        kind_map = {\"f\": \"float\", \"u\": \"uint\", \"i\": \"int\", \"b\": \"bool\"}\n        return \"%sarray[%s]\" % (\n            parent_iters, kind_map[kind] if kind in kind_map else kind)\n\n    # even int, str etc. are objects in python, so anything left should\n    # offer a __class__ attribute\n    assert isinstance(nonempty, object), (\n        \"Expected 'nonempty' to be an object, got type %s.\" % (\n            type(nonempty),))\n    return \"%s%s\" % (parent_iters, nonempty.__class__.__name__)\n"
  },
  {
    "path": "imgaug/augmentables/polys.py",
    "content": "\"\"\"Classes dealing with polygons.\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport traceback\nimport collections\n\nimport numpy as np\nimport scipy.spatial.distance\nimport six.moves as sm\nimport skimage.draw\nimport skimage.measure\n\nfrom .. import imgaug as ia\nfrom .. import random as iarandom\nfrom .base import IAugmentable\nfrom .utils import (\n    normalize_imglike_shape,\n    interpolate_points,\n    _remove_out_of_image_fraction_,\n    project_coords_,\n    _normalize_shift_args,\n    _handle_on_image_shape\n)\n\n\ndef recover_psois_(psois, psois_orig, recoverer, random_state):\n    \"\"\"Apply a polygon recoverer to input polygons in-place.\n\n    Parameters\n    ----------\n    psois : list of imgaug.augmentables.polys.PolygonsOnImage or imgaug.augmentables.polys.PolygonsOnImage\n        The possibly broken polygons, e.g. after augmentation.\n        The `recoverer` is applied to them.\n\n    psois_orig : list of imgaug.augmentables.polys.PolygonsOnImage or imgaug.augmentables.polys.PolygonsOnImage\n        Original polygons that were later changed to `psois`.\n        They are an extra input to `recoverer`.\n\n    recoverer : imgaug.augmentables.polys._ConcavePolygonRecoverer\n        The polygon recoverer used to repair broken input polygons.\n\n    random_state : None or int or RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState\n        An RNG to use during the polygon recovery.\n\n    Returns\n    -------\n    list of imgaug.augmentables.polys.PolygonsOnImage or imgaug.augmentables.polys.PolygonsOnImage\n        List of repaired polygons. Note that this is `psois`, which was\n        changed in-place.\n\n    \"\"\"\n    input_was_list = True\n    if not isinstance(psois, list):\n        input_was_list = False\n        psois = [psois]\n        psois_orig = [psois_orig]\n\n    for i, psoi in enumerate(psois):\n        for j, polygon in enumerate(psoi.polygons):\n            poly_rec = recoverer.recover_from(\n                polygon.exterior, psois_orig[i].polygons[j],\n                random_state)\n\n            # Don't write into `polygon.exterior[...] = ...` because the\n            # shapes might have changed. We could also first check if the\n            # shapes are identical and only then write in-place, but as the\n            # array for `poly_rec.exterior` was already created, that would\n            # not provide any benefits.\n            polygon.exterior = poly_rec.exterior\n\n    if not input_was_list:\n        return psois[0]\n    return psois\n\n\n# TODO somehow merge with BoundingBox\n# TODO add functions: simplify() (eg via shapely.ops.simplify()),\n# extend(all_sides=0, top=0, right=0, bottom=0, left=0),\n# intersection(other, default=None), union(other), iou(other), to_heatmap, to_mask\nclass Polygon(object):\n    \"\"\"Class representing polygons.\n\n    Each polygon is parameterized by its corner points, given as absolute\n    x- and y-coordinates with sub-pixel accuracy.\n\n    Parameters\n    ----------\n    exterior : list of imgaug.augmentables.kps.Keypoint or list of tuple of float or (N,2) ndarray\n        List of points defining the polygon. May be either a ``list`` of\n        :class:`~imgaug.augmentables.kps.Keypoint` objects or a ``list`` of\n        ``tuple`` s in xy-form or a numpy array of shape (N,2) for ``N``\n        points in xy-form.\n        All coordinates are expected to be the absolute subpixel-coordinates\n        on the image, given as ``float`` s, e.g. ``x=10.7`` and ``y=3.4`` for a\n        point at coordinates ``(10.7, 3.4)``. Their order is expected to be\n        clock-wise. They are expected to not be closed (i.e. first and last\n        coordinate differ).\n\n    label : None or str, optional\n        Label of the polygon, e.g. a string representing the class.\n\n    \"\"\"\n\n    def __init__(self, exterior, label=None):\n        \"\"\"Create a new Polygon instance.\"\"\"\n        # TODO get rid of this deferred import\n        from imgaug.augmentables.kps import Keypoint\n\n        if isinstance(exterior, list):\n            if not exterior:\n                # for empty lists, make sure that the shape is (0, 2) and\n                # not (0,) as that is also expected when the input is a numpy\n                # array\n                self.exterior = np.zeros((0, 2), dtype=np.float32)\n            elif isinstance(exterior[0], Keypoint):\n                # list of Keypoint\n                self.exterior = np.float32([[point.x, point.y]\n                                            for point in exterior])\n            else:\n                # list of tuples (x, y)\n                # TODO just np.float32(exterior) here?\n                self.exterior = np.float32([[point[0], point[1]]\n                                            for point in exterior])\n        else:\n            assert ia.is_np_array(exterior), (\n                \"Expected exterior to be a list of tuples (x, y) or \"\n                \"an (N, 2) array, got type %s\" % (exterior,))\n            assert exterior.ndim == 2 and exterior.shape[1] == 2, (\n                \"Expected exterior to be a list of tuples (x, y) or \"\n                \"an (N, 2) array, got an array of shape %s\" % (\n                    exterior.shape,))\n            # TODO deal with int inputs here?\n            self.exterior = np.float32(exterior)\n\n        # Remove last point if it is essentially the same as the first\n        # point (polygons are always assumed to be closed anyways). This also\n        # prevents problems with shapely, which seems to add the last point\n        # automatically.\n        is_closed = (\n            len(self.exterior) >= 2\n            and np.allclose(self.exterior[0, :], self.exterior[-1, :]))\n        if is_closed:\n            self.exterior = self.exterior[:-1]\n\n        self.label = label\n\n    @property\n    def coords(self):\n        \"\"\"Alias for attribute ``exterior``.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        ndarray\n            An ``(N, 2)`` ``float32`` ndarray containing the coordinates of\n            this polygon. This identical to the attribute ``exterior``.\n\n        \"\"\"\n        return self.exterior\n\n    @property\n    def xx(self):\n        \"\"\"Get the x-coordinates of all points on the exterior.\n\n        Returns\n        -------\n        (N,2) ndarray\n            ``float32`` x-coordinates array of all points on the exterior.\n\n        \"\"\"\n        return self.exterior[:, 0]\n\n    @property\n    def yy(self):\n        \"\"\"Get the y-coordinates of all points on the exterior.\n\n        Returns\n        -------\n        (N,2) ndarray\n            ``float32`` y-coordinates array of all points on the exterior.\n\n        \"\"\"\n        return self.exterior[:, 1]\n\n    @property\n    def xx_int(self):\n        \"\"\"Get the discretized x-coordinates of all points on the exterior.\n\n        The conversion from ``float32`` coordinates to ``int32`` is done\n        by first rounding the coordinates to the closest integer and then\n        removing everything after the decimal point.\n\n        Returns\n        -------\n        (N,2) ndarray\n            ``int32`` x-coordinates of all points on the exterior.\n\n        \"\"\"\n        return np.int32(np.round(self.xx))\n\n    @property\n    def yy_int(self):\n        \"\"\"Get the discretized y-coordinates of all points on the exterior.\n\n        The conversion from ``float32`` coordinates to ``int32`` is done\n        by first rounding the coordinates to the closest integer and then\n        removing everything after the decimal point.\n\n        Returns\n        -------\n        (N,2) ndarray\n            ``int32`` y-coordinates of all points on the exterior.\n\n        \"\"\"\n        return np.int32(np.round(self.yy))\n\n    @property\n    def is_valid(self):\n        \"\"\"Estimate whether the polygon has a valid geometry.\n\n        To to be considered valid, the polygon must be made up of at\n        least ``3`` points and have a concave shape, i.e. line segments may\n        not intersect or overlap. Multiple consecutive points are allowed to\n        have the same coordinates.\n\n        Returns\n        -------\n        bool\n            ``True`` if polygon has at least ``3`` points and is concave,\n            otherwise ``False``.\n\n        \"\"\"\n        if len(self.exterior) < 3:\n            return False\n        return self.to_shapely_polygon().is_valid\n\n    @property\n    def area(self):\n        \"\"\"Compute the area of the polygon.\n\n        Returns\n        -------\n        number\n            Area of the polygon.\n\n        \"\"\"\n        if len(self.exterior) < 3:\n            return 0.0\n        poly = self.to_shapely_polygon()\n        return poly.area\n\n    @property\n    def height(self):\n        \"\"\"Compute the height of a bounding box encapsulating the polygon.\n\n        The height is computed based on the two exterior coordinates with\n        lowest and largest x-coordinates.\n\n        Returns\n        -------\n        number\n            Height of the polygon.\n\n        \"\"\"\n        yy = self.yy\n        return max(yy) - min(yy)\n\n    @property\n    def width(self):\n        \"\"\"Compute the width of a bounding box encapsulating the polygon.\n\n        The width is computed based on the two exterior coordinates with\n        lowest and largest x-coordinates.\n\n        Returns\n        -------\n        number\n            Width of the polygon.\n\n        \"\"\"\n        xx = self.xx\n        return max(xx) - min(xx)\n\n    def project_(self, from_shape, to_shape):\n        \"\"\"Project the polygon onto an image with different shape in-place.\n\n        The relative coordinates of all points remain the same.\n        E.g. a point at ``(x=20, y=20)`` on an image\n        ``(width=100, height=200)`` will be projected on a new\n        image ``(width=200, height=100)`` to ``(x=40, y=10)``.\n\n        This is intended for cases where the original image is resized.\n        It cannot be used for more complex changes (e.g. padding, cropping).\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        from_shape : tuple of int\n            Shape of the original image. (Before resize.)\n\n        to_shape : tuple of int\n            Shape of the new image. (After resize.)\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            Polygon object with new coordinates.\n            The object may have been modified in-place.\n\n        \"\"\"\n        self.exterior = project_coords_(self.coords, from_shape, to_shape)\n        return self\n\n    def project(self, from_shape, to_shape):\n        \"\"\"Project the polygon onto an image with different shape.\n\n        The relative coordinates of all points remain the same.\n        E.g. a point at ``(x=20, y=20)`` on an image\n        ``(width=100, height=200)`` will be projected on a new\n        image ``(width=200, height=100)`` to ``(x=40, y=10)``.\n\n        This is intended for cases where the original image is resized.\n        It cannot be used for more complex changes (e.g. padding, cropping).\n\n        Parameters\n        ----------\n        from_shape : tuple of int\n            Shape of the original image. (Before resize.)\n\n        to_shape : tuple of int\n            Shape of the new image. (After resize.)\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            Polygon object with new coordinates.\n\n        \"\"\"\n        return self.deepcopy().project_(from_shape, to_shape)\n\n    def find_closest_point_index(self, x, y, return_distance=False):\n        \"\"\"Find the index of the exterior point closest to given coordinates.\n\n        \"Closeness\" is here defined based on euclidean distance.\n        This method will raise an ``AssertionError`` if the exterior contains\n        no points.\n\n        Parameters\n        ----------\n        x : number\n            X-coordinate around which to search for close points.\n\n        y : number\n            Y-coordinate around which to search for close points.\n\n        return_distance : bool, optional\n            Whether to also return the distance of the closest point.\n\n        Returns\n        -------\n        int\n            Index of the closest point.\n\n        number\n            Euclidean distance to the closest point.\n            This value is only returned if `return_distance` was set\n            to ``True``.\n\n        \"\"\"\n        assert len(self.exterior) > 0, (\n            \"Cannot find the closest point on a polygon which's exterior \"\n            \"contains no points.\")\n        distances = []\n        for x2, y2 in self.exterior:\n            dist = (x2 - x) ** 2 + (y2 - y) ** 2\n            distances.append(dist)\n        distances = np.sqrt(distances)\n        closest_idx = np.argmin(distances)\n        if return_distance:\n            return closest_idx, distances[closest_idx]\n        return closest_idx\n\n    def compute_out_of_image_area(self, image):\n        \"\"\"Compute the area of the BB that is outside of the image plane.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape\n            and must contain at least two integers.\n\n        Returns\n        -------\n        float\n            Total area of the bounding box that is outside of the image plane.\n            Can be ``0.0``.\n\n        \"\"\"\n        polys_clipped = self.clip_out_of_image(image)\n        if len(polys_clipped) == 0:\n            return self.area\n        return self.area - sum([poly.area for poly in polys_clipped])\n\n    def compute_out_of_image_fraction(self, image):\n        \"\"\"Compute fraction of polygon area outside of the image plane.\n\n        This estimates ``f = A_ooi / A``, where ``A_ooi`` is the area of the\n        polygon that is outside of the image plane, while ``A`` is the\n        total area of the bounding box.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape\n            and must contain at least two integers.\n\n        Returns\n        -------\n        float\n            Fraction of the polygon area that is outside of the image\n            plane. Returns ``0.0`` if the polygon is fully inside of\n            the image plane or has zero points. If the polygon has an area\n            of zero, the polygon is treated similarly to a :class:`LineString`,\n            i.e. the fraction of the line that is outside the image plane is\n            returned.\n\n        \"\"\"\n        area = self.area\n        if area == 0:\n            return self.to_line_string().compute_out_of_image_fraction(image)\n        return self.compute_out_of_image_area(image) / area\n\n    # TODO keep this method? it is almost an alias for is_out_of_image()\n    def is_fully_within_image(self, image):\n        \"\"\"Estimate whether the polygon is fully inside an image plane.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape and\n            must contain at least two ``int`` s.\n\n        Returns\n        -------\n        bool\n            ``True`` if the polygon is fully inside the image area.\n            ``False`` otherwise.\n\n        \"\"\"\n        return not self.is_out_of_image(image, fully=True, partly=True)\n\n    # TODO keep this method? it is almost an alias for is_out_of_image()\n    def is_partly_within_image(self, image):\n        \"\"\"Estimate whether the polygon is at least partially inside an image.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape and\n            must contain at least two ``int`` s.\n\n        Returns\n        -------\n        bool\n            ``True`` if the polygon is at least partially inside the image area.\n            ``False`` otherwise.\n\n        \"\"\"\n        return not self.is_out_of_image(image, fully=True, partly=False)\n\n    def is_out_of_image(self, image, fully=True, partly=False):\n        \"\"\"Estimate whether the polygon is partially/fully outside of an image.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape and\n            must contain at least two ``int`` s.\n\n        fully : bool, optional\n            Whether to return ``True`` if the polygon is fully outside of the\n            image area.\n\n        partly : bool, optional\n            Whether to return ``True`` if the polygon is at least partially\n            outside fo the image area.\n\n        Returns\n        -------\n        bool\n            ``True`` if the polygon is partially/fully outside of the image\n            area, depending on defined parameters.\n            ``False`` otherwise.\n\n        \"\"\"\n        # TODO this is inconsistent with line strings, which return a default\n        #      value in these cases\n        if len(self.exterior) == 0:\n            raise Exception(\"Cannot determine whether the polygon is inside \"\n                            \"the image, because it contains no points.\")\n\n        # The line string is identical to the edge of the polygon.\n        # If the edge is fully inside the image, we know that the polygon must\n        # be fully inside the image.\n        # If the edge is partially outside of the image, we know that the\n        # polygon is partially outside of the image.\n        # Only if the edge is fully outside of the image we cannot be sure if\n        # the polygon's inner area overlaps with the image (e.g. if the\n        # polygon contains the whole image in it).\n        ls = self.to_line_string()\n        if ls.is_fully_within_image(image):\n            return False\n        if ls.is_out_of_image(image, fully=False, partly=True):\n            return partly\n\n        # LS is fully outside of the image. Estimate whether there is any\n        # intersection with the image plane. If so, we know that there is\n        # partial overlap (full overlap would mean that the LS was fully inside\n        # the image).\n        polys = self.clip_out_of_image(image)\n        if len(polys) > 0:\n            return partly\n        return fully\n\n    @ia.deprecated(alt_func=\"Polygon.clip_out_of_image()\",\n                   comment=\"clip_out_of_image() has the exactly same \"\n                           \"interface.\")\n    def cut_out_of_image(self, image):\n        \"\"\"Cut off all parts of the polygon that are outside of an image.\"\"\"\n        return self.clip_out_of_image(image)\n\n    # TODO this currently can mess up the order of points - change somehow to\n    #      keep the order\n    def clip_out_of_image(self, image):\n        \"\"\"Cut off all parts of the polygon that are outside of an image.\n\n        This operation may lead to new points being created.\n        As a single polygon may be split into multiple new polygons, the result\n        is always a list, which may contain more than one output polygon.\n\n        This operation will return an empty list if the polygon is completely\n        outside of the image plane.\n\n        Parameters\n        ----------\n        image : (H,W,...) ndarray or tuple of int\n            Image dimensions to use for the clipping of the polygon.\n            If an ``ndarray``, its shape will be used.\n            If a ``tuple``, it is assumed to represent the image shape and must\n            contain at least two ``int`` s.\n\n        Returns\n        -------\n        list of imgaug.augmentables.polys.Polygon\n            Polygon, clipped to fall within the image dimensions.\n            Returned as a ``list``, because the clipping can split the polygon\n            into multiple parts. The list may also be empty, if the polygon was\n            fully outside of the image plane.\n\n        \"\"\"\n        # load shapely lazily, which makes the dependency more optional\n        import shapely.geometry\n\n        # Shapely polygon conversion requires at least 3 coordinates\n        if len(self.exterior) == 0:\n            return []\n        if len(self.exterior) in [1, 2]:\n            ls = self.to_line_string(closed=False)\n            ls_clipped = ls.clip_out_of_image(image)\n            assert len(ls_clipped) <= 1\n            if len(ls_clipped) == 0:\n                return []\n            return [self.deepcopy(exterior=ls_clipped[0].coords)]\n\n        h, w = image.shape[0:2] if ia.is_np_array(image) else image[0:2]\n        poly_shapely = self.to_shapely_polygon()\n        poly_image = shapely.geometry.Polygon([(0, 0), (w, 0), (w, h), (0, h)])\n        multipoly_inter_shapely = poly_shapely.intersection(poly_image)\n        ignore_types = (shapely.geometry.LineString,\n                        shapely.geometry.MultiLineString,\n                        shapely.geometry.point.Point,\n                        shapely.geometry.MultiPoint)\n        if isinstance(multipoly_inter_shapely, shapely.geometry.Polygon):\n            multipoly_inter_shapely = shapely.geometry.MultiPolygon(\n                [multipoly_inter_shapely])\n        elif isinstance(multipoly_inter_shapely,\n                        shapely.geometry.MultiPolygon):\n            # we got a multipolygon from shapely, no need to change anything\n            # anymore\n            pass\n        elif isinstance(multipoly_inter_shapely, ignore_types):\n            # polygons that become (one or more) lines/points after clipping\n            # are here ignored\n            multipoly_inter_shapely = shapely.geometry.MultiPolygon([])\n        elif isinstance(multipoly_inter_shapely,\n                        shapely.geometry.GeometryCollection):\n            # Shapely returns GEOMETRYCOLLECTION EMPTY if there is nothing\n            # remaining after the clip.\n            assert multipoly_inter_shapely.is_empty\n            return []\n        else:\n            raise Exception(\n                \"Got an unexpected result of type %s from Shapely for \"\n                \"image (%d, %d) and polygon %s. This is an internal error. \"\n                \"Please report.\" % (\n                    type(multipoly_inter_shapely), h, w, self.exterior)\n            )\n\n        polygons = []\n        for poly_inter_shapely in multipoly_inter_shapely.geoms:\n            polygons.append(Polygon.from_shapely(poly_inter_shapely,\n                                                 label=self.label))\n\n        # Shapely changes the order of points, we try here to preserve it as\n        # much as possible.\n        # Note here, that all points of the new polygon might have high\n        # distance to the points on the old polygon. This can happen if the\n        # polygon overlaps with the image plane, but all of its points are\n        # outside of the image plane. The new polygon will not be made up of\n        # any of the old points.\n        polygons_reordered = []\n        for polygon in polygons:\n            best_idx = None\n            best_dist = None\n            for x, y in self.exterior:\n                point_idx, dist = polygon.find_closest_point_index(\n                    x=x, y=y, return_distance=True)\n                if best_idx is None or dist < best_dist:\n                    best_idx = point_idx\n                    best_dist = dist\n            if best_idx is not None:\n                polygon_reordered = \\\n                    polygon.change_first_point_by_index(best_idx)\n                polygons_reordered.append(polygon_reordered)\n\n        return polygons_reordered\n\n    def shift_(self, x=0, y=0):\n        \"\"\"Move this polygon along the x/y-axis in-place.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            Shifted polygon.\n            The object may have been modified in-place.\n\n        \"\"\"\n        self.exterior[:, 0] += x\n        self.exterior[:, 1] += y\n        return self\n\n    def shift(self, x=0, y=0, top=None, right=None, bottom=None, left=None):\n        \"\"\"Move this polygon along the x/y-axis.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        top : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            top (towards the bottom).\n\n        right : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            right (towards the left).\n\n        bottom : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            bottom (towards the top).\n\n        left : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift this object *from* the\n            left (towards the right).\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            Shifted polygon.\n\n        \"\"\"\n        x, y = _normalize_shift_args(\n            x, y, top=top, right=right, bottom=bottom, left=left)\n        return self.deepcopy().shift_(x=x, y=y)\n\n    # TODO separate this into draw_face_on_image() and draw_border_on_image()\n    # TODO add tests for line thickness\n    def draw_on_image(self,\n                      image,\n                      color=(0, 255, 0), color_face=None,\n                      color_lines=None, color_points=None,\n                      alpha=1.0, alpha_face=None,\n                      alpha_lines=None, alpha_points=None,\n                      size=1, size_lines=None, size_points=None,\n                      raise_if_out_of_image=False):\n        \"\"\"Draw the polygon on an image.\n\n        Parameters\n        ----------\n        image : (H,W,C) ndarray\n            The image onto which to draw the polygon. Usually expected to be\n            of dtype ``uint8``, though other dtypes are also handled.\n\n        color : iterable of int, optional\n            The color to use for the whole polygon.\n            Must correspond to the channel layout of the image. Usually RGB.\n            The values for `color_face`, `color_lines` and `color_points`\n            will be derived from this color if they are set to ``None``.\n            This argument has no effect if `color_face`, `color_lines`\n            and `color_points` are all set anything other than ``None``.\n\n        color_face : None or iterable of int, optional\n            The color to use for the inner polygon area (excluding perimeter).\n            Must correspond to the channel layout of the image. Usually RGB.\n            If this is ``None``, it will be derived from ``color * 1.0``.\n\n        color_lines : None or iterable of int, optional\n            The color to use for the line (aka perimeter/border) of the\n            polygon.\n            Must correspond to the channel layout of the image. Usually RGB.\n            If this is ``None``, it will be derived from ``color * 0.5``.\n\n        color_points : None or iterable of int, optional\n            The color to use for the corner points of the polygon.\n            Must correspond to the channel layout of the image. Usually RGB.\n            If this is ``None``, it will be derived from ``color * 0.5``.\n\n        alpha : float, optional\n            The opacity of the whole polygon, where ``1.0`` denotes a\n            completely visible polygon and ``0.0`` an invisible one.\n            The values for `alpha_face`, `alpha_lines` and `alpha_points`\n            will be derived from this alpha value if they are set to ``None``.\n            This argument has no effect if `alpha_face`, `alpha_lines`\n            and `alpha_points` are all set anything other than ``None``.\n\n        alpha_face : None or number, optional\n            The opacity of the polygon's inner area (excluding the perimeter),\n            where ``1.0`` denotes a completely visible inner area and ``0.0``\n            an invisible one.\n            If this is ``None``, it will be derived from ``alpha * 0.5``.\n\n        alpha_lines : None or number, optional\n            The opacity of the polygon's line (aka perimeter/border),\n            where ``1.0`` denotes a completely visible line and ``0.0`` an\n            invisible one.\n            If this is ``None``, it will be derived from ``alpha * 1.0``.\n\n        alpha_points : None or number, optional\n            The opacity of the polygon's corner points, where ``1.0`` denotes\n            completely visible corners and ``0.0`` invisible ones.\n            If this is ``None``, it will be derived from ``alpha * 1.0``.\n\n        size : int, optional\n            Size of the polygon.\n            The sizes of the line and points are derived from this value,\n            unless they are set.\n\n        size_lines : None or int, optional\n            Thickness of the polygon's line (aka perimeter/border).\n            If ``None``, this value is derived from `size`.\n\n        size_points : int, optional\n            Size of the points in pixels.\n            If ``None``, this value is derived from ``3 * size``.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if the polygon is fully\n            outside of the image. If set to ``False``, no error will be\n            raised and only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        (H,W,C) ndarray\n            Image with the polygon drawn on it. Result dtype is the same as the\n            input dtype.\n\n        \"\"\"\n        # pylint: disable=invalid-name\n        def _assert_not_none(arg_name, arg_value):\n            assert arg_value is not None, (\n                \"Expected '%s' to not be None, got type %s.\" % (\n                    arg_name, type(arg_value),))\n\n        def _default_to(var, default):\n            if var is None:\n                return default\n            return var\n\n        _assert_not_none(\"color\", color)\n        _assert_not_none(\"alpha\", alpha)\n        _assert_not_none(\"size\", size)\n\n        # FIXME due to the np.array(.) and the assert at ndim==2 below, this\n        #       will always fail on 2D images?\n        color_face = _default_to(color_face, np.array(color))\n        color_lines = _default_to(color_lines, np.array(color) * 0.5)\n        color_points = _default_to(color_points, np.array(color) * 0.5)\n\n        alpha_face = _default_to(alpha_face, alpha * 0.5)\n        alpha_lines = _default_to(alpha_lines, alpha)\n        alpha_points = _default_to(alpha_points, alpha)\n\n        size_lines = _default_to(size_lines, size)\n        size_points = _default_to(size_points, size * 3)\n\n        if image.ndim == 2:\n            assert ia.is_single_number(color_face), (\n                \"Got a 2D image. Expected then 'color_face' to be a single \"\n                \"number, but got %s.\" % (str(color_face),))\n            color_face = [color_face]\n        elif image.ndim == 3 and ia.is_single_number(color_face):\n            color_face = [color_face] * image.shape[-1]\n\n        if alpha_face < 0.01:\n            alpha_face = 0\n        elif alpha_face > 0.99:\n            alpha_face = 1\n\n        if raise_if_out_of_image and self.is_out_of_image(image):\n            raise Exception(\"Cannot draw polygon %s on image with \"\n                            \"shape %s.\" % (str(self), image.shape))\n\n        # TODO np.clip to image plane if is_fully_within_image(), similar to\n        #      how it is done for bounding boxes\n\n        # TODO improve efficiency by only drawing in rectangle that covers\n        #      poly instead of drawing in the whole image\n        # TODO for a rectangular polygon, the face coordinates include the\n        #      top/left boundary but not the right/bottom boundary. This may\n        #      be unintuitive when not drawing the boundary. Maybe somehow\n        #      remove the boundary coordinates from the face coordinates after\n        #      generating both?\n        input_dtype = image.dtype\n        result = image.astype(np.float32)\n        rr, cc = skimage.draw.polygon(\n            self.yy_int, self.xx_int, shape=image.shape)\n        if len(rr) > 0:\n            if alpha_face == 1:\n                result[rr, cc] = np.float32(color_face)\n            elif alpha_face == 0:\n                pass\n            else:\n                result[rr, cc] = (\n                    (1 - alpha_face) * result[rr, cc, :]\n                    + alpha_face * np.float32(color_face)\n                )\n\n        ls_open = self.to_line_string(closed=False)\n        ls_closed = self.to_line_string(closed=True)\n        result = ls_closed.draw_lines_on_image(\n            result, color=color_lines, alpha=alpha_lines,\n            size=size_lines, raise_if_out_of_image=raise_if_out_of_image)\n        result = ls_open.draw_points_on_image(\n            result, color=color_points, alpha=alpha_points,\n            size=size_points, raise_if_out_of_image=raise_if_out_of_image)\n\n        if input_dtype.type == np.uint8:\n            # TODO make clipping more flexible\n            result = np.clip(np.round(result), 0, 255).astype(input_dtype)\n        else:\n            result = result.astype(input_dtype)\n\n        return result\n\n    # TODO add pad, similar to LineStrings\n    # TODO add pad_max, similar to LineStrings\n    # TODO add prevent_zero_size, similar to LineStrings\n    def extract_from_image(self, image):\n        \"\"\"Extract all image pixels within the polygon area.\n\n        This method returns a rectangular image array. All pixels within\n        that rectangle that do not belong to the polygon area will be filled\n        with zeros (i.e. they will be black).\n        The method will also zero-pad the image if the polygon is\n        partially/fully outside of the image.\n\n        Parameters\n        ----------\n        image : (H,W) ndarray or (H,W,C) ndarray\n            The image from which to extract the pixels within the polygon.\n\n        Returns\n        -------\n        (H',W') ndarray or (H',W',C) ndarray\n            Pixels within the polygon. Zero-padded if the polygon is\n            partially/fully outside of the image.\n\n        \"\"\"\n        assert image.ndim in [2, 3], (\n            \"Expected image of shape (H,W,[C]), got shape %s.\" % (\n                image.shape,))\n\n        if len(self.exterior) <= 2:\n            raise Exception(\"Polygon must be made up of at least 3 points to \"\n                            \"extract its area from an image.\")\n\n        bb = self.to_bounding_box()\n        bb_area = bb.extract_from_image(image)\n        if self.is_out_of_image(image, fully=True, partly=False):\n            return bb_area\n\n        xx = self.xx_int\n        yy = self.yy_int\n        xx_mask = xx - np.min(xx)\n        yy_mask = yy - np.min(yy)\n        height_mask = np.max(yy_mask)\n        width_mask = np.max(xx_mask)\n\n        rr_face, cc_face = skimage.draw.polygon(\n            yy_mask, xx_mask, shape=(height_mask, width_mask))\n\n        mask = np.zeros((height_mask, width_mask), dtype=np.bool)\n        mask[rr_face, cc_face] = True\n\n        if image.ndim == 3:\n            mask = np.tile(mask[:, :, np.newaxis], (1, 1, image.shape[2]))\n\n        return bb_area * mask\n\n    def change_first_point_by_coords(self, x, y, max_distance=1e-4,\n                                     raise_if_too_far_away=True):\n        \"\"\"\n        Reorder exterior points so that the point closest to given x/y is first.\n\n        This method takes a given ``(x,y)`` coordinate, finds the closest\n        corner point on the exterior and reorders all exterior corner points\n        so that the found point becomes the first one in the array.\n\n        If no matching points are found, an exception is raised.\n\n        Parameters\n        ----------\n        x : number\n            X-coordinate of the point.\n\n        y : number\n            Y-coordinate of the point.\n\n        max_distance : None or number, optional\n            Maximum distance past which possible matches are ignored.\n            If ``None`` the distance limit is deactivated.\n\n        raise_if_too_far_away : bool, optional\n            Whether to raise an exception if the closest found point is too\n            far away (``True``) or simply return an unchanged copy if this\n            object (``False``).\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            Copy of this polygon with the new point order.\n\n        \"\"\"\n        if len(self.exterior) == 0:\n            raise Exception(\"Cannot reorder polygon points, because it \"\n                            \"contains no points.\")\n\n        closest_idx, closest_dist = self.find_closest_point_index(\n            x=x, y=y, return_distance=True)\n        if max_distance is not None and closest_dist > max_distance:\n            if not raise_if_too_far_away:\n                return self.deepcopy()\n\n            closest_point = self.exterior[closest_idx, :]\n            raise Exception(\n                \"Closest found point (%.9f, %.9f) exceeds max_distance of \"\n                \"%.9f exceeded\" % (\n                    closest_point[0], closest_point[1], closest_dist))\n        return self.change_first_point_by_index(closest_idx)\n\n    def change_first_point_by_index(self, point_idx):\n        \"\"\"\n        Reorder exterior points so that the point with given index is first.\n\n        This method takes a given index and reorders all exterior corner points\n        so that the point with that index becomes the first one in the array.\n\n        An ``AssertionError`` will be raised if the index does not match\n        any exterior point's index or the exterior does not contain any points.\n\n        Parameters\n        ----------\n        point_idx : int\n            Index of the desired starting point.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            Copy of this polygon with the new point order.\n\n        \"\"\"\n        assert 0 <= point_idx < len(self.exterior), (\n            \"Expected index of new first point to be in the discrete interval \"\n            \"[0..%d). Got index %d.\" % (len(self.exterior), point_idx))\n\n        if point_idx == 0:\n            return self.deepcopy()\n        exterior = np.concatenate(\n            (self.exterior[point_idx:, :], self.exterior[:point_idx, :]),\n            axis=0\n        )\n        return self.deepcopy(exterior=exterior)\n\n    def subdivide_(self, points_per_edge):\n        \"\"\"Derive a new poly with ``N`` interpolated points per edge in-place.\n\n        See :func:`~imgaug.augmentables.lines.LineString.subdivide` for details.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        points_per_edge : int\n            Number of points to interpolate on each edge.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            Polygon with subdivided edges.\n            The object may have been modified in-place.\n\n        \"\"\"\n        if len(self.exterior) == 1:\n            return self\n        ls = self.to_line_string(closed=True)\n        ls_sub = ls.subdivide(points_per_edge)\n        # [:-1] even works if the polygon contains zero points\n        exterior_subdivided = ls_sub.coords[:-1]\n        self.exterior = exterior_subdivided\n        return self\n\n    def subdivide(self, points_per_edge):\n        \"\"\"Derive a new polygon with ``N`` interpolated points per edge.\n\n        See :func:`~imgaug.augmentables.lines.LineString.subdivide` for details.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        points_per_edge : int\n            Number of points to interpolate on each edge.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            Polygon with subdivided edges.\n\n        \"\"\"\n        return self.deepcopy().subdivide_(points_per_edge)\n\n    def to_shapely_polygon(self):\n        \"\"\"Convert this polygon to a ``Shapely`` ``Polygon``.\n\n        Returns\n        -------\n        shapely.geometry.Polygon\n            The ``Shapely`` ``Polygon`` matching this polygon's exterior.\n\n        \"\"\"\n        # load shapely lazily, which makes the dependency more optional\n        import shapely.geometry\n\n        return shapely.geometry.Polygon(\n            [(point[0], point[1]) for point in self.exterior])\n\n    def to_shapely_line_string(self, closed=False, interpolate=0):\n        \"\"\"Convert this polygon to a ``Shapely`` ``LineString`` object.\n\n        Parameters\n        ----------\n        closed : bool, optional\n            Whether to return the line string with the last point being\n            identical to the first point.\n\n        interpolate : int, optional\n            Number of points to interpolate between any pair of two\n            consecutive points. These points are added to the final line string.\n\n        Returns\n        -------\n        shapely.geometry.LineString\n            The ``Shapely`` ``LineString`` matching the polygon's exterior.\n\n        \"\"\"\n        return _convert_points_to_shapely_line_string(\n            self.exterior, closed=closed, interpolate=interpolate)\n\n    def to_bounding_box(self):\n        \"\"\"Convert this polygon to a bounding box containing the polygon.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBox\n            Bounding box that tightly encapsulates the polygon.\n\n        \"\"\"\n        # TODO get rid of this deferred import\n        from imgaug.augmentables.bbs import BoundingBox\n\n        xx = self.xx\n        yy = self.yy\n        return BoundingBox(x1=min(xx), x2=max(xx),\n                           y1=min(yy), y2=max(yy),\n                           label=self.label)\n\n    def to_keypoints(self):\n        \"\"\"Convert this polygon's exterior to ``Keypoint`` instances.\n\n        Returns\n        -------\n        list of imgaug.augmentables.kps.Keypoint\n            Exterior vertices as :class:`~imgaug.augmentables.kps.Keypoint`\n            instances.\n\n        \"\"\"\n        # TODO get rid of this deferred import\n        from imgaug.augmentables.kps import Keypoint\n\n        return [Keypoint(x=point[0], y=point[1]) for point in self.exterior]\n\n    def to_line_string(self, closed=True):\n        \"\"\"Convert this polygon's exterior to a ``LineString`` instance.\n\n        Parameters\n        ----------\n        closed : bool, optional\n            Whether to close the line string, i.e. to add the first point of\n            the `exterior` also as the last point at the end of the line string.\n            This has no effect if the polygon has a single point or zero\n            points.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineString\n            Exterior of the polygon as a line string.\n\n        \"\"\"\n        from imgaug.augmentables.lines import LineString\n        if not closed or len(self.exterior) <= 1:\n            return LineString(self.exterior, label=self.label)\n        return LineString(\n            np.concatenate([self.exterior, self.exterior[0:1, :]], axis=0),\n            label=self.label)\n\n    @staticmethod\n    def from_shapely(polygon_shapely, label=None):\n        \"\"\"Create a polygon from a ``Shapely`` ``Polygon``.\n\n        .. note::\n\n            This will remove any holes in the shapely polygon.\n\n        Parameters\n        ----------\n        polygon_shapely : shapely.geometry.Polygon\n             The shapely polygon.\n\n        label : None or str, optional\n            The label of the new polygon.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            A polygon with the same exterior as the ``Shapely`` ``Polygon``.\n\n        \"\"\"\n        # load shapely lazily, which makes the dependency more optional\n        import shapely.geometry\n\n        assert isinstance(polygon_shapely, shapely.geometry.Polygon), (\n            \"Expected the input to be a shapely.geometry.Polgon instance. \"\n            \"Got %s.\" % (type(polygon_shapely),))\n        # polygon_shapely.exterior can be None if the polygon was\n        # instantiated without points\n        has_no_exterior = (\n            polygon_shapely.exterior is None\n            or len(polygon_shapely.exterior.coords) == 0)\n        if has_no_exterior:\n            return Polygon([], label=label)\n        exterior = np.float32([[x, y]\n                               for (x, y)\n                               in polygon_shapely.exterior.coords])\n        return Polygon(exterior, label=label)\n\n    def coords_almost_equals(self, other, max_distance=1e-4,\n                             points_per_edge=8):\n        \"\"\"Alias for :func:`Polygon.exterior_almost_equals`.\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.polys.Polygon or (N,2) ndarray or list of tuple\n            See\n            :func:`~imgaug.augmentables.polys.Polygon.exterior_almost_equals`.\n\n        max_distance : number, optional\n            See\n            :func:`~imgaug.augmentables.polys.Polygon.exterior_almost_equals`.\n\n        points_per_edge : int, optional\n            See\n            :func:`~imgaug.augmentables.polys.Polygon.exterior_almost_equals`.\n\n        Returns\n        -------\n        bool\n            Whether the two polygon's exteriors can be viewed as equal\n            (approximate test).\n\n        \"\"\"\n        return self.exterior_almost_equals(\n            other, max_distance=max_distance, points_per_edge=points_per_edge)\n\n    def exterior_almost_equals(self, other, max_distance=1e-4,\n                               points_per_edge=8):\n        \"\"\"Estimate if this and another polygon's exterior are almost identical.\n\n        The two exteriors can have different numbers of points, but any point\n        randomly sampled on the exterior of one polygon should be close to the\n        closest point on the exterior of the other polygon.\n\n        .. note::\n\n            This method works in an approximative way. One can come up with\n            polygons with fairly different shapes that will still be estimated\n            as equal by this method. In practice however this should be\n            unlikely to be the case. The probability for something like that\n            goes down as the interpolation parameter is increased.\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.polys.Polygon or (N,2) ndarray or list of tuple\n            The other polygon with which to compare the exterior.\n            If this is an ``ndarray``, it is assumed to represent an exterior.\n            It must then have dtype ``float32`` and shape ``(N,2)`` with the\n            second dimension denoting xy-coordinates.\n            If this is a ``list`` of ``tuple`` s, it is assumed to represent\n            an exterior. Each tuple then must contain exactly two ``number`` s,\n            denoting xy-coordinates.\n\n        max_distance : number, optional\n            The maximum euclidean distance between a point on one polygon and\n            the closest point on the other polygon. If the distance is exceeded\n            for any such pair, the two exteriors are not viewed as equal. The\n            points are either the points contained in the polygon's exterior\n            ndarray or interpolated points between these.\n\n        points_per_edge : int, optional\n            How many points to interpolate on each edge.\n\n        Returns\n        -------\n        bool\n            Whether the two polygon's exteriors can be viewed as equal\n            (approximate test).\n\n        \"\"\"\n        if isinstance(other, list):\n            other = Polygon(np.float32(other))\n        elif ia.is_np_array(other):\n            other = Polygon(other)\n        else:\n            assert isinstance(other, Polygon), (\n                \"Expected 'other' to be a list of coordinates, a coordinate \"\n                \"array or a single Polygon. Got type %s.\" % (type(other),))\n\n        return self.to_line_string(closed=True).coords_almost_equals(\n            other.to_line_string(closed=True),\n            max_distance=max_distance,\n            points_per_edge=points_per_edge\n        )\n\n    def almost_equals(self, other, max_distance=1e-4, points_per_edge=8):\n        \"\"\"\n        Estimate if this polygon's and another's geometry/labels are similar.\n\n        This is the same as\n        :func:`~imgaug.augmentables.polys.Polygon.exterior_almost_equals` but\n        additionally compares the labels.\n\n        Parameters\n        ----------\n        other : imgaug.augmentables.polys.Polygon\n            The other object to compare against. Expected to be a ``Polygon``.\n\n        max_distance : float, optional\n            See\n            :func:`~imgaug.augmentables.polys.Polygon.exterior_almost_equals`.\n\n        points_per_edge : int, optional\n            See\n            :func:`~imgaug.augmentables.polys.Polygon.exterior_almost_equals`.\n\n        Returns\n        -------\n        bool\n            ``True`` if the coordinates are almost equal and additionally\n            the labels are equal. Otherwise ``False``.\n\n        \"\"\"\n        if self.label != other.label:\n            return False\n        return self.exterior_almost_equals(\n            other, max_distance=max_distance, points_per_edge=points_per_edge)\n\n    def copy(self, exterior=None, label=None):\n        \"\"\"Create a shallow copy of this object.\n\n        Parameters\n        ----------\n        exterior : list of imgaug.augmentables.kps.Keypoint or list of tuple or (N,2) ndarray, optional\n            List of points defining the polygon. See\n            :func:`~imgaug.augmentables.polys.Polygon.__init__` for details.\n\n        label : None or str, optional\n            If not ``None``, the ``label`` of the copied object will be set\n            to this value.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            Shallow copy.\n\n        \"\"\"\n        return self.deepcopy(exterior=exterior, label=label)\n\n    def deepcopy(self, exterior=None, label=None):\n        \"\"\"Create a deep copy of this object.\n\n        Parameters\n        ----------\n        exterior : list of Keypoint or list of tuple or (N,2) ndarray, optional\n            List of points defining the polygon. See\n            `imgaug.augmentables.polys.Polygon.__init__` for details.\n\n        label : None or str\n            If not ``None``, the ``label`` of the copied object will be set\n            to this value.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.Polygon\n            Deep copy.\n\n        \"\"\"\n        return Polygon(\n            exterior=np.copy(self.exterior) if exterior is None else exterior,\n            label=self.label if label is None else label)\n\n    def __getitem__(self, indices):\n        \"\"\"Get the coordinate(s) with given indices.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        ndarray\n            xy-coordinate(s) as ``ndarray``.\n\n        \"\"\"\n        return self.exterior[indices]\n\n    def __iter__(self):\n        \"\"\"Iterate over the coordinates of this instance.\n\n        Added in 0.4.0.\n\n        Yields\n        ------\n        ndarray\n            An ``(2,)`` ``ndarray`` denoting an xy-coordinate pair.\n\n        \"\"\"\n        return iter(self.exterior)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        points_str = \", \".join([\n            \"(x=%.3f, y=%.3f)\" % (point[0], point[1])\n            for point\n            in self.exterior])\n        return \"Polygon([%s] (%d points), label=%s)\" % (\n            points_str, len(self.exterior), self.label)\n\n\n# TODO add tests for this\nclass PolygonsOnImage(IAugmentable):\n    \"\"\"Container for all polygons on a single image.\n\n    Parameters\n    ----------\n    polygons : list of imgaug.augmentables.polys.Polygon\n        List of polygons on the image.\n\n    shape : tuple of int\n        The shape of the image on which the objects are placed, i.e. the\n        result of ``image.shape``.\n        Should include the number of channels, not only height and width.\n\n    Examples\n    --------\n    >>> import numpy as np\n    >>> from imgaug.augmentables.polys import Polygon, PolygonsOnImage\n    >>> image = np.zeros((100, 100))\n    >>> polys = [\n    >>>     Polygon([(0.5, 0.5), (100.5, 0.5), (100.5, 100.5), (0.5, 100.5)]),\n    >>>     Polygon([(50.5, 0.5), (100.5, 50.5), (50.5, 100.5), (0.5, 50.5)])\n    >>> ]\n    >>> polys_oi = PolygonsOnImage(polys, shape=image.shape)\n\n    \"\"\"\n\n    def __init__(self, polygons, shape):\n        self.polygons = polygons\n        self.shape = _handle_on_image_shape(shape, self)\n\n    @property\n    def items(self):\n        \"\"\"Get the polygons in this container.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of Polygon\n            Polygons within this container.\n\n        \"\"\"\n        return self.polygons\n\n    @items.setter\n    def items(self, value):\n        \"\"\"Set the polygons in this container.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        value : list of Polygon\n            Polygons within this container.\n\n        \"\"\"\n        self.polygons = value\n\n    @property\n    def empty(self):\n        \"\"\"Estimate whether this object contains zero polygons.\n\n        Returns\n        -------\n        bool\n            ``True`` if this object contains zero polygons.\n\n        \"\"\"\n        return len(self.polygons) == 0\n\n    def on_(self, image):\n        \"\"\"Project all polygons from one image shape to a new one in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            New image onto which the polygons are to be projected.\n            May also simply be that new image's shape ``tuple``.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Object containing all projected polygons.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        # pylint: disable=invalid-name\n        on_shape = normalize_imglike_shape(image)\n        if on_shape[0:2] == self.shape[0:2]:\n            self.shape = on_shape  # channels may differ\n            return self\n\n        for i, item in enumerate(self.items):\n            self.polygons[i] = item.project_(self.shape, on_shape)\n        self.shape = on_shape\n        return self\n\n    def on(self, image):\n        \"\"\"Project all polygons from one image shape to a new one.\n\n        Parameters\n        ----------\n        image : ndarray or tuple of int\n            New image onto which the polygons are to be projected.\n            May also simply be that new image's shape ``tuple``.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Object containing all projected polygons.\n\n        \"\"\"\n        # pylint: disable=invalid-name\n        return self.deepcopy().on_(image)\n\n    def draw_on_image(self,\n                      image,\n                      color=(0, 255, 0), color_face=None,\n                      color_lines=None, color_points=None,\n                      alpha=1.0, alpha_face=None,\n                      alpha_lines=None, alpha_points=None,\n                      size=1, size_lines=None, size_points=None,\n                      raise_if_out_of_image=False):\n        \"\"\"Draw all polygons onto a given image.\n\n        Parameters\n        ----------\n        image : (H,W,C) ndarray\n            The image onto which to draw the bounding boxes.\n            This image should usually have the same shape as set in\n            ``PolygonsOnImage.shape``.\n\n        color : iterable of int, optional\n            The color to use for the whole polygons.\n            Must correspond to the channel layout of the image. Usually RGB.\n            The values for `color_face`, `color_lines` and `color_points`\n            will be derived from this color if they are set to ``None``.\n            This argument has no effect if `color_face`, `color_lines`\n            and `color_points` are all set anything other than ``None``.\n\n        color_face : None or iterable of int, optional\n            The color to use for the inner polygon areas (excluding perimeters).\n            Must correspond to the channel layout of the image. Usually RGB.\n            If this is ``None``, it will be derived from ``color * 1.0``.\n\n        color_lines : None or iterable of int, optional\n            The color to use for the lines (aka perimeters/borders) of the\n            polygons. Must correspond to the channel layout of the image.\n            Usually RGB. If this is ``None``, it will be derived\n            from ``color * 0.5``.\n\n        color_points : None or iterable of int, optional\n            The color to use for the corner points of the polygons.\n            Must correspond to the channel layout of the image. Usually RGB.\n            If this is ``None``, it will be derived from ``color * 0.5``.\n\n        alpha : float, optional\n            The opacity of the whole polygons, where ``1.0`` denotes\n            completely visible polygons and ``0.0`` invisible ones.\n            The values for `alpha_face`, `alpha_lines` and `alpha_points`\n            will be derived from this alpha value if they are set to ``None``.\n            This argument has no effect if `alpha_face`, `alpha_lines`\n            and `alpha_points` are all set anything other than ``None``.\n\n        alpha_face : None or number, optional\n            The opacity of the polygon's inner areas (excluding the perimeters),\n            where ``1.0`` denotes completely visible inner areas and ``0.0``\n            invisible ones.\n            If this is ``None``, it will be derived from ``alpha * 0.5``.\n\n        alpha_lines : None or number, optional\n            The opacity of the polygon's lines (aka perimeters/borders),\n            where ``1.0`` denotes completely visible perimeters and ``0.0``\n            invisible ones.\n            If this is ``None``, it will be derived from ``alpha * 1.0``.\n\n        alpha_points : None or number, optional\n            The opacity of the polygon's corner points, where ``1.0`` denotes\n            completely visible corners and ``0.0`` invisible ones.\n            Currently this is an on/off choice, i.e. only ``0.0`` or ``1.0``\n            are allowed.\n            If this is ``None``, it will be derived from ``alpha * 1.0``.\n\n        size : int, optional\n            Size of the polygons.\n            The sizes of the line and points are derived from this value,\n            unless they are set.\n\n        size_lines : None or int, optional\n            Thickness of the polygon lines (aka perimeter/border).\n            If ``None``, this value is derived from `size`.\n\n        size_points : int, optional\n            The size of all corner points. If set to ``C``, each corner point\n            will be drawn as a square of size ``C x C``.\n\n        raise_if_out_of_image : bool, optional\n            Whether to raise an error if any polygon is fully\n            outside of the image. If set to False, no error will be raised and\n            only the parts inside the image will be drawn.\n\n        Returns\n        -------\n        (H,W,C) ndarray\n            Image with drawn polygons.\n\n        \"\"\"\n        for poly in self.polygons:\n            image = poly.draw_on_image(\n                image,\n                color=color,\n                color_face=color_face,\n                color_lines=color_lines,\n                color_points=color_points,\n                alpha=alpha,\n                alpha_face=alpha_face,\n                alpha_lines=alpha_lines,\n                alpha_points=alpha_points,\n                size=size,\n                size_lines=size_lines,\n                size_points=size_points,\n                raise_if_out_of_image=raise_if_out_of_image\n            )\n        return image\n\n    def remove_out_of_image_(self, fully=True, partly=False):\n        \"\"\"Remove all polygons that are fully/partially OOI in-place.\n\n        'OOI' is the abbreviation for 'out of image'.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        fully : bool, optional\n            Whether to remove polygons that are fully outside of the image.\n\n        partly : bool, optional\n            Whether to remove polygons that are partially outside of the image.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Reduced set of polygons. Those that are fully/partially\n            outside of the given image plane are removed.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        self.polygons = [\n            poly for poly in self.polygons\n            if not poly.is_out_of_image(self.shape, fully=fully, partly=partly)\n        ]\n        return self\n\n    def remove_out_of_image(self, fully=True, partly=False):\n        \"\"\"Remove all polygons that are fully/partially outside of an image.\n\n        Parameters\n        ----------\n        fully : bool, optional\n            Whether to remove polygons that are fully outside of the image.\n\n        partly : bool, optional\n            Whether to remove polygons that are partially outside of the image.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Reduced set of polygons. Those that are fully/partially\n            outside of the given image plane are removed.\n\n        \"\"\"\n        return self.deepcopy().remove_out_of_image_(fully, partly)\n\n    def remove_out_of_image_fraction_(self, fraction):\n        \"\"\"Remove all Polys with an OOI fraction of ``>=fraction`` in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        fraction : number\n            Minimum out of image fraction that a polygon has to have in\n            order to be removed. A fraction of ``1.0`` removes only polygons\n            that are ``100%`` outside of the image. A fraction of ``0.0``\n            removes all polygons.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Reduced set of polygons, with those that had an out of image\n            fraction greater or equal the given one removed.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        return _remove_out_of_image_fraction_(self, fraction)\n\n    def remove_out_of_image_fraction(self, fraction):\n        \"\"\"Remove all Polys with an out of image fraction of ``>=fraction``.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        fraction : number\n            Minimum out of image fraction that a polygon has to have in\n            order to be removed. A fraction of ``1.0`` removes only polygons\n            that are ``100%`` outside of the image. A fraction of ``0.0``\n            removes all polygons.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Reduced set of polygons, with those that had an out of image\n            fraction greater or equal the given one removed.\n\n        \"\"\"\n        return self.copy().remove_out_of_image_fraction_(fraction)\n\n    def clip_out_of_image_(self):\n        \"\"\"Clip off all parts from all polygons that are OOI in-place.\n\n        'OOI' is the abbreviation for 'out of image'.\n\n        .. note::\n\n            The result can contain fewer polygons than the input did. That\n            happens when a polygon is fully outside of the image plane.\n\n        .. note::\n\n            The result can also contain *more* polygons than the input\n            did. That happens when distinct parts of a polygon are only\n            connected by areas that are outside of the image plane and hence\n            will be clipped off, resulting in two or more unconnected polygon\n            parts that are left in the image plane.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Polygons, clipped to fall within the image dimensions.\n            The count of output polygons may differ from the input count.\n            The object and its items may have been modified in-place.\n\n        \"\"\"\n        self.polygons = [\n            poly_clipped\n            for poly in self.polygons\n            for poly_clipped in poly.clip_out_of_image(self.shape)]\n        return self\n\n    def clip_out_of_image(self):\n        \"\"\"Clip off all parts from all polygons that are outside of an image.\n\n        .. note::\n\n            The result can contain fewer polygons than the input did. That\n            happens when a polygon is fully outside of the image plane.\n\n        .. note::\n\n            The result can also contain *more* polygons than the input\n            did. That happens when distinct parts of a polygon are only\n            connected by areas that are outside of the image plane and hence\n            will be clipped off, resulting in two or more unconnected polygon\n            parts that are left in the image plane.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Polygons, clipped to fall within the image dimensions.\n            The count of output polygons may differ from the input count.\n\n        \"\"\"\n        return self.copy().clip_out_of_image_()\n\n    def shift_(self, x=0, y=0):\n        \"\"\"Move the polygons along the x/y-axis in-place.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Shifted polygons.\n\n        \"\"\"\n        for i, poly in enumerate(self.polygons):\n            self.polygons[i] = poly.shift_(x=x, y=y)\n        return self\n\n    def shift(self, x=0, y=0, top=None, right=None, bottom=None, left=None):\n        \"\"\"Move the polygons along the x/y-axis.\n\n        The origin ``(0, 0)`` is at the top left of the image.\n\n        Parameters\n        ----------\n        x : number, optional\n            Value to be added to all x-coordinates. Positive values shift\n            towards the right images.\n\n        y : number, optional\n            Value to be added to all y-coordinates. Positive values shift\n            towards the bottom images.\n\n        top : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            top (towards the bottom).\n\n        right : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            right (towads the left).\n\n        bottom : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            bottom (towards the top).\n\n        left : None or int, optional\n            Deprecated since 0.4.0.\n            Amount of pixels by which to shift all objects *from* the\n            left (towards the right).\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Shifted polygons.\n\n        \"\"\"\n        x, y = _normalize_shift_args(\n            x, y, top=top, right=right, bottom=bottom, left=left)\n        return self.deepcopy().shift_(x=x, y=y)\n\n    def subdivide_(self, points_per_edge):\n        \"\"\"Interpolate ``N`` points on each polygon.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        points_per_edge : int\n            Number of points to interpolate on each edge.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Subdivided polygons.\n\n        \"\"\"\n        for i, poly in enumerate(self.polygons):\n            self.polygons[i] = poly.subdivide_(points_per_edge)\n        return self\n\n    def subdivide(self, points_per_edge):\n        \"\"\"Interpolate ``N`` points on each polygon.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        points_per_edge : int\n            Number of points to interpolate on each edge.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Subdivided polygons.\n\n        \"\"\"\n        return self.deepcopy().subdivide_(points_per_edge)\n\n    def to_xy_array(self):\n        \"\"\"Convert all polygon coordinates to one array of shape ``(N,2)``.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        (N, 2) ndarray\n            Array containing all xy-coordinates of all polygons within this\n            instance.\n\n        \"\"\"\n        if self.empty:\n            return np.zeros((0, 2), dtype=np.float32)\n        return np.concatenate([poly.exterior for poly in self.polygons])\n\n    def fill_from_xy_array_(self, xy):\n        \"\"\"Modify the corner coordinates of all polygons in-place.\n\n        .. note::\n\n            This currently expects that `xy` contains exactly as many\n            coordinates as the polygons within this instance have corner\n            points. Otherwise, an ``AssertionError`` will be raised.\n\n        .. warning::\n\n            This does not validate the new coordinates or repair the resulting\n            polygons. If bad coordinates are provided, the result will be\n            invalid polygons (e.g. self-intersections).\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        xy : (N, 2) ndarray or iterable of iterable of number\n            XY-Coordinates of ``N`` corner points. ``N`` must match the\n            number of corner points in all polygons within this instance.\n\n        Returns\n        -------\n        PolygonsOnImage\n            This instance itself, with updated coordinates.\n            Note that the instance was modified in-place.\n\n        \"\"\"\n        xy = np.array(xy, dtype=np.float32)\n\n        # note that np.array([]) is (0,), not (0, 2)\n        assert xy.shape[0] == 0 or (xy.ndim == 2 and xy.shape[-1] == 2), (  # pylint: disable=unsubscriptable-object\n            \"Expected input array to have shape (N,2), \"\n            \"got shape %s.\" % (xy.shape,))\n\n        counter = 0\n        for poly in self.polygons:\n            nb_points = len(poly.exterior)\n            assert counter + nb_points <= len(xy), (\n                \"Received fewer points than there are corner points in the \"\n                \"exteriors of all polygons. Got %d points, expected %d.\" % (\n                    len(xy), sum([len(p.exterior) for p in self.polygons])))\n\n            poly.exterior[:, ...] = xy[counter:counter+nb_points]\n            counter += nb_points\n\n        assert counter == len(xy), (\n            \"Expected to get exactly as many xy-coordinates as there are \"\n            \"points in the exteriors of all polygons within this instance. \"\n            \"Got %d points, could only assign %d points.\" % (\n                len(xy), counter,))\n\n        return self\n\n    def to_keypoints_on_image(self):\n        \"\"\"Convert the polygons to one ``KeypointsOnImage`` instance.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage\n            A keypoints instance containing ``N`` coordinates for a total\n            of ``N`` points in all exteriors of the polygons within this\n            container. Order matches the order in ``polygons``.\n\n        \"\"\"\n        from . import KeypointsOnImage\n        if self.empty:\n            return KeypointsOnImage([], shape=self.shape)\n        exteriors = np.concatenate(\n            [poly.exterior for poly in self.polygons],\n            axis=0)\n        return KeypointsOnImage.from_xy_array(exteriors, shape=self.shape)\n\n    def invert_to_keypoints_on_image_(self, kpsoi):\n        \"\"\"Invert the output of ``to_keypoints_on_image()`` in-place.\n\n        This function writes in-place into this ``PolygonsOnImage``\n        instance.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        kpsoi : imgaug.augmentables.kps.KeypointsOnImages\n            Keypoints to convert back to polygons, i.e. the outputs\n            of ``to_keypoints_on_image()``.\n\n        Returns\n        -------\n        PolygonsOnImage\n            Polygons container with updated coordinates.\n            Note that the instance is also updated in-place.\n\n        \"\"\"\n        polys = self.polygons\n        exteriors = [poly.exterior for poly in polys]\n        nb_points_exp = sum([len(exterior) for exterior in exteriors])\n        assert len(kpsoi.keypoints) == nb_points_exp, (\n            \"Expected %d coordinates, got %d.\" % (\n                nb_points_exp, len(kpsoi.keypoints)))\n\n        xy_arr = kpsoi.to_xy_array()\n\n        counter = 0\n        for poly in polys:\n            exterior = poly.exterior\n            exterior[:, :] = xy_arr[counter:counter+len(exterior), :]\n            counter += len(exterior)\n        self.shape = kpsoi.shape\n        return self\n\n    def copy(self, polygons=None, shape=None):\n        \"\"\"Create a shallow copy of this object.\n\n        Parameters\n        ----------\n        polygons : None or list of imgaug.augmentables.polys.Polygons, optional\n            List of polygons on the image.\n            If not ``None``, then the ``polygons`` attribute of the copied\n            object will be set to this value.\n\n        shape : None or tuple of int or ndarray, optional\n            The shape of the image on which the objects are placed.\n            Either an image with shape ``(H,W,[C])`` or a tuple denoting\n            such an image shape.\n            If not ``None``, then the ``shape`` attribute of the copied object\n            will be set to this value.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Shallow copy.\n\n        \"\"\"\n        if polygons is None:\n            polygons = self.polygons[:]\n        if shape is None:\n            # use tuple() here in case the shape was provided as a list\n            shape = tuple(self.shape)\n\n        return PolygonsOnImage(polygons, shape)\n\n    def deepcopy(self, polygons=None, shape=None):\n        \"\"\"Create a deep copy of this object.\n\n        Parameters\n        ----------\n        polygons : None or list of imgaug.augmentables.polys.Polygons, optional\n            List of polygons on the image.\n            If not ``None``, then the ``polygons`` attribute of the copied\n            object will be set to this value.\n\n        shape : None or tuple of int or ndarray, optional\n            The shape of the image on which the objects are placed.\n            Either an image with shape ``(H,W,[C])`` or a tuple denoting\n            such an image shape.\n            If not ``None``, then the ``shape`` attribute of the copied object\n            will be set to this value.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage\n            Deep copy.\n\n        \"\"\"\n        # Manual copy is far faster than deepcopy, so use manual copy here.\n        if polygons is None:\n            polygons = [poly.deepcopy() for poly in self.polygons]\n        if shape is None:\n            # use tuple() here in case the shape was provided as a list\n            shape = tuple(self.shape)\n\n        return PolygonsOnImage(polygons, shape)\n\n    def __getitem__(self, indices):\n        \"\"\"Get the polygon(s) with given indices.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        list of imgaug.augmentables.polys.Polygon\n            Polygon(s) with given indices.\n\n        \"\"\"\n        return self.polygons[indices]\n\n    def __iter__(self):\n        \"\"\"Iterate over the polygons in this container.\n\n        Added in 0.4.0.\n\n        Yields\n        ------\n        Polygon\n            A polygon in this container.\n            The order is identical to the order in the polygon list\n            provided upon class initialization.\n\n        \"\"\"\n        return iter(self.polygons)\n\n    def __len__(self):\n        \"\"\"Get the number of items in this instance.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        int\n            Number of items in this instance.\n\n        \"\"\"\n        return len(self.items)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"PolygonsOnImage(%s, shape=%s)\" % (\n            str(self.polygons), self.shape)\n\n\ndef _convert_points_to_shapely_line_string(points, closed=False,\n                                           interpolate=0):\n    # load shapely lazily, which makes the dependency more optional\n    import shapely.geometry\n\n    if len(points) <= 1:\n        raise Exception(\n            \"Conversion to shapely line string requires at least two points, \"\n            \"but points input contains only %d points.\" % (len(points),))\n\n    points_tuples = [(point[0], point[1]) for point in points]\n\n    # interpolate points between each consecutive pair of points\n    if interpolate > 0:\n        points_tuples = interpolate_points(points_tuples, interpolate)\n\n    # close if requested and not yet closed\n    # used here intentionally `points` instead of `points_tuples`\n    if closed and len(points) > 1:\n        points_tuples.append(points_tuples[0])\n\n    return shapely.geometry.LineString(points_tuples)\n\n\nclass _ConcavePolygonRecoverer(object):\n    def __init__(self, threshold_duplicate_points=1e-4, noise_strength=1e-4,\n                 oversampling=0.01, max_segment_difference=1e-4):\n        self.threshold_duplicate_points = threshold_duplicate_points\n        self.noise_strength = noise_strength\n        self.oversampling = oversampling\n        self.max_segment_difference = max_segment_difference\n\n        # this limits the maximum amount of points after oversampling, i.e.\n        # if N points are input into oversampling, then M oversampled points\n        # are generated such that N+M <= this value\n        self.oversample_up_to_n_points_max = 75\n\n        # ----\n        # parameters for _fit_best_valid_polygon()\n        # ----\n        # how many changes may be done max to the initial (convex hull) polygon\n        # before simply returning the result\n        self.fit_n_changes_max = 100\n        # for how many iterations the optimization loop may run max\n        # before simply returning the result\n        self.fit_n_iters_max = 3\n        # how far (wrt. to their position in the input list) two points may be\n        # apart max to consider adding an edge between them (in the first loop\n        # iteration and the ones after that)\n        self.fit_max_dist_first_iter = 1\n        self.fit_max_dist_other_iters = 2\n        # The fit loop first generates candidate edges and then modifies the\n        # polygon based on these candidates. This limits the maximum amount\n        # of considered candidates. If the number is less than the possible\n        # number of candidates, they are randomly subsampled. Values beyond\n        # 100 significantly increase runtime (for polygons that reach that\n        # number).\n        self.fit_n_candidates_before_sort_max = 100\n\n        # If abs(x) or abs(y) of any coordinate of a polygon is beyond this\n        # value, no intersection points will be computed anymore. That is done,\n        # because the underlying library to find these points uses float\n        # values as keys and may therefore start to encounter inaccuracies\n        # leading to exceptions within that library.\n        self.limit_coords_values_for_inter_search = 50000\n\n        # Rounding of coordinates to use before feeding them into the\n        # library to search for intersection points. Note that the library\n        # was set to also use a corresponding eps of 1e-4.\n        self.decimals = 4\n\n    def recover_from(self, new_exterior, old_polygon, random_state=0):\n        assert isinstance(new_exterior, list) or (\n            ia.is_np_array(new_exterior)\n            and new_exterior.ndim == 2\n            and new_exterior.shape[1] == 2), (\n                \"Expected exterior as list or (N,2) ndarray, got type %s.\" % (\n                    type(new_exterior),))\n        assert len(new_exterior) >= 3, \\\n            \"Cannot recover a concave polygon from less than three points.\"\n\n        # create Polygon instance, if it is already valid then just return\n        # immediately\n        polygon = old_polygon.deepcopy(exterior=new_exterior)\n        if polygon.is_valid:\n            return polygon\n\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        rss = random_state.duplicate(3)\n\n        # remove consecutive duplicate points\n        new_exterior = self._remove_consecutive_duplicate_points(new_exterior)\n\n        # check that points are not all identical or on a line\n        new_exterior = self._fix_polygon_is_line(new_exterior, rss[0])\n\n        # jitter duplicate points\n        new_exterior = self._jitter_duplicate_points(new_exterior, rss[1])\n\n        # generate intersection points\n        segment_add_points = self._generate_intersection_points(\n            new_exterior, decimals=self.decimals)\n\n        # oversample points around intersections\n        if self.oversampling is not None and self.oversampling > 0:\n            segment_add_points = self._oversample_intersection_points(\n                new_exterior, segment_add_points)\n\n        # integrate new points into exterior\n        new_exterior_inter = self._insert_intersection_points(\n            new_exterior, segment_add_points)\n\n        # find best fit polygon, starting from convext polygon\n        new_exterior_concave_ids = self._fit_best_valid_polygon(\n            new_exterior_inter, rss[2])\n        new_exterior_concave = [\n            new_exterior_inter[idx] for idx in new_exterior_concave_ids]\n\n        # TODO return new_exterior_concave here instead of polygon, leave it to\n        #      caller to decide what to do with it\n        return old_polygon.deepcopy(exterior=new_exterior_concave)\n\n    def _remove_consecutive_duplicate_points(self, points):\n        result = []\n        for point in points:\n            if result:\n                dist = np.linalg.norm(\n                    np.float32(point) - np.float32(result[-1]))\n                is_same = (dist < self.threshold_duplicate_points)\n                if not is_same:\n                    result.append(point)\n            else:\n                result.append(point)\n        if len(result) >= 2:\n            dist = np.linalg.norm(\n                np.float32(result[0]) - np.float32(result[-1]))\n            is_same = (dist < self.threshold_duplicate_points)\n            result = result[0:-1] if is_same else result\n        return result\n\n    # fix polygons for which all points are on a line\n    def _fix_polygon_is_line(self, exterior, random_state):\n        assert len(exterior) >= 3, (\n            \"Can only fix line-like polygons with an exterior containing at \"\n            \"least 3 points. Got one with %d points.\" % (len(exterior),))\n        noise_strength = self.noise_strength\n        while self._is_polygon_line(exterior):\n            noise = random_state.uniform(\n                -noise_strength, noise_strength, size=(len(exterior), 2)\n            ).astype(np.float32)\n            exterior = [(point[0] + noise_i[0], point[1] + noise_i[1])\n                        for point, noise_i in zip(exterior, noise)]\n            noise_strength = noise_strength * 10\n            assert noise_strength > 0, (\n                \"Expected noise strength to be >0, got %.4f.\" % (\n                    noise_strength,))\n        return exterior\n\n    @classmethod\n    def _is_polygon_line(cls, exterior):\n        vec_down = np.float32([0, 1])\n        point1 = exterior[0]\n        angles = set()\n        for point2 in exterior[1:]:\n            vec = np.float32(point2) - np.float32(point1)\n            angle = ia.angle_between_vectors(vec_down, vec)\n            angles.add(int(angle * 1000))\n        return len(angles) <= 1\n\n    def _jitter_duplicate_points(self, exterior, random_state):\n        def _find_duplicates(exterior_with_duplicates):\n            points_map = collections.defaultdict(list)\n\n            for i, point in enumerate(exterior_with_duplicates):\n                # we use 10/x here to be a bit more lenient, the precise\n                # distance test is further below\n                x = int(np.round(point[0]\n                                 * ((1/10) / self.threshold_duplicate_points)))\n                y = int(np.round(point[1]\n                                 * ((1/10) / self.threshold_duplicate_points)))\n                for direction0 in [-1, 0, 1]:\n                    for direction1 in [-1, 0, 1]:\n                        points_map[(x+direction0, y+direction1)].append(i)\n\n            duplicates = [False] * len(exterior_with_duplicates)\n            for key in points_map:\n                candidates = points_map[key]\n                for i, p0_idx in enumerate(candidates):\n                    p0_idx = candidates[i]\n                    point0 = exterior_with_duplicates[p0_idx]\n                    if duplicates[p0_idx]:\n                        continue\n\n                    for j in range(i+1, len(candidates)):\n                        p1_idx = candidates[j]\n                        point1 = exterior_with_duplicates[p1_idx]\n                        if duplicates[p1_idx]:\n                            continue\n\n                        dist = np.sqrt(\n                            (point0[0] - point1[0])**2\n                            + (point0[1] - point1[1])**2)\n                        if dist < self.threshold_duplicate_points:\n                            duplicates[p1_idx] = True\n\n            return duplicates\n\n        noise_strength = self.noise_strength\n        assert noise_strength > 0, (\n            \"Expected noise strength to be >0, got %.4f.\" % (noise_strength,))\n        exterior = exterior[:]\n        converged = False\n        while not converged:\n            duplicates = _find_duplicates(exterior)\n            if any(duplicates):\n                noise = random_state.uniform(\n                    -self.noise_strength,\n                    self.noise_strength,\n                    size=(len(exterior), 2)\n                ).astype(np.float32)\n\n                for i, is_duplicate in enumerate(duplicates):\n                    if is_duplicate:\n                        exterior[i] = (\n                            exterior[i][0] + noise[i][0],\n                            exterior[i][1] + noise[i][1])\n\n                noise_strength *= 10\n            else:\n                converged = True\n\n        return exterior\n\n    # TODO remove?\n    @classmethod\n    def _calculate_circumference(cls, points):\n        assert len(points) >= 3, (\n            \"Need at least 3 points on the exterior to compute the \"\n            \"circumference. Got %d.\" % (len(points),))\n        points = np.array(points, dtype=np.float32)\n        points_matrix = np.zeros((len(points), 4), dtype=np.float32)\n        points_matrix[:, 0:2] = points\n        points_matrix[0:-1, 2:4] = points_matrix[1:, 0:2]\n        points_matrix[-1, 2:4] = points_matrix[0, 0:2]\n        distances = np.linalg.norm(\n            points_matrix[:, 0:2] - points_matrix[:, 2:4], axis=1)\n        return np.sum(distances)\n\n    def _generate_intersection_points(self, exterior,\n                                      one_point_per_intersection=True,\n                                      decimals=4):\n        # pylint: disable=broad-except\n        largest_value = np.max(np.abs(np.array(exterior, dtype=np.float32)))\n        too_large_values = (\n            largest_value > self.limit_coords_values_for_inter_search)\n        if too_large_values:\n            ia.warn(\n                \"Encountered during polygon repair a polygon with extremely \"\n                \"large coordinate values beyond %d. Will skip intersection \"\n                \"point computation for that polygon. This avoids exceptions \"\n                \"and is -- due to the extreme distortion -- likely pointless \"\n                \"anyways (i.e. the polygon is already broken beyond repair). \"\n                \"Try using weaker augmentation parameters to avoid such \"\n                \"large coordinate values.\" % (\n                    self.limit_coords_values_for_inter_search,)\n            )\n            return [[] for _ in range(len(exterior))]\n\n        if ia.is_np_array(exterior):\n            exterior = list(exterior)\n        assert isinstance(exterior, list), (\n            \"Expected 'exterior' to be a list or a ndarray. \"\n            \"Got type %s.\" % (type(exterior),))\n        assert all([len(point) == 2 for point in exterior]), (\n            \"Expected 'exterior' to contain (x,y) coordinate pairs. \"\n            \"Got lengths %s.\" % (\n                \", \".join([str(len(point)) for point in exterior])))\n        if len(exterior) <= 0:\n            return []\n\n        # use (*[i][0], *[i][1]) formulation here instead of just *[i],\n        # because this way we convert numpy arrays to tuples of floats, which\n        # is required by isect_segments_include_segments\n        segments = [\n            (\n                (\n                    np.round(float(exterior[i][0]), decimals),\n                    np.round(float(exterior[i][1]), decimals)\n                ),\n                (\n                    np.round(float(exterior[(i + 1) % len(exterior)][0]),\n                             decimals),\n                    np.round(float(exterior[(i + 1) % len(exterior)][1]),\n                             decimals)\n                )\n            )\n            for i in range(len(exterior))\n        ]\n\n        # returns [(point, [(segment_p0, segment_p1), ..]), ...]\n        from imgaug.external.poly_point_isect_py2py3 import (\n            isect_segments_include_segments)\n\n        try:\n            intersections = isect_segments_include_segments(segments)\n        except Exception as exc:\n            # Exceptions in the segment intersection search can at least\n            # happen due to large float coords (the library uses\n            # floats as indices, which is bound to cause inaccuracies).\n            # Usually such exceptions should not appear, as too large\n            # coordinate values are already caught at the start of this\n            # function. For the case that there are more errors, this block\n            # will prevent a full crash.\n            ia.warn(\n                \"Encountered exception %s during polygon repair in segment \"\n                \"intersection computation. Will skip that step.\" % (\n                    str(exc),))\n            traceback.print_exc()\n            return [[] for _ in range(len(exterior))]\n\n        # estimate to which segment the found intersection points belong\n        segments_add_points = [[] for _ in range(len(segments))]\n        for point, associated_segments in intersections:\n            # the intersection point may be associated with multiple segments,\n            # but we only want to add it once, so pick the first segment\n            if one_point_per_intersection:\n                associated_segments = [associated_segments[0]]\n\n            for seg_inter_p0, seg_inter_p1 in associated_segments:\n                diffs = []\n                dists = []\n                for seg_p0, seg_p1 in segments:\n                    dist_p0p0 = np.linalg.norm(seg_p0 - np.array(seg_inter_p0))\n                    dist_p1p1 = np.linalg.norm(seg_p1 - np.array(seg_inter_p1))\n                    dist_p0p1 = np.linalg.norm(seg_p0 - np.array(seg_inter_p1))\n                    dist_p1p0 = np.linalg.norm(seg_p1 - np.array(seg_inter_p0))\n                    diff = min(dist_p0p0 + dist_p1p1, dist_p0p1 + dist_p1p0)\n                    diffs.append(diff)\n                    dists.append(np.linalg.norm(\n                        (seg_p0[0] - point[0], seg_p0[1] - point[1])\n                    ))\n\n                min_diff = np.min(diffs)\n                if min_diff < self.max_segment_difference:\n                    idx = int(np.argmin(diffs))\n                    segments_add_points[idx].append((point, dists[idx]))\n                else:\n                    ia.warn(\n                        \"Couldn't find fitting segment in \"\n                        \"_generate_intersection_points(). Ignoring \"\n                        \"intersection point.\")\n\n        # sort intersection points by their distance to point 0 in each segment\n        # (clockwise ordering, this does something only for segments with\n        # >=2 intersection points)\n        segment_add_points_sorted = []\n        for idx in range(len(segments_add_points)):\n            points = [t[0] for t in segments_add_points[idx]]\n            dists = [t[1] for t in segments_add_points[idx]]\n            if len(points) < 2:\n                segment_add_points_sorted.append(points)\n            else:\n                both = sorted(zip(points, dists), key=lambda t: t[1])\n                # keep points, drop distances\n                segment_add_points_sorted.append([a for a, _b in both])\n        return segment_add_points_sorted\n\n    def _oversample_intersection_points(self, exterior, segment_add_points):\n        # segment_add_points must be sorted\n\n        if self.oversampling is None or self.oversampling <= 0:\n            return segment_add_points\n\n        segment_add_points_sorted_overs = [\n            [] for _ in range(len(segment_add_points))]\n\n        n_points = len(exterior)\n        for i, last in enumerate(exterior):\n            for j, p_inter in enumerate(segment_add_points[i]):\n                direction = (p_inter[0] - last[0], p_inter[1] - last[1])\n\n                if j == 0:\n                    # previous point was non-intersection, place 1 new point\n                    oversample = [1.0 - self.oversampling]\n                else:\n                    # previous point was intersection, place 2 new points\n                    oversample = [self.oversampling, 1.0 - self.oversampling]\n\n                for dist in oversample:\n                    point_over = (last[0] + dist * direction[0],\n                                  last[1] + dist * direction[1])\n                    segment_add_points_sorted_overs[i].append(point_over)\n                segment_add_points_sorted_overs[i].append(p_inter)\n                last = p_inter\n\n                is_last_in_group = (j == len(segment_add_points[i]) - 1)\n                if is_last_in_group:\n                    # previous point was oversampled, next point is\n                    # non-intersection, place 1 new point between the two\n                    exterior_point = exterior[(i + 1) % len(exterior)]\n                    direction = (exterior_point[0] - last[0],\n                                 exterior_point[1] - last[1])\n                    segment_add_points_sorted_overs[i].append(\n                        (last[0] + self.oversampling * direction[0],\n                         last[1] + self.oversampling * direction[1])\n                    )\n                    last = segment_add_points_sorted_overs[i][-1]\n\n                n_points += len(segment_add_points_sorted_overs[i])\n                if n_points > self.oversample_up_to_n_points_max:\n                    return segment_add_points_sorted_overs\n\n        return segment_add_points_sorted_overs\n\n    @classmethod\n    def _insert_intersection_points(cls, exterior, segment_add_points):\n        # segment_add_points must be sorted\n\n        assert len(exterior) == len(segment_add_points), (\n            \"Expected one entry in 'segment_add_points' for every point in \"\n            \"the exterior. Got %d (segment_add_points) and %d (exterior) \"\n            \"entries instead.\" % (len(segment_add_points), len(exterior)))\n        exterior_interp = []\n        for i, point0 in enumerate(exterior):\n            point0 = exterior[i]\n            exterior_interp.append(point0)\n            for p_inter in segment_add_points[i]:\n                exterior_interp.append(p_inter)\n        return exterior_interp\n\n    def _fit_best_valid_polygon(self, points, random_state):\n        if len(points) < 2:\n            return None\n\n        def _compute_distance_point_to_line(point, line_start, line_end):\n            x_diff = line_end[0] - line_start[0]\n            y_diff = line_end[1] - line_start[1]\n            num = abs(\n                y_diff*point[0] - x_diff*point[1]\n                + line_end[0]*line_start[1] - line_end[1]*line_start[0]\n            )\n            den = np.sqrt(y_diff**2 + x_diff**2)\n            if den == 0:\n                return np.sqrt(\n                    (point[0] - line_start[0])**2\n                    + (point[1] - line_start[1])**2)\n            return num / den\n\n        poly = Polygon(points)\n        if poly.is_valid:\n            return sm.xrange(len(points))\n\n        hull = scipy.spatial.ConvexHull(points)\n        points_kept = list(hull.vertices)\n        points_left = [i for i in range(len(points)) if i not in points_kept]\n\n        iteration = 0\n        n_changes = 0\n        converged = False\n        while not converged:\n            candidates = []\n\n            # estimate distance metrics for points-segment pairs:\n            #  (1) distance (in vertices) between point and segment-start-point\n            #      in original input point chain\n            #  (2) euclidean distance between point and segment/line\n            # TODO this can be done more efficiently by caching the values and\n            #      only computing distances to segments that have changed in\n            #      the last iteration\n            # TODO these distances are not really the best metrics here.\n            #      Something like IoU between new and old (invalid) polygon\n            #      would be better, but can probably only be computed for\n            #      pairs of valid polygons. Maybe something based on pointwise\n            #      distances, where the points are sampled on the edges (not\n            #      edge vertices themselves). Maybe something based on drawing\n            #      the perimeter on images or based on distance maps.\n            point_kept_idx_to_pos = {\n                point_idx: i for i, point_idx in enumerate(points_kept)}\n\n            # generate all possible combinations from <points_kept> and\n            # <points_left>\n            combos = np.transpose([\n                np.tile(\n                    np.int32(points_left), len(np.int32(points_kept))\n                ),\n                np.repeat(\n                    np.int32(points_kept), len(np.int32(points_left))\n                )\n            ])\n            combos = np.concatenate(\n                (combos, np.zeros((combos.shape[0], 3), dtype=np.int32)),\n                axis=1)\n\n            # copy columns 0, 1 into 2, 3 so that 2 is always the lower value\n            mask = combos[:, 0] < combos[:, 1]\n            combos[:, 2:4] = combos[:, 0:2]\n            combos[mask, 2] = combos[mask, 1]\n            combos[mask, 3] = combos[mask, 0]\n\n            # distance (in indices) between each pair of <point_kept> and\n            # <point_left>\n            combos[:, 4] = np.minimum(\n                combos[:, 3] - combos[:, 2],\n                len(points) - combos[:, 3] + combos[:, 2]\n            )\n\n            # limit candidates\n            max_dist = self.fit_max_dist_other_iters\n            if iteration > 0:\n                max_dist = self.fit_max_dist_first_iter\n            candidate_rows = combos[combos[:, 4] <= max_dist]\n            do_limit = (\n                self.fit_n_candidates_before_sort_max is not None\n                and len(candidate_rows) > self.fit_n_candidates_before_sort_max)\n            if do_limit:\n                random_state.shuffle(candidate_rows)\n                candidate_rows = candidate_rows[\n                    0:self.fit_n_candidates_before_sort_max]\n\n            for row in candidate_rows:\n                point_left_idx = row[0]\n                point_kept_idx = row[1]\n                in_points_kept_pos = point_kept_idx_to_pos[point_kept_idx]\n                segment_start_idx = point_kept_idx\n                segment_end_idx = points_kept[\n                    (in_points_kept_pos+1) % len(points_kept)]\n                segment_start = points[segment_start_idx]\n                segment_end = points[segment_end_idx]\n                if iteration == 0:\n                    dist_eucl = 0\n                else:\n                    dist_eucl = _compute_distance_point_to_line(\n                        points[point_left_idx], segment_start, segment_end)\n                candidates.append(\n                    (point_left_idx, point_kept_idx, row[4], dist_eucl))\n\n            # Sort computed distances first by minimal vertex-distance (see\n            # above, metric 1) (ASC), then by euclidean distance\n            # (metric 2) (ASC).\n            candidate_ids = np.arange(len(candidates))\n            candidate_ids = sorted(\n                candidate_ids,\n                key=lambda idx: (candidates[idx][2], candidates[idx][3]))\n            if self.fit_n_changes_max is not None:\n                candidate_ids = candidate_ids[:self.fit_n_changes_max]\n\n            # Iterate over point-segment pairs in sorted order. For each such\n            # candidate: Add the point to the already collected points,\n            # create a polygon from that and check if the polygon is valid.\n            # If it is, add the point to the output list and recalculate\n            # distance metrics. If it isn't valid, proceed with the next\n            # candidate until no more candidates are left.\n            #\n            # small change: this now no longer breaks upon the first found\n            # point that leads to a valid polygon, but checks all candidates\n            # instead\n            is_valid = False\n            done = set()\n            for candidate_idx in candidate_ids:\n                point_left_idx = candidates[candidate_idx][0]\n                point_kept_idx = candidates[candidate_idx][1]\n                if (point_left_idx, point_kept_idx) not in done:\n                    in_points_kept_idx = [\n                        i\n                        for i, point_idx\n                        in enumerate(points_kept)\n                        if point_idx == point_kept_idx\n                    ][0]\n                    points_kept_hypothesis = points_kept[:]\n                    points_kept_hypothesis.insert(\n                        in_points_kept_idx+1,\n                        point_left_idx)\n                    poly_hypothesis = Polygon([\n                        points[idx] for idx in points_kept_hypothesis])\n                    if poly_hypothesis.is_valid:\n                        is_valid = True\n                        points_kept = points_kept_hypothesis\n                        points_left = [point_idx\n                                       for point_idx\n                                       in points_left\n                                       if point_idx != point_left_idx]\n                        n_changes += 1\n                        if n_changes >= self.fit_n_changes_max:\n                            return points_kept\n                    done.add((point_left_idx, point_kept_idx))\n                    done.add((point_kept_idx, point_left_idx))\n\n            # none of the left points could be used to create a valid polygon?\n            # (this automatically covers the case of no points being left)\n            if not is_valid and iteration > 0:\n                converged = True\n\n            iteration += 1\n            has_reached_iters_max = (\n                self.fit_n_iters_max is not None\n                and iteration > self.fit_n_iters_max)\n            if has_reached_iters_max:\n                break\n\n        return points_kept\n\n\n# TODO remove this? was previously only used by Polygon.clip_*(), but that\n#      doesn't use it anymore\nclass MultiPolygon(object):\n    \"\"\"\n    Class that represents several polygons.\n\n    Parameters\n    ----------\n    geoms : list of imgaug.augmentables.polys.Polygon\n        List of the polygons.\n\n    \"\"\"\n    def __init__(self, geoms):\n        \"\"\"Create a new MultiPolygon instance.\"\"\"\n        assert (\n            len(geoms) == 0\n            or all([isinstance(el, Polygon) for el in geoms])), (\n                \"Expected 'geoms' to a list of Polygon instances. \"\n                \"Got types %s.\" % (\", \".join([str(el) for el in geoms])))\n        self.geoms = geoms\n\n    @staticmethod\n    def from_shapely(geometry, label=None):\n        \"\"\"Create a MultiPolygon from a shapely object.\n\n        This also creates all necessary ``Polygon`` s contained in this\n        ``MultiPolygon``.\n\n        Parameters\n        ----------\n        geometry : shapely.geometry.MultiPolygon or shapely.geometry.Polygon or shapely.geometry.collection.GeometryCollection\n            The object to convert to a MultiPolygon.\n\n        label : None or str, optional\n            A label assigned to all Polygons within the MultiPolygon.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.MultiPolygon\n            The derived MultiPolygon.\n\n        \"\"\"\n        # load shapely lazily, which makes the dependency more optional\n        import shapely.geometry\n\n        if isinstance(geometry, shapely.geometry.MultiPolygon):\n            return MultiPolygon([\n                Polygon.from_shapely(poly, label=label)\n                for poly\n                in geometry.geoms])\n        if isinstance(geometry, shapely.geometry.Polygon):\n            return MultiPolygon([Polygon.from_shapely(geometry, label=label)])\n        if isinstance(geometry,\n                      shapely.geometry.collection.GeometryCollection):\n            assert all([\n                isinstance(poly, shapely.geometry.Polygon)\n                for poly\n                in geometry.geoms]), (\n                    \"Expected the geometry collection to only contain shapely \"\n                    \"polygons. Got types %s.\" % (\n                        \", \".join([str(type(v)) for v in geometry.geoms])))\n            return MultiPolygon([\n                Polygon.from_shapely(poly, label=label)\n                for poly\n                in geometry.geoms])\n\n        raise Exception(\n            \"Unknown datatype '%s'. Expected shapely.geometry.Polygon or \"\n            \"shapely.geometry.MultiPolygon or \"\n            \"shapely.geometry.collections.GeometryCollection.\" % (\n                type(geometry),))\n"
  },
  {
    "path": "imgaug/augmentables/segmaps.py",
    "content": "\"\"\"Classes dealing with segmentation maps.\n\nE.g. masks, semantic or instance segmentation maps.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport numpy as np\nimport six.moves as sm\n\nfrom .. import imgaug as ia\nfrom ..augmenters import blend as blendlib\nfrom .base import IAugmentable\n\n\n@ia.deprecated(alt_func=\"SegmentationMapsOnImage\",\n               comment=\"(Note the plural 'Maps' instead of old 'Map'.)\")\ndef SegmentationMapOnImage(*args, **kwargs):\n    \"\"\"Object representing a segmentation map associated with an image.\"\"\"\n    # pylint: disable=invalid-name\n    return SegmentationMapsOnImage(*args, **kwargs)\n\n\nclass SegmentationMapsOnImage(IAugmentable):\n    \"\"\"\n    Object representing a segmentation map associated with an image.\n\n    Attributes\n    ----------\n    DEFAULT_SEGMENT_COLORS : list of tuple of int\n        Standard RGB colors to use during drawing, ordered by class index.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Array representing the segmentation map(s). May have dtypes bool,\n        int or uint.\n\n    shape : tuple of int\n        Shape of the image on which the segmentation map(s) is/are placed.\n        **Not** the shape of the segmentation map(s) array, unless it is\n        identical to the image shape (note the likely difference between the\n        arrays in the number of channels).\n        This is expected to be ``(H, W)`` or ``(H, W, C)`` with ``C`` usually\n        being ``3``.\n        If there is no corresponding image, use ``(H_arr, W_arr)`` instead,\n        where ``H_arr`` is the height of the segmentation map(s) array\n        (analogous ``W_arr``).\n\n    nb_classes : None or int, optional\n        Deprecated.\n\n    \"\"\"\n\n    # TODO replace this by matplotlib colormap\n    DEFAULT_SEGMENT_COLORS = [\n        (0, 0, 0),  # black\n        (230, 25, 75),  # red\n        (60, 180, 75),  # green\n        (255, 225, 25),  # yellow\n        (0, 130, 200),  # blue\n        (245, 130, 48),  # orange\n        (145, 30, 180),  # purple\n        (70, 240, 240),  # cyan\n        (240, 50, 230),  # magenta\n        (210, 245, 60),  # lime\n        (250, 190, 190),  # pink\n        (0, 128, 128),  # teal\n        (230, 190, 255),  # lavender\n        (170, 110, 40),  # brown\n        (255, 250, 200),  # beige\n        (128, 0, 0),  # maroon\n        (170, 255, 195),  # mint\n        (128, 128, 0),  # olive\n        (255, 215, 180),  # coral\n        (0, 0, 128),  # navy\n        (128, 128, 128),  # grey\n        (255, 255, 255),  # white\n        # --\n        (115, 12, 37),  # dark red\n        (30, 90, 37),  # dark green\n        (127, 112, 12),  # dark yellow\n        (0, 65, 100),  # dark blue\n        (122, 65, 24),  # dark orange\n        (72, 15, 90),  # dark purple\n        (35, 120, 120),  # dark cyan\n        (120, 25, 115),  # dark magenta\n        (105, 122, 30),  # dark lime\n        (125, 95, 95),  # dark pink\n        (0, 64, 64),  # dark teal\n        (115, 95, 127),  # dark lavender\n        (85, 55, 20),  # dark brown\n        (127, 125, 100),  # dark beige\n        (64, 0, 0),  # dark maroon\n        (85, 127, 97),  # dark mint\n        (64, 64, 0),  # dark olive\n        (127, 107, 90),  # dark coral\n        (0, 0, 64),  # dark navy\n        (64, 64, 64),  # dark grey\n    ]\n\n    def __init__(self, arr, shape, nb_classes=None):\n        assert ia.is_np_array(arr), (\n            \"Expected to get numpy array, got %s.\" % (type(arr),))\n        assert arr.ndim in [2, 3], (\n            \"Expected segmentation map array to be 2- or \"\n            \"3-dimensional, got %d dimensions and shape %s.\" % (\n                arr.ndim, arr.shape))\n        assert isinstance(shape, tuple), (\n            \"Expected 'shape' to be a tuple denoting the shape of the image \"\n            \"on which the segmentation map is placed. Got type %s instead.\" % (\n                type(shape)))\n\n        if arr.dtype.kind == \"f\":\n            ia.warn_deprecated(\n                \"Got a float array as the segmentation map in \"\n                \"SegmentationMapsOnImage. That is deprecated. Please provide \"\n                \"instead a (H,W,[C]) array of dtype bool_, int or uint, where \"\n                \"C denotes the segmentation map index.\"\n            )\n\n            if arr.ndim == 2:\n                arr = (arr > 0.5)\n            else:  # arr.ndim == 3\n                arr = np.argmax(arr, axis=2).astype(np.int32)\n\n        if arr.dtype.name == \"bool\":\n            self._input_was = (arr.dtype, arr.ndim)\n            if arr.ndim == 2:\n                arr = arr[..., np.newaxis]\n        elif arr.dtype.kind in [\"i\", \"u\"]:\n            assert np.min(arr.flat[0:100]) >= 0, (\n                \"Expected segmentation map array to only contain values >=0, \"\n                \"got a minimum of %d.\" % (np.min(arr),))\n            if arr.dtype.kind == \"u\":\n                # allow only <=uint16 due to conversion to int32\n                assert arr.dtype.itemsize <= 2, (\n                    \"When using uint arrays as segmentation maps, only uint8 \"\n                    \"and uint16 are allowed. Got dtype %s.\" % (arr.dtype.name,)\n                )\n            elif arr.dtype.kind == \"i\":\n                # allow only <=uint16 due to conversion to int32\n                assert arr.dtype.itemsize <= 4, (\n                    \"When using int arrays as segmentation maps, only int8, \"\n                    \"int16 and int32 are allowed. Got dtype %s.\" % (\n                        arr.dtype.name,)\n                )\n\n            self._input_was = (arr.dtype, arr.ndim)\n            if arr.ndim == 2:\n                arr = arr[..., np.newaxis]\n        else:\n            raise Exception((\n                \"Input was expected to be an array of dtype 'bool', 'int' \"\n                \"or 'uint'. Got dtype '%s'.\") % (arr.dtype.name,))\n\n        if arr.dtype.name != \"int32\":\n            arr = arr.astype(np.int32)\n\n        self.arr = arr\n        self.shape = shape\n\n        if nb_classes is not None:\n            ia.warn_deprecated(\n                \"Providing nb_classes to SegmentationMapsOnImage is no longer \"\n                \"necessary and hence deprecated. The argument is ignored \"\n                \"and can be safely removed.\")\n\n    def get_arr(self):\n        \"\"\"Return the seg.map array, with original dtype and shape ndim.\n\n        Here, \"original\" denotes the dtype and number of shape dimensions that\n        was used when the :class:`SegmentationMapsOnImage` instance was\n        created, i.e. upon the call of\n        :func:`SegmentationMapsOnImage.__init__`.\n        Internally, this class may use a different dtype and shape to simplify\n        computations.\n\n        .. note::\n\n            The height and width may have changed compared to the original\n            input due to e.g. pooling operations.\n\n        Returns\n        -------\n        ndarray\n            Segmentation map array.\n            Same dtype and number of dimensions as was originally used when\n            the :class:`SegmentationMapsOnImage` instance was created.\n\n        \"\"\"\n        input_dtype, input_ndim = self._input_was\n        # The internally used int32 has a wider value range than any other\n        # input dtype, hence we can simply convert via astype() here.\n        arr_input = self.arr.astype(input_dtype)\n        if input_ndim == 2:\n            assert arr_input.shape[2] == 1, (\n                \"Originally got a (H,W) segmentation map. Internal array \"\n                \"should now have shape (H,W,1), but got %s. This might be \"\n                \"an internal error.\" % (arr_input.shape,))\n            return arr_input[:, :, 0]\n        return arr_input\n\n    @ia.deprecated(alt_func=\"SegmentationMapsOnImage.get_arr()\")\n    def get_arr_int(self, *args, **kwargs):\n        \"\"\"Return the seg.map array, with original dtype and shape ndim.\"\"\"\n        # pylint: disable=unused-argument\n        return self.get_arr()\n\n    def draw(self, size=None, colors=None):\n        \"\"\"\n        Render the segmentation map as an RGB image.\n\n        Parameters\n        ----------\n        size : None or float or iterable of int or iterable of float, optional\n            Size of the rendered RGB image as ``(height, width)``.\n            See :func:`~imgaug.imgaug.imresize_single_image` for details.\n            If set to ``None``, no resizing is performed and the size of the\n            segmentation map array is used.\n\n        colors : None or list of tuple of int, optional\n            Colors to use. One for each class to draw.\n            If ``None``, then default colors will be used.\n\n        Returns\n        -------\n        list of (H,W,3) ndarray\n            Rendered segmentation map (dtype is ``uint8``).\n            One per ``C`` in the original input array ``(H,W,C)``.\n\n        \"\"\"\n        def _handle_sizeval(sizeval, arr_axis_size):\n            if sizeval is None:\n                return arr_axis_size\n            if ia.is_single_float(sizeval):\n                return max(int(arr_axis_size * sizeval), 1)\n            if ia.is_single_integer(sizeval):\n                return sizeval\n            raise ValueError(\"Expected float or int, got %s.\" % (\n                type(sizeval),))\n\n        if size is None:\n            size = [size, size]\n        elif not ia.is_iterable(size):\n            size = [size, size]\n\n        height = _handle_sizeval(size[0], self.arr.shape[0])\n        width = _handle_sizeval(size[1], self.arr.shape[1])\n        image = np.zeros((height, width, 3), dtype=np.uint8)\n\n        return self.draw_on_image(\n            image,\n            alpha=1.0,\n            resize=\"segmentation_map\",\n            colors=colors,\n            draw_background=True\n        )\n\n    def draw_on_image(self, image, alpha=0.75, resize=\"segmentation_map\",\n                      colors=None, draw_background=False,\n                      background_class_id=0, background_threshold=None):\n        \"\"\"Draw the segmentation map as an overlay over an image.\n\n        Parameters\n        ----------\n        image : (H,W,3) ndarray\n            Image onto which to draw the segmentation map. Expected dtype\n            is ``uint8``.\n\n        alpha : float, optional\n            Alpha/opacity value to use for the mixing of image and\n            segmentation map. Larger values mean that the segmentation map\n            will be more visible and the image less visible.\n\n        resize : {'segmentation_map', 'image'}, optional\n            In case of size differences between the image and segmentation\n            map, either the image or the segmentation map can be resized.\n            This parameter controls which of the two will be resized to the\n            other's size.\n\n        colors : None or list of tuple of int, optional\n            Colors to use. One for each class to draw.\n            If ``None``, then default colors will be used.\n\n        draw_background : bool, optional\n            If ``True``, the background will be drawn like any other class.\n            If ``False``, the background will not be drawn, i.e. the respective\n            background pixels will be identical with the image's RGB color at\n            the corresponding spatial location and no color overlay will be\n            applied.\n\n        background_class_id : int, optional\n            Class id to interpret as the background class.\n            See `draw_background`.\n\n        background_threshold : None, optional\n            Deprecated.\n            This parameter is ignored.\n\n        Returns\n        -------\n        list of (H,W,3) ndarray\n            Rendered overlays as ``uint8`` arrays.\n            Always a **list** containing one RGB image per segmentation map\n            array channel.\n\n        \"\"\"\n        if background_threshold is not None:\n            ia.warn_deprecated(\n                \"The argument `background_threshold` is deprecated and \"\n                \"ignored. Please don't use it anymore.\")\n\n        assert image.ndim == 3, (\n            \"Expected to draw on 3-dimensional image, got image with %d \"\n            \"dimensions.\" % (image.ndim,))\n        assert image.shape[2] == 3, (\n            \"Expected to draw on RGB image, got image with %d channels \"\n            \"instead.\" % (image.shape[2],))\n        assert image.dtype.name == \"uint8\", (\n            \"Expected to get image with dtype uint8, got dtype %s.\" % (\n                image.dtype.name,))\n        assert 0 - 1e-8 <= alpha <= 1.0 + 1e-8, (\n            \"Expected 'alpha' to be in interval [0.0, 1.0], got %.4f.\" % (\n                alpha,))\n        assert resize in [\"segmentation_map\", \"image\"], (\n            \"Expected 'resize' to be \\\"segmentation_map\\\" or \\\"image\\\", got \"\n            \"%s.\" % (resize,))\n\n        colors = (\n            colors\n            if colors is not None\n            else SegmentationMapsOnImage.DEFAULT_SEGMENT_COLORS\n        )\n\n        if resize == \"image\":\n            image = ia.imresize_single_image(\n                image, self.arr.shape[0:2], interpolation=\"cubic\")\n\n        segmaps_drawn = []\n        arr_channelwise = np.dsplit(self.arr, self.arr.shape[2])\n        for arr in arr_channelwise:\n            arr = arr[:, :, 0]\n\n            nb_classes = 1 + np.max(arr)\n            segmap_drawn = np.zeros((arr.shape[0], arr.shape[1], 3),\n                                    dtype=np.uint8)\n            assert nb_classes <= len(colors), (\n                \"Can't draw all %d classes as it would exceed the maximum \"\n                \"number of %d available colors.\" % (nb_classes, len(colors),))\n\n            ids_in_map = np.unique(arr)\n            for c, color in zip(sm.xrange(nb_classes), colors):\n                if c in ids_in_map:\n                    class_mask = (arr == c)\n                    segmap_drawn[class_mask] = color\n\n            segmap_drawn = ia.imresize_single_image(\n                segmap_drawn, image.shape[0:2], interpolation=\"nearest\")\n\n            segmap_on_image = blendlib.blend_alpha(segmap_drawn, image, alpha)\n\n            if draw_background:\n                mix = segmap_on_image\n            else:\n                foreground_mask = ia.imresize_single_image(\n                    (arr != background_class_id),\n                    image.shape[0:2],\n                    interpolation=\"nearest\")\n                # without this, the merge below does nothing\n                foreground_mask = np.atleast_3d(foreground_mask)\n\n                mix = (\n                    (~foreground_mask) * image\n                    + foreground_mask * segmap_on_image\n                )\n            segmaps_drawn.append(mix)\n        return segmaps_drawn\n\n    def pad(self, top=0, right=0, bottom=0, left=0, mode=\"constant\", cval=0):\n        \"\"\"Pad the segmentation maps at their top/right/bottom/left side.\n\n        Parameters\n        ----------\n        top : int, optional\n            Amount of pixels to add at the top side of the segmentation map.\n            Must be ``0`` or greater.\n\n        right : int, optional\n            Amount of pixels to add at the right side of the segmentation map.\n            Must be ``0`` or greater.\n\n        bottom : int, optional\n            Amount of pixels to add at the bottom side of the segmentation map.\n            Must be ``0`` or greater.\n\n        left : int, optional\n            Amount of pixels to add at the left side of the segmentation map.\n            Must be ``0`` or greater.\n\n        mode : str, optional\n            Padding mode to use. See :func:`~imgaug.imgaug.pad` for details.\n\n        cval : number, optional\n            Value to use for padding if `mode` is ``constant``.\n            See :func:`~imgaug.imgaug.pad` for details.\n\n        Returns\n        -------\n        imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            Padded segmentation map with height ``H'=H+top+bottom`` and\n            width ``W'=W+left+right``.\n\n        \"\"\"\n        from ..augmenters import size as iasize\n        arr_padded = iasize.pad(self.arr, top=top, right=right, bottom=bottom,\n                                left=left, mode=mode, cval=cval)\n        return self.deepcopy(arr=arr_padded)\n\n    def pad_to_aspect_ratio(self, aspect_ratio, mode=\"constant\", cval=0,\n                            return_pad_amounts=False):\n        \"\"\"Pad the segmentation maps until they match a target aspect ratio.\n\n        Depending on which dimension is smaller (height or width), only the\n        corresponding sides (left/right or top/bottom) will be padded. In\n        each case, both of the sides will be padded equally.\n\n        Parameters\n        ----------\n        aspect_ratio : float\n            Target aspect ratio, given as width/height. E.g. ``2.0`` denotes\n            the image having twice as much width as height.\n\n        mode : str, optional\n            Padding mode to use.\n            See :func:`~imgaug.imgaug.pad` for details.\n\n        cval : number, optional\n            Value to use for padding if `mode` is ``constant``.\n            See :func:`~imgaug.imgaug.pad` for details.\n\n        return_pad_amounts : bool, optional\n            If ``False``, then only the padded instance will be returned.\n            If ``True``, a tuple with two entries will be returned, where\n            the first entry is the padded instance and the second entry are\n            the amounts by which each array side was padded. These amounts are\n            again a tuple of the form ``(top, right, bottom, left)``, with\n            each value being an integer.\n\n        Returns\n        -------\n        imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            Padded segmentation map as :class:`SegmentationMapsOnImage`\n            instance.\n\n        tuple of int\n            Amounts by which the instance's array was padded on each side,\n            given as a tuple ``(top, right, bottom, left)``.\n            This tuple is only returned if `return_pad_amounts` was set to\n            ``True``.\n\n        \"\"\"\n        from ..augmenters import size as iasize\n        arr_padded, pad_amounts = iasize.pad_to_aspect_ratio(\n            self.arr,\n            aspect_ratio=aspect_ratio,\n            mode=mode,\n            cval=cval,\n            return_pad_amounts=True)\n        segmap = self.deepcopy(arr=arr_padded)\n        if return_pad_amounts:\n            return segmap, pad_amounts\n        return segmap\n\n    @ia.deprecated(alt_func=\"SegmentationMapsOnImage.resize()\",\n                   comment=\"resize() has the exactly same interface.\")\n    def scale(self, *args, **kwargs):\n        \"\"\"Resize the seg.map(s) array given a target size and interpolation.\"\"\"\n        return self.resize(*args, **kwargs)\n\n    def resize(self, sizes, interpolation=\"nearest\"):\n        \"\"\"Resize the seg.map(s) array given a target size and interpolation.\n\n        Parameters\n        ----------\n        sizes : float or iterable of int or iterable of float\n            New size of the array in ``(height, width)``.\n            See :func:`~imgaug.imgaug.imresize_single_image` for details.\n\n        interpolation : None or str or int, optional\n            The interpolation to use during resize.\n            Nearest neighbour interpolation (``\"nearest\"``) is almost always\n            the best choice.\n            See :func:`~imgaug.imgaug.imresize_single_image` for details.\n\n        Returns\n        -------\n        imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            Resized segmentation map object.\n\n        \"\"\"\n        arr_resized = ia.imresize_single_image(self.arr, sizes,\n                                               interpolation=interpolation)\n        return self.deepcopy(arr_resized)\n\n    # TODO how best to handle changes to _input_was due to changed 'arr'?\n    def copy(self, arr=None, shape=None):\n        \"\"\"Create a shallow copy of the segmentation map object.\n\n        Parameters\n        ----------\n        arr : None or (H,W) ndarray or (H,W,C) ndarray, optional\n            Optionally the `arr` attribute to use for the new segmentation map\n            instance. Will be copied from the old instance if not provided.\n            See\n            :func:`~imgaug.augmentables.segmaps.SegmentationMapsOnImage.__init__`\n            for details.\n\n        shape : None or tuple of int, optional\n            Optionally the shape attribute to use for the the new segmentation\n            map instance. Will be copied from the old instance if not provided.\n            See\n            :func:`~imgaug.augmentables.segmaps.SegmentationMapsOnImage.__init__`\n            for details.\n\n        Returns\n        -------\n        imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            Shallow copy.\n\n        \"\"\"\n        # pylint: disable=protected-access\n        segmap = SegmentationMapsOnImage(\n            self.arr if arr is None else arr,\n            shape=self.shape if shape is None else shape)\n        segmap._input_was = self._input_was\n        return segmap\n\n    def deepcopy(self, arr=None, shape=None):\n        \"\"\"Create a deep copy of the segmentation map object.\n\n        Parameters\n        ----------\n        arr : None or (H,W) ndarray or (H,W,C) ndarray, optional\n            Optionally the `arr` attribute to use for the new segmentation map\n            instance. Will be copied from the old instance if not provided.\n            See\n            :func:`~imgaug.augmentables.segmaps.SegmentationMapsOnImage.__init__`\n            for details.\n\n        shape : None or tuple of int, optional\n            Optionally the shape attribute to use for the the new segmentation\n            map instance. Will be copied from the old instance if not provided.\n            See\n            :func:`~imgaug.augmentables.segmaps.SegmentationMapsOnImage.__init__`\n            for details.\n\n        Returns\n        -------\n        imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            Deep copy.\n\n        \"\"\"\n        # pylint: disable=protected-access\n        segmap = SegmentationMapsOnImage(\n            np.copy(self.arr if arr is None else arr),\n            shape=self.shape if shape is None else shape)\n        segmap._input_was = self._input_was\n        return segmap\n"
  },
  {
    "path": "imgaug/augmentables/utils.py",
    "content": "\"\"\"Utility functions used in augmentable modules.\"\"\"\nfrom __future__ import print_function, division, absolute_import\nimport copy as copylib\nimport numpy as np\nimport six.moves as sm\nimport imgaug as ia\n\n\n# TODO add tests\ndef copy_augmentables(augmentables):\n    if ia.is_np_array(augmentables):\n        return np.copy(augmentables)\n\n    result = []\n    for augmentable in augmentables:\n        if ia.is_np_array(augmentable):\n            result.append(np.copy(augmentable))\n        else:\n            result.append(augmentable.deepcopy())\n    return result\n\n\n# Added in 0.4.0.\ndef deepcopy_fast(obj):\n    if obj is None:\n        return None\n    if ia.is_single_number(obj) or ia.is_string(obj):\n        return obj\n    if isinstance(obj, list):\n        return [deepcopy_fast(el) for el in obj]\n    if isinstance(obj, tuple):\n        return tuple([deepcopy_fast(el) for el in obj])\n    if ia.is_np_array(obj):\n        return np.copy(obj)\n    if hasattr(obj, \"deepcopy\"):\n        return obj.deepcopy()\n    return copylib.deepcopy(obj)\n\n\n# Added in 0.5.0.\ndef _handle_on_image_shape(shape, obj):\n    if hasattr(shape, \"shape\"):\n        ia.warn_deprecated(\n            \"Providing a numpy array for parameter `shape` in \"\n            \"`%s` is deprecated. Please provide a shape tuple, \"\n            \"i.e. a tuple of integers denoting (height, width, [channels]). \"\n            \"Use something similar to `image.shape` to convert an array \"\n            \"to a shape tuple.\" % (\n                obj.__class__.__name__,\n            )\n        )\n        shape = normalize_shape(shape)\n    else:\n        assert isinstance(shape, tuple), (\n            \"Expected to get a tuple of integers or a numpy array \"\n            \"(deprecated) for parameter `shape` in `%s`. Got type %s.\" % (\n                obj.__class__.__name__, type(shape).__name__\n            )\n        )\n    return shape\n\n\ndef normalize_shape(shape):\n    \"\"\"Normalize a shape ``tuple`` or ``array`` to a shape ``tuple``.\n\n    Parameters\n    ----------\n    shape : tuple of int or ndarray\n        The input to normalize. May optionally be an array.\n\n    Returns\n    -------\n    tuple of int\n        Shape ``tuple``.\n\n    \"\"\"\n    if isinstance(shape, tuple):\n        return shape\n    assert ia.is_np_array(shape), (\n        \"Expected tuple of ints or array, got %s.\" % (type(shape),))\n    return shape.shape\n\n\ndef normalize_imglike_shape(shape):\n    \"\"\"Normalize a shape tuple or image-like ``array`` to a shape tuple.\n\n    Added in 0.5.0.\n\n    Parameters\n    ----------\n    shape : tuple of int or ndarray\n        The input to normalize. May optionally be an array. If it is an\n        array, it must be 2-dimensional (height, width) or 3-dimensional\n        (height, width, channels). Otherwise an error will be raised.\n\n    Returns\n    -------\n    tuple of int\n        Shape ``tuple``.\n\n    \"\"\"\n    if isinstance(shape, tuple):\n        return shape\n    assert ia.is_np_array(shape), (\n        \"Expected tuple of ints or array, got %s.\" % (type(shape),))\n    shape = shape.shape\n    assert len(shape) in [2, 3], (\n        \"Expected image array to be 2-dimensional or 3-dimensional, got \"\n        \"%d-dimensional input of shape %s.\" % (len(shape), shape)\n    )\n    return shape\n\n\ndef project_coords_(coords, from_shape, to_shape):\n    \"\"\"Project coordinates from one image shape to another in-place.\n\n    This performs a relative projection, e.g. a point at ``60%`` of the old\n    image width will be at ``60%`` of the new image width after projection.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    coords : ndarray or list of tuple of number\n        Coordinates to project.\n        Either an ``(N,2)`` numpy array or a ``list`` containing ``(x,y)``\n        coordinate ``tuple`` s.\n\n    from_shape : tuple of int or ndarray\n        Old image shape.\n\n    to_shape : tuple of int or ndarray\n        New image shape.\n\n    Returns\n    -------\n    ndarray\n        Projected coordinates as ``(N,2)`` ``float32`` numpy array.\n        This function may change the input data in-place.\n\n    \"\"\"\n    from_shape = normalize_shape(from_shape)\n    to_shape = normalize_shape(to_shape)\n    if from_shape[0:2] == to_shape[0:2]:\n        return coords\n\n    from_height, from_width = from_shape[0:2]\n    to_height, to_width = to_shape[0:2]\n\n    no_zeros_in_shapes = (\n        all([v > 0 for v in [from_height, from_width, to_height, to_width]]))\n    assert no_zeros_in_shapes, (\n        \"Expected from_shape and to_shape to not contain zeros. Got shapes \"\n        \"%s (from_shape) and %s (to_shape).\" % (from_shape, to_shape))\n\n    coords_proj = coords\n    if not ia.is_np_array(coords) or coords.dtype.kind != \"f\":\n        coords_proj = np.array(coords).astype(np.float32)\n\n    coords_proj[:, 0] = (coords_proj[:, 0] / from_width) * to_width\n    coords_proj[:, 1] = (coords_proj[:, 1] / from_height) * to_height\n\n    return coords_proj\n\n\ndef project_coords(coords, from_shape, to_shape):\n    \"\"\"Project coordinates from one image shape to another.\n\n    This performs a relative projection, e.g. a point at ``60%`` of the old\n    image width will be at ``60%`` of the new image width after projection.\n\n    Parameters\n    ----------\n    coords : ndarray or list of tuple of number\n        Coordinates to project.\n        Either an ``(N,2)`` numpy array or a ``list`` containing ``(x,y)``\n        coordinate ``tuple`` s.\n\n    from_shape : tuple of int or ndarray\n        Old image shape.\n\n    to_shape : tuple of int or ndarray\n        New image shape.\n\n    Returns\n    -------\n    ndarray\n        Projected coordinates as ``(N,2)`` ``float32`` numpy array.\n\n    \"\"\"\n    if ia.is_np_array(coords):\n        coords = np.copy(coords)\n\n    return project_coords_(coords, from_shape, to_shape)\n\n\n# TODO does that include point_b in the result?\ndef interpolate_point_pair(point_a, point_b, nb_steps):\n    \"\"\"Interpolate ``N`` points on a line segment.\n\n    Parameters\n    ----------\n    point_a : iterable of number\n        Start point of the line segment, given as ``(x,y)`` coordinates.\n\n    point_b : iterable of number\n        End point of the line segment, given as ``(x,y)`` coordinates.\n\n    nb_steps : int\n        Number of points to interpolate between `point_a` and `point_b`.\n\n    Returns\n    -------\n    list of tuple of number\n        The interpolated points.\n        Does not include `point_a`.\n\n    \"\"\"\n    if nb_steps < 1:\n        return []\n    x1, y1 = point_a\n    x2, y2 = point_b\n    vec = np.float32([x2 - x1, y2 - y1])\n    step_size = vec / (1 + nb_steps)\n    return [\n        (x1 + (i + 1) * step_size[0], y1 + (i + 1) * step_size[1])\n        for i\n        in sm.xrange(nb_steps)]\n\n\ndef interpolate_points(points, nb_steps, closed=True):\n    \"\"\"Interpolate ``N`` on each line segment in a line string.\n\n    Parameters\n    ----------\n    points : iterable of iterable of number\n        Points on the line segments, each one given as ``(x,y)`` coordinates.\n        They are assumed to form one connected line string.\n\n    nb_steps : int\n        Number of points to interpolate on each individual line string.\n\n    closed : bool, optional\n        If ``True`` the output contains the last point in `points`.\n        Otherwise it does not (but it will contain the interpolated points\n        leading to the last point).\n\n    Returns\n    -------\n    list of tuple of number\n        Coordinates of `points`, with additional `nb_steps` new points\n        interpolated between each point pair. If `closed` is ``False``,\n        the last point in `points` is not returned.\n\n    \"\"\"\n    if len(points) <= 1:\n        return points\n    if closed:\n        points = list(points) + [points[0]]\n    points_interp = []\n    for point_a, point_b in zip(points[:-1], points[1:]):\n        points_interp.extend(\n            [point_a]\n            + interpolate_point_pair(point_a, point_b, nb_steps)\n        )\n    if not closed:\n        points_interp.append(points[-1])\n    # close does not have to be reverted here, as last point is not included\n    # in the extend()\n    return points_interp\n\n\ndef interpolate_points_by_max_distance(points, max_distance, closed=True):\n    \"\"\"Interpolate points with distance ``d`` on a line string.\n\n    For a list of points ``A, B, C``, if the distance between ``A`` and ``B``\n    is greater than `max_distance`, it will place at least one point between\n    ``A`` and ``B`` at ``A + max_distance * (B - A)``. Multiple points can\n    be placed between the two points if they are far enough away from each\n    other. The process is repeated for ``B`` and ``C``.\n\n    Parameters\n    ----------\n    points : iterable of iterable of number\n        Points on the line segments, each one given as ``(x,y)`` coordinates.\n        They are assumed to form one connected line string.\n\n    max_distance : number\n        Maximum distance between any two points in the result.\n\n    closed : bool, optional\n        If ``True`` the output contains the last point in `points`.\n        Otherwise it does not (but it will contain the interpolated points\n        leading to the last point).\n\n    Returns\n    -------\n    list of tuple of number\n        Coordinates of `points`, with interpolated points added to the\n        iterable. If `closed` is ``False``, the last point in `points` is not\n        returned.\n\n    \"\"\"\n    assert max_distance > 0, (\n        \"Expected max_distance to have a value >0, got %.8f.\" % (\n            max_distance,))\n    if len(points) <= 1:\n        return points\n    if closed:\n        points = list(points) + [points[0]]\n    points_interp = []\n    for point_a, point_b in zip(points[:-1], points[1:]):\n        dist = np.sqrt(\n            (point_a[0] - point_b[0]) ** 2\n            + (point_a[1] - point_b[1]) ** 2)\n        nb_steps = int((dist / max_distance) - 1)\n        points_interp.extend(\n            [point_a]\n            + interpolate_point_pair(point_a, point_b, nb_steps))\n    if not closed:\n        points_interp.append(points[-1])\n    return points_interp\n\n\ndef convert_cbaois_to_kpsois(cbaois):\n    \"\"\"Convert coordinate-based augmentables to KeypointsOnImage instances.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    cbaois : list of imgaug.augmentables.bbs.BoundingBoxesOnImage or list of imgaug.augmentables.bbs.PolygonsOnImage or list of imgaug.augmentables.bbs.LineStringsOnImage or imgaug.augmentables.bbs.BoundingBoxesOnImage or imgaug.augmentables.bbs.PolygonsOnImage or imgaug.augmentables.bbs.LineStringsOnImage\n        Coordinate-based augmentables to convert, e.g. bounding boxes.\n\n    Returns\n    -------\n    list of imgaug.augmentables.kps.KeypointsOnImage or imgaug.augmentables.kps.KeypointsOnImage\n        ``KeypointsOnImage`` instances containing the coordinates of input\n        `cbaois`.\n\n    \"\"\"\n    if not isinstance(cbaois, list):\n        return cbaois.to_keypoints_on_image()\n\n    kpsois = []\n    for cbaoi in cbaois:\n        kpsois.append(cbaoi.to_keypoints_on_image())\n    return kpsois\n\n\ndef invert_convert_cbaois_to_kpsois_(cbaois, kpsois):\n    \"\"\"Invert the output of :func:`convert_to_cbaois_to_kpsois` in-place.\n\n    This function writes in-place into `cbaois`.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    cbaois : list of imgaug.augmentables.bbs.BoundingBoxesOnImage or list of imgaug.augmentables.bbs.PolygonsOnImage or list of imgaug.augmentables.bbs.LineStringsOnImage or imgaug.augmentables.bbs.BoundingBoxesOnImage or imgaug.augmentables.bbs.PolygonsOnImage or imgaug.augmentables.bbs.LineStringsOnImage\n        Original coordinate-based augmentables before they were converted,\n        i.e. the same inputs as provided to :func:`convert_to_kpsois`.\n\n    kpsois : list of imgaug.augmentables.kps.KeypointsOnImages or imgaug.augmentables.kps.KeypointsOnImages\n        Keypoints to convert back to the types of `cbaois`, i.e. the outputs\n        of :func:`convert_cbaois_to_kpsois`.\n\n    Returns\n    -------\n    list of imgaug.augmentables.bbs.BoundingBoxesOnImage or list of imgaug.augmentables.bbs.PolygonsOnImage or list of imgaug.augmentables.bbs.LineStringsOnImage or imgaug.augmentables.bbs.BoundingBoxesOnImage or imgaug.augmentables.bbs.PolygonsOnImage or imgaug.augmentables.bbs.LineStringsOnImage\n        Parameter `cbaois`, with updated coordinates and shapes derived from\n        `kpsois`. `cbaois` is modified in-place.\n\n    \"\"\"\n    if not isinstance(cbaois, list):\n        assert not isinstance(kpsois, list), (\n            \"Expected non-list for `kpsois` when `cbaois` is non-list. \"\n            \"Got type %s.\" % (type(kpsois.__name__)),)\n        return cbaois.invert_to_keypoints_on_image_(kpsois)\n\n    result = []\n    for cbaoi, kpsoi in zip(cbaois, kpsois):\n        cbaoi_recovered = cbaoi.invert_to_keypoints_on_image_(kpsoi)\n        result.append(cbaoi_recovered)\n\n    return result\n\n\n# Added in 0.4.0.\ndef _remove_out_of_image_fraction_(cbaoi, fraction):\n    cbaoi.items = [\n        item for item in cbaoi.items\n        if item.compute_out_of_image_fraction(cbaoi.shape) < fraction]\n    return cbaoi\n\n\n# Added in 0.4.0.\ndef _normalize_shift_args(x, y, top=None, right=None, bottom=None, left=None):\n    \"\"\"Normalize ``shift()`` arguments to x, y and handle deprecated args.\"\"\"\n    if any([v is not None for v in [top, right, bottom, left]]):\n        ia.warn_deprecated(\n            \"Got one of the arguments `top` (%s), `right` (%s), \"\n            \"`bottom` (%s), `left` (%s) in a shift() call. \"\n            \"These are deprecated. Use `x` and `y` instead.\" % (\n                top, right, bottom, left),\n            stacklevel=3)\n        top = top if top is not None else 0\n        right = right if right is not None else 0\n        bottom = bottom if bottom is not None else 0\n        left = left if left is not None else 0\n        x = x + left - right\n        y = y + top - bottom\n    return x, y\n"
  },
  {
    "path": "imgaug/augmenters/__init__.py",
    "content": "\"\"\"Combination of all augmenters, related classes and related functions.\"\"\"\n# pylint: disable=unused-import\nfrom __future__ import absolute_import\nfrom imgaug.augmenters.base import *\nfrom imgaug.augmenters.arithmetic import *\nfrom imgaug.augmenters.artistic import *\nfrom imgaug.augmenters.blend import *\nfrom imgaug.augmenters.blur import *\nfrom imgaug.augmenters.collections import *\nfrom imgaug.augmenters.color import *\nfrom imgaug.augmenters.contrast import *\nfrom imgaug.augmenters.convolutional import *\nfrom imgaug.augmenters.debug import *\nfrom imgaug.augmenters.edges import *\nfrom imgaug.augmenters.flip import *\nfrom imgaug.augmenters.geometric import *\nimport imgaug.augmenters.imgcorruptlike  # use as iaa.imgcorrupt.<Augmenter>\nfrom imgaug.augmenters.meta import *\nimport imgaug.augmenters.pillike  # use via: iaa.pillike.*\nfrom imgaug.augmenters.pooling import *\nfrom imgaug.augmenters.segmentation import *\nfrom imgaug.augmenters.size import *\nfrom imgaug.augmenters.weather import *\n"
  },
  {
    "path": "imgaug/augmenters/arithmetic.py",
    "content": "\"\"\"\nAugmenters that perform simple arithmetic changes.\n\nList of augmenters:\n\n    * :class:`Add`\n    * :class:`AddElementwise`\n    * :class:`AdditiveGaussianNoise`\n    * :class:`AdditiveLaplaceNoise`\n    * :class:`AdditivePoissonNoise`\n    * :class:`Multiply`\n    * :class:`MultiplyElementwise`\n    * :class:`Cutout`\n    * :class:`Dropout`\n    * :class:`CoarseDropout`\n    * :class:`Dropout2d`\n    * :class:`TotalDropout`\n    * :class:`ReplaceElementwise`\n    * :class:`ImpulseNoise`\n    * :class:`SaltAndPepper`\n    * :class:`CoarseSaltAndPepper`\n    * :class:`Salt`\n    * :class:`CoarseSalt`\n    * :class:`Pepper`\n    * :class:`CoarsePepper`\n    * :class:`Invert`\n    * :class:`Solarize`\n    * :class:`ContrastNormalization`\n    * :class:`JpegCompression`\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport tempfile\n\nimport imageio\nimport numpy as np\nimport cv2\n\nimport imgaug as ia\nfrom . import meta\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\nfrom .. import random as iarandom\nfrom ..imgaug import _normalize_cv2_input_arr_\n\n\n# fill modes for apply_cutout_() and Cutout augmenter\n# contains roughly:\n#     'str fill_mode_name => (str module_name, str function_name)'\n# We could also assign the function to each fill mode name instead of its\n# name, but that has the disadvantage that these aren't defined yet (they\n# are defined further below) and that during unittesting they would be harder\n# to mock. (mock.patch() seems to not automatically replace functions\n# assigned in that way.)\n_CUTOUT_FILL_MODES = {\n    \"constant\": (\"imgaug.augmenters.arithmetic\", \"_fill_rectangle_constant_\"),\n    \"gaussian\": (\"imgaug.augmenters.arithmetic\", \"_fill_rectangle_gaussian_\")\n}\n\n\ndef add_scalar(image, value):\n    \"\"\"Add a scalar value (or one scalar per channel) to an image.\n\n    This method ensures that ``uint8`` does not overflow during the addition.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: limited; tested (1)\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: limited; tested (1)\n        * ``int16``: limited; tested (1)\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: limited; tested (1)\n        * ``float32``: limited; tested (1)\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: limited; tested (1)\n\n        - (1) Non-uint8 dtypes can overflow. For floats, this can result\n              in +/-inf.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n        If `value` contains more than one value, the shape of the image is\n        expected to be ``(H,W,C)``.\n\n    value : number or ndarray\n        The value to add to the image. Either a single value or an array\n        containing exactly one component per channel, i.e. ``C`` components.\n\n    Returns\n    -------\n    ndarray\n        Image with value added to it.\n\n    \"\"\"\n    return add_scalar_(np.copy(image), value)\n\n\ndef add_scalar_(image, value):\n    \"\"\"Add in-place a scalar value (or one scalar per channel) to an image.\n\n    This method ensures that ``uint8`` does not overflow during the addition.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: limited; tested (1)\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: limited; tested (1)\n        * ``int16``: limited; tested (1)\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: limited; tested (1)\n        * ``float32``: limited; tested (1)\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: limited; tested (1)\n\n        - (1) Non-uint8 dtypes can overflow. For floats, this can result\n              in +/-inf.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n        If `value` contains more than one value, the shape of the image is\n        expected to be ``(H,W,C)``.\n        The image might be changed in-place.\n\n    value : number or ndarray\n        The value to add to the image. Either a single value or an array\n        containing exactly one component per channel, i.e. ``C`` components.\n\n    Returns\n    -------\n    ndarray\n        Image with value added to it.\n        This might be the input `image`, changed in-place.\n\n    \"\"\"\n    if image.size == 0:\n        return np.copy(image)\n\n    iadt.gate_dtypes_strs(\n        {image.dtype},\n        allowed=\"bool uint8 uint16 int8 int16 float16 float32\",\n        disallowed=\"uint32 uint64 int32 int64 float64 float128\",\n        augmenter=None)\n\n    if image.dtype == iadt._UINT8_DTYPE:\n        return _add_scalar_to_uint8_(image, value)\n    return _add_scalar_to_non_uint8(image, value)\n\n\ndef _add_scalar_to_uint8_(image, value):\n    if ia.is_single_number(value):\n        is_single_value = True\n        value = round(value)\n    elif ia.is_np_scalar(value) or ia.is_np_array(value):\n        is_single_value = (value.size == 1)\n        value = np.round(value) if value.dtype.kind == \"f\" else value\n    else:\n        is_single_value = False\n    is_channelwise = not is_single_value\n\n    if image.ndim == 2 and is_single_value:\n        return cv2.add(image, value, dst=image, dtype=cv2.CV_8U)\n\n    input_shape = image.shape\n    image = image.ravel()\n    values = np.array(value)\n    if not is_channelwise:\n        values = np.broadcast_to(values, image.shape)\n    else:\n        values = np.tile(values, image.size // len(values))\n\n    image_add = cv2.add(image, values, dst=image, dtype=cv2.CV_8U)\n\n    return image_add.reshape(input_shape)\n\n\ndef _add_scalar_to_non_uint8(image, value):\n    input_dtype = image.dtype\n\n    is_single_value = (\n        ia.is_single_number(value)\n        or ia.is_np_scalar(value)\n        or (ia.is_np_array(value) and value.size == 1))\n    is_channelwise = not is_single_value\n    nb_channels = 1 if image.ndim == 2 else image.shape[-1]\n\n    shape = (1, 1, nb_channels if is_channelwise else 1)\n    value = np.array(value).reshape(shape)\n\n    # We limit here the value range of the value parameter to the\n    # bytes in the image's dtype. This prevents overflow problems\n    # and makes it less likely that the image has to be up-casted,\n    # which again improves performance and saves memory. Note that\n    # this also enables more dtypes for image inputs.\n    # The downside is that the mul parameter is limited in its\n    # value range.\n    #\n    # We need 2* the itemsize of the image here to allow to shift\n    # the image's max value to the lowest possible value, e.g. for\n    # uint8 it must allow for -255 to 255.\n    itemsize = image.dtype.itemsize * 2\n    dtype_target = np.dtype(\"%s%d\" % (value.dtype.kind, itemsize))\n    value = iadt.clip_to_dtype_value_range_(\n        value, dtype_target, validate=True)\n\n    # Itemsize is currently reduced from 2 to 1 due to clip no\n    # longer supporting int64, which can cause issues with int32\n    # samples (32*2 = 64bit).\n    # TODO limit value ranges of samples to int16/uint16 for\n    #      security\n    image, value = iadt.promote_array_dtypes_(\n        [image, value],\n        dtypes=[image.dtype, dtype_target],\n        increase_itemsize_factor=1)\n    image = np.add(image, value, out=image, casting=\"no\")\n\n    return iadt.restore_dtypes_(image, input_dtype)\n\n\ndef add_elementwise(image, values):\n    \"\"\"Add an array of values to an image.\n\n    This method ensures that ``uint8`` does not overflow during the addition.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: limited; tested (1)\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: limited; tested (1)\n        * ``int16``: limited; tested (1)\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: limited; tested (1)\n        * ``float32``: limited; tested (1)\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: limited; tested (1)\n\n        - (1) Non-uint8 dtypes can overflow. For floats, this can result\n              in +/-inf.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n\n    values : ndarray\n        The values to add to the image. Expected to have the same height\n        and width as `image` and either no channels or one channel or\n        the same number of channels as `image`.\n        This array is expected to have dtype ``int8``, ``int16``, ``int32``,\n        ``uint8``, ``uint16``, ``float32``, ``float64``. Other dtypes may\n        or may not work.\n        For ``uint8`` inputs, only `value` arrays with values in the interval\n        ``[-1000, 1000]`` are supported. Values beyond that interval may\n        result in an output array of zeros (no error is raised due to\n        performance reasons).\n\n    Returns\n    -------\n    ndarray\n        Image with values added to it.\n\n    \"\"\"\n    iadt.gate_dtypes_strs(\n        {image.dtype},\n        allowed=\"bool uint8 uint16 int8 int16 float16 float32\",\n        disallowed=\"uint32 uint64 int32 int64 float64 float128\",\n        augmenter=None)\n\n    if image.dtype == iadt._UINT8_DTYPE:\n        vdt = values.dtype\n        valid_value_dtypes_cv2 = iadt._convert_dtype_strs_to_types(\n            \"int8 int16 int32 uint8 uint16 float32 float64\"\n        )\n\n        ishape = image.shape\n\n        is_image_valid_shape_cv2 = (\n            (\n                len(ishape) == 2\n                or (len(ishape) == 3 and ishape[-1] <= 512)\n            )\n            and 0 not in ishape\n        )\n\n        use_cv2 = (\n            is_image_valid_shape_cv2\n            and vdt in valid_value_dtypes_cv2\n        )\n        if use_cv2:\n            return _add_elementwise_cv2_to_uint8(image, values)\n        return _add_elementwise_np_to_uint8(image, values)\n    return _add_elementwise_np_to_non_uint8(image, values)\n\n\ndef _add_elementwise_cv2_to_uint8(image, values):\n    ind, vnd = image.ndim, values.ndim\n    valid_vnd = [ind] if ind == 2 else [ind-1, ind]\n    assert vnd in valid_vnd, (\n        \"Expected values with any of %s dimensions, \"\n        \"got %d dimensions (shape %s vs. image shape %s).\" % (\n            valid_vnd, vnd, values.shape, image.shape\n        )\n    )\n\n    if vnd == ind - 1:\n        values = values[:, :, np.newaxis]\n    if values.shape[-1] == 1:\n        values = np.broadcast_to(values, image.shape)\n    # add does not seem to require normalization\n    result = cv2.add(image, values, dtype=cv2.CV_8U)\n    if result.ndim == 2 and ind == 3:\n        return result[:, :, np.newaxis]\n    return result\n\n\ndef _add_elementwise_np_to_uint8(image, values):\n    # This special uint8 block is around 60-100% faster than the\n    # corresponding non-uint8 function further below (more speedup\n    # for smaller images).\n    #\n    # Also tested to instead compute min/max of image and value\n    # and then only convert image/value dtype if actually\n    # necessary, but that was like 20-30% slower, even for 224x224\n    # images.\n    #\n    if values.dtype.kind == \"f\":\n        values = np.round(values)\n\n    image = image.astype(np.int16)\n    values = np.clip(values, -255, 255).astype(np.int16)\n\n    image_aug = image + values\n    image_aug = np.clip(image_aug, 0, 255).astype(np.uint8)\n\n    return image_aug\n\n\ndef _add_elementwise_np_to_non_uint8(image, values):\n    # We limit here the value range of the value parameter to the\n    # bytes in the image's dtype. This prevents overflow problems\n    # and makes it less likely that the image has to be up-casted,\n    # which again improves performance and saves memory. Note that\n    # this also enables more dtypes for image inputs.\n    # The downside is that the mul parameter is limited in its\n    # value range.\n    #\n    # We need 2* the itemsize of the image here to allow to shift\n    # the image's max value to the lowest possible value, e.g. for\n    # uint8 it must allow for -255 to 255.\n    if image.dtype.kind != \"f\" and values.dtype.kind == \"f\":\n        values = np.round(values)\n\n    input_shape = image.shape\n    input_dtype = image.dtype\n\n    if image.ndim == 2:\n        image = image[..., np.newaxis]\n    if values.ndim == 2:\n        values = values[..., np.newaxis]\n    nb_channels = image.shape[-1]\n\n    itemsize = image.dtype.itemsize * 2\n    dtype_target = np.dtype(\"%s%d\" % (values.dtype.kind, itemsize))\n    values = iadt.clip_to_dtype_value_range_(values, dtype_target,\n                                             validate=100)\n\n    if values.shape[2] == 1:\n        values = np.tile(values, (1, 1, nb_channels))\n\n    # Decreased itemsize from 2 to 1 here, see explanation in Add.\n    image, values = iadt.promote_array_dtypes_(\n        [image, values],\n        dtypes=[image.dtype, dtype_target],\n        increase_itemsize_factor=1)\n    image = np.add(image, values, out=image, casting=\"no\")\n    image = iadt.restore_dtypes_(image, input_dtype)\n\n    if len(input_shape) == 2:\n        return image[..., 0]\n    return image\n\n\ndef multiply_scalar(image, multiplier):\n    \"\"\"Multiply an image by a single scalar or one scalar per channel.\n\n    This method ensures that ``uint8`` does not overflow during the\n    multiplication.\n\n    note::\n\n        Tests were only conducted for rather small multipliers, around\n        ``-10.0`` to ``+10.0``.\n\n        In general, the multipliers sampled from `multiplier` must be in a\n        value range that corresponds to the input image's dtype. E.g. if the\n        input image has dtype ``uint16`` and the samples generated from\n        `multiplier` are ``float64``, this function will still force all\n        samples to be within the value range of ``float16``, as it has the\n        same number of bytes (two) as ``uint16``. This is done to make\n        overflows less likely to occur.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: limited; tested (1)\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: limited; tested (1)\n        * ``int16``: limited; tested (1)\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: limited; tested (1)\n        * ``float32``: limited; tested (1)\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: limited; tested (1)\n\n        - (1) Non-uint8 dtypes can overflow. For floats, this can result in\n              +/-inf.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n        If `value` contains more than one value, the shape of the image is\n        expected to be ``(H,W,C)``.\n\n    multiplier : number or ndarray\n        The multiplier to use. Either a single value or an array\n        containing exactly one component per channel, i.e. ``C`` components.\n\n    Returns\n    -------\n    ndarray\n        Image, multiplied by `multiplier`.\n\n    \"\"\"\n    return multiply_scalar_(np.copy(image), multiplier)\n\n\ndef multiply_scalar_(image, multiplier):\n    \"\"\"Multiply in-place an image by a single scalar or one scalar per channel.\n\n    This method ensures that ``uint8`` does not overflow during the\n    multiplication.\n\n    note::\n\n        Tests were only conducted for rather small multipliers, around\n        ``-10.0`` to ``+10.0``.\n\n        In general, the multipliers sampled from `multiplier` must be in a\n        value range that corresponds to the input image's dtype. E.g. if the\n        input image has dtype ``uint16`` and the samples generated from\n        `multiplier` are ``float64``, this function will still force all\n        samples to be within the value range of ``float16``, as it has the\n        same number of bytes (two) as ``uint16``. This is done to make\n        overflows less likely to occur.\n\n    Added in 0.5.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: limited; tested (1)\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: limited; tested (1)\n        * ``int16``: limited; tested (1)\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: limited; tested (1)\n        * ``float32``: limited; tested (1)\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: limited; tested (1)\n\n        - (1) Non-uint8 dtypes can overflow. For floats, this can result in\n              +/-inf.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n        If `value` contains more than one value, the shape of the image is\n        expected to be ``(H,W,C)``.\n        May be changed in-place.\n\n    multiplier : number or ndarray\n        The multiplier to use. Either a single value or an array\n        containing exactly one component per channel, i.e. ``C`` components.\n\n    Returns\n    -------\n    ndarray\n        Image, multiplied by `multiplier`.\n        Might be the same image instance as was provided in `image`.\n\n    \"\"\"\n    size = image.size\n    if size == 0:\n        return image\n\n    iadt.gate_dtypes_strs(\n        {image.dtype},\n        allowed=\"bool uint8 uint16 int8 int16 float16 float32\",\n        disallowed=\"uint32 uint64 int32 int64 float64 float128\",\n        augmenter=None)\n\n    if image.dtype == iadt._UINT8_DTYPE:\n        if size >= 224*224*3:\n            return _multiply_scalar_to_uint8_lut_(image, multiplier)\n        return _multiply_scalar_to_uint8_cv2_mul_(image, multiplier)\n    return _multiply_scalar_to_non_uint8(image, multiplier)\n\n\n# TODO add a c++/cython method here to compute the LUT tables\n# Added in 0.5.0.\ndef _multiply_scalar_to_uint8_lut_(image, multiplier):\n    is_single_value = (\n        ia.is_single_number(multiplier)\n        or ia.is_np_scalar(multiplier)\n        or (ia.is_np_array(multiplier) and multiplier.size == 1))\n    is_channelwise = not is_single_value\n    nb_channels = 1 if image.ndim == 2 else image.shape[-1]\n\n    multiplier = np.float32(multiplier)\n    value_range = np.arange(0, 256, dtype=np.float32)\n\n    if is_channelwise:\n        assert multiplier.ndim == 1, (\n            \"Expected `multiplier` to be 1-dimensional, got %d-dimensional \"\n            \"data with shape %s.\" % (multiplier.ndim, multiplier.shape))\n        assert image.ndim == 3, (\n            \"Expected `image` to be 3-dimensional when multiplying by one \"\n            \"value per channel, got %d-dimensional data with shape %s.\" % (\n                image.ndim, image.shape))\n        assert image.shape[-1] == multiplier.size, (\n            \"Expected number of channels in `image` and number of components \"\n            \"in `multiplier` to be identical. Got %d vs. %d.\" % (\n                image.shape[-1], multiplier.size))\n\n        value_range = np.broadcast_to(value_range[:, np.newaxis],\n                                      (256, nb_channels))\n        value_range = value_range * multiplier[np.newaxis, :]\n    else:\n        value_range = value_range * multiplier\n    value_range = np.clip(value_range, 0, 255).astype(image.dtype)\n    return ia.apply_lut_(image, value_range)\n\n\n# Added in 0.5.0.\ndef _multiply_scalar_to_uint8_cv2_mul_(image, multiplier):\n    # multiplier must already be an array_like\n    if multiplier.size > 1:\n        multiplier = multiplier[np.newaxis, np.newaxis, :]\n        multiplier = np.broadcast_to(multiplier, image.shape)\n    else:\n        multiplier = np.full(image.shape, multiplier, dtype=np.float32)\n\n    image = _normalize_cv2_input_arr_(image)\n    result = cv2.multiply(\n        image,\n        multiplier,\n        dtype=cv2.CV_8U,\n        dst=image\n    )\n\n    return result\n\n\ndef _multiply_scalar_to_non_uint8(image, multiplier):\n    # TODO estimate via image min/max values whether a resolution\n    #      increase is necessary\n    input_dtype = image.dtype\n\n    is_single_value = (\n        ia.is_single_number(multiplier)\n        or ia.is_np_scalar(multiplier)\n        or (ia.is_np_array(multiplier) and multiplier.size == 1))\n    is_channelwise = not is_single_value\n    nb_channels = 1 if image.ndim == 2 else image.shape[-1]\n\n    shape = (1, 1, nb_channels if is_channelwise else 1)\n    multiplier = np.array(multiplier).reshape(shape)\n\n    # deactivated itemsize increase due to clip causing problems\n    # with int64, see Add\n    # mul_min = np.min(mul)\n    # mul_max = np.max(mul)\n    # is_not_increasing_value_range = (\n    #         (-1 <= mul_min <= 1)\n    #         and (-1 <= mul_max <= 1))\n\n    # We limit here the value range of the mul parameter to the\n    # bytes in the image's dtype. This prevents overflow problems\n    # and makes it less likely that the image has to be up-casted,\n    # which again improves performance and saves memory. Note that\n    # this also enables more dtypes for image inputs.\n    # The downside is that the mul parameter is limited in its\n    # value range.\n    itemsize = max(\n        image.dtype.itemsize,\n        2 if multiplier.dtype.kind == \"f\" else 1\n    )  # float min itemsize is 2 not 1\n    dtype_target = np.dtype(\"%s%d\" % (multiplier.dtype.kind, itemsize))\n    multiplier = iadt.clip_to_dtype_value_range_(\n        multiplier, dtype_target, validate=True)\n\n    image, multiplier = iadt.promote_array_dtypes_(\n        [image, multiplier],\n        dtypes=[image.dtype, dtype_target],\n        # increase_itemsize_factor=(\n        #     1 if is_not_increasing_value_range else 2)\n        increase_itemsize_factor=1\n    )\n    image = np.multiply(image, multiplier, out=image, casting=\"no\")\n\n    return iadt.restore_dtypes_(image, input_dtype)\n\n\ndef multiply_elementwise(image, multipliers):\n    \"\"\"Multiply an image with an array of values.\n\n    This method ensures that ``uint8`` does not overflow during the addition.\n\n    note::\n\n        Tests were only conducted for rather small multipliers, around\n        ``-10.0`` to ``+10.0``.\n\n        In general, the multipliers sampled from `multipliers` must be in a\n        value range that corresponds to the input image's dtype. E.g. if the\n        input image has dtype ``uint16`` and the samples generated from\n        `multipliers` are ``float64``, this function will still force all\n        samples to be within the value range of ``float16``, as it has the\n        same number of bytes (two) as ``uint16``. This is done to make\n        overflows less likely to occur.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: limited; tested (1)\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: limited; tested (1)\n        * ``int16``: limited; tested (1)\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: limited; tested (1)\n        * ``float32``: limited; tested (1)\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: limited; tested (1)\n\n        - (1) Non-uint8 dtypes can overflow. For floats, this can result\n              in +/-inf.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n\n    multipliers : ndarray\n        The multipliers with which to multiply the image. Expected to have\n        the same height and width as `image` and either no channels or one\n        channel or the same number of channels as `image`.\n\n    Returns\n    -------\n    ndarray\n        Image, multiplied by `multipliers`.\n\n    \"\"\"\n    return multiply_elementwise_(np.copy(image), multipliers)\n\n\ndef multiply_elementwise_(image, multipliers):\n    \"\"\"Multiply in-place an image with an array of values.\n\n    This method ensures that ``uint8`` does not overflow during the addition.\n\n    note::\n\n        Tests were only conducted for rather small multipliers, around\n        ``-10.0`` to ``+10.0``.\n\n        In general, the multipliers sampled from `multipliers` must be in a\n        value range that corresponds to the input image's dtype. E.g. if the\n        input image has dtype ``uint16`` and the samples generated from\n        `multipliers` are ``float64``, this function will still force all\n        samples to be within the value range of ``float16``, as it has the\n        same number of bytes (two) as ``uint16``. This is done to make\n        overflows less likely to occur.\n\n    Added in 0.5.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: limited; tested (1)\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: limited; tested (1)\n        * ``int16``: limited; tested (1)\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: limited; tested (1)\n        * ``float32``: limited; tested (1)\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: limited; tested (1)\n\n        - (1) Non-uint8 dtypes can overflow. For floats, this can result\n              in +/-inf.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n\n    multipliers : ndarray\n        The multipliers with which to multiply the image. Expected to have\n        the same height and width as `image` and either no channels or one\n        channel or the same number of channels as `image`.\n\n    Returns\n    -------\n    ndarray\n        Image, multiplied by `multipliers`.\n\n    \"\"\"\n    iadt.gate_dtypes_strs(\n        {image.dtype},\n        allowed=\"bool uint8 uint16 int8 int16 float16 float32\",\n        disallowed=\"uint32 uint64 int32 int64 float64 float128\",\n        augmenter=None)\n\n    if 0 in image.shape:\n        return image\n\n    if multipliers.dtype.kind == \"b\":\n        # TODO extend this with some shape checks\n        image *= multipliers\n        return image\n    if image.dtype == iadt._UINT8_DTYPE:\n        return _multiply_elementwise_to_uint8_(image, multipliers)\n    return _multiply_elementwise_to_non_uint8(image, multipliers)\n\n\n# Added in 0.5.0.\ndef _multiply_elementwise_to_uint8_(image, multipliers):\n    dt = multipliers.dtype\n    kind = dt.kind\n    if kind == \"f\" and dt != iadt._FLOAT32_DTYPE:\n        multipliers = multipliers.astype(np.float32)\n    elif kind == \"i\" and dt != iadt._INT32_DTYPE:\n        multipliers = multipliers.astype(np.int32)\n    elif kind == \"u\" and dt != iadt._UINT8_DTYPE:\n        multipliers = multipliers.astype(np.uint8)\n\n    if multipliers.ndim < image.ndim:\n        multipliers = multipliers[:, :, np.newaxis]\n    if multipliers.shape != image.shape:\n        multipliers = np.broadcast_to(multipliers, image.shape)\n\n    assert image.shape == multipliers.shape, (\n        \"Expected multipliers to have shape (H,W) or (H,W,1) or (H,W,C) \"\n        \"(H = image height, W = image width, C = image channels). Reached \"\n        \"shape %s after broadcasting, compared to image shape %s.\" % (\n            multipliers.shape, image.shape\n        )\n    )\n\n    # views seem to be fine here\n    if image.flags[\"C_CONTIGUOUS\"] is False:\n        image = np.ascontiguousarray(image)\n\n    result = cv2.multiply(image, multipliers, dst=image, dtype=cv2.CV_8U)\n\n    return result\n\n\ndef _multiply_elementwise_to_non_uint8(image, multipliers):\n    input_dtype = image.dtype\n\n    # TODO maybe introduce to stochastic parameters some way to\n    #      get the possible min/max values, could make things\n    #      faster for dropout to get 0/1 min/max from the binomial\n    # itemsize decrease is currently deactivated due to issues\n    # with clip and int64, see Add\n    mul_min = np.min(multipliers)\n    mul_max = np.max(multipliers)\n    # is_not_increasing_value_range = (\n    #     (-1 <= mul_min <= 1) and (-1 <= mul_max <= 1))\n\n    # We limit here the value range of the mul parameter to the\n    # bytes in the image's dtype. This prevents overflow problems\n    # and makes it less likely that the image has to be up-casted,\n    # which again improves performance and saves memory. Note that\n    # this also enables more dtypes for image inputs.\n    # The downside is that the mul parameter is limited in its\n    # value range.\n    itemsize = max(\n        image.dtype.itemsize,\n        2 if multipliers.dtype.kind == \"f\" else 1\n    )  # float min itemsize is 2\n    dtype_target = np.dtype(\"%s%d\" % (multipliers.dtype.kind, itemsize))\n    multipliers = iadt.clip_to_dtype_value_range_(\n        multipliers, dtype_target,\n        validate=True, validate_values=(mul_min, mul_max))\n\n    if multipliers.shape[2] == 1:\n        # TODO check if tile() is here actually needed\n        nb_channels = image.shape[-1]\n        multipliers = np.tile(multipliers, (1, 1, nb_channels))\n\n    image, multipliers = iadt.promote_array_dtypes_(\n        [image, multipliers],\n        dtypes=[image, dtype_target],\n        increase_itemsize_factor=1\n        # increase_itemsize_factor=(\n        #     1 if is_not_increasing_value_range else 2)\n    )\n    image = np.multiply(image, multipliers, out=image, casting=\"no\")\n    return iadt.restore_dtypes_(image, input_dtype)\n\n\ndef cutout(image, x1, y1, x2, y2,\n           fill_mode=\"constant\", cval=0, fill_per_channel=False,\n           seed=None):\n    \"\"\"Fill a single area within an image using a fill mode.\n\n    This cutout method uses the top-left and bottom-right corner coordinates\n    of the cutout region given as absolute pixel values.\n\n    .. note::\n\n        Gaussian fill mode will assume that float input images contain values\n        in the interval ``[0.0, 1.0]`` and hence sample values from a\n        gaussian within that interval, i.e. from ``N(0.5, std=0.5/3)``.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.arithmetic.cutout_`.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image to modify.\n\n    x1 : number\n        See :func:`~imgaug.augmenters.arithmetic.cutout_`.\n\n    y1 : number\n        See :func:`~imgaug.augmenters.arithmetic.cutout_`.\n\n    x2 : number\n        See :func:`~imgaug.augmenters.arithmetic.cutout_`.\n\n    y2 : number\n        See :func:`~imgaug.augmenters.arithmetic.cutout_`.\n\n    fill_mode : {'constant', 'gaussian'}, optional\n        See :func:`~imgaug.augmenters.arithmetic.cutout_`.\n\n    cval : number or tuple of number, optional\n        See :func:`~imgaug.augmenters.arithmetic.cutout_`.\n\n    fill_per_channel : number or bool, optional\n        See :func:`~imgaug.augmenters.arithmetic.cutout_`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.arithmetic.cutout_`.\n\n    Returns\n    -------\n    ndarray\n        Image with area filled in.\n\n    \"\"\"\n    return cutout_(np.copy(image),\n                   x1, y1, x2, y2,\n                   fill_mode, cval, fill_per_channel, seed)\n\n\ndef cutout_(image, x1, y1, x2, y2,\n            fill_mode=\"constant\", cval=0, fill_per_channel=False,\n            seed=None):\n    \"\"\"Fill a single area within an image using a fill mode (in-place).\n\n    This cutout method uses the top-left and bottom-right corner coordinates\n    of the cutout region given as absolute pixel values.\n\n    .. note::\n\n        Gaussian fill mode will assume that float input images contain values\n        in the interval ``[0.0, 1.0]`` and hence sample values from a\n        gaussian within that interval, i.e. from ``N(0.5, std=0.5/3)``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    minimum of (\n        :func:`~imgaug.augmenters.arithmetic._fill_rectangle_gaussian_`,\n        :func:`~imgaug.augmenters.arithmetic._fill_rectangle_constant_`\n    )\n\n    Parameters\n    ----------\n    image : ndarray\n        Image to modify. Might be modified in-place.\n\n    x1 : number\n        X-coordinate of the top-left corner of the cutout region.\n\n    y1 : number\n        Y-coordinate of the top-left corner of the cutout region.\n\n    x2 : number\n        X-coordinate of the bottom-right corner of the cutout region.\n\n    y2 : number\n        Y-coordinate of the bottom-right corner of the cutout region.\n\n    fill_mode : {'constant', 'gaussian'}, optional\n        Fill mode to use.\n\n    cval : number or tuple of number, optional\n        The constant value to use when filling with mode ``constant``.\n        May be an intensity value or color tuple.\n\n    fill_per_channel : number or bool, optional\n        Whether to fill in a channelwise fashion.\n        If number then a value ``>=0.5`` will be interpreted as ``True``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        A random number generator to sample random values from.\n        Usually an integer seed value or an ``RNG`` instance.\n        See :class:`imgaug.random.RNG` for details.\n\n    Returns\n    -------\n    ndarray\n        Image with area filled in.\n        The input image might have been modified in-place.\n\n    \"\"\"\n    import importlib\n\n    height, width = image.shape[0:2]\n    x1 = min(max(int(x1), 0), width)\n    y1 = min(max(int(y1), 0), height)\n    x2 = min(max(int(x2), 0), width)\n    y2 = min(max(int(y2), 0), height)\n\n    if x2 > x1 and y2 > y1:\n        assert fill_mode in _CUTOUT_FILL_MODES, (\n            \"Expected one of the following fill modes: %s. \"\n            \"Got: %s.\" % (\n                str(list(_CUTOUT_FILL_MODES.keys())), fill_mode))\n\n        module_name, fname = _CUTOUT_FILL_MODES[fill_mode]\n        module = importlib.import_module(module_name)\n        func = getattr(module, fname)\n        image = func(\n            image,\n            x1=x1, y1=y1, x2=x2, y2=y2,\n            cval=cval,\n            per_channel=(fill_per_channel >= 0.5),\n            random_state=(\n                iarandom.RNG(seed)\n                if not isinstance(seed, iarandom.RNG)\n                else seed)  # only RNG(.) without \"if\" is ~8x slower\n        )\n    return image\n\n\ndef _fill_rectangle_gaussian_(image, x1, y1, x2, y2, cval, per_channel,\n                              random_state):\n    \"\"\"Fill a rectangular image area with samples from a gaussian.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: limited; tested (1)\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: limited; tested (1)\n        * ``float16``: yes; tested (2)\n        * ``float32``: yes; tested (2)\n        * ``float64``: yes; tested (2)\n        * ``float128``: limited; tested (1) (2)\n        * ``bool``: yes; tested\n\n        - (1) Possible loss of resolution due to gaussian values being sampled\n              as ``float64`` s.\n        - (2) Float input arrays are assumed to be in interval ``[0.0, 1.0]``\n              and all gaussian samples are within that interval too.\n\n    \"\"\"\n    # for float we assume value range [0.0, 1.0]\n    # that matches the common use case and also makes the tests way easier\n    # we also set bool here manually as the center value returned by\n    # get_value_range_for_dtype() is None\n    kind = image.dtype.kind\n    if kind in [\"f\", \"b\"]:\n        min_value = 0.0\n        center_value = 0.5\n        max_value = 1.0\n    else:\n        min_value, center_value, max_value = iadt.get_value_range_of_dtype(\n            image.dtype)\n\n    # set standard deviation to 1/3 of value range to get 99.7% of values\n    # within [min v.r., max v.r.]\n    # we also divide by 2 because we want to spread towards the\n    # \"left\"/\"right\" of the center value by half of the value range\n    stddev = (float(max_value) - float(min_value)) / 2.0 / 3.0\n\n    height = y2 - y1\n    width = x2 - x1\n    shape = (height, width)\n    if per_channel and image.ndim == 3:\n        shape = shape + (image.shape[2],)\n    rect = random_state.normal(center_value, stddev, size=shape)\n    if image.dtype.kind == \"b\":\n        rect_vr = (rect > 0.5)\n    else:\n        rect_vr = np.clip(rect, min_value, max_value).astype(image.dtype)\n\n    if image.ndim == 3:\n        image[y1:y2, x1:x2, :] = np.atleast_3d(rect_vr)\n    else:\n        image[y1:y2, x1:x2] = rect_vr\n\n    return image\n\n\ndef _fill_rectangle_constant_(image, x1, y1, x2, y2, cval, per_channel,\n                              random_state):\n    \"\"\"Fill a rectangular area within an image with constant value(s).\n\n    `cval` may be a single value or one per channel. If the number of items\n    in `cval` does not match the number of channels in `image`, it may\n    be tiled up to the number of channels.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    \"\"\"\n    if ia.is_iterable(cval):\n        if per_channel:\n            nb_channels = None if image.ndim == 2 else image.shape[-1]\n            if nb_channels is None:\n                cval = cval[0]\n            elif len(cval) < nb_channels:\n                mul = int(np.ceil(nb_channels / len(cval)))\n                cval = np.tile(cval, (mul,))[0:nb_channels]\n            elif len(cval) > nb_channels:\n                cval = cval[0:nb_channels]\n        else:\n            cval = cval[0]\n\n    # without the array(), uint64 max value is assigned as 0\n    image[y1:y2, x1:x2, ...] = np.array(cval, dtype=image.dtype)\n\n    return image\n\n\ndef replace_elementwise_(image, mask, replacements):\n    \"\"\"Replace components in an image array with new values.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: no (1)\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: no (2)\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no\n        * ``bool``: yes; tested\n\n        - (1) ``uint64`` is currently not supported, because\n              :func:`~imgaug.dtypes.clip_to_dtype_value_range_()` does not\n              support it, which again is because numpy.clip() seems to not\n              support it.\n        - (2) `int64` is disallowed due to being converted to `float64`\n              by :func:`numpy.clip` since 1.17 (possibly also before?).\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n\n    mask : ndarray\n        Mask of shape ``(H,W,[C])`` denoting which components to replace.\n        If ``C`` is provided, it must be ``1`` or match the ``C`` of `image`.\n        May contain floats in the interval ``[0.0, 1.0]``.\n\n    replacements : iterable\n        Replacements to place in `image` at the locations defined by `mask`.\n        This 1-dimensional iterable must contain exactly as many values\n        as there are replaced components in `image`.\n\n    Returns\n    -------\n    ndarray\n        Image with replaced components.\n\n    \"\"\"\n    iadt.gate_dtypes_strs(\n        {image.dtype},\n        allowed=\"bool uint8 uint16 uint32 int8 int16 int32 float16 float32 \"\n                \"float64\",\n        disallowed=\"uint64 int64 float128\",\n        augmenter=None\n    )\n\n    # This is slightly faster (~20%) for masks that are True at many\n    # locations, but slower (~50%) for masks with few Trues, which is\n    # probably the more common use-case:\n    #\n    # replacement_samples = self.replacement.draw_samples(\n    #     sampling_shape, random_state=rs_replacement)\n    #\n    # # round, this makes 0.2 e.g. become 0 in case of boolean\n    # # image (otherwise replacing values with 0.2 would\n    # # lead to True instead of False).\n    # if (image.dtype.kind in [\"i\", \"u\", \"b\"]\n    #         and replacement_samples.dtype.kind == \"f\"):\n    #     replacement_samples = np.round(replacement_samples)\n    #\n    # replacement_samples = iadt.clip_to_dtype_value_range_(\n    #     replacement_samples, image.dtype, validate=False)\n    # replacement_samples = replacement_samples.astype(\n    #     image.dtype, copy=False)\n    #\n    # if sampling_shape[2] == 1:\n    #     mask_samples = np.tile(mask_samples, (1, 1, nb_channels))\n    #     replacement_samples = np.tile(\n    #         replacement_samples, (1, 1, nb_channels))\n    # mask_thresh = mask_samples > 0.5\n    # image[mask_thresh] = replacement_samples[mask_thresh]\n    input_shape = image.shape\n    if image.ndim == 2:\n        image = image[..., np.newaxis]\n    if mask.ndim == 2:\n        mask = mask[..., np.newaxis]\n\n    mask_thresh = mask > 0.5\n    if mask.shape[2] == 1:\n        nb_channels = image.shape[-1]\n        # TODO verify if tile() is here really necessary\n        mask_thresh = np.tile(mask_thresh, (1, 1, nb_channels))\n\n    # round, this makes 0.2 e.g. become 0 in case of boolean\n    # image (otherwise replacing values with 0.2 would lead to True\n    # instead of False).\n    if image.dtype.kind in [\"i\", \"u\", \"b\"] and replacements.dtype.kind == \"f\":\n        replacements = np.round(replacements)\n\n    replacement_samples = iadt.clip_to_dtype_value_range_(\n        replacements, image.dtype, validate=False)\n    replacement_samples = replacement_samples.astype(image.dtype, copy=False)\n\n    image[mask_thresh] = replacement_samples\n    if len(input_shape) == 2:\n        return image[..., 0]\n    return image\n\n\ndef invert(image, min_value=None, max_value=None, threshold=None,\n           invert_above_threshold=True):\n    \"\"\"Invert an array.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.arithmetic.invert_`.\n\n    Parameters\n    ----------\n    image : ndarray\n        See :func:`invert_`.\n\n    min_value : None or number, optional\n        See :func:`invert_`.\n\n    max_value : None or number, optional\n        See :func:`invert_`.\n\n    threshold : None or number, optional\n        See :func:`invert_`.\n\n    invert_above_threshold : bool, optional\n        See :func:`invert_`.\n\n    Returns\n    -------\n    ndarray\n        Inverted image.\n\n    \"\"\"\n    return invert_(np.copy(image), min_value=min_value, max_value=max_value,\n                   threshold=threshold,\n                   invert_above_threshold=invert_above_threshold)\n\n\ndef invert_(image, min_value=None, max_value=None, threshold=None,\n            invert_above_threshold=True):\n    \"\"\"Invert an array in-place.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    if (min_value=None and max_value=None):\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    if (min_value!=None or max_value!=None):\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: no (1)\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: no (2)\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: no (2)\n        * ``float128``: no (3)\n        * ``bool``: no (4)\n\n        - (1) Not allowed due to numpy's clip converting from ``uint64`` to\n              ``float64``.\n        - (2) Not allowed as int/float have to be increased in resolution\n              when using min/max values.\n        - (3) Not tested.\n        - (4) Makes no sense when using min/max values.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n        The array *might* be modified in-place.\n\n    min_value : None or number, optional\n        Minimum of the value range of input images, e.g. ``0`` for ``uint8``\n        images. If set to ``None``, the value will be automatically derived\n        from the image's dtype.\n\n    max_value : None or number, optional\n        Maximum of the value range of input images, e.g. ``255`` for ``uint8``\n        images. If set to ``None``, the value will be automatically derived\n        from the image's dtype.\n\n    threshold : None or number, optional\n        A threshold to use in order to invert only numbers above or below\n        the threshold. If ``None`` no thresholding will be used.\n\n    invert_above_threshold : bool, optional\n        If ``True``, only values ``>=threshold`` will be inverted.\n        Otherwise, only values ``<threshold`` will be inverted.\n        If `threshold` is ``None`` this parameter has no effect.\n\n    Returns\n    -------\n    ndarray\n        Inverted image. This *can* be the same array as input in `image`,\n        modified in-place.\n\n    \"\"\"\n    if image.size == 0:\n        return image\n\n    # when no custom min/max are chosen, all bool, uint, int and float dtypes\n    # should be invertable (float tested only up to 64bit)\n    # when chosing custom min/max:\n    # - bool makes no sense, not allowed\n    # - int and float must be increased in resolution if custom min/max values\n    #   are chosen, hence they are limited to 32 bit and below\n    # - uint64 is converted by numpy's clip to float64, hence loss of accuracy\n    # - float16 seems to not be perfectly accurate, but still ok-ish -- was\n    #   off by 10 for center value of range (float 16 min, 16), where float\n    #   16 min is around -65500\n    allow_dtypes_custom_minmax = iadt._convert_dtype_strs_to_types(\n        \"uint8 uint16 uint32 int8 int16 int32 float16 float32\"\n    )\n\n    min_value_dt, _, max_value_dt = \\\n        iadt.get_value_range_of_dtype(image.dtype)\n    min_value = (min_value_dt\n                 if min_value is None else min_value)\n    max_value = (max_value_dt\n                 if max_value is None else max_value)\n    assert min_value >= min_value_dt, (\n        \"Expected min_value to be above or equal to dtype's min \"\n        \"value, got %s (vs. min possible %s for %s)\" % (\n            str(min_value), str(min_value_dt), image.dtype.name)\n    )\n    assert max_value <= max_value_dt, (\n        \"Expected max_value to be below or equal to dtype's max \"\n        \"value, got %s (vs. max possible %s for %s)\" % (\n            str(max_value), str(max_value_dt), image.dtype.name)\n    )\n    assert min_value < max_value, (\n        \"Expected min_value to be below max_value, got %s \"\n        \"and %s\" % (\n            str(min_value), str(max_value))\n    )\n\n    if min_value != min_value_dt or max_value != max_value_dt:\n        assert image.dtype in allow_dtypes_custom_minmax, (\n            \"Can use custom min/max values only with the following \"\n            \"dtypes: %s. Got: %s.\" % (\n                \", \".join(allow_dtypes_custom_minmax), image.dtype.name))\n\n    if image.dtype == iadt._UINT8_DTYPE:\n        return _invert_uint8_(image, min_value, max_value, threshold,\n                              invert_above_threshold)\n\n    dtype_kind_to_invert_func = {\n        \"b\": _invert_bool,\n        \"u\": _invert_uint16_or_larger_,  # uint8 handled above\n        \"i\": _invert_int_,\n        \"f\": _invert_float\n    }\n\n    func = dtype_kind_to_invert_func[image.dtype.kind]\n\n    if threshold is None:\n        return func(image, min_value, max_value)\n\n    arr_inv = func(np.copy(image), min_value, max_value)\n    if invert_above_threshold:\n        mask = (image >= threshold)\n    else:\n        mask = (image < threshold)\n    image[mask] = arr_inv[mask]\n    return image\n\n\ndef _invert_bool(arr, min_value, max_value):\n    assert min_value == 0 and max_value == 1, (\n        \"min_value and max_value must be 0 and 1 for bool arrays. \"\n        \"Got %.4f and %.4f.\" % (min_value, max_value))\n    return ~arr\n\n\n# Added in 0.4.0.\ndef _invert_uint8_(arr, min_value, max_value, threshold,\n                   invert_above_threshold):\n    shape = arr.shape\n    nb_channels = shape[-1] if len(shape) == 3 else 1\n    valid_for_cv2 = (\n        threshold is None\n        and min_value == 0\n        and len(shape) >= 2\n        and shape[0]*shape[1]*nb_channels != 4\n    )\n    if valid_for_cv2:\n        return _invert_uint8_subtract_(arr, max_value)\n    return _invert_uint8_lut_pregenerated_(\n        arr, min_value, max_value, threshold, invert_above_threshold\n    )\n\n\n# Added in 0.5.0.\ndef _invert_uint8_lut_pregenerated_(arr, min_value, max_value, threshold,\n                                    invert_above_threshold):\n    table = _InvertTablesSingleton.get_instance().get_table(\n        min_value=min_value,\n        max_value=max_value,\n        threshold=threshold,\n        invert_above_threshold=invert_above_threshold\n    )\n    arr = ia.apply_lut_(arr, table)\n    return arr\n\n\n# Added in 0.5.0.\ndef _invert_uint8_subtract_(arr, max_value):\n    # seems to work with arr.base.shape[0] > 1\n    if arr.base is not None and arr.base.shape[0] == 1:\n        arr = np.copy(arr)\n    if not arr.flags[\"C_CONTIGUOUS\"]:\n        arr = np.ascontiguousarray(arr)\n\n    input_shape = arr.shape\n    if len(input_shape) > 2 and input_shape[-1] > 1:\n        arr = arr.ravel()\n    # This also supports a mask, which would help for thresholded invert, but\n    # it seems that all non-masked components are set to zero in the output\n    # array. Tackling this issue seems to rather require more time than just\n    # using a LUT.\n    arr = cv2.subtract(int(max_value), arr, dst=arr)\n    if arr.shape != input_shape:\n        return arr.reshape(input_shape)\n    return arr\n\n\n# Added in 0.4.0.\ndef _invert_uint16_or_larger_(arr, min_value, max_value):\n    min_max_is_vr = (min_value == 0\n                     and max_value == np.iinfo(arr.dtype).max)\n    if min_max_is_vr:\n        return max_value - arr\n    return _invert_by_distance(\n        np.clip(arr, min_value, max_value),\n        min_value, max_value\n    )\n\n\n# Added in 0.4.0.\ndef _invert_int_(arr, min_value, max_value):\n    # note that for int dtypes the max value is\n    #   (-1) * min_value - 1\n    # e.g. -128 and 127 (min/max) for int8\n    # mapping example:\n    #  [-4, -3, -2, -1,  0,  1,  2,  3]\n    # will be mapped to\n    #  [ 3,  2,  1,  0, -1, -2, -3, -4]\n    # hence we can not simply compute the inverse as:\n    #  after = (-1) * before\n    # but instead need\n    #  after = (-1) * before - 1\n    # however, this exceeds the value range for the minimum value, e.g.\n    # for int8: -128 -> 128 -> 127, where 128 exceeds it. Hence, we must\n    # compute the inverse via a mask (extra step for the minimum)\n    # or we have to increase the resolution of the array. Here, a\n    # two-step approach is used.\n\n    if min_value == (-1) * max_value - 1:\n        arr_inv = np.copy(arr)\n        mask = (arr_inv == min_value)\n\n        # there is probably a one-liner here to do this, but\n        #  ((-1) * (arr_inv * ~mask) - 1) + mask * max_value\n        # has the disadvantage of inverting min_value to max_value - 1\n        # while\n        #  ((-1) * (arr_inv * ~mask) - 1) + mask * (max_value+1)\n        #  ((-1) * (arr_inv * ~mask) - 1) + mask * max_value + mask\n        # both sometimes increase the dtype resolution (e.g. int32 to int64)\n        arr_inv[mask] = max_value\n        arr_inv[~mask] = (-1) * arr_inv[~mask] - 1\n\n        return arr_inv\n\n    return _invert_by_distance(\n        np.clip(arr, min_value, max_value),\n        min_value, max_value\n    )\n\n\ndef _invert_float(arr, min_value, max_value):\n    if np.isclose(max_value, (-1)*min_value, rtol=0):\n        return (-1) * arr\n    return _invert_by_distance(\n        np.clip(arr, min_value, max_value),\n        min_value, max_value\n    )\n\n\ndef _invert_by_distance(arr, min_value, max_value):\n    arr_inv = arr\n    if arr.dtype.kind in [\"i\", \"f\"]:\n        arr_inv = iadt.increase_array_resolutions_([np.copy(arr)], 2)[0]\n    distance_from_min = np.abs(arr_inv - min_value)  # d=abs(v-min)\n    arr_inv = max_value - distance_from_min  # v'=MAX-d\n    # due to floating point inaccuracies, we might exceed the min/max\n    # values for floats here, hence clip this happens especially for\n    # values close to the float dtype's maxima\n    if arr.dtype.kind == \"f\":\n        arr_inv = np.clip(arr_inv, min_value, max_value)\n    if arr.dtype.kind in [\"i\", \"f\"]:\n        arr_inv = iadt.restore_dtypes_(\n            arr_inv, arr.dtype, clip=False)\n    return arr_inv\n\n\n# Added in 0.4.0.\ndef _generate_table_for_invert_uint8(min_value, max_value, threshold,\n                                     invert_above_threshold):\n    table = np.arange(256).astype(np.int32)\n    full_value_range = (min_value == 0 and max_value == 255)\n    if full_value_range:\n        table_inv = table[::-1]\n    else:\n        distance_from_min = np.abs(table - min_value)\n        table_inv = max_value - distance_from_min\n    table_inv = np.clip(table_inv, min_value, max_value).astype(np.uint8)\n\n    if threshold is not None:\n        table = table.astype(np.uint8)\n        if invert_above_threshold:\n            table_inv = np.concatenate([\n                table[0:int(threshold)],\n                table_inv[int(threshold):]\n            ], axis=0)\n        else:\n            table_inv = np.concatenate([\n                table_inv[0:int(threshold)],\n                table[int(threshold):]\n            ], axis=0)\n\n    return table_inv\n\n\n# Added in 0.5.0.\nclass _InvertTables(object):\n    # Added in 0.5.0.\n    def __init__(self):\n        self.tables = {}\n\n    # Added in 0.5.0.\n    def get_table(self, min_value, max_value, threshold,\n                  invert_above_threshold):\n        if min_value == 0 and max_value == 255:\n            key = (threshold, invert_above_threshold)\n            table = self.tables.get(key, None)\n            if table is None:\n                table = _generate_table_for_invert_uint8(\n                    min_value, max_value, threshold, invert_above_threshold\n                )\n                self.tables[key] = table\n            return table\n        return _generate_table_for_invert_uint8(\n            min_value, max_value, threshold, invert_above_threshold\n        )\n\n\n# Added in 0.5.0.\nclass _InvertTablesSingleton(object):\n    _INSTANCE = None\n\n    # Added in 0.5.0.\n    @classmethod\n    def get_instance(cls):\n        if cls._INSTANCE is None:\n            cls._INSTANCE = _InvertTables()\n        return cls._INSTANCE\n\n\ndef solarize(image, threshold=128):\n    \"\"\"Invert pixel values above a threshold.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.arithmetic.solarize_`.\n\n    Parameters\n    ----------\n    image : ndarray\n        See :func:`solarize_`.\n\n    threshold : None or number, optional\n        See :func:`solarize_`.\n\n    Returns\n    -------\n    ndarray\n        Inverted image.\n\n    \"\"\"\n    return solarize_(np.copy(image), threshold=threshold)\n\n\ndef solarize_(image, threshold=128):\n    \"\"\"Invert pixel values above a threshold in-place.\n\n    This function is a wrapper around :func:`invert`.\n\n    This function performs the same transformation as\n    :func:`PIL.ImageOps.solarize`.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See ``~imgaug.augmenters.arithmetic.invert_(min_value=None and max_value=None)``.\n\n    Parameters\n    ----------\n    image : ndarray\n        See :func:`invert_`.\n\n    threshold : None or number, optional\n        See :func:`invert_`.\n        Note: The default threshold is optimized for ``uint8`` images.\n\n\n    Returns\n    -------\n    ndarray\n        Inverted image. This *can* be the same array as input in `image`,\n        modified in-place.\n\n    \"\"\"\n    return invert_(image, threshold=threshold)\n\n\ndef compress_jpeg(image, compression):\n    \"\"\"Compress an image using jpeg compression.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: ?\n        * ``uint32``: ?\n        * ``uint64``: ?\n        * ``int8``: ?\n        * ``int16``: ?\n        * ``int32``: ?\n        * ``int64``: ?\n        * ``float16``: ?\n        * ``float32``: ?\n        * ``float64``: ?\n        * ``float128``: ?\n        * ``bool``: ?\n\n    Parameters\n    ----------\n    image : ndarray\n        Image of dtype ``uint8`` and shape ``(H,W,[C])``. If ``C`` is provided,\n        it must be ``1`` or ``3``.\n\n    compression : int\n        Strength of the compression in the interval ``[0, 100]``.\n\n    Returns\n    -------\n    ndarray\n        Input image after applying jpeg compression to it and reloading\n        the result into a new array. Same shape and dtype as the input.\n\n    \"\"\"\n    import PIL.Image\n\n    if image.size == 0:\n        return np.copy(image)\n\n    # The value range 1 to 95 is suggested by PIL's save() documentation\n    # Values above 95 seem to not make sense (no improvement in visual\n    # quality, but large file size).\n    # A value of 100 would mostly deactivate jpeg compression.\n    # A value of 0 would lead to no compression (instead of maximum\n    # compression).\n    # We use range 1 to 100 here, because this augmenter is about\n    # generating images for training and not for saving, hence we do not\n    # care about large file sizes.\n    maximum_quality = 100\n    minimum_quality = 1\n\n    iadt.allow_only_uint8({image.dtype})\n    assert 0 <= compression <= 100, (\n        \"Expected compression to be in the interval [0, 100], \"\n        \"got %.4f.\" % (compression,))\n\n    has_no_channels = (image.ndim == 2)\n    is_single_channel = (image.ndim == 3 and image.shape[-1] == 1)\n    if is_single_channel:\n        image = image[..., 0]\n\n    assert has_no_channels or is_single_channel or image.shape[-1] == 3, (\n        \"Expected either a grayscale image of shape (H,W) or (H,W,1) or an \"\n        \"RGB image of shape (H,W,3). Got shape %s.\" % (image.shape,))\n\n    # Map from compression to quality used by PIL\n    # We have valid compressions from 0 to 100, i.e. 101 possible\n    # values\n    quality = int(\n        np.clip(\n            np.round(\n                minimum_quality\n                + (maximum_quality - minimum_quality)\n                * (1.0 - (compression / 101))\n            ),\n            minimum_quality,\n            maximum_quality\n        )\n    )\n\n    image_pil = PIL.Image.fromarray(image)\n    with tempfile.NamedTemporaryFile(mode=\"wb+\", suffix=\".jpg\") as f:\n        image_pil.save(f, quality=quality)\n\n        # Read back from file.\n        # We dont read from f.name, because that leads to PermissionDenied\n        # errors on Windows. We add f.seek(0) here, because otherwise we get\n        # `SyntaxError: index out of range` in PIL.\n        f.seek(0)\n        pilmode = \"RGB\"\n        if has_no_channels or is_single_channel:\n            pilmode = \"L\"\n        image = imageio.imread(f, pilmode=pilmode, format=\"jpeg\")\n    if is_single_channel:\n        image = image[..., np.newaxis]\n    return image\n\n\nclass Add(meta.Augmenter):\n    \"\"\"\n    Add a value to all pixels in an image.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.arithmetic.add_scalar`.\n\n    Parameters\n    ----------\n    value : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Value to add to all pixels.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, then a value from the discrete\n              interval ``[a..b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Add(10)\n\n    Always adds a value of 10 to all channels of all pixels of all input\n    images.\n\n    >>> aug = iaa.Add((-10, 10))\n\n    Adds a value from the discrete interval ``[-10..10]`` to all pixels of\n    input images. The exact value is sampled per image.\n\n    >>> aug = iaa.Add((-10, 10), per_channel=True)\n\n    Adds a value from the discrete interval ``[-10..10]`` to all pixels of\n    input images. The exact value is sampled per image *and* channel,\n    i.e. to a red-channel it might add 5 while subtracting 7 from the\n    blue channel of the same image.\n\n    >>> aug = iaa.Add((-10, 10), per_channel=0.5)\n\n    Identical to the previous example, but the `per_channel` feature is only\n    active for 50 percent of all images.\n\n    \"\"\"\n\n    def __init__(self, value=(-20, 20), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Add, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.value = iap.handle_continuous_param(\n            value, \"value\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True, prefetch=True)\n        self.per_channel = iap.handle_probability_param(\n            per_channel, \"per_channel\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        nb_images = len(images)\n        nb_channels_max = meta.estimate_max_number_of_channels(images)\n        rss = random_state.duplicate(2)\n\n        per_channel_samples = self.per_channel.draw_samples(\n            (nb_images,), random_state=rss[0])\n        value_samples = self.value.draw_samples(\n            (nb_images, nb_channels_max), random_state=rss[1])\n\n        gen = enumerate(zip(images, value_samples, per_channel_samples))\n        for i, (image, value_samples_i, per_channel_samples_i) in gen:\n            nb_channels = image.shape[2]\n\n            # Example code to directly add images via image+sample (uint8 only)\n            # if per_channel_samples_i > 0.5:\n            #     result = []\n            #     image = image.astype(np.int16)\n            #     value_samples_i = value_samples_i.astype(np.int16)\n            #     for c, value in enumerate(value_samples_i[0:nb_channels]):\n            #         result.append(\n            #             np.clip(\n            #                 image[..., c:c+1] + value, 0, 255\n            #             ).astype(np.uint8))\n            #     images[i] = np.concatenate(result, axis=2)\n            # else:\n            #     images[i] = np.clip(\n            #         image.astype(np.int16)\n            #         + value_samples_i[0].astype(np.int16),\n            #         0, 255\n            #     ).astype(np.uint8)\n\n            if per_channel_samples_i > 0.5:\n                value = value_samples_i[0:nb_channels]\n            else:\n                # the if/else here catches the case of the channel axis being 0\n                value = value_samples_i[0] if value_samples_i.size > 0 else []\n\n            batch.images[i] = add_scalar_(image, value)\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.value, self.per_channel]\n\n\n# TODO merge this with Add\nclass AddElementwise(meta.Augmenter):\n    \"\"\"\n    Add to the pixels of images values that are pixelwise randomly sampled.\n\n    While the ``Add`` Augmenter samples one value to add *per image* (and\n    optionally per channel), this augmenter samples different values per image\n    and *per pixel* (and optionally per channel), i.e. intensities of\n    neighbouring pixels may be increased/decreased by different amounts.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.arithmetic.add_elementwise`.\n\n    Parameters\n    ----------\n    value : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Value to add to the pixels.\n\n            * If an int, exactly that value will always be used.\n            * If a tuple ``(a, b)``, then values from the discrete interval\n              ``[a..b]`` will be sampled per image and pixel.\n            * If a list of integers, a random value will be sampled from the\n              list per image and pixel.\n            * If a ``StochasticParameter``, then values will be sampled per\n              image and pixel from that parameter.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AddElementwise(10)\n\n    Always adds a value of 10 to all channels of all pixels of all input\n    images.\n\n    >>> aug = iaa.AddElementwise((-10, 10))\n\n    Samples per image and pixel a value from the discrete interval\n    ``[-10..10]`` and adds that value to the respective pixel.\n\n    >>> aug = iaa.AddElementwise((-10, 10), per_channel=True)\n\n    Samples per image, pixel *and also channel* a value from the discrete\n    interval ``[-10..10]`` and adds it to the respective pixel's channel value.\n    Therefore, added values may differ between channels of the same pixel.\n\n    >>> aug = iaa.AddElementwise((-10, 10), per_channel=0.5)\n\n    Identical to the previous example, but the `per_channel` feature is only\n    active for 50 percent of all images.\n\n    \"\"\"\n\n    def __init__(self, value=(-20, 20), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(AddElementwise, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.value = iap.handle_continuous_param(\n            value, \"value\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True)\n        self.per_channel = iap.handle_probability_param(\n            per_channel, \"per_channel\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        nb_images = len(images)\n        rss = random_state.duplicate(1+nb_images)\n        per_channel_samples = self.per_channel.draw_samples(\n            (nb_images,), random_state=rss[0])\n\n        gen = enumerate(zip(images, per_channel_samples, rss[1:]))\n        for i, (image, per_channel_samples_i, rs) in gen:\n            height, width, nb_channels = image.shape\n            sample_shape = (height,\n                            width,\n                            nb_channels if per_channel_samples_i > 0.5 else 1)\n            values = self.value.draw_samples(sample_shape, random_state=rs)\n\n            batch.images[i] = add_elementwise(image, values)\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.value, self.per_channel]\n\n\n# TODO rename to AddGaussianNoise?\n# TODO examples say that iaa.AdditiveGaussianNoise(scale=(0, 0.1*255)) samples\n#      the scale from the uniform dist. per image, but is that still the case?\n#      AddElementwise seems to now sample once for all images, which should\n#      lead to a single scale value.\nclass AdditiveGaussianNoise(AddElementwise):\n    \"\"\"\n    Add noise sampled from gaussian distributions elementwise to images.\n\n    This augmenter samples and adds noise elementwise, i.e. it can add\n    different noise values to neighbouring pixels and is comparable\n    to ``AddElementwise``.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.AddElementwise`.\n\n    Parameters\n    ----------\n    loc : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Mean of the normal distribution from which the noise is sampled.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value from the interval\n              ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list per\n              image.\n            * If a ``StochasticParameter``, a value will be sampled from the\n              parameter per image.\n\n    scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Standard deviation of the normal distribution that generates the noise.\n        Must be ``>=0``. If ``0`` then `loc` will simply be added to all\n        pixels.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value from the interval\n              ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list per\n              image.\n            * If a ``StochasticParameter``, a value will be sampled from the\n              parameter per image.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AdditiveGaussianNoise(scale=0.1*255)\n\n    Adds gaussian noise from the distribution ``N(0, 0.1*255)`` to images.\n    The samples are drawn per image and pixel.\n\n    >>> aug = iaa.AdditiveGaussianNoise(scale=(0, 0.1*255))\n\n    Adds gaussian noise from the distribution ``N(0, s)`` to images,\n    where ``s`` is sampled per image from the interval ``[0, 0.1*255]``.\n\n    >>> aug = iaa.AdditiveGaussianNoise(scale=0.1*255, per_channel=True)\n\n    Adds gaussian noise from the distribution ``N(0, 0.1*255)`` to images,\n    where the noise value is different per image and pixel *and* channel (e.g.\n    a different one for red, green and blue channels of the same pixel).\n    This leads to \"colorful\" noise.\n\n    >>> aug = iaa.AdditiveGaussianNoise(scale=0.1*255, per_channel=0.5)\n\n    Identical to the previous example, but the `per_channel` feature is only\n    active for 50 percent of all images.\n\n    \"\"\"\n    def __init__(self, loc=0, scale=(0, 15), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        loc2 = iap.handle_continuous_param(\n            loc, \"loc\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True)\n        scale2 = iap.handle_continuous_param(\n            scale, \"scale\", value_range=(0, None), tuple_to_uniform=True,\n            list_to_choice=True)\n\n        value = iap.Normal(loc=loc2, scale=scale2)\n\n        super(AdditiveGaussianNoise, self).__init__(\n            value, per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO add tests\n# TODO rename to AddLaplaceNoise?\nclass AdditiveLaplaceNoise(AddElementwise):\n    \"\"\"\n    Add noise sampled from laplace distributions elementwise to images.\n\n    The laplace distribution is similar to the gaussian distribution, but\n    puts more weight on the long tail. Hence, this noise will add more\n    outliers (very high/low values). It is somewhere between gaussian noise and\n    salt and pepper noise.\n\n    Values of around ``255 * 0.05`` for `scale` lead to visible noise (for\n    ``uint8``).\n    Values of around ``255 * 0.10`` for `scale` lead to very visible\n    noise (for ``uint8``).\n    It is recommended to usually set `per_channel` to ``True``.\n\n    This augmenter samples and adds noise elementwise, i.e. it can add\n    different noise values to neighbouring pixels and is comparable\n    to ``AddElementwise``.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.AddElementwise`.\n\n    Parameters\n    ----------\n    loc : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Mean of the laplace distribution that generates the noise.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value from the interval\n              ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list per\n              image.\n            * If a ``StochasticParameter``, a value will be sampled from the\n              parameter per image.\n\n    scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Standard deviation of the laplace distribution that generates the noise.\n        Must be ``>=0``. If ``0`` then only `loc` will be used.\n        Recommended to be around ``255*0.05``.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value from the interval\n              ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list per\n              image.\n            * If a ``StochasticParameter``, a value will be sampled from the\n              parameter per image.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AdditiveLaplaceNoise(scale=0.1*255)\n\n    Adds laplace noise from the distribution ``Laplace(0, 0.1*255)`` to images.\n    The samples are drawn per image and pixel.\n\n    >>> aug = iaa.AdditiveLaplaceNoise(scale=(0, 0.1*255))\n\n    Adds laplace noise from the distribution ``Laplace(0, s)`` to images,\n    where ``s`` is sampled per image from the interval ``[0, 0.1*255]``.\n\n    >>> aug = iaa.AdditiveLaplaceNoise(scale=0.1*255, per_channel=True)\n\n    Adds laplace noise from the distribution ``Laplace(0, 0.1*255)`` to images,\n    where the noise value is different per image and pixel *and* channel (e.g.\n    a different one for the red, green and blue channels of the same pixel).\n    This leads to \"colorful\" noise.\n\n    >>> aug = iaa.AdditiveLaplaceNoise(scale=0.1*255, per_channel=0.5)\n\n    Identical to the previous example, but the `per_channel` feature is only\n    active for 50 percent of all images.\n\n    \"\"\"\n    def __init__(self, loc=0, scale=(0, 15), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        loc2 = iap.handle_continuous_param(\n            loc, \"loc\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True)\n        scale2 = iap.handle_continuous_param(\n            scale, \"scale\", value_range=(0, None), tuple_to_uniform=True,\n            list_to_choice=True)\n\n        value = iap.Laplace(loc=loc2, scale=scale2)\n\n        super(AdditiveLaplaceNoise, self).__init__(\n            value,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO add tests\n# TODO rename to AddPoissonNoise?\nclass AdditivePoissonNoise(AddElementwise):\n    \"\"\"\n    Add noise sampled from poisson distributions elementwise to images.\n\n    Poisson noise is comparable to gaussian noise, as e.g. generated via\n    ``AdditiveGaussianNoise``. As poisson distributions produce only positive\n    numbers, the sign of the sampled values are here randomly flipped.\n\n    Values of around ``10.0`` for `lam` lead to visible noise (for ``uint8``).\n    Values of around ``20.0`` for `lam` lead to very visible noise (for\n    ``uint8``).\n    It is recommended to usually set `per_channel` to ``True``.\n\n    This augmenter samples and adds noise elementwise, i.e. it can add\n    different noise values to neighbouring pixels and is comparable\n    to ``AddElementwise``.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.AddElementwise`.\n\n    Parameters\n    ----------\n    lam : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Lambda parameter of the poisson distribution. Must be ``>=0``.\n        Recommended values are around ``0.0`` to ``10.0``.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value from the interval\n              ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, a value will be sampled from the\n              parameter per image.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AdditivePoissonNoise(lam=5.0)\n\n    Adds poisson noise sampled from a poisson distribution with a ``lambda``\n    parameter of ``5.0`` to images.\n    The samples are drawn per image and pixel.\n\n    >>> aug = iaa.AdditivePoissonNoise(lam=(0.0, 15.0))\n\n    Adds poisson noise sampled from ``Poisson(x)`` to images, where ``x`` is\n    randomly sampled per image from the interval ``[0.0, 15.0]``.\n\n    >>> aug = iaa.AdditivePoissonNoise(lam=5.0, per_channel=True)\n\n    Adds poisson noise sampled from ``Poisson(5.0)`` to images,\n    where the values are different per image and pixel *and* channel (e.g. a\n    different one for red, green and blue channels for the same pixel).\n\n    >>> aug = iaa.AdditivePoissonNoise(lam=(0.0, 15.0), per_channel=True)\n\n    Adds poisson noise sampled from ``Poisson(x)`` to images,\n    with ``x`` being sampled from ``uniform(0.0, 15.0)`` per image and\n    channel. This is the *recommended* configuration.\n\n    >>> aug = iaa.AdditivePoissonNoise(lam=(0.0, 15.0), per_channel=0.5)\n\n    Identical to the previous example, but the `per_channel` feature is only\n    active for 50 percent of all images.\n\n    \"\"\"\n    def __init__(self, lam=(0.0, 15.0), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        lam2 = iap.handle_continuous_param(\n            lam, \"lam\",\n            value_range=(0, None), tuple_to_uniform=True, list_to_choice=True)\n\n        value = iap.RandomSign(iap.Poisson(lam=lam2))\n\n        super(AdditivePoissonNoise, self).__init__(\n            value,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Multiply(meta.Augmenter):\n    \"\"\"\n    Multiply all pixels in an image with a random value sampled once per image.\n\n    This augmenter can be used to make images lighter or darker.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.arithmetic.multiply_scalar`.\n\n    Parameters\n    ----------\n    mul : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        The value with which to multiply the pixel values in each image.\n\n            * If a number, then that value will always be used.\n            * If a tuple ``(a, b)``, then a value from the interval ``[a, b]``\n              will be sampled per image and used for all pixels.\n            * If a list, then a random value will be sampled from that list per\n              image.\n            * If a ``StochasticParameter``, then that parameter will be used to\n              sample a new value per image.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Multiply(2.0)\n\n    Multiplies all images by a factor of ``2``, making the images significantly\n    brighter.\n\n    >>> aug = iaa.Multiply((0.5, 1.5))\n\n    Multiplies images by a random value sampled uniformly from the interval\n    ``[0.5, 1.5]``, making some images darker and others brighter.\n\n    >>> aug = iaa.Multiply((0.5, 1.5), per_channel=True)\n\n    Identical to the previous example, but the sampled multipliers differ by\n    image *and* channel, instead of only by image.\n\n    >>> aug = iaa.Multiply((0.5, 1.5), per_channel=0.5)\n\n    Identical to the previous example, but the `per_channel` feature is only\n    active for 50 percent of all images.\n\n    \"\"\"\n\n    def __init__(self, mul=(0.8, 1.2), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Multiply, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.mul = iap.handle_continuous_param(\n            mul, \"mul\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True)\n        self.per_channel = iap.handle_probability_param(\n            per_channel, \"per_channel\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        nb_images = len(images)\n        nb_channels_max = meta.estimate_max_number_of_channels(images)\n        rss = random_state.duplicate(2)\n        per_channel_samples = self.per_channel.draw_samples(\n            (nb_images,), random_state=rss[0])\n        mul_samples = self.mul.draw_samples(\n            (nb_images, nb_channels_max), random_state=rss[1])\n\n        gen = enumerate(zip(images, mul_samples, per_channel_samples))\n        for i, (image, mul_samples_i, per_channel_samples_i) in gen:\n            nb_channels = image.shape[2]\n\n            # Example code to directly multiply images via image*sample\n            # (uint8 only) -- apparently slower than LUT\n            # if per_channel_samples_i > 0.5:\n            #     result = []\n            #     image = image.astype(np.float32)\n            #     mul_samples_i = mul_samples_i.astype(np.float32)\n            #     for c, mul in enumerate(mul_samples_i[0:nb_channels]):\n            #         result.append(\n            #             np.clip(\n            #                 image[..., c:c+1] * mul, 0, 255\n            #             ).astype(np.uint8))\n            #     images[i] = np.concatenate(result, axis=2)\n            # else:\n            #     images[i] = np.clip(\n            #         image.astype(np.float32)\n            #         * mul_samples_i[0].astype(np.float32),\n            #         0, 255\n            #     ).astype(np.uint8)\n\n            if per_channel_samples_i > 0.5:\n                mul = mul_samples_i[0:nb_channels]\n            else:\n                # the if/else here catches the case of the channel axis being 0\n                mul = mul_samples_i[0] if mul_samples_i.size > 0 else []\n            batch.images[i] = multiply_scalar_(image, mul)\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.mul, self.per_channel]\n\n\n# TODO merge with Multiply\nclass MultiplyElementwise(meta.Augmenter):\n    \"\"\"\n    Multiply image pixels with values that are pixelwise randomly sampled.\n\n    While the ``Multiply`` Augmenter uses a constant multiplier *per\n    image* (and optionally channel), this augmenter samples the multipliers\n    to use per image and *per pixel* (and optionally per channel).\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.arithmetic.multiply_elementwise`.\n\n    Parameters\n    ----------\n    mul : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        The value with which to multiply pixel values in the image.\n\n            * If a number, then that value will always be used.\n            * If a tuple ``(a, b)``, then a value from the interval ``[a, b]``\n              will be sampled per image and pixel.\n            * If a list, then a random value will be sampled from that list\n              per image and pixel.\n            * If a ``StochasticParameter``, then that parameter will be used to\n              sample a new value per image and pixel.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MultiplyElementwise(2.0)\n\n    Multiply all images by a factor of ``2.0``, making them significantly\n    bighter.\n\n    >>> aug = iaa.MultiplyElementwise((0.5, 1.5))\n\n    Samples per image and pixel uniformly a value from the interval\n    ``[0.5, 1.5]`` and multiplies the pixel with that value.\n\n    >>> aug = iaa.MultiplyElementwise((0.5, 1.5), per_channel=True)\n\n    Samples per image and pixel *and channel* uniformly a value from the\n    interval ``[0.5, 1.5]`` and multiplies the pixel with that value. Therefore,\n    used multipliers may differ between channels of the same pixel.\n\n    >>> aug = iaa.MultiplyElementwise((0.5, 1.5), per_channel=0.5)\n\n    Identical to the previous example, but the `per_channel` feature is only\n    active for 50 percent of all images.\n\n    \"\"\"\n\n    def __init__(self, mul=(0.8, 1.2), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(MultiplyElementwise, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.mul = iap.handle_continuous_param(\n            mul, \"mul\",\n            value_range=None, tuple_to_uniform=True, list_to_choice=True)\n        self.per_channel = iap.handle_probability_param(per_channel,\n                                                        \"per_channel\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        nb_images = len(images)\n        rss = random_state.duplicate(1+nb_images)\n        per_channel_samples = self.per_channel.draw_samples(\n            (nb_images,), random_state=rss[0])\n        is_mul_binomial = isinstance(self.mul, iap.Binomial) or (\n            isinstance(self.mul, iap.FromLowerResolution)\n            and isinstance(self.mul.other_param, iap.Binomial)\n        )\n\n        gen = enumerate(zip(images, per_channel_samples, rss[1:]))\n        for i, (image, per_channel_samples_i, rs) in gen:\n            height, width, nb_channels = image.shape\n            sample_shape = (height,\n                            width,\n                            nb_channels if per_channel_samples_i > 0.5 else 1)\n            mul = self.mul.draw_samples(sample_shape, random_state=rs)\n            # TODO let Binomial return boolean mask directly instead of [0, 1]\n            #      integers?\n\n            # hack to improve performance for Dropout and CoarseDropout\n            # converts mul samples to mask if mul is binomial\n            if mul.dtype.kind != \"b\" and is_mul_binomial:\n                mul = mul.astype(bool, copy=False)\n\n            batch.images[i] = multiply_elementwise_(image, mul)\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.mul, self.per_channel]\n\n\n# Added in 0.4.0.\nclass _CutoutSamples(object):\n    # Added in 0.4.0.\n    def __init__(self, nb_iterations, pos_x, pos_y, size_h, size_w, squared,\n                 fill_mode, cval, fill_per_channel):\n        self.nb_iterations = nb_iterations\n        self.pos_x = pos_x\n        self.pos_y = pos_y\n        self.size_h = size_h\n        self.size_w = size_w\n        self.squared = squared\n        self.fill_mode = fill_mode\n        self.cval = cval\n        self.fill_per_channel = fill_per_channel\n\n\nclass Cutout(meta.Augmenter):\n    \"\"\"Fill one or more rectangular areas in an image using a fill mode.\n\n    See paper \"Improved Regularization of Convolutional Neural Networks with\n    Cutout\" by DeVries and Taylor.\n\n    In contrast to the paper, this implementation also supports replacing\n    image sub-areas with gaussian noise, random intensities or random RGB\n    colors. It also supports non-squared areas. While the paper uses\n    absolute pixel values for the size and position, this implementation\n    uses relative values, which seems more appropriate for mixed-size\n    datasets. The position parameter furthermore allows more flexibility, e.g.\n    gaussian distributions around the center.\n\n    .. note::\n\n        This augmenter affects only image data. Other datatypes (e.g.\n        segmentation map pixels or keypoints within the filled areas)\n        are not affected.\n\n    .. note::\n\n        Gaussian fill mode will assume that float input images contain values\n        in the interval ``[0.0, 1.0]`` and hence sample values from a\n        gaussian within that interval, i.e. from ``N(0.5, std=0.5/3)``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.arithmetic.cutout_`.\n\n    Parameters\n    ----------\n    nb_iterations : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        How many rectangular areas to fill.\n\n            * If ``int``: Exactly that many areas will be filled on all images.\n            * If ``tuple`` ``(a, b)``: A value from the interval ``[a, b]``\n              will be sampled per image.\n            * If ``list``: A random value will be sampled from that ``list``\n              per image.\n            * If ``StochasticParameter``: That parameter will be used to\n              sample ``(B,)`` values per batch of ``B`` images.\n\n    position : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n        Defines the position of each area to fill.\n        Analogous to the definition in e.g.\n        :class:`~imgaug.augmenters.size.CropToFixedSize`.\n        Usually, ``uniform`` (anywhere in the image) or ``normal`` (anywhere\n        in the image with preference around the center) are sane values.\n\n    size : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        The size of the rectangle to fill as a fraction of the corresponding\n        image size, i.e. with value range ``[0.0, 1.0]``. The size is sampled\n        independently per image axis.\n\n            * If ``number``: Exactly that size is always used.\n            * If ``tuple`` ``(a, b)``: A value from the interval ``[a, b]``\n              will be sampled per area and axis.\n            * If ``list``: A random value will be sampled from that ``list``\n              per area and axis.\n            * If ``StochasticParameter``: That parameter will be used to\n              sample ``(N, 2)`` values per batch, where ``N`` is the total\n              number of areas to fill within the whole batch.\n\n    squared : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to generate only squared areas cutout areas or allow\n        rectangular ones. If this evaluates to a true-like value, the\n        first value from `size` will be converted to absolute pixels and used\n        for both axes.\n\n        If this value is a float ``p``, then for ``p`` percent of all areas\n        to be filled `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    fill_mode : str or list of str or imgaug.parameters.StochasticParameter, optional\n        Mode to use in order to fill areas. Corresponds to ``mode`` parameter\n        in some other augmenters. Valid strings for the mode are:\n\n            * ``contant``: Fill each area with a single value.\n            * ``gaussian``: Fill each area with gaussian noise.\n\n        Valid datatypes are:\n\n            * If ``str``: Exactly that mode will alaways be used.\n            * If ``list``: A random value will be sampled from that ``list``\n              per area.\n            * If ``StochasticParameter``: That parameter will be used to\n              sample ``(N,)`` values per batch, where ``N`` is the total number\n              of areas to fill within the whole batch.\n\n    cval : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        The value to use (i.e. the color) to fill areas if `fill_mode` is\n        ```constant``.\n\n            * If ``number``: Exactly that value is used for all areas\n              and channels.\n            * If ``tuple`` ``(a, b)``: A value from the interval ``[a, b]``\n              will be sampled per area (and channel if ``per_channel=True``).\n            * If ``list``: A random value will be sampled from that ``list``\n              per area (and channel if ``per_channel=True``).\n            * If ``StochasticParameter``: That parameter will be used to\n              sample ``(N, Cmax)`` values per batch, where ``N`` is the total\n              number of areas to fill within the whole batch and ``Cmax``\n              is the maximum number of channels in any image (usually ``3``).\n              If ``per_channel=False``, only the first value of the second\n              axis is used.\n\n    fill_per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to fill each area in a channelwise fashion (``True``) or\n        not (``False``).\n        The behaviour per fill mode is:\n\n            * ``constant``: Whether to fill all channels with the same value\n              (i.e, grayscale) or different values (i.e. usually RGB color).\n            * ``gaussian``: Whether to sample once from a gaussian and use the\n              values for all channels (i.e. grayscale) or to sample\n              channelwise (i.e. RGB colors)\n\n        If this value is a float ``p``, then for ``p`` percent of all areas\n        to be filled `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    deterministic : bool, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.bit_generator.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Cutout(nb_iterations=2)\n\n    Fill per image two random areas, by default with grayish pixels.\n\n    >>> aug = iaa.Cutout(nb_iterations=(1, 5), size=0.2, squared=False)\n\n    Fill per image between one and five areas, each having ``20%``\n    of the corresponding size of the height and width (for non-square\n    images this results in non-square areas to be filled).\n\n    >>> aug = iaa.Cutout(fill_mode=\"constant\", cval=255)\n\n    Fill all areas with white pixels.\n\n    >>> aug = iaa.Cutout(fill_mode=\"constant\", cval=(0, 255),\n    >>>                  fill_per_channel=0.5)\n\n    Fill ``50%`` of all areas with a random intensity value between\n    ``0`` and ``256``. Fill the other ``50%`` of all areas with\n    random colors.\n\n    >>> aug = iaa.Cutout(fill_mode=\"gaussian\", fill_per_channel=True)\n\n    Fill areas with gaussian channelwise noise (i.e. usually RGB).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 nb_iterations=1,\n                 position=\"uniform\",\n                 size=0.2,\n                 squared=True,\n                 fill_mode=\"constant\",\n                 cval=128,\n                 fill_per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        from .size import _handle_position_parameter  # TODO move to iap\n        from .geometric import _handle_cval_arg  # TODO move to iap\n\n        super(Cutout, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.nb_iterations = iap.handle_discrete_param(\n            nb_iterations, \"nb_iterations\", value_range=(0, None),\n            tuple_to_uniform=True, list_to_choice=True, allow_floats=False)\n        self.position = _handle_position_parameter(position)\n        self.size = iap.handle_continuous_param(\n            size, \"size\", value_range=(0.0, 1.0+1e-4),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.squared = iap.handle_probability_param(squared, \"squared\")\n        self.fill_mode = self._handle_fill_mode_param(fill_mode)\n        self.cval = _handle_cval_arg(cval)\n        self.fill_per_channel = iap.handle_probability_param(\n            fill_per_channel, \"fill_per_channel\")\n\n    # Added in 0.4.0.\n    @classmethod\n    def _handle_fill_mode_param(cls, fill_mode):\n        if ia.is_string(fill_mode):\n            assert fill_mode in _CUTOUT_FILL_MODES, (\n                \"Expected 'fill_mode' to be one of: %s. Got %s.\" % (\n                    str(list(_CUTOUT_FILL_MODES.keys())), fill_mode))\n            return iap.Deterministic(fill_mode)\n        if isinstance(fill_mode, iap.StochasticParameter):\n            return fill_mode\n        assert ia.is_iterable(fill_mode), (\n            \"Expected 'fill_mode' to be a string, \"\n            \"StochasticParameter or list of strings. Got type %s.\" % (\n                type(fill_mode).__name__))\n        return iap.Choice(fill_mode)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        samples = self._draw_samples(batch.images, random_state)\n\n        # map from xyhw to xyxy (both relative coords)\n        cutout_height_half = samples.size_h / 2\n        cutout_width_half = samples.size_w / 2\n        x1_rel = samples.pos_x - cutout_width_half\n        y1_rel = samples.pos_y - cutout_height_half\n        x2_rel = samples.pos_x + cutout_width_half\n        y2_rel = samples.pos_y + cutout_height_half\n\n        nb_iterations_sum = 0\n        gen = enumerate(zip(batch.images, samples.nb_iterations))\n        for i, (image, nb_iterations) in gen:\n            start = nb_iterations_sum\n            end = start + nb_iterations\n\n            height, width = image.shape[0:2]\n\n            # map from relative xyxy to absolute xyxy coords\n            batch.images[i] = self._augment_image_by_samples(\n                image,\n                x1_rel[start:end] * width,\n                y1_rel[start:end] * height,\n                x2_rel[start:end] * width,\n                y2_rel[start:end] * height,\n                samples.squared[start:end],\n                samples.fill_mode[start:end],\n                samples.cval[start:end],\n                samples.fill_per_channel[start:end],\n                random_state)\n\n            nb_iterations_sum += nb_iterations\n\n        return batch\n\n    # Added in 0.4.0.\n    def _draw_samples(self, images, random_state):\n        rngs = random_state.duplicate(8)\n        nb_rows = len(images)\n        nb_channels_max = meta.estimate_max_number_of_channels(images)\n\n        nb_iterations = self.nb_iterations.draw_samples(\n            (nb_rows,), random_state=rngs[0])\n        nb_dropped_areas = int(np.sum(nb_iterations))\n\n        if isinstance(self.position, tuple):\n            pos_x = self.position[0].draw_samples((nb_dropped_areas,),\n                                                  random_state=rngs[1])\n            pos_y = self.position[1].draw_samples((nb_dropped_areas,),\n                                                  random_state=rngs[2])\n        else:\n            pos = self.position.draw_samples((nb_dropped_areas, 2),\n                                             random_state=rngs[1])\n            pos_x = pos[:, 0]\n            pos_y = pos[:, 1]\n\n        size = self.size.draw_samples((nb_dropped_areas, 2),\n                                      random_state=rngs[3])\n        squared = self.squared.draw_samples((nb_dropped_areas,),\n                                            random_state=rngs[4])\n        fill_mode = self.fill_mode.draw_samples(\n            (nb_dropped_areas,), random_state=rngs[5])\n\n        cval = self.cval.draw_samples((nb_dropped_areas, nb_channels_max),\n                                      random_state=rngs[6])\n\n        fill_per_channel = self.fill_per_channel.draw_samples(\n            (nb_dropped_areas,), random_state=rngs[7])\n\n        return _CutoutSamples(\n            nb_iterations=nb_iterations,\n            pos_x=pos_x,\n            pos_y=pos_y,\n            size_h=size[:, 0],\n            size_w=size[:, 1],\n            squared=squared,\n            fill_mode=fill_mode,\n            cval=cval,\n            fill_per_channel=fill_per_channel\n        )\n\n    # Added in 0.4.0.\n    @classmethod\n    def _augment_image_by_samples(cls, image, x1, y1, x2, y2, squared,\n                                  fill_mode, cval, fill_per_channel,\n                                  random_state):\n        for i, x1_i in enumerate(x1):\n            x2_i = x2[i]\n            if squared[i] >= 0.5:\n                height_h = (y2[i] - y1[i]) / 2\n                x_center = x1_i + (x2_i - x1_i) / 2\n                x1_i = x_center - height_h\n                x2_i = x_center + height_h\n\n            image = cutout_(\n                image,\n                x1=x1_i,\n                y1=y1[i],\n                x2=x2_i,\n                y2=y2[i],\n                fill_mode=fill_mode[i],\n                cval=cval[i],\n                fill_per_channel=fill_per_channel[i],\n                seed=random_state)\n        return image\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.nb_iterations, self.position, self.size, self.squared,\n                self.fill_mode, self.cval, self.fill_per_channel]\n\n\n# TODO verify that (a, b) still leads to a p being sampled per image and not\n#      per batch\nclass Dropout(MultiplyElementwise):\n    \"\"\"\n    Set a fraction of pixels in images to zero.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.MultiplyElementwise`.\n\n    Parameters\n    ----------\n    p : float or tuple of float or imgaug.parameters.StochasticParameter, optional\n        The probability of any pixel being dropped (i.e. to set it to zero).\n\n            * If a float, then that value will be used for all images. A value\n              of ``1.0`` would mean that all pixels will be dropped\n              and ``0.0`` that no pixels will be dropped. A value of ``0.05``\n              corresponds to ``5`` percent of all pixels being dropped.\n            * If a tuple ``(a, b)``, then a value ``p`` will be sampled from\n              the interval ``[a, b]`` per image and be used as the pixel's\n              dropout probability.\n            * If a list, then a value will be sampled from that list per\n              batch and used as the probability.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              determine per pixel whether it should be *kept* (sampled value\n              of ``>0.5``) or shouldn't be kept (sampled value of ``<=0.5``).\n              If you instead want to provide the probability as a stochastic\n              parameter, you can usually do ``imgaug.parameters.Binomial(1-p)``\n              to convert parameter `p` to a 0/1 representation.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Dropout(0.02)\n\n    Drops ``2`` percent of all pixels.\n\n    >>> aug = iaa.Dropout((0.0, 0.05))\n\n    Drops in each image a random fraction of all pixels, where the fraction\n    is uniformly sampled from the interval ``[0.0, 0.05]``.\n\n    >>> aug = iaa.Dropout(0.02, per_channel=True)\n\n    Drops ``2`` percent of all pixels in a channelwise fashion, i.e. it is\n    unlikely for any pixel to have all channels set to zero (black pixels).\n\n    >>> aug = iaa.Dropout(0.02, per_channel=0.5)\n\n    Identical to the previous example, but the `per_channel` feature is only\n    active for ``50`` percent of all images.\n\n    \"\"\"\n    def __init__(self, p=(0.0, 0.05), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        p_param = _handle_dropout_probability_param(p, \"p\")\n\n        super(Dropout, self).__init__(\n            p_param,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# Added in 0.4.0.\ndef _handle_dropout_probability_param(p, name):\n    if ia.is_single_number(p):\n        p_param = iap.Binomial(1 - p)\n    elif isinstance(p, tuple):\n        assert len(p) == 2, (\n            \"Expected `%s` to be given as a tuple containing exactly 2 values, \"\n            \"got %d values.\" % (name, len(p),))\n        assert p[0] < p[1], (\n            \"Expected `%s` to be given as a tuple containing exactly 2 values \"\n            \"(a, b) with a < b. Got %.4f and %.4f.\" % (name, p[0], p[1]))\n        assert 0 <= p[0] <= 1.0 and 0 <= p[1] <= 1.0, (\n            \"Expected `%s` given as tuple to only contain values in the \"\n            \"interval [0.0, 1.0], got %.4f and %.4f.\" % (name, p[0], p[1]))\n\n        p_param = iap.Binomial(iap.Uniform(1 - p[1], 1 - p[0]))\n    elif ia.is_iterable(p):\n        assert all([ia.is_single_number(v) for v in p]), (\n            \"Expected iterable parameter '%s' to only contain numbers, \"\n            \"got %s.\" % (name, [type(v) for v in p],))\n        assert all([0 <= p_i <= 1.0 for p_i in p]), (\n            \"Expected iterable parameter '%s' to only contain probabilities \"\n            \"in the interval [0.0, 1.0], got values %s.\" % (\n                name, \", \".join([\"%.4f\" % (p_i,) for p_i in p])))\n        p_param = iap.Binomial(1 - iap.Choice(p))\n    elif isinstance(p, iap.StochasticParameter):\n        p_param = p\n    else:\n        raise Exception(\n            \"Expected `%s` to be float or int or tuple (<number>, <number>) \"\n            \"or StochasticParameter, got type '%s'.\" % (\n                name, type(p).__name__,))\n\n    return p_param\n\n\n# TODO invert size_px and size_percent so that larger values denote larger\n#      areas being dropped instead of the opposite way around\nclass CoarseDropout(MultiplyElementwise):\n    \"\"\"\n    Set rectangular areas within images to zero.\n\n    In contrast to ``Dropout``, these areas can have larger sizes.\n    (E.g. you might end up with three large black rectangles in an image.)\n    Note that the current implementation leads to correlated sizes,\n    so if e.g. there is any thin and high rectangle that is dropped, there is\n    a high likelihood that all other dropped areas are also thin and high.\n\n    This method is implemented by generating the dropout mask at a\n    lower resolution (than the image has) and then upsampling the mask\n    before dropping the pixels.\n\n    This augmenter is similar to Cutout. Usually, cutout is defined as an\n    operation that drops exactly one rectangle from an image, while here\n    ``CoarseDropout`` can drop multiple rectangles (with some correlation\n    between the sizes of these rectangles).\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.MultiplyElementwise`.\n\n    Parameters\n    ----------\n    p : float or tuple of float or imgaug.parameters.StochasticParameter, optional\n        The probability of any pixel being dropped (i.e. set to zero) in\n        the lower-resolution dropout mask.\n\n            * If a float, then that value will be used for all pixels. A value\n              of ``1.0`` would mean, that all pixels will be dropped. A value\n              of ``0.0`` would lead to no pixels being dropped.\n            * If a tuple ``(a, b)``, then a value ``p`` will be sampled from\n              the interval ``[a, b]`` per image and be used as the dropout\n              probability.\n            * If a list, then a value will be sampled from that list per\n              batch and used as the probability.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              determine per pixel whether it should be *kept* (sampled value\n              of ``>0.5``) or shouldn't be kept (sampled value of ``<=0.5``).\n              If you instead want to provide the probability as a stochastic\n              parameter, you can usually do ``imgaug.parameters.Binomial(1-p)``\n              to convert parameter `p` to a 0/1 representation.\n\n    size_px : None or int or tuple of int or imgaug.parameters.StochasticParameter, optional\n        The size of the lower resolution image from which to sample the dropout\n        mask in absolute pixel dimensions.\n        Note that this means that *lower* values of this parameter lead to\n        *larger* areas being dropped (as any pixel in the lower resolution\n        image will correspond to a larger area at the original resolution).\n\n            * If ``None`` then `size_percent` must be set.\n            * If an integer, then that size will always be used for both height\n              and width. E.g. a value of ``3`` would lead to a ``3x3`` mask,\n              which is then upsampled to ``HxW``, where ``H`` is the image size\n              and ``W`` the image width.\n            * If a tuple ``(a, b)``, then two values ``M``, ``N`` will be\n              sampled from the discrete interval ``[a..b]``. The dropout mask\n              will then be generated at size ``MxN`` and upsampled to ``HxW``.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              determine the sizes. It is expected to be discrete.\n\n    size_percent : None or float or tuple of float or imgaug.parameters.StochasticParameter, optional\n        The size of the lower resolution image from which to sample the dropout\n        mask *in percent* of the input image.\n        Note that this means that *lower* values of this parameter lead to\n        *larger* areas being dropped (as any pixel in the lower resolution\n        image will correspond to a larger area at the original resolution).\n\n            * If ``None`` then `size_px` must be set.\n            * If a float, then that value will always be used as the percentage\n              of the height and width (relative to the original size). E.g. for\n              value ``p``, the mask will be sampled from ``(p*H)x(p*W)`` and\n              later upsampled to ``HxW``.\n            * If a tuple ``(a, b)``, then two values ``m``, ``n`` will be\n              sampled from the interval ``(a, b)`` and used as the size\n              fractions, i.e the mask size will be ``(m*H)x(n*W)``.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              sample the percentage values. It is expected to be continuous.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    min_size : int, optional\n        Minimum height and width of the low resolution mask. If\n        `size_percent` or `size_px` leads to a lower value than this,\n        `min_size` will be used instead. This should never have a value of\n        less than ``2``, otherwise one may end up with a ``1x1`` low resolution\n        mask, leading easily to the whole image being dropped.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CoarseDropout(0.02, size_percent=0.5)\n\n    Drops ``2`` percent of all pixels on a lower-resolution image that has\n    ``50`` percent of the original image's size, leading to dropped areas that\n    have roughly ``2x2`` pixels size.\n\n    >>> aug = iaa.CoarseDropout((0.0, 0.05), size_percent=(0.05, 0.5))\n\n    Generates a dropout mask at ``5`` to ``50`` percent of each input image's\n    size. In that mask, ``0`` to ``5`` percent of all pixels are marked as\n    being dropped. The mask is afterwards projected to the input image's\n    size to apply the actual dropout operation.\n\n    >>> aug = iaa.CoarseDropout((0.0, 0.05), size_px=(2, 16))\n\n    Same as the previous example, but the lower resolution image has ``2`` to\n    ``16`` pixels size. On images of e.g. ``224x224` pixels in size this would\n    lead to fairly large areas being dropped (height/width of ``224/2`` to\n    ``224/16``).\n\n    >>> aug = iaa.CoarseDropout(0.02, size_percent=0.5, per_channel=True)\n\n    Drops ``2`` percent of all pixels at ``50`` percent resolution (``2x2``\n    sizes) in a channel-wise fashion, i.e. it is unlikely for any pixel to\n    have all channels set to zero (black pixels).\n\n    >>> aug = iaa.CoarseDropout(0.02, size_percent=0.5, per_channel=0.5)\n\n    Same as the previous example, but the `per_channel` feature is only active\n    for ``50`` percent of all images.\n\n    \"\"\"\n    def __init__(self, p=(0.02, 0.1), size_px=None, size_percent=None,\n                 per_channel=False, min_size=3,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        p_param = _handle_dropout_probability_param(p, \"p\")\n\n        if size_px is not None:\n            p_param = iap.FromLowerResolution(other_param=p_param,\n                                              size_px=size_px,\n                                              min_size=min_size)\n        elif size_percent is not None:\n            p_param = iap.FromLowerResolution(other_param=p_param,\n                                              size_percent=size_percent,\n                                              min_size=min_size)\n        else:\n            # default if neither size_px nor size_percent is provided\n            # is size_px=(3, 8)\n            p_param = iap.FromLowerResolution(other_param=p_param,\n                                              size_px=(3, 8),\n                                              min_size=min_size)\n\n        super(CoarseDropout, self).__init__(\n            p_param,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Dropout2d(meta.Augmenter):\n    \"\"\"Drop random channels from images.\n\n    For image data, dropped channels will be filled with zeros.\n\n    .. note::\n\n        This augmenter may also set the arrays of heatmaps and segmentation\n        maps to zero and remove all coordinate-based data (e.g. it removes\n        all bounding boxes on images that were filled with zeros).\n        It does so if and only if *all* channels of an image are dropped.\n        If ``nb_keep_channels >= 1`` then that never happens.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    p : float or tuple of float or imgaug.parameters.StochasticParameter, optional\n        The probability of any channel to be dropped (i.e. set to zero).\n\n            * If a ``float``, then that value will be used for all channels.\n              A value of ``1.0`` would mean, that all channels will be dropped.\n              A value of ``0.0`` would lead to no channels being dropped.\n            * If a tuple ``(a, b)``, then a value ``p`` will be sampled from\n              the interval ``[a, b)`` per batch and be used as the dropout\n              probability.\n            * If a list, then a value will be sampled from that list per\n              batch and used as the probability.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              determine per channel whether it should be *kept* (sampled value\n              of ``>=0.5``) or shouldn't be kept (sampled value of ``<0.5``).\n              If you instead want to provide the probability as a stochastic\n              parameter, you can usually do ``imgaug.parameters.Binomial(1-p)``\n              to convert parameter `p` to a 0/1 representation.\n\n    nb_keep_channels : int\n        Minimum number of channels to keep unaltered in all images.\n        E.g. a value of ``1`` means that at least one channel in every image\n        will not be dropped, even if ``p=1.0``. Set to ``0`` to allow dropping\n        all channels.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Dropout2d(p=0.5)\n\n    Create a dropout augmenter that drops on average half of all image\n    channels. Dropped channels will be filled with zeros. At least one\n    channel is kept unaltered in each image (default setting).\n\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Dropout2d(p=0.5, nb_keep_channels=0)\n\n    Create a dropout augmenter that drops on average half of all image\n    channels *and* may drop *all* channels in an image (i.e. images may\n    contain nothing but zeros).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, p=0.1, nb_keep_channels=1,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Dropout2d, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.p = _handle_dropout_probability_param(p, \"p\")\n        self.nb_keep_channels = max(nb_keep_channels, 0)\n\n        self._drop_images = True\n        self._drop_heatmaps = True\n        self._drop_segmentation_maps = True\n        self._drop_keypoints = True\n        self._drop_bounding_boxes = True\n        self._drop_polygons = True\n        self._drop_line_strings = True\n\n        self._heatmaps_cval = 0.0\n        self._segmentation_maps_cval = 0\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        imagewise_drop_channel_ids, all_dropped_ids = self._draw_samples(\n            batch, random_state)\n\n        if batch.images is not None:\n            for image, drop_ids in zip(batch.images,\n                                       imagewise_drop_channel_ids):\n                image[:, :, drop_ids] = 0\n\n        # Skip the non-image data steps below if we won't modify non-image\n        # anyways. Minor performance improvement.\n        if len(all_dropped_ids) == 0:\n            return batch\n\n        if batch.heatmaps is not None and self._drop_heatmaps:\n            cval = self._heatmaps_cval\n            for drop_idx in all_dropped_ids:\n                batch.heatmaps[drop_idx].arr_0to1[...] = cval\n\n        if batch.segmentation_maps is not None and self._drop_segmentation_maps:\n            cval = self._segmentation_maps_cval\n            for drop_idx in all_dropped_ids:\n                batch.segmentation_maps[drop_idx].arr[...] = cval\n\n        for attr_name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                          \"line_strings\"]:\n            do_drop = getattr(self, \"_drop_%s\" % (attr_name,))\n            attr_value = getattr(batch, attr_name)\n            if attr_value is not None and do_drop:\n                for drop_idx in all_dropped_ids:\n                    # same as e.g.:\n                    #     batch.bounding_boxes[drop_idx].bounding_boxes = []\n                    setattr(attr_value[drop_idx], attr_name, [])\n\n        return batch\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        # maybe noteworthy here that the channel axis can have size 0,\n        # e.g. (5, 5, 0)\n        shapes = batch.get_rowwise_shapes()\n        shapes = [shape\n                  if len(shape) >= 2\n                  else tuple(list(shape) + [1])\n                  for shape in shapes]\n        imagewise_channels = np.array([\n            shape[2] for shape in shapes\n        ], dtype=np.int32)\n\n        # channelwise drop value over all images (float <0.5 = drop channel)\n        p_samples = self.p.draw_samples((int(np.sum(imagewise_channels)),),\n                                        random_state=random_state)\n\n        # We map the flat p_samples array to an imagewise one,\n        # convert the mask to channel-ids to drop and remove channel ids if\n        # there are more to be dropped than are allowed to be dropped (see\n        # nb_keep_channels).\n        # We also track all_dropped_ids, which contains the ids of examples\n        # (not channel ids!) where all channels were dropped.\n        imagewise_channels_to_drop = []\n        all_dropped_ids = []\n        channel_idx = 0\n        for i, nb_channels in enumerate(imagewise_channels):\n            p_samples_i = p_samples[channel_idx:channel_idx+nb_channels]\n\n            drop_ids = np.nonzero(p_samples_i < 0.5)[0]\n            nb_dropable = max(nb_channels - self.nb_keep_channels, 0)\n            if len(drop_ids) > nb_dropable:\n                random_state.shuffle(drop_ids)\n                drop_ids = drop_ids[:nb_dropable]\n            imagewise_channels_to_drop.append(drop_ids)\n\n            all_dropped = (len(drop_ids) == nb_channels)\n            if all_dropped:\n                all_dropped_ids.append(i)\n\n            channel_idx += nb_channels\n\n        return imagewise_channels_to_drop, all_dropped_ids\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.p, self.nb_keep_channels]\n\n\nclass TotalDropout(meta.Augmenter):\n    \"\"\"Drop all channels of a defined fraction of all images.\n\n    For image data, all components of dropped images will be filled with zeros.\n\n    .. note::\n\n        This augmenter also sets the arrays of heatmaps and segmentation\n        maps to zero and removes all coordinate-based data (e.g. it removes\n        all bounding boxes on images that were filled with zeros).\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    p : float or tuple of float or imgaug.parameters.StochasticParameter, optional\n        The probability of an image to be filled with zeros.\n\n            * If ``float``: The value will be used for all images.\n              A value of ``1.0`` would mean that all images will be set to zero.\n              A value of ``0.0`` would lead to no images being set to zero.\n            * If ``tuple`` ``(a, b)``: A value ``p`` will be sampled from\n              the interval ``[a, b)`` per batch and be used as the dropout\n              probability.\n            * If a list, then a value will be sampled from that list per\n              batch and used as the probability.\n            * If ``StochasticParameter``: The parameter will be used to\n              determine per image whether it should be *kept* (sampled value\n              of ``>=0.5``) or shouldn't be kept (sampled value of ``<0.5``).\n              If you instead want to provide the probability as a stochastic\n              parameter, you can usually do ``imgaug.parameters.Binomial(1-p)``\n              to convert parameter `p` to a 0/1 representation.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.TotalDropout(1.0)\n\n    Create an augmenter that sets *all* components of all images to zero.\n\n    >>> aug = iaa.TotalDropout(0.5)\n\n    Create an augmenter that sets *all* components of ``50%`` of all images to\n    zero.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, p=1,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(TotalDropout, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.p = _handle_dropout_probability_param(p, \"p\")\n\n        self._drop_images = True\n        self._drop_heatmaps = True\n        self._drop_segmentation_maps = True\n        self._drop_keypoints = True\n        self._drop_bounding_boxes = True\n        self._drop_polygons = True\n        self._drop_line_strings = True\n\n        self._heatmaps_cval = 0.0\n        self._segmentation_maps_cval = 0\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        drop_mask = self._draw_samples(batch, random_state)\n        drop_ids = None\n\n        if batch.images is not None and self._drop_images:\n            if ia.is_np_array(batch.images):\n                batch.images[drop_mask, ...] = 0\n            else:\n                drop_ids = self._generate_drop_ids_once(drop_mask, drop_ids)\n                for drop_idx in drop_ids:\n                    batch.images[drop_idx][...] = 0\n\n        if batch.heatmaps is not None and self._drop_heatmaps:\n            drop_ids = self._generate_drop_ids_once(drop_mask, drop_ids)\n            cval = self._heatmaps_cval\n            for drop_idx in drop_ids:\n                batch.heatmaps[drop_idx].arr_0to1[...] = cval\n\n        if batch.segmentation_maps is not None and self._drop_segmentation_maps:\n            drop_ids = self._generate_drop_ids_once(drop_mask, drop_ids)\n            cval = self._segmentation_maps_cval\n            for drop_idx in drop_ids:\n                batch.segmentation_maps[drop_idx].arr[...] = cval\n\n        for attr_name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                          \"line_strings\"]:\n            do_drop = getattr(self, \"_drop_%s\" % (attr_name,))\n            attr_value = getattr(batch, attr_name)\n            if attr_value is not None and do_drop:\n                drop_ids = self._generate_drop_ids_once(drop_mask, drop_ids)\n                for drop_idx in drop_ids:\n                    # same as e.g.:\n                    #     batch.bounding_boxes[drop_idx].bounding_boxes = []\n                    setattr(attr_value[drop_idx], attr_name, [])\n\n        return batch\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        p = self.p.draw_samples((batch.nb_rows,), random_state=random_state)\n        drop_mask = (p < 0.5)\n        return drop_mask\n\n    # Added in 0.4.0.\n    @classmethod\n    def _generate_drop_ids_once(cls, drop_mask, drop_ids):\n        if drop_ids is None:\n            drop_ids = np.nonzero(drop_mask)[0]\n        return drop_ids\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.p]\n\n\nclass ReplaceElementwise(meta.Augmenter):\n    \"\"\"\n    Replace pixels in an image with new values.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.arithmetic.replace_elementwise_`.\n\n    Parameters\n    ----------\n    mask : float or tuple of float or list of float or imgaug.parameters.StochasticParameter\n        Mask that indicates the pixels that are supposed to be replaced.\n        The mask will be binarized using a threshold of ``0.5``. A value\n        of ``1`` then indicates a pixel that is supposed to be replaced.\n\n            * If this is a float, then that value will be used as the\n              probability of being a ``1`` in the mask (sampled per image and\n              pixel) and hence being replaced.\n            * If a tuple ``(a, b)``, then the probability will be uniformly\n              sampled per image from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image and pixel.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              sample a mask per image.\n\n    replacement : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        The replacement to use at all locations that are marked as ``1`` in\n        the mask.\n\n            * If this is a number, then that value will always be used as the\n              replacement.\n            * If a tuple ``(a, b)``, then the replacement will be sampled\n              uniformly per image and pixel from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image and pixel.\n            * If a ``StochasticParameter``, then this parameter will be used\n              sample replacement values per image and pixel.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = ReplaceElementwise(0.05, [0, 255])\n\n    Replaces ``5`` percent of all pixels in each image by either ``0``\n    or ``255``.\n\n    >>> import imgaug.augmenters as iaa\n    >>> aug = ReplaceElementwise(0.1, [0, 255], per_channel=0.5)\n\n    For ``50%`` of all images, replace ``10%`` of all pixels with either the\n    value ``0`` or the value ``255`` (same as in the previous example). For\n    the other ``50%`` of all images, replace *channelwise* ``10%`` of all\n    pixels with either the value ``0`` or the value ``255``. So, it will be\n    very rare for each pixel to have all channels replaced by ``255`` or\n    ``0``.\n\n    >>> import imgaug.augmenters as iaa\n    >>> import imgaug.parameters as iap\n    >>> aug = ReplaceElementwise(0.1, iap.Normal(128, 0.4*128), per_channel=0.5)\n\n    Replace ``10%`` of all pixels by gaussian noise centered around ``128``.\n    Both the replacement mask and the gaussian noise are sampled channelwise\n    for ``50%`` of all images.\n\n    >>> import imgaug.augmenters as iaa\n    >>> import imgaug.parameters as iap\n    >>> aug = ReplaceElementwise(\n    >>>     iap.FromLowerResolution(iap.Binomial(0.1), size_px=8),\n    >>>     iap.Normal(128, 0.4*128),\n    >>>     per_channel=0.5)\n\n    Replace ``10%`` of all pixels by gaussian noise centered around ``128``.\n    Sample the replacement mask at a lower resolution (``8x8`` pixels) and\n    upscale it to the image size, resulting in coarse areas being replaced by\n    gaussian noise.\n\n    \"\"\"\n\n    def __init__(self, mask, replacement, per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ReplaceElementwise, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.mask = iap.handle_probability_param(\n            mask, \"mask\", tuple_to_uniform=True, list_to_choice=True)\n        self.replacement = iap.handle_continuous_param(replacement,\n                                                       \"replacement\")\n        self.per_channel = iap.handle_probability_param(per_channel,\n                                                        \"per_channel\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        nb_images = len(images)\n        rss = random_state.duplicate(1+2*nb_images)\n        per_channel_samples = self.per_channel.draw_samples(\n            (nb_images,), random_state=rss[0])\n\n        gen = enumerate(zip(images, per_channel_samples, rss[1::2], rss[2::2]))\n        for i, (image, per_channel_i, rs_mask, rs_replacement) in gen:\n            height, width, nb_channels = image.shape\n            sampling_shape = (height,\n                              width,\n                              nb_channels if per_channel_i > 0.5 else 1)\n            mask_samples = self.mask.draw_samples(sampling_shape,\n                                                  random_state=rs_mask)\n\n            # TODO add separate per_channels for mask and replacement\n            # TODO add test that replacement with per_channel=False is not\n            #      sampled per channel\n            if per_channel_i <= 0.5:\n                nb_channels = image.shape[-1]\n                replacement_samples = self.replacement.draw_samples(\n                    (int(np.sum(mask_samples[:, :, 0])),),\n                    random_state=rs_replacement)\n                # important here to use repeat instead of tile. repeat\n                # converts e.g. [0, 1, 2] to [0, 0, 1, 1, 2, 2], while tile\n                # leads to [0, 1, 2, 0, 1, 2]. The assignment below iterates\n                # over each channel and pixel simultaneously, *not* first\n                # over all pixels of channel 0, then all pixels in\n                # channel 1, ...\n                replacement_samples = np.repeat(replacement_samples,\n                                                nb_channels)\n            else:\n                replacement_samples = self.replacement.draw_samples(\n                    (int(np.sum(mask_samples)),), random_state=rs_replacement)\n\n            batch.images[i] = replace_elementwise_(image, mask_samples,\n                                                   replacement_samples)\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.mask, self.replacement, self.per_channel]\n\n\nclass SaltAndPepper(ReplaceElementwise):\n    \"\"\"\n    Replace pixels in images with salt/pepper noise (white/black-ish colors).\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.ReplaceElementwise`.\n\n    Parameters\n    ----------\n    p : float or tuple of float or list of float or imgaug.parameters.StochasticParameter, optional\n        Probability of replacing a pixel to salt/pepper noise.\n\n            * If a float, then that value will always be used as the\n              probability.\n            * If a tuple ``(a, b)``, then a probability will be sampled\n              uniformly per image from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a image-sized mask will be\n              sampled from that parameter per image. Any value ``>0.5`` in\n              that mask will be replaced with salt and pepper noise.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.SaltAndPepper(0.05)\n\n    Replace ``5%`` of all pixels with salt and pepper noise.\n\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.SaltAndPepper(0.05, per_channel=True)\n\n    Replace *channelwise* ``5%`` of all pixels with salt and pepper\n    noise.\n\n    \"\"\"\n    def __init__(self, p=(0.0, 0.03), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(SaltAndPepper, self).__init__(\n            mask=p,\n            replacement=iap.Beta(0.5, 0.5) * 255,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass ImpulseNoise(SaltAndPepper):\n    \"\"\"\n    Add impulse noise to images.\n\n    This is identical to ``SaltAndPepper``, except that `per_channel` is\n    always set to ``True``.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.SaltAndPepper`.\n\n    Parameters\n    ----------\n    p : float or tuple of float or list of float or imgaug.parameters.StochasticParameter, optional\n        Probability of replacing a pixel to impulse noise.\n\n            * If a float, then that value will always be used as the\n              probability.\n            * If a tuple ``(a, b)``, then a probability will be sampled\n              uniformly per image from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a image-sized mask will be\n              sampled from that parameter per image. Any value ``>0.5`` in\n              that mask will be replaced with impulse noise noise.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.ImpulseNoise(0.1)\n\n    Replace ``10%`` of all pixels with impulse noise.\n\n    \"\"\"\n\n    def __init__(self, p=(0.0, 0.03),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ImpulseNoise, self).__init__(\n            p=p,\n            per_channel=True,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass CoarseSaltAndPepper(ReplaceElementwise):\n    \"\"\"\n    Replace rectangular areas in images with white/black-ish pixel noise.\n\n    This adds salt and pepper noise (noisy white-ish and black-ish pixels) to\n    rectangular areas within the image. Note that this means that within these\n    rectangular areas the color varies instead of each rectangle having only\n    one color.\n\n    See also the similar ``CoarseDropout``.\n\n    TODO replace dtype support with uint8 only, because replacement is\n         geared towards that value range\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.ReplaceElementwise`.\n\n    Parameters\n    ----------\n    p : float or tuple of float or list of float or imgaug.parameters.StochasticParameter, optional\n        Probability of changing a pixel to salt/pepper noise.\n\n            * If a float, then that value will always be used as the\n              probability.\n            * If a tuple ``(a, b)``, then a probability will be sampled\n              uniformly per image from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a lower-resolution mask will\n              be sampled from that parameter per image. Any value ``>0.5`` in\n              that mask will denote a spatial location that is to be replaced\n              by salt and pepper noise.\n\n    size_px : int or tuple of int or imgaug.parameters.StochasticParameter, optional\n        The size of the lower resolution image from which to sample the\n        replacement mask in absolute pixel dimensions.\n        Note that this means that *lower* values of this parameter lead to\n        *larger* areas being replaced (as any pixel in the lower resolution\n        image will correspond to a larger area at the original resolution).\n\n            * If ``None`` then `size_percent` must be set.\n            * If an integer, then that size will always be used for both height\n              and width. E.g. a value of ``3`` would lead to a ``3x3`` mask,\n              which is then upsampled to ``HxW``, where ``H`` is the image size\n              and ``W`` the image width.\n            * If a tuple ``(a, b)``, then two values ``M``, ``N`` will be\n              sampled from the discrete interval ``[a..b]``. The mask\n              will then be generated at size ``MxN`` and upsampled to ``HxW``.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              determine the sizes. It is expected to be discrete.\n\n    size_percent : float or tuple of float or imgaug.parameters.StochasticParameter, optional\n        The size of the lower resolution image from which to sample the\n        replacement mask *in percent* of the input image.\n        Note that this means that *lower* values of this parameter lead to\n        *larger* areas being replaced (as any pixel in the lower resolution\n        image will correspond to a larger area at the original resolution).\n\n            * If ``None`` then `size_px` must be set.\n            * If a float, then that value will always be used as the percentage\n              of the height and width (relative to the original size). E.g. for\n              value ``p``, the mask will be sampled from ``(p*H)x(p*W)`` and\n              later upsampled to ``HxW``.\n            * If a tuple ``(a, b)``, then two values ``m``, ``n`` will be\n              sampled from the interval ``(a, b)`` and used as the size\n              fractions, i.e the mask size will be ``(m*H)x(n*W)``.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              sample the percentage values. It is expected to be continuous.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    min_size : int, optional\n        Minimum height and width of the low resolution mask. If\n        `size_percent` or `size_px` leads to a lower value than this,\n        `min_size` will be used instead. This should never have a value of\n        less than ``2``, otherwise one may end up with a ``1x1`` low resolution\n        mask, leading easily to the whole image being replaced.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CoarseSaltAndPepper(0.05, size_percent=(0.01, 0.1))\n\n    Marks ``5%`` of all pixels in a mask to be replaced by salt/pepper\n    noise. The mask has ``1%`` to ``10%`` the size of the input image.\n    The mask is then upscaled to the input image size, leading to large\n    rectangular areas being marked as to be replaced. These areas are then\n    replaced in the input image by salt/pepper noise.\n\n    >>> aug = iaa.CoarseSaltAndPepper(0.05, size_px=(4, 16))\n\n    Same as in the previous example, but the replacement mask before upscaling\n    has a size between ``4x4`` and ``16x16`` pixels (the axis sizes are sampled\n    independently, i.e. the mask may be rectangular).\n\n    >>> aug = iaa.CoarseSaltAndPepper(\n    >>>    0.05, size_percent=(0.01, 0.1), per_channel=True)\n\n    Same as in the first example, but mask and replacement are each sampled\n    independently per image channel.\n\n    \"\"\"\n\n    def __init__(self, p=(0.02, 0.1), size_px=None, size_percent=None,\n                 per_channel=False, min_size=3,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        mask = iap.handle_probability_param(\n            p, \"p\", tuple_to_uniform=True, list_to_choice=True)\n\n        if size_px is not None:\n            mask_low = iap.FromLowerResolution(\n                other_param=mask, size_px=size_px, min_size=min_size)\n        elif size_percent is not None:\n            mask_low = iap.FromLowerResolution(\n                other_param=mask, size_percent=size_percent, min_size=min_size)\n        else:\n            mask_low = iap.FromLowerResolution(\n                other_param=mask, size_px=(3, 8), min_size=min_size)\n\n        replacement = iap.Beta(0.5, 0.5) * 255\n\n        super(CoarseSaltAndPepper, self).__init__(\n            mask=mask_low,\n            replacement=replacement,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Salt(ReplaceElementwise):\n    \"\"\"\n    Replace pixels in images with salt noise, i.e. white-ish pixels.\n\n    This augmenter is similar to ``SaltAndPepper``, but adds no pepper noise to\n    images.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.ReplaceElementwise`.\n\n    Parameters\n    ----------\n    p : float or tuple of float or list of float or imgaug.parameters.StochasticParameter, optional\n        Probability of replacing a pixel with salt noise.\n\n            * If a float, then that value will always be used as the\n              probability.\n            * If a tuple ``(a, b)``, then a probability will be sampled\n              uniformly per image from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a image-sized mask will be\n              sampled from that parameter per image. Any value ``>0.5`` in\n              that mask will be replaced with salt noise.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Salt(0.05)\n\n    Replace ``5%`` of all pixels with salt noise (white-ish colors).\n\n    \"\"\"\n\n    def __init__(self, p=(0.0, 0.03), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        replacement01 = iap.ForceSign(\n            iap.Beta(0.5, 0.5) - 0.5,\n            positive=True,\n            mode=\"invert\"\n        ) + 0.5\n        # FIXME max replacement seems to essentially never exceed 254\n        replacement = replacement01 * 255\n\n        super(Salt, self).__init__(\n            mask=p,\n            replacement=replacement,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass CoarseSalt(ReplaceElementwise):\n    \"\"\"\n    Replace rectangular areas in images with white-ish pixel noise.\n\n    See also the similar ``CoarseSaltAndPepper``.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.ReplaceElementwise`.\n\n    Parameters\n    ----------\n    p : float or tuple of float or list of float or imgaug.parameters.StochasticParameter, optional\n        Probability of changing a pixel to salt noise.\n\n            * If a float, then that value will always be used as the\n              probability.\n            * If a tuple ``(a, b)``, then a probability will be sampled\n              uniformly per image from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a lower-resolution mask will\n              be sampled from that parameter per image. Any value ``>0.5`` in\n              that mask will denote a spatial location that is to be replaced\n              by salt noise.\n\n    size_px : int or tuple of int or imgaug.parameters.StochasticParameter, optional\n        The size of the lower resolution image from which to sample the\n        replacement mask in absolute pixel dimensions.\n        Note that this means that *lower* values of this parameter lead to\n        *larger* areas being replaced (as any pixel in the lower resolution\n        image will correspond to a larger area at the original resolution).\n\n            * If ``None`` then `size_percent` must be set.\n            * If an integer, then that size will always be used for both height\n              and width. E.g. a value of ``3`` would lead to a ``3x3`` mask,\n              which is then upsampled to ``HxW``, where ``H`` is the image size\n              and ``W`` the image width.\n            * If a tuple ``(a, b)``, then two values ``M``, ``N`` will be\n              sampled from the discrete interval ``[a..b]``. The mask\n              will then be generated at size ``MxN`` and upsampled to ``HxW``.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              determine the sizes. It is expected to be discrete.\n\n    size_percent : float or tuple of float or imgaug.parameters.StochasticParameter, optional\n        The size of the lower resolution image from which to sample the\n        replacement mask *in percent* of the input image.\n        Note that this means that *lower* values of this parameter lead to\n        *larger* areas being replaced (as any pixel in the lower resolution\n        image will correspond to a larger area at the original resolution).\n\n            * If ``None`` then `size_px` must be set.\n            * If a float, then that value will always be used as the percentage\n              of the height and width (relative to the original size). E.g. for\n              value ``p``, the mask will be sampled from ``(p*H)x(p*W)`` and\n              later upsampled to ``HxW``.\n            * If a tuple ``(a, b)``, then two values ``m``, ``n`` will be\n              sampled from the interval ``(a, b)`` and used as the size\n              fractions, i.e the mask size will be ``(m*H)x(n*W)``.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              sample the percentage values. It is expected to be continuous.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    min_size : int, optional\n        Minimum height and width of the low resolution mask. If\n        `size_percent` or `size_px` leads to a lower value than this,\n        `min_size` will be used instead. This should never have a value of\n        less than ``2``, otherwise one may end up with a ``1x1`` low resolution\n        mask, leading easily to the whole image being replaced.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CoarseSalt(0.05, size_percent=(0.01, 0.1))\n\n    Mark ``5%`` of all pixels in a mask to be replaced by salt\n    noise. The mask has ``1%`` to ``10%`` the size of the input image.\n    The mask is then upscaled to the input image size, leading to large\n    rectangular areas being marked as to be replaced. These areas are then\n    replaced in the input image by salt noise.\n\n    \"\"\"\n\n    def __init__(self, p=(0.02, 0.1), size_px=None, size_percent=None,\n                 per_channel=False, min_size=3,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        mask = iap.handle_probability_param(\n            p, \"p\", tuple_to_uniform=True, list_to_choice=True)\n\n        if size_px is not None:\n            mask_low = iap.FromLowerResolution(\n                other_param=mask, size_px=size_px, min_size=min_size)\n        elif size_percent is not None:\n            mask_low = iap.FromLowerResolution(\n                other_param=mask, size_percent=size_percent, min_size=min_size)\n        else:\n            mask_low = iap.FromLowerResolution(\n                other_param=mask, size_px=(3, 8), min_size=min_size)\n\n        replacement01 = iap.ForceSign(\n            iap.Beta(0.5, 0.5) - 0.5,\n            positive=True,\n            mode=\"invert\"\n        ) + 0.5\n        replacement = replacement01 * 255\n\n        super(CoarseSalt, self).__init__(\n            mask=mask_low,\n            replacement=replacement,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Pepper(ReplaceElementwise):\n    \"\"\"\n    Replace pixels in images with pepper noise, i.e. black-ish pixels.\n\n    This augmenter is similar to ``SaltAndPepper``, but adds no salt noise to\n    images.\n\n    This augmenter is similar to ``Dropout``, but slower and the black pixels\n    are not uniformly black.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.ReplaceElementwise`.\n\n    Parameters\n    ----------\n    p : float or tuple of float or list of float or imgaug.parameters.StochasticParameter, optional\n        Probability of replacing a pixel with pepper noise.\n\n            * If a float, then that value will always be used as the\n              probability.\n            * If a tuple ``(a, b)``, then a probability will be sampled\n              uniformly per image from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a image-sized mask will be\n              sampled from that parameter per image. Any value ``>0.5`` in\n              that mask will be replaced with pepper noise.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Pepper(0.05)\n\n    Replace ``5%`` of all pixels with pepper noise (black-ish colors).\n\n    \"\"\"\n\n    def __init__(self, p=(0.0, 0.05), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        replacement01 = iap.ForceSign(\n            iap.Beta(0.5, 0.5) - 0.5,\n            positive=False,\n            mode=\"invert\"\n        ) + 0.5\n        replacement = replacement01 * 255\n\n        super(Pepper, self).__init__(\n            mask=p,\n            replacement=replacement,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass CoarsePepper(ReplaceElementwise):\n    \"\"\"\n    Replace rectangular areas in images with black-ish pixel noise.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.ReplaceElementwise`.\n\n    Parameters\n    ----------\n    p : float or tuple of float or list of float or imgaug.parameters.StochasticParameter, optional\n        Probability of changing a pixel to pepper noise.\n\n            * If a float, then that value will always be used as the\n              probability.\n            * If a tuple ``(a, b)``, then a probability will be sampled\n              uniformly per image from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a lower-resolution mask will\n              be sampled from that parameter per image. Any value ``>0.5`` in\n              that mask will denote a spatial location that is to be replaced\n              by pepper noise.\n\n    size_px : int or tuple of int or imgaug.parameters.StochasticParameter, optional\n        The size of the lower resolution image from which to sample the\n        replacement mask in absolute pixel dimensions.\n        Note that this means that *lower* values of this parameter lead to\n        *larger* areas being replaced (as any pixel in the lower resolution\n        image will correspond to a larger area at the original resolution).\n\n            * If ``None`` then `size_percent` must be set.\n            * If an integer, then that size will always be used for both height\n              and width. E.g. a value of ``3`` would lead to a ``3x3`` mask,\n              which is then upsampled to ``HxW``, where ``H`` is the image size\n              and ``W`` the image width.\n            * If a tuple ``(a, b)``, then two values ``M``, ``N`` will be\n              sampled from the discrete interval ``[a..b]``. The mask\n              will then be generated at size ``MxN`` and upsampled to ``HxW``.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              determine the sizes. It is expected to be discrete.\n\n    size_percent : float or tuple of float or imgaug.parameters.StochasticParameter, optional\n        The size of the lower resolution image from which to sample the\n        replacement mask *in percent* of the input image.\n        Note that this means that *lower* values of this parameter lead to\n        *larger* areas being replaced (as any pixel in the lower resolution\n        image will correspond to a larger area at the original resolution).\n\n            * If ``None`` then `size_px` must be set.\n            * If a float, then that value will always be used as the percentage\n              of the height and width (relative to the original size). E.g. for\n              value ``p``, the mask will be sampled from ``(p*H)x(p*W)`` and\n              later upsampled to ``HxW``.\n            * If a tuple ``(a, b)``, then two values ``m``, ``n`` will be\n              sampled from the interval ``(a, b)`` and used as the size\n              fractions, i.e the mask size will be ``(m*H)x(n*W)``.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              sample the percentage values. It is expected to be continuous.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    min_size : int, optional\n        Minimum size of the low resolution mask, both width and height. If\n        `size_percent` or `size_px` leads to a lower value than this, `min_size`\n        will be used instead. This should never have a value of less than 2,\n        otherwise one may end up with a ``1x1`` low resolution mask, leading\n        easily to the whole image being replaced.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CoarsePepper(0.05, size_percent=(0.01, 0.1))\n\n    Mark ``5%`` of all pixels in a mask to be replaced by pepper\n    noise. The mask has ``1%`` to ``10%`` the size of the input image.\n    The mask is then upscaled to the input image size, leading to large\n    rectangular areas being marked as to be replaced. These areas are then\n    replaced in the input image by pepper noise.\n\n    \"\"\"\n\n    def __init__(self, p=(0.02, 0.1), size_px=None, size_percent=None,\n                 per_channel=False, min_size=3,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        mask = iap.handle_probability_param(\n            p, \"p\", tuple_to_uniform=True, list_to_choice=True)\n\n        if size_px is not None:\n            mask_low = iap.FromLowerResolution(\n                other_param=mask, size_px=size_px, min_size=min_size)\n        elif size_percent is not None:\n            mask_low = iap.FromLowerResolution(\n                other_param=mask, size_percent=size_percent, min_size=min_size)\n        else:\n            mask_low = iap.FromLowerResolution(\n                other_param=mask, size_px=(3, 8), min_size=min_size)\n\n        replacement01 = iap.ForceSign(\n            iap.Beta(0.5, 0.5) - 0.5,\n            positive=False,\n            mode=\"invert\"\n        ) + 0.5\n        replacement = replacement01 * 255\n\n        super(CoarsePepper, self).__init__(\n            mask=mask_low,\n            replacement=replacement,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Invert(meta.Augmenter):\n    \"\"\"\n    Invert all values in images, e.g. turn ``5`` into ``255-5=250``.\n\n    For the standard value range of 0-255 it converts ``0`` to ``255``,\n    ``255`` to ``0`` and ``10`` to ``(255-10)=245``.\n    Let ``M`` be the maximum value possible, ``m`` the minimum value possible,\n    ``v`` a value. Then the distance of ``v`` to ``m`` is ``d=abs(v-m)`` and\n    the new value is given by ``v'=M-d``.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.arithmetic.invert_`.\n\n    Parameters\n    ----------\n    p : float or imgaug.parameters.StochasticParameter, optional\n        The probability of an image to be inverted.\n\n            * If a float, then that probability will be used for all images,\n              i.e. `p` percent of all images will be inverted.\n            * If a ``StochasticParameter``, then that parameter will be queried\n              per image and is expected to return values in the interval\n              ``[0.0, 1.0]``, where values ``>0.5`` mean that the image\n              is supposed to be inverted. Recommended to be some form of\n              ``imgaug.parameters.Binomial``.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    min_value : None or number, optional\n        Minimum of the value range of input images, e.g. ``0`` for ``uint8``\n        images. If set to ``None``, the value will be automatically derived\n        from the image's dtype.\n\n    max_value : None or number, optional\n        Maximum of the value range of input images, e.g. ``255`` for ``uint8``\n        images. If set to ``None``, the value will be automatically derived\n        from the image's dtype.\n\n    threshold : None or number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        A threshold to use in order to invert only numbers above or below\n        the threshold. If ``None`` no thresholding will be used.\n\n            * If ``None``: No thresholding will be used.\n            * If ``number``: The value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A value will be uniformly sampled per\n              image from the interval ``[a, b)``.\n            * If ``list``: A random value will be picked from the list per\n              image.\n            * If ``StochasticParameter``: Per batch of size ``N``, the\n              parameter will be queried once to return ``(N,)`` samples.\n\n    invert_above_threshold : bool or float or imgaug.parameters.StochasticParameter, optional\n        If ``True``, only values ``>=threshold`` will be inverted.\n        Otherwise, only values ``<threshold`` will be inverted.\n        If a ``number``, then expected to be in the interval ``[0.0, 1.0]`` and\n        denoting an imagewise probability. If a ``StochasticParameter`` then\n        ``(N,)`` values will be sampled from the parameter per batch of size\n        ``N`` and interpreted as ``True`` if ``>0.5``.\n        If `threshold` is ``None`` this parameter has no effect.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Invert(0.1)\n\n    Inverts the colors in ``10`` percent of all images.\n\n    >>> aug = iaa.Invert(0.1, per_channel=True)\n\n    Inverts the colors in ``10`` percent of all image channels. This may or\n    may not lead to multiple channels in an image being inverted.\n\n    >>> aug = iaa.Invert(0.1, per_channel=0.5)\n\n    Identical to the previous example, but the `per_channel` feature is only\n    active for 50 percent of all images.\n\n    \"\"\"\n    # when no custom min/max are chosen, all bool, uint, int and float dtypes\n    # should be invertable (float tested only up to 64bit)\n    # when chosing custom min/max:\n    # - bool makes no sense, not allowed\n    # - int and float must be increased in resolution if custom min/max values\n    #   are chosen, hence they are limited to 32 bit and below\n    # - uint64 is converted by numpy's clip to float64, hence loss of accuracy\n    # - float16 seems to not be perfectly accurate, but still ok-ish -- was\n    #   off by 10 for center value of range (float 16 min, 16), where float\n    #   16 min is around -65500\n    ALLOW_DTYPES_CUSTOM_MINMAX = [\n        np.dtype(dt) for dt in [\n            np.uint8, np.uint16, np.uint32,\n            np.int8, np.int16, np.int32,\n            np.float16, np.float32\n        ]\n    ]\n\n    def __init__(self, p=1, per_channel=False, min_value=None, max_value=None,\n                 threshold=None, invert_above_threshold=0.5,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Invert, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        # TODO allow list and tuple for p\n        self.p = iap.handle_probability_param(p, \"p\")\n        self.per_channel = iap.handle_probability_param(per_channel,\n                                                        \"per_channel\")\n        self.min_value = min_value\n        self.max_value = max_value\n\n        if threshold is None:\n            self.threshold = None\n        else:\n            self.threshold = iap.handle_continuous_param(\n                threshold, \"threshold\", value_range=None, tuple_to_uniform=True,\n                list_to_choice=True)\n        self.invert_above_threshold = iap.handle_probability_param(\n            invert_above_threshold, \"invert_above_threshold\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        samples = self._draw_samples(batch, random_state)\n\n        for i, image in enumerate(batch.images):\n            if 0 in image.shape:\n                continue\n\n            kwargs = {\n                \"min_value\": samples.min_value[i],\n                \"max_value\": samples.max_value[i],\n                \"threshold\": samples.threshold[i],\n                \"invert_above_threshold\": samples.invert_above_threshold[i]\n            }\n\n            if samples.per_channel[i]:\n                nb_channels = image.shape[2]\n                mask = samples.p[i, :nb_channels]\n                image[..., mask] = invert_(image[..., mask], **kwargs)\n            else:\n                if samples.p[i, 0]:\n                    image[:, :, :] = invert_(image, **kwargs)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        nb_images = batch.nb_rows\n        nb_channels = meta.estimate_max_number_of_channels(batch.images)\n        p = self.p.draw_samples((nb_images, nb_channels),\n                                random_state=random_state)\n        p = (p > 0.5)\n        per_channel = self.per_channel.draw_samples((nb_images,),\n                                                    random_state=random_state)\n        per_channel = (per_channel > 0.5)\n        min_value = [self.min_value] * nb_images\n        max_value = [self.max_value] * nb_images\n\n        if self.threshold is None:\n            threshold = [None] * nb_images\n        else:\n            threshold = self.threshold.draw_samples(\n                (nb_images,), random_state=random_state)\n\n        invert_above_threshold = self.invert_above_threshold.draw_samples(\n            (nb_images,), random_state=random_state)\n        invert_above_threshold = (invert_above_threshold > 0.5)\n\n        return _InvertSamples(\n            p=p,\n            per_channel=per_channel,\n            min_value=min_value,\n            max_value=max_value,\n            threshold=threshold,\n            invert_above_threshold=invert_above_threshold\n        )\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.p, self.per_channel, self.min_value, self.max_value,\n                self.threshold, self.invert_above_threshold]\n\n\n# Added in 0.4.0.\nclass _InvertSamples(object):\n    # Added in 0.4.0.\n    def __init__(self, p, per_channel, min_value, max_value,\n                 threshold, invert_above_threshold):\n        self.p = p\n        self.per_channel = per_channel\n        self.min_value = min_value\n        self.max_value = max_value\n        self.threshold = threshold\n        self.invert_above_threshold = invert_above_threshold\n\n\nclass Solarize(Invert):\n    \"\"\"Invert all pixel values above a threshold.\n\n    This is the same as :class:`Invert`, but sets a default threshold around\n    ``128`` (+/- 64, decided per image) and default `invert_above_threshold`\n    to ``True`` (i.e. only values above the threshold will be inverted).\n\n    See :class:`Invert` for more details.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.arithmetic.Invert`.\n\n    Parameters\n    ----------\n    p : float or imgaug.parameters.StochasticParameter\n        See :class:`Invert`.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        See :class:`Invert`.\n\n    min_value : None or number, optional\n        See :class:`Invert`.\n\n    max_value : None or number, optional\n        See :class:`Invert`.\n\n    threshold : None or number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`Invert`.\n\n    invert_above_threshold : bool or float or imgaug.parameters.StochasticParameter, optional\n        See :class:`Invert`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Solarize(0.5, threshold=(32, 128))\n\n    Invert the colors in ``50`` percent of all images for pixels with a\n    value between ``32`` and ``128`` or more. The threshold is sampled once\n    per image. The thresholding operation happens per channel.\n\n    \"\"\"\n    def __init__(self, p=1, per_channel=False, min_value=None, max_value=None,\n                 threshold=(128-64, 128+64), invert_above_threshold=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Solarize, self).__init__(\n            p=p, per_channel=per_channel,\n            min_value=min_value, max_value=max_value,\n            threshold=threshold, invert_above_threshold=invert_above_threshold,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO remove from examples\n@ia.deprecated(\"imgaug.contrast.LinearContrast\")\ndef ContrastNormalization(alpha=1.0, per_channel=False,\n                          seed=None, name=None,\n                          random_state=\"deprecated\",\n                          deterministic=\"deprecated\"):\n    \"\"\"\n    Change the contrast of images.\n\n    dtype support:\n\n        See ``imgaug.augmenters.contrast.LinearContrast``.\n\n    Deprecated since 0.3.0.\n\n    Parameters\n    ----------\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Strength of the contrast normalization. Higher values than 1.0\n        lead to higher contrast, lower values decrease the contrast.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value will be sampled per image\n              uniformly from the interval ``[a, b]`` and be used as the alpha\n              value.\n            * If a list, then a random value will be picked per image from\n              that list.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              sample the alpha value per image.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use (imagewise) the same sample(s) for all\n        channels (``False``) or to sample value(s) for each channel (``True``).\n        Setting this to ``True`` will therefore lead to different\n        transformations per image *and* channel, otherwise only per image.\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``.\n        If it is a ``StochasticParameter`` it is expected to produce samples\n        with values between ``0.0`` and ``1.0``, where values ``>0.5`` will\n        lead to per-channel behaviour (i.e. same as ``True``).\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> iaa.ContrastNormalization((0.5, 1.5))\n\n    Decreases oder improves contrast per image by a random factor between\n    ``0.5`` and ``1.5``. The factor ``0.5`` means that any difference from\n    the center value (i.e. 128) will be halved, leading to less contrast.\n\n    >>> iaa.ContrastNormalization((0.5, 1.5), per_channel=0.5)\n\n    Same as before, but for 50 percent of all images the normalization is done\n    independently per channel (i.e. factors can vary per channel for the same\n    image). In the other 50 percent of all images, the factor is the same for\n    all channels.\n\n    \"\"\"\n    # pylint: disable=invalid-name\n    # placed here to avoid cyclic dependency\n    from . import contrast as contrast_lib\n    return contrast_lib.LinearContrast(\n        alpha=alpha, per_channel=per_channel,\n        seed=seed, name=name, random_state=random_state, deterministic=deterministic)\n\n\n# TODO try adding per channel somehow\nclass JpegCompression(meta.Augmenter):\n    \"\"\"\n    Degrade the quality of images by JPEG-compressing them.\n\n    During JPEG compression, high frequency components (e.g. edges) are removed.\n    With low compression (strength) only the highest frequency components are\n    removed, while very high compression (strength) will lead to only the\n    lowest frequency components \"surviving\". This lowers the image quality.\n    For more details, see https://en.wikipedia.org/wiki/Compression_artifact.\n\n    Note that this augmenter still returns images as numpy arrays (i.e. saves\n    the images with JPEG compression and then reloads them into arrays). It\n    does not return the raw JPEG file content.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.arithmetic.compress_jpeg`.\n\n    Parameters\n    ----------\n    compression : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Degree of compression used during JPEG compression within value range\n        ``[0, 100]``. Higher values denote stronger compression and will cause\n        low-frequency components to disappear. Note that JPEG's compression\n        strength is also often set as a *quality*, which is the inverse of this\n        parameter. Common choices for the *quality* setting are around 80 to 95,\n        depending on the image. This translates here to a *compression*\n        parameter of around 20 to 5.\n\n            * If a single number, then that value always will be used as the\n              compression.\n            * If a tuple ``(a, b)``, then the compression will be\n              a value sampled uniformly from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image and used as the compression.\n            * If a ``StochasticParameter``, then ``N`` samples will be drawn\n              from that parameter per ``N`` input images, each representing the\n              compression for the ``n``-th image.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.JpegCompression(compression=(70, 99))\n\n    Remove high frequency components in images via JPEG compression with\n    a *compression strength* between ``70`` and ``99`` (randomly and\n    uniformly sampled per image). This corresponds to a (very low) *quality*\n    setting of ``1`` to ``30``.\n\n    \"\"\"\n\n    def __init__(self, compression=(0, 100),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(JpegCompression, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        # will be converted to int during augmentation, which is why we allow\n        # floats here\n        self.compression = iap.handle_continuous_param(\n            compression, \"compression\",\n            value_range=(0, 100), tuple_to_uniform=True, list_to_choice=True)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        nb_images = len(images)\n        samples = self.compression.draw_samples((nb_images,),\n                                                random_state=random_state)\n\n        for i, (image, sample) in enumerate(zip(images, samples)):\n            batch.images[i] = compress_jpeg(image, int(sample))\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.compression]\n"
  },
  {
    "path": "imgaug/augmenters/artistic.py",
    "content": "\"\"\"\nAugmenters that apply artistic image filters.\n\nList of augmenters:\n\n    * :class:`Cartoon`\n\nAdded in 0.4.0.\n\n\"\"\"\n\nfrom __future__ import print_function, division, absolute_import\n\nimport numpy as np\nimport cv2\n\nfrom imgaug.imgaug import _normalize_cv2_input_arr_\nfrom . import meta\nfrom . import color as colorlib\nfrom .. import dtypes as iadt\nfrom .. import parameters as iap\n\n\ndef stylize_cartoon(image, blur_ksize=3, segmentation_size=1.0,\n                    saturation=2.0, edge_prevalence=1.0,\n                    suppress_edges=True,\n                    from_colorspace=colorlib.CSPACE_RGB):\n    \"\"\"Convert the style of an image to a more cartoonish one.\n\n    This function was primarily designed for images with a size of ``200``\n    to ``800`` pixels. Smaller or larger images may cause issues.\n\n    Note that the quality of the results can currently not compete with\n    learned style transfer, let alone human-made images. A lack of detected\n    edges or also too many detected edges are probably the most significant\n    drawbacks.\n\n    This method is loosely based on the one proposed in\n    https://stackoverflow.com/a/11614479/3760780\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        A ``(H,W,3) uint8`` image array.\n\n    blur_ksize : int, optional\n        Kernel size of the median blur filter applied initially to the input\n        image. Expected to be an odd value and ``>=0``. If an even value,\n        thn automatically increased to an odd one. If ``<=1``, no blur will\n        be applied.\n\n    segmentation_size : float, optional\n        Size multiplier to decrease/increase the base size of the initial\n        mean-shift segmentation of the image. Expected to be ``>=0``.\n        Note that the base size is increased by roughly a factor of two for\n        images with height and/or width ``>=400``.\n\n    edge_prevalence : float, optional\n        Multiplier for the prevalance of edges. Higher values lead to more\n        edges. Note that the default value of ``1.0`` is already fairly\n        conservative, so there is limit effect from lowerin it further.\n\n    saturation : float, optional\n        Multiplier for the saturation. Set to ``1.0`` to not change the\n        image's saturation.\n\n    suppress_edges : bool, optional\n        Whether to run edge suppression to remove blobs containing too many\n        or too few edge pixels.\n\n    from_colorspace : str, optional\n        The source colorspace. Use one of ``imgaug.augmenters.color.CSPACE_*``.\n        Defaults to ``RGB``.\n\n    Returns\n    -------\n    ndarray\n        Image in cartoonish style.\n\n    \"\"\"\n    iadt.allow_only_uint8({image.dtype})\n\n    assert image.ndim == 3 and image.shape[2] == 3, (\n        \"Expected to get a (H,W,C) image, got shape %s.\" % (image.shape,))\n\n    blur_ksize = max(int(np.round(blur_ksize)), 1)\n    segmentation_size = max(segmentation_size, 0.0)\n    saturation = max(saturation, 0.0)\n\n    is_small_image = max(image.shape[0:2]) < 400\n\n    image = _blur_median(image, blur_ksize)\n    image_seg = np.zeros_like(image)\n\n    if is_small_image:\n        spatial_window_radius = int(10 * segmentation_size)\n        color_window_radius = int(20 * segmentation_size)\n    else:\n        spatial_window_radius = int(15 * segmentation_size)\n        color_window_radius = int(40 * segmentation_size)\n\n    if segmentation_size <= 0:\n        image_seg = image\n    else:\n        cv2.pyrMeanShiftFiltering(_normalize_cv2_input_arr_(image),\n                                  sp=spatial_window_radius,\n                                  sr=color_window_radius,\n                                  dst=image_seg)\n\n    if is_small_image:\n        edges_raw = _find_edges_canny(image_seg,\n                                      edge_prevalence,\n                                      from_colorspace)\n    else:\n        edges_raw = _find_edges_laplacian(image_seg,\n                                          edge_prevalence,\n                                          from_colorspace)\n\n    edges = edges_raw\n\n    edges = ((edges > 100) * 255).astype(np.uint8)\n\n    if suppress_edges:\n        # Suppress dense 3x3 blobs full of detected edges. They are visually\n        # ugly.\n        edges = _suppress_edge_blobs(edges, 3, 8, inverse=False)\n\n        # Suppress spurious few-pixel edges (5x5 size with <=3 edge pixels).\n        edges = _suppress_edge_blobs(edges, 5, 3, inverse=True)\n\n    return _saturate(_blend_edges(image_seg, edges),\n                     saturation,\n                     from_colorspace)\n\n\n# Added in 0.4.0.\ndef _find_edges_canny(image, edge_multiplier, from_colorspace):\n    image_gray = colorlib.change_colorspace_(np.copy(image),\n                                             to_colorspace=colorlib.CSPACE_GRAY,\n                                             from_colorspace=from_colorspace)\n    image_gray = image_gray[..., 0]\n    thresh = min(int(200 * (1/edge_multiplier)), 254)\n    edges = cv2.Canny(_normalize_cv2_input_arr_(image_gray), thresh, thresh)\n    return edges\n\n\n# Added in 0.4.0.\ndef _find_edges_laplacian(image, edge_multiplier, from_colorspace):\n    image_gray = colorlib.change_colorspace_(np.copy(image),\n                                             to_colorspace=colorlib.CSPACE_GRAY,\n                                             from_colorspace=from_colorspace)\n    image_gray = image_gray[..., 0]\n    edges_f = cv2.Laplacian(_normalize_cv2_input_arr_(image_gray / 255.0),\n                            cv2.CV_64F)\n    edges_f = np.abs(edges_f)\n    edges_f = edges_f ** 2\n    vmax = np.percentile(edges_f, min(int(90 * (1/edge_multiplier)), 99))\n    edges_f = np.clip(edges_f, 0.0, vmax) / vmax\n\n    edges_uint8 = np.clip(np.round(edges_f * 255), 0, 255.0).astype(np.uint8)\n    edges_uint8 = _blur_median(edges_uint8, 3)\n    edges_uint8 = _threshold(edges_uint8, 50)\n    return edges_uint8\n\n\n# Added in 0.4.0.\ndef _blur_median(image, ksize):\n    if ksize % 2 == 0:\n        ksize += 1\n    if ksize <= 1:\n        return image\n    return cv2.medianBlur(_normalize_cv2_input_arr_(image), ksize)\n\n\n# Added in 0.4.0.\ndef _threshold(image, thresh):\n    mask = (image < thresh)\n    result = np.copy(image)\n    result[mask] = 0\n    return result\n\n\n# Added in 0.4.0.\ndef _suppress_edge_blobs(edges, size, thresh, inverse):\n    kernel = np.ones((size, size), dtype=np.float32)\n    counts = cv2.filter2D(_normalize_cv2_input_arr_(edges / 255.0), -1, kernel)\n\n    if inverse:\n        mask = (counts < thresh)\n    else:\n        mask = (counts >= thresh)\n\n    edges = np.copy(edges)\n    edges[mask] = 0\n    return edges\n\n\n# Added in 0.4.0.\ndef _saturate(image, factor, from_colorspace):\n    image = np.copy(image)\n    if np.isclose(factor, 1.0, atol=1e-2):\n        return image\n\n    hsv = colorlib.change_colorspace_(image,\n                                      to_colorspace=colorlib.CSPACE_HSV,\n                                      from_colorspace=from_colorspace)\n    sat = hsv[:, :, 1]\n    sat = np.clip(sat.astype(np.int32) * factor, 0, 255).astype(np.uint8)\n    hsv[:, :, 1] = sat\n    image_sat = colorlib.change_colorspace_(hsv,\n                                            to_colorspace=from_colorspace,\n                                            from_colorspace=colorlib.CSPACE_HSV)\n    return image_sat\n\n\n# Added in 0.4.0.\ndef _blend_edges(image, image_edges):\n    image_edges = 1.0 - (image_edges / 255.0)\n    image_edges = np.tile(image_edges[..., np.newaxis], (1, 1, 3))\n    return np.clip(\n        np.round(image * image_edges),\n        0.0, 255.0\n    ).astype(np.uint8)\n\n\nclass Cartoon(meta.Augmenter):\n    \"\"\"Convert the style of images to a more cartoonish one.\n\n    This augmenter was primarily designed for images with a size of ``200``\n    to ``800`` pixels. Smaller or larger images may cause issues.\n\n    Note that the quality of the results can currently not compete with\n    learned style transfer, let alone human-made images. A lack of detected\n    edges or also too many detected edges are probably the most significant\n    drawbacks.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.artistic.stylize_cartoon`.\n\n    Parameters\n    ----------\n    blur_ksize : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Median filter kernel size.\n        See :func:`~imgaug.augmenters.artistic.stylize_cartoon` for details.\n\n            * If ``number``: That value will be used for all images.\n            * If ``tuple (a, b) of number``: A random value will be uniformly\n              sampled per image from the interval ``[a, b)``.\n            * If ``list``: A random value will be picked per image from the\n              ``list``.\n            * If ``StochasticParameter``: The parameter will be queried once\n              per batch for ``(N,)`` values, where ``N`` is the number of\n              images.\n\n    segmentation_size : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Mean-Shift segmentation size multiplier.\n        See :func:`~imgaug.augmenters.artistic.stylize_cartoon` for details.\n\n            * If ``number``: That value will be used for all images.\n            * If ``tuple (a, b) of number``: A random value will be uniformly\n              sampled per image from the interval ``[a, b)``.\n            * If ``list``: A random value will be picked per image from the\n              ``list``.\n            * If ``StochasticParameter``: The parameter will be queried once\n              per batch for ``(N,)`` values, where ``N`` is the number of\n              images.\n\n    saturation : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Saturation multiplier.\n        See :func:`~imgaug.augmenters.artistic.stylize_cartoon` for details.\n\n            * If ``number``: That value will be used for all images.\n            * If ``tuple (a, b) of number``: A random value will be uniformly\n              sampled per image from the interval ``[a, b)``.\n            * If ``list``: A random value will be picked per image from the\n              ``list``.\n            * If ``StochasticParameter``: The parameter will be queried once\n              per batch for ``(N,)`` values, where ``N`` is the number of\n              images.\n\n    edge_prevalence : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Multiplier for the prevalence of edges.\n        See :func:`~imgaug.augmenters.artistic.stylize_cartoon` for details.\n\n            * If ``number``: That value will be used for all images.\n            * If ``tuple (a, b) of number``: A random value will be uniformly\n              sampled per image from the interval ``[a, b)``.\n            * If ``list``: A random value will be picked per image from the\n              ``list``.\n            * If ``StochasticParameter``: The parameter will be queried once\n              per batch for ``(N,)`` values, where ``N`` is the number of\n              images.\n\n    from_colorspace : str, optional\n        The source colorspace. Use one of ``imgaug.augmenters.color.CSPACE_*``.\n        Defaults to ``RGB``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Cartoon()\n\n    Create an example image, then apply a cartoon filter to it.\n\n    >>> aug = iaa.Cartoon(blur_ksize=3, segmentation_size=1.0,\n    >>>                   saturation=2.0, edge_prevalence=1.0)\n\n    Create a non-stochastic cartoon augmenter that produces decent-looking\n    images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, blur_ksize=(1, 5), segmentation_size=(0.8, 1.2),\n                 saturation=(1.5, 2.5), edge_prevalence=(0.9, 1.1),\n                 from_colorspace=colorlib.CSPACE_RGB,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Cartoon, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.blur_ksize = iap.handle_continuous_param(\n            blur_ksize, \"blur_ksize\", value_range=(0, None),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.segmentation_size = iap.handle_continuous_param(\n            segmentation_size, \"segmentation_size\", value_range=(0.0, None),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.saturation = iap.handle_continuous_param(\n            saturation, \"saturation\", value_range=(0.0, None),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.edge_prevalence = iap.handle_continuous_param(\n            edge_prevalence, \"edge_prevalence\", value_range=(0.0, None),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.from_colorspace = from_colorspace\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is not None:\n            samples = self._draw_samples(batch, random_state)\n            for i, image in enumerate(batch.images):\n                image[...] = stylize_cartoon(\n                    image,\n                    blur_ksize=samples[0][i],\n                    segmentation_size=samples[1][i],\n                    saturation=samples[2][i],\n                    edge_prevalence=samples[3][i],\n                    from_colorspace=self.from_colorspace\n                )\n        return batch\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        nb_rows = batch.nb_rows\n        return (\n            self.blur_ksize.draw_samples((nb_rows,), random_state=random_state),\n            self.segmentation_size.draw_samples((nb_rows,),\n                                                random_state=random_state),\n            self.saturation.draw_samples((nb_rows,), random_state=random_state),\n            self.edge_prevalence.draw_samples((nb_rows,),\n                                              random_state=random_state)\n        )\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.blur_ksize, self.segmentation_size, self.saturation,\n                self.edge_prevalence, self.from_colorspace]\n"
  },
  {
    "path": "imgaug/augmenters/base.py",
    "content": "\"\"\"Base classes and functions used by all/most augmenters.\n\nThis module is planned to contain :class:`imgaug.augmenters.meta.Augmenter`\nin the future.\n\nAdded in 0.4.0.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\nimport imgaug as ia\n\n\nclass SuspiciousMultiImageShapeWarning(UserWarning):\n    \"\"\"Warning multi-image inputs that look like a single image.\"\"\"\n\n\nclass SuspiciousSingleImageShapeWarning(UserWarning):\n    \"\"\"Warning for single-image inputs that look like multiple images.\"\"\"\n\n\ndef _warn_on_suspicious_multi_image_shapes(images):\n    if images is None:\n        return\n\n    # check if it looks like (H, W, C) instead of (N, H, W)\n    if ia.is_np_array(images):\n        if images.ndim == 3 and images.shape[-1] in [1, 3]:\n            ia.warn(\n                \"You provided a numpy array of shape %s as a \"\n                \"multi-image augmentation input, which was interpreted as \"\n                \"(N, H, W). The last dimension however has value 1 or \"\n                \"3, which indicates that you provided a single image \"\n                \"with shape (H, W, C) instead. If that is the case, \"\n                \"you should use e.g. augmenter(image=<your input>) or \"\n                \"augment_image(<your input>) -- note the singular 'image' \"\n                \"instead of 'imageS'. Otherwise your single input image \"\n                \"will be interpreted as multiple images of shape (H, W) \"\n                \"during augmentation.\" % (images.shape,),\n                category=SuspiciousMultiImageShapeWarning)\n\n\ndef _warn_on_suspicious_single_image_shape(image):\n    if image is None:\n        return\n\n    # Check if it looks like (N, H, W) instead of (H, W, C).\n    # We don't react to (1, 1, C) though, mostly because that is used in many\n    # unittests.\n    if image.ndim == 3 and image.shape[-1] >= 32 and image.shape[0:2] != (1, 1):\n        ia.warn(\n            \"You provided a numpy array of shape %s as a \"\n            \"single-image augmentation input, which was interpreted as \"\n            \"(H, W, C). The last dimension however has a size of >=32, \"\n            \"which indicates that you provided a multi-image array \"\n            \"with shape (N, H, W) instead. If that is the case, \"\n            \"you should use e.g. augmenter(imageS=<your input>) or \"\n            \"augment_imageS(<your input>). Otherwise your multi-image \"\n            \"input will be interpreted as a single image during \"\n            \"augmentation.\" % (image.shape,),\n            category=SuspiciousSingleImageShapeWarning)\n"
  },
  {
    "path": "imgaug/augmenters/blend.py",
    "content": "\"\"\"\nAugmenters that blend two images with each other.\n\nList of augmenters:\n\n    * :class:`BlendAlpha`\n    * :class:`BlendAlphaMask`\n    * :class:`BlendAlphaElementwise`\n    * :class:`BlendAlphaSimplexNoise`\n    * :class:`BlendAlphaFrequencyNoise`\n    * :class:`BlendAlphaSomeColors`\n    * :class:`BlendAlphaHorizontalLinearGradient`\n    * :class:`BlendAlphaVerticalLinearGradient`\n    * :class:`BlendAlphaSegMapClassIds`\n    * :class:`BlendAlphaBoundingBoxes`\n    * :class:`BlendAlphaRegularGrid`\n    * :class:`BlendAlphaCheckerboard`\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nfrom abc import ABCMeta, abstractmethod\n\nimport numpy as np\nimport six\nimport cv2\n\nimport imgaug as ia\nfrom imgaug.imgaug import _normalize_cv2_input_arr_\nfrom . import meta\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\nfrom .. import random as iarandom\nfrom ..augmentables import utils as augm_utils\n\n\ndef _split_1d_array_to_list(arr, sizes):\n    result = []\n    i = 0\n    for size in sizes:\n        result.append(arr[i:i+size])\n        i += size\n    return result\n\n\ndef blend_alpha(image_fg, image_bg, alpha, eps=1e-2):\n    \"\"\"\n    Blend two images using an alpha blending.\n\n    In alpha blending, the two images are naively mixed using a multiplier.\n    Let ``A`` be the foreground image and ``B`` the background image and\n    ``a`` is the alpha value. Each pixel intensity is then computed as\n    ``a * A_ij + (1-a) * B_ij``.\n\n    **Supported dtypes**:\n\n    See :func:`imgaug.augmenters.blend.blend_alpha_`.\n\n    Parameters\n    ----------\n    image_fg : (H,W,[C]) ndarray\n        Foreground image. Shape and dtype kind must match the one of the\n        background image.\n\n    image_bg : (H,W,[C]) ndarray\n        Background image. Shape and dtype kind must match the one of the\n        foreground image.\n\n    alpha : number or iterable of number or ndarray\n        The blending factor, between ``0.0`` and ``1.0``. Can be interpreted\n        as the opacity of the foreground image. Values around ``1.0`` result\n        in only the foreground image being visible. Values around ``0.0``\n        result in only the background image being visible. Multiple alphas\n        may be provided. In these cases, there must be exactly one alpha per\n        channel in the foreground/background image. Alternatively, for\n        ``(H,W,C)`` images, either one ``(H,W)`` array or an ``(H,W,C)``\n        array of alphas may be provided, denoting the elementwise alpha value.\n\n    eps : number, optional\n        Controls when an alpha is to be interpreted as exactly ``1.0`` or\n        exactly ``0.0``, resulting in only the foreground/background being\n        visible and skipping the actual computation.\n\n    Returns\n    -------\n    image_blend : (H,W,C) ndarray\n        Blend of foreground and background image.\n\n    \"\"\"\n    return blend_alpha_(np.copy(image_fg), image_bg, alpha, eps=eps)\n\n\ndef blend_alpha_(image_fg, image_bg, alpha, eps=1e-2):\n    \"\"\"\n    Blend two images in-place using an alpha blending.\n\n    In alpha blending, the two images are naively mixed using a multiplier.\n    Let ``A`` be the foreground image and ``B`` the background image and\n    ``a`` is the alpha value. Each pixel intensity is then computed as\n    ``a * A_ij + (1-a) * B_ij``.\n\n    Added in 0.5.0. (Extracted from :func:`blend_alpha`.)\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; fully tested\n        * ``uint32``: yes; fully tested\n        * ``uint64``: limited; fully tested (1)\n        * ``int8``: yes; fully tested\n        * ``int16``: yes; fully tested\n        * ``int32``: yes; fully tested\n        * ``int64``: limited; fully tested (1)\n        * ``float16``: yes; fully tested\n        * ``float32``: yes; fully tested\n        * ``float64``: limited; fully tested (1)\n        * ``float128``: no (2)\n        * ``bool``: yes; fully tested (2)\n\n        - (1) Tests show that these dtypes work, but a conversion to\n              ``float128`` happens, which only has 96 bits of size instead of\n              true 128 bits and hence not twice as much resolution. It is\n              possible that these dtypes result in inaccuracies, though the\n              tests did not indicate that.\n              Note that ``float128`` support is required for these dtypes\n              and thus they are not expected to work on Windows machines.\n        - (2) Not available due to the input dtype having to be increased to\n              an equivalent float dtype with two times the input resolution.\n        - (3) Mapped internally to ``float16``.\n\n    Parameters\n    ----------\n    image_fg : (H,W,[C]) ndarray\n        Foreground image. Shape and dtype kind must match the one of the\n        background image.\n        This image might be modified in-place.\n\n    image_bg : (H,W,[C]) ndarray\n        Background image. Shape and dtype kind must match the one of the\n        foreground image.\n\n    alpha : number or iterable of number or ndarray\n        The blending factor, between ``0.0`` and ``1.0``. Can be interpreted\n        as the opacity of the foreground image. Values around ``1.0`` result\n        in only the foreground image being visible. Values around ``0.0``\n        result in only the background image being visible. Multiple alphas\n        may be provided. In these cases, there must be exactly one alpha per\n        channel in the foreground/background image. Alternatively, for\n        ``(H,W,C)`` images, either one ``(H,W)`` array or an ``(H,W,C)``\n        array of alphas may be provided, denoting the elementwise alpha value.\n\n    eps : number, optional\n        Controls when an alpha is to be interpreted as exactly ``1.0`` or\n        exactly ``0.0``, resulting in only the foreground/background being\n        visible and skipping the actual computation.\n\n    Returns\n    -------\n    image_blend : (H,W,C) ndarray\n        Blend of foreground and background image.\n        This might be an in-place modified version of `image_fg`.\n\n    \"\"\"\n    assert image_fg.shape == image_bg.shape, (\n        \"Expected foreground and background images to have the same shape. \"\n        \"Got %s and %s.\" % (image_fg.shape, image_bg.shape))\n    assert image_fg.dtype.kind == image_bg.dtype.kind, (\n        \"Expected foreground and background images to have the same dtype \"\n        \"kind. Got %s and %s.\" % (image_fg.dtype.kind, image_bg.dtype.kind))\n\n    # Note: If float128 is not available on the system, _FLOAT128_DTYPE is\n    # None, but 'np.dtype(\"float64\") == None' actually equates to True\n    # for whatever reason, so we check first if the constant is not None\n    # (i.e. if float128 exists).\n    if iadt._FLOAT128_DTYPE is not None:\n        assert image_fg.dtype != iadt._FLOAT128_DTYPE, (\n            \"Foreground image was float128, but blend_alpha_() cannot handle \"\n            \"that dtype.\")\n        assert image_bg.dtype != iadt._FLOAT128_DTYPE, (\n            \"Background image was float128, but blend_alpha_() cannot handle \"\n            \"that dtype.\")\n\n    if image_fg.size == 0:\n        return image_fg\n\n    input_was_2d = (image_fg.ndim == 2)\n    if input_was_2d:\n        image_fg = image_fg[..., np.newaxis]\n        image_bg = image_bg[..., np.newaxis]\n\n    input_was_bool = False\n    if image_fg.dtype.kind == \"b\":\n        input_was_bool = True\n        # use float32 instead of float16 here because it seems to be faster\n        image_fg = image_fg.astype(np.float32)\n        image_bg = image_bg.astype(np.float32)\n\n    alpha = np.array(alpha, dtype=np.float64)\n    if alpha.size == 1:\n        pass\n    else:\n        if alpha.ndim == 2:\n            assert alpha.shape == image_fg.shape[0:2], (\n                \"'alpha' given as an array must match the height and width \"\n                \"of the foreground and background image. Got shape %s vs \"\n                \"foreground/background shape %s.\" % (\n                    alpha.shape, image_fg.shape))\n        elif alpha.ndim == 3:\n            assert (\n                alpha.shape == image_fg.shape\n                or alpha.shape == image_fg.shape[0:2] + (1,)), (\n                    \"'alpha' given as an array must match the height and \"\n                    \"width of the foreground and background image. Got \"\n                    \"shape %s vs foreground/background shape %s.\" % (\n                        alpha.shape, image_fg.shape))\n        else:\n            alpha = alpha.reshape((1, 1, -1))\n\n    if not input_was_bool:\n        if np.all(alpha >= 1.0 - eps):\n            if input_was_2d:\n                image_fg = image_fg[..., 0]\n            return image_fg\n        if np.all(alpha <= eps):\n            if input_was_2d:\n                image_bg = image_bg[..., 0]\n            # use copy() here so that only image_fg has to be copied in\n            # blend_alpha()\n            return np.copy(image_bg)\n\n    # for efficiency reasons, only test one value of alpha here, even if alpha\n    # is much larger\n    if alpha.size > 0:\n        assert 0 <= alpha.item(0) <= 1.0, (\n            \"Expected 'alpha' value(s) to be in the interval [0.0, 1.0]. \"\n            \"Got min %.4f and max %.4f.\" % (np.min(alpha), np.max(alpha)))\n\n    uint8 = iadt._UINT8_DTYPE\n    both_uint8 = (image_fg.dtype, image_bg.dtype) == (uint8, uint8)\n    if both_uint8:\n        if alpha.size == 1:\n            image_blend = _blend_alpha_uint8_single_alpha_(\n                image_fg, image_bg, float(alpha), inplace=True\n            )\n        elif alpha.shape == (1, 1, image_fg.shape[2]):\n            image_blend = _blend_alpha_uint8_channelwise_alphas_(\n                image_fg, image_bg, alpha[0, 0, :]\n            )\n        else:\n            image_blend = _blend_alpha_uint8_elementwise_(\n                image_fg, image_bg, alpha\n            )\n    else:\n        image_blend = _blend_alpha_non_uint8(image_fg, image_bg, alpha)\n\n    if input_was_bool:\n        image_blend = image_blend > 0.5\n\n    if input_was_2d:\n        return image_blend[:, :, 0]\n    return image_blend\n\n\n# Added in 0.5.0.\ndef _blend_alpha_uint8_single_alpha_(image_fg, image_bg, alpha, inplace):\n    # here we are not guarantueed that inputs have ndim=3, can be ndim=2\n    result = cv2.addWeighted(\n        _normalize_cv2_input_arr_(image_fg),\n        alpha,\n        _normalize_cv2_input_arr_(image_bg),\n        beta=(1 - alpha),\n        gamma=0.0,\n        dst=image_fg if inplace else None\n    )\n    if result.ndim == 2 and image_fg.ndim == 3:\n        return result[:, :, np.newaxis]\n    return result\n\n\n# Added in 0.5.0.\ndef _blend_alpha_uint8_channelwise_alphas_(image_fg, image_bg, alphas):\n    # we are guarantueed here that image_fg and image_bg have ndim=3\n    result = []\n    for i, alpha in enumerate(alphas):\n        result.append(\n            _blend_alpha_uint8_single_alpha_(\n                image_fg[:, :, i],\n                image_bg[:, :, i],\n                float(alpha),\n                inplace=False\n            )\n        )\n\n    image_blend = _merge_channels(result, image_fg.ndim == 3)\n    return image_blend\n\n\n# Added in 0.5.0.\ndef _blend_alpha_uint8_elementwise_(image_fg, image_bg, alphas):\n    betas = 1.0 - alphas\n\n    is_2d = (alphas.ndim == 2 or alphas.shape[2] == 1)\n    area = image_fg.shape[0] * image_fg.shape[1]\n    if is_2d and area >= 64*64:\n        if alphas.ndim == 3:\n            alphas = alphas[:, :, 0]\n            betas = betas[:, :, 0]\n\n        result = []\n        for c in range(image_fg.shape[2]):\n            image_fg_mul = image_fg[:, :, c]\n            image_bg_mul = image_bg[:, :, c]\n            image_fg_mul = cv2.multiply(image_fg_mul, alphas, dtype=cv2.CV_8U)\n            image_bg_mul = cv2.multiply(image_bg_mul, betas, dtype=cv2.CV_8U)\n            image_fg_mul = cv2.add(image_fg_mul, image_bg_mul, dst=image_fg_mul)\n            result.append(image_fg_mul)\n\n        image_blend = _merge_channels(result, image_fg.ndim == 3)\n        return image_blend\n    else:\n        if alphas.ndim == 2:\n            alphas = alphas[..., np.newaxis]\n            betas = betas[..., np.newaxis]\n        if alphas.shape[2] != image_fg.shape[2]:\n            alphas = np.tile(alphas, (1, 1, image_fg.shape[2]))\n            betas = np.tile(betas, (1, 1, image_fg.shape[2]))\n\n        alphas = alphas.ravel()\n        betas = betas.ravel()\n        input_shape = image_fg.shape\n\n        image_fg_mul = image_fg.ravel()\n        image_bg_mul = image_bg.ravel()\n        image_fg_mul = cv2.multiply(\n            image_fg_mul, alphas, dtype=cv2.CV_8U, dst=image_fg_mul\n        )\n        image_bg_mul = cv2.multiply(\n            image_bg_mul, betas, dtype=cv2.CV_8U, dst=image_bg_mul\n        )\n\n        image_fg_mul = cv2.add(image_fg_mul, image_bg_mul, dst=image_fg_mul)\n\n        return image_fg_mul.reshape(input_shape)\n\n\n# Added in 0.5.0.\n# (Extracted from blend_alpha().)\ndef _blend_alpha_non_uint8(image_fg, image_bg, alpha):\n    if alpha.ndim == 2:\n        alpha = alpha[:, :, np.newaxis]\n\n    dt_images = iadt.get_minimal_dtype([image_fg, image_bg])\n\n    # doing the below itemsize increase only for non-float images led to\n    # inaccuracies for large float values\n    # we also use a minimum of 4 bytes (=float32), as float32 tends to be\n    # faster than float16\n    isize = dt_images.itemsize * 2\n    isize = max(isize, 4)\n    dt_name = \"f%d\" % (isize,)\n\n    # check if float128 (16*8=128) is supported\n    assert dt_name != \"f16\" or hasattr(np, \"float128\"), (\n        \"The input images use dtype '%s', for which alpha-blending \"\n        \"requires float128 support to compute accurately its output, \"\n        \"but float128 seems to not be available on the current \"\n        \"system.\" % (image_fg.dtype.name,)\n    )\n\n    dt_blend = np.dtype(dt_name)\n\n    if alpha.dtype != dt_blend:\n        alpha = alpha.astype(dt_blend)\n    if image_fg.dtype != dt_blend:\n        image_fg = image_fg.astype(dt_blend)\n    if image_bg.dtype != dt_blend:\n        image_bg = image_bg.astype(dt_blend)\n\n    # the following is\n    #     image_blend = image_bg + alpha * (image_fg - image_bg)\n    # which is equivalent to\n    #     image_blend = alpha * image_fg + (1 - alpha) * image_bg\n    # but supposedly faster\n    image_blend = image_fg - image_bg\n    image_blend *= alpha\n    image_blend += image_bg\n\n    # Skip clip, because alpha is expected to be in range [0.0, 1.0] and\n    # both images must have same dtype.\n    # Dont skip round, because otherwise it is very unlikely to hit the\n    # image's max possible value\n    image_blend = iadt.restore_dtypes_(\n        image_blend, dt_images, clip=False, round=True)\n\n    return image_blend\n\n\ndef _merge_channels(channels, input_was_3d):\n    if len(channels) <= 512:\n        image_blend = cv2.merge(channels)\n    else:\n        image_blend = np.stack(channels, axis=-1)\n    if image_blend.ndim == 2 and input_was_3d:\n        image_blend = image_blend[:, :, np.newaxis]\n    return image_blend\n\n\n# Added in 0.4.0.\ndef _generate_branch_outputs(augmenter, batch, hooks, parents):\n    parents_extended = parents + [augmenter]\n\n    # Note here that the propagation hook removes columns in the batch\n    # and re-adds them afterwards. So the batch should not be copied\n    # after the `with` statement.\n    outputs_fg = batch\n    if augmenter.foreground is not None:\n        outputs_fg = outputs_fg.deepcopy()\n        with outputs_fg.propagation_hooks_ctx(augmenter, hooks, parents):\n            if augmenter.foreground is not None:\n                outputs_fg = augmenter.foreground.augment_batch_(\n                    outputs_fg,\n                    parents=parents_extended,\n                    hooks=hooks\n                )\n\n    outputs_bg = batch\n    if augmenter.background is not None:\n        outputs_bg = outputs_bg.deepcopy()\n        with outputs_bg.propagation_hooks_ctx(augmenter, hooks, parents):\n            outputs_bg = augmenter.background.augment_batch_(\n                outputs_bg,\n                parents=parents_extended,\n                hooks=hooks\n            )\n\n    return outputs_fg, outputs_bg\n\n\n# Added in 0.4.0.\ndef _to_deterministic(augmenter):\n    aug = augmenter.copy()\n    aug.foreground = (\n        aug.foreground.to_deterministic()\n        if aug.foreground is not None\n        else None)\n    aug.background = (\n        aug.background.to_deterministic()\n        if aug.background is not None\n        else None)\n    aug.deterministic = True\n    aug.random_state = augmenter.random_state.derive_rng_()\n    return aug\n\n\nclass BlendAlpha(meta.Augmenter):\n    \"\"\"\n    Alpha-blend two image sources using an alpha/opacity value.\n\n    The two image sources can be imagined as branches.\n    If a source is not given, it is automatically the same as the input.\n    Let ``FG`` be the foreground branch and ``BG`` be the background branch.\n    Then the result images are defined as ``factor * FG + (1-factor) * BG``,\n    where ``factor`` is an overlay factor.\n\n    .. note::\n\n        It is not recommended to use ``BlendAlpha`` with augmenters\n        that change the geometry of images (e.g. horizontal flips, affine\n        transformations) if you *also* want to augment coordinates (e.g.\n        keypoints, polygons, ...), as it is unclear which of the two\n        coordinate results (foreground or background branch) should be used\n        as the coordinates after augmentation.\n\n        Currently, if ``factor >= 0.5`` (per image), the results of the\n        foreground branch are used as the new coordinates, otherwise the\n        results of the background branch.\n\n    Added in 0.4.0. (Before that named `Alpha`.)\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.blend.blend_alpha_`.\n\n    Parameters\n    ----------\n    factor : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Opacity of the results of the foreground branch. Values close to\n        ``0.0`` mean that the results from the background branch (see\n        parameter `background`) make up most of the final image.\n\n            * If float, then that value will be used for all images.\n            * If tuple ``(a, b)``, then a random value from the interval\n              ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be picked from that list per\n              image.\n            * If ``StochasticParameter``, then that parameter will be used to\n              sample a value per image.\n\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use the same factor for all channels (``False``)\n        or to sample a new value for each channel (``True``).\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as True, otherwise as False.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlpha(0.5, iaa.Grayscale(1.0))\n\n    Convert each image to pure grayscale and alpha-blend the result with the\n    original image using an alpha of ``50%``, thereby removing about ``50%`` of\n    all color. This is equivalent to ``iaa.Grayscale(0.5)``.\n\n    >>> aug = iaa.BlendAlpha((0.0, 1.0), iaa.Grayscale(1.0))\n\n    Same as in the previous example, but the alpha factor is sampled uniformly\n    from the interval ``[0.0, 1.0]`` once per image, thereby removing a random\n    fraction of all colors. This is equivalent to\n    ``iaa.Grayscale((0.0, 1.0))``.\n\n    >>> aug = iaa.BlendAlpha(\n    >>>     (0.0, 1.0),\n    >>>     iaa.Affine(rotate=(-20, 20)),\n    >>>     per_channel=0.5)\n\n    First, rotate each image by a random degree sampled uniformly from the\n    interval ``[-20, 20]``. Then, alpha-blend that new image with the original\n    one using a random factor sampled uniformly from the interval\n    ``[0.0, 1.0]``. For ``50%`` of all images, the blending happens\n    channel-wise and the factor is sampled independently per channel\n    (``per_channel=0.5``). As a result, e.g. the red channel may look visibly\n    rotated (factor near ``1.0``), while the green and blue channels may not\n    look rotated (factors near ``0.0``).\n\n    >>> aug = iaa.BlendAlpha(\n    >>>     (0.0, 1.0),\n    >>>     foreground=iaa.Add(100),\n    >>>     background=iaa.Multiply(0.2))\n\n    Apply two branches of augmenters -- ``A`` and ``B`` -- *independently*\n    to input images and alpha-blend the results of these branches using a\n    factor ``f``. Branch ``A`` increases image pixel intensities by ``100``\n    and ``B`` multiplies the pixel intensities by ``0.2``. ``f`` is sampled\n    uniformly from the interval ``[0.0, 1.0]`` per image. The resulting images\n    contain a bit of ``A`` and a bit of ``B``.\n\n    >>> aug = iaa.BlendAlpha([0.25, 0.75], iaa.MedianBlur(13))\n\n    Apply median blur to each image and alpha-blend the result with the\n    original image using an alpha factor of either exactly ``0.25`` or\n    exactly ``0.75`` (sampled once per image).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, factor=(0.0, 1.0), foreground=None, background=None,\n                 per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(BlendAlpha, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.factor = iap.handle_continuous_param(\n            factor, \"factor\", value_range=(0, 1.0), tuple_to_uniform=True,\n            list_to_choice=True)\n\n        assert foreground is not None or background is not None, (\n            \"Expected 'foreground' and/or 'background' to not be None (i.e. \"\n            \"at least one Augmenter), but got two None values.\")\n        self.foreground = meta.handle_children_list(\n            foreground, self.name, \"foreground\", default=None)\n        self.background = meta.handle_children_list(\n            background, self.name, \"background\", default=None)\n\n        self.per_channel = iap.handle_probability_param(per_channel,\n                                                        \"per_channel\")\n\n        self.epsilon = 1e-2\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        batch_fg, batch_bg = _generate_branch_outputs(\n            self, batch, hooks, parents)\n\n        columns = batch.columns\n        shapes = batch.get_rowwise_shapes()\n        nb_images = len(shapes)\n        nb_channels_max = max([shape[2] if len(shape) > 2 else 1\n                               for shape in shapes])\n        rngs = random_state.duplicate(2)\n        per_channel = self.per_channel.draw_samples(nb_images,\n                                                    random_state=rngs[0])\n        alphas = self.factor.draw_samples((nb_images, nb_channels_max),\n                                          random_state=rngs[1])\n\n        for i, shape in enumerate(shapes):\n            if per_channel[i] > 0.5:\n                nb_channels = shape[2] if len(shape) > 2 else 1\n                alphas_i = alphas[i, 0:nb_channels]\n            else:\n                # We catch here the case of alphas[i] being empty, which can\n                # happen if all images have 0 channels.\n                # In that case the alpha value doesn't matter as the image\n                # contains zero values anyways.\n                alphas_i = alphas[i, 0] if alphas[i].size > 0 else 0\n\n            # compute alpha for non-image data -- average() also works with\n            # scalars\n            alphas_i_avg = np.average(alphas_i)\n            use_fg_branch = alphas_i_avg >= 0.5\n\n            # blend images\n            if batch.images is not None:\n                batch.images[i] = blend_alpha_(batch_fg.images[i],\n                                               batch_bg.images[i],\n                                               alphas_i, eps=self.epsilon)\n\n            # blend non-images\n            # TODO Use gradual blending for heatmaps here (as for images)?\n            #      Heatmaps are probably the only augmentable where this makes\n            #      sense.\n            for column in columns:\n                if column.name != \"images\":\n                    batch_use = (batch_fg if use_fg_branch\n                                 else batch_bg)\n                    column.value[i] = getattr(batch_use, column.attr_name)[i]\n\n        return batch\n\n    # Added in 0.4.0.\n    def _to_deterministic(self):\n        return _to_deterministic(self)\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.factor, self.per_channel]\n\n    # Added in 0.4.0.\n    def get_children_lists(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists`.\"\"\"\n        return [lst for lst in [self.foreground, self.background]\n                if lst is not None]\n\n    # Added in 0.4.0.\n    def __str__(self):\n        pattern = (\n            \"%s(\"\n            \"factor=%s, per_channel=%s, name=%s, \"\n            \"foreground=%s, background=%s, \"\n            \"deterministic=%s\"\n            \")\"\n        )\n        return pattern % (\n            self.__class__.__name__, self.factor, self.per_channel, self.name,\n            self.foreground, self.background, self.deterministic)\n\n\n# tested indirectly via BlendAlphaElementwise for historic reasons\nclass BlendAlphaMask(meta.Augmenter):\n    \"\"\"\n    Alpha-blend two image sources using non-binary masks generated per image.\n\n    This augmenter queries for each image a mask generator to generate\n    a ``(H,W)`` or ``(H,W,C)`` channelwise mask ``[0.0, 1.0]``, where\n    ``H`` is the image height and ``W`` the width.\n    The mask will then be used to alpha-blend pixel- and possibly channel-wise\n    between a foreground branch of augmenters and a background branch.\n    (Both branches default to the identity operation if not provided.)\n\n    See also :class:`~imgaug.augmenters.blend.BlendAlpha`.\n\n    .. note::\n\n        It is not recommended to use ``BlendAlphaMask`` with augmenters\n        that change the geometry of images (e.g. horizontal flips, affine\n        transformations) if you *also* want to augment coordinates (e.g.\n        keypoints, polygons, ...), as it is unclear which of the two\n        coordinate results (foreground or background branch) should be used\n        as the final output coordinates after augmentation.\n\n        Currently, for keypoints the results of the\n        foreground and background branch will be mixed. That means that for\n        each coordinate the augmented result will be picked from the\n        foreground or background branch based on the average alpha mask value\n        at the corresponding spatial location.\n\n        For bounding boxes, line strings and polygons, either all objects\n        (on an image) of the foreground or all of the background branch will\n        be used, based on the average over the whole alpha mask.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.blend.blend_alpha_`.\n\n    Parameters\n    ----------\n    mask_generator : IBatchwiseMaskGenerator\n        A generator that will be queried per image to generate a mask.\n\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch (i.e. identity function).\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch (i.e. identity function).\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlphaMask(\n    >>>     iaa.InvertMaskGen(0.5, iaa.VerticalLinearGradientMaskGen()),\n    >>>     iaa.Sequential([\n    >>>         iaa.Clouds(),\n    >>>         iaa.WithChannels([1, 2], iaa.Multiply(0.5))\n    >>>     ])\n    >>> )\n\n    Create an augmenter that sometimes adds clouds at the bottom and sometimes\n    at the top of the image.\n\n    \"\"\"\n\n    # Currently the mode is only used for keypoint augmentation.\n    # either or: use all keypoints from fg or all from bg branch (based\n    #   on average of the whole mask).\n    # pointwise: decide for each point whether to use the fg or bg\n    #   branch's keypoint (based on the average mask value at the point's\n    #   xy-location).\n    _MODE_EITHER_OR = \"either-or\"\n    _MODE_POINTWISE = \"pointwise\"\n    _MODES = [_MODE_POINTWISE, _MODE_EITHER_OR]\n\n    # Added in 0.4.0.\n    def __init__(self, mask_generator,\n                 foreground=None, background=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(BlendAlphaMask, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.mask_generator = mask_generator\n\n        assert foreground is not None or background is not None, (\n            \"Expected 'foreground' and/or 'background' to not be None (i.e. \"\n            \"at least one Augmenter), but got two None values.\")\n        self.foreground = meta.handle_children_list(\n            foreground, self.name, \"foreground\", default=None)\n        self.background = meta.handle_children_list(\n            background, self.name, \"background\", default=None)\n\n        # this controls how keypoints and polygons are augmented\n        # Non-keypoints currently uses an either-or approach.\n        # Using pointwise augmentation is problematic for polygons and line\n        # strings, because the order of the points may have changed (e.g.\n        # from clockwise to counter-clockwise). For polygons, it is also\n        # overall more likely that some child-augmenter added/deleted points\n        # and we would need a polygon recoverer.\n        # Overall it seems to be the better approach to use all polygons\n        # from one branch or the other, which guarantuees their validity.\n        # TODO decide the either-or not based on the whole average mask\n        #      value but on the average mask value within the polygon's area?\n        self._coord_modes = {\n            \"keypoints\": self._MODE_POINTWISE,\n            \"polygons\": self._MODE_EITHER_OR,\n            \"line_strings\": self._MODE_EITHER_OR,\n            \"bounding_boxes\": self._MODE_EITHER_OR\n        }\n\n        self.epsilon = 1e-2\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        batch_fg, batch_bg = _generate_branch_outputs(\n            self, batch, hooks, parents)\n\n        masks = self.mask_generator.draw_masks(batch, random_state)\n\n        for i, mask in enumerate(masks):\n            if batch.images is not None:\n                batch.images[i] = blend_alpha_(batch_fg.images[i],\n                                               batch_bg.images[i],\n                                               mask, eps=self.epsilon)\n\n            if batch.heatmaps is not None:\n                arr = batch.heatmaps[i].arr_0to1\n                arr_height, arr_width = arr.shape[0:2]\n                mask_binarized = self._binarize_mask(mask,\n                                                     arr_height, arr_width)\n                batch.heatmaps[i].arr_0to1 = blend_alpha_(\n                    batch_fg.heatmaps[i].arr_0to1,\n                    batch_bg.heatmaps[i].arr_0to1,\n                    mask_binarized, eps=self.epsilon)\n\n            if batch.segmentation_maps is not None:\n                arr = batch.segmentation_maps[i].arr\n                arr_height, arr_width = arr.shape[0:2]\n                mask_binarized = self._binarize_mask(mask,\n                                                     arr_height, arr_width)\n                batch.segmentation_maps[i].arr = blend_alpha_(\n                    batch_fg.segmentation_maps[i].arr,\n                    batch_bg.segmentation_maps[i].arr,\n                    mask_binarized, eps=self.epsilon)\n\n            for augm_attr_name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                                   \"line_strings\"]:\n                augm_value = getattr(batch, augm_attr_name)\n                if augm_value is not None:\n                    augm_value[i] = self._blend_coordinates(\n                        augm_value[i],\n                        getattr(batch_fg, augm_attr_name)[i],\n                        getattr(batch_bg, augm_attr_name)[i],\n                        mask,\n                        self._coord_modes[augm_attr_name]\n                    )\n\n        return batch\n\n    # Added in 0.4.0.\n    @classmethod\n    def _binarize_mask(cls, mask, arr_height, arr_width):\n        # Average over channels, resize to heatmap/segmap array size\n        # (+clip for cubic interpolation). We can use none-NN interpolation\n        # for segmaps here as this is just the mask and not the segmap\n        # array.\n        mask_3d = np.atleast_3d(mask)\n\n        # masks with zero-sized axes crash in np.average() and cannot be\n        # upscaled in imresize_single_image()\n        if mask.size == 0:\n            mask_rs = np.zeros((arr_height, arr_width),\n                               dtype=np.float32)\n        else:\n            mask_avg = (\n                np.average(mask_3d, axis=2) if mask_3d.shape[2] > 0 else 1.0)\n            mask_rs = ia.imresize_single_image(mask_avg,\n                                               (arr_height, arr_width))\n        mask_arr = iadt.clip_(mask_rs, 0, 1.0)\n        mask_arr_binarized = (mask_arr >= 0.5)\n        return mask_arr_binarized\n\n    # Added in 0.4.0.\n    @classmethod\n    def _blend_coordinates(cls, cbaoi, cbaoi_fg, cbaoi_bg, mask_image,\n                           mode):\n        coords = augm_utils.convert_cbaois_to_kpsois(cbaoi)\n        coords_fg = augm_utils.convert_cbaois_to_kpsois(cbaoi_fg)\n        coords_bg = augm_utils.convert_cbaois_to_kpsois(cbaoi_bg)\n\n        coords = coords.to_xy_array()\n        coords_fg = coords_fg.to_xy_array()\n        coords_bg = coords_bg.to_xy_array()\n\n        assert coords.shape == coords_fg.shape == coords_bg.shape, (\n            \"Expected number of coordinates to not be changed by foreground \"\n            \"or background branch in BlendAlphaMask. But input coordinates \"\n            \"of shape %s were changed to %s (foreground) and %s \"\n            \"(background). Make sure to not use any augmenters that affect \"\n            \"the existence of coordinates.\" % (\n                coords.shape, coords_fg.shape, coords_bg.shape))\n\n        h_img, w_img = mask_image.shape[0:2]\n\n        if mode == cls._MODE_POINTWISE:\n            # Augment pointwise, i.e. check for each point and its\n            # xy-location the average mask value and pick based on that\n            # either the point from the foreground or background branch.\n            assert len(coords_fg) == len(coords_bg), (\n                \"Got different numbers of coordinates before/after \"\n                \"augmentation in BlendAlphaMask. The number of \"\n                \"coordinates is currently not allowed to change for this \"\n                \"augmenter. Input contained %d coordinates, foreground \"\n                \"branch %d, backround branch %d.\" % (\n                    len(coords), len(coords_fg), len(coords_bg)))\n\n            coords_aug = []\n            subgen = zip(coords, coords_fg, coords_bg)\n            for coord, coord_fg, coord_bg in subgen:\n                x_int = int(np.round(coord[0]))\n                y_int = int(np.round(coord[1]))\n                if 0 <= y_int < h_img and 0 <= x_int < w_img:\n                    alphas_i = mask_image[y_int, x_int, ...]\n                    alpha = (\n                        np.average(alphas_i) if alphas_i.size > 0 else 1.0)\n                    if alpha > 0.5:\n                        coords_aug.append(coord_fg)\n                    else:\n                        coords_aug.append(coord_bg)\n                else:\n                    coords_aug.append((x_int, y_int))\n        else:\n            # Augment with an either-or approach over all points, i.e.\n            # based on the average of the whole mask, either all points\n            # from the foreground or all points from the background branch\n            # are used.\n            # Note that we ensured above that _keypoint_mode must be\n            # _MODE_EITHER_OR if it wasn't _MODE_POINTWISE.\n            mask_image_avg = (\n                np.average(mask_image) if mask_image.size > 0 else 1.0)\n            if mask_image_avg > 0.5:\n                coords_aug = coords_fg\n            else:\n                coords_aug = coords_bg\n\n        kpsoi_aug = ia.KeypointsOnImage.from_xy_array(\n            coords_aug, shape=cbaoi.shape)\n        return augm_utils.invert_convert_cbaois_to_kpsois_(cbaoi, kpsoi_aug)\n\n    # Added in 0.4.0.\n    def _to_deterministic(self):\n        return _to_deterministic(self)\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.mask_generator]\n\n    # Added in 0.4.0.\n    def get_children_lists(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists`.\"\"\"\n        return [lst for lst in [self.foreground, self.background]\n                if lst is not None]\n\n    # Added in 0.4.0.\n    def __str__(self):\n        pattern = (\n            \"%s(\"\n            \"mask_generator=%s, name=%s, foreground=%s, background=%s, \"\n            \"deterministic=%s\"\n            \")\"\n        )\n        return pattern % (\n            self.__class__.__name__, self.mask_generator, self.name,\n            self.foreground, self.background, self.deterministic)\n\n\n# FIXME the output of the third example makes it look like per_channel isn't\n#       working\nclass BlendAlphaElementwise(BlendAlphaMask):\n    \"\"\"\n    Alpha-blend two image sources using alpha/opacity values sampled per pixel.\n\n    This is the same as :class:`BlendAlpha`, except that the opacity factor is\n    sampled once per *pixel* instead of once per *image* (or a few times per\n    image, if ``BlendAlpha.per_channel`` is set to ``True``).\n\n    See :class:`BlendAlpha` for more details.\n\n    This class is a wrapper around\n    :class:`~imgaug.augmenters.blend.BlendAlphaMask`.\n\n    .. note::\n\n        Avoid using augmenters as children that affect pixel locations (e.g.\n        horizontal flips). See\n        :class:`~imgaug.augmenters.blend.BlendAlphaMask` for details.\n\n    Added in 0.4.0. (Before that named `AlphaElementwise`.)\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.blend.BlendAlphaMask`.\n\n    Parameters\n    ----------\n    factor : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Opacity of the results of the foreground branch. Values close to\n        ``0.0`` mean that the results from the background branch (see\n        parameter `background`) make up most of the final image.\n\n            * If float, then that value will be used for all images.\n            * If tuple ``(a, b)``, then a random value from the interval\n              ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be picked from that list per\n              image.\n            * If ``StochasticParameter``, then that parameter will be used to\n              sample a value per image.\n\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    per_channel : bool or float, optional\n        Whether to use the same factor for all channels (``False``)\n        or to sample a new value for each channel (``True``).\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as True, otherwise as False.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlphaElementwise(0.5, iaa.Grayscale(1.0))\n\n    Convert each image to pure grayscale and alpha-blend the result with the\n    original image using an alpha of ``50%`` for all pixels, thereby removing\n    about ``50%`` of all color. This is equivalent to ``iaa.Grayscale(0.5)``.\n    This is also equivalent to ``iaa.BlendAlpha(0.5, iaa.Grayscale(1.0))``, as\n    the opacity has a fixed value of ``0.5`` and is hence identical for all\n    pixels.\n\n    >>> aug = iaa.BlendAlphaElementwise((0, 1.0), iaa.AddToHue(100))\n\n    Same as in the previous example, but here with hue-shift instead\n    of grayscaling and additionally the alpha factor is sampled uniformly\n    from the interval ``[0.0, 1.0]`` once per pixel, thereby shifting the\n    hue by a random fraction for each pixel.\n\n    >>> aug = iaa.BlendAlphaElementwise(\n    >>>     (0.0, 1.0),\n    >>>     iaa.Affine(rotate=(-20, 20)),\n    >>>     per_channel=0.5)\n\n    First, rotate each image by a random degree sampled uniformly from the\n    interval ``[-20, 20]``. Then, alpha-blend that new image with the original\n    one using a random factor sampled uniformly from the interval\n    ``[0.0, 1.0]`` per pixel. For ``50%`` of all images, the blending happens\n    channel-wise and the factor is sampled independently per pixel *and*\n    channel (``per_channel=0.5``). As a result, e.g. the red channel may look\n    visibly rotated (factor near ``1.0``), while the green and blue channels\n    may not look rotated (factors near ``0.0``).\n\n    >>> aug = iaa.BlendAlphaElementwise(\n    >>>     (0.0, 1.0),\n    >>>     foreground=iaa.Add(100),\n    >>>     background=iaa.Multiply(0.2))\n\n    Apply two branches of augmenters -- ``A`` and ``B`` -- *independently*\n    to input images and alpha-blend the results of these branches using a\n    factor ``f``. Branch ``A`` increases image pixel intensities by ``100``\n    and ``B`` multiplies the pixel intensities by ``0.2``. ``f`` is sampled\n    uniformly from the interval ``[0.0, 1.0]`` per pixel. The resulting images\n    contain a bit of ``A`` and a bit of ``B``.\n\n    >>> aug = iaa.BlendAlphaElementwise([0.25, 0.75], iaa.MedianBlur(13))\n\n    Apply median blur to each image and alpha-blend the result with the\n    original image using an alpha factor of either exactly ``0.25`` or\n    exactly ``0.75`` (sampled once per pixel).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, factor=(0.0, 1.0), foreground=None, background=None,\n                 per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        factor = iap.handle_continuous_param(\n            factor, \"factor\", value_range=(0, 1.0), tuple_to_uniform=True,\n            list_to_choice=True)\n        mask_gen = StochasticParameterMaskGen(factor, per_channel)\n        super(BlendAlphaElementwise, self).__init__(\n            mask_gen, foreground, background,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    # Added in 0.4.0.\n    @property\n    def factor(self):\n        return self.mask_generator.parameter\n\n\nclass BlendAlphaSimplexNoise(BlendAlphaElementwise):\n    \"\"\"Alpha-blend two image sources using simplex noise alpha masks.\n\n    The alpha masks are sampled using a simplex noise method, roughly creating\n    connected blobs of 1s surrounded by 0s. If nearest neighbour\n    upsampling is used, these blobs can be rectangular with sharp edges.\n\n    Added in 0.4.0. (Before that named `SimplexNoiseAlpha`.)\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.blend.BlendAlphaElementwise`.\n\n    Parameters\n    ----------\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    per_channel : bool or float, optional\n        Whether to use the same factor for all channels (``False``)\n        or to sample a new value for each channel (``True``).\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``, otherwise as ``False``.\n\n    size_px_max : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        The simplex noise is always generated in a low resolution environment.\n        This parameter defines the maximum size of that environment (in\n        pixels). The environment is initialized at the same size as the input\n        image and then downscaled, so that no side exceeds `size_px_max`\n        (aspect ratio is kept).\n\n            * If int, then that number will be used as the size for all\n              iterations.\n            * If tuple of two ``int`` s ``(a, b)``, then a value will be\n              sampled per iteration from the discrete interval ``[a..b]``.\n            * If a list of ``int`` s, then a value will be picked per iteration\n              at random from that list.\n            * If a ``StochasticParameter``, then a value will be sampled from\n              that parameter per iteration.\n\n    upscale_method : None or imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        After generating the noise maps in low resolution environments, they\n        have to be upscaled to the input image size. This parameter controls\n        the upscaling method.\n\n            * If ``None``, then either ``nearest`` or ``linear`` or ``cubic``\n              is picked. Most weight is put on ``linear``, followed by\n              ``cubic``.\n            * If ``imgaug.ALL``, then either ``nearest`` or ``linear`` or\n              ``area`` or ``cubic`` is picked per iteration (all same\n              probability).\n            * If a string, then that value will be used as the method (must be\n              ``nearest`` or ``linear`` or ``area`` or ``cubic``).\n            * If list of string, then a random value will be picked from that\n              list per iteration.\n            * If ``StochasticParameter``, then a random value will be sampled\n              from that parameter per iteration.\n\n    iterations : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        How often to repeat the simplex noise generation process per image.\n\n            * If ``int``, then that number will be used as the iterations for\n              all images.\n            * If tuple of two ``int`` s ``(a, b)``, then a value will be\n              sampled per image from the discrete interval ``[a..b]``.\n            * If a list of ``int`` s, then a value will be picked per image at\n              random from that list.\n            * If a ``StochasticParameter``, then a value will be sampled from\n              that parameter per image.\n\n    aggregation_method : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        The noise maps (from each iteration) are combined to one noise map\n        using an aggregation process. This parameter defines the method used\n        for that process. Valid methods are ``min``, ``max`` or ``avg``,\n        where ``min`` combines the noise maps by taking the (elementwise)\n        minimum over all iteration's results, ``max`` the (elementwise)\n        maximum and ``avg`` the (elementwise) average.\n\n            * If ``imgaug.ALL``, then a random value will be picked per image\n              from the valid ones.\n            * If a string, then that value will always be used as the method.\n            * If a list of string, then a random value will be picked from\n              that list per image.\n            * If a ``StochasticParameter``, then a random value will be\n              sampled from that paramter per image.\n\n    sigmoid : bool or number, optional\n        Whether to apply a sigmoid function to the final noise maps, resulting\n        in maps that have more extreme values (close to 0.0 or 1.0).\n\n            * If ``bool``, then a sigmoid will always (``True``) or never\n              (``False``) be applied.\n            * If a number ``p`` with ``0<=p<=1``, then a sigmoid will be\n              applied to ``p`` percent of all final noise maps.\n\n    sigmoid_thresh : None or number or tuple of number or imgaug.parameters.StochasticParameter, optional\n        Threshold of the sigmoid, when applied. Thresholds above zero\n        (e.g. ``5.0``) will move the saddle point towards the right, leading\n        to more values close to 0.0.\n\n            * If ``None``, then ``Normal(0, 5.0)`` will be used.\n            * If number, then that threshold will be used for all images.\n            * If tuple of two numbers ``(a, b)``, then a random value will\n              be sampled per image from the interval ``[a, b]``.\n            * If ``StochasticParameter``, then a random value will be sampled\n              from that parameter per image.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlphaSimplexNoise(iaa.EdgeDetect(1.0))\n\n    Detect per image all edges, mark them in a black and white image and\n    then alpha-blend the result with the original image using simplex noise\n    masks.\n\n    >>> aug = iaa.BlendAlphaSimplexNoise(\n    >>>     iaa.EdgeDetect(1.0),\n    >>>     upscale_method=\"nearest\")\n\n    Same as in the previous example, but using only nearest neighbour\n    upscaling to scale the simplex noise masks to the final image sizes, i.e.\n    no nearest linear upsampling is used. This leads to rectangles with sharp\n    edges.\n\n    >>> aug = iaa.BlendAlphaSimplexNoise(\n    >>>     iaa.EdgeDetect(1.0),\n    >>>     upscale_method=\"linear\")\n\n    Same as in the previous example, but using only linear upscaling to\n    scale the simplex noise masks to the final image sizes, i.e. no nearest\n    neighbour upsampling is used. This leads to rectangles with smooth edges.\n\n    >>> aug = iaa.BlendAlphaSimplexNoise(\n    >>>     iaa.EdgeDetect(1.0),\n    >>>     sigmoid_thresh=iap.Normal(10.0, 5.0))\n\n    Same as in the first example, but using a threshold for the sigmoid\n    function that is further to the right. This is more conservative, i.e.\n    the generated noise masks will be mostly black (values around ``0.0``),\n    which means that most of the original images (parameter/branch\n    `background`) will be kept, rather than using the results of the\n    augmentation (parameter/branch `foreground`).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, foreground=None, background=None, per_channel=False,\n                 size_px_max=(2, 16), upscale_method=None,\n                 iterations=(1, 3), aggregation_method=\"max\",\n                 sigmoid=True, sigmoid_thresh=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        upscale_method_default = iap.Choice([\"nearest\", \"linear\", \"cubic\"],\n                                            p=[0.05, 0.6, 0.35])\n        sigmoid_thresh_default = iap.Normal(0.0, 5.0)\n\n        noise = iap.SimplexNoise(\n            size_px_max=size_px_max,\n            upscale_method=(upscale_method\n                            if upscale_method is not None\n                            else upscale_method_default)\n        )\n\n        if iterations != 1:\n            noise = iap.IterativeNoiseAggregator(\n                noise,\n                iterations=iterations,\n                aggregation_method=aggregation_method\n            )\n\n        use_sigmoid = (\n            sigmoid is True\n            or (ia.is_single_number(sigmoid) and sigmoid >= 0.01))\n        if use_sigmoid:\n            noise = iap.Sigmoid.create_for_noise(\n                noise,\n                threshold=(sigmoid_thresh\n                           if sigmoid_thresh is not None\n                           else sigmoid_thresh_default),\n                activated=sigmoid\n            )\n\n        super(BlendAlphaSimplexNoise, self).__init__(\n            factor=noise, foreground=foreground, background=background,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass BlendAlphaFrequencyNoise(BlendAlphaElementwise):\n    \"\"\"Alpha-blend two image sources using frequency noise masks.\n\n    The alpha masks are sampled using frequency noise of varying scales,\n    which can sometimes create large connected blobs of ``1`` s surrounded\n    by ``0`` s and other times results in smaller patterns. If nearest\n    neighbour upsampling is used, these blobs can be rectangular with sharp\n    edges.\n\n    Added in 0.4.0. (Before that named `FrequencyNoiseAlpha`.)\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.blend.BlendAlphaElementwise`.\n\n    Parameters\n    ----------\n    exponent : number or tuple of number of list of number or imgaug.parameters.StochasticParameter, optional\n        Exponent to use when scaling in the frequency domain.\n        Sane values are in the range ``-4`` (large blobs) to ``4`` (small\n        patterns). To generate cloud-like structures, use roughly ``-2``.\n\n            * If number, then that number will be used as the exponent for all\n              iterations.\n            * If tuple of two numbers ``(a, b)``, then a value will be sampled\n              per iteration from the interval ``[a, b]``.\n            * If a list of numbers, then a value will be picked per iteration\n              at random from that list.\n            * If a ``StochasticParameter``, then a value will be sampled from\n              that parameter per iteration.\n\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    per_channel : bool or float, optional\n        Whether to use the same factor for all channels (``False``)\n        or to sample a new value for each channel (``True``).\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``, otherwise as ``False``.\n\n    size_px_max : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        The noise is generated in a low resolution environment.\n        This parameter defines the maximum size of that environment (in\n        pixels). The environment is initialized at the same size as the input\n        image and then downscaled, so that no side exceeds `size_px_max`\n        (aspect ratio is kept).\n\n            * If ``int``, then that number will be used as the size for all\n              iterations.\n            * If tuple of two ``int`` s ``(a, b)``, then a value will be\n              sampled per iteration from the discrete interval ``[a..b]``.\n            * If a list of ``int`` s, then a value will be picked per\n              iteration at random from that list.\n            * If a ``StochasticParameter``, then a value will be sampled from\n              that parameter per iteration.\n\n    upscale_method : None or imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        After generating the noise maps in low resolution environments, they\n        have to be upscaled to the input image size. This parameter controls\n        the upscaling method.\n\n            * If ``None``, then either ``nearest`` or ``linear`` or ``cubic``\n              is picked. Most weight is put on ``linear``, followed by\n              ``cubic``.\n            * If ``imgaug.ALL``, then either ``nearest`` or ``linear`` or\n              ``area`` or ``cubic`` is picked per iteration (all same\n              probability).\n            * If string, then that value will be used as the method (must be\n              ``nearest`` or ``linear`` or ``area`` or ``cubic``).\n            * If list of string, then a random value will be picked from that\n              list per iteration.\n            * If ``StochasticParameter``, then a random value will be sampled\n              from that parameter per iteration.\n\n    iterations : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        How often to repeat the simplex noise generation process per\n        image.\n\n            * If ``int``, then that number will be used as the iterations for\n              all images.\n            * If tuple of two ``int`` s ``(a, b)``, then a value will be\n              sampled per image from the discrete interval ``[a..b]``.\n            * If a list of ``int`` s, then a value will be picked per image at\n              random from that list.\n            * If a ``StochasticParameter``, then a value will be sampled from\n              that parameter per image.\n\n    aggregation_method : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        The noise maps (from each iteration) are combined to one noise map\n        using an aggregation process. This parameter defines the method used\n        for that process. Valid methods are ``min``, ``max`` or ``avg``,\n        where 'min' combines the noise maps by taking the (elementwise) minimum\n        over all iteration's results, ``max`` the (elementwise) maximum and\n        ``avg`` the (elementwise) average.\n\n            * If ``imgaug.ALL``, then a random value will be picked per image\n              from the valid ones.\n            * If a string, then that value will always be used as the method.\n            * If a list of string, then a random value will be picked from\n              that list per image.\n            * If a ``StochasticParameter``, then a random value will be sampled\n              from that parameter per image.\n\n    sigmoid : bool or number, optional\n        Whether to apply a sigmoid function to the final noise maps, resulting\n        in maps that have more extreme values (close to ``0.0`` or ``1.0``).\n\n            * If ``bool``, then a sigmoid will always (``True``) or never\n              (``False``) be applied.\n            * If a number ``p`` with ``0<=p<=1``, then a sigmoid will be applied to\n              ``p`` percent of all final noise maps.\n\n    sigmoid_thresh : None or number or tuple of number or imgaug.parameters.StochasticParameter, optional\n        Threshold of the sigmoid, when applied. Thresholds above zero\n        (e.g. ``5.0``) will move the saddle point towards the right, leading to\n        more values close to ``0.0``.\n\n            * If ``None``, then ``Normal(0, 5.0)`` will be used.\n            * If number, then that threshold will be used for all images.\n            * If tuple of two numbers ``(a, b)``, then a random value will\n              be sampled per image from the range ``[a, b]``.\n            * If ``StochasticParameter``, then a random value will be sampled\n              from that parameter per image.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlphaFrequencyNoise(foreground=iaa.EdgeDetect(1.0))\n\n    Detect per image all edges, mark them in a black and white image and\n    then alpha-blend the result with the original image using frequency noise\n    masks.\n\n    >>> aug = iaa.BlendAlphaFrequencyNoise(\n    >>>     foreground=iaa.EdgeDetect(1.0),\n    >>>     upscale_method=\"nearest\")\n\n    Same as the first example, but using only linear upscaling to\n    scale the frequency noise masks to the final image sizes, i.e. no nearest\n    neighbour upsampling is used. This results in smooth edges.\n\n    >>> aug = iaa.BlendAlphaFrequencyNoise(\n    >>>     foreground=iaa.EdgeDetect(1.0),\n    >>>     upscale_method=\"linear\")\n\n    Same as the first example, but using only linear upscaling to\n    scale the frequency noise masks to the final image sizes, i.e. no nearest\n    neighbour upsampling is used. This results in smooth edges.\n\n    >>> aug = iaa.BlendAlphaFrequencyNoise(\n    >>>     foreground=iaa.EdgeDetect(1.0),\n    >>>     upscale_method=\"linear\",\n    >>>     exponent=-2,\n    >>>     sigmoid=False)\n\n    Same as in the previous example, but with the exponent set to a constant\n    ``-2`` and the sigmoid deactivated, resulting in cloud-like patterns\n    without sharp edges.\n\n    >>> aug = iaa.BlendAlphaFrequencyNoise(\n    >>>     foreground=iaa.EdgeDetect(1.0),\n    >>>     sigmoid_thresh=iap.Normal(10.0, 5.0))\n\n    Same as the first example, but using a threshold for the sigmoid function\n    that is further to the right. This is more conservative, i.e. the generated\n    noise masks will be mostly black (values around ``0.0``), which means that\n    most of the original images (parameter/branch `background`) will be kept,\n    rather than using the results of the augmentation (parameter/branch\n    `foreground`).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, exponent=(-4, 4), foreground=None, background=None,\n                 per_channel=False, size_px_max=(4, 16), upscale_method=None,\n                 iterations=(1, 3), aggregation_method=[\"avg\", \"max\"],\n                 sigmoid=0.5, sigmoid_thresh=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=dangerous-default-value\n        upscale_method_default = iap.Choice([\"nearest\", \"linear\", \"cubic\"],\n                                            p=[0.05, 0.6, 0.35])\n        sigmoid_thresh_default = iap.Normal(0.0, 5.0)\n\n        noise = iap.FrequencyNoise(\n            exponent=exponent,\n            size_px_max=size_px_max,\n            upscale_method=(upscale_method\n                            if upscale_method is not None\n                            else upscale_method_default)\n        )\n\n        if iterations != 1:\n            noise = iap.IterativeNoiseAggregator(\n                noise,\n                iterations=iterations,\n                aggregation_method=aggregation_method\n            )\n\n        use_sigmoid = (\n            sigmoid is True\n            or (ia.is_single_number(sigmoid) and sigmoid >= 0.01))\n        if use_sigmoid:\n            noise = iap.Sigmoid.create_for_noise(\n                noise,\n                threshold=(sigmoid_thresh\n                           if sigmoid_thresh is not None\n                           else sigmoid_thresh_default),\n                activated=sigmoid\n            )\n\n        super(BlendAlphaFrequencyNoise, self).__init__(\n            factor=noise, foreground=foreground, background=background,\n            per_channel=per_channel,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass BlendAlphaSomeColors(BlendAlphaMask):\n    \"\"\"Blend images from two branches using colorwise masks.\n\n    This class generates masks that \"mark\" a few colors and replace the\n    pixels within these colors with the results of the foreground branch.\n    The remaining pixels are replaced with the results of the background\n    branch (usually the identity function). That allows to e.g. selectively\n    grayscale a few colors, while keeping other colors unchanged.\n\n    This class is a thin wrapper around\n    :class:`~imgaug.augmenters.blend.BlendAlphaMask` together with\n    :class:`~imgaug.augmenters.blend.SomeColorsMaskGen`.\n\n    .. note::\n\n        The underlying mask generator will produce an ``AssertionError`` for\n        batches that contain no images.\n\n    .. note::\n\n        Avoid using augmenters as children that affect pixel locations (e.g.\n        horizontal flips). See\n        :class:`~imgaug.augmenters.blend.BlendAlphaMask` for details.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.change_colorspaces_`.\n\n    Parameters\n    ----------\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    nb_bins : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.SomeColorsMaskGen`.\n\n    smoothness : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.SomeColorsMaskGen`.\n\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.SomeColorsMaskGen`.\n\n    rotation_deg : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.SomeColorsMaskGen`.\n\n    from_colorspace : str, optional\n        See :class:`~imgaug.augmenters.blend.SomeColorsMaskGen`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlphaSomeColors(iaa.Grayscale(1.0))\n\n    Create an augmenter that turns randomly removes some colors in images by\n    grayscaling them.\n\n    >>> aug = iaa.BlendAlphaSomeColors(iaa.TotalDropout(1.0))\n\n    Create an augmenter that removes some colors in images by replacing them\n    with black pixels.\n\n    >>> aug = iaa.BlendAlphaSomeColors(\n    >>>     iaa.MultiplySaturation(0.5), iaa.MultiplySaturation(1.5))\n\n    Create an augmenter that desaturates some colors and increases the\n    saturation of the remaining ones.\n\n    >>> aug = iaa.BlendAlphaSomeColors(\n    >>>     iaa.AveragePooling(7), alpha=[0.0, 1.0], smoothness=0.0)\n\n    Create an augmenter that applies average pooling to some colors.\n    Each color tune is either selected (alpha of ``1.0``) or not\n    selected (``0.0``). There is no gradual change between similar colors.\n\n    >>> aug = iaa.BlendAlphaSomeColors(\n    >>>     iaa.AveragePooling(7), nb_bins=2, smoothness=0.0)\n\n    Create an augmenter that applies average pooling to some colors.\n    Choose on average half of all colors in images for the blending operation.\n\n    >>> aug = iaa.BlendAlphaSomeColors(\n    >>>     iaa.AveragePooling(7), from_colorspace=\"BGR\")\n\n    Create an augmenter that applies average pooling to some colors with\n    input images being in BGR colorspace.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, foreground=None, background=None,\n                 nb_bins=(5, 15), smoothness=(0.1, 0.3),\n                 alpha=[0.0, 1.0], rotation_deg=(0, 360),\n                 from_colorspace=\"RGB\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=dangerous-default-value\n        super(BlendAlphaSomeColors, self).__init__(\n            SomeColorsMaskGen(\n                nb_bins=nb_bins,\n                smoothness=smoothness,\n                alpha=alpha,\n                rotation_deg=rotation_deg,\n                from_colorspace=from_colorspace\n            ),\n            foreground=foreground,\n            background=background,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass BlendAlphaHorizontalLinearGradient(BlendAlphaMask):\n    \"\"\"Blend images from two branches along a horizontal linear gradient.\n\n    This class generates a horizontal linear gradient mask (i.e. usually a\n    mask with low values on the left and high values on the right) and\n    alphas-blends between foreground and background branch using that\n    mask.\n\n    This class is a thin wrapper around\n    :class:`~imgaug.augmenters.blend.BlendAlphaMask` together with\n    :class:`~imgaug.augmenters.blend.HorizontalLinearGradientMaskGen`.\n\n    .. note::\n\n        Avoid using augmenters as children that affect pixel locations (e.g.\n        horizontal flips). See\n        :class:`~imgaug.augmenters.blend.BlendAlphaMask` for details.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.blend.BlendAlphaMask`.\n\n    Parameters\n    ----------\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    min_value : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.HorizontalLinearGradientMaskGen`.\n\n    max_value : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.HorizontalLinearGradientMaskGen`.\n\n    start_at : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.HorizontalLinearGradientMaskGen`.\n\n    end_at : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.HorizontalLinearGradientMaskGen`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlphaHorizontalLinearGradient(iaa.AddToHue((-100, 100)))\n\n    Create an augmenter that randomizes the hue towards the right of the\n    image.\n\n    >>> aug = iaa.BlendAlphaHorizontalLinearGradient(\n    >>>     iaa.TotalDropout(1.0),\n    >>>     min_value=0.2, max_value=0.8)\n\n    Create an augmenter that replaces pixels towards the right with darker\n    and darker values. However it always keeps at least\n    20% (``1.0 - max_value``) of the original pixel value on the far right\n    and always replaces at least 20% on the far left (``min_value=0.2``).\n\n    >>> aug = iaa.BlendAlphaHorizontalLinearGradient(\n    >>>     iaa.AveragePooling(11),\n    >>>     start_at=(0.0, 1.0), end_at=(0.0, 1.0))\n\n    Create an augmenter that blends with an average-pooled image according\n    to a horizontal gradient that starts at a random x-coordinate and reaches\n    its maximum at another random x-coordinate. Due to that randomness,\n    the gradient may increase towards the left or right.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, foreground=None, background=None,\n                 min_value=(0.0, 0.2), max_value=(0.8, 1.0),\n                 start_at=(0.0, 0.2), end_at=(0.8, 1.0),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(BlendAlphaHorizontalLinearGradient, self).__init__(\n            HorizontalLinearGradientMaskGen(\n                min_value=min_value,\n                max_value=max_value,\n                start_at=start_at,\n                end_at=end_at\n            ),\n            foreground=foreground,\n            background=background,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass BlendAlphaVerticalLinearGradient(BlendAlphaMask):\n    \"\"\"Blend images from two branches along a vertical linear gradient.\n\n    This class generates a vertical linear gradient mask (i.e. usually a\n    mask with low values on the left and high values on the right) and\n    alphas-blends between foreground and background branch using that\n    mask.\n\n    This class is a thin wrapper around\n    :class:`~imgaug.augmenters.blend.BlendAlphaMask` together with\n    :class:`~imgaug.augmenters.blend.VerticalLinearGradientMaskGen`.\n\n    .. note::\n\n        Avoid using augmenters as children that affect pixel locations (e.g.\n        horizontal flips). See\n        :class:`~imgaug.augmenters.blend.BlendAlphaMask` for details.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.blend.BlendAlphaMask`.\n\n    Parameters\n    ----------\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    min_value : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.VerticalLinearGradientMaskGen`.\n\n    max_value : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.VerticalLinearGradientMaskGen`.\n\n    start_at : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.VerticalLinearGradientMaskGen`.\n\n    end_at : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.VerticalLinearGradientMaskGen`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlphaVerticalLinearGradient(iaa.AddToHue((-100, 100)))\n\n    Create an augmenter that randomizes the hue towards the bottom of the\n    image.\n\n    >>> aug = iaa.BlendAlphaVerticalLinearGradient(\n    >>>     iaa.TotalDropout(1.0),\n    >>>     min_value=0.2, max_value=0.8)\n\n    Create an augmenter that replaces pixels towards the bottom with darker\n    and darker values. However it always keeps at least\n    20% (``1.0 - max_value``) of the original pixel value on the far bottom\n    and always replaces at least 20% on the far top (``min_value=0.2``).\n\n    >>> aug = iaa.BlendAlphaVerticalLinearGradient(\n    >>>     iaa.AveragePooling(11),\n    >>>     start_at=(0.0, 1.0), end_at=(0.0, 1.0))\n\n    Create an augmenter that blends with an average-pooled image according\n    to a vertical gradient that starts at a random y-coordinate and reaches\n    its maximum at another random y-coordinate. Due to that randomness,\n    the gradient may increase towards the bottom or top.\n\n    >>> aug = iaa.BlendAlphaVerticalLinearGradient(\n    >>>     iaa.Clouds(),\n    >>>     start_at=(0.15, 0.35), end_at=0.0)\n\n    Create an augmenter that draws clouds in roughly the top quarter of the\n    image.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, foreground=None, background=None,\n                 min_value=(0.0, 0.2), max_value=(0.8, 1.0),\n                 start_at=(0.0, 0.2), end_at=(0.8, 1.0),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(BlendAlphaVerticalLinearGradient, self).__init__(\n            VerticalLinearGradientMaskGen(\n                min_value=min_value,\n                max_value=max_value,\n                start_at=start_at,\n                end_at=end_at\n            ),\n            foreground=foreground,\n            background=background,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass BlendAlphaRegularGrid(BlendAlphaMask):\n    \"\"\"Blend images from two branches according to a regular grid.\n\n    This class generates for each image a mask that splits the image into a\n    grid-like pattern of ``H`` rows and ``W`` columns. Each cell is then\n    filled with an alpha value, sampled randomly per cell.\n\n    The difference to :class:`AlphaBlendCheckerboard` is that this class\n    samples random alpha values per grid cell, while in the checkerboard the\n    alpha values follow a fixed pattern.\n\n    This class is a thin wrapper around\n    :class:`~imgaug.augmenters.blend.BlendAlphaMask` together with\n    :class:`~imgaug.augmenters.blend.RegularGridMaskGen`.\n\n    .. note::\n\n        Avoid using augmenters as children that affect pixel locations (e.g.\n        horizontal flips). See\n        :class:`~imgaug.augmenters.blend.BlendAlphaMask` for details.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.blend.BlendAlphaMask`.\n\n    Parameters\n    ----------\n    nb_rows : int or tuple of int or list of int or imgaug.parameters.StochasticParameter\n        Number of rows of the checkerboard.\n        See :class:`~imgaug.augmenters.blend.CheckerboardMaskGen` for details.\n\n    nb_cols : int or tuple of int or list of int or imgaug.parameters.StochasticParameter\n        Number of columns of the checkerboard. Analogous to `nb_rows`.\n        See :class:`~imgaug.augmenters.blend.CheckerboardMaskGen` for details.\n\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Alpha value of each cell.\n\n        * If ``number``: Exactly that value will be used for all images.\n        * If ``tuple`` ``(a, b)``: A random value will be uniformly sampled\n          per image from the interval ``[a, b]``.\n        * If ``list``: A random value will be picked per image from that list.\n        * If ``StochasticParameter``: That parameter will be queried once\n          per batch for ``(N,)`` values -- one per image.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlphaRegularGrid(nb_rows=(4, 6), nb_cols=(1, 4),\n    >>>                                 foreground=iaa.Multiply(0.0))\n\n    Create an augmenter that places a ``HxW`` grid on each image, where\n    ``H`` (rows) is randomly and uniformly sampled from the interval ``[4, 6]``\n    and ``W`` is analogously sampled from the interval ``[1, 4]``. Roughly\n    half of the cells in the grid are filled with ``0.0``, the remaining ones\n    are unaltered. Which cells exactly are \"dropped\" is randomly decided\n    per image. The resulting effect is similar to\n    :class:`~imgaug.augmenters.arithmetic.CoarseDropout`.\n\n    >>> aug = iaa.BlendAlphaRegularGrid(nb_rows=2, nb_cols=2,\n    >>>                                 foreground=iaa.Multiply(0.0),\n    >>>                                 background=iaa.AveragePooling(8),\n    >>>                                 alpha=[0.0, 0.0, 1.0])\n\n    Create an augmenter that always placed ``2x2`` cells on each image\n    and sets about ``1/3`` of them to zero (foreground branch) and\n    the remaining ``2/3`` to a pixelated version (background branch).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, nb_rows, nb_cols,\n                 foreground=None, background=None,\n                 alpha=[0.0, 1.0],\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=dangerous-default-value\n        super(BlendAlphaRegularGrid, self).__init__(\n            RegularGridMaskGen(\n                nb_rows=nb_rows,\n                nb_cols=nb_cols,\n                alpha=alpha\n            ),\n            foreground=foreground,\n            background=background,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass BlendAlphaCheckerboard(BlendAlphaMask):\n    \"\"\"Blend images from two branches according to a checkerboard pattern.\n\n    This class generates for each image a mask following a checkboard layout of\n    ``H`` rows and ``W`` columns. Each cell is then filled with either\n    ``1.0`` or ``0.0``. The cell at the top-left is always ``1.0``. Its right\n    and bottom neighbour cells are ``0.0``. The 4-neighbours of any cell always\n    have a value opposite to the cell's value (``0.0`` vs. ``1.0``).\n\n    This class is a thin wrapper around\n    :class:`~imgaug.augmenters.blend.BlendAlphaMask` together with\n    :class:`~imgaug.augmenters.blend.CheckerboardMaskGen`.\n\n    .. note::\n\n        Avoid using augmenters as children that affect pixel locations (e.g.\n        horizontal flips). See\n        :class:`~imgaug.augmenters.blend.BlendAlphaMask` for details.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.blend.BlendAlphaMask`.\n\n    Parameters\n    ----------\n    nb_rows : int or tuple of int or list of int or imgaug.parameters.StochasticParameter\n        Number of rows of the checkerboard.\n        See :class:`~imgaug.augmenters.blend.CheckerboardMaskGen` for details.\n\n    nb_cols : int or tuple of int or list of int or imgaug.parameters.StochasticParameter\n        Number of columns of the checkerboard. Analogous to `nb_rows`.\n        See :class:`~imgaug.augmenters.blend.CheckerboardMaskGen` for details.\n\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlphaCheckerboard(nb_rows=2, nb_cols=(1, 4),\n    >>>                                  foreground=iaa.AddToHue((-100, 100)))\n\n    Create an augmenter that places a ``HxW`` grid on each image, where\n    ``H`` (rows) is always ``2`` and ``W`` is randomly and uniformly sampled\n    from the interval ``[1, 4]``. For half of the cells in the grid the hue\n    is randomly modified, the other half of the cells is unaltered.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, nb_rows, nb_cols,\n                 foreground=None, background=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(BlendAlphaCheckerboard, self).__init__(\n            CheckerboardMaskGen(\n                nb_rows=nb_rows,\n                nb_cols=nb_cols\n            ),\n            foreground=foreground,\n            background=background,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass BlendAlphaSegMapClassIds(BlendAlphaMask):\n    \"\"\"Blend images from two branches based on segmentation map ids.\n\n    This class generates masks that are ``1.0`` at pixel locations covered\n    by specific classes in segmentation maps.\n\n    This class is a thin wrapper around\n    :class:`~imgaug.augmenters.blend.BlendAlphaMask` together with\n    :class:`~imgaug.augmenters.blend.SegMapClassIdsMaskGen`.\n\n    .. note::\n\n        Avoid using augmenters as children that affect pixel locations (e.g.\n        horizontal flips). See\n        :class:`~imgaug.augmenters.blend.BlendAlphaMask` for details.\n\n    .. note::\n\n        Segmentation maps can have multiple channels. If that is the case\n        then for each position ``(x, y)`` it is sufficient that any class id\n        in any channel matches one of the desired class ids.\n\n    .. note::\n\n        This class will produce an ``AssertionError`` if there are no\n        segmentation maps in a batch.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.blend.BlendAlphaMask`.\n\n    Parameters\n    ----------\n    class_ids : int or tuple of int or list of int or imgaug.parameters.StochasticParameter\n        See :class:`~imgaug.augmenters.blend.SegMapClassIdsMaskGen`.\n\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    nb_sample_classes : None or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.SegMapClassIdsMaskGen`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlphaSegMapClassIds(\n    >>>     [1, 3],\n    >>>     foreground=iaa.AddToHue((-100, 100)))\n\n    Create an augmenter that randomizes the hue wherever the segmentation maps\n    contain the classes ``1`` or ``3``.\n\n    >>> aug = iaa.BlendAlphaSegMapClassIds(\n    >>>     [1, 2, 3, 4],\n    >>>     nb_sample_classes=2,\n    >>>     foreground=iaa.GaussianBlur(3.0))\n\n    Create an augmenter that randomly picks ``2`` classes from the\n    list ``[1, 2, 3, 4]`` and blurs the image content wherever these classes\n    appear in the segmentation map. Note that as the sampling of class ids\n    happens *with replacement*, it is not guaranteed to sample two *unique*\n    class ids.\n\n    >>> aug = iaa.Sometimes(0.2,\n    >>>     iaa.BlendAlphaSegMapClassIds(\n    >>>         2,\n    >>>         background=iaa.TotalDropout(1.0)))\n\n    Create an augmenter that zeros for roughly every fifth image all\n    image pixels that do *not* belong to class id ``2`` (note that the\n    `background` branch was used, not the `foreground` branch).\n    Example use case: Human body landmark detection where both the\n    landmarks/keypoints and the body segmentation map are known. Train the\n    model to detect landmarks and sometimes remove all non-body information\n    to force the model to become more independent of the background.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 class_ids,\n                 foreground=None, background=None,\n                 nb_sample_classes=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(BlendAlphaSegMapClassIds, self).__init__(\n            SegMapClassIdsMaskGen(\n                class_ids=class_ids,\n                nb_sample_classes=nb_sample_classes\n            ),\n            foreground=foreground,\n            background=background,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass BlendAlphaBoundingBoxes(BlendAlphaMask):\n    \"\"\"Blend images from two branches based on areas enclosed in bounding boxes.\n\n    This class generates masks that are ``1.0`` within bounding boxes of given\n    labels. A mask pixel will be set to ``1.0`` if *at least* one bounding box\n    covers the area and has one of the requested labels.\n\n    This class is a thin wrapper around\n    :class:`~imgaug.augmenters.blend.BlendAlphaMask` together with\n    :class:`~imgaug.augmenters.blend.BoundingBoxesMaskGen`.\n\n    .. note::\n\n        Avoid using augmenters as children that affect pixel locations (e.g.\n        horizontal flips). See\n        :class:`~imgaug.augmenters.blend.BlendAlphaMask` for details.\n\n    .. note::\n\n        This class will produce an ``AssertionError`` if there are no\n        bounding boxes in a batch.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.blend.BlendAlphaMask`.\n\n    Parameters\n    ----------\n    labels : None or str or list of str or imgaug.parameters.StochasticParameter\n        See :class:`~imgaug.augmenters.blend.BoundingBoxesMaskGen`.\n\n    foreground : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the foreground branch.\n        High alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the foreground branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    background : None or imgaug.augmenters.meta.Augmenter or iterable of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) that make up the background branch.\n        Low alpha values will show this branch's results.\n\n            * If ``None``, then the input images will be reused as the output\n              of the background branch.\n            * If ``Augmenter``, then that augmenter will be used as the branch.\n            * If iterable of ``Augmenter``, then that iterable will be\n              converted into a ``Sequential`` and used as the augmenter.\n\n    nb_sample_labels : None or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.blend.BoundingBoxesMaskGen`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BlendAlphaBoundingBoxes(\"person\",\n    >>>                                   foreground=iaa.Grayscale(1.0))\n\n    Create an augmenter that removes color within bounding boxes having the\n    label ``person``.\n\n    >>> aug = iaa.BlendAlphaBoundingBoxes([\"person\", \"car\"],\n    >>>                                   foreground=iaa.AddToHue((-255, 255)))\n\n    Create an augmenter that randomizes the hue within bounding boxes that\n    have the label ``person`` or ``car``.\n\n    >>> aug = iaa.BlendAlphaBoundingBoxes([\"person\", \"car\"],\n    >>>                                   foreground=iaa.AddToHue((-255, 255)),\n    >>>                                   nb_sample_labels=1)\n\n    Create an augmenter that randomizes the hue within bounding boxes that\n    have either the label ``person`` or ``car``. Only one label is picked per\n    image. Note that the sampling happens with replacement, so if\n    ``nb_sample_classes`` would be ``>1``, it could still lead to only one\n    *unique* label being sampled.\n\n    >>> aug = iaa.BlendAlphaBoundingBoxes(None,\n    >>>                                   background=iaa.Multiply(0.0))\n\n    Create an augmenter that zeros all pixels (``Multiply(0.0)``)\n    that are *not* (``background`` branch) within bounding boxes of\n    *any* (``None``) label. In other words, all pixels outside of bounding\n    boxes become black.\n    Note that we don't use ``TotalDropout`` here, because by default it will\n    also remove all coordinate-based augmentables, which will break the\n    blending of such inputs.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 labels,\n                 foreground=None, background=None,\n                 nb_sample_labels=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(BlendAlphaBoundingBoxes, self).__init__(\n            BoundingBoxesMaskGen(\n                labels=labels,\n                nb_sample_labels=nb_sample_labels\n            ),\n            foreground=foreground,\n            background=background,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n@six.add_metaclass(ABCMeta)\nclass IBatchwiseMaskGenerator(object):\n    \"\"\"Interface for classes generating masks for batches.\n\n    Child classes are supposed to receive a batch and generate an iterable\n    of masks, one per row (i.e. image), matching the row shape (i.e. image\n    shape). This is used in :class:`~imgaug.augmenters.blend.BlendAlphaMask`.\n\n    Added in 0.4.0.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def draw_masks(self, batch, random_state=None):\n        \"\"\"Generate a mask with given shape.\n\n        Parameters\n        ----------\n        batch : imgaug.augmentables.batches._BatchInAugmentation\n            Shape of the mask to sample.\n\n        random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n            A seed or random number generator to use during the sampling\n            process. If ``None``, the global RNG will be used.\n            See also :func:`~imgaug.augmenters.meta.Augmenter.__init__`\n            for a similar parameter with more details.\n\n        Returns\n        -------\n        iterable of ndarray\n            Masks, one per row in the batch.\n            Each mask must be a ``float32`` array in interval ``[0.0, 1.0]``.\n            It must either have the same shape as the row (i.e. the image)\n            or shape ``(H, W)`` if all channels are supposed to have the\n            same mask.\n\n        \"\"\"\n\n\nclass StochasticParameterMaskGen(IBatchwiseMaskGenerator):\n    \"\"\"Mask generator that queries stochastic parameters for mask values.\n\n    This class receives batches for which to generate masks, iterates over\n    the batch rows (i.e. images) and generates one mask per row.\n    For a row with shape ``(H, W, C)`` (= image shape), it generates\n    either a ``(H, W)`` mask (if ``per_channel`` is false-like) or a\n    ``(H, W, C)`` mask (if ``per_channel`` is true-like).\n    The ``per_channel`` is sampled per batch for each row/image.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    parameter : imgaug.parameters.StochasticParameter\n        Stochastic parameter to draw mask samples from.\n        Expected to return values in interval ``[0.0, 1.0]`` (not all\n        stochastic parameters do that) and must be able to handle sampling\n        shapes ``(H, W)`` and ``(H, W, C)`` (all stochastic parameters should\n        do that).\n\n    per_channel : bool or float or imgaug.parameters.StochasticParameter, optional\n        Whether to use the same mask for all channels (``False``)\n        or to sample a new mask for each channel (``True``).\n        If this value is a float ``p``, then for ``p`` percent of all rows\n        (i.e. images) `per_channel` will be treated as ``True``, otherwise\n        as ``False``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, parameter, per_channel):\n        super(StochasticParameterMaskGen, self).__init__()\n        self.parameter = parameter\n        self.per_channel = iap.handle_probability_param(per_channel,\n                                                        \"per_channel\")\n\n    # Added in 0.4.0.\n    def draw_masks(self, batch, random_state=None):\n        \"\"\"\n        See :func:`~imgaug.augmenters.blend.IBatchwiseMaskGenerator.draw_masks`.\n\n        \"\"\"\n        shapes = batch.get_rowwise_shapes()\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        per_channel = self.per_channel.draw_samples((len(shapes),),\n                                                    random_state=random_state)\n\n        return [self._draw_mask(shape, random_state, per_channel_i)\n                for shape, per_channel_i\n                in zip(shapes, per_channel)]\n\n    # Added in 0.4.0.\n    def _draw_mask(self, shape, random_state, per_channel):\n        if len(shape) == 2 or per_channel >= 0.5:\n            mask = self.parameter.draw_samples(shape,\n                                               random_state=random_state)\n        else:\n            # TODO When this was wrongly sampled directly as (H,W,C) no\n            #      test for AlphaElementwise ended up failing. That should not\n            #      happen.\n\n            # We are guarantueed here to have (H, W, C) as shape (H, W) is\n            # handled by the above block.\n            # As the mask is not channelwise, we will just return (H, W)\n            # instead of (H, W, C).\n            mask = self.parameter.draw_samples(shape[0:2],\n                                               random_state=random_state)\n\n        # mask has no elements if height or width in shape is 0\n        if mask.size > 0:\n            assert 0 <= mask.item(0) <= 1.0, (\n                \"Expected 'parameter' samples to be in the interval \"\n                \"[0.0, 1.0]. Got min %.4f and max %.4f.\" % (\n                    np.min(mask), np.max(mask),))\n\n        return mask\n\n\nclass SomeColorsMaskGen(IBatchwiseMaskGenerator):\n    \"\"\"Generator that produces masks based on some similar colors in images.\n\n    This class receives batches for which to generate masks, iterates over\n    the batch rows (i.e. images) and generates one mask per row.\n    The mask contains high alpha values for some colors, while other colors\n    get low mask values. Which colors are chosen is random. How wide or\n    narrow the selection is (e.g. very specific blue tone or all blue-ish\n    colors) is determined by the hyperparameters.\n\n    The color selection method performs roughly the following steps:\n\n      1. Split the full color range of the hue in ``HSV`` into ``nb_bins``\n         bins (i.e. ``256/nb_bins`` different possible hue tones).\n      2. Shift the bins by ``rotation_deg`` degrees. (This way, the ``0th``\n         bin does not always start at exactly ``0deg`` of hue.)\n      3. Sample ``alpha`` values for each bin.\n      4. Repeat the ``nb_bins`` bins until there are ``256`` bins.\n      5. Smoothen the alpha values of neighbouring bins using a gaussian\n         kernel. The kernel's ``sigma`` is derived from ``smoothness``.\n      6. Associate all hue values in the image with the corresponding bin's\n         alpha value. This results in the alpha mask.\n\n    .. note::\n\n        This mask generator will produce an ``AssertionError`` for batches\n        that contain no images.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.change_colorspaces_`.\n\n    Parameters\n    ----------\n    nb_bins : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of bins. For ``B`` bins, each bin denotes roughly ``360/B``\n        degrees of colors in the hue channel. Lower values lead to a coarser\n        selection of colors. Expected value range is ``[2, 256]``.\n\n            * If ``int``: Exactly that value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A random value will be uniformly sampled\n              per image from the discrete interval ``[a..b]``.\n            * If ``list``: A random value will be picked per image from that\n              list.\n            * If ``StochasticParameter``: That parameter will be queried once\n              per batch for ``(N,)`` values -- one per image.\n\n    smoothness : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Strength of the 1D gaussian kernel applied to the sampled binwise\n        alpha values. Larger values will lead to more similar grayscaling of\n        neighbouring colors. Expected value range is ``[0.0, 1.0]``.\n\n            * If ``number``: Exactly that value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A random value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If ``list``: A random value will be picked per image from that\n              list.\n            * If ``StochasticParameter``: That parameter will be queried once\n              per batch for ``(N,)`` values -- one per image.\n\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Parameter to sample binwise alpha blending factors from. Expected\n        value range is ``[0.0, 1.0]``.  Note that the alpha values will be\n        smoothed between neighbouring bins. Hence, it is usually a good idea\n        to set this so that the probability distribution peaks are around\n        ``0.0`` and ``1.0``, e.g. via a list ``[0.0, 1.0]`` or a ``Beta``\n        distribution.\n        It is not recommended to set this to a deterministic value, otherwise\n        all bins and hence all pixels in the generated mask will have the\n        same value.\n\n            * If ``number``: Exactly that value will be used for all bins.\n            * If ``tuple`` ``(a, b)``: A random value will be uniformly sampled\n              per bin from the interval ``[a, b]``.\n            * If ``list``: A random value will be picked per bin from that list.\n            * If ``StochasticParameter``: That parameter will be queried once\n              per batch for ``(N*B,)`` values -- one per image and bin.\n\n    rotation_deg : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Rotiational shift of each bin as a fraction of ``360`` degrees.\n        E.g. ``0.0`` will not shift any bins, while a value of ``0.5`` will\n        shift by around ``180`` degrees. This shift is mainly used so that\n        the ``0th`` bin does not always start at ``0deg``. Expected value\n        range is ``[-360, 360]``. This parameter can usually be kept at the\n        default value.\n\n            * If ``number``: Exactly that value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A random value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If ``list``: A random value will be picked per image from that\n              list.\n            * If ``StochasticParameter``: That parameter will be queried once\n              per batch for ``(N,)`` values -- one per image.\n\n    from_colorspace : str, optional\n        The source colorspace (of the input images).\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    # TODO colorlib.CSPACE_RGB produces 'has no attribute' error?\n    def __init__(self, nb_bins=(5, 15), smoothness=(0.1, 0.3),\n                 alpha=[0.0, 1.0], rotation_deg=(0, 360),\n                 from_colorspace=\"RGB\"):\n        # pylint: disable=dangerous-default-value\n        super(SomeColorsMaskGen, self).__init__()\n\n        self.nb_bins = iap.handle_discrete_param(\n            nb_bins, \"nb_bins\", value_range=(1, 256),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.smoothness = iap.handle_continuous_param(\n            smoothness, \"smoothness\", value_range=(0.0, 1.0),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.alpha = iap.handle_continuous_param(\n            alpha, \"alpha\", value_range=(0.0, 1.0),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.rotation_deg = iap.handle_continuous_param(\n            rotation_deg, \"rotation_deg\", value_range=(-360, 360),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.from_colorspace = from_colorspace\n\n        self.sigma_max = 10.0\n\n    # Added in 0.4.0.\n    def draw_masks(self, batch, random_state=None):\n        \"\"\"\n        See :func:`~imgaug.augmenters.blend.IBatchwiseMaskGenerator.draw_masks`.\n\n        \"\"\"\n        assert batch.images is not None, (\n            \"Can only generate masks for batches that contain images, but \"\n            \"got a batch without images.\")\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        samples = self._draw_samples(batch, random_state=random_state)\n\n        return [self._draw_mask(image, i, samples)\n                for i, image\n                in enumerate(batch.images)]\n\n    # Added in 0.4.0.\n    def _draw_mask(self, image, image_idx, samples):\n        return self.generate_mask(\n            image,\n            samples[0][image_idx],\n            samples[1][image_idx] * self.sigma_max,\n            samples[2][image_idx],\n            self.from_colorspace)\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        nb_rows = batch.nb_rows\n        nb_bins = self.nb_bins.draw_samples((nb_rows,),\n                                            random_state=random_state)\n        smoothness = self.smoothness.draw_samples((nb_rows,),\n                                                  random_state=random_state)\n        alpha = self.alpha.draw_samples((np.sum(nb_bins),),\n                                        random_state=random_state)\n        rotation_deg = self.rotation_deg.draw_samples(\n            (nb_rows,), random_state=random_state)\n\n        nb_bins = np.clip(nb_bins, 1, 256)\n        smoothness = np.clip(smoothness, 0.0, 1.0)\n        alpha = np.clip(alpha, 0.0, 1.0)\n        rotation_bins = np.mod(\n            np.round(rotation_deg * (256/360)).astype(np.int32),\n            256)\n\n        binwise_alphas = _split_1d_array_to_list(alpha, nb_bins)\n\n        return binwise_alphas, smoothness, rotation_bins\n\n    @classmethod\n    def generate_mask(cls, image, binwise_alphas, sigma,\n                      rotation_bins, from_colorspace):\n        \"\"\"Generate a colorwise alpha mask for a single image.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : ndarray\n            Image for which to generate the mask. Must have shape ``(H,W,3)``\n            in colorspace `from_colorspace`.\n\n        binwise_alphas : ndarray\n            Alpha values of shape ``(B,)`` with ``B`` in ``[1, 256]``\n            and values in interval ``[0.0, 1.0]``. Will be upscaled to\n            256 bins by simple repetition. Each bin represents ``1/256`` th\n            of the hue.\n\n        sigma : float\n            Sigma of the 1D gaussian kernel applied to the upscaled binwise\n            alpha value array.\n\n        rotation_bins : int\n            By how much to rotate the 256 bin alpha array. The rotation is\n            given in number of bins.\n\n        from_colorspace : str\n            Colorspace of the input image. One of\n            ``imgaug.augmenters.color.CSPACE_*``.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` mask array of shape ``(H, W)`` with values in\n            ``[0.0, 1.0]``\n\n        \"\"\"\n        # import has to be deferred, otherwise python 2.7 fails\n        from . import color as colorlib\n\n        image_hsv = colorlib.change_colorspace_(\n            np.copy(image),\n            to_colorspace=colorlib.CSPACE_HSV,\n            from_colorspace=from_colorspace)\n\n        if 0 in image_hsv.shape[0:2]:\n            return np.zeros(image_hsv.shape[0:2], dtype=np.float32)\n\n        binwise_alphas = cls._upscale_to_256_alpha_bins(binwise_alphas)\n        binwise_alphas = cls._rotate_alpha_bins(binwise_alphas, rotation_bins)\n        binwise_alphas_smooth = cls._smoothen_alphas(binwise_alphas, sigma)\n\n        mask = cls._generate_pixelwise_alpha_mask(image_hsv,\n                                                  binwise_alphas_smooth)\n\n        return mask\n\n    # Added in 0.4.0.\n    @classmethod\n    def _upscale_to_256_alpha_bins(cls, alphas):\n        # repeat alphas bins so that B sampled bins become 256 bins\n        nb_bins = len(alphas)\n        nb_repeats_per_bin = int(np.ceil(256/nb_bins))\n        alphas = np.repeat(alphas, (nb_repeats_per_bin,))\n        alphas = alphas[0:256]\n        return alphas\n\n    # Added in 0.4.0.\n    @classmethod\n    def _rotate_alpha_bins(cls, alphas, rotation_bins):\n        # e.g. for offset 2: abcdef -> cdefab\n        # note: offset here is expected to be in [0, 256]\n        if rotation_bins > 0:\n            alphas = np.roll(alphas, -rotation_bins)\n        return alphas\n\n    # Added in 0.4.0.\n    @classmethod\n    def _smoothen_alphas(cls, alphas, sigma):\n        if sigma <= 0.0+1e-2:\n            return alphas\n\n        ksize = max(int(sigma * 2.5), 3)\n        ksize_y, ksize_x = (1, ksize)\n        if ksize_x % 2 == 0:\n            ksize_x += 1\n\n        # we fake here cv2.BORDER_WRAP, because GaussianBlur does not\n        # support that mode, i.e. we want:\n        #   cdefgh|abcdefgh|abcdefg\n        alphas = np.concatenate([\n            alphas[-ksize_x:],\n            alphas,\n            alphas[:ksize_x],\n        ])\n\n        alphas = cv2.GaussianBlur(\n            _normalize_cv2_input_arr_(alphas[np.newaxis, :]),\n            ksize=(ksize_x, ksize_y),\n            sigmaX=sigma, sigmaY=sigma,\n            borderType=cv2.BORDER_REPLICATE\n        )[0, :]\n\n        # revert fake BORDER_WRAP\n        alphas = alphas[ksize_x:-ksize_x]\n\n        return alphas\n\n    # Added in 0.4.0.\n    @classmethod\n    def _generate_pixelwise_alpha_mask(cls, image_hsv, hue_to_alpha):\n        hue = image_hsv[:, :, 0]\n        table = hue_to_alpha * 255\n        table = np.clip(np.round(table), 0, 255).astype(np.uint8)\n        mask = ia.apply_lut(hue, table)\n        return mask.astype(np.float32) / 255.0\n\n\n# Added in 0.4.0.\nclass _LinearGradientMaskGen(IBatchwiseMaskGenerator):\n    # Added in 0.4.0.\n    def __init__(self, axis, min_value=0.0, max_value=1.0,\n                 start_at=0.0, end_at=1.0):\n        self.axis = axis\n        self.min_value = iap.handle_continuous_param(\n            min_value, \"min_value\", value_range=(0.0, 1.0),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.max_value = iap.handle_continuous_param(\n            max_value, \"max_value\", value_range=(0.0, 1.0),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.start_at = iap.handle_continuous_param(\n            start_at, \"start_at\", value_range=(0.0, 1.0),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.end_at = iap.handle_continuous_param(\n            end_at, \"end_at\", value_range=(0.0, 1.0),\n            tuple_to_uniform=True, list_to_choice=True)\n\n    def draw_masks(self, batch, random_state=None):\n        \"\"\"\n        See :func:`~imgaug.augmenters.blend.IBatchwiseMaskGenerator.draw_masks`.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        shapes = batch.get_rowwise_shapes()\n        samples = self._draw_samples(len(shapes), random_state=random_state)\n\n        return [self._draw_mask(shape, i, samples)\n                for i, shape\n                in enumerate(shapes)]\n\n    # Added in 0.4.0.\n    def _draw_mask(self, shape, image_idx, samples):\n        return self.generate_mask(\n            shape,\n            samples[0][image_idx],\n            samples[1][image_idx],\n            samples[2][image_idx],\n            samples[3][image_idx])\n\n    # Added in 0.4.0.\n    def _draw_samples(self, nb_rows, random_state):\n        min_value = self.min_value.draw_samples((nb_rows,),\n                                                random_state=random_state)\n        max_value = self.max_value.draw_samples((nb_rows,),\n                                                random_state=random_state)\n        start_at = self.start_at.draw_samples(\n            (nb_rows,), random_state=random_state)\n        end_at = self.end_at.draw_samples(\n            (nb_rows,), random_state=random_state)\n\n        return min_value, max_value, start_at, end_at\n\n    @classmethod\n    @abstractmethod\n    def generate_mask(cls, shape, min_value, max_value, start_at, end_at):\n        \"\"\"Generate a horizontal gradient mask.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        shape : tuple of int\n            Shape of the image. The mask will have the same height and\n            width.\n\n        min_value : number\n            Minimum value of the gradient in interval ``[0.0, 1.0]``.\n\n        max_value : number\n            Maximum value of the gradient in interval ``[0.0, 1.0]``.\n\n        start_at : number\n            Position on the x-axis where the linear gradient starts, given as\n            a fraction of the axis size. Interval is ``[0.0, 1.0]``.\n\n        end_at : number\n            Position on the x-axis where the linear gradient ends, given as\n            a fraction of the axis size. Interval is ``[0.0, 1.0]``.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` mask array with same height and width as the image.\n            Values are in ``[0.0, 1.0]``.\n\n        \"\"\"\n\n    # Added in 0.4.0.\n    @classmethod\n    def _generate_mask(cls, shape, axis, min_value, max_value, start_at,\n                       end_at):\n        height, width = shape[0:2]\n\n        axis_size = shape[axis]\n        min_value = min(max(min_value, 0.0), 1.0)\n        max_value = min(max(max_value, 0.0), 1.0)\n\n        start_at_px = min(max(int(start_at * axis_size), 0), axis_size)\n        end_at_px = min(max(int(end_at * axis_size), 0), axis_size)\n\n        inverted = False\n        if end_at_px < start_at_px:\n            inverted = True\n            start_at_px, end_at_px = end_at_px, start_at_px\n\n        before_grad = np.full((start_at_px,), min_value,\n                              dtype=np.float32)\n        grad = np.linspace(start=min_value,\n                           stop=max_value,\n                           num=end_at_px - start_at_px,\n                           dtype=np.float32)\n        after_grad = np.full((axis_size - end_at_px,), max_value,\n                             dtype=np.float32)\n\n        mask = np.concatenate((\n            before_grad,\n            grad,\n            after_grad\n        ), axis=0)\n\n        if inverted:\n            mask = 1.0 - mask\n\n        if axis == 0:\n            mask = mask[:, np.newaxis]\n            mask = np.tile(mask, (1, width))\n        else:\n            mask = mask[np.newaxis, :]\n            mask = np.tile(mask, (height, 1))\n\n        return mask\n\n\nclass HorizontalLinearGradientMaskGen(_LinearGradientMaskGen):\n    \"\"\"Generator that produces horizontal linear gradient masks.\n\n    This class receives batches and produces for each row (i.e. image)\n    a horizontal linear gradient that matches the row's shape (i.e. image\n    shape). The gradient increases linearly from a minimum value to a\n    maximum value along the x-axis. The start and end points (i.e. where the\n    minimum value starts to increase and where it reaches the maximum)\n    may be defines as fractions of the width. E.g. for width ``100`` and\n    ``start=0.25``, ``end=0.75``, the gradient would have its minimum\n    in interval ``[0px, 25px]`` and its maximum in interval ``[75px, 100px]``.\n\n    Note that this has nothing to do with a *derivative* along the x-axis.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    min_value : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Minimum value that the mask will have up to the start point of the\n        linear gradient.\n        Note that `min_value` is allowed to be larger than `max_value`,\n        in which case the gradient will start at the (higher) `min_value`\n        and decrease towards the (lower) `max_value`.\n\n        * If ``number``: Exactly that value will be used for all images.\n        * If ``tuple`` ``(a, b)``: A random value will be uniformly sampled\n          per image from the interval ``[a, b]``.\n        * If ``list``: A random value will be picked per image from that list.\n        * If ``StochasticParameter``: That parameter will be queried once\n          per batch for ``(N,)`` values -- one per image.\n\n    max_value : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Maximum value that the mask will have at the end of the\n        linear gradient.\n\n        Datatypes are analogous to `min_value`.\n\n    start_at : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Position on the x-axis where the linear gradient starts, given as a\n        fraction of the axis size. Interval is ``[0.0, 1.0]``, where ``0.0``\n        is at the left of the image.\n        If ``end_at < start_at`` the gradient will be inverted.\n\n        Datatypes are analogous to `min_value`.\n\n    end_at : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Position on the x-axis where the linear gradient ends, given as a\n        fraction of the axis size. Interval is ``[0.0, 1.0]``, where ``0.0``\n        is at the right of the image.\n\n        Datatypes are analogous to `min_value`.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, min_value=(0.0, 0.2), max_value=(0.8, 1.0),\n                 start_at=(0.0, 0.2), end_at=(0.8, 1.0)):\n        super(HorizontalLinearGradientMaskGen, self).__init__(\n            axis=1,\n            min_value=min_value,\n            max_value=max_value,\n            start_at=start_at,\n            end_at=end_at)\n\n    @classmethod\n    def generate_mask(cls, shape, min_value, max_value, start_at, end_at):\n        \"\"\"Generate a linear horizontal gradient mask.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        shape : tuple of int\n            Shape of the image. The mask will have the same height and\n            width.\n\n        min_value : number\n            Minimum value of the gradient in interval ``[0.0, 1.0]``.\n\n        max_value : number\n            Maximum value of the gradient in interval ``[0.0, 1.0]``.\n\n        start_at : number\n            Position on the x-axis where the linear gradient starts, given as\n            a fraction of the axis size. Interval is ``[0.0, 1.0]``.\n\n        end_at : number\n            Position on the x-axis where the linear gradient ends, given as\n            a fraction of the axis size. Interval is ``[0.0, 1.0]``.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` mask array with same height and width as the image.\n            Values are in ``[0.0, 1.0]``.\n\n        \"\"\"\n        return cls._generate_mask(\n            axis=1,\n            shape=shape,\n            min_value=min_value,\n            max_value=max_value,\n            start_at=start_at,\n            end_at=end_at)\n\n\nclass VerticalLinearGradientMaskGen(_LinearGradientMaskGen):\n    \"\"\"Generator that produces vertical linear gradient masks.\n\n    See :class:`~imgaug.augmenters.blend.HorizontalLinearGradientMaskGen`\n    for details.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    min_value : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Minimum value that the mask will have up to the start point of the\n        linear gradient.\n        Note that `min_value` is allowed to be larger than `max_value`,\n        in which case the gradient will start at the (higher) `min_value`\n        and decrease towards the (lower) `max_value`.\n\n        * If ``number``: Exactly that value will be used for all images.\n        * If ``tuple`` ``(a, b)``: A random value will be uniformly sampled\n          per image from the interval ``[a, b]``.\n        * If ``list``: A random value will be picked per image from that list.\n        * If ``StochasticParameter``: That parameter will be queried once\n          per batch for ``(N,)`` values -- one per image.\n\n    max_value : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Maximum value that the mask will have at the end of the\n        linear gradient.\n\n        Datatypes are analogous to `min_value`.\n\n    start_at : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Position on the y-axis where the linear gradient starts, given as a\n        fraction of the axis size. Interval is ``[0.0, 1.0]``, where ``0.0``\n        is at the top of the image.\n        If ``end_at < start_at`` the gradient will be inverted.\n\n        Datatypes are analogous to `min_value`.\n\n    end_at : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Position on the x-axis where the linear gradient ends, given as a\n        fraction of the axis size. Interval is ``[0.0, 1.0]``, where ``1.0``\n        is at the bottom of the image.\n\n        Datatypes are analogous to `min_value`.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, min_value=(0.0, 0.2), max_value=(0.8, 1.0),\n                 start_at=(0.0, 0.2), end_at=(0.8, 1.0)):\n        super(VerticalLinearGradientMaskGen, self).__init__(\n            axis=0,\n            min_value=min_value,\n            max_value=max_value,\n            start_at=start_at,\n            end_at=end_at)\n\n    @classmethod\n    def generate_mask(cls, shape, min_value, max_value, start_at, end_at):\n        \"\"\"Generate a linear horizontal gradient mask.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        shape : tuple of int\n            Shape of the image. The mask will have the same height and\n            width.\n\n        min_value : number\n            Minimum value of the gradient in interval ``[0.0, 1.0]``.\n\n        max_value : number\n            Maximum value of the gradient in interval ``[0.0, 1.0]``.\n\n        start_at : number\n            Position on the x-axis where the linear gradient starts, given as\n            a fraction of the axis size. Interval is ``[0.0, 1.0]``.\n\n        end_at : number\n            Position on the x-axis where the linear gradient ends, given as\n            a fraction of the axis size. Interval is ``[0.0, 1.0]``.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` mask array with same height and width as the image.\n            Values are in ``[0.0, 1.0]``.\n\n        \"\"\"\n        return cls._generate_mask(\n            axis=0,\n            shape=shape,\n            min_value=min_value,\n            max_value=max_value,\n            start_at=start_at,\n            end_at=end_at)\n\n\nclass RegularGridMaskGen(IBatchwiseMaskGenerator):\n    \"\"\"Generate masks following a regular grid pattern.\n\n    This mask generator splits each image into a grid-like pattern of\n    ``H`` rows and ``W`` columns. Each cell is then filled with an alpha\n    value, sampled randomly per cell.\n\n    The difference to :class:`CheckerboardMaskGen` is that this mask generator\n    samples random alpha values per cell, while in the checkerboard the\n    alpha values follow a fixed pattern.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    nb_rows : int or tuple of int or list of int or imgaug.parameters.StochasticParameter\n        Number of rows of the regular grid.\n\n            * If ``int``: Exactly that value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A random value will be uniformly sampled\n              per image from the discrete interval ``[a..b]``.\n            * If ``list``: A random value will be picked per image from that\n              list.\n            * If ``StochasticParameter``: That parameter will be queried once\n              per batch for ``(N,)`` values -- one per image.\n\n    nb_cols : int or tuple of int or list of int or imgaug.parameters.StochasticParameter\n        Number of columns of the checkerboard. Analogous to `nb_rows`.\n\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Alpha value of each cell.\n\n        * If ``number``: Exactly that value will be used for all images.\n        * If ``tuple`` ``(a, b)``: A random value will be uniformly sampled\n          per image from the interval ``[a, b]``.\n        * If ``list``: A random value will be picked per image from that list.\n        * If ``StochasticParameter``: That parameter will be queried once\n          per batch for ``(N,)`` values -- one per image.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, nb_rows, nb_cols, alpha=[0.0, 1.0]):\n        # pylint: disable=dangerous-default-value\n        self.nb_rows = iap.handle_discrete_param(\n            nb_rows, \"nb_rows\", value_range=(1, None),\n            tuple_to_uniform=True, list_to_choice=True,\n            allow_floats=False)\n        self.nb_cols = iap.handle_discrete_param(\n            nb_cols, \"nb_cols\", value_range=(1, None),\n            tuple_to_uniform=True, list_to_choice=True,\n            allow_floats=False)\n        self.alpha = iap.handle_continuous_param(\n            alpha, \"alpha\", value_range=(0.0, 1.0),\n            tuple_to_uniform=True, list_to_choice=True)\n\n    def draw_masks(self, batch, random_state=None):\n        \"\"\"\n        See :func:`~imgaug.augmenters.blend.IBatchwiseMaskGenerator.draw_masks`.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        shapes = batch.get_rowwise_shapes()\n        nb_rows, nb_cols, alpha = self._draw_samples(len(shapes),\n                                                     random_state=random_state)\n\n        return [self.generate_mask(shape, nb_rows_i, nb_cols_i, alpha_i)\n                for shape, nb_rows_i, nb_cols_i, alpha_i\n                in zip(shapes, nb_rows, nb_cols, alpha)]\n\n    # Added in 0.4.0.\n    def _draw_samples(self, nb_images, random_state):\n        nb_rows = self.nb_rows.draw_samples((nb_images,),\n                                            random_state=random_state)\n        nb_cols = self.nb_cols.draw_samples((nb_images,),\n                                            random_state=random_state)\n        nb_alphas_per_img = nb_rows * nb_cols\n        alpha_raw = self.alpha.draw_samples(\n            (np.sum(nb_alphas_per_img),),\n            random_state=random_state)\n\n        alpha = _split_1d_array_to_list(alpha_raw, nb_alphas_per_img)\n\n        return nb_rows, nb_cols, alpha\n\n    @classmethod\n    def generate_mask(cls, shape, nb_rows, nb_cols, alphas):\n        \"\"\"Generate a mask following a checkerboard pattern.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        shape : tuple of int\n            Height and width of the output mask.\n\n        nb_rows : int\n            Number of rows of the checkerboard pattern.\n\n        nb_cols : int\n            Number of columns of the checkerboard pattern.\n\n        alphas : ndarray\n            1D or 2D array containing for each cell the alpha value, i.e.\n            ``nb_rows*nb_cols`` values.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` mask array with same height and width as\n            ``segmap.shape``. Values are in ``[0.0, 1.0]``.\n\n        \"\"\"\n        from . import size as sizelib\n\n        height, width = shape[0:2]\n        if 0 in (height, width):\n            return np.zeros((height, width), dtype=np.float32)\n\n        nb_rows = min(max(nb_rows, 1), height)\n        nb_cols = min(max(nb_cols, 1), width)\n\n        cell_height = int(height / nb_rows)\n        cell_width = int(width / nb_cols)\n\n        # If there are more alpha values than nb_rows*nb_cols we reduce the\n        # number of alpha values.\n        alphas = alphas.flat[0:nb_rows*nb_cols]\n        assert alphas.size == nb_rows*nb_cols, (\n            \"Expected `alphas` to not contain less values than \"\n            \"`nb_rows * nb_cols` (both clipped to [1, height] and \"\n            \"[1, width] respectively). Got %d alpha values vs %d expected \"\n            \"values (nb_rows=%d, nb_cols=%d) for requested mask shape %s.\" % (\n                alphas.size, nb_rows * nb_cols, nb_rows, nb_cols,\n                (height, width)))\n        mask = alphas.astype(np.float32).reshape((nb_rows, nb_cols))\n        mask = np.repeat(mask, cell_height, axis=0)\n        mask = np.repeat(mask, cell_width, axis=1)\n\n        # if mask is too small, reflection pad it on all sides\n        missing_height = height - mask.shape[0]\n        missing_width = width - mask.shape[1]\n        top = int(np.floor(missing_height / 2))\n        bottom = int(np.ceil(missing_height / 2))\n        left = int(np.floor(missing_width / 2))\n        right = int(np.ceil(missing_width / 2))\n        mask = sizelib.pad(mask,\n                           top=top, right=right, bottom=bottom, left=left,\n                           mode=\"reflect\")\n\n        return mask\n\n\nclass CheckerboardMaskGen(IBatchwiseMaskGenerator):\n    \"\"\"Generate masks following a checkerboard-like pattern.\n\n    This mask generator splits each image into a regular grid of\n    ``H`` rows and ``W`` columns. Each cell is then filled with either\n    ``1.0`` or ``0.0``. The cell at the top-left is always ``1.0``. Its right\n    and bottom neighbour cells are ``0.0``. The 4-neighbours of any cell always\n    have a value opposite to the cell's value (``0.0`` vs. ``1.0``).\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    nb_rows : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of rows of the checkerboard.\n\n            * If ``int``: Exactly that value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A random value will be uniformly sampled\n              per image from the discrete interval ``[a..b]``.\n            * If ``list``: A random value will be picked per image from that\n              list.\n            * If ``StochasticParameter``: That parameter will be queried once\n              per batch for ``(N,)`` values -- one per image.\n\n    nb_cols : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of columns of the checkerboard. Analogous to `nb_rows`.\n\n    \"\"\"\n\n    def __init__(self, nb_rows, nb_cols):\n        self.grid = RegularGridMaskGen(nb_rows=nb_rows,\n                                       nb_cols=nb_cols,\n                                       alpha=1)\n\n    @property\n    def nb_rows(self):\n        \"\"\"Get the number of rows of the checkerboard grid.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        int\n            The number of rows.\n\n        \"\"\"\n        return self.grid.nb_rows\n\n    @property\n    def nb_cols(self):\n        \"\"\"Get the number of columns of the checkerboard grid.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        int\n            The number of columns.\n\n        \"\"\"\n        return self.grid.nb_cols\n\n    def draw_masks(self, batch, random_state=None):\n        \"\"\"\n        See :func:`~imgaug.augmenters.blend.IBatchwiseMaskGenerator.draw_masks`.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        # pylint: disable=protected-access\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        shapes = batch.get_rowwise_shapes()\n        nb_rows, nb_cols, _alpha = self.grid._draw_samples(\n            len(shapes), random_state=random_state)\n\n        return [self.generate_mask(shape, nb_rows_i, nb_cols_i)\n                for shape, nb_rows_i, nb_cols_i\n                in zip(shapes, nb_rows, nb_cols)]\n\n    @classmethod\n    def generate_mask(cls, shape, nb_rows, nb_cols):\n        \"\"\"Generate a mask following a checkerboard pattern.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        shape : tuple of int\n            Height and width of the output mask.\n\n        nb_rows : int\n            Number of rows of the checkerboard pattern.\n\n        nb_cols : int\n            Number of columns of the checkerboard pattern.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` mask array with same height and width as\n            ``segmap.shape``. Values are in ``[0.0, 1.0]``.\n\n        \"\"\"\n        height, width = shape[0:2]\n        if 0 in (height, width):\n            return np.zeros((height, width), dtype=np.float32)\n        nb_rows = min(max(nb_rows, 1), height)\n        nb_cols = min(max(nb_cols, 1), width)\n\n        alphas = np.full((nb_cols,), 1.0, dtype=np.float32)\n        alphas[::2] = 0.0\n        alphas = np.tile(alphas[np.newaxis, :], (nb_rows, 1))\n        alphas[::2, :] = 1.0 - alphas[::2, :]\n\n        return RegularGridMaskGen.generate_mask(shape, nb_rows, nb_cols, alphas)\n\n\nclass SegMapClassIdsMaskGen(IBatchwiseMaskGenerator):\n    \"\"\"Generator that produces masks highlighting segmentation map classes.\n\n    This class produces for each segmentation map in a batch a mask in which\n    the locations of a set of provided classes are highlighted (i.e. ``1.0``).\n    The classes may be provided as a fixed list of class ids or a stochastic\n    parameter from which class ids will be sampled.\n\n    The produced masks are initially of the same height and width as the\n    segmentation map arrays and later upscaled to the image height and width.\n\n    .. note::\n\n        Segmentation maps can have multiple channels. If that is the case\n        then for each position ``(x, y)`` it is sufficient that any class id\n        in any channel matches one of the desired class ids.\n\n    .. note::\n\n        This class will produce an ``AssertionError`` if there are no\n        segmentation maps in a batch.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    class_ids : int or tuple of int or list of int or imgaug.parameters.StochasticParameter\n        Segmentation map classes to mark in the produced mask.\n\n        If `nb_sample_classes` is ``None`` then this is expected to be either\n        a single ``int`` (always mark this one class id) or a ``list`` of\n        ``int`` s (always mark these class ids).\n\n        If `nb_sample_classes` is set, then this parameter will be treated\n        as a stochastic parameter with the following valid types:\n\n            * If ``int``: Exactly that class id will be used for all\n              segmentation maps.\n            * If ``tuple`` ``(a, b)``: ``N`` random values will be uniformly\n              sampled per segmentation map from the discrete interval\n              ``[a..b]`` and used as the class ids.\n            * If ``list``: ``N`` random values will be picked per segmentation\n              map from that list and used as the class ids.\n            * If ``StochasticParameter``: That parameter will be queried once\n              per batch for ``(sum(N),)`` values.\n\n        ``N`` denotes the number of classes to sample per segmentation\n        map (derived from `nb_sample_classes`) and ``sum(N)`` denotes the\n        sum of ``N`` s over all segmentation maps.\n\n    nb_sample_classes : None or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of class ids to sample (with replacement) per segmentation map.\n        As sampling happens with replacement, fewer *unique* class ids may be\n        sampled.\n\n            * If ``None``: `class_ids` is expected to be a fixed value of\n              class ids to be used for all segmentation maps.\n            * If ``int``: Exactly that many class ids will be sampled for all\n              segmentation maps.\n            * If ``tuple`` ``(a, b)``: A random value will be uniformly\n              sampled per segmentation map from the discrete interval\n              ``[a..b]``.\n            * If ``list`` or ``int``: A random value will be picked per\n              segmentation map from that list.\n            * If ``StochasticParameter``: That parameter will be queried once\n              per batch for ``(B,)`` values, where ``B`` is the number of\n              segmentation maps.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, class_ids, nb_sample_classes=None):\n        if nb_sample_classes is None:\n            if ia.is_single_integer(class_ids):\n                class_ids = [class_ids]\n            assert isinstance(class_ids, list), (\n                \"Expected `class_ids` to be a single integer or a list of \"\n                \"integers if `nb_sample_classes` is None. Got type `%s`. \"\n                \"Set `nb_sample_classes` to e.g. an integer to enable \"\n                \"stochastic parameters for `class_ids`.\" % (\n                    type(class_ids).__name__,))\n            self.class_ids = class_ids\n            self.nb_sample_classes = None\n        else:\n            self.class_ids = iap.handle_discrete_param(\n                class_ids, \"class_ids\", value_range=(0, None),\n                tuple_to_uniform=True, list_to_choice=True,\n                allow_floats=False)\n            self.nb_sample_classes = iap.handle_discrete_param(\n                nb_sample_classes, \"nb_sample_classes\", value_range=(0, None),\n                tuple_to_uniform=True, list_to_choice=True,\n                allow_floats=False)\n\n    def draw_masks(self, batch, random_state=None):\n        \"\"\"\n        See :func:`~imgaug.augmenters.blend.IBatchwiseMaskGenerator.draw_masks`.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        assert batch.segmentation_maps is not None, (\n            \"Can only generate masks for batches that contain segmentation \"\n            \"maps, but got a batch without them.\")\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        class_ids = self._draw_samples(batch.nb_rows,\n                                       random_state=random_state)\n\n        return [self.generate_mask(segmap, class_ids_i)\n                for segmap, class_ids_i\n                in zip(batch.segmentation_maps, class_ids)]\n\n    # Added in 0.4.0.\n    def _draw_samples(self, nb_rows, random_state):\n        nb_sample_classes = self.nb_sample_classes\n        if nb_sample_classes is None:\n            assert isinstance(self.class_ids, list), (\n                \"Expected list got %s.\" % (type(self.class_ids).__name__,))\n            return [self.class_ids] * nb_rows\n\n        nb_sample_classes = nb_sample_classes.draw_samples(\n            (nb_rows,), random_state=random_state)\n        nb_sample_classes = np.clip(nb_sample_classes, 0, None)\n        class_ids_raw = self.class_ids.draw_samples(\n            (np.sum(nb_sample_classes),),\n            random_state=random_state)\n\n        class_ids = _split_1d_array_to_list(class_ids_raw, nb_sample_classes)\n\n        return class_ids\n\n    # TODO this could be simplified to something like:\n    #      segmap.keep_only_classes(class_ids).draw_mask()\n    @classmethod\n    def generate_mask(cls, segmap, class_ids):\n        \"\"\"Generate a mask of where the segmentation map has the given classes.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        segmap : imgaug.augmentables.segmap.SegmentationMapsOnImage\n            The segmentation map for which to generate the mask.\n\n        class_ids : iterable of int\n            IDs of the classes to set to ``1.0``.\n            For an ``(x, y)`` position, it is enough that *any* channel\n            at the given location to have one of these class ids to be marked\n            as ``1.0``.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` mask array with same height and width as\n            ``segmap.shape``. Values are in ``[0.0, 1.0]``.\n\n        \"\"\"\n        mask = np.zeros(segmap.arr.shape[0:2], dtype=bool)\n\n        for class_id in class_ids:\n            # note that segmap has shape (H,W,C), so we max() along C\n            mask_i = np.any(segmap.arr == class_id, axis=2)\n            mask = np.logical_or(mask, mask_i)\n\n        mask = mask.astype(np.float32)\n        mask = ia.imresize_single_image(mask, segmap.shape[0:2])\n\n        return mask\n\n\nclass BoundingBoxesMaskGen(IBatchwiseMaskGenerator):\n    \"\"\"Generator that produces masks highlighting bounding boxes.\n\n    This class produces for each row (i.e. image + bounding boxes) in a batch\n    a mask in which the inner areas of bounding box rectangles with given\n    labels are marked (i.e. set to ``1.0``). The labels may be provided as a\n    fixed list of strings or a stochastic parameter from which labels will be\n    sampled. If no labels are provided, all bounding boxes will be marked.\n\n    A pixel will be set to ``1.0`` if *at least* one bounding box at that\n    location has one of the requested labels, even if there is *also* one\n    bounding box at that location with a not requested label.\n\n    .. note::\n\n        This class will produce an ``AssertionError`` if there are no\n        bounding boxes in a batch.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    labels : None or str or list of str or imgaug.parameters.StochasticParameter\n        Labels of bounding boxes to select for.\n\n        If `nb_sample_labels` is ``None`` then this is expected to be either\n        also ``None`` (select all BBs) or a single ``str`` (select BBs with\n        this one label) or a ``list`` of ``str`` s (always select BBs with\n        these labels).\n\n        If `nb_sample_labels` is set, then this parameter will be treated\n        as a stochastic parameter with the following valid types:\n\n            * If ``None``: Ignore the sampling count  and always use all\n              bounding boxes.\n            * If ``str``: Exactly that label will be used for all\n              images.\n            * If ``list`` of ``str``: ``N`` random values will be picked per\n              image from that list and used as the labels.\n            * If ``StochasticParameter``: That parameter will be queried once\n              per batch for ``(sum(N),)`` values.\n\n        ``N`` denotes the number of labels to sample per segmentation\n        map (derived from `nb_sample_labels`) and ``sum(N)`` denotes the\n        sum of ``N`` s over all images.\n\n    nb_sample_labels : None or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of labels to sample (with replacement) per image.\n        As sampling happens with replacement, fewer *unique* labels may be\n        sampled.\n\n            * If ``None``: `labels` is expected to also be ``None`` or a fixed\n              value of labels to be used for all images.\n            * If ``int``: Exactly that many labels will be sampled for all\n              images.\n            * If ``tuple`` ``(a, b)``: A random value will be uniformly\n              sampled per image from the discrete interval ``[a..b]``.\n            * If ``list``: A random value will be picked per image from\n              that list.\n            * If ``StochasticParameter``: That parameter will be queried once\n              per batch for ``(B,)`` values, where ``B`` is the number of\n              images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, labels=None, nb_sample_labels=None):\n        if labels is None:\n            self.labels = None\n            self.nb_sample_labels = None\n        elif nb_sample_labels is None:\n            if ia.is_string(labels):\n                labels = [labels]\n            assert isinstance(labels, list), (\n                \"Expected `labels` a single string or a list of \"\n                \"strings if `nb_sample_labels` is None. Got type `%s`. \"\n                \"Set `nb_sample_labels` to e.g. an integer to enable \"\n                \"stochastic parameters for `labels`.\" % (\n                    type(labels).__name__,))\n            self.labels = labels\n            self.nb_sample_labels = None\n        else:\n            self.labels = iap.handle_categorical_string_param(labels, \"labels\")\n            self.nb_sample_labels = iap.handle_discrete_param(\n                nb_sample_labels, \"nb_sample_labels\", value_range=(0, None),\n                tuple_to_uniform=True, list_to_choice=True,\n                allow_floats=False)\n\n    def draw_masks(self, batch, random_state=None):\n        \"\"\"\n        See :func:`~imgaug.augmenters.blend.IBatchwiseMaskGenerator.draw_masks`.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        assert batch.bounding_boxes is not None, (\n            \"Can only generate masks for batches that contain bounding boxes, \"\n            \"but got a batch without them.\")\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n\n        if self.labels is None:\n            return [self.generate_mask(bbsoi, None)\n                    for bbsoi in batch.bounding_boxes]\n\n        labels = self._draw_samples(batch.nb_rows, random_state=random_state)\n\n        return [self.generate_mask(bbsoi, labels_i)\n                for bbsoi, labels_i\n                in zip(batch.bounding_boxes, labels)]\n\n    # Added in 0.4.0.\n    def _draw_samples(self, nb_rows, random_state):\n        nb_sample_labels = self.nb_sample_labels\n        if nb_sample_labels is None:\n            assert isinstance(self.labels, list), (\n                \"Expected list got %s.\" % (type(self.labels).__name__,))\n            return [self.labels] * nb_rows\n\n        nb_sample_labels = nb_sample_labels.draw_samples(\n            (nb_rows,), random_state=random_state)\n        nb_sample_labels = np.clip(nb_sample_labels, 0, None)\n        labels_raw = self.labels.draw_samples(\n            (np.sum(nb_sample_labels),),\n            random_state=random_state)\n\n        labels = _split_1d_array_to_list(labels_raw, nb_sample_labels)\n\n        return labels\n\n    # TODO this could be simplified to something like\n    #      bbsoi.only_labels(labels).draw_mask()\n    @classmethod\n    def generate_mask(cls, bbsoi, labels):\n        \"\"\"Generate a mask of the areas of bounding boxes with given labels.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        bbsoi : imgaug.augmentables.bbs.BoundingBoxesOnImage\n            The bounding boxes for which to generate the mask.\n\n        labels : None or iterable of str\n            Labels of the bounding boxes to set to ``1.0``.\n            For an ``(x, y)`` position, it is enough that *any* bounding box\n            at the given location has one of the labels.\n            If this is ``None``, all bounding boxes will be marked.\n\n        Returns\n        -------\n        ndarray\n            ``float32`` mask array with same height and width as\n            ``segmap.shape``. Values are in ``[0.0, 1.0]``.\n\n        \"\"\"\n        labels = set(labels) if labels is not None else None\n        height, width = bbsoi.shape[0:2]\n        mask = np.zeros((height, width), dtype=np.float32)\n\n        for bb in bbsoi:\n            if labels is None or bb.label in labels:\n                x1 = min(max(int(bb.x1), 0), width)\n                y1 = min(max(int(bb.y1), 0), height)\n                x2 = min(max(int(bb.x2), 0), width)\n                y2 = min(max(int(bb.y2), 0), height)\n                if x1 < x2 and y1 < y2:\n                    mask[y1:y2, x1:x2] = 1.0\n\n        return mask\n\n\nclass InvertMaskGen(IBatchwiseMaskGenerator):\n    \"\"\"Generator that inverts the outputs of other mask generators.\n\n    This class receives batches and calls for each row (i.e. image)\n    a child mask generator to produce a mask. That mask is then inverted\n    for ``p%`` of all rows, i.e. converted to ``1.0 - mask``.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    p : bool or float or imgaug.parameters.StochasticParameter, optional\n        Probability of inverting each mask produced by the other mask\n        generator.\n\n    child : IBatchwiseMaskGenerator\n        The other mask generator to invert.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, p, child):\n        self.p = iap.handle_probability_param(p, \"p\")\n        self.child = child\n\n    def draw_masks(self, batch, random_state=None):\n        \"\"\"\n        See :func:`~imgaug.augmenters.blend.IBatchwiseMaskGenerator.draw_masks`.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        masks = self.child.draw_masks(batch, random_state=random_state)\n        p = self.p.draw_samples(len(masks), random_state=random_state)\n        for mask, p_i in zip(masks, p):\n            if p_i >= 0.5:\n                mask[...] = 1.0 - mask\n        return masks\n\n\n@ia.deprecated(alt_func=\"Alpha\",\n               comment=\"Alpha is deprecated. \"\n                       \"Use BlendAlpha instead. \"\n                       \"The order of parameters is the same. \"\n                       \"Parameter 'first' was renamed to 'foreground'. \"\n                       \"Parameter 'second' was renamed to 'background'.\")\ndef Alpha(factor=0, first=None, second=None, per_channel=False,\n          seed=None, name=None,\n          random_state=\"deprecated\", deterministic=\"deprecated\"):\n    \"\"\"See :class:`BlendAlpha`.\n\n    Deprecated since 0.4.0.\n\n    \"\"\"\n    # pylint: disable=invalid-name\n    return BlendAlpha(\n        factor=factor,\n        foreground=first,\n        background=second,\n        per_channel=per_channel,\n        seed=seed, name=name,\n        random_state=random_state, deterministic=deterministic)\n\n\n@ia.deprecated(alt_func=\"AlphaElementwise\",\n               comment=\"AlphaElementwise is deprecated. \"\n                       \"Use BlendAlphaElementwise instead. \"\n                       \"The order of parameters is the same. \"\n                       \"Parameter 'first' was renamed to 'foreground'. \"\n                       \"Parameter 'second' was renamed to 'background'.\")\ndef AlphaElementwise(factor=0, first=None, second=None, per_channel=False,\n                     seed=None, name=None,\n                     random_state=\"deprecated\", deterministic=\"deprecated\"):\n    \"\"\"See :class:`BlendAlphaElementwise`.\n\n    Deprecated since 0.4.0.\n\n    \"\"\"\n    # pylint: disable=invalid-name\n    return BlendAlphaElementwise(\n        factor=factor,\n        foreground=first,\n        background=second,\n        per_channel=per_channel,\n        seed=seed, name=name,\n        random_state=random_state, deterministic=deterministic)\n\n\n@ia.deprecated(alt_func=\"BlendAlphaSimplexNoise\",\n               comment=\"SimplexNoiseAlpha is deprecated. \"\n                       \"Use BlendAlphaSimplexNoise instead. \"\n                       \"The order of parameters is the same. \"\n                       \"Parameter 'first' was renamed to 'foreground'. \"\n                       \"Parameter 'second' was renamed to 'background'.\")\ndef SimplexNoiseAlpha(first=None, second=None, per_channel=False,\n                      size_px_max=(2, 16), upscale_method=None,\n                      iterations=(1, 3), aggregation_method=\"max\",\n                      sigmoid=True, sigmoid_thresh=None,\n                      seed=None, name=None,\n                      random_state=\"deprecated\", deterministic=\"deprecated\"):\n    \"\"\"See :class:`BlendAlphaSimplexNoise`.\n\n    Deprecated since 0.4.0.\n\n    \"\"\"\n    # pylint: disable=invalid-name\n    return BlendAlphaSimplexNoise(\n        foreground=first,\n        background=second,\n        per_channel=per_channel,\n        size_px_max=size_px_max,\n        upscale_method=upscale_method,\n        iterations=iterations,\n        aggregation_method=aggregation_method,\n        sigmoid=sigmoid,\n        sigmoid_thresh=sigmoid_thresh,\n        seed=seed, name=name,\n        random_state=random_state, deterministic=deterministic)\n\n\n@ia.deprecated(alt_func=\"BlendAlphaFrequencyNoise\",\n               comment=\"FrequencyNoiseAlpha is deprecated. \"\n                       \"Use BlendAlphaFrequencyNoise instead. \"\n                       \"The order of parameters is the same. \"\n                       \"Parameter 'first' was renamed to 'foreground'. \"\n                       \"Parameter 'second' was renamed to 'background'.\")\ndef FrequencyNoiseAlpha(exponent=(-4, 4), first=None, second=None,\n                        per_channel=False, size_px_max=(4, 16),\n                        upscale_method=None,\n                        iterations=(1, 3), aggregation_method=[\"avg\", \"max\"],\n                        sigmoid=0.5, sigmoid_thresh=None,\n                        seed=None, name=None,\n                        random_state=\"deprecated\", deterministic=\"deprecated\"):\n    \"\"\"See :class:`BlendAlphaFrequencyNoise`.\n\n    Deprecated since 0.4.0.\n\n    \"\"\"\n    # pylint: disable=invalid-name, dangerous-default-value\n    return BlendAlphaFrequencyNoise(\n        exponent=exponent,\n        foreground=first,\n        background=second,\n        per_channel=per_channel,\n        size_px_max=size_px_max,\n        upscale_method=upscale_method,\n        iterations=iterations,\n        aggregation_method=aggregation_method,\n        sigmoid=sigmoid,\n        sigmoid_thresh=sigmoid_thresh,\n        seed=seed, name=name,\n        random_state=random_state, deterministic=deterministic)\n"
  },
  {
    "path": "imgaug/augmenters/blur.py",
    "content": "\"\"\"\nAugmenters that blur images.\n\nList of augmenters:\n\n    * :class:`GaussianBlur`\n    * :class:`AverageBlur`\n    * :class:`MedianBlur`\n    * :class:`BilateralBlur`\n    * :class:`MotionBlur`\n    * :class:`MeanShiftBlur`\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport numpy as np\nfrom scipy import ndimage\nimport cv2\nimport six.moves as sm\n\nimport imgaug as ia\nfrom imgaug.imgaug import _normalize_cv2_input_arr_\nfrom . import meta\nfrom . import convolutional as iaa_convolutional\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\n\n\n# TODO add border mode, cval\ndef blur_gaussian_(image, sigma, ksize=None, backend=\"auto\", eps=1e-3):\n    \"\"\"Blur an image using gaussian blurring in-place.\n\n    This operation *may* change the input image in-place.\n\n    **Supported dtypes**:\n\n    if (backend=\"auto\"):\n\n        * ``uint8``: yes; fully tested (1)\n        * ``uint16``: yes; tested (1)\n        * ``uint32``: yes; tested (2)\n        * ``uint64``: yes; tested (2)\n        * ``int8``: yes; tested (1)\n        * ``int16``: yes; tested (1)\n        * ``int32``: yes; tested (1)\n        * ``int64``: yes; tested (2)\n        * ``float16``: yes; tested (1)\n        * ``float32``: yes; tested (1)\n        * ``float64``: yes; tested (1)\n        * ``float128``: no\n        * ``bool``: yes; tested (1)\n\n        - (1) Handled by ``cv2``. See ``backend=\"cv2\"``.\n        - (2) Handled by ``scipy``. See ``backend=\"scipy\"``.\n\n    if (backend=\"cv2\"):\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: no (2)\n        * ``uint64``: no (3)\n        * ``int8``: yes; tested (4)\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested (5)\n        * ``int64``: no (6)\n        * ``float16``: yes; tested (7)\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no (8)\n        * ``bool``: yes; tested (1)\n\n        - (1) Mapped internally to ``float32``. Otherwise causes\n              ``TypeError: src data type = 0 is not supported``.\n        - (2) Causes ``TypeError: src data type = 6 is not supported``.\n        - (3) Causes ``cv2.error: OpenCV(3.4.5) (...)/filter.cpp:2957:\n              error: (-213:The function/feature is not implemented)\n              Unsupported combination of source format (=4), and buffer\n              format (=5) in function 'getLinearRowFilter'``.\n        - (4) Mapped internally to ``int16``. Otherwise causes\n              ``cv2.error: OpenCV(3.4.5) (...)/filter.cpp:2957: error:\n              (-213:The function/feature is not implemented) Unsupported\n              combination of source format (=1), and buffer format (=5)\n              in function 'getLinearRowFilter'``.\n        - (5) Mapped internally to ``float64``. Otherwise causes\n              ``cv2.error: OpenCV(3.4.5) (...)/filter.cpp:2957: error:\n              (-213:The function/feature is not implemented) Unsupported\n              combination of source format (=4), and buffer format (=5)\n              in function 'getLinearRowFilter'``.\n        - (6) Causes ``cv2.error: OpenCV(3.4.5) (...)/filter.cpp:2957:\n              error: (-213:The function/feature is not implemented)\n              Unsupported combination of source format (=4), and buffer\n              format (=5) in function 'getLinearRowFilter'``.\n        - (7) Mapped internally to ``float32``. Otherwise causes\n              ``TypeError: src data type = 23 is not supported``.\n        - (8) Causes ``TypeError: src data type = 13 is not supported``.\n\n    if (backend=\"scipy\"):\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested (1)\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no (2)\n        * ``bool``: yes; tested (3)\n\n        - (1) Mapped internally to ``float32``. Otherwise causes\n              ``RuntimeError: array type dtype('float16') not supported``.\n        - (2) Causes ``RuntimeError: array type dtype('float128') not\n              supported``.\n        - (3) Mapped internally to ``float32``. Otherwise too inaccurate.\n\n    Parameters\n    ----------\n    image : numpy.ndarray\n        The image to blur. Expected to be of shape ``(H, W)`` or ``(H, W, C)``.\n\n    sigma : number\n        Standard deviation of the gaussian blur. Larger numbers result in\n        more large-scale blurring, which is overall slower than small-scale\n        blurring.\n\n    ksize : None or int, optional\n        Size in height/width of the gaussian kernel. This argument is only\n        understood by the ``cv2`` backend. If it is set to ``None``, an\n        appropriate value for `ksize` will automatically be derived from\n        `sigma`. The value is chosen tighter for larger sigmas to avoid as\n        much as possible very large kernel sizes and therey improve\n        performance.\n\n    backend : {'auto', 'cv2', 'scipy'}, optional\n        Backend library to use. If ``auto``, then the likely best library\n        will be automatically picked per image. That is usually equivalent\n        to ``cv2`` (OpenCV) and it will fall back to ``scipy`` for datatypes\n        not supported by OpenCV.\n\n    eps : number, optional\n        A threshold used to decide whether `sigma` can be considered zero.\n\n    Returns\n    -------\n    numpy.ndarray\n        The blurred image. Same shape and dtype as the input.\n        (Input image *might* have been altered in-place.)\n\n    \"\"\"\n    if image.size == 0:\n        return image\n\n    if sigma < eps:\n        return image\n\n    iadt.gate_dtypes_strs(\n        {image.dtype},\n        allowed=\"bool uint8 uint16 uint32 \"\n                \"int8 int16 int32 int64 \"\n                \"uint64 \"\n                \"float16 float32 float64\",\n        disallowed=\"float128\",\n        augmenter=None\n    )\n\n    dts_not_supported_by_cv2 = iadt._convert_dtype_strs_to_types(\n        \"uint32 uint64 int64 float128\"\n    )\n    backend_to_use = backend\n    if backend == \"auto\":\n        backend_to_use = (\n            \"cv2\"\n            if image.dtype not in dts_not_supported_by_cv2\n            else \"scipy\")\n    elif backend == \"cv2\":\n        assert image.dtype not in dts_not_supported_by_cv2, (\n            \"Requested 'cv2' backend, but provided %s input image, which \"\n            \"cannot be handled by that backend. Choose a different \"\n            \"backend or set backend to 'auto' or use a different \"\n            \"datatype.\" % (\n                image.dtype.name,))\n    elif backend == \"scipy\":\n        # can handle all dtypes that were allowed in gate_dtypes()\n        pass\n\n    if backend_to_use == \"scipy\":\n        image = _blur_gaussian_scipy_(image, sigma, ksize)\n    else:\n        image = _blur_gaussian_cv2(image, sigma, ksize)\n\n    return image\n\n\n# Added in 0.5.0.\ndef _blur_gaussian_scipy_(image, sigma, ksize):\n    dtype = image.dtype\n\n    if dtype.kind == \"b\":\n        # We convert bool to float32 here, because gaussian_filter()\n        # seems to only return True when the underlying value is\n        # approximately 1.0, not when it is above 0.5. So we do that\n        # here manually. cv2 does not support bool for gaussian blur.\n        image = image.astype(np.float32, copy=False)\n    elif dtype == iadt._FLOAT16_DTYPE:\n        image = image.astype(np.float32, copy=False)\n\n    # gaussian_filter() has no ksize argument\n    # TODO it does have a truncate argument that truncates at x\n    #      standard deviations -- maybe can be used similarly to ksize\n    if ksize is not None:\n        ia.warn(\n            \"Requested 'scipy' backend or picked it automatically by \"\n            \"backend='auto' n blur_gaussian_(), but also provided \"\n            \"'ksize' argument, which is not understood by that \"\n            \"backend and will be ignored.\")\n\n    # Note that while gaussian_filter can be applied to all channels\n    # at the same time, that should not be done here, because then\n    # the blurring would also happen across channels (e.g. red values\n    # might be mixed with blue values in RGB)\n    if image.ndim == 2:\n        image[:, :] = ndimage.gaussian_filter(image[:, :], sigma,\n                                              mode=\"mirror\")\n    else:\n        nb_channels = image.shape[2]\n        for channel in sm.xrange(nb_channels):\n            image[:, :, channel] = ndimage.gaussian_filter(\n                image[:, :, channel], sigma, mode=\"mirror\")\n\n    if dtype.kind == \"b\":\n        image = image > 0.5\n    elif dtype != image.dtype:\n        image = iadt.restore_dtypes_(image, dtype)\n\n    return image\n\n\n# Added in 0.5.0.\ndef _blur_gaussian_cv2(image, sigma, ksize):\n    dtype = image.dtype\n\n    if dtype.kind == \"b\":\n        image = image.astype(np.float32, copy=False)\n    elif dtype == iadt._FLOAT16_DTYPE:\n        image = image.astype(np.float32, copy=False)\n    elif dtype == iadt._INT8_DTYPE:\n        image = image.astype(np.int16, copy=False)\n    elif dtype == iadt._INT32_DTYPE:\n        image = image.astype(np.float64, copy=False)\n\n    # ksize here is derived from the equation to compute sigma based\n    # on ksize, see\n    # https://docs.opencv.org/3.1.0/d4/d86/group__imgproc__filter.html\n    # -> cv::getGaussianKernel()\n    # example values:\n    #   sig = 0.1 -> ksize = -1.666\n    #   sig = 0.5 -> ksize = 0.9999\n    #   sig = 1.0 -> ksize = 1.0\n    #   sig = 2.0 -> ksize = 11.0\n    #   sig = 3.0 -> ksize = 17.666\n    # ksize = ((sig - 0.8)/0.3 + 1)/0.5 + 1\n\n    if ksize is None:\n        ksize = _compute_gaussian_blur_ksize(sigma)\n    else:\n        assert ia.is_single_integer(ksize), (\n            \"Expected 'ksize' argument to be a number, \"\n            \"got %s.\" % (type(ksize),))\n        ksize = ksize + 1 if ksize % 2 == 0 else ksize\n\n    image_warped = image\n    if ksize > 0:\n        # works with >512 channels\n        # normalization not required here\n        # dst seems to not help here\n        image_warped = cv2.GaussianBlur(\n            image,\n            (ksize, ksize),\n            sigmaX=sigma,\n            sigmaY=sigma,\n            borderType=cv2.BORDER_REFLECT_101\n        )\n\n        if image_warped.ndim == 2 and image.ndim == 3:\n            image_warped = image_warped[..., np.newaxis]\n\n    if dtype.kind == \"b\":\n        image_warped = image_warped > 0.5\n    elif dtype != image.dtype:\n        image_warped = iadt.restore_dtypes_(image_warped, dtype)\n\n    return image_warped\n\n\ndef _compute_gaussian_blur_ksize(sigma):\n    if sigma < 3.0:\n        ksize = 3.3 * sigma  # 99% of weight\n    elif sigma < 5.0:\n        ksize = 2.9 * sigma  # 97% of weight\n    else:\n        ksize = 2.6 * sigma  # 95% of weight\n\n    # we use 5x5 here as the minimum size as that simplifies\n    # comparisons with gaussian_filter() in the tests\n    # TODO reduce this to 3x3\n    ksize = int(max(ksize, 5))\n    if ksize % 2 == 0:\n        ksize += 1\n    return ksize\n\n\ndef blur_avg_(image, k):\n    \"\"\"Blur an image in-place by computing averages over local neighbourhoods.\n\n    This operation *may* change the input image in-place.\n\n    The padding behaviour around the image borders is cv2's\n    ``BORDER_REFLECT_101``.\n\n    Added in 0.5.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: no (1)\n        * ``uint64``: no (2)\n        * ``int8``: yes; tested (3)\n        * ``int16``: yes; tested\n        * ``int32``: no (4)\n        * ``int64``: no (5)\n        * ``float16``: yes; tested (6)\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no\n        * ``bool``: yes; tested (7)\n\n        - (1) rejected by ``cv2.blur()``\n        - (2) loss of resolution in ``cv2.blur()`` (result is ``int32``)\n        - (3) ``int8`` is mapped internally to ``int16``, ``int8`` itself\n              leads to cv2 error \"Unsupported combination of source format\n              (=1), and buffer format (=4) in function 'getRowSumFilter'\" in\n              ``cv2``\n        - (4) results too inaccurate\n        - (5) loss of resolution in ``cv2.blur()`` (result is ``int32``)\n        - (6) ``float16`` is mapped internally to ``float32``\n        - (7) ``bool`` is mapped internally to ``float32``\n\n    Parameters\n    ----------\n    image : numpy.ndarray\n        The image to blur. Expected to be of shape ``(H, W)`` or ``(H, W, C)``.\n\n    k : int or tuple of int\n        Kernel size to use. A single ``int`` will lead to an ``k x k``\n        kernel. Otherwise a ``tuple`` of two ``int`` ``(height, width)``\n        is expected.\n\n    Returns\n    -------\n    numpy.ndarray\n        The blurred image. Same shape and dtype as the input.\n        (Input image *might* have been altered in-place.)\n\n    \"\"\"\n    if isinstance(k, tuple):\n        k_height, k_width = k\n    else:\n        k_height, k_width = k, k\n\n    shape = image.shape\n    if 0 in shape:\n        return image\n\n    if k_height <= 0 or k_width <= 0 or (k_height, k_width) == (1, 1):\n        return image\n\n    iadt.gate_dtypes_strs(\n        {image.dtype},\n        allowed=\"bool uint8 uint16 int8 int16 float16 float32 float64\",\n        disallowed=\"uint32 uint64 int32 int64 float128\"\n    )\n\n    input_dtype = image.dtype\n    if image.dtype in {iadt._BOOL_DTYPE, iadt._FLOAT16_DTYPE}:\n        image = image.astype(np.float32, copy=False)\n    elif image.dtype == iadt._INT8_DTYPE:\n        image = image.astype(np.int16, copy=False)\n\n    input_ndim = len(shape)\n    if input_ndim == 2 or shape[-1] <= 512:\n        image = _normalize_cv2_input_arr_(image)\n        image_aug = cv2.blur(\n            image,\n            (k_width, k_height),\n            dst=image\n        )\n        # cv2.blur() removes channel axis for single-channel images\n        if input_ndim == 3 and image_aug.ndim == 2:\n            image_aug = image_aug[..., np.newaxis]\n    else:\n        # TODO this is quite inefficient\n        # handling more than 512 channels in cv2.blur()\n        channels = [\n            cv2.blur(\n                _normalize_cv2_input_arr_(image[..., c]),\n                (k_width, k_height)\n            )\n            for c in sm.xrange(shape[-1])\n        ]\n        image_aug = np.stack(channels, axis=-1)\n\n    if input_dtype.kind == \"b\":\n        image_aug = image_aug > 0.5\n    elif input_dtype in {iadt._INT8_DTYPE, iadt._FLOAT16_DTYPE}:\n        image_aug = iadt.restore_dtypes_(image_aug, input_dtype)\n\n    return image_aug\n\n\ndef blur_mean_shift_(image, spatial_window_radius, color_window_radius):\n    \"\"\"Apply a pyramidic mean shift filter to the input image in-place.\n\n    This produces an output image that has similarity with one modified by\n    a bilateral filter. That is different from mean shift *segmentation*,\n    which averages the colors in segments found by mean shift clustering.\n\n    This function is a thin wrapper around ``cv2.pyrMeanShiftFiltering``.\n\n    .. note::\n\n        This function does *not* change the image's colorspace to ``RGB``\n        before applying the mean shift filter. A non-``RGB`` colorspace will\n        hence influence the results.\n\n    .. note::\n\n        This function is quite slow.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no (1)\n        * ``uint32``: no (1)\n        * ``uint64``: no (1)\n        * ``int8``: no (1)\n        * ``int16``: no (1)\n        * ``int32``: no (1)\n        * ``int64``: no (1)\n        * ``float16``: no (1)\n        * ``float32``: no (1)\n        * ``float64``: no (1)\n        * ``float128``: no (1)\n        * ``bool``: no (1)\n\n        - (1) Not supported by ``cv2.pyrMeanShiftFiltering``.\n\n    Parameters\n    ----------\n    image : ndarray\n        ``(H,W)`` or ``(H,W,1)`` or ``(H,W,3)`` image to blur.\n        Images with no or one channel will be temporarily tiled to have\n        three channels.\n\n    spatial_window_radius : number\n        Spatial radius for pixels that are assumed to be similar.\n\n    color_window_radius : number\n        Color radius for pixels that are assumed to be similar.\n\n    Returns\n    -------\n    ndarray\n        Blurred input image. Same shape and dtype as the input.\n        (Input image *might* have been altered in-place.)\n\n    \"\"\"\n    if 0 in image.shape[0:2]:\n        return image\n\n    # opencv method only supports uint8\n    iadt.allow_only_uint8({image.dtype})\n\n    shape_is_hw = (image.ndim == 2)\n    shape_is_hw1 = (image.ndim == 3 and image.shape[-1] == 1)\n    shape_is_hw3 = (image.ndim == 3 and image.shape[-1] == 3)\n\n    assert shape_is_hw or shape_is_hw1 or shape_is_hw3, (\n        \"Expected (H,W) or (H,W,1) or (H,W,3) image, \"\n        \"got shape %s.\" % (image.shape,))\n\n    # opencv method only supports (H,W,3), so we have to tile here for (H,W)\n    # and (H,W,1)\n    if shape_is_hw:\n        image = np.tile(image[..., np.newaxis], (1, 1, 3))\n    elif shape_is_hw1:\n        image = np.tile(image, (1, 1, 3))\n\n    spatial_window_radius = max(spatial_window_radius, 0)\n    color_window_radius = max(color_window_radius, 0)\n\n    image = _normalize_cv2_input_arr_(image)\n    image = cv2.pyrMeanShiftFiltering(\n        image,\n        sp=spatial_window_radius,\n        sr=color_window_radius,\n        dst=image)\n\n    if shape_is_hw:\n        image = image[..., 0]\n    elif shape_is_hw1:\n        image = image[..., 0:1]\n\n    return image\n\n\n# TODO offer different values for sigma on x/y-axis, supported by cv2 but not\n#      by scipy\n# TODO add channelwise flag - channelwise=False would be supported by scipy\nclass GaussianBlur(meta.Augmenter):\n    \"\"\"Augmenter to blur images using gaussian kernels.\n\n    **Supported dtypes**:\n\n    See ``~imgaug.augmenters.blur.blur_gaussian_(backend=\"auto\")``.\n\n    Parameters\n    ----------\n    sigma : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Standard deviation of the gaussian kernel.\n        Values in the range ``0.0`` (no blur) to ``3.0`` (strong blur) are\n        common.\n\n            * If a single ``float``, that value will always be used as the\n              standard deviation.\n            * If a tuple ``(a, b)``, then a random value from the interval\n              ``[a, b]`` will be picked per image.\n            * If a list, then a random value will be sampled per image from\n              that list.\n            * If a ``StochasticParameter``, then ``N`` samples will be drawn\n              from that parameter per ``N`` input images.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.GaussianBlur(sigma=1.5)\n\n    Blur all images using a gaussian kernel with a standard deviation of\n    ``1.5``.\n\n    >>> aug = iaa.GaussianBlur(sigma=(0.0, 3.0))\n\n    Blur images using a gaussian kernel with a random standard deviation\n    sampled uniformly (per image) from the interval ``[0.0, 3.0]``.\n\n    \"\"\"\n\n    def __init__(self, sigma=(0.0, 3.0),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(GaussianBlur, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.sigma = iap.handle_continuous_param(\n            sigma, \"sigma\", value_range=(0, None), tuple_to_uniform=True,\n            list_to_choice=True)\n\n        # epsilon value to estimate whether sigma is sufficently above 0 to\n        # apply the blur\n        self.eps = 1e-3\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        nb_images = len(images)\n        samples = self.sigma.draw_samples((nb_images,),\n                                          random_state=random_state)\n        for image, sig in zip(images, samples):\n            image[...] = blur_gaussian_(image, sigma=sig, eps=self.eps)\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.sigma]\n\n\nclass AverageBlur(meta.Augmenter):\n    \"\"\"Blur an image by computing simple means over neighbourhoods.\n\n    The padding behaviour around the image borders is cv2's\n    ``BORDER_REFLECT_101``.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: no (1)\n        * ``uint64``: no (2)\n        * ``int8``: yes; tested (3)\n        * ``int16``: yes; tested\n        * ``int32``: no (4)\n        * ``int64``: no (5)\n        * ``float16``: yes; tested (6)\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no\n        * ``bool``: yes; tested (7)\n\n        - (1) rejected by ``cv2.blur()``\n        - (2) loss of resolution in ``cv2.blur()`` (result is ``int32``)\n        - (3) ``int8`` is mapped internally to ``int16``, ``int8`` itself\n              leads to cv2 error \"Unsupported combination of source format\n              (=1), and buffer format (=4) in function 'getRowSumFilter'\" in\n              ``cv2``\n        - (4) results too inaccurate\n        - (5) loss of resolution in ``cv2.blur()`` (result is ``int32``)\n        - (6) ``float16`` is mapped internally to ``float32``\n        - (7) ``bool`` is mapped internally to ``float32``\n\n    Parameters\n    ----------\n    k : int or tuple of int or tuple of tuple of int or imgaug.parameters.StochasticParameter or tuple of StochasticParameter, optional\n        Kernel size to use.\n\n            * If a single ``int``, then that value will be used for the height\n              and width of the kernel.\n            * If a tuple of two ``int`` s ``(a, b)``, then the kernel size will\n              be sampled from the interval ``[a..b]``.\n            * If a tuple of two tuples of ``int`` s ``((a, b), (c, d))``,\n              then per image a random kernel height will be sampled from the\n              interval ``[a..b]`` and a random kernel width will be sampled\n              from the interval ``[c..d]``.\n            * If a ``StochasticParameter``, then ``N`` samples will be drawn\n              from that parameter per ``N`` input images, each representing\n              the kernel size for the n-th image.\n            * If a tuple ``(a, b)``, where either ``a`` or ``b`` is a tuple,\n              then ``a`` and ``b`` will be treated according to the rules\n              above. This leads to different values for height and width of\n              the kernel.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AverageBlur(k=5)\n\n    Blur all images using a kernel size of ``5x5``.\n\n    >>> aug = iaa.AverageBlur(k=(2, 5))\n\n    Blur images using a varying kernel size, which is sampled (per image)\n    uniformly from the interval ``[2..5]``.\n\n    >>> aug = iaa.AverageBlur(k=((5, 7), (1, 3)))\n\n    Blur images using a varying kernel size, which's height is sampled\n    (per image) uniformly from the interval ``[5..7]`` and which's width is\n    sampled (per image) uniformly from ``[1..3]``.\n\n    \"\"\"\n\n    def __init__(self, k=(1, 7),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(AverageBlur, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        # TODO replace this by iap.handle_discrete_kernel_size()\n        self.mode = \"single\"\n        if ia.is_single_number(k):\n            self.k = iap.Deterministic(int(k))\n        elif ia.is_iterable(k):\n            assert len(k) == 2, (\n                \"Expected iterable 'k' to contain exactly 2 entries, \"\n                \"got %d.\" % (len(k),))\n            if all([ia.is_single_number(ki) for ki in k]):\n                self.k = iap.DiscreteUniform(int(k[0]), int(k[1]))\n            elif all([isinstance(ki, iap.StochasticParameter) for ki in k]):\n                self.mode = \"two\"\n                self.k = (k[0], k[1])\n            else:\n                k_tuple = [None, None]\n                if ia.is_single_number(k[0]):\n                    k_tuple[0] = iap.Deterministic(int(k[0]))\n                elif (ia.is_iterable(k[0])\n                      and all([ia.is_single_number(ki) for ki in k[0]])):\n                    k_tuple[0] = iap.DiscreteUniform(int(k[0][0]),\n                                                     int(k[0][1]))\n                else:\n                    raise Exception(\n                        \"k[0] expected to be int or tuple of two ints, \"\n                        \"got %s\" % (type(k[0]),))\n\n                if ia.is_single_number(k[1]):\n                    k_tuple[1] = iap.Deterministic(int(k[1]))\n                elif (ia.is_iterable(k[1])\n                      and all([ia.is_single_number(ki) for ki in k[1]])):\n                    k_tuple[1] = iap.DiscreteUniform(int(k[1][0]),\n                                                     int(k[1][1]))\n                else:\n                    raise Exception(\n                        \"k[1] expected to be int or tuple of two ints, \"\n                        \"got %s\" % (type(k[1]),))\n\n                self.mode = \"two\"\n                self.k = k_tuple\n        elif isinstance(k, iap.StochasticParameter):\n            self.k = k\n        else:\n            raise Exception(\n                \"Expected int, tuple/list with 2 entries or \"\n                \"StochasticParameter. Got %s.\" % (type(k),))\n\n        self.k = iap._wrap_leafs_of_param_in_prefetchers(\n            self.k, iap._NB_PREFETCH\n        )\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        nb_images = len(images)\n        if self.mode == \"single\":\n            samples = self.k.draw_samples((nb_images,),\n                                          random_state=random_state)\n            samples = (samples, samples)\n        else:\n            rss = random_state.duplicate(2)\n            samples = (\n                self.k[0].draw_samples((nb_images,), random_state=rss[0]),\n                self.k[1].draw_samples((nb_images,), random_state=rss[1]),\n            )\n\n        gen = enumerate(zip(images, samples[0], samples[1]))\n        for i, (image, ksize_h, ksize_w) in gen:\n            batch.images[i] = blur_avg_(image, (ksize_h, ksize_w))\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.k]\n\n\nclass MedianBlur(meta.Augmenter):\n    \"\"\"Blur an image by computing median values over neighbourhoods.\n\n    Median blurring can be used to remove small dirt from images.\n    At larger kernel sizes, its effects have some similarity with Superpixels.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: ?\n        * ``uint32``: ?\n        * ``uint64``: ?\n        * ``int8``: ?\n        * ``int16``: ?\n        * ``int32``: ?\n        * ``int64``: ?\n        * ``float16``: ?\n        * ``float32``: ?\n        * ``float64``: ?\n        * ``float128``: ?\n        * ``bool``: ?\n\n    Parameters\n    ----------\n    k : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Kernel size.\n\n            * If a single ``int``, then that value will be used for the\n              height and width of the kernel. Must be an odd value.\n            * If a tuple of two ints ``(a, b)``, then the kernel size will be\n              an odd value sampled from the interval ``[a..b]``. ``a`` and\n              ``b`` must both be odd values.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then ``N`` samples will be drawn\n              from that parameter per ``N`` input images, each representing\n              the kernel size for the nth image. Expected to be discrete. If\n              a sampled value is not odd, then that value will be increased\n              by ``1``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MedianBlur(k=5)\n\n    Blur all images using a kernel size of ``5x5``.\n\n    >>> aug = iaa.MedianBlur(k=(3, 7))\n\n    Blur images using varying kernel sizes, which are sampled uniformly from\n    the interval ``[3..7]``. Only odd values will be sampled, i.e. ``3``\n    or ``5`` or ``7``.\n\n    \"\"\"\n\n    def __init__(self, k=(1, 7),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(MedianBlur, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        # TODO replace this by iap.handle_discrete_kernel_size()\n        self.k = iap.handle_discrete_param(\n            k, \"k\", value_range=(1, None), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=False)\n        if ia.is_single_integer(k):\n            assert k % 2 != 0, (\n                \"Expected k to be odd, got %d. Add or subtract 1.\" % (\n                    int(k),))\n        elif ia.is_iterable(k):\n            assert all([ki % 2 != 0 for ki in k]), (\n                \"Expected all values in iterable k to be odd, but at least \"\n                \"one was not. Add or subtract 1 to/from that value.\")\n        self.k = iap._wrap_leafs_of_param_in_prefetchers(\n            self.k, iap._NB_PREFETCH\n        )\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        nb_images = len(images)\n        samples = self.k.draw_samples((nb_images,), random_state=random_state)\n        for i, (image, ksize) in enumerate(zip(images, samples)):\n            has_zero_sized_axes = (image.size == 0)\n            if ksize > 1 and not has_zero_sized_axes:\n                ksize = ksize + 1 if ksize % 2 == 0 else ksize\n                if image.ndim == 2 or image.shape[-1] <= 512:\n                    image_aug = cv2.medianBlur(\n                        _normalize_cv2_input_arr_(image), ksize)\n                    # cv2.medianBlur() removes channel axis for single-channel\n                    # images\n                    if image_aug.ndim == 2:\n                        image_aug = image_aug[..., np.newaxis]\n                else:\n                    # TODO this is quite inefficient\n                    # handling more than 512 channels in cv2.medainBlur()\n                    channels = [\n                        cv2.medianBlur(\n                            _normalize_cv2_input_arr_(image[..., c]), ksize)\n                        for c in sm.xrange(image.shape[-1])\n                    ]\n                    image_aug = np.stack(channels, axis=-1)\n\n                batch.images[i] = image_aug\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.k]\n\n\n# TODO tests\nclass BilateralBlur(meta.Augmenter):\n    \"\"\"Blur/Denoise an image using a bilateral filter.\n\n    Bilateral filters blur homogenous and textured areas, while trying to\n    preserve edges.\n\n    See\n    http://docs.opencv.org/2.4/modules/imgproc/doc/filtering.html#bilateralfilter\n    for more information regarding the parameters.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; not tested\n        * ``uint16``: ?\n        * ``uint32``: ?\n        * ``uint64``: ?\n        * ``int8``: ?\n        * ``int16``: ?\n        * ``int32``: ?\n        * ``int64``: ?\n        * ``float16``: ?\n        * ``float32``: ?\n        * ``float64``: ?\n        * ``float128``: ?\n        * ``bool``: ?\n\n    Parameters\n    ----------\n    d : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Diameter of each pixel neighborhood with value range ``[1 .. inf)``.\n        High values for `d` lead to significantly worse performance. Values\n        equal or less than ``10`` seem to be good. Use ``<5`` for real-time\n        applications.\n\n            * If a single ``int``, then that value will be used for the\n              diameter.\n            * If a tuple of two ``int`` s ``(a, b)``, then the diameter will\n              be a value sampled from the interval ``[a..b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then ``N`` samples will be drawn\n              from that parameter per ``N`` input images, each representing\n              the diameter for the n-th image. Expected to be discrete.\n\n    sigma_color : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Filter sigma in the color space with value range ``[1, inf)``. A\n        large value of the parameter means that farther colors within the\n        pixel neighborhood (see `sigma_space`) will be mixed together,\n        resulting in larger areas of semi-equal color.\n\n            * If a single ``int``, then that value will be used for the\n              diameter.\n            * If a tuple of two ``int`` s ``(a, b)``, then the diameter will\n              be a value sampled from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then ``N`` samples will be drawn\n              from that parameter per ``N`` input images, each representing\n              the diameter for the n-th image. Expected to be discrete.\n\n    sigma_space : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Filter sigma in the coordinate space with value range ``[1, inf)``. A\n        large value of the parameter means that farther pixels will influence\n        each other as long as their colors are close enough (see\n        `sigma_color`).\n\n            * If a single ``int``, then that value will be used for the\n              diameter.\n            * If a tuple of two ``int`` s ``(a, b)``, then the diameter will\n              be a value sampled from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then ``N`` samples will be drawn\n              from that parameter per ``N`` input images, each representing\n              the diameter for the n-th image. Expected to be discrete.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.BilateralBlur(\n    >>>     d=(3, 10), sigma_color=(10, 250), sigma_space=(10, 250))\n\n    Blur all images using a bilateral filter with a `max distance` sampled\n    uniformly from the interval ``[3, 10]`` and wide ranges for `sigma_color`\n    and `sigma_space`.\n\n    \"\"\"\n\n    def __init__(self, d=(1, 9), sigma_color=(10, 250), sigma_space=(10, 250),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=invalid-name\n        super(BilateralBlur, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.d = iap.handle_discrete_param(\n            d, \"d\", value_range=(1, None), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=False)\n        self.sigma_color = iap.handle_continuous_param(\n            sigma_color, \"sigma_color\", value_range=(1, None),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.sigma_space = iap.handle_continuous_param(\n            sigma_space, \"sigma_space\", value_range=(1, None),\n            tuple_to_uniform=True, list_to_choice=True)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        # pylint: disable=invalid-name\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        # Make sure that all images have 3 channels\n        assert all([image.shape[2] == 3 for image in images]), (\n            \"BilateralBlur can currently only be applied to images with 3 \"\n            \"channels. Got channels: %s\" % (\n                [image.shape[2] for image in images],))\n\n        nb_images = len(images)\n        rss = random_state.duplicate(3)\n        samples_d = self.d.draw_samples((nb_images,), random_state=rss[0])\n        samples_sigma_color = self.sigma_color.draw_samples(\n            (nb_images,), random_state=rss[1])\n        samples_sigma_space = self.sigma_space.draw_samples(\n            (nb_images,), random_state=rss[2])\n        gen = enumerate(zip(images, samples_d, samples_sigma_color,\n                            samples_sigma_space))\n        for i, (image, di, sigma_color_i, sigma_space_i) in gen:\n            has_zero_sized_axes = (image.size == 0)\n            if di != 1 and not has_zero_sized_axes:\n                batch.images[i] = cv2.bilateralFilter(\n                    _normalize_cv2_input_arr_(image),\n                    di, sigma_color_i, sigma_space_i)\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.d, self.sigma_color, self.sigma_space]\n\n\n# TODO add k sizing via float/percentage\nclass MotionBlur(iaa_convolutional.Convolve):\n    \"\"\"Blur images in a way that fakes camera or object movements.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.convolutional.Convolve`.\n\n    Parameters\n    ----------\n    k : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Kernel size to use.\n\n            * If a single ``int``, then that value will be used for the height\n              and width of the kernel.\n            * If a tuple of two ``int`` s ``(a, b)``, then the kernel size\n              will be sampled from the interval ``[a..b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then ``N`` samples will be drawn\n              from that parameter per ``N`` input images, each representing\n              the kernel size for the n-th image.\n\n    angle : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Angle of the motion blur in degrees (clockwise, relative to top center\n        direction).\n\n            * If a number, exactly that value will be used.\n            * If a tuple ``(a, b)``, a random value from the interval\n              ``[a, b]`` will be uniformly sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, a value will be sampled from the\n              parameter per image.\n\n    direction : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Forward/backward direction of the motion blur. Lower values towards\n        ``-1.0`` will point the motion blur towards the back (with angle\n        provided via `angle`). Higher values towards ``1.0`` will point the\n        motion blur forward. A value of ``0.0`` leads to a uniformly (but\n        still angled) motion blur.\n\n            * If a number, exactly that value will be used.\n            * If a tuple ``(a, b)``, a random value from the interval\n              ``[a, b]`` will be uniformly sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, a value will be sampled from the\n              parameter per image.\n\n    order : int or iterable of int or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        Interpolation order to use when rotating the kernel according to\n        `angle`.\n        See :func:`~imgaug.augmenters.geometric.Affine.__init__`.\n        Recommended to be ``0`` or ``1``, with ``0`` being faster, but less\n        continuous/smooth as `angle` is changed, particularly around multiple\n        of ``45`` degrees.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MotionBlur(k=15)\n\n    Apply motion blur with a kernel size of ``15x15`` pixels to images.\n\n    >>> aug = iaa.MotionBlur(k=15, angle=[-45, 45])\n\n    Apply motion blur with a kernel size of ``15x15`` pixels and a blur angle\n    of either ``-45`` or ``45`` degrees (randomly picked per image).\n\n    \"\"\"\n\n    def __init__(self, k=(3, 7), angle=(0, 360), direction=(-1.0, 1.0), order=1,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # TODO allow (1, None) and set to identity matrix if k == 1\n        k_param = iap.handle_discrete_param(\n            k, \"k\", value_range=(3, None), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=False)\n        angle_param = iap.handle_continuous_param(\n            angle, \"angle\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True)\n        direction_param = iap.handle_continuous_param(\n            direction, \"direction\", value_range=(-1.0-1e-6, 1.0+1e-6),\n            tuple_to_uniform=True, list_to_choice=True)\n\n        matrix_gen = _MotionBlurMatrixGenerator(k_param, angle_param,\n                                                direction_param, order)\n\n        super(MotionBlur, self).__init__(\n            matrix_gen,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# Added in 0.4.0.\nclass _MotionBlurMatrixGenerator(object):\n    # Added in 0.4.0.\n    def __init__(self, k, angle, direction, order):\n        self.k = k\n        self.angle = angle\n        self.direction = direction\n        self.order = order\n\n    # Added in 0.4.0.\n    def __call__(self, _image, nb_channels, random_state):\n        # avoid cyclic import between blur and geometric\n        from . import geometric as iaa_geometric\n\n        # force discrete for k_sample via int() in case of stochastic\n        # parameter\n        k_sample = int(\n            self.k.draw_sample(random_state=random_state))\n        angle_sample = self.angle.draw_sample(\n            random_state=random_state)\n        direction_sample = self.direction.draw_sample(\n            random_state=random_state)\n\n        k_sample = k_sample if k_sample % 2 != 0 else k_sample + 1\n        direction_sample = np.clip(direction_sample, -1.0, 1.0)\n        direction_sample = (direction_sample + 1.0) / 2.0\n\n        matrix = np.zeros((k_sample, k_sample), dtype=np.float32)\n        matrix[:, k_sample//2] = np.linspace(\n            float(direction_sample),\n            1.0 - float(direction_sample),\n            num=k_sample)\n        rot = iaa_geometric.Affine(rotate=angle_sample, order=self.order)\n\n        matrix = (\n            rot.augment_image(\n                (matrix * 255).astype(np.uint8)\n            ).astype(np.float32) / 255.0\n        )\n\n        return [matrix/np.sum(matrix)] * nb_channels\n\n\n# TODO add a per_channel flag?\n# TODO make spatial_radius a fraction of the input image size?\nclass MeanShiftBlur(meta.Augmenter):\n    \"\"\"Apply a pyramidic mean shift filter to each image.\n\n    See also :func:`blur_mean_shift_` for details.\n\n    This augmenter expects input images of shape ``(H,W)`` or ``(H,W,1)``\n    or ``(H,W,3)``.\n\n    .. note::\n\n        This augmenter is quite slow.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.blur.blur_mean_shift_`.\n\n    Parameters\n    ----------\n    spatial_radius : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Spatial radius for pixels that are assumed to be similar.\n\n            * If ``number``: Exactly that value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A random value will be uniformly\n              sampled per image from the interval ``[a, b)``.\n            * If ``list``: A random value will be sampled from that ``list``\n              per image.\n            * If ``StochasticParameter``: The parameter will be queried once\n              per batch for ``(N,)`` values with ``N`` denoting the number of\n              images.\n\n    color_radius : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Color radius for pixels that are assumed to be similar.\n\n            * If ``number``: Exactly that value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A random value will be uniformly\n              sampled per image from the interval ``[a, b)``.\n            * If ``list``: A random value will be sampled from that ``list``\n              per image.\n            * If ``StochasticParameter``: The parameter will be queried once\n              per batch for ``(N,)`` values with ``N`` denoting the number of\n              images.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MeanShiftBlur()\n\n    Create a mean shift blur augmenter.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, spatial_radius=(5.0, 40.0), color_radius=(5.0, 40.0),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(MeanShiftBlur, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.spatial_window_radius = iap.handle_continuous_param(\n            spatial_radius, \"spatial_radius\",\n            value_range=(0.01, None), tuple_to_uniform=True,\n            list_to_choice=True)\n        self.color_window_radius = iap.handle_continuous_param(\n            color_radius, \"color_radius\",\n            value_range=(0.01, None), tuple_to_uniform=True,\n            list_to_choice=True)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is not None:\n            samples = self._draw_samples(batch, random_state)\n            for i, image in enumerate(batch.images):\n                batch.images[i] = blur_mean_shift_(\n                    image,\n                    spatial_window_radius=samples[0][i],\n                    color_window_radius=samples[1][i]\n                )\n\n        return batch\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        nb_rows = batch.nb_rows\n        return (\n            self.spatial_window_radius.draw_samples((nb_rows,),\n                                                    random_state=random_state),\n            self.color_window_radius.draw_samples((nb_rows,),\n                                                  random_state=random_state)\n        )\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.spatial_window_radius, self.color_window_radius]\n"
  },
  {
    "path": "imgaug/augmenters/collections.py",
    "content": "\"\"\"Augmenters that are collections of other augmenters.\n\nList of augmenters:\n\n    * :class:`RandAugment`\n\nAdded in 0.4.0.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport numpy as np\n\nfrom .. import parameters as iap\nfrom .. import random as iarandom\nfrom . import meta\nfrom . import arithmetic\nfrom . import flip\nfrom . import pillike\nfrom . import size as sizelib\n\n\nclass RandAugment(meta.Sequential):\n    \"\"\"Apply RandAugment to inputs as described in the corresponding paper.\n\n    See paper::\n\n        Cubuk et al.\n\n        RandAugment: Practical automated data augmentation with a reduced\n        search space\n\n    .. note::\n\n        The paper contains essentially no hyperparameters for the individual\n        augmentation techniques. The hyperparameters used here come mostly\n        from the official code repository, which however seems to only contain\n        code for CIFAR10 and SVHN, not for ImageNet. So some guesswork was\n        involved and a few of the hyperparameters were also taken from\n        https://github.com/ildoonet/pytorch-randaugment/blob/master/RandAugment/augmentations.py .\n\n        This implementation deviates from the code repository for all PIL\n        enhance operations. In the repository these use a factor of\n        ``0.1 + M*1.8/M_max``, which would lead to a factor of ``0.1`` for the\n        weakest ``M`` of ``M=0``. For e.g. ``Brightness`` that would result in\n        a basically black image. This definition is fine for AutoAugment (from\n        where the code and hyperparameters are copied), which optimizes\n        each transformation's ``M`` individually, but not for RandAugment,\n        which uses a single fixed ``M``. We hence redefine these\n        hyperparameters to ``1.0 + S * M * 0.9/M_max``, where ``S`` is\n        randomly either ``1`` or ``-1``.\n\n        We also note that it is not entirely clear which transformations\n        were used in the ImageNet experiments. The paper lists some\n        transformations in Figure 2, but names others in the text too (e.g.\n        crops, flips, cutout). While Figure 2 lists the Identity function,\n        this transformation seems to not appear in the repository (and in fact,\n        the function ``randaugment(N, M)`` doesn't seem to exist in the\n        repository either). So we also make a best guess here about what\n        transformations might have been used.\n\n    .. warning::\n\n        This augmenter only works with image data, not e.g. bounding boxes.\n        The used PIL-based affine transformations are not yet able to\n        process non-image data. (This augmenter uses PIL-based affine\n        transformations to ensure that outputs are as similar as possible\n        to the paper's implementation.)\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    minimum of (\n        :class:`~imgaug.augmenters.flip.Fliplr`,\n        :class:`~imgaug.augmenters.size.KeepSizeByResize`,\n        :class:`~imgaug.augmenters.size.Crop`,\n        :class:`~imgaug.augmenters.meta.Sequential`,\n        :class:`~imgaug.augmenters.meta.SomeOf`,\n        :class:`~imgaug.augmenters.meta.Identity`,\n        :class:`~imgaug.augmenters.pillike.Autocontrast`,\n        :class:`~imgaug.augmenters.pillike.Equalize`,\n        :class:`~imgaug.augmenters.arithmetic.Invert`,\n        :class:`~imgaug.augmenters.pillike.Affine`,\n        :class:`~imgaug.augmenters.pillike.Posterize`,\n        :class:`~imgaug.augmenters.pillike.Solarize`,\n        :class:`~imgaug.augmenters.pillike.EnhanceColor`,\n        :class:`~imgaug.augmenters.pillike.EnhanceContrast`,\n        :class:`~imgaug.augmenters.pillike.EnhanceBrightness`,\n        :class:`~imgaug.augmenters.pillike.EnhanceSharpness`,\n        :class:`~imgaug.augmenters.arithmetic.Cutout`,\n        :class:`~imgaug.augmenters.pillike.FilterBlur`,\n        :class:`~imgaug.augmenters.pillike.FilterSmooth`\n    )\n\n    Parameters\n    ----------\n    n : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or None, optional\n        Parameter ``N`` in the paper, i.e. number of transformations to apply.\n        The paper suggests ``N=2`` for ImageNet.\n        See also parameter ``n`` in :class:`~imgaug.augmenters.meta.SomeOf`\n        for more details.\n\n        Note that horizontal flips (p=50%) and crops are always applied. This\n        parameter only determines how many of the other transformations\n        are applied per image.\n\n\n    m : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or None, optional\n        Parameter ``M`` in the paper, i.e. magnitude/severity/strength of the\n        applied transformations in interval ``[0 .. 30]`` with ``M=0`` being\n        the weakest. The paper suggests for ImageNet ``M=9`` in case of\n        ResNet-50 and ``M=28`` in case of EfficientNet-B7.\n        This implementation uses a default value of ``(6, 12)``, i.e. the\n        value is uniformly sampled per image from the interval ``[6 .. 12]``.\n        This ensures greater diversity of transformations than using a single\n        fixed value.\n\n        * If ``int``: That value will always be used.\n        * If ``tuple`` ``(a, b)``: A random value will be uniformly sampled per\n          image from the discrete interval ``[a .. b]``.\n        * If ``list``: A random value will be picked from the list per image.\n        * If ``StochasticParameter``: For ``B`` images in a batch, ``B`` values\n          will be sampled per augmenter (provided the augmenter is dependent\n          on the magnitude).\n\n    cval : number or tuple of number or list of number or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        The constant value to use when filling in newly created pixels.\n        See parameter `fillcolor` in\n        :class:`~imgaug.augmenters.pillike.Affine` for details.\n\n        The paper's repository uses an RGB value of ``125, 122, 113``.\n        This implementation uses a single intensity value of ``128``, which\n        should work better for cases where input images don't have exactly\n        ``3`` channels or come from a different dataset than used by the\n        paper.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.RandAugment(n=2, m=9)\n\n    Create a RandAugment augmenter similar to the suggested hyperparameters\n    in the paper.\n\n    >>> aug = iaa.RandAugment(m=30)\n\n    Create a RandAugment augmenter with maximum magnitude/strength.\n\n    >>> aug = iaa.RandAugment(m=(0, 9))\n\n    Create a RandAugment augmenter that applies its transformations with a\n    random magnitude between ``0`` (very weak) and ``9`` (recommended for\n    ImageNet and ResNet-50). ``m`` is sampled per transformation.\n\n    >>> aug = iaa.RandAugment(n=(0, 3))\n\n    Create a RandAugment augmenter that applies ``0`` to ``3`` of its\n    child transformations to images. Horizontal flips (p=50%) and crops are\n    always applied.\n\n    \"\"\"\n\n    _M_MAX = 30\n\n    # according to paper:\n    # N=2, M=9 is optimal for ImageNet with ResNet-50\n    # N=2, M=28 is optimal for ImageNet with EfficientNet-B7\n    # for cval they use [125, 122, 113]\n    # Added in 0.4.0.\n    def __init__(self, n=2, m=(6, 12), cval=128,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=invalid-name\n        seed = seed if random_state == \"deprecated\" else random_state\n        rng = iarandom.RNG.create_if_not_rng_(seed)\n\n        # we don't limit the value range to 10 here, because the paper\n        # gives several examples of using more than 10 for M\n        m = iap.handle_discrete_param(\n            m, \"m\", value_range=(0, None),\n            tuple_to_uniform=True, list_to_choice=True,\n            allow_floats=False)\n        self._m = m\n        self._cval = cval\n\n        # The paper says in Appendix A.2.3 \"ImageNet\", that they actually\n        # always execute Horizontal Flips and Crops first and only then a\n        # random selection of the other transformations.\n        # Hence, we split here into two groups.\n        # It's not really clear what crop parameters they use, so we\n        # choose [0..M] here.\n        initial_augs = self._create_initial_augmenters_list(m)\n        main_augs = self._create_main_augmenters_list(m, cval)\n\n        # assign random state to all child augmenters\n        for lst in [initial_augs, main_augs]:\n            for augmenter in lst:\n                augmenter.random_state = rng\n\n        super(RandAugment, self).__init__(\n            [\n                meta.Sequential(initial_augs,\n                                seed=rng.derive_rng_()),\n                meta.SomeOf(n, main_augs, random_order=True,\n                            seed=rng.derive_rng_())\n            ],\n            seed=rng, name=name,\n            random_state=random_state, deterministic=deterministic\n        )\n\n    # Added in 0.4.0.\n    @classmethod\n    def _create_initial_augmenters_list(cls, m):\n        # pylint: disable=invalid-name\n        return [\n            flip.Fliplr(0.5),\n            sizelib.KeepSizeByResize(\n                # assuming that the paper implementation crops M pixels from\n                # 224px ImageNet images, we crop here a fraction of\n                # M*(M_max/224)\n                sizelib.Crop(\n                    percent=iap.Divide(\n                        iap.Uniform(0, m),\n                        224,\n                        elementwise=True),\n                    sample_independently=True,\n                    keep_size=False),\n                interpolation=\"linear\"\n            )\n        ]\n\n    # Added in 0.4.0.\n    @classmethod\n    def _create_main_augmenters_list(cls, m, cval):\n        # pylint: disable=invalid-name\n        m_max = cls._M_MAX\n\n        def _float_parameter(level, maxval):\n            maxval_norm = maxval / m_max\n            return iap.Multiply(level, maxval_norm, elementwise=True)\n\n        def _int_parameter(level, maxval):\n            # paper applies just int(), so we don't round here\n            return iap.Discretize(_float_parameter(level, maxval),\n                                  round=False)\n\n        # In the paper's code they use the definition from AutoAugment,\n        # which is 0.1 + M*1.8/10. But that results in 0.1 for M=0, i.e. for\n        # Brightness an almost black image, while M=5 would result in an\n        # unaltered image. For AutoAugment that may be fine, as M is optimized\n        # for each operation individually, but here we have only one fixed M\n        # for all operations. Hence, we rather set this to 1.0 +/- M*0.9/10,\n        # so that M=10 would result in 0.1 or 1.9.\n        def _enhance_parameter(level):\n            fparam = _float_parameter(level, 0.9)\n            return iap.Clip(\n                iap.Add(1.0, iap.RandomSign(fparam), elementwise=True),\n                0.1, 1.9\n            )\n\n        def _subtract(a, b):\n            return iap.Subtract(a, b, elementwise=True)\n\n        def _affine(*args, **kwargs):\n            kwargs[\"fillcolor\"] = cval\n            if \"center\" not in kwargs:\n                kwargs[\"center\"] = (0.0, 0.0)\n            return pillike.Affine(*args, **kwargs)\n\n        _rnd_s = iap.RandomSign\n        shear_max = np.rad2deg(0.3)\n\n        # we don't add vertical flips here, paper is not really clear about\n        # whether they used them or not\n        return [\n            meta.Identity(),\n            pillike.Autocontrast(cutoff=0),\n            pillike.Equalize(),\n            arithmetic.Invert(p=1.0),\n            # they use Image.rotate() for the rotation, which uses\n            # the image center as the rotation center\n            _affine(rotate=_rnd_s(_float_parameter(m, 30)),\n                    center=(0.5, 0.5)),\n            # paper uses 4 - int_parameter(M, 4)\n            pillike.Posterize(\n                nb_bits=_subtract(\n                    8,\n                    iap.Clip(_int_parameter(m, 6), 0, 6)\n                )\n            ),\n            # paper uses 256 - int_parameter(M, 256)\n            pillike.Solarize(\n                p=1.0,\n                threshold=iap.Clip(\n                    _subtract(256, _int_parameter(m, 256)),\n                    0, 256\n                )\n            ),\n            pillike.EnhanceColor(_enhance_parameter(m)),\n            pillike.EnhanceContrast(_enhance_parameter(m)),\n            pillike.EnhanceBrightness(_enhance_parameter(m)),\n            pillike.EnhanceSharpness(_enhance_parameter(m)),\n            _affine(shear={\"x\": _rnd_s(_float_parameter(m, shear_max))}),\n            _affine(shear={\"y\": _rnd_s(_float_parameter(m, shear_max))}),\n            _affine(translate_percent={\"x\": _rnd_s(_float_parameter(m, 0.33))}),\n            _affine(translate_percent={\"y\": _rnd_s(_float_parameter(m, 0.33))}),\n            # paper code uses 20px on CIFAR (i.e. size 20/32), no information\n            # on ImageNet values so we just use the same values\n            arithmetic.Cutout(1,\n                              size=iap.Clip(\n                                  _float_parameter(m, 20 / 32), 0, 20 / 32),\n                              squared=True,\n                              fill_mode=\"constant\",\n                              cval=cval),\n            pillike.FilterBlur(),\n            pillike.FilterSmooth()\n        ]\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        someof = self[1]\n        return [someof.n, self._m, self._cval]\n"
  },
  {
    "path": "imgaug/augmenters/color.py",
    "content": "\"\"\"\nAugmenters that affect image colors or image colorspaces.\n\nList of augmenters:\n\n    * :class:`InColorspace` (deprecated)\n    * :class:`WithColorspace`\n    * :class:`WithBrightnessChannels`\n    * :class:`MultiplyAndAddToBrightness`\n    * :class:`MultiplyBrightness`\n    * :class:`AddToBrightness`\n    * :class:`WithHueAndSaturation`\n    * :class:`MultiplyHueAndSaturation`\n    * :class:`MultiplyHue`\n    * :class:`MultiplySaturation`\n    * :class:`RemoveSaturation`\n    * :class:`AddToHueAndSaturation`\n    * :class:`AddToHue`\n    * :class:`AddToSaturation`\n    * :class:`ChangeColorspace`\n    * :class:`Grayscale`\n    * :class:`ChangeColorTemperature`\n    * :class:`KMeansColorQuantization`\n    * :class:`UniformColorQuantization`\n    * :class:`Posterize`\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nfrom abc import ABCMeta, abstractmethod\n\nimport numpy as np\nimport cv2\nimport six\nimport six.moves as sm\n\nimport imgaug as ia\nfrom imgaug.imgaug import _normalize_cv2_input_arr_\nfrom . import meta\nfrom . import blend\nfrom . import arithmetic\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\nfrom .. import random as iarandom\n\n\n# pylint: disable=invalid-name\nCSPACE_RGB = \"RGB\"\nCSPACE_BGR = \"BGR\"\nCSPACE_GRAY = \"GRAY\"\nCSPACE_YCrCb = \"YCrCb\"\nCSPACE_HSV = \"HSV\"\nCSPACE_HLS = \"HLS\"\nCSPACE_Lab = \"Lab\"  # aka CIELAB\n# TODO add Luv to various color/contrast augmenters as random default choice?\nCSPACE_Luv = \"Luv\"  # aka CIE 1976, aka CIELUV\nCSPACE_YUV = \"YUV\"  # aka CIE 1960\nCSPACE_CIE = \"CIE\"  # aka CIE 1931, aka XYZ in OpenCV\nCSPACE_ALL = {CSPACE_RGB, CSPACE_BGR, CSPACE_GRAY, CSPACE_YCrCb,\n              CSPACE_HSV, CSPACE_HLS, CSPACE_Lab, CSPACE_Luv,\n              CSPACE_YUV, CSPACE_CIE}\n# pylint: enable=invalid-name\n\n\ndef _get_opencv_attr(attr_names):\n    for attr_name in attr_names:\n        if hasattr(cv2, attr_name):\n            return getattr(cv2, attr_name)\n    ia.warn(\"Could not find any of the following attributes in cv2: %s. \"\n            \"This can cause issues with colorspace transformations.\" % (\n                attr_names))\n    return None\n\n\n_CSPACE_OPENCV_CONV_VARS = {\n    # RGB\n    (CSPACE_RGB, CSPACE_BGR): cv2.COLOR_RGB2BGR,\n    (CSPACE_RGB, CSPACE_GRAY): cv2.COLOR_RGB2GRAY,\n    (CSPACE_RGB, CSPACE_YCrCb): _get_opencv_attr([\"COLOR_RGB2YCR_CB\"]),\n    (CSPACE_RGB, CSPACE_HSV): cv2.COLOR_RGB2HSV,\n    (CSPACE_RGB, CSPACE_HLS): cv2.COLOR_RGB2HLS,\n    (CSPACE_RGB, CSPACE_Lab): _get_opencv_attr([\"COLOR_RGB2LAB\",\n                                                \"COLOR_RGB2Lab\"]),\n    (CSPACE_RGB, CSPACE_Luv): cv2.COLOR_RGB2LUV,\n    (CSPACE_RGB, CSPACE_YUV): cv2.COLOR_RGB2YUV,\n    (CSPACE_RGB, CSPACE_CIE): cv2.COLOR_RGB2XYZ,\n    # BGR\n    (CSPACE_BGR, CSPACE_RGB): cv2.COLOR_BGR2RGB,\n    (CSPACE_BGR, CSPACE_GRAY): cv2.COLOR_BGR2GRAY,\n    (CSPACE_BGR, CSPACE_YCrCb): _get_opencv_attr([\"COLOR_BGR2YCR_CB\"]),\n    (CSPACE_BGR, CSPACE_HSV): cv2.COLOR_BGR2HSV,\n    (CSPACE_BGR, CSPACE_HLS): cv2.COLOR_BGR2HLS,\n    (CSPACE_BGR, CSPACE_Lab): _get_opencv_attr([\"COLOR_BGR2LAB\",\n                                                \"COLOR_BGR2Lab\"]),\n    (CSPACE_BGR, CSPACE_Luv): cv2.COLOR_BGR2LUV,\n    (CSPACE_BGR, CSPACE_YUV): cv2.COLOR_BGR2YUV,\n    (CSPACE_BGR, CSPACE_CIE): cv2.COLOR_BGR2XYZ,\n    # GRAY\n    # YCrCb\n    (CSPACE_YCrCb, CSPACE_RGB): _get_opencv_attr([\"COLOR_YCrCb2RGB\",\n                                                  \"COLOR_YCR_CB2RGB\"]),\n    (CSPACE_YCrCb, CSPACE_BGR): _get_opencv_attr([\"COLOR_YCrCb2BGR\",\n                                                  \"COLOR_YCR_CB2BGR\"]),\n    # HSV\n    (CSPACE_HSV, CSPACE_RGB): cv2.COLOR_HSV2RGB,\n    (CSPACE_HSV, CSPACE_BGR): cv2.COLOR_HSV2BGR,\n    # HLS\n    (CSPACE_HLS, CSPACE_RGB): cv2.COLOR_HLS2RGB,\n    (CSPACE_HLS, CSPACE_BGR): cv2.COLOR_HLS2BGR,\n    # Lab\n    (CSPACE_Lab, CSPACE_RGB): _get_opencv_attr([\"COLOR_Lab2RGB\",\n                                                \"COLOR_LAB2RGB\"]),\n    (CSPACE_Lab, CSPACE_BGR): _get_opencv_attr([\"COLOR_Lab2BGR\",\n                                                \"COLOR_LAB2BGR\"]),\n    # Luv\n    (CSPACE_Luv, CSPACE_RGB): _get_opencv_attr([\"COLOR_Luv2RGB\",\n                                                \"COLOR_LUV2RGB\"]),\n    (CSPACE_Luv, CSPACE_BGR): _get_opencv_attr([\"COLOR_Luv2BGR\",\n                                                \"COLOR_LUV2BGR\"]),\n    # YUV\n    (CSPACE_YUV, CSPACE_RGB): cv2.COLOR_YUV2RGB,\n    (CSPACE_YUV, CSPACE_BGR): cv2.COLOR_YUV2BGR,\n    # CIE\n    (CSPACE_CIE, CSPACE_RGB): cv2.COLOR_XYZ2RGB,\n    (CSPACE_CIE, CSPACE_BGR): cv2.COLOR_XYZ2BGR,\n}\n\n# This defines which colorspace pairs will be converted in-place in\n# change_colorspace_(). Currently, all colorspaces seem to work fine with\n# in-place transformations, which is why they are all set to True.\n_CHANGE_COLORSPACE_INPLACE = {\n    # RGB\n    (CSPACE_RGB, CSPACE_BGR): True,\n    (CSPACE_RGB, CSPACE_GRAY): True,\n    (CSPACE_RGB, CSPACE_YCrCb): True,\n    (CSPACE_RGB, CSPACE_HSV): True,\n    (CSPACE_RGB, CSPACE_HLS): True,\n    (CSPACE_RGB, CSPACE_Lab): True,\n    (CSPACE_RGB, CSPACE_Luv): True,\n    (CSPACE_RGB, CSPACE_YUV): True,\n    (CSPACE_RGB, CSPACE_CIE): True,\n    # BGR\n    (CSPACE_BGR, CSPACE_RGB): True,\n    (CSPACE_BGR, CSPACE_GRAY): True,\n    (CSPACE_BGR, CSPACE_YCrCb): True,\n    (CSPACE_BGR, CSPACE_HSV): True,\n    (CSPACE_BGR, CSPACE_HLS): True,\n    (CSPACE_BGR, CSPACE_Lab): True,\n    (CSPACE_BGR, CSPACE_Luv): True,\n    (CSPACE_BGR, CSPACE_YUV): True,\n    (CSPACE_BGR, CSPACE_CIE): True,\n    # GRAY\n    # YCrCb\n    (CSPACE_YCrCb, CSPACE_RGB): True,\n    (CSPACE_YCrCb, CSPACE_BGR): True,\n    # HSV\n    (CSPACE_HSV, CSPACE_RGB): True,\n    (CSPACE_HSV, CSPACE_BGR): True,\n    # HLS\n    (CSPACE_HLS, CSPACE_RGB): True,\n    (CSPACE_HLS, CSPACE_BGR): True,\n    # Lab\n    (CSPACE_Lab, CSPACE_RGB): True,\n    (CSPACE_Lab, CSPACE_BGR): True,\n    # Luv\n    (CSPACE_Luv, CSPACE_RGB): True,\n    (CSPACE_Luv, CSPACE_BGR): True,\n    # YUV\n    (CSPACE_YUV, CSPACE_RGB): True,\n    (CSPACE_YUV, CSPACE_BGR): True,\n    # CIE\n    (CSPACE_CIE, CSPACE_RGB): True,\n    (CSPACE_CIE, CSPACE_BGR): True,\n}\n\n\ndef change_colorspace_(image, to_colorspace, from_colorspace=CSPACE_RGB):\n    \"\"\"Change the colorspace of an image inplace.\n\n    .. note::\n\n        All outputs of this function are `uint8`. For some colorspaces this\n        may not be optimal.\n\n    .. note::\n\n        Output grayscale images will still have three channels.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to convert from one colorspace into another.\n        Usually expected to have shape ``(H,W,3)``.\n\n    to_colorspace : str\n        The target colorspace. See the ``CSPACE`` constants,\n        e.g. ``imgaug.augmenters.color.CSPACE_RGB``.\n\n    from_colorspace : str, optional\n        The source colorspace. Analogous to `to_colorspace`. Defaults\n        to ``RGB``.\n\n    Returns\n    -------\n    ndarray\n        Image with target colorspace. *Can* be the same array instance as was\n        originally provided (i.e. changed inplace). Grayscale images will\n        still have three channels.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> import numpy as np\n    >>> # fake RGB image\n    >>> image_rgb = np.arange(4*4*3).astype(np.uint8).reshape((4, 4, 3))\n    >>> image_bgr = iaa.change_colorspace_(np.copy(image_rgb), iaa.CSPACE_BGR)\n\n    \"\"\"\n    # some colorspaces here should use image/255.0 according to\n    # the docs, but at least for conversion to grayscale that\n    # results in errors, ie uint8 is expected\n\n    # this was once used to accomodate for image .flags -- still necessary?\n    def _get_dst(image_, from_to_cspace):\n        if _CHANGE_COLORSPACE_INPLACE[from_to_cspace]:\n            return image_\n        return None\n\n    # cv2 does not support height/width 0\n    # we don't check here if the channel axis is zero-sized as for colorspace\n    # transformations it should never be 0\n    if 0 in image.shape[0:2]:\n        return image\n\n    iadt.allow_only_uint8({image.dtype})\n\n    for arg_name in [\"to_colorspace\", \"from_colorspace\"]:\n        assert locals()[arg_name] in CSPACE_ALL, (\n            \"Expected `%s` to be one of: %s. Got: %s.\" % (\n                arg_name, CSPACE_ALL, locals()[arg_name]))\n\n    assert from_colorspace != CSPACE_GRAY, (\n        \"Cannot convert from grayscale to another colorspace as colors \"\n        \"cannot be recovered.\")\n\n    assert image.ndim == 3, (\n        \"Expected image shape to be three-dimensional, i.e. (H,W,C), \"\n        \"got %d dimensions with shape %s.\" % (image.ndim, image.shape))\n    assert image.shape[2] == 3, (\n        \"Expected number of channels to be three, \"\n        \"got %d channels (shape %s).\" % (image.shape[2], image.shape,))\n\n    if from_colorspace == to_colorspace:\n        return image\n\n    from_to_direct = (from_colorspace, to_colorspace)\n    from_to_indirect = [\n        (from_colorspace, CSPACE_RGB),\n        (CSPACE_RGB, to_colorspace)\n    ]\n\n    image = _normalize_cv2_input_arr_(image)\n    image_aug = image\n    if from_to_direct in _CSPACE_OPENCV_CONV_VARS:\n        from2to_var = _CSPACE_OPENCV_CONV_VARS[from_to_direct]\n        dst = _get_dst(image_aug, from_to_direct)\n        image_aug = cv2.cvtColor(image_aug, from2to_var, dst=dst)\n    else:\n        from2rgb_var = _CSPACE_OPENCV_CONV_VARS[from_to_indirect[0]]\n        rgb2to_var = _CSPACE_OPENCV_CONV_VARS[from_to_indirect[1]]\n\n        dst1 = _get_dst(image_aug, from_to_indirect[0])\n        dst2 = _get_dst(image_aug, from_to_indirect[1])\n\n        image_aug = cv2.cvtColor(image_aug, from2rgb_var, dst=dst1)\n        image_aug = cv2.cvtColor(image_aug, rgb2to_var, dst=dst2)\n\n    # for grayscale: covnert from (H, W) to (H, W, 3)\n    if len(image_aug.shape) == 2:\n        image_aug = image_aug[:, :, np.newaxis]\n        image_aug = np.tile(image_aug, (1, 1, 3))\n\n    return image_aug\n\n\ndef change_colorspaces_(images, to_colorspaces, from_colorspaces=CSPACE_RGB):\n    \"\"\"Change the colorspaces of a batch of images inplace.\n\n    .. note::\n\n        All outputs of this function are `uint8`. For some colorspaces this\n        may not be optimal.\n\n    .. note::\n\n        Output grayscale images will still have three channels.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    Parameters\n    ----------\n    images : ndarray or list of ndarray\n        The images to convert from one colorspace into another.\n        Either a list of ``(H,W,3)`` arrays or a single ``(N,H,W,3)`` array.\n\n    to_colorspaces : str or iterable of str\n        The target colorspaces. Either a single string (all images will be\n        converted to the same colorspace) or an iterable of strings (one per\n        image). See the ``CSPACE`` constants, e.g.\n        ``imgaug.augmenters.color.CSPACE_RGB``.\n\n    from_colorspaces : str or list of str, optional\n        The source colorspace. Analogous to `to_colorspace`. Defaults\n        to ``RGB``.\n\n    Returns\n    -------\n    ndarray or list of ndarray\n        Images with target colorspaces. *Can* contain the same array instances\n        as were originally provided (i.e. changed inplace). Grayscale images\n        will still have three channels.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> import numpy as np\n    >>> # fake RGB image\n    >>> image_rgb = np.arange(4*4*3).astype(np.uint8).reshape((4, 4, 3))\n    >>> images_rgb = [image_rgb, image_rgb, image_rgb]\n    >>> images_rgb_copy = [np.copy(image_rgb) for image_rgb in images_rgb]\n    >>> images_bgr = iaa.change_colorspaces_(images_rgb_copy, iaa.CSPACE_BGR)\n\n    Create three example ``RGB`` images and convert them to ``BGR`` colorspace.\n\n    >>> images_rgb_copy = [np.copy(image_rgb) for image_rgb in images_rgb]\n    >>> images_various = iaa.change_colorspaces_(\n    >>>     images_rgb_copy, [iaa.CSPACE_BGR, iaa.CSPACE_HSV, iaa.CSPACE_GRAY])\n\n    Chnage the colorspace of the first image to ``BGR``, the one of the second\n    image to ``HSV`` and the one of the third image to ``grayscale`` (note\n    that in the latter case the image will still have shape ``(H,W,3)``,\n    not ``(H,W,1)``).\n\n    \"\"\"\n    def _validate(arg, arg_name):\n        if ia.is_string(arg):\n            arg = [arg] * len(images)\n        else:\n            assert ia.is_iterable(arg), (\n                \"Expected `%s` to be either an iterable of strings or a single \"\n                \"string. Got type: %s.\" % (arg_name, type(arg).__name__)\n            )\n            assert len(arg) == len(images), (\n                \"If `%s` is provided as a list it must have the same length \"\n                \"as `images`. Got length %d, expected %d.\" % (\n                    arg_name, len(arg), len(images)))\n\n        return arg\n\n    to_colorspaces = _validate(to_colorspaces, \"to_colorspaces\")\n    from_colorspaces = _validate(from_colorspaces, \"from_colorspaces\")\n\n    gen = zip(images, to_colorspaces, from_colorspaces)\n    for i, (image, to_colorspace, from_colorspace) in enumerate(gen):\n        images[i] = change_colorspace_(image, to_colorspace, from_colorspace)\n    return images\n\n\n# Added in 0.4.0.\nclass _KelvinToRGBTableSingleton(object):\n    _INSTANCE = None\n\n    # Added in 0.4.0.\n    @classmethod\n    def get_instance(cls):\n        if cls._INSTANCE is None:\n            cls._INSTANCE = _KelvinToRGBTable()\n        return cls._INSTANCE\n\n\n# Added in 0.4.0.\nclass _KelvinToRGBTable(object):\n    # Added in 0.4.0.\n    def __init__(self):\n        self.table = self.create_table()\n\n    def transform_kelvins_to_rgb_multipliers(self, kelvins):\n        \"\"\"Transform kelvin values to corresponding multipliers for RGB images.\n\n        A single returned multiplier denotes the channelwise multipliers\n        in the range ``[0.0, 1.0]`` to apply to an image to change its kelvin\n        value to the desired one.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        kelvins : iterable of number\n            Imagewise temperatures in kelvin.\n\n        Returns\n        -------\n        ndarray\n            ``float32 (N, 3) ndarrays``, one per kelvin.\n\n        \"\"\"\n        kelvins = np.clip(kelvins, 1000, 40000)\n\n        tbl_indices = kelvins / 100 - (1000//100)\n        tbl_indices_floored = np.floor(tbl_indices)\n        tbl_indices_ceiled = np.ceil(tbl_indices)\n        interpolation_factors = tbl_indices - tbl_indices_floored\n\n        tbl_indices_floored_int = tbl_indices_floored.astype(np.int32)\n        tbl_indices_ceiled_int = tbl_indices_ceiled.astype(np.int32)\n\n        multipliers_floored = self.table[tbl_indices_floored_int, :]\n        multipliers_ceiled = self.table[tbl_indices_ceiled_int, :]\n        multipliers = (\n            multipliers_floored\n            + interpolation_factors[:, np.newaxis]\n            * (multipliers_ceiled - multipliers_floored)\n        )\n\n        return multipliers\n\n    # Added in 0.4.0.\n    @classmethod\n    def create_table(cls):\n        table = np.float32([\n            [255, 56, 0],  # K=1000\n            [255, 71, 0],  # K=1100\n            [255, 83, 0],  # K=1200\n            [255, 93, 0],  # K=1300\n            [255, 101, 0],  # K=1400\n            [255, 109, 0],  # K=1500\n            [255, 115, 0],  # K=1600\n            [255, 121, 0],  # K=1700\n            [255, 126, 0],  # K=1800\n            [255, 131, 0],  # K=1900\n            [255, 137, 18],  # K=2000\n            [255, 142, 33],  # K=2100\n            [255, 147, 44],  # K=2200\n            [255, 152, 54],  # K=2300\n            [255, 157, 63],  # K=2400\n            [255, 161, 72],  # K=2500\n            [255, 165, 79],  # K=2600\n            [255, 169, 87],  # K=2700\n            [255, 173, 94],  # K=2800\n            [255, 177, 101],  # K=2900\n            [255, 180, 107],  # K=3000\n            [255, 184, 114],  # K=3100\n            [255, 187, 120],  # K=3200\n            [255, 190, 126],  # K=3300\n            [255, 193, 132],  # K=3400\n            [255, 196, 137],  # K=3500\n            [255, 199, 143],  # K=3600\n            [255, 201, 148],  # K=3700\n            [255, 204, 153],  # K=3800\n            [255, 206, 159],  # K=3900\n            [255, 209, 163],  # K=4000\n            [255, 211, 168],  # K=4100\n            [255, 213, 173],  # K=4200\n            [255, 215, 177],  # K=4300\n            [255, 217, 182],  # K=4400\n            [255, 219, 186],  # K=4500\n            [255, 221, 190],  # K=4600\n            [255, 223, 194],  # K=4700\n            [255, 225, 198],  # K=4800\n            [255, 227, 202],  # K=4900\n            [255, 228, 206],  # K=5000\n            [255, 230, 210],  # K=5100\n            [255, 232, 213],  # K=5200\n            [255, 233, 217],  # K=5300\n            [255, 235, 220],  # K=5400\n            [255, 236, 224],  # K=5500\n            [255, 238, 227],  # K=5600\n            [255, 239, 230],  # K=5700\n            [255, 240, 233],  # K=5800\n            [255, 242, 236],  # K=5900\n            [255, 243, 239],  # K=6000\n            [255, 244, 242],  # K=6100\n            [255, 245, 245],  # K=6200\n            [255, 246, 248],  # K=6300\n            [255, 248, 251],  # K=6400\n            [255, 249, 253],  # K=6500\n            [254, 249, 255],  # K=6600\n            [252, 247, 255],  # K=6700\n            [249, 246, 255],  # K=6800\n            [247, 245, 255],  # K=6900\n            [245, 243, 255],  # K=7000\n            [243, 242, 255],  # K=7100\n            [240, 241, 255],  # K=7200\n            [239, 240, 255],  # K=7300\n            [237, 239, 255],  # K=7400\n            [235, 238, 255],  # K=7500\n            [233, 237, 255],  # K=7600\n            [231, 236, 255],  # K=7700\n            [230, 235, 255],  # K=7800\n            [228, 234, 255],  # K=7900\n            [227, 233, 255],  # K=8000\n            [225, 232, 255],  # K=8100\n            [224, 231, 255],  # K=8200\n            [222, 230, 255],  # K=8300\n            [221, 230, 255],  # K=8400\n            [220, 229, 255],  # K=8500\n            [218, 228, 255],  # K=8600\n            [217, 227, 255],  # K=8700\n            [216, 227, 255],  # K=8800\n            [215, 226, 255],  # K=8900\n            [214, 225, 255],  # K=9000\n            [212, 225, 255],  # K=9100\n            [211, 224, 255],  # K=9200\n            [210, 223, 255],  # K=9300\n            [209, 223, 255],  # K=9400\n            [208, 222, 255],  # K=9500\n            [207, 221, 255],  # K=9600\n            [207, 221, 255],  # K=9700\n            [206, 220, 255],  # K=9800\n            [205, 220, 255],  # K=9900\n            [204, 219, 255],  # K=10000\n            [203, 219, 255],  # K=10100\n            [202, 218, 255],  # K=10200\n            [201, 218, 255],  # K=10300\n            [201, 217, 255],  # K=10400\n            [200, 217, 255],  # K=10500\n            [199, 216, 255],  # K=10600\n            [199, 216, 255],  # K=10700\n            [198, 216, 255],  # K=10800\n            [197, 215, 255],  # K=10900\n            [196, 215, 255],  # K=11000\n            [196, 214, 255],  # K=11100\n            [195, 214, 255],  # K=11200\n            [195, 214, 255],  # K=11300\n            [194, 213, 255],  # K=11400\n            [193, 213, 255],  # K=11500\n            [193, 212, 255],  # K=11600\n            [192, 212, 255],  # K=11700\n            [192, 212, 255],  # K=11800\n            [191, 211, 255],  # K=11900\n            [191, 211, 255],  # K=12000\n            [190, 211, 255],  # K=12100\n            [190, 210, 255],  # K=12200\n            [189, 210, 255],  # K=12300\n            [189, 210, 255],  # K=12400\n            [188, 210, 255],  # K=12500\n            [188, 209, 255],  # K=12600\n            [187, 209, 255],  # K=12700\n            [187, 209, 255],  # K=12800\n            [186, 208, 255],  # K=12900\n            [186, 208, 255],  # K=13000\n            [185, 208, 255],  # K=13100\n            [185, 208, 255],  # K=13200\n            [185, 207, 255],  # K=13300\n            [184, 207, 255],  # K=13400\n            [184, 207, 255],  # K=13500\n            [183, 207, 255],  # K=13600\n            [183, 206, 255],  # K=13700\n            [183, 206, 255],  # K=13800\n            [182, 206, 255],  # K=13900\n            [182, 206, 255],  # K=14000\n            [182, 205, 255],  # K=14100\n            [181, 205, 255],  # K=14200\n            [181, 205, 255],  # K=14300\n            [181, 205, 255],  # K=14400\n            [180, 205, 255],  # K=14500\n            [180, 204, 255],  # K=14600\n            [180, 204, 255],  # K=14700\n            [179, 204, 255],  # K=14800\n            [179, 204, 255],  # K=14900\n            [179, 204, 255],  # K=15000\n            [178, 203, 255],  # K=15100\n            [178, 203, 255],  # K=15200\n            [178, 203, 255],  # K=15300\n            [178, 203, 255],  # K=15400\n            [177, 203, 255],  # K=15500\n            [177, 202, 255],  # K=15600\n            [177, 202, 255],  # K=15700\n            [177, 202, 255],  # K=15800\n            [176, 202, 255],  # K=15900\n            [176, 202, 255],  # K=16000\n            [176, 202, 255],  # K=16100\n            [175, 201, 255],  # K=16200\n            [175, 201, 255],  # K=16300\n            [175, 201, 255],  # K=16400\n            [175, 201, 255],  # K=16500\n            [175, 201, 255],  # K=16600\n            [174, 201, 255],  # K=16700\n            [174, 201, 255],  # K=16800\n            [174, 200, 255],  # K=16900\n            [174, 200, 255],  # K=17000\n            [173, 200, 255],  # K=17100\n            [173, 200, 255],  # K=17200\n            [173, 200, 255],  # K=17300\n            [173, 200, 255],  # K=17400\n            [173, 200, 255],  # K=17500\n            [172, 199, 255],  # K=17600\n            [172, 199, 255],  # K=17700\n            [172, 199, 255],  # K=17800\n            [172, 199, 255],  # K=17900\n            [172, 199, 255],  # K=18000\n            [171, 199, 255],  # K=18100\n            [171, 199, 255],  # K=18200\n            [171, 199, 255],  # K=18300\n            [171, 198, 255],  # K=18400\n            [171, 198, 255],  # K=18500\n            [170, 198, 255],  # K=18600\n            [170, 198, 255],  # K=18700\n            [170, 198, 255],  # K=18800\n            [170, 198, 255],  # K=18900\n            [170, 198, 255],  # K=19000\n            [170, 198, 255],  # K=19100\n            [169, 198, 255],  # K=19200\n            [169, 197, 255],  # K=19300\n            [169, 197, 255],  # K=19400\n            [169, 197, 255],  # K=19500\n            [169, 197, 255],  # K=19600\n            [169, 197, 255],  # K=19700\n            [169, 197, 255],  # K=19800\n            [168, 197, 255],  # K=19900\n            [168, 197, 255],  # K=20000\n            [168, 197, 255],  # K=20100\n            [168, 197, 255],  # K=20200\n            [168, 196, 255],  # K=20300\n            [168, 196, 255],  # K=20400\n            [168, 196, 255],  # K=20500\n            [167, 196, 255],  # K=20600\n            [167, 196, 255],  # K=20700\n            [167, 196, 255],  # K=20800\n            [167, 196, 255],  # K=20900\n            [167, 196, 255],  # K=21000\n            [167, 196, 255],  # K=21100\n            [167, 196, 255],  # K=21200\n            [166, 196, 255],  # K=21300\n            [166, 195, 255],  # K=21400\n            [166, 195, 255],  # K=21500\n            [166, 195, 255],  # K=21600\n            [166, 195, 255],  # K=21700\n            [166, 195, 255],  # K=21800\n            [166, 195, 255],  # K=21900\n            [166, 195, 255],  # K=22000\n            [165, 195, 255],  # K=22100\n            [165, 195, 255],  # K=22200\n            [165, 195, 255],  # K=22300\n            [165, 195, 255],  # K=22400\n            [165, 195, 255],  # K=22500\n            [165, 195, 255],  # K=22600\n            [165, 194, 255],  # K=22700\n            [165, 194, 255],  # K=22800\n            [165, 194, 255],  # K=22900\n            [164, 194, 255],  # K=23000\n            [164, 194, 255],  # K=23100\n            [164, 194, 255],  # K=23200\n            [164, 194, 255],  # K=23300\n            [164, 194, 255],  # K=23400\n            [164, 194, 255],  # K=23500\n            [164, 194, 255],  # K=23600\n            [164, 194, 255],  # K=23700\n            [164, 194, 255],  # K=23800\n            [164, 194, 255],  # K=23900\n            [163, 194, 255],  # K=24000\n            [163, 194, 255],  # K=24100\n            [163, 193, 255],  # K=24200\n            [163, 193, 255],  # K=24300\n            [163, 193, 255],  # K=24400\n            [163, 193, 255],  # K=24500\n            [163, 193, 255],  # K=24600\n            [163, 193, 255],  # K=24700\n            [163, 193, 255],  # K=24800\n            [163, 193, 255],  # K=24900\n            [163, 193, 255],  # K=25000\n            [162, 193, 255],  # K=25100\n            [162, 193, 255],  # K=25200\n            [162, 193, 255],  # K=25300\n            [162, 193, 255],  # K=25400\n            [162, 193, 255],  # K=25500\n            [162, 193, 255],  # K=25600\n            [162, 193, 255],  # K=25700\n            [162, 193, 255],  # K=25800\n            [162, 192, 255],  # K=25900\n            [162, 192, 255],  # K=26000\n            [162, 192, 255],  # K=26100\n            [162, 192, 255],  # K=26200\n            [162, 192, 255],  # K=26300\n            [161, 192, 255],  # K=26400\n            [161, 192, 255],  # K=26500\n            [161, 192, 255],  # K=26600\n            [161, 192, 255],  # K=26700\n            [161, 192, 255],  # K=26800\n            [161, 192, 255],  # K=26900\n            [161, 192, 255],  # K=27000\n            [161, 192, 255],  # K=27100\n            [161, 192, 255],  # K=27200\n            [161, 192, 255],  # K=27300\n            [161, 192, 255],  # K=27400\n            [161, 192, 255],  # K=27500\n            [161, 192, 255],  # K=27600\n            [161, 192, 255],  # K=27700\n            [160, 192, 255],  # K=27800\n            [160, 192, 255],  # K=27900\n            [160, 191, 255],  # K=28000\n            [160, 191, 255],  # K=28100\n            [160, 191, 255],  # K=28200\n            [160, 191, 255],  # K=28300\n            [160, 191, 255],  # K=28400\n            [160, 191, 255],  # K=28500\n            [160, 191, 255],  # K=28600\n            [160, 191, 255],  # K=28700\n            [160, 191, 255],  # K=28800\n            [160, 191, 255],  # K=28900\n            [160, 191, 255],  # K=29000\n            [160, 191, 255],  # K=29100\n            [160, 191, 255],  # K=29200\n            [159, 191, 255],  # K=29300\n            [159, 191, 255],  # K=29400\n            [159, 191, 255],  # K=29500\n            [159, 191, 255],  # K=29600\n            [159, 191, 255],  # K=29700\n            [159, 191, 255],  # K=29800\n            [159, 191, 255],  # K=29900\n            [159, 191, 255],  # K=30000\n            [159, 191, 255],  # K=30100\n            [159, 191, 255],  # K=30200\n            [159, 191, 255],  # K=30300\n            [159, 190, 255],  # K=30400\n            [159, 190, 255],  # K=30500\n            [159, 190, 255],  # K=30600\n            [159, 190, 255],  # K=30700\n            [159, 190, 255],  # K=30800\n            [159, 190, 255],  # K=30900\n            [159, 190, 255],  # K=31000\n            [158, 190, 255],  # K=31100\n            [158, 190, 255],  # K=31200\n            [158, 190, 255],  # K=31300\n            [158, 190, 255],  # K=31400\n            [158, 190, 255],  # K=31500\n            [158, 190, 255],  # K=31600\n            [158, 190, 255],  # K=31700\n            [158, 190, 255],  # K=31800\n            [158, 190, 255],  # K=31900\n            [158, 190, 255],  # K=32000\n            [158, 190, 255],  # K=32100\n            [158, 190, 255],  # K=32200\n            [158, 190, 255],  # K=32300\n            [158, 190, 255],  # K=32400\n            [158, 190, 255],  # K=32500\n            [158, 190, 255],  # K=32600\n            [158, 190, 255],  # K=32700\n            [158, 190, 255],  # K=32800\n            [158, 190, 255],  # K=32900\n            [158, 190, 255],  # K=33000\n            [158, 190, 255],  # K=33100\n            [157, 190, 255],  # K=33200\n            [157, 190, 255],  # K=33300\n            [157, 189, 255],  # K=33400\n            [157, 189, 255],  # K=33500\n            [157, 189, 255],  # K=33600\n            [157, 189, 255],  # K=33700\n            [157, 189, 255],  # K=33800\n            [157, 189, 255],  # K=33900\n            [157, 189, 255],  # K=34000\n            [157, 189, 255],  # K=34100\n            [157, 189, 255],  # K=34200\n            [157, 189, 255],  # K=34300\n            [157, 189, 255],  # K=34400\n            [157, 189, 255],  # K=34500\n            [157, 189, 255],  # K=34600\n            [157, 189, 255],  # K=34700\n            [157, 189, 255],  # K=34800\n            [157, 189, 255],  # K=34900\n            [157, 189, 255],  # K=35000\n            [157, 189, 255],  # K=35100\n            [157, 189, 255],  # K=35200\n            [157, 189, 255],  # K=35300\n            [157, 189, 255],  # K=35400\n            [157, 189, 255],  # K=35500\n            [156, 189, 255],  # K=35600\n            [156, 189, 255],  # K=35700\n            [156, 189, 255],  # K=35800\n            [156, 189, 255],  # K=35900\n            [156, 189, 255],  # K=36000\n            [156, 189, 255],  # K=36100\n            [156, 189, 255],  # K=36200\n            [156, 189, 255],  # K=36300\n            [156, 189, 255],  # K=36400\n            [156, 189, 255],  # K=36500\n            [156, 189, 255],  # K=36600\n            [156, 189, 255],  # K=36700\n            [156, 189, 255],  # K=36800\n            [156, 189, 255],  # K=36900\n            [156, 189, 255],  # K=37000\n            [156, 189, 255],  # K=37100\n            [156, 188, 255],  # K=37200\n            [156, 188, 255],  # K=37300\n            [156, 188, 255],  # K=37400\n            [156, 188, 255],  # K=37500\n            [156, 188, 255],  # K=37600\n            [156, 188, 255],  # K=37700\n            [156, 188, 255],  # K=37800\n            [156, 188, 255],  # K=37900\n            [156, 188, 255],  # K=38000\n            [156, 188, 255],  # K=38100\n            [156, 188, 255],  # K=38200\n            [156, 188, 255],  # K=38300\n            [155, 188, 255],  # K=38400\n            [155, 188, 255],  # K=38500\n            [155, 188, 255],  # K=38600\n            [155, 188, 255],  # K=38700\n            [155, 188, 255],  # K=38800\n            [155, 188, 255],  # K=38900\n            [155, 188, 255],  # K=39000\n            [155, 188, 255],  # K=39100\n            [155, 188, 255],  # K=39200\n            [155, 188, 255],  # K=39300\n            [155, 188, 255],  # K=39400\n            [155, 188, 255],  # K=39500\n            [155, 188, 255],  # K=39600\n            [155, 188, 255],  # K=39700\n            [155, 188, 255],  # K=39800\n            [155, 188, 255],  # K=39900\n            [155, 188, 255],  # K=40000\n        ]) / 255.0\n        _KelvinToRGBTable._TABLE = table\n        return table\n\n\ndef change_color_temperatures_(images, kelvins, from_colorspaces=CSPACE_RGB):\n    \"\"\"Change in-place the temperature of images to given values in Kelvin.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.change_colorspace_`.\n\n    Parameters\n    ----------\n    images : ndarray or list of ndarray\n        The images which's color temperature is supposed to be changed.\n        Either a list of ``(H,W,3)`` arrays or a single ``(N,H,W,3)`` array.\n\n    kelvins : iterable of number\n        Temperatures in Kelvin. One per image. Expected value range is in\n        the interval ``(1000, 4000)``.\n\n    from_colorspaces : str or list of str, optional\n        The source colorspace.\n        See :func:`~imgaug.augmenters.color.change_colorspaces_`.\n        Defaults to ``RGB``.\n\n    Returns\n    -------\n    ndarray or list of ndarray\n        Images with target color temperatures.\n        The input array(s) might have been changed in-place.\n\n    \"\"\"\n    # we return here early, because we validate below the first kelvin value\n    if len(images) == 0:\n        return images\n\n    # TODO this is very similar to the validation in change_colorspaces_().\n    #      Make DRY.\n    def _validate(arg, arg_name, datatype):\n        if ia.is_iterable(arg) and not ia.is_string(arg):\n            assert len(arg) == len(images), (\n                \"If `%s` is provided as an iterable it must have the same \"\n                \"length as `images`. Got length %d, expected %d.\" % (\n                    arg_name, len(arg), len(images)))\n        elif datatype == \"str\":\n            assert ia.is_string(arg), (\n                \"Expected `%s` to be either an iterable of strings or a single \"\n                \"string. Got type %s.\" % (arg_name, type(arg).__name__))\n            arg = [arg] * len(images)\n        else:\n            assert ia.is_single_number(arg), (\n                \"Expected `%s` to be either an iterable of numbers or a single \"\n                \"number. Got type %s.\" % (arg_name, type(arg).__name__))\n            arg = np.tile(np.float32([arg]), (len(images),))\n        return arg\n\n    kelvins = _validate(kelvins, \"kelvins\", \"number\")\n    from_colorspaces = _validate(from_colorspaces, \"from_colorspaces\", \"str\")\n\n    # list `kelvins` inputs are not yet converted to ndarray by _validate()\n    kelvins = np.array(kelvins, dtype=np.float32)\n\n    # Validate only one kelvin value for performance reasons.\n    # If values are outside that range, the kelvin table simply clips them.\n    # If there are no images (and hence no kelvin values), we already returned\n    # above.\n    assert 1000 <= kelvins[0] <= 40000, (\n        \"Expected Kelvin values in the interval [1000, 40000]. \"\n        \"Got interval [%.8f, %.8f].\" % (np.min(kelvins), np.max(kelvins)))\n\n    table = _KelvinToRGBTableSingleton.get_instance()\n    rgb_multipliers = table.transform_kelvins_to_rgb_multipliers(kelvins)\n    rgb_multipliers_nhwc = rgb_multipliers.reshape((-1, 1, 1, 3))\n\n    gen = enumerate(zip(images, rgb_multipliers_nhwc, from_colorspaces))\n    for i, (image, rgb_multiplier_hwc, from_colorspace) in gen:\n        image_rgb = change_colorspace_(image,\n                                       to_colorspace=CSPACE_RGB,\n                                       from_colorspace=from_colorspace)\n\n        # we always have uint8 at this point as only that is accepted by\n        # convert_colorspace\n\n        # all multipliers are in the range [0.0, 1.0], hence we can afford to\n        # not clip here\n        image_temp_adj = np.round(\n            image_rgb.astype(np.float32) * rgb_multiplier_hwc\n        ).astype(np.uint8)\n\n        image_orig_cspace = change_colorspace_(image_temp_adj,\n                                               to_colorspace=from_colorspace,\n                                               from_colorspace=CSPACE_RGB)\n        images[i] = image_orig_cspace\n    return images\n\n\ndef change_color_temperature(image, kelvin, from_colorspace=CSPACE_RGB):\n    \"\"\"Change the temperature of an image to a given value in Kelvin.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.change_color_temperatures_`.\n\n    Parameters\n    ----------\n    image : ndarray\n        The image which's color temperature is supposed to be changed.\n        Expected to be of shape ``(H,W,3)`` array.\n\n    kelvin : number\n        The temperature in Kelvin. Expected value range is in\n        the interval ``(1000, 4000)``.\n\n    from_colorspace : str, optional\n        The source colorspace.\n        See :func:`~imgaug.augmenters.color.change_colorspaces_`.\n        Defaults to ``RGB``.\n\n    Returns\n    -------\n    ndarray\n        Image with target color temperature.\n\n    \"\"\"\n    return change_color_temperatures_(image[np.newaxis, ...],\n                                      [kelvin],\n                                      from_colorspaces=[from_colorspace])[0]\n\n\n@ia.deprecated(alt_func=\"WithColorspace\")\ndef InColorspace(to_colorspace, from_colorspace=\"RGB\", children=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n    \"\"\"Convert images to another colorspace.\"\"\"\n    # pylint: disable=invalid-name\n    return WithColorspace(to_colorspace, from_colorspace, children,\n                          seed=seed, name=name,\n                          random_state=random_state,\n                          deterministic=deterministic)\n\n\n# TODO add tests\nclass WithColorspace(meta.Augmenter):\n    \"\"\"\n    Apply child augmenters within a specific colorspace.\n\n    This augumenter takes a source colorspace A and a target colorspace B\n    as well as children C. It changes images from A to B, then applies the\n    child augmenters C and finally changes the colorspace back from B to A.\n    See also ChangeColorspace() for more.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.change_colorspaces_`.\n\n    Parameters\n    ----------\n    to_colorspace : str\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    from_colorspace : str, optional\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    children : imgaug.augmenters.meta.Augmenter or list of imgaug.augmenters.meta.Augmenter or None, optional\n        One or more augmenters to apply to converted images.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.WithColorspace(\n    >>>     to_colorspace=iaa.CSPACE_HSV,\n    >>>     from_colorspace=iaa.CSPACE_RGB,\n    >>>     children=iaa.WithChannels(\n    >>>         0,\n    >>>         iaa.Add((0, 50))\n    >>>     )\n    >>> )\n\n    Convert to ``HSV`` colorspace, add a value between ``0`` and ``50``\n    (uniformly sampled per image) to the Hue channel, then convert back to the\n    input colorspace (``RGB``).\n\n    \"\"\"\n\n    def __init__(self, to_colorspace, from_colorspace=CSPACE_RGB, children=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(WithColorspace, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.to_colorspace = to_colorspace\n        self.from_colorspace = from_colorspace\n        self.children = meta.handle_children_list(children, self.name, \"then\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        with batch.propagation_hooks_ctx(self, hooks, parents):\n            # TODO this did not fail in the tests when there was only one\n            #      `if` with all three steps in it\n            if batch.images is not None:\n                batch.images = change_colorspaces_(\n                    batch.images,\n                    to_colorspaces=self.to_colorspace,\n                    from_colorspaces=self.from_colorspace)\n\n            batch = self.children.augment_batch_(\n                batch,\n                parents=parents + [self],\n                hooks=hooks\n            )\n\n            if batch.images is not None:\n                batch.images = change_colorspaces_(\n                    batch.images,\n                    to_colorspaces=self.from_colorspace,\n                    from_colorspaces=self.to_colorspace)\n        return batch\n\n    def _to_deterministic(self):\n        aug = self.copy()\n        aug.children = aug.children.to_deterministic()\n        aug.deterministic = True\n        aug.random_state = self.random_state.derive_rng_()\n        return aug\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.channels]\n\n    def get_children_lists(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists`.\"\"\"\n        return [self.children]\n\n    def __str__(self):\n        return (\n            \"WithColorspace(from_colorspace=%s, \"\n            \"to_colorspace=%s, name=%s, children=[%s], deterministic=%s)\" % (\n                self.from_colorspace, self.to_colorspace, self.name,\n                self.children, self.deterministic)\n        )\n\n\nclass WithBrightnessChannels(meta.Augmenter):\n    \"\"\"Augmenter to apply child augmenters to brightness-related image channels.\n\n    This augmenter first converts an image to a random colorspace containing a\n    brightness-related channel (e.g. ``V`` in ``HSV``), then extracts that\n    channel and applies its child augmenters to this one channel. Afterwards,\n    it reintegrates the augmented channel into the full image and converts\n    back to the input colorspace.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.change_colorspaces_`.\n\n    Parameters\n    ----------\n    children : imgaug.augmenters.meta.Augmenter or list of imgaug.augmenters.meta.Augmenter or None, optional\n        One or more augmenters to apply to the brightness channels.\n        They receive images with a single channel and have to modify these.\n\n    to_colorspace : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        Colorspace in which to extract the brightness-related channels.\n        Currently, ``imgaug.augmenters.color.CSPACE_YCrCb``, ``CSPACE_HSV``,\n        ``CSPACE_HLS``, ``CSPACE_Lab``, ``CSPACE_Luv``, ``CSPACE_YUV``,\n        ``CSPACE_CIE`` are supported.\n\n            * If ``imgaug.ALL``: Will pick imagewise a random colorspace from\n              all supported colorspaces.\n            * If ``str``: Will always use this colorspace.\n            * If ``list`` or ``str``: Will pick imagewise a random colorspace\n              from this list.\n            * If :class:`~imgaug.parameters.StochasticParameter`:\n              A parameter that will be queried once per batch to generate\n              all target colorspaces. Expected to return strings matching the\n              ``CSPACE_*`` constants.\n\n    from_colorspace : str, optional\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.WithBrightnessChannels(iaa.Add((-50, 50)))\n\n    Add ``-50`` to ``50`` to the brightness-related channels of each image.\n\n    >>> aug = iaa.WithBrightnessChannels(\n    >>>     iaa.Add((-50, 50)), to_colorspace=[iaa.CSPACE_Lab, iaa.CSPACE_HSV])\n\n    Add ``-50`` to ``50`` to the brightness-related channels of each image,\n    but pick those brightness-related channels only from ``Lab`` (``L``) and\n    ``HSV`` (``V``) colorspaces.\n\n    >>> aug = iaa.WithBrightnessChannels(\n    >>>     iaa.Add((-50, 50)), from_colorspace=iaa.CSPACE_BGR)\n\n    Add ``-50`` to ``50`` to the brightness-related channels of each image,\n    where the images are provided in ``BGR`` colorspace instead of the\n    standard ``RGB``.\n\n    \"\"\"\n\n    # Usually one would think that CSPACE_CIE (=XYZ) would also work, as\n    # wikipedia says that Y denotes luminance, but this resulted in strong\n    # color changes (tried also the other channels).\n    _CSPACE_TO_CHANNEL_ID = {\n        CSPACE_YCrCb: 0,\n        CSPACE_HSV: 2,\n        CSPACE_HLS: 1,\n        CSPACE_Lab: 0,\n        CSPACE_Luv: 0,\n        CSPACE_YUV: 0\n    }\n\n    _VALID_COLORSPACES = set(_CSPACE_TO_CHANNEL_ID.keys())\n\n    # Added in 0.4.0.\n    def __init__(self, children=None,\n                 to_colorspace=[\n                     CSPACE_YCrCb,\n                     CSPACE_HSV,\n                     CSPACE_HLS,\n                     CSPACE_Lab,\n                     CSPACE_Luv,\n                     CSPACE_YUV],\n                 from_colorspace=\"RGB\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=dangerous-default-value\n        super(WithBrightnessChannels, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.children = meta.handle_children_list(children, self.name, \"then\")\n        self.to_colorspace = iap.handle_categorical_string_param(\n            to_colorspace, \"to_colorspace\",\n            valid_values=self._VALID_COLORSPACES)\n        self.from_colorspace = from_colorspace\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        with batch.propagation_hooks_ctx(self, hooks, parents):\n            images_cvt = None\n            to_colorspaces = None\n\n            if batch.images is not None:\n                to_colorspaces = self.to_colorspace.draw_samples(\n                    (len(batch.images),), random_state)\n                images_cvt = change_colorspaces_(\n                    batch.images,\n                    from_colorspaces=self.from_colorspace,\n                    to_colorspaces=to_colorspaces,)\n                brightness_channels = self._extract_brightness_channels(\n                    images_cvt, to_colorspaces)\n\n                batch.images = brightness_channels\n\n            batch = self.children.augment_batch_(\n                batch, parents=parents + [self], hooks=hooks)\n\n            if batch.images is not None:\n                batch.images = self._invert_extract_brightness_channels(\n                    batch.images, images_cvt, to_colorspaces)\n\n                batch.images = change_colorspaces_(\n                    batch.images,\n                    from_colorspaces=to_colorspaces,\n                    to_colorspaces=self.from_colorspace)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _extract_brightness_channels(self, images, colorspaces):\n        result = []\n        for image, colorspace in zip(images, colorspaces):\n            channel_id = self._CSPACE_TO_CHANNEL_ID[colorspace]\n            # Note that augmenters expect (H,W,C) and not (H,W), so cannot\n            # just use image[:, :, channel_id] here.\n            channel = image[:, :, channel_id:channel_id+1]\n            result.append(channel)\n        return result\n\n    # Added in 0.4.0.\n    def _invert_extract_brightness_channels(self, channels, images,\n                                            colorspaces):\n        for channel, image, colorspace in zip(channels, images, colorspaces):\n            channel_id = self._CSPACE_TO_CHANNEL_ID[colorspace]\n            image[:, :, channel_id:channel_id+1] = channel\n        return images\n\n    # Added in 0.4.0.\n    def _to_deterministic(self):\n        aug = self.copy()\n        aug.children = aug.children.to_deterministic()\n        aug.deterministic = True\n        aug.random_state = self.random_state.derive_rng_()\n        return aug\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.to_colorspace, self.from_colorspace]\n\n    # Added in 0.4.0.\n    def get_children_lists(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists`.\"\"\"\n        return [self.children]\n\n    # Added in 0.4.0.\n    def __str__(self):\n        return (\n            \"WithBrightnessChannels(\"\n            \"to_colorspace=%s, \"\n            \"from_colorspace=%s, \"\n            \"name=%s, \"\n            \"children=%s, \"\n            \"deterministic=%s)\" % (\n                self.to_colorspace,\n                self.from_colorspace,\n                self.name,\n                self.children,\n                self.deterministic)\n        )\n\n\nclass MultiplyAndAddToBrightness(WithBrightnessChannels):\n    \"\"\"Multiply and add to the brightness channels of input images.\n\n    This is a wrapper around :class:`WithBrightnessChannels` and hence\n    performs internally the same projection to random colorspaces.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.WithBrightnessChannels`.\n\n    Parameters\n    ----------\n    mul : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.airthmetic.Multiply`.\n\n    add : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.airthmetic.Add`.\n\n    to_colorspace : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.color.WithBrightnessChannels`.\n\n    from_colorspace : str, optional\n        See :class:`~imgaug.augmenters.color.WithBrightnessChannels`.\n\n    random_order : bool, optional\n        Whether to apply the add and multiply operations in random\n        order (``True``). If ``False``, this augmenter will always first\n        multiply and then add.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MultiplyAndAddToBrightness(mul=(0.5, 1.5), add=(-30, 30))\n\n    Convert each image to a colorspace with a brightness-related channel,\n    extract that channel, multiply it by a factor between ``0.5`` and ``1.5``,\n    add a value between ``-30`` and ``30`` and convert back to the original\n    colorspace.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, mul=(0.7, 1.3), add=(-30, 30),\n                 to_colorspace=[\n                     CSPACE_YCrCb,\n                     CSPACE_HSV,\n                     CSPACE_HLS,\n                     CSPACE_Lab,\n                     CSPACE_Luv,\n                     CSPACE_YUV],\n                 from_colorspace=\"RGB\",\n                 random_order=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=dangerous-default-value\n        mul = (\n            meta.Identity()\n            if ia.is_single_number(mul) and np.isclose(mul, 1.0)\n            else arithmetic.Multiply(mul))\n        add = meta.Identity() if add == 0 else arithmetic.Add(add)\n\n        super(MultiplyAndAddToBrightness, self).__init__(\n            children=meta.Sequential(\n                [mul, add],\n                random_order=random_order\n            ),\n            to_colorspace=to_colorspace,\n            from_colorspace=from_colorspace,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    # Added in 0.4.0.\n    def __str__(self):\n        return (\n            \"MultiplyAndAddToBrightness(\"\n            \"mul=%s, \"\n            \"add=%s, \"\n            \"to_colorspace=%s, \"\n            \"from_colorspace=%s, \"\n            \"random_order=%s, \"\n            \"name=%s, \"\n            \"deterministic=%s)\" % (\n                str(self.children[0]),\n                str(self.children[1]),\n                self.to_colorspace,\n                self.from_colorspace,\n                self.children.random_order,\n                self.name,\n                self.deterministic)\n        )\n\n\nclass MultiplyBrightness(MultiplyAndAddToBrightness):\n    \"\"\"Multiply the brightness channels of input images.\n\n    This is a wrapper around :class:`WithBrightnessChannels` and hence\n    performs internally the same projection to random colorspaces.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.MultiplyAndAddToBrightness`.\n\n    Parameters\n    ----------\n    mul : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.airthmetic.Multiply`.\n\n    to_colorspace : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.color.WithBrightnessChannels`.\n\n    from_colorspace : str, optional\n        See :class:`~imgaug.augmenters.color.WithBrightnessChannels`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MultiplyBrightness((0.5, 1.5))\n\n    Convert each image to a colorspace with a brightness-related channel,\n    extract that channel, multiply it by a factor between ``0.5`` and ``1.5``,\n    and convert back to the original colorspace.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, mul=(0.7, 1.3),\n                 to_colorspace=[\n                     CSPACE_YCrCb,\n                     CSPACE_HSV,\n                     CSPACE_HLS,\n                     CSPACE_Lab,\n                     CSPACE_Luv,\n                     CSPACE_YUV],\n                 from_colorspace=\"RGB\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=dangerous-default-value\n        super(MultiplyBrightness, self).__init__(\n            mul=mul,\n            add=0,\n            to_colorspace=to_colorspace,\n            from_colorspace=from_colorspace,\n            random_order=False,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass AddToBrightness(MultiplyAndAddToBrightness):\n    \"\"\"Add to the brightness channels of input images.\n\n    This is a wrapper around :class:`WithBrightnessChannels` and hence\n    performs internally the same projection to random colorspaces.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.MultiplyAndAddToBrightness`.\n\n    Parameters\n    ----------\n    add : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.airthmetic.Add`.\n\n    to_colorspace : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.color.WithBrightnessChannels`.\n\n    from_colorspace : str, optional\n        See :class:`~imgaug.augmenters.color.WithBrightnessChannels`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AddToBrightness((-30, 30))\n\n    Convert each image to a colorspace with a brightness-related channel,\n    extract that channel, add between ``-30`` and ``30`` and convert back\n    to the original colorspace.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, add=(-30, 30),\n                 to_colorspace=[\n                     CSPACE_YCrCb,\n                     CSPACE_HSV,\n                     CSPACE_HLS,\n                     CSPACE_Lab,\n                     CSPACE_Luv,\n                     CSPACE_YUV],\n                 from_colorspace=\"RGB\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=dangerous-default-value\n        super(AddToBrightness, self).__init__(\n            mul=1.0,\n            add=add,\n            to_colorspace=to_colorspace,\n            from_colorspace=from_colorspace,\n            random_order=False,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO Merge this into WithColorspace? A bit problematic due to int16\n#      conversion that would make WithColorspace less flexible.\n# TODO add option to choose overflow behaviour for hue and saturation channels,\n#      e.g. clip, modulo or wrap\nclass WithHueAndSaturation(meta.Augmenter):\n    \"\"\"\n    Apply child augmenters to hue and saturation channels.\n\n    This augumenter takes an image in a source colorspace, converts\n    it to HSV, extracts the H (hue) and S (saturation) channels,\n    applies the provided child augmenters to these channels\n    and finally converts back to the original colorspace.\n\n    The image array generated by this augmenter and provided to its children\n    is in ``int16`` (**sic!** only augmenters that can handle ``int16`` arrays\n    can be children!). The hue channel is mapped to the value\n    range ``[0, 255]``. Before converting back to the source colorspace, the\n    saturation channel's values are clipped to ``[0, 255]``. A modulo operation\n    is applied to the hue channel's values, followed by a mapping from\n    ``[0, 255]`` to ``[0, 180]`` (and finally the colorspace conversion).\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.change_colorspaces_`.\n\n    Parameters\n    ----------\n    from_colorspace : str, optional\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    children : imgaug.augmenters.meta.Augmenter or list of imgaug.augmenters.meta.Augmenter or None, optional\n        One or more augmenters to apply to converted images.\n        They receive ``int16`` images with two channels (hue, saturation)\n        and have to modify these.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.WithHueAndSaturation(\n    >>>     iaa.WithChannels(0, iaa.Add((0, 50)))\n    >>> )\n\n    Create an augmenter that will add a random value between ``0`` and ``50``\n    (uniformly sampled per image) hue channel in HSV colorspace. It\n    automatically accounts for the hue being in angular representation, i.e.\n    if the angle goes beyond 360 degrees, it will start again at 0 degrees.\n    The colorspace is finally converted back to ``RGB`` (default setting).\n\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.WithHueAndSaturation([\n    >>>     iaa.WithChannels(0, iaa.Add((-30, 10))),\n    >>>     iaa.WithChannels(1, [\n    >>>         iaa.Multiply((0.5, 1.5)),\n    >>>         iaa.LinearContrast((0.75, 1.25))\n    >>>     ])\n    >>> ])\n\n    Create an augmenter that adds a random value sampled uniformly\n    from the range ``[-30, 10]`` to the hue and multiplies the saturation\n    by a random factor sampled uniformly from ``[0.5, 1.5]``. It also\n    modifies the contrast of the saturation channel. After these steps,\n    the ``HSV`` image is converted back to ``RGB``.\n\n    \"\"\"\n\n    def __init__(self, children=None, from_colorspace=\"RGB\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(WithHueAndSaturation, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.children = meta.handle_children_list(children, self.name, \"then\")\n        self.from_colorspace = from_colorspace\n\n        # this dtype needs to be able to go beyond [0, 255] to e.g. accomodate\n        # for Add or Multiply\n        self._internal_dtype = np.int16\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        with batch.propagation_hooks_ctx(self, hooks, parents):\n            images_hs, images_hsv = self._images_to_hsv_(batch.images)\n            batch.images = images_hs\n\n            batch = self.children.augment_batch_(\n                batch, parents=parents + [self], hooks=hooks)\n\n            batch.images = self._hs_to_images_(batch.images, images_hsv)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _images_to_hsv_(self, images):\n        if images is None:\n            return None, None\n\n        # RGB (or other source colorspace) -> HSV\n        images_hsv = change_colorspaces_(\n            images, CSPACE_HSV, self.from_colorspace)\n\n        # HSV -> HS\n        images_hs = []\n        for image_hsv in images_hsv:\n            image_hsv = image_hsv.astype(np.int16)\n            # project hue from [0,180] to [0,255] so that child augmenters\n            # can assume the same value range for all channels\n            hue = (\n                (image_hsv[:, :, 0].astype(np.float32) / 180.0) * 255.0\n            ).astype(self._internal_dtype)\n            saturation = image_hsv[:, :, 1]\n            images_hs.append(np.stack([hue, saturation], axis=-1))\n        if ia.is_np_array(images_hsv):\n            images_hs = np.stack(images_hs, axis=0)\n        return images_hs, images_hsv\n\n    # Added in 0.4.0.\n    def _hs_to_images_(self, images_hs, images_hsv):\n        if images_hs is None:\n            return None\n        # postprocess augmented HS int16 data\n        # hue: modulo to [0, 255] then project to [0, 360/2]\n        # saturation: clip to [0, 255]\n        # + convert to uint8\n        # + re-attach V channel to HS\n        hue_and_sat_proj = []\n        for i, hs_aug in enumerate(images_hs):\n            hue_aug = hs_aug[:, :, 0]\n            sat_aug = hs_aug[:, :, 1]\n            hue_aug = (\n                (np.mod(hue_aug, 255).astype(np.float32) / 255.0)\n                * (360/2)\n            ).astype(np.uint8)\n            sat_aug = iadt.clip_(sat_aug, 0, 255).astype(np.uint8)\n            hue_and_sat_proj.append(\n                np.stack([hue_aug, sat_aug, images_hsv[i][:, :, 2]],\n                         axis=-1)\n            )\n        if ia.is_np_array(images_hs):\n            hue_and_sat_proj = np.uint8(hue_and_sat_proj)\n\n        # HSV -> RGB (or whatever the source colorspace was)\n        images_rgb = change_colorspaces_(\n            hue_and_sat_proj,\n            to_colorspaces=self.from_colorspace,\n            from_colorspaces=CSPACE_HSV)\n        return images_rgb\n\n    def _to_deterministic(self):\n        aug = self.copy()\n        aug.children = aug.children.to_deterministic()\n        aug.deterministic = True\n        aug.random_state = self.random_state.derive_rng_()\n        return aug\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.from_colorspace]\n\n    def get_children_lists(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists`.\"\"\"\n        return [self.children]\n\n    def __str__(self):\n        return (\n            \"WithHueAndSaturation(from_colorspace=%s, \"\n            \"name=%s, children=[%s], deterministic=%s)\" % (\n                self.from_colorspace, self.name,\n                self.children, self.deterministic)\n        )\n\n\nclass MultiplyHueAndSaturation(WithHueAndSaturation):\n    \"\"\"\n    Multipy hue and saturation by random values.\n\n    The augmenter first transforms images to HSV colorspace, then multiplies\n    the pixel values in the H and S channels and afterwards converts back to\n    RGB.\n\n    This augmenter is a wrapper around ``WithHueAndSaturation``.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.WithHueAndSaturation`.\n\n    Parameters\n    ----------\n    mul : None or number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Multiplier with which to multiply all hue *and* saturation values of\n        all pixels.\n        It is expected to be in the range ``-10.0`` to ``+10.0``.\n        Note that values of ``0.0`` or lower will remove all saturation.\n\n            * If this is ``None``, `mul_hue` and/or `mul_saturation`\n              may be set to values other than ``None``.\n            * If a number, then that multiplier will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the continuous\n              range ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, then a value will be sampled from that\n              parameter per image.\n\n    mul_hue : None or number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Multiplier with which to multiply all hue values.\n        This is expected to be in the range ``-10.0`` to ``+10.0`` and will\n        automatically be projected to an angular representation using\n        ``(hue/255) * (360/2)`` (OpenCV's hue representation is in the\n        range ``[0, 180]`` instead of ``[0, 360]``).\n        Only this or `mul` may be set, not both.\n\n            * If this and `mul_saturation` are both ``None``, `mul` may\n              be set to a non-``None`` value.\n            * If a number, then that multiplier will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the continuous\n              range ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, then a value will be sampled from that\n              parameter per image.\n\n    mul_saturation : None or number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Multiplier with which to multiply all saturation values.\n        It is expected to be in the range ``0.0`` to ``+10.0``.\n        Only this or `mul` may be set, not both.\n\n            * If this and `mul_hue` are both ``None``, `mul` may\n              be set to a non-``None`` value.\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the continuous\n              range ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, then a value will be sampled from that\n              parameter per image.\n\n    per_channel : bool or float, optional\n        Whether to sample per image only one value from `mul` and use it for\n        both hue and saturation (``False``) or to sample independently one\n        value for hue and one for saturation (``True``).\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``, otherwise as ``False``.\n\n        This parameter has no effect if `mul_hue` and/or `mul_saturation`\n        are used instead of `mul`.\n\n    from_colorspace : str, optional\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MultiplyHueAndSaturation((0.5, 1.5), per_channel=True)\n\n    Multiply hue and saturation by random values between ``0.5`` and ``1.5``\n    (independently per channel and the same value for all pixels within\n    that channel). The hue will be automatically projected to an angular\n    representation.\n\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MultiplyHueAndSaturation(mul_hue=(0.5, 1.5))\n\n    Multiply only the hue by random values between ``0.5`` and ``1.5``.\n\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MultiplyHueAndSaturation(mul_saturation=(0.5, 1.5))\n\n    Multiply only the saturation by random values between ``0.5`` and ``1.5``.\n\n    \"\"\"\n\n    def __init__(self, mul=None, mul_hue=None, mul_saturation=None,\n                 per_channel=False, from_colorspace=\"RGB\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        if mul is None and mul_hue is None and mul_saturation is None:\n            mul_hue = (0.5, 1.5)\n            mul_saturation = (0.0, 1.7)\n\n        if mul is not None:\n            assert mul_hue is None, (\n                \"`mul_hue` may not be set if `mul` is set. \"\n                \"It is set to: %s (type: %s).\" % (\n                    str(mul_hue), type(mul_hue)))\n            assert mul_saturation is None, (\n                \"`mul_saturation` may not be set if `mul` is set. \"\n                \"It is set to: %s (type: %s).\" % (\n                    str(mul_saturation), type(mul_saturation)))\n            mul = iap.handle_continuous_param(\n                mul, \"mul\", value_range=(-10.0, 10.0), tuple_to_uniform=True,\n                list_to_choice=True)\n        else:\n            if mul_hue is not None:\n                mul_hue = iap.handle_continuous_param(\n                    mul_hue, \"mul_hue\", value_range=(-10.0, 10.0),\n                    tuple_to_uniform=True, list_to_choice=True)\n            if mul_saturation is not None:\n                mul_saturation = iap.handle_continuous_param(\n                    mul_saturation, \"mul_saturation\", value_range=(0.0, 10.0),\n                    tuple_to_uniform=True, list_to_choice=True)\n\n        if random_state != \"deprecated\":\n            seed = random_state\n            random_state = \"deprecated\"\n\n        if seed is None:\n            rss = [None] * 5\n        else:\n            rss = iarandom.RNG.create_if_not_rng_(seed).derive_rngs_(5)\n\n        children = []\n        if mul is not None:\n            children.append(\n                arithmetic.Multiply(\n                    mul,\n                    per_channel=per_channel,\n                    seed=rss[0],\n                    name=\"%s-Multiply\" % (name,),\n                    random_state=random_state,\n                    deterministic=deterministic\n                )\n            )\n        else:\n            if mul_hue is not None:\n                children.append(\n                    meta.WithChannels(\n                        0,\n                        arithmetic.Multiply(\n                            mul_hue,\n                            seed=rss[0],\n                            name=\"%s-MultiplyHue\" % (name,),\n                            random_state=random_state,\n                            deterministic=deterministic\n                        ),\n                        seed=rss[1],\n                        name=\"%s-WithChannelsHue\" % (name,),\n                        random_state=random_state,\n                        deterministic=deterministic\n                    )\n                )\n            if mul_saturation is not None:\n                children.append(\n                    meta.WithChannels(\n                        1,\n                        arithmetic.Multiply(\n                            mul_saturation,\n                            seed=rss[2],\n                            name=\"%s-MultiplySaturation\" % (name,),\n                            random_state=random_state,\n                            deterministic=deterministic\n                        ),\n                        seed=rss[3],\n                        name=\"%s-WithChannelsSaturation\" % (name,),\n                        random_state=random_state,\n                        deterministic=deterministic\n                    )\n                )\n\n        super(MultiplyHueAndSaturation, self).__init__(\n            children,\n            from_colorspace=from_colorspace,\n            seed=rss[4],\n            name=name,\n            random_state=random_state, deterministic=deterministic\n        )\n\n\nclass MultiplyHue(MultiplyHueAndSaturation):\n    \"\"\"\n    Multiply the hue of images by random values.\n\n    The augmenter first transforms images to HSV colorspace, then multiplies\n    the pixel values in the H channel and afterwards converts back to\n    RGB.\n\n    This augmenter is a shortcut for ``MultiplyHueAndSaturation(mul_hue=...)``.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.MultiplyHueAndSaturation`.\n\n    Parameters\n    ----------\n    mul : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Multiplier with which to multiply all hue values.\n        This is expected to be in the range ``-10.0`` to ``+10.0`` and will\n        automatically be projected to an angular representation using\n        ``(hue/255) * (360/2)`` (OpenCV's hue representation is in the\n        range ``[0, 180]`` instead of ``[0, 360]``).\n        Only this or `mul` may be set, not both.\n\n            * If a number, then that multiplier will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the continuous\n              range ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, then a value will be sampled from that\n              parameter per image.\n\n    from_colorspace : str, optional\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MultiplyHue((0.5, 1.5))\n\n    Multiply the hue channel of images using random values between ``0.5``\n    and ``1.5``.\n\n    \"\"\"\n\n    def __init__(self, mul=(-3.0, 3.0), from_colorspace=\"RGB\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(MultiplyHue, self).__init__(\n            mul_hue=mul,\n            from_colorspace=from_colorspace,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass MultiplySaturation(MultiplyHueAndSaturation):\n    \"\"\"\n    Multiply the saturation of images by random values.\n\n    The augmenter first transforms images to HSV colorspace, then multiplies\n    the pixel values in the H channel and afterwards converts back to\n    RGB.\n\n    This augmenter is a shortcut for\n    ``MultiplyHueAndSaturation(mul_saturation=...)``.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.MultiplyHueAndSaturation`.\n\n    Parameters\n    ----------\n    mul : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Multiplier with which to multiply all saturation values.\n        It is expected to be in the range ``0.0`` to ``+10.0``.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the continuous\n              range ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, then a value will be sampled from that\n              parameter per image.\n\n    from_colorspace : str, optional\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MultiplySaturation((0.5, 1.5))\n\n    Multiply the saturation channel of images using random values between\n    ``0.5`` and ``1.5``.\n\n    \"\"\"\n\n    def __init__(self, mul=(0.0, 3.0), from_colorspace=\"RGB\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(MultiplySaturation, self).__init__(\n            mul_saturation=mul,\n            from_colorspace=from_colorspace,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass RemoveSaturation(MultiplySaturation):\n    \"\"\"Decrease the saturation of images by varying degrees.\n\n    This creates images looking similar to :class:`Grayscale`.\n\n    This augmenter is the same as ``MultiplySaturation((0.0, 1.0))``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.MultiplySaturation`.\n\n    Parameters\n    ----------\n    mul : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        *Inverse* multiplier to use for the saturation values.\n        High values denote stronger color removal. E.g. ``1.0`` will remove\n        all saturation, ``0.0`` will remove nothing.\n        Expected value range is ``[0.0, 1.0]``.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the continuous\n              range ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, then a value will be sampled from that\n              parameter per image.\n\n    from_colorspace : str, optional\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.RemoveSaturation((0.0, 1.0))\n\n    Create an augmenter that decreases saturation by varying degrees.\n\n    >>> aug = iaa.RemoveSaturation(1.0)\n\n    Create an augmenter that removes all saturation from input images.\n    This is similar to :class:`Grayscale`.\n\n    >>> aug = iaa.RemoveSaturation(from_colorspace=iaa.CSPACE_BGR)\n\n    Create an augmenter that decreases saturation of images in ``BGR``\n    colorspace by varying degrees.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, mul=1, from_colorspace=CSPACE_RGB,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        mul = iap.Subtract(\n            1.0,\n            iap.handle_continuous_param(mul, \"mul\",\n                                        value_range=(0.0, 1.0),\n                                        tuple_to_uniform=True,\n                                        list_to_choice=True),\n            elementwise=True\n        )\n        super(RemoveSaturation, self).__init__(\n            mul, from_colorspace=from_colorspace,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO removed deterministic and random_state here as parameters, because this\n# function creates multiple child augmenters. not sure if this is sensible\n# (give them all the same random state instead?)\n# TODO this is for now deactivated, because HSV images returned by opencv have\n#      value range 0-180 for the hue channel\n#      and are supposed to be angular representations, i.e. if values go below\n#      0 or above 180 they are supposed to overflow\n#      to 180 and 0\n# pylint: disable=pointless-string-statement\n\"\"\"\ndef AddToHueAndSaturation(value=0, per_channel=False, from_colorspace=\"RGB\",\n                          channels=[0, 1], name=None):  # pylint: disable=locally-disabled, dangerous-default-value, line-too-long\n    \"\"\n    Augmenter that transforms images into HSV space, selects the H and S\n    channels and then adds a given range of values to these.\n\n    Parameters\n    ----------\n    value : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.arithmetic.Add.__init__()`.\n\n    per_channel : bool or float, optional\n        See :func:`~imgaug.augmenters.arithmetic.Add.__init__()`.\n\n    from_colorspace : str, optional\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    channels : int or list of int or None, optional\n        See :func:`~imgaug.augmenters.meta.WithChannels.__init__()`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    Examples\n    --------\n    >>> aug = AddToHueAndSaturation((-20, 20), per_channel=True)\n\n    Adds random values between -20 and 20 to the hue and saturation\n    (independently per channel and the same value for all pixels within\n    that channel).\n\n    \"\"\n    if name is None:\n        name = \"Unnamed%s\" % (ia.caller_name(),)\n\n    return WithColorspace(\n        to_colorspace=\"HSV\",\n        from_colorspace=from_colorspace,\n        children=meta.WithChannels(\n            channels=channels,\n            children=arithmetic.Add(value=value, per_channel=per_channel)\n        ),\n        name=name\n    )\n\"\"\"\n# pylint: enable=pointless-string-statement\n\n\nclass AddToHueAndSaturation(meta.Augmenter):\n    \"\"\"\n    Increases or decreases hue and saturation by random values.\n\n    The augmenter first transforms images to HSV colorspace, then adds random\n    values to the H and S channels and afterwards converts back to RGB.\n\n    This augmenter is faster than using ``WithHueAndSaturation`` in combination\n    with ``Add``.\n\n    TODO add float support\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    Parameters\n    ----------\n    value : None or int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Value to add to the hue *and* saturation of all pixels.\n        It is expected to be in the range ``-255`` to ``+255``.\n\n            * If this is ``None``, `value_hue` and/or `value_saturation`\n              may be set to values other than ``None``.\n            * If an integer, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the discrete\n              range ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, then a value will be sampled from that\n              parameter per image.\n\n    value_hue : None or int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Value to add to the hue of all pixels.\n        This is expected to be in the range ``-255`` to ``+255`` and will\n        automatically be projected to an angular representation using\n        ``(hue/255) * (360/2)`` (OpenCV's hue representation is in the\n        range ``[0, 180]`` instead of ``[0, 360]``).\n        Only this or `value` may be set, not both.\n\n            * If this and `value_saturation` are both ``None``, `value` may\n              be set to a non-``None`` value.\n            * If an integer, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the discrete\n              range ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, then a value will be sampled from that\n              parameter per image.\n\n    value_saturation : None or int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Value to add to the saturation of all pixels.\n        It is expected to be in the range ``-255`` to ``+255``.\n        Only this or `value` may be set, not both.\n\n            * If this and `value_hue` are both ``None``, `value` may\n              be set to a non-``None`` value.\n            * If an integer, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the discrete\n              range ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, then a value will be sampled from that\n              parameter per image.\n\n    per_channel : bool or float, optional\n        Whether to sample per image only one value from `value` and use it for\n        both hue and saturation (``False``) or to sample independently one\n        value for hue and one for saturation (``True``).\n        If this value is a float ``p``, then for ``p`` percent of all images\n        `per_channel` will be treated as ``True``, otherwise as ``False``.\n\n        This parameter has no effect is `value_hue` and/or `value_saturation`\n        are used instead of `value`.\n\n    from_colorspace : str, optional\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AddToHueAndSaturation((-50, 50), per_channel=True)\n\n    Add random values between ``-50`` and ``50`` to the hue and saturation\n    (independently per channel and the same value for all pixels within\n    that channel).\n\n    \"\"\"\n\n    _LUT_CACHE = None\n\n    def __init__(self, value=None, value_hue=None, value_saturation=None,\n                 per_channel=False, from_colorspace=\"RGB\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(AddToHueAndSaturation, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        if value is None and value_hue is None and value_saturation is None:\n            value_hue = (-40, 40)\n            value_saturation = (-40, 40)\n\n        self.value = self._handle_value_arg(value, value_hue, value_saturation)\n        self.value_hue = self._handle_value_hue_arg(value_hue)\n        self.value_saturation = self._handle_value_saturation_arg(\n            value_saturation)\n        self.per_channel = iap.handle_probability_param(per_channel,\n                                                        \"per_channel\")\n        self.from_colorspace = from_colorspace\n        self.backend = \"cv2\"\n\n        # precompute tables for cv2.LUT\n        if self.backend == \"cv2\" and AddToHueAndSaturation._LUT_CACHE is None:\n            AddToHueAndSaturation._LUT_CACHE = self._generate_lut_table()\n\n    def _draw_samples(self, augmentables, random_state):\n        nb_images = len(augmentables)\n        rss = random_state.duplicate(2)\n\n        if self.value is not None:\n            per_channel = self.per_channel.draw_samples(\n                (nb_images,), random_state=rss[0])\n            per_channel = (per_channel > 0.5)\n\n            samples = self.value.draw_samples(\n                (nb_images, 2), random_state=rss[1]).astype(np.int32)\n            assert -255 <= samples[0, 0] <= 255, (\n                \"Expected values sampled from `value` in \"\n                \"AddToHueAndSaturation to be in range [-255, 255], \"\n                \"but got %.8f.\" % (samples[0, 0]))\n\n            samples_hue = samples[:, 0]\n            samples_saturation = np.copy(samples[:, 0])\n            samples_saturation[per_channel] = samples[per_channel, 1]\n        else:\n            if self.value_hue is not None:\n                samples_hue = self.value_hue.draw_samples(\n                    (nb_images,), random_state=rss[0]).astype(np.int32)\n            else:\n                samples_hue = np.zeros((nb_images,), dtype=np.int32)\n\n            if self.value_saturation is not None:\n                samples_saturation = self.value_saturation.draw_samples(\n                    (nb_images,), random_state=rss[1]).astype(np.int32)\n            else:\n                samples_saturation = np.zeros((nb_images,), dtype=np.int32)\n\n        # project hue to angular representation\n        # OpenCV uses range [0, 180] for the hue\n        samples_hue = (\n            (samples_hue.astype(np.float32) / 255.0) * (360/2)\n        ).astype(np.int32)\n\n        return samples_hue, samples_saturation\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        input_dtypes = iadt.copy_dtypes_for_restore(images, force_list=True)\n\n        # surprisingly, placing this here seems to be slightly slower than\n        # placing it inside the loop\n        # if isinstance(images_hsv, list):\n        #    images_hsv = [img.astype(np.int32) for img in images_hsv]\n        # else:\n        #    images_hsv = images_hsv.astype(np.int32)\n\n        images_hsv = change_colorspaces_(\n            images, CSPACE_HSV, self.from_colorspace)\n        samples = self._draw_samples(images, random_state)\n        hues = samples[0]\n        saturations = samples[1]\n\n        # this is needed if no cache for LUT is used:\n        # value_range = np.arange(0, 256, dtype=np.int16)\n\n        gen = enumerate(zip(images_hsv, hues, saturations))\n        for i, (image_hsv, hue_i, saturation_i) in gen:\n            if image_hsv.size == 0:\n                continue\n\n            if self.backend == \"cv2\":\n                image_hsv = self._transform_image_cv2(\n                    image_hsv, hue_i, saturation_i)\n            else:\n                image_hsv = self._transform_image_numpy(\n                    image_hsv, hue_i, saturation_i)\n\n            image_hsv = image_hsv.astype(input_dtypes[i])\n            image_rgb = change_colorspace_(\n                image_hsv,\n                to_colorspace=self.from_colorspace,\n                from_colorspace=CSPACE_HSV)\n            batch.images[i] = image_rgb\n\n        return batch\n\n    @classmethod\n    def _transform_image_cv2(cls, image_hsv, hue, saturation):\n        # this has roughly the same speed as the numpy backend\n        # for 64x64 and is about 25% faster for 224x224\n\n        # code without using cache:\n        # table_hue = np.mod(value_range + sample_hue, 180)\n        # table_saturation = np.clip(value_range + sample_saturation, 0, 255)\n\n        # table_hue = table_hue.astype(np.uint8, copy=False)\n        # table_saturation = table_saturation.astype(np.uint8, copy=False)\n\n        # image_hsv[..., 0] = cv2.LUT(image_hsv[..., 0], table_hue)\n        # image_hsv[..., 1] = cv2.LUT(image_hsv[..., 1], table_saturation)\n\n        # code with using cache (at best maybe 10% faster for 64x64):\n        table_hue = cls._LUT_CACHE[0]\n        table_saturation = cls._LUT_CACHE[1]\n        tables = [\n            table_hue[255+int(hue)],\n            table_saturation[255+int(saturation)]\n        ]\n\n        image_hsv[..., [0, 1]] = ia.apply_lut(image_hsv[..., [0, 1]],\n                                              tables)\n\n        return image_hsv\n\n    @classmethod\n    def _transform_image_numpy(cls, image_hsv, hue, saturation):\n        # int16 seems to be slightly faster than int32\n        image_hsv = image_hsv.astype(np.int16)\n        # np.mod() works also as required here for negative values\n        image_hsv[..., 0] = np.mod(image_hsv[..., 0] + hue, 180)\n        image_hsv[..., 1] = np.clip(\n            image_hsv[..., 1] + saturation, 0, 255)\n        return image_hsv\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.value, self.value_hue, self.value_saturation,\n                self.per_channel, self.from_colorspace]\n\n    @classmethod\n    def _handle_value_arg(cls, value, value_hue, value_saturation):\n        if value is not None:\n            assert value_hue is None, (\n                \"`value_hue` may not be set if `value` is set. \"\n                \"It is set to: %s (type: %s).\" % (\n                    str(value_hue), type(value_hue)))\n            assert value_saturation is None, (\n                \"`value_saturation` may not be set if `value` is set. \"\n                \"It is set to: %s (type: %s).\" % (\n                    str(value_saturation), type(value_saturation)))\n            return iap.handle_discrete_param(\n                value, \"value\", value_range=(-255, 255), tuple_to_uniform=True,\n                list_to_choice=True, allow_floats=False)\n\n        return None\n\n    @classmethod\n    def _handle_value_hue_arg(cls, value_hue):\n        if value_hue is not None:\n            # we don't have to verify here that value is None, as the\n            # exclusivity was already ensured in _handle_value_arg()\n            return iap.handle_discrete_param(\n                value_hue, \"value_hue\", value_range=(-255, 255),\n                tuple_to_uniform=True, list_to_choice=True, allow_floats=False)\n\n        return None\n\n    @classmethod\n    def _handle_value_saturation_arg(cls, value_saturation):\n        if value_saturation is not None:\n            # we don't have to verify here that value is None, as the\n            # exclusivity was already ensured in _handle_value_arg()\n            return iap.handle_discrete_param(\n                value_saturation, \"value_saturation\", value_range=(-255, 255),\n                tuple_to_uniform=True, list_to_choice=True, allow_floats=False)\n        return None\n\n    @classmethod\n    def _generate_lut_table(cls):\n        # TODO Changing the dtype here to int8 makes gen test for this method\n        #      fail, but all other tests still succeed. How can this be?\n        #      The dtype was verified to remain int8, having min & max at\n        #      -128 & 127.\n        dtype = np.uint8\n        table = (np.zeros((256*2, 256), dtype=dtype),\n                 np.zeros((256*2, 256), dtype=dtype))\n        value_range = np.arange(0, 256, dtype=np.int16)\n        # this could be done slightly faster by vectorizing the loop\n        for i in sm.xrange(-255, 255+1):\n            table_hue = np.mod(value_range + i, 180)\n            table_saturation = np.clip(value_range + i, 0, 255)\n            table[0][255+i, :] = table_hue\n            table[1][255+i, :] = table_saturation\n        return table\n\n\nclass AddToHue(AddToHueAndSaturation):\n    \"\"\"\n    Add random values to the hue of images.\n\n    The augmenter first transforms images to HSV colorspace, then adds random\n    values to the H channel and afterwards converts back to RGB.\n\n    If you want to change both the hue and the saturation, it is recommended\n    to use ``AddToHueAndSaturation`` as otherwise the image will be\n    converted twice to HSV and back to RGB.\n\n    This augmenter is a shortcut for ``AddToHueAndSaturation(value_hue=...)``.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.AddToHueAndSaturation`.\n\n    Parameters\n    ----------\n    value : None or int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Value to add to the hue of all pixels.\n        This is expected to be in the range ``-255`` to ``+255`` and will\n        automatically be projected to an angular representation using\n        ``(hue/255) * (360/2)`` (OpenCV's hue representation is in the\n        range ``[0, 180]`` instead of ``[0, 360]``).\n\n            * If an integer, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the discrete\n              range ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, then a value will be sampled from that\n              parameter per image.\n\n    from_colorspace : str, optional\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AddToHue((-50, 50))\n\n    Sample random values from the discrete uniform range ``[-50..50]``,\n    convert them to angular representation and add them to the hue, i.e.\n    to the ``H`` channel in ``HSV`` colorspace.\n\n    \"\"\"\n\n    def __init__(self, value=(-255, 255), from_colorspace=CSPACE_RGB,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(AddToHue, self).__init__(\n            value_hue=value,\n            from_colorspace=from_colorspace,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass AddToSaturation(AddToHueAndSaturation):\n    \"\"\"\n    Add random values to the saturation of images.\n\n    The augmenter first transforms images to HSV colorspace, then adds random\n    values to the S channel and afterwards converts back to RGB.\n\n    If you want to change both the hue and the saturation, it is recommended\n    to use ``AddToHueAndSaturation`` as otherwise the image will be\n    converted twice to HSV and back to RGB.\n\n    This augmenter is a shortcut for\n    ``AddToHueAndSaturation(value_saturation=...)``.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.AddToHueAndSaturation`.\n\n    Parameters\n    ----------\n    value : None or int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Value to add to the saturation of all pixels.\n        It is expected to be in the range ``-255`` to ``+255``.\n\n            * If an integer, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the discrete\n              range ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, then a value will be sampled from that\n              parameter per image.\n\n    from_colorspace : str, optional\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AddToSaturation((-50, 50))\n\n    Sample random values from the discrete uniform range ``[-50..50]``,\n    and add them to the saturation, i.e. to the ``S`` channel in ``HSV``\n    colorspace.\n\n    \"\"\"\n\n    def __init__(self, value=(-75, 75), from_colorspace=\"RGB\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(AddToSaturation, self).__init__(\n            value_saturation=value,\n            from_colorspace=from_colorspace,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO tests\n# TODO rename to ChangeColorspace3D and then introduce ChangeColorspace, which\n#      does not enforce 3d images?\nclass ChangeColorspace(meta.Augmenter):\n    \"\"\"\n    Augmenter to change the colorspace of images.\n\n    .. note::\n\n        This augmenter is not tested. Some colorspaces might work, others\n        might not.\n\n    ..note::\n\n        This augmenter tries to project the colorspace value range on\n        0-255. It outputs dtype=uint8 images.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    Parameters\n    ----------\n    to_colorspace : str or list of str or imgaug.parameters.StochasticParameter\n        The target colorspace.\n        Allowed strings are: ``RGB``, ``BGR``, ``GRAY``, ``CIE``, ``YCrCb``,\n        ``HSV``, ``HLS``, ``Lab``, ``Luv``.\n        These are also accessible via\n        ``imgaug.augmenters.color.CSPACE_<NAME>``,\n        e.g. ``imgaug.augmenters.CSPACE_YCrCb``.\n\n            * If a string, it must be among the allowed colorspaces.\n            * If a list, it is expected to be a list of strings, each one\n              being an allowed colorspace. A random element from the list\n              will be chosen per image.\n            * If a StochasticParameter, it is expected to return string. A new\n              sample will be drawn per image.\n\n    from_colorspace : str, optional\n        The source colorspace (of the input images).\n        See `to_colorspace`. Only a single string is allowed.\n\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        The alpha value of the new colorspace when overlayed over the\n        old one. A value close to 1.0 means that mostly the new\n        colorspace is visible. A value close to 0.0 means, that mostly the\n        old image is visible.\n\n            * If an int or float, exactly that value will be used.\n            * If a tuple ``(a, b)``, a random value from the range\n              ``a <= x <= b`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, a value will be sampled from the\n              parameter per image.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    \"\"\"\n\n    # TODO mark these as deprecated\n    RGB = CSPACE_RGB\n    BGR = CSPACE_BGR\n    GRAY = CSPACE_GRAY\n    CIE = CSPACE_CIE\n    YCrCb = CSPACE_YCrCb\n    HSV = CSPACE_HSV\n    HLS = CSPACE_HLS\n    Lab = CSPACE_Lab\n    Luv = CSPACE_Luv\n    COLORSPACES = {RGB, BGR, GRAY, CIE, YCrCb, HSV, HLS, Lab, Luv}\n    # TODO access cv2 COLOR_ variables directly instead of indirectly via\n    #      dictionary mapping\n    CV_VARS = {\n        # RGB\n        \"RGB2BGR\": cv2.COLOR_RGB2BGR,\n        \"RGB2GRAY\": cv2.COLOR_RGB2GRAY,\n        \"RGB2CIE\": cv2.COLOR_RGB2XYZ,\n        \"RGB2YCrCb\": cv2.COLOR_RGB2YCR_CB,\n        \"RGB2HSV\": cv2.COLOR_RGB2HSV,\n        \"RGB2HLS\": cv2.COLOR_RGB2HLS,\n        \"RGB2Lab\": cv2.COLOR_RGB2LAB,\n        \"RGB2Luv\": cv2.COLOR_RGB2LUV,\n        # BGR\n        \"BGR2RGB\": cv2.COLOR_BGR2RGB,\n        \"BGR2GRAY\": cv2.COLOR_BGR2GRAY,\n        \"BGR2CIE\": cv2.COLOR_BGR2XYZ,\n        \"BGR2YCrCb\": cv2.COLOR_BGR2YCR_CB,\n        \"BGR2HSV\": cv2.COLOR_BGR2HSV,\n        \"BGR2HLS\": cv2.COLOR_BGR2HLS,\n        \"BGR2Lab\": cv2.COLOR_BGR2LAB,\n        \"BGR2Luv\": cv2.COLOR_BGR2LUV,\n        # HSV\n        \"HSV2RGB\": cv2.COLOR_HSV2RGB,\n        \"HSV2BGR\": cv2.COLOR_HSV2BGR,\n        # HLS\n        \"HLS2RGB\": cv2.COLOR_HLS2RGB,\n        \"HLS2BGR\": cv2.COLOR_HLS2BGR,\n        # Lab\n        \"Lab2RGB\": (\n            cv2.COLOR_Lab2RGB\n            if hasattr(cv2, \"COLOR_Lab2RGB\") else cv2.COLOR_LAB2RGB),\n        \"Lab2BGR\": (\n            cv2.COLOR_Lab2BGR\n            if hasattr(cv2, \"COLOR_Lab2BGR\") else cv2.COLOR_LAB2BGR)\n    }\n\n    def __init__(self, to_colorspace, from_colorspace=CSPACE_RGB, alpha=1.0,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ChangeColorspace, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        # TODO somehow merge this with Alpha augmenter?\n        self.alpha = iap.handle_continuous_param(\n            alpha, \"alpha\", value_range=(0, 1.0), tuple_to_uniform=True,\n            list_to_choice=True)\n\n        if ia.is_string(to_colorspace):\n            assert to_colorspace in CSPACE_ALL, (\n                \"Expected 'to_colorspace' to be one of %s. Got %s.\" % (\n                    CSPACE_ALL, to_colorspace))\n            self.to_colorspace = iap.Deterministic(to_colorspace)\n        elif ia.is_iterable(to_colorspace):\n            all_strings = all(\n                [ia.is_string(colorspace) for colorspace in to_colorspace])\n            assert all_strings, (\n                \"Expected list of 'to_colorspace' to only contain strings. \"\n                \"Got types %s.\" % (\n                    \", \".join([str(type(v)) for v in to_colorspace])))\n            all_valid = all(\n                [(colorspace in CSPACE_ALL)\n                 for colorspace in to_colorspace])\n            assert all_valid, (\n                \"Expected list of 'to_colorspace' to only contain strings \"\n                \"that are in %s. Got strings %s.\" % (\n                    CSPACE_ALL, to_colorspace))\n            self.to_colorspace = iap.Choice(to_colorspace)\n        elif isinstance(to_colorspace, iap.StochasticParameter):\n            self.to_colorspace = to_colorspace\n        else:\n            raise Exception(\"Expected to_colorspace to be string, list of \"\n                            \"strings or StochasticParameter, got %s.\" % (\n                                type(to_colorspace),))\n        self.to_colorspace = iap._wrap_leafs_of_param_in_prefetchers(\n            self.to_colorspace, iap._NB_PREFETCH_STRINGS\n        )\n\n        assert ia.is_string(from_colorspace), (\n            \"Expected from_colorspace to be a single string, \"\n            \"got type %s.\" % (type(from_colorspace),))\n        assert from_colorspace in CSPACE_ALL, (\n            \"Expected from_colorspace to be one of: %s. Got: %s.\" % (\n                \", \".join(CSPACE_ALL), from_colorspace))\n        assert from_colorspace != CSPACE_GRAY, (\n            \"Cannot convert from grayscale images to other colorspaces.\")\n        self.from_colorspace = from_colorspace\n\n        # epsilon value to check if alpha is close to 1.0 or 0.0\n        self.eps = 0.001\n\n    def _draw_samples(self, n_augmentables, random_state):\n        rss = random_state.duplicate(2)\n        alphas = self.alpha.draw_samples(\n            (n_augmentables,), random_state=rss[0])\n        to_colorspaces = self.to_colorspace.draw_samples(\n            (n_augmentables,), random_state=rss[1])\n        return alphas, to_colorspaces\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        nb_images = len(images)\n        alphas, to_colorspaces = self._draw_samples(nb_images, random_state)\n        for i, image in enumerate(images):\n            alpha = alphas[i]\n            to_colorspace = to_colorspaces[i]\n\n            assert to_colorspace in CSPACE_ALL, (\n                \"Expected 'to_colorspace' to be one of %s. Got %s.\" % (\n                    CSPACE_ALL, to_colorspace))\n\n            if alpha <= self.eps or self.from_colorspace == to_colorspace:\n                pass  # no change necessary\n            else:\n                image_aug = change_colorspace_(image, to_colorspace,\n                                               self.from_colorspace)\n                batch.images[i] = blend.blend_alpha(image_aug, image, alpha,\n                                                    self.eps)\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.to_colorspace, self.alpha]\n\n\n# TODO This should rather do the blending in RGB or BGR space.\n#      Currently, if the input image is in e.g. HSV space, it will blend in\n#      that space.\n# TODO rename to Grayscale3D and add Grayscale that keeps the image at 1D?\nclass Grayscale(ChangeColorspace):\n    \"\"\"Augmenter to convert images to their grayscale versions.\n\n    .. note::\n\n        Number of output channels is still ``3``, i.e. this augmenter just\n        \"removes\" color.\n\n    TODO check dtype support\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    Parameters\n    ----------\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        The alpha value of the grayscale image when overlayed over the\n        old image. A value close to 1.0 means, that mostly the new grayscale\n        image is visible. A value close to 0.0 means, that mostly the\n        old image is visible.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value from the range\n              ``a <= x <= b`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, a value will be sampled from the\n              parameter per image.\n\n    from_colorspace : str, optional\n        The source colorspace (of the input images).\n        See :func:`~imgaug.augmenters.color.change_colorspace_`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Grayscale(alpha=1.0)\n\n    Creates an augmenter that turns images to their grayscale versions.\n\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Grayscale(alpha=(0.0, 1.0))\n\n    Creates an augmenter that turns images to their grayscale versions with\n    an alpha value in the range ``0 <= alpha <= 1``. An alpha value of 0.5 would\n    mean, that the output image is 50 percent of the input image and 50\n    percent of the grayscale image (i.e. 50 percent of color removed).\n\n    \"\"\"\n\n    def __init__(self, alpha=1, from_colorspace=CSPACE_RGB,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Grayscale, self).__init__(\n            to_colorspace=CSPACE_GRAY,\n            alpha=alpha,\n            from_colorspace=from_colorspace,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass ChangeColorTemperature(meta.Augmenter):\n    \"\"\"Change the temperature to a provided Kelvin value.\n\n    Low Kelvin values around ``1000`` to ``4000`` will result in red, yellow\n    or orange images. Kelvin values around ``10000`` to ``40000`` will result\n    in progressively darker blue tones.\n\n    Color temperatures taken from\n    `<http://www.vendian.org/mncharity/dir3/blackbody/UnstableURLs/bbr_color.html>`_\n\n    Basic method to change color temperatures taken from\n    `<https://stackoverflow.com/a/11888449>`_\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.change_color_temperatures_`.\n\n    Parameters\n    ----------\n    kelvin : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Temperature in Kelvin. The temperatures of images will be modified to\n        this value. Must be in the interval ``[1000, 40000]``.\n\n            * If a number, exactly that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value from the\n              interval ``[a, b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n            ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.ChangeColorTemperature((1100, 10000))\n\n    Create an augmenter that changes the color temperature of images to\n    a random value between ``1100`` and ``10000`` Kelvin.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, kelvin=(1000, 11000), from_colorspace=CSPACE_RGB,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ChangeColorTemperature, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.kelvin = iap.handle_continuous_param(\n            kelvin, \"kelvin\", value_range=(1000, 40000), tuple_to_uniform=True,\n            list_to_choice=True)\n        self.from_colorspace = from_colorspace\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is not None:\n            nb_rows = batch.nb_rows\n            kelvins = self.kelvin.draw_samples((nb_rows,),\n                                               random_state=random_state)\n\n            batch.images = change_color_temperatures_(\n                batch.images, kelvins, from_colorspaces=self.from_colorspace)\n\n        return batch\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.kelvin, self.from_colorspace]\n\n\n@six.add_metaclass(ABCMeta)\nclass _AbstractColorQuantization(meta.Augmenter):\n    def __init__(self,\n                 counts=(2, 16),  # number of bits or colors\n                 counts_value_range=(2, None),\n                 from_colorspace=CSPACE_RGB,\n                 to_colorspace=[CSPACE_RGB, CSPACE_Lab],\n                 max_size=128,\n                 interpolation=\"linear\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=dangerous-default-value\n        super(_AbstractColorQuantization, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.counts_value_range = counts_value_range\n        self.counts = iap.handle_discrete_param(\n            counts, \"counts\", value_range=counts_value_range,\n            tuple_to_uniform=True, list_to_choice=True, allow_floats=False)\n        self.from_colorspace = from_colorspace\n        self.to_colorspace = to_colorspace\n        self.max_size = max_size\n        self.interpolation = interpolation\n\n    def _draw_samples(self, n_augmentables, random_state):\n        counts = self.counts.draw_samples((n_augmentables,), random_state)\n        counts = np.round(counts).astype(np.int32)\n\n        # Note that we can get values outside of the value range for counts\n        # here if a StochasticParameter was provided, e.g.\n        # Deterministic(1) is currently not verified.\n        counts = np.clip(counts,\n                         self.counts_value_range[0],\n                         self.counts_value_range[1])\n\n        return counts\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        rss = random_state.duplicate(1 + len(images))\n        counts = self._draw_samples(len(images), rss[-1])\n\n        for i, image in enumerate(images):\n            batch.images[i] = self._augment_single_image(image, counts[i],\n                                                         rss[i])\n        return batch\n\n    def _augment_single_image(self, image, counts, random_state):\n        # pylint: disable=protected-access, invalid-name\n        assert image.shape[-1] in [1, 3, 4], (\n            \"Expected image with 1, 3 or 4 channels, \"\n            \"got %d (shape: %s).\" % (image.shape[-1], image.shape))\n\n        orig_shape = image.shape\n        image = self._ensure_max_size(\n            image, self.max_size, self.interpolation)\n\n        if image.shape[-1] == 1:\n            # 2D image\n            image_aug = self._quantize(image, counts)\n        else:\n            # 3D image with 3 or 4 channels\n            alpha_channel = None\n            if image.shape[-1] == 4:\n                alpha_channel = image[:, :, 3:4]\n                image = image[:, :, 0:3]\n\n            if self.to_colorspace is None:\n                cs = meta.Identity()\n                cs_inv = meta.Identity()\n            else:\n                # TODO quite hacky to recover the sampled to_colorspace here\n                #      by accessing _draw_samples(). Would be better to have\n                #      an inverse augmentation method in ChangeColorspace.\n\n                # We use random_state.copy() in this method, but that is not\n                # expected to cause unchanged an random_state, because\n                # _augment_batch_() uses an un-copied one for _draw_samples()\n                cs = ChangeColorspace(\n                    from_colorspace=self.from_colorspace,\n                    to_colorspace=self.to_colorspace,\n                    random_state=random_state.copy(),)\n                _, to_colorspaces = cs._draw_samples(\n                    1, random_state.copy())\n                cs_inv = ChangeColorspace(\n                    from_colorspace=to_colorspaces[0],\n                    to_colorspace=self.from_colorspace,\n                    random_state=random_state.copy())\n\n            image_tf = cs.augment_image(image)\n            image_tf_aug = self._quantize(image_tf, counts)\n            image_aug = cs_inv.augment_image(image_tf_aug)\n\n            if alpha_channel is not None:\n                image_aug = np.concatenate([image_aug, alpha_channel], axis=2)\n\n        if orig_shape != image_aug.shape:\n            image_aug = ia.imresize_single_image(\n                image_aug,\n                orig_shape[0:2],\n                interpolation=self.interpolation)\n\n        return image_aug\n\n    @abstractmethod\n    def _quantize(self, image, counts):\n        \"\"\"Apply the augmenter-specific quantization function to an image.\"\"\"\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.counts,\n                self.from_colorspace,\n                self.to_colorspace,\n                self.max_size,\n                self.interpolation]\n\n    # TODO this is the same function as in Superpixels._ensure_max_size\n    #      make DRY\n    @classmethod\n    def _ensure_max_size(cls, image, max_size, interpolation):\n        if max_size is not None:\n            size = max(image.shape[0], image.shape[1])\n            if size > max_size:\n                resize_factor = max_size / size\n                new_height = int(image.shape[0] * resize_factor)\n                new_width = int(image.shape[1] * resize_factor)\n                image = ia.imresize_single_image(\n                    image,\n                    (new_height, new_width),\n                    interpolation=interpolation)\n        return image\n\n\nclass KMeansColorQuantization(_AbstractColorQuantization):\n    \"\"\"\n    Quantize colors using k-Means clustering.\n\n    This \"collects\" the colors from the input image, groups them into\n    ``k`` clusters using k-Means clustering and replaces the colors in the\n    input image using the cluster centroids.\n\n    This is slower than ``UniformColorQuantization``, but adapts dynamically\n    to the color range in the input image.\n\n    .. note::\n\n        This augmenter expects input images to be either grayscale\n        or to have 3 or 4 channels and use colorspace `from_colorspace`. If\n        images have 4 channels, it is assumed that the 4th channel is an alpha\n        channel and it will not be quantized.\n\n    **Supported dtypes**:\n\n    if (image size <= max_size):\n\n        minimum of (\n            :class:`~imgaug.augmenters.color.ChangeColorspace`,\n            :func:`~imgaug.augmenters.color.quantize_kmeans`\n        )\n\n    if (image size > max_size):\n\n        minimum of (\n            :class:`~imgaug.augmenters.color.ChangeColorspace`,\n            :func:`~imgaug.augmenters.color.quantize_kmeans`,\n            :func:`~imgaug.imgaug.imresize_single_image`\n        )\n\n    Parameters\n    ----------\n    n_colors : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Target number of colors in the generated output image.\n        This corresponds to the number of clusters in k-Means, i.e. ``k``.\n        Sampled values below ``2`` will always be clipped to ``2``.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, then a value from the discrete\n              interval ``[a..b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter.\n\n    to_colorspace : None or str or list of str or imgaug.parameters.StochasticParameter\n        The colorspace in which to perform the quantization.\n        See :func:`~imgaug.augmenters.color.change_colorspace_` for valid values.\n        This will be ignored for grayscale input images.\n\n            * If ``None`` the colorspace of input images will not be changed.\n            * If a string, it must be among the allowed colorspaces.\n            * If a list, it is expected to be a list of strings, each one\n              being an allowed colorspace. A random element from the list\n              will be chosen per image.\n            * If a StochasticParameter, it is expected to return string. A new\n              sample will be drawn per image.\n\n    from_colorspace : str, optional\n        The colorspace of the input images.\n        See `to_colorspace`. Only a single string is allowed.\n\n    max_size : int or None, optional\n        Maximum image size at which to perform the augmentation.\n        If the width or height of an image exceeds this value, it will be\n        downscaled before running the augmentation so that the longest side\n        matches `max_size`.\n        This is done to speed up the augmentation. The final output image has\n        the same size as the input image. Use ``None`` to apply no downscaling.\n\n    interpolation : int or str, optional\n        Interpolation method to use during downscaling when `max_size` is\n        exceeded. Valid methods are the same as in\n        :func:`~imgaug.imgaug.imresize_single_image`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.KMeansColorQuantization()\n\n    Create an augmenter to apply k-Means color quantization to images using a\n    random amount of colors, sampled uniformly from the interval ``[2..16]``.\n    It assumes the input image colorspace to be ``RGB`` and clusters colors\n    randomly in ``RGB`` or ``Lab`` colorspace.\n\n    >>> aug = iaa.KMeansColorQuantization(n_colors=8)\n\n    Create an augmenter that quantizes images to (up to) eight colors.\n\n    >>> aug = iaa.KMeansColorQuantization(n_colors=(4, 16))\n\n    Create an augmenter that quantizes images to (up to) ``n`` colors,\n    where ``n`` is randomly and uniformly sampled from the discrete interval\n    ``[4..16]``.\n\n    >>> aug = iaa.KMeansColorQuantization(\n    >>>     from_colorspace=iaa.CSPACE_BGR)\n\n    Create an augmenter that quantizes input images that are in\n    ``BGR`` colorspace. The quantization happens in ``RGB`` or ``Lab``\n    colorspace, into which the images are temporarily converted.\n\n    >>> aug = iaa.KMeansColorQuantization(\n    >>>     to_colorspace=[iaa.CSPACE_RGB, iaa.CSPACE_HSV])\n\n    Create an augmenter that quantizes images by clustering colors randomly\n    in either ``RGB`` or ``HSV`` colorspace. The assumed input colorspace\n    of images is ``RGB``.\n\n    \"\"\"\n\n    def __init__(self, n_colors=(2, 16), from_colorspace=CSPACE_RGB,\n                 to_colorspace=[CSPACE_RGB, CSPACE_Lab],\n                 max_size=128, interpolation=\"linear\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=dangerous-default-value\n        super(KMeansColorQuantization, self).__init__(\n            counts=n_colors,\n            from_colorspace=from_colorspace,\n            to_colorspace=to_colorspace,\n            max_size=max_size,\n            interpolation=interpolation,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    @property\n    def n_colors(self):\n        \"\"\"Alias for property ``counts``.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        return self.counts\n\n    def _quantize(self, image, counts):\n        return quantize_kmeans(image, counts)\n\n\n@ia.deprecated(\"imgaug.augmenters.colors.quantize_kmeans\")\ndef quantize_colors_kmeans(image, n_colors, n_max_iter=10, eps=1.0):\n    \"\"\"Outdated name of :func:`quantize_kmeans`.\n\n    Deprecated since 0.4.0.\n\n    \"\"\"\n    return quantize_kmeans(arr=image, nb_clusters=n_colors,\n                           nb_max_iter=n_max_iter, eps=eps)\n\n\ndef quantize_kmeans(arr, nb_clusters, nb_max_iter=10, eps=1.0):\n    \"\"\"Quantize an array into N bins using k-means clustering.\n\n    If the input is an image, this method returns in an image with a maximum\n    of ``N`` colors. Similar colors are grouped to their mean. The k-means\n    clustering happens across channels and not channelwise.\n\n    Code similar to https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_ml/\n    py_kmeans/py_kmeans_opencv/py_kmeans_opencv.html\n\n    .. warning::\n\n        This function currently changes the RNG state of both OpenCV's\n        internal RNG and imgaug's global RNG. This is necessary in order\n        to ensure that the k-means clustering happens deterministically.\n\n    Added in 0.4.0. (Previously called ``quantize_colors_kmeans()``.)\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    arr : ndarray\n        Array to quantize. Expected to be of shape ``(H,W)`` or ``(H,W,C)``\n        with ``C`` usually being ``1`` or ``3``.\n\n    nb_clusters : int\n        Number of clusters to quantize into, i.e. ``k`` in k-means clustering.\n        This corresponds to the maximum number of colors in an output image.\n\n    nb_max_iter : int, optional\n        Maximum number of iterations that the k-means clustering algorithm\n        is run.\n\n    eps : float, optional\n        Minimum change of all clusters per k-means iteration. If all clusters\n        change by less than this amount in an iteration, the clustering is\n        stopped.\n\n    Returns\n    -------\n    ndarray\n        Image with quantized colors.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> import numpy as np\n    >>> image = np.arange(4 * 4 * 3, dtype=np.uint8).reshape((4, 4, 3))\n    >>> image_quantized = iaa.quantize_kmeans(image, 6)\n\n    Generates a ``4x4`` image with ``3`` channels, containing consecutive\n    values from ``0`` to ``4*4*3``, leading to an equal number of colors.\n    These colors are then quantized so that only ``6`` are remaining. Note\n    that the six remaining colors do have to appear in the input image.\n\n    \"\"\"\n    iadt.allow_only_uint8({arr.dtype})\n    assert arr.ndim in [2, 3], (\n        \"Expected two- or three-dimensional array shape, \"\n        \"got shape %s.\" % (arr.shape,))\n    assert 2 <= nb_clusters <= 256, (\n        \"Expected nb_clusters to be in the discrete interval [2..256]. \"\n        \"Got a value of %d instead.\" % (nb_clusters,))\n\n    # without this check, kmeans throws an exception\n    n_pixels = np.prod(arr.shape[0:2])\n    if nb_clusters >= n_pixels:\n        return np.copy(arr)\n\n    nb_channels = 1 if arr.ndim == 2 else arr.shape[-1]\n    pixel_vectors = arr.reshape((-1, nb_channels)).astype(np.float32)\n\n    criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS,\n                nb_max_iter, eps)\n    attempts = 1\n\n    # We want our quantization function to be deterministic (so that the\n    # augmenter using it can also be executed deterministically). Hence we\n    # set the RGN seed here.\n    # This is fairly ugly, but in cv2 there seems to be no other way to\n    # achieve determinism. Using cv2.KMEANS_PP_CENTERS does not help, as it\n    # is non-deterministic (tested). In C++ the function has an rgn argument,\n    # but not in python. In python there also seems to be no way to read out\n    # cv2's RNG state, so we can't set it back after executing this function.\n    # TODO this is quite hacky\n    cv2.setRNGSeed(1)\n    _compactness, labels, centers = cv2.kmeans(\n        pixel_vectors, nb_clusters, None, criteria, attempts,\n        cv2.KMEANS_RANDOM_CENTERS)\n    # TODO replace by sample_seed function\n    # cv2 seems to be able to handle SEED_MAX_VALUE (tested) but not floats\n    cv2.setRNGSeed(iarandom.get_global_rng().generate_seed_())\n\n    # Convert back to uint8 (or whatever the image dtype was) and to input\n    # image shape\n    centers_uint8 = np.array(centers, dtype=arr.dtype)\n    quantized_flat = centers_uint8[labels.flatten()]\n    return quantized_flat.reshape(arr.shape)\n\n\nclass UniformColorQuantization(_AbstractColorQuantization):\n    \"\"\"Quantize colors into N bins with regular distance.\n\n    For ``uint8`` images the equation is ``floor(v/q)*q + q/2`` with\n    ``q = 256/N``, where ``v`` is a pixel intensity value and ``N`` is\n    the target number of colors after quantization.\n\n    This augmenter is faster than ``KMeansColorQuantization``, but the\n    set of possible output colors is constant (i.e. independent of the\n    input images). It may produce unsatisfying outputs for input images\n    that are made up of very similar colors.\n\n    .. note::\n\n        This augmenter expects input images to be either grayscale\n        or to have 3 or 4 channels and use colorspace `from_colorspace`. If\n        images have 4 channels, it is assumed that the 4th channel is an alpha\n        channel and it will not be quantized.\n\n    **Supported dtypes**:\n\n    if (image size <= max_size):\n\n        minimum of (\n            :class:`~imgaug.augmenters.color.ChangeColorspace`,\n            :func:`~imgaug.augmenters.color.quantize_uniform_`\n        )\n\n    if (image size > max_size):\n\n        minimum of (\n            :class:`~imgaug.augmenters.color.ChangeColorspace`,\n            :func:`~imgaug.augmenters.color.quantize_uniform_`,\n            :func:`~imgaug.imgaug.imresize_single_image`\n        )\n\n    Parameters\n    ----------\n    n_colors : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Target number of colors to use in the generated output image.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, then a value from the discrete\n              interval ``[a..b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter.\n\n    to_colorspace : None or str or list of str or imgaug.parameters.StochasticParameter\n        The colorspace in which to perform the quantization.\n        See :func:`~imgaug.augmenters.color.change_colorspace_` for valid values.\n        This will be ignored for grayscale input images.\n\n            * If ``None`` the colorspace of input images will not be changed.\n            * If a string, it must be among the allowed colorspaces.\n            * If a list, it is expected to be a list of strings, each one\n              being an allowed colorspace. A random element from the list\n              will be chosen per image.\n            * If a StochasticParameter, it is expected to return string. A new\n              sample will be drawn per image.\n\n    from_colorspace : str, optional\n        The colorspace of the input images.\n        See `to_colorspace`. Only a single string is allowed.\n\n    max_size : None or int, optional\n        Maximum image size at which to perform the augmentation.\n        If the width or height of an image exceeds this value, it will be\n        downscaled before running the augmentation so that the longest side\n        matches `max_size`.\n        This is done to speed up the augmentation. The final output image has\n        the same size as the input image. Use ``None`` to apply no downscaling.\n\n    interpolation : int or str, optional\n        Interpolation method to use during downscaling when `max_size` is\n        exceeded. Valid methods are the same as in\n        :func:`~imgaug.imgaug.imresize_single_image`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.UniformColorQuantization()\n\n    Create an augmenter to apply uniform color quantization to images using a\n    random amount of colors, sampled uniformly from the discrete interval\n    ``[2..16]``.\n\n    >>> aug = iaa.UniformColorQuantization(n_colors=8)\n\n    Create an augmenter that quantizes images to (up to) eight colors.\n\n    >>> aug = iaa.UniformColorQuantization(n_colors=(4, 16))\n\n    Create an augmenter that quantizes images to (up to) ``n`` colors,\n    where ``n`` is randomly and uniformly sampled from the discrete interval\n    ``[4..16]``.\n\n    >>> aug = iaa.UniformColorQuantization(\n    >>>     from_colorspace=iaa.CSPACE_BGR,\n    >>>     to_colorspace=[iaa.CSPACE_RGB, iaa.CSPACE_HSV])\n\n    Create an augmenter that uniformly quantizes images in either ``RGB``\n    or ``HSV`` colorspace (randomly picked per image). The input colorspace\n    of all images has to be ``BGR``.\n\n    \"\"\"\n\n    def __init__(self,\n                 n_colors=(2, 16),\n                 from_colorspace=CSPACE_RGB,\n                 to_colorspace=None,\n                 max_size=None,\n                 interpolation=\"linear\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=dangerous-default-value\n        super(UniformColorQuantization, self).__init__(\n            counts=n_colors,\n            from_colorspace=from_colorspace,\n            to_colorspace=to_colorspace,\n            max_size=max_size,\n            interpolation=interpolation,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    @property\n    def n_colors(self):\n        \"\"\"Alias for property ``counts``.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        return self.counts\n\n    def _quantize(self, image, counts):\n        return quantize_uniform_(image, counts)\n\n\nclass UniformColorQuantizationToNBits(_AbstractColorQuantization):\n    \"\"\"Quantize images by setting ``8-B`` bits of each component to zero.\n\n    This augmenter sets the ``8-B`` highest frequency (rightmost) bits of\n    each array component to zero. For ``B`` bits this is equivalent to\n    changing each component's intensity value ``v`` to\n    ``v' = v & (2**(8-B) - 1)``, e.g. for ``B=3`` this results in\n    ``v' = c & ~(2**(3-1) - 1) = c & ~3 = c & ~0000 0011 = c & 1111 1100``.\n\n    This augmenter behaves for ``B`` similarly to\n    ``UniformColorQuantization(2**B)``, but quantizes each bin with interval\n    ``(a, b)`` to ``a`` instead of to ``a + (b-a)/2``.\n\n    This augmenter is comparable to :func:`PIL.ImageOps.posterize`.\n\n    .. note::\n\n        This augmenter expects input images to be either grayscale\n        or to have 3 or 4 channels and use colorspace `from_colorspace`. If\n        images have 4 channels, it is assumed that the 4th channel is an alpha\n        channel and it will not be quantized.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    if (image size <= max_size):\n\n        minimum of (\n            :class:`~imgaug.augmenters.color.ChangeColorspace`,\n            :func:`~imgaug.augmenters.color.quantize_uniform`\n        )\n\n    if (image size > max_size):\n\n        minimum of (\n            :class:`~imgaug.augmenters.color.ChangeColorspace`,\n            :func:`~imgaug.augmenters.color.quantize_uniform`,\n            :func:`~imgaug.imgaug.imresize_single_image`\n        )\n\n    Parameters\n    ----------\n    nb_bits : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of bits to keep in each image's array component.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, then a value from the discrete\n              interval ``[a..b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter.\n\n    to_colorspace : None or str or list of str or imgaug.parameters.StochasticParameter\n        The colorspace in which to perform the quantization.\n        See :func:`~imgaug.augmenters.color.change_colorspace_` for valid values.\n        This will be ignored for grayscale input images.\n\n            * If ``None`` the colorspace of input images will not be changed.\n            * If a string, it must be among the allowed colorspaces.\n            * If a list, it is expected to be a list of strings, each one\n              being an allowed colorspace. A random element from the list\n              will be chosen per image.\n            * If a StochasticParameter, it is expected to return string. A new\n              sample will be drawn per image.\n\n    from_colorspace : str, optional\n        The colorspace of the input images.\n        See `to_colorspace`. Only a single string is allowed.\n\n    max_size : None or int, optional\n        Maximum image size at which to perform the augmentation.\n        If the width or height of an image exceeds this value, it will be\n        downscaled before running the augmentation so that the longest side\n        matches `max_size`.\n        This is done to speed up the augmentation. The final output image has\n        the same size as the input image. Use ``None`` to apply no downscaling.\n\n    interpolation : int or str, optional\n        Interpolation method to use during downscaling when `max_size` is\n        exceeded. Valid methods are the same as in\n        :func:`~imgaug.imgaug.imresize_single_image`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.UniformColorQuantizationToNBits()\n\n    Create an augmenter to apply uniform color quantization to images using a\n    random amount of bits to remove, sampled uniformly from the discrete\n    interval ``[1..8]``.\n\n    >>> aug = iaa.UniformColorQuantizationToNBits(nb_bits=(2, 8))\n\n    Create an augmenter that quantizes images by removing ``8-B`` rightmost\n    bits from each component, where ``B`` is uniformly sampled from the\n    discrete interval ``[2..8]``.\n\n    >>> aug = iaa.UniformColorQuantizationToNBits(\n    >>>     from_colorspace=iaa.CSPACE_BGR,\n    >>>     to_colorspace=[iaa.CSPACE_RGB, iaa.CSPACE_HSV])\n\n    Create an augmenter that uniformly quantizes images in either ``RGB``\n    or ``HSV`` colorspace (randomly picked per image). The input colorspace\n    of all images has to be ``BGR``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 nb_bits=(1, 8),\n                 from_colorspace=CSPACE_RGB,\n                 to_colorspace=None,\n                 max_size=None,\n                 interpolation=\"linear\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=dangerous-default-value\n\n        # wrt value range: for discrete params, (1, 8) results in\n        # DiscreteUniform with interval [1, 8]\n        super(UniformColorQuantizationToNBits, self).__init__(\n            counts=nb_bits,\n            counts_value_range=(1, 8),\n            from_colorspace=from_colorspace,\n            to_colorspace=to_colorspace,\n            max_size=max_size,\n            interpolation=interpolation,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    # Added in 0.4.0.\n    def _quantize(self, image, counts):\n        return quantize_uniform_to_n_bits_(image, counts)\n\n\nclass Posterize(UniformColorQuantizationToNBits):\n    \"\"\"Alias for :class:`UniformColorQuantizationToNBits`.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.UniformColorQuantizationToNBits`.\n\n    \"\"\"\n\n\n@ia.deprecated(\"imgaug.augmenters.colors.quantize_uniform\")\ndef quantize_colors_uniform(image, n_colors):\n    \"\"\"Outdated name for :func:`quantize_uniform`.\n\n    Deprecated since 0.4.0.\n\n    \"\"\"\n    return quantize_uniform(arr=image, nb_bins=n_colors)\n\n\ndef quantize_uniform(arr, nb_bins, to_bin_centers=True):\n    \"\"\"Quantize an array into N equally-sized bins.\n\n    See :func:`quantize_uniform_` for details.\n\n    Added in 0.4.0. (Previously called ``quantize_colors_uniform()``.)\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.quantize_uniform_`.\n\n    Parameters\n    ----------\n    arr : ndarray\n        See :func:`quantize_uniform_`.\n\n    nb_bins : int\n        See :func:`quantize_uniform_`.\n\n    to_bin_centers : bool\n        See :func:`quantize_uniform_`.\n\n    Returns\n    -------\n    ndarray\n        Array with quantized components.\n\n    \"\"\"\n    return quantize_uniform_(np.copy(arr),\n                             nb_bins=nb_bins,\n                             to_bin_centers=to_bin_centers)\n\n\ndef quantize_uniform_(arr, nb_bins, to_bin_centers=True):\n    \"\"\"Quantize an array into N equally-sized bins in-place.\n\n    This can be used to quantize/posterize an image into N colors.\n\n    For ``uint8`` arrays the equation is ``floor(v/q)*q + q/2`` with\n    ``q = 256/N``, where ``v`` is a pixel intensity value and ``N`` is\n    the target number of bins (roughly matches number of colors) after\n    quantization.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    arr : ndarray\n        Array to quantize, usually an image. Expected to be of shape ``(H,W)``\n        or ``(H,W,C)`` with ``C`` usually being ``1`` or ``3``.\n        This array *may* be changed in-place.\n\n    nb_bins : int\n        Number of equally-sized bins to quantize into. This corresponds to\n        the maximum number of colors in an output image.\n\n    to_bin_centers : bool\n        Whether to quantize each bin ``(a, b)`` to ``a + (b-a)/2`` (center\n        of bin, ``True``) or to ``a`` (lower boundary, ``False``).\n\n    Returns\n    -------\n    ndarray\n        Array with quantized components. This *may* be the input array with\n        components changed in-place.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> import numpy as np\n    >>> image = np.arange(4 * 4 * 3, dtype=np.uint8).reshape((4, 4, 3))\n    >>> image_quantized = iaa.quantize_uniform_(np.copy(image), 6)\n\n    Generates a ``4x4`` image with ``3`` channels, containing consecutive\n    values from ``0`` to ``4*4*3``, leading to an equal number of colors.\n    Each component is then quantized into one of ``6`` bins that regularly\n    split up the value range of ``[0..255]``, i.e. the resolution w.r.t. to\n    the value range is reduced.\n\n    \"\"\"\n    if nb_bins == 256 or 0 in arr.shape:\n        return arr\n\n    # TODO remove dtype check here? apply_lut_() does that already\n    iadt.allow_only_uint8({arr.dtype})\n    assert 2 <= nb_bins <= 256, (\n        \"Expected nb_bins to be in the discrete interval [2..256]. \"\n        \"Got a value of %d instead.\" % (nb_bins,))\n\n    table_class = (_QuantizeUniformCenterizedLUTTableSingleton\n                   if to_bin_centers\n                   else _QuantizeUniformNotCenterizedLUTTableSingleton)\n    table = (table_class\n             .get_instance()\n             .get_for_nb_bins(nb_bins))\n    arr = ia.apply_lut_(arr, table)\n    return arr\n\n\n# Added in 0.4.0.\nclass _QuantizeUniformCenterizedLUTTableSingleton(object):\n    _INSTANCE = None\n\n    @classmethod\n    def get_instance(cls):\n        \"\"\"Get singleton instance of :class:`_QuantizeUniformLUTTable`.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        _QuantizeUniformLUTTable\n            The global instance of :class:`_QuantizeUniformLUTTable`.\n\n        \"\"\"\n        if cls._INSTANCE is None:\n            cls._INSTANCE = _QuantizeUniformLUTTable(centerize=True)\n        return cls._INSTANCE\n\n\n# Added in 0.4.0.\nclass _QuantizeUniformNotCenterizedLUTTableSingleton(object):\n    \"\"\"Table for :func:`quantize_uniform` with ``to_bin_centers=False``.\"\"\"\n    _INSTANCE = None\n\n    @classmethod\n    def get_instance(cls):\n        \"\"\"Get singleton instance of :class:`_QuantizeUniformLUTTable`.\n\n        Added in 0.4.0.\n\n        Returns\n        -------\n        _QuantizeUniformLUTTable\n            The global instance of :class:`_QuantizeUniformLUTTable`.\n\n        \"\"\"\n        if cls._INSTANCE is None:\n            cls._INSTANCE = _QuantizeUniformLUTTable(centerize=False)\n        return cls._INSTANCE\n\n\n# Added in 0.4.0.\nclass _QuantizeUniformLUTTable(object):\n    def __init__(self, centerize):\n        self.table = self._generate_quantize_uniform_table(centerize)\n\n    def get_for_nb_bins(self, nb_bins):\n        \"\"\"Get LUT ndarray for a provided number of bins.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        return self.table[nb_bins, :]\n\n    # Added in 0.4.0.\n    @classmethod\n    def _generate_quantize_uniform_table(cls, centerize):\n        # For simplicity, we generate here the tables for nb_bins=0 (results\n        # in all zeros) and nb_bins=256 too, even though these should usually\n        # not be requested.\n        table = np.arange(0, 256).astype(np.float32)\n        table_all_nb_bins = np.zeros((256, 256), dtype=np.float32)\n\n        # This loop could be done a little bit faster by vectorizing it.\n        # It is expected to be run exactly once per run of a whole script,\n        # making the difference negligible.\n        for nb_bins in np.arange(1, 255).astype(np.uint8):\n            binsize = 256 / nb_bins\n            table_q_f32 = np.floor(table / binsize) * binsize\n            if centerize:\n                table_q_f32 = table_q_f32 + binsize/2\n            table_all_nb_bins[nb_bins] = table_q_f32\n        table_all_nb_bins = np.clip(\n            np.round(table_all_nb_bins), 0, 255).astype(np.uint8)\n        return table_all_nb_bins\n\n\ndef quantize_uniform_to_n_bits(arr, nb_bits):\n    \"\"\"Reduce each component in an array to a maximum number of bits.\n\n    See :func:`quantize_uniform_to_n_bits` for details.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.quantize_uniform_to_n_bits_`.\n\n    Parameters\n    ----------\n    arr : ndarray\n        See :func:`quantize_uniform_to_n_bits`.\n\n    nb_bits : int\n        See :func:`quantize_uniform_to_n_bits`.\n\n    Returns\n    -------\n    ndarray\n        Array with quantized components.\n\n    \"\"\"\n    return quantize_uniform_to_n_bits_(np.copy(arr), nb_bits=nb_bits)\n\n\ndef quantize_uniform_to_n_bits_(arr, nb_bits):\n    \"\"\"Reduce each component in an array to a maximum number of bits in-place.\n\n    This operation sets the ``8-B`` highest frequency (rightmost) bits to zero.\n    For ``B`` bits this is equivalent to changing each component's intensity\n    value ``v`` to ``v' = v & (2**(8-B) - 1)``, e.g. for ``B=3`` this results\n    in ``v' = c & ~(2**(3-1) - 1) = c & ~3 = c & ~0000 0011 = c & 1111 1100``.\n\n    This is identical to :func:`quantize_uniform` with ``nb_bins=2**nb_bits``\n    and ``to_bin_centers=False``.\n\n    This function produces the same outputs as :func:`PIL.ImageOps.posterize`,\n    but is significantly faster.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.quantize_uniform_`.\n\n    Parameters\n    ----------\n    arr : ndarray\n        Array to quantize, usually an image. Expected to be of shape ``(H,W)``\n        or ``(H,W,C)`` with ``C`` usually being ``1`` or ``3``.\n        This array *may* be changed in-place.\n\n    nb_bits : int\n        Number of bits to keep in each array component.\n\n    Returns\n    -------\n    ndarray\n        Array with quantized components. This *may* be the input array with\n        components changed in-place.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> import numpy as np\n    >>> image = np.arange(4 * 4 * 3, dtype=np.uint8).reshape((4, 4, 3))\n    >>> image_quantized = iaa.quantize_uniform_to_n_bits_(np.copy(image), 6)\n\n    Generates a ``4x4`` image with ``3`` channels, containing consecutive\n    values from ``0`` to ``4*4*3``, leading to an equal number of colors.\n    These colors are then quantized so that each component's ``8-6=2``\n    rightmost bits are set to zero.\n\n    \"\"\"\n    assert 1 <= nb_bits <= 8, (\n        \"Expected nb_bits to be in the discrete interval [1..8]. \"\n        \"Got a value of %d instead.\" % (nb_bits,))\n    return quantize_uniform_(arr, nb_bins=2**nb_bits, to_bin_centers=False)\n\n\ndef posterize(arr, nb_bits):\n    \"\"\"Alias for :func:`quantize_uniform_to_n_bits`.\n\n    This function is an alias for :func:`quantize_uniform_to_n_bits` and was\n    added for users familiar with the same function in PIL.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.quantize_uniform_to_n_bits`.\n\n    Parameters\n    ----------\n    arr : ndarray\n        See :func:`quantize_uniform_to_n_bits`.\n\n    nb_bits : int\n        See :func:`quantize_uniform_to_n_bits`.\n\n    Returns\n    -------\n    ndarray\n        Array with quantized components.\n\n    \"\"\"\n    return quantize_uniform_to_n_bits(arr, nb_bits)\n"
  },
  {
    "path": "imgaug/augmenters/contrast.py",
    "content": "\"\"\"\nAugmenters that perform contrast changes.\n\nList of augmenters:\n\n    * :class:`GammaContrast`\n    * :class:`SigmoidContrast`\n    * :class:`LogContrast`\n    * :class:`LinearContrast`\n    * :class:`AllChannelsHistogramEqualization`\n    * :class:`HistogramEqualization`\n    * :class:`AllChannelsCLAHE`\n    * :class:`CLAHE`\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport numpy as np\nimport six.moves as sm\nimport skimage.exposure as ski_exposure\nimport cv2\n\nimport imgaug as ia\nfrom imgaug.imgaug import _normalize_cv2_input_arr_\nfrom . import meta\nfrom . import color as color_lib\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\nfrom ..augmentables.batches import _BatchInAugmentation\n\n\nclass _ContrastFuncWrapper(meta.Augmenter):\n    def __init__(self, func, params1d, per_channel, dtypes_allowed=None,\n                 dtypes_disallowed=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(_ContrastFuncWrapper, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.func = func\n        self.params1d = params1d\n        self.per_channel = iap.handle_probability_param(per_channel,\n                                                        \"per_channel\")\n        self.dtypes_allowed = dtypes_allowed\n        self.dtypes_disallowed = dtypes_disallowed\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        if self.dtypes_allowed is not None:\n            iadt.gate_dtypes_strs(\n                images,\n                allowed=self.dtypes_allowed,\n                disallowed=self.dtypes_disallowed,\n                augmenter=self\n            )\n\n        nb_images = len(images)\n        rss = random_state.duplicate(1+nb_images)\n        per_channel = self.per_channel.draw_samples((nb_images,),\n                                                    random_state=rss[0])\n\n        gen = enumerate(zip(images, per_channel, rss[1:]))\n        for i, (image, per_channel_i, rs) in gen:\n            nb_channels = 1 if per_channel_i <= 0.5 else image.shape[2]\n            # TODO improve efficiency by sampling once\n            samples_i = [\n                param.draw_samples((nb_channels,), random_state=rs)\n                for param in self.params1d]\n            if per_channel_i > 0.5:\n                input_dtype = image.dtype\n                # TODO This was previously a cast of image to float64. Do the\n                #      adjust_* functions return float64?\n                result = []\n                for c in sm.xrange(nb_channels):\n                    samples_i_c = [sample_i[c] for sample_i in samples_i]\n                    args = tuple([image[..., c]] + samples_i_c)\n                    result.append(self.func(*args))\n                image_aug = np.stack(result, axis=-1)\n                image_aug = image_aug.astype(input_dtype)\n            else:\n                # don't use something like samples_i[...][0] here, because\n                # that returns python scalars and is slightly less accurate\n                # than keeping the numpy values\n                args = tuple([image] + samples_i)\n                image_aug = self.func(*args)\n            batch.images[i] = image_aug\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return self.params1d\n\n\n# TODO quite similar to the other adjust_contrast_*() functions, make DRY\ndef adjust_contrast_gamma(arr, gamma):\n    \"\"\"\n    Adjust image contrast by scaling pixel values to ``255*((v/255)**gamma)``.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested (1) (2) (3)\n        * ``uint16``: yes; tested (2) (3)\n        * ``uint32``: yes; tested (2) (3)\n        * ``uint64``: yes; tested (2) (3) (4)\n        * ``int8``: limited; tested (2) (3) (5)\n        * ``int16``: limited; tested (2) (3) (5)\n        * ``int32``: limited; tested (2) (3) (5)\n        * ``int64``: limited; tested (2) (3) (4) (5)\n        * ``float16``: limited; tested (5)\n        * ``float32``: limited; tested (5)\n        * ``float64``: limited; tested (5)\n        * ``float128``: no (6)\n        * ``bool``: no (7)\n\n        - (1) Handled by ``cv2``. Other dtypes are handled by ``skimage``.\n        - (2) Normalization is done as ``I_ij/max``, where ``max`` is the\n              maximum value of the dtype, e.g. 255 for ``uint8``. The\n              normalization is reversed afterwards, e.g. ``result*255`` for\n              ``uint8``.\n        - (3) Integer-like values are not rounded after applying the contrast\n              adjustment equation (before inverting the normalization to\n              ``[0.0, 1.0]`` space), i.e. projection from continuous\n              space to discrete happens according to floor function.\n        - (4) Note that scikit-image doc says that integers are converted to\n              ``float64`` values before applying the contrast normalization\n              method. This might lead to inaccuracies for large 64bit integer\n              values. Tests showed no indication of that happening though.\n        - (5) Must not contain negative values. Values >=0 are fully supported.\n        - (6) Leads to error in scikit-image.\n        - (7) Does not make sense for contrast adjustments.\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        Array for which to adjust the contrast. Dtype ``uint8`` is fastest.\n\n    gamma : number\n        Exponent for the contrast adjustment. Higher values darken the image.\n\n    Returns\n    -------\n    numpy.ndarray\n        Array with adjusted contrast.\n\n    \"\"\"\n    if arr.size == 0:\n        return np.copy(arr)\n\n    # int8 is also possible according to docs\n    # https://docs.opencv.org/3.0-beta/modules/core/doc/operations_on_arrays.html#cv2.LUT ,\n    # but here it seemed like `d` was 0 for CV_8S, causing that to fail\n    if arr.dtype == iadt._UINT8_DTYPE:\n        min_value, _center_value, max_value = \\\n            iadt.get_value_range_of_dtype(arr.dtype)\n        dynamic_range = max_value - min_value\n\n        value_range = np.linspace(0, 1.0, num=dynamic_range+1,\n                                  dtype=np.float32)\n\n        # 255 * ((I_ij/255)**gamma)\n        # using np.float32(.) here still works when the input is a numpy array\n        # of size 1\n        table = (min_value\n                 + (value_range ** np.float32(gamma))\n                 * dynamic_range)\n        table = np.clip(table, min_value, max_value).astype(arr.dtype)\n        arr_aug = ia.apply_lut(arr, table)\n        return arr_aug\n    return ski_exposure.adjust_gamma(arr, gamma)\n\n\n# TODO quite similar to the other adjust_contrast_*() functions, make DRY\ndef adjust_contrast_sigmoid(arr, gain, cutoff):\n    \"\"\"\n    Adjust image contrast to ``255*1/(1+exp(gain*(cutoff-I_ij/255)))``.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested (1) (2) (3)\n        * ``uint16``: yes; tested (2) (3)\n        * ``uint32``: yes; tested (2) (3)\n        * ``uint64``: yes; tested (2) (3) (4)\n        * ``int8``: limited; tested (2) (3) (5)\n        * ``int16``: limited; tested (2) (3) (5)\n        * ``int32``: limited; tested (2) (3) (5)\n        * ``int64``: limited; tested (2) (3) (4) (5)\n        * ``float16``: limited; tested (5)\n        * ``float32``: limited; tested (5)\n        * ``float64``: limited; tested (5)\n        * ``float128``: no (6)\n        * ``bool``: no (7)\n\n        - (1) Handled by ``cv2``. Other dtypes are handled by ``skimage``.\n        - (2) Normalization is done as ``I_ij/max``, where ``max`` is the\n              maximum value of the dtype, e.g. 255 for ``uint8``. The\n              normalization is reversed afterwards, e.g. ``result*255``\n              for ``uint8``.\n        - (3) Integer-like values are not rounded after applying the contrast\n              adjustment equation before inverting the normalization\n              to ``[0.0, 1.0]`` space), i.e. projection from continuous\n              space to discrete happens according to floor function.\n        - (4) Note that scikit-image doc says that integers are converted to\n              ``float64`` values before applying the contrast normalization\n              method. This might lead to inaccuracies for large 64bit integer\n              values. Tests showed no indication of that happening though.\n        - (5) Must not contain negative values. Values >=0 are fully supported.\n        - (6) Leads to error in scikit-image.\n        - (7) Does not make sense for contrast adjustments.\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        Array for which to adjust the contrast. Dtype ``uint8`` is fastest.\n\n    gain : number\n        Multiplier for the sigmoid function's output.\n        Higher values lead to quicker changes from dark to light pixels.\n\n    cutoff : number\n        Cutoff that shifts the sigmoid function in horizontal direction.\n        Higher values mean that the switch from dark to light pixels happens\n        later, i.e. the pixels will remain darker.\n\n    Returns\n    -------\n    numpy.ndarray\n        Array with adjusted contrast.\n\n    \"\"\"\n    if arr.size == 0:\n        return np.copy(arr)\n\n    # int8 is also possible according to docs\n    # https://docs.opencv.org/3.0-beta/modules/core/doc/operations_on_arrays.html#cv2.LUT ,\n    # but here it seemed like `d` was 0 for CV_8S, causing that to fail\n    if arr.dtype == iadt._UINT8_DTYPE:\n        min_value, _center_value, max_value = \\\n            iadt.get_value_range_of_dtype(arr.dtype)\n        dynamic_range = max_value - min_value\n\n        value_range = np.linspace(0, 1.0, num=dynamic_range+1,\n                                  dtype=np.float32)\n\n        # 255 * 1/(1 + exp(gain*(cutoff - I_ij/255)))\n        # using np.float32(.) here still works when the input is a numpy array\n        # of size 1\n        gain = np.float32(gain)\n        cutoff = np.float32(cutoff)\n        table = (min_value\n                 + dynamic_range\n                 * 1/(1 + np.exp(gain * (cutoff - value_range))))\n        table = np.clip(table, min_value, max_value).astype(arr.dtype)\n        arr_aug = ia.apply_lut(arr, table)\n        return arr_aug\n    return ski_exposure.adjust_sigmoid(arr, cutoff=cutoff, gain=gain)\n\n\n# TODO quite similar to the other adjust_contrast_*() functions, make DRY\n# TODO add dtype gating\ndef adjust_contrast_log(arr, gain):\n    \"\"\"\n    Adjust image contrast by scaling pixels to ``255*gain*log_2(1+v/255)``.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested (1) (2) (3)\n        * ``uint16``: yes; tested (2) (3)\n        * ``uint32``: no; tested (2) (3) (8)\n        * ``uint64``: no; tested (2) (3) (4) (8)\n        * ``int8``: limited; tested (2) (3) (5)\n        * ``int16``: limited; tested (2) (3) (5)\n        * ``int32``: no; tested (2) (3) (5) (8)\n        * ``int64``: no; tested (2) (3) (4) (5) (8)\n        * ``float16``: limited; tested (5)\n        * ``float32``: limited; tested (5)\n        * ``float64``: limited; tested (5)\n        * ``float128``: no (6)\n        * ``bool``: no (7)\n\n        - (1) Handled by ``cv2``. Other dtypes are handled by ``skimage``.\n        - (2) Normalization is done as ``I_ij/max``, where ``max`` is the\n              maximum value of the dtype, e.g. 255 for ``uint8``. The\n              normalization is reversed afterwards, e.g. ``result*255`` for\n              ``uint8``.\n        - (3) Integer-like values are not rounded after applying the contrast\n              adjustment equation (before inverting the normalization\n              to ``[0.0, 1.0]`` space), i.e. projection from continuous\n              space to discrete happens according to floor function.\n        - (4) Note that scikit-image doc says that integers are converted to\n              ``float64`` values before applying the contrast normalization\n              method. This might lead to inaccuracies for large 64bit integer\n              values. Tests showed no indication of that happening though.\n        - (5) Must not contain negative values. Values >=0 are fully supported.\n        - (6) Leads to error in scikit-image.\n        - (7) Does not make sense for contrast adjustments.\n        - (8) No longer supported since numpy 1.17. Previously: 'yes' for\n              ``uint32``, ``uint64``; 'limited' for ``int32``, ``int64``.\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        Array for which to adjust the contrast. Dtype ``uint8`` is fastest.\n\n    gain : number\n        Multiplier for the logarithm result. Values around 1.0 lead to a\n        contrast-adjusted images. Values above 1.0 quickly lead to partially\n        broken images due to exceeding the datatype's value range.\n\n    Returns\n    -------\n    numpy.ndarray\n        Array with adjusted contrast.\n\n    \"\"\"\n    if arr.size == 0:\n        return np.copy(arr)\n\n    # int8 is also possible according to docs\n    # https://docs.opencv.org/3.0-beta/modules/core/doc/operations_on_arrays.html#cv2.LUT ,\n    # but here it seemed like `d` was 0 for CV_8S, causing that to fail\n    if arr.dtype == iadt._UINT8_DTYPE:\n        min_value, _center_value, max_value = \\\n            iadt.get_value_range_of_dtype(arr.dtype)\n        dynamic_range = max_value - min_value\n\n        value_range = np.linspace(0, 1.0, num=dynamic_range+1,\n                                  dtype=np.float32)\n\n        # 255 * 1/(1 + exp(gain*(cutoff - I_ij/255)))\n        # using np.float32(.) here still works when the input is a numpy array\n        # of size 1\n        gain = np.float32(gain)\n        table = min_value + dynamic_range * gain * np.log2(1 + value_range)\n        table = np.clip(table, min_value, max_value).astype(arr.dtype)\n        arr_aug = ia.apply_lut(arr, table)\n        return arr_aug\n    return ski_exposure.adjust_log(arr, gain=gain)\n\n\n# TODO quite similar to the other adjust_contrast_*() functions, make DRY\ndef adjust_contrast_linear(arr, alpha):\n    \"\"\"Adjust contrast by scaling each pixel to ``127 + alpha*(v-127)``.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested (1) (2)\n        * ``uint16``: yes; tested (2)\n        * ``uint32``: yes; tested (2)\n        * ``uint64``: no (3)\n        * ``int8``: yes; tested (2)\n        * ``int16``: yes; tested (2)\n        * ``int32``: yes; tested (2)\n        * ``int64``: no (2)\n        * ``float16``: yes; tested (2)\n        * ``float32``: yes; tested (2)\n        * ``float64``: yes; tested (2)\n        * ``float128``: no (2)\n        * ``bool``: no (4)\n\n        - (1) Handled by ``cv2``. Other dtypes are handled by raw ``numpy``.\n        - (2) Only tested for reasonable alphas with up to a value of\n              around ``100``.\n        - (3) Conversion to ``float64`` is done during augmentation, hence\n              ``uint64``, ``int64``, and ``float128`` support cannot be\n              guaranteed.\n        - (4) Does not make sense for contrast adjustments.\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        Array for which to adjust the contrast. Dtype ``uint8`` is fastest.\n\n    alpha : number\n        Multiplier to linearly pronounce (``>1.0``), dampen (``0.0`` to\n        ``1.0``) or invert (``<0.0``) the difference between each pixel value\n        and the dtype's center value, e.g. ``127`` for ``uint8``.\n\n    Returns\n    -------\n    numpy.ndarray\n        Array with adjusted contrast.\n\n    \"\"\"\n    # pylint: disable=no-else-return\n    if arr.size == 0:\n        return np.copy(arr)\n\n    # int8 is also possible according to docs\n    # https://docs.opencv.org/3.0-beta/modules/core/doc/operations_on_arrays.html#cv2.LUT ,\n    # but here it seemed like `d` was 0 for CV_8S, causing that to fail\n    if arr.dtype == iadt._UINT8_DTYPE:\n        min_value, center_value, max_value = \\\n            iadt.get_value_range_of_dtype(arr.dtype)\n        # TODO get rid of this int(...)\n        center_value = int(center_value)\n\n        value_range = np.arange(0, 256, dtype=np.float32)\n\n        # 127 + alpha*(I_ij-127)\n        # using np.float32(.) here still works when the input is a numpy array\n        # of size 1\n        alpha = np.float32(alpha)\n        table = center_value + alpha * (value_range - center_value)\n        table = np.clip(table, min_value, max_value).astype(arr.dtype)\n        arr_aug = ia.apply_lut(arr, table)\n        return arr_aug\n    else:\n        input_dtype = arr.dtype\n        _min_value, center_value, _max_value = \\\n            iadt.get_value_range_of_dtype(input_dtype)\n        # TODO get rid of this int(...)\n        if input_dtype.kind in [\"u\", \"i\"]:\n            center_value = int(center_value)\n        image_aug = (center_value\n                     + alpha\n                     * (arr.astype(np.float64)-center_value))\n        image_aug = iadt.restore_dtypes_(image_aug, input_dtype)\n        return image_aug\n\n\nclass GammaContrast(_ContrastFuncWrapper):\n    \"\"\"\n    Adjust image contrast by scaling pixel values to ``255*((v/255)**gamma)``.\n\n    Values in the range ``gamma=(0.5, 2.0)`` seem to be sensible.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.contrast.adjust_contrast_gamma`.\n\n    Parameters\n    ----------\n    gamma : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Exponent for the contrast adjustment. Higher values darken the image.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the range ``[a, b]``\n              will be used per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter.\n\n    per_channel : bool or float, optional\n        Whether to use the same value for all channels (``False``) or to\n        sample a new value for each channel (``True``). If this value is a\n        float ``p``, then for ``p`` percent of all images `per_channel` will\n        be treated as ``True``, otherwise as ``False``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.GammaContrast((0.5, 2.0))\n\n    Modify the contrast of images according to ``255*((v/255)**gamma)``,\n    where ``v`` is a pixel value and ``gamma`` is sampled uniformly from\n    the interval ``[0.5, 2.0]`` (once per image).\n\n    >>> aug = iaa.GammaContrast((0.5, 2.0), per_channel=True)\n\n    Same as in the previous example, but ``gamma`` is sampled once per image\n    *and* channel.\n\n    \"\"\"\n\n    def __init__(self, gamma=(0.7, 1.7), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        params1d = [iap.handle_continuous_param(\n            gamma, \"gamma\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True)]\n        func = adjust_contrast_gamma\n        super(GammaContrast, self).__init__(\n            func, params1d, per_channel,\n            dtypes_allowed=\"uint8 uint16 uint32 uint64 int8 int16 int32 int64 \"\n                           \"float16 float32 float64\",\n            dtypes_disallowed=\"float128 bool\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass SigmoidContrast(_ContrastFuncWrapper):\n    \"\"\"\n    Adjust image contrast to ``255*1/(1+exp(gain*(cutoff-I_ij/255)))``.\n\n    Values in the range ``gain=(5, 20)`` and ``cutoff=(0.25, 0.75)`` seem to\n    be sensible.\n\n    A combination of ``gain=5.5`` and ``cutof=0.45`` is fairly close to\n    the identity function.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.contrast.adjust_contrast_sigmoid`.\n\n    Parameters\n    ----------\n    gain : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Multiplier for the sigmoid function's output.\n        Higher values lead to quicker changes from dark to light pixels.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the interval\n              ``[a, b]`` will be sampled uniformly per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter.\n\n    cutoff : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Cutoff that shifts the sigmoid function in horizontal direction.\n        Higher values mean that the switch from dark to light pixels happens\n        later, i.e. the pixels will remain darker.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the range ``[a, b]``\n              will be used per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter.\n\n    per_channel : bool or float, optional\n        Whether to use the same value for all channels (``False``) or to\n        sample a new value for each channel (``True``). If this value is a\n        float ``p``, then for ``p`` percent of all images `per_channel` will\n        be treated as ``True``, otherwise as ``False``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.SigmoidContrast(gain=(3, 10), cutoff=(0.4, 0.6))\n\n    Modify the contrast of images according to\n    ``255*1/(1+exp(gain*(cutoff-v/255)))``, where ``v`` is a pixel value,\n    ``gain`` is sampled uniformly from the interval ``[3, 10]`` (once per\n    image) and ``cutoff`` is sampled uniformly from the interval\n    ``[0.4, 0.6]`` (also once per image).\n\n    >>> aug = iaa.SigmoidContrast(\n    >>>     gain=(3, 10), cutoff=(0.4, 0.6), per_channel=True)\n\n    Same as in the previous example, but ``gain`` and ``cutoff`` are each\n    sampled once per image *and* channel.\n\n    \"\"\"\n    def __init__(self, gain=(5, 6), cutoff=(0.3, 0.6), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # TODO add inv parameter?\n        params1d = [\n            iap.handle_continuous_param(\n                gain, \"gain\", value_range=(0, None), tuple_to_uniform=True,\n                list_to_choice=True),\n            iap.handle_continuous_param(\n                cutoff, \"cutoff\", value_range=(0, 1.0), tuple_to_uniform=True,\n                list_to_choice=True)\n        ]\n        func = adjust_contrast_sigmoid\n\n        super(SigmoidContrast, self).__init__(\n            func, params1d, per_channel,\n            dtypes_allowed=\"uint8 uint16 uint32 uint64 int8 int16 int32 int64 \"\n                           \"float16 float32 float64\",\n            dtypes_disallowed=\"float128 bool\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass LogContrast(_ContrastFuncWrapper):\n    \"\"\"Adjust image contrast by scaling pixels to ``255*gain*log_2(1+v/255)``.\n\n    This augmenter is fairly similar to\n    ``imgaug.augmenters.arithmetic.Multiply``.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.contrast.adjust_contrast_log`.\n\n    Parameters\n    ----------\n    gain : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Multiplier for the logarithm result. Values around ``1.0`` lead to a\n        contrast-adjusted images. Values above ``1.0`` quickly lead to\n        partially broken images due to exceeding the datatype's value range.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the interval ``[a, b]``\n              will uniformly sampled be used per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter.\n\n    per_channel : bool or float, optional\n        Whether to use the same value for all channels (``False``) or to\n        sample a new value for each channel (``True``). If this value is a\n        float ``p``, then for ``p`` percent of all images `per_channel` will\n        be treated as ``True``, otherwise as ``False``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.LogContrast(gain=(0.6, 1.4))\n\n    Modify the contrast of images according to ``255*gain*log_2(1+v/255)``,\n    where ``v`` is a pixel value and ``gain`` is sampled uniformly from the\n    interval ``[0.6, 1.4]`` (once per image).\n\n    >>> aug = iaa.LogContrast(gain=(0.6, 1.4), per_channel=True)\n\n    Same as in the previous example, but ``gain`` is sampled once per image\n    *and* channel.\n\n    \"\"\"\n    def __init__(self, gain=(0.4, 1.6), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # TODO add inv parameter?\n        params1d = [iap.handle_continuous_param(\n            gain, \"gain\", value_range=(0, None), tuple_to_uniform=True,\n            list_to_choice=True)]\n        func = adjust_contrast_log\n\n        super(LogContrast, self).__init__(\n            func, params1d, per_channel,\n            dtypes_allowed=\"uint8 uint16 uint32 uint64 int8 int16 int32 int64 \"\n                           \"float16 float32 float64\",\n            dtypes_disallowed=\"float128 bool\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass LinearContrast(_ContrastFuncWrapper):\n    \"\"\"Adjust contrast by scaling each pixel to ``127 + alpha*(v-127)``.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.contrast.adjust_contrast_linear`.\n\n    Parameters\n    ----------\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Multiplier to linearly pronounce (``>1.0``), dampen (``0.0`` to\n        ``1.0``) or invert (``<0.0``) the difference between each pixel value\n        and the dtype's center value, e.g. ``127`` for ``uint8``.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the interval ``[a, b]``\n              will be used per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter.\n\n    per_channel : bool or float, optional\n        Whether to use the same value for all channels (``False``) or to\n        sample a new value for each channel (``True``). If this value is a\n        float ``p``, then for ``p`` percent of all images `per_channel` will\n        be treated as ``True``, otherwise as ``False``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.LinearContrast((0.4, 1.6))\n\n    Modify the contrast of images according to `127 + alpha*(v-127)``,\n    where ``v`` is a pixel value and ``alpha`` is sampled uniformly from the\n    interval ``[0.4, 1.6]`` (once per image).\n\n    >>> aug = iaa.LinearContrast((0.4, 1.6), per_channel=True)\n\n    Same as in the previous example, but ``alpha`` is sampled once per image\n    *and* channel.\n\n    \"\"\"\n    def __init__(self, alpha=(0.6, 1.4), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        params1d = [\n            iap.handle_continuous_param(\n                alpha, \"alpha\", value_range=None, tuple_to_uniform=True,\n                list_to_choice=True)\n        ]\n        func = adjust_contrast_linear\n\n        super(LinearContrast, self).__init__(\n            func, params1d, per_channel,\n            dtypes_allowed=\"uint8 uint16 uint32 int8 int16 int32 float16 \"\n                           \"float32 float64\",\n            dtypes_disallowed=\"uint64 int64 float128 bool\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO maybe offer the other contrast augmenters also wrapped in this, similar\n#      to CLAHE and HistogramEqualization?\n#      this is essentially tested by tests for CLAHE\nclass _IntensityChannelBasedApplier(object):\n    RGB = color_lib.CSPACE_RGB\n    BGR = color_lib.CSPACE_BGR\n    HSV = color_lib.CSPACE_HSV\n    HLS = color_lib.CSPACE_HLS\n    Lab = color_lib.CSPACE_Lab\n    _CHANNEL_MAPPING = {\n        HSV: 2,\n        HLS: 1,\n        Lab: 0\n    }\n\n    def __init__(self, from_colorspace, to_colorspace):\n        super(_IntensityChannelBasedApplier, self).__init__()\n\n        # TODO maybe add CIE, Luv?\n        valid_from_colorspaces = [self.RGB, self.BGR, self.Lab, self.HLS,\n                                  self.HSV]\n        assert from_colorspace in valid_from_colorspaces, (\n            \"Expected 'from_colorspace' to be one of %s, got %s.\" % (\n                valid_from_colorspaces, from_colorspace))\n\n        valid_to_colorspaces = [self.Lab, self.HLS, self.HSV]\n        assert to_colorspace in valid_to_colorspaces, (\n            \"Expected 'to_colorspace' to be one of %s, got %s.\" % (\n                valid_to_colorspaces, to_colorspace))\n\n        self.from_colorspace = from_colorspace\n        self.to_colorspace = to_colorspace\n\n    def apply(self, images, random_state, parents, hooks, func):\n        input_was_array = ia.is_np_array(images)\n        rss = random_state.duplicate(3)\n\n        # normalize images\n        # (H, W, 1)      will be used directly in AllChannelsCLAHE\n        # (H, W, 3)      will be converted to target colorspace in the next\n        #                block\n        # (H, W, 4)      will be reduced to (H, W, 3) (remove 4th channel) and\n        #                converted to target colorspace in next block\n        # (H, W, <else>) will raise a warning and be treated channelwise by\n        #                AllChannelsCLAHE\n        images_normalized = []\n        images_change_cs = []\n        images_change_cs_indices = []\n        for i, image in enumerate(images):\n            nb_channels = image.shape[2]\n            if nb_channels == 1:\n                images_normalized.append(image)\n            elif nb_channels == 3:\n                images_normalized.append(None)\n                images_change_cs.append(image)\n                images_change_cs_indices.append(i)\n            elif nb_channels == 4:\n                # assume that 4th channel is an alpha channel, e.g. in RGBA\n                images_normalized.append(None)\n                images_change_cs.append(image[..., 0:3])\n                images_change_cs_indices.append(i)\n            else:\n                ia.warn(\n                    \"Got image with %d channels in \"\n                    \"_IntensityChannelBasedApplier (parents: %s), \"\n                    \"expected 0, 1, 3 or 4 channels.\" % (\n                        nb_channels, \", \".join(\n                            parent.name for parent in parents)))\n                images_normalized.append(image)\n\n        # convert colorspaces of normalized 3-channel images\n        images_after_color_conversion = [None] * len(images_normalized)\n        if len(images_change_cs) > 0:\n            images_new_cs = color_lib.change_colorspaces_(\n                images_change_cs,\n                to_colorspaces=self.to_colorspace,\n                from_colorspaces=self.from_colorspace)\n\n            for image_new_cs, target_idx in zip(images_new_cs,\n                                                images_change_cs_indices):\n                chan_idx = self._CHANNEL_MAPPING[self.to_colorspace]\n                images_normalized[target_idx] = image_new_cs[\n                    ..., chan_idx:chan_idx+1]\n                images_after_color_conversion[target_idx] = image_new_cs\n\n        # apply function channelwise\n        images_aug = func(images_normalized, rss[1])\n\n        # denormalize\n        result = []\n        images_change_cs = []\n        images_change_cs_indices = []\n        gen = enumerate(zip(images, images_after_color_conversion, images_aug))\n        for i, (image, image_conv, image_aug) in gen:\n            nb_channels = image.shape[2]\n            if nb_channels in [3, 4]:\n                chan_idx = self._CHANNEL_MAPPING[self.to_colorspace]\n                image_tmp = image_conv\n                image_tmp[..., chan_idx:chan_idx+1] = image_aug\n\n                result.append(None if nb_channels == 3 else image[..., 3:4])\n                images_change_cs.append(image_tmp)\n                images_change_cs_indices.append(i)\n            else:\n                result.append(image_aug)\n\n        # invert colorspace conversion\n        if len(images_change_cs) > 0:\n            images_new_cs = color_lib.change_colorspaces_(\n                images_change_cs,\n                to_colorspaces=self.from_colorspace,\n                from_colorspaces=self.to_colorspace)\n            for image_new_cs, target_idx in zip(images_new_cs,\n                                                images_change_cs_indices):\n                if result[target_idx] is None:\n                    result[target_idx] = image_new_cs\n                else:\n                    # input image had four channels, 4th channel is already\n                    # in result\n                    result[target_idx] = np.dstack((image_new_cs,\n                                                    result[target_idx]))\n\n        # convert to array if necessary\n        if input_was_array:\n            result = np.array(result, dtype=result[0].dtype)\n\n        return result\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.from_colorspace, self.to_colorspace]\n\n\n# TODO add parameter `tile_grid_size_percent`\nclass AllChannelsCLAHE(meta.Augmenter):\n    \"\"\"Apply CLAHE to all channels of images in their original colorspaces.\n\n    CLAHE (Contrast Limited Adaptive Histogram Equalization) performs\n    histogram equilization within image patches, i.e. over local\n    neighbourhoods.\n\n    In contrast to ``imgaug.augmenters.contrast.CLAHE``, this augmenter\n    operates directly on all channels of the input images. It does not\n    perform any colorspace transformations and does not focus on specific\n    channels (e.g. ``L`` in ``Lab`` colorspace).\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: no (1)\n        * ``uint64``: no (2)\n        * ``int8``: no (2)\n        * ``int16``: no (2)\n        * ``int32``: no (2)\n        * ``int64``: no (2)\n        * ``float16``: no (2)\n        * ``float32``: no (2)\n        * ``float64``: no (2)\n        * ``float128``: no (1)\n        * ``bool``: no (1)\n\n        - (1) rejected by cv2\n        - (2) results in error in cv2: ``cv2.error:\n              OpenCV(3.4.2) (...)/clahe.cpp:351: error: (-215:Assertion\n              failed) src.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3))\n              || _src.type() == (((2) & ((1 << 3) - 1)) + (((1)-1) << 3)) in\n              function 'apply'``\n\n    Parameters\n    ----------\n    clip_limit : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See ``imgaug.augmenters.contrast.CLAHE``.\n\n    tile_grid_size_px : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or tuple of tuple of int or tuple of list of int or tuple of imgaug.parameters.StochasticParameter, optional\n        See ``imgaug.augmenters.contrast.CLAHE``.\n\n    tile_grid_size_px_min : int, optional\n        See ``imgaug.augmenters.contrast.CLAHE``.\n\n    per_channel : bool or float, optional\n        Whether to use the same value for all channels (``False``) or to\n        sample a new value for each channel (``True``). If this value is a\n        float ``p``, then for ``p`` percent of all images `per_channel` will\n        be treated as ``True``, otherwise as ``False``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AllChannelsCLAHE()\n\n    Create an augmenter that applies CLAHE to all channels of input images.\n\n    >>> aug = iaa.AllChannelsCLAHE(clip_limit=(1, 10))\n\n    Same as in the previous example, but the `clip_limit` used by CLAHE is\n    uniformly sampled per image from the interval ``[1, 10]``. Some images\n    will therefore have stronger contrast than others (i.e. higher clip limit\n    values).\n\n    >>> aug = iaa.AllChannelsCLAHE(clip_limit=(1, 10), per_channel=True)\n\n    Same as in the previous example, but the `clip_limit` is sampled per\n    image *and* channel, leading to different levels of contrast for each\n    channel.\n\n    \"\"\"\n\n    def __init__(self, clip_limit=(0.1, 8), tile_grid_size_px=(3, 12),\n                 tile_grid_size_px_min=3, per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(AllChannelsCLAHE, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.clip_limit = iap.handle_continuous_param(\n            clip_limit, \"clip_limit\", value_range=(0+1e-4, None),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.tile_grid_size_px = iap.handle_discrete_kernel_size_param(\n            tile_grid_size_px, \"tile_grid_size_px\", value_range=(0, None),\n            allow_floats=False)\n        self.tile_grid_size_px_min = tile_grid_size_px_min\n        self.per_channel = iap.handle_probability_param(per_channel,\n                                                        \"per_channel\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        iadt.gate_dtypes_strs(\n            images,\n            allowed=\"uint8 uint16\",\n            disallowed=\"bool uint32 uint64 int8 int16 int32 int64 \"\n                       \"float16 float32 float64 float128\",\n            augmenter=self\n        )\n\n        nb_images = len(images)\n        nb_channels = meta.estimate_max_number_of_channels(images)\n\n        mode = \"single\" if self.tile_grid_size_px[1] is None else \"two\"\n        rss = random_state.duplicate(3 if mode == \"single\" else 4)\n        per_channel = self.per_channel.draw_samples((nb_images,),\n                                                    random_state=rss[0])\n        clip_limit = self.clip_limit.draw_samples((nb_images, nb_channels),\n                                                  random_state=rss[1])\n        tile_grid_size_px_h = self.tile_grid_size_px[0].draw_samples(\n            (nb_images, nb_channels), random_state=rss[2])\n        if mode == \"single\":\n            tile_grid_size_px_w = tile_grid_size_px_h\n        else:\n            tile_grid_size_px_w = self.tile_grid_size_px[1].draw_samples(\n                (nb_images, nb_channels), random_state=rss[3])\n\n        tile_grid_size_px_w = np.maximum(tile_grid_size_px_w,\n                                         self.tile_grid_size_px_min)\n        tile_grid_size_px_h = np.maximum(tile_grid_size_px_h,\n                                         self.tile_grid_size_px_min)\n\n        gen = enumerate(zip(images, clip_limit, tile_grid_size_px_h,\n                            tile_grid_size_px_w, per_channel))\n        for i, (image, clip_limit_i, tgs_px_h_i, tgs_px_w_i, pchannel_i) in gen:\n            if image.size == 0:\n                continue\n\n            nb_channels = image.shape[2]\n            c_param = 0\n            image_warped = []\n            for c in sm.xrange(nb_channels):\n                if tgs_px_w_i[c_param] > 1 or tgs_px_h_i[c_param] > 1:\n                    clahe = cv2.createCLAHE(\n                        clipLimit=clip_limit_i[c_param],\n                        tileGridSize=(tgs_px_w_i[c_param], tgs_px_h_i[c_param])\n                    )\n                    channel_warped = clahe.apply(\n                        _normalize_cv2_input_arr_(image[..., c])\n                    )\n                    image_warped.append(channel_warped)\n                else:\n                    image_warped.append(image[..., c])\n                if pchannel_i > 0.5:\n                    c_param += 1\n\n            # combine channels to one image\n            image_warped = np.stack(image_warped, axis=-1)\n\n            batch.images[i] = image_warped\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.clip_limit, self.tile_grid_size_px,\n                self.tile_grid_size_px_min, self.per_channel]\n\n\nclass CLAHE(meta.Augmenter):\n    \"\"\"Apply CLAHE to L/V/L channels in HLS/HSV/Lab colorspaces.\n\n    This augmenter applies CLAHE (Contrast Limited Adaptive Histogram\n    Equalization) to images, a form of histogram equalization that normalizes\n    within local image patches.\n    The augmenter transforms input images to a target colorspace (e.g.\n    ``Lab``), extracts an intensity-related channel from the converted\n    images (e.g. ``L`` for ``Lab``), applies CLAHE to the channel and then\n    converts the resulting image back to the original colorspace.\n\n    Grayscale images (images without channel axis or with only one channel\n    axis) are automatically handled, `from_colorspace` does not have to be\n    adjusted for them. For images with four channels (e.g. ``RGBA``), the\n    fourth channel is ignored in the colorspace conversion (e.g. from an\n    ``RGBA`` image, only the ``RGB`` part is converted, normalized, converted\n    back and concatenated with the input ``A`` channel). Images with unusual\n    channel numbers (2, 5 or more than 5) are normalized channel-by-channel\n    (same behaviour as ``AllChannelsCLAHE``, though a warning will be raised).\n\n    If you want to apply CLAHE to each channel of the original input image's\n    colorspace (without any colorspace conversion), use\n    ``imgaug.augmenters.contrast.AllChannelsCLAHE`` instead.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no (1)\n        * ``uint32``: no (1)\n        * ``uint64``: no (1)\n        * ``int8``: no (1)\n        * ``int16``: no (1)\n        * ``int32``: no (1)\n        * ``int64``: no (1)\n        * ``float16``: no (1)\n        * ``float32``: no (1)\n        * ``float64``: no (1)\n        * ``float128``: no (1)\n        * ``bool``: no (1)\n\n        - (1) This augmenter uses\n              :class:`~imgaug.augmenters.color.ChangeColorspace`, which is\n              currently limited to ``uint8``.\n\n    Parameters\n    ----------\n    clip_limit : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Clipping limit. Higher values result in stronger contrast. OpenCV\n        uses a default of ``40``, though values around ``5`` seem to already\n        produce decent contrast.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value from the range ``[a, b]``\n              will be used per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter.\n\n    tile_grid_size_px : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or tuple of tuple of int or tuple of list of int or tuple of imgaug.parameters.StochasticParameter, optional\n        Kernel size, i.e. size of each local neighbourhood in pixels.\n\n            * If an ``int``, then that value will be used for all images for\n              both kernel height and width.\n            * If a tuple ``(a, b)``, then a value from the discrete interval\n              ``[a..b]`` will be uniformly sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image and used for both kernel height and width.\n            * If a ``StochasticParameter``, then a value will be sampled per\n              image from that parameter per image and used for both kernel\n              height and width.\n            * If a tuple of tuple of ``int`` given as ``((a, b), (c, d))``,\n              then two values will be sampled independently from the discrete\n              ranges ``[a..b]`` and ``[c..d]`` per image and used as the\n              kernel height and width.\n            * If a tuple of lists of ``int``, then two values will be sampled\n              independently per image, one from the first list and one from\n              the second, and used as the kernel height and width.\n            * If a tuple of ``StochasticParameter``, then two values will be\n              sampled indepdently per image, one from the first parameter and\n              one from the second, and used as the kernel height and width.\n\n    tile_grid_size_px_min : int, optional\n        Minimum kernel size in px, per axis. If the sampling results in a\n        value lower than this minimum, it will be clipped to this value.\n\n    from_colorspace : {\"RGB\", \"BGR\", \"HSV\", \"HLS\", \"Lab\"}, optional\n        Colorspace of the input images.\n        If any input image has only one or zero channels, this setting will\n        be ignored and it will be assumed that the input is grayscale.\n        If a fourth channel is present in an input image, it will be removed\n        before the colorspace conversion and later re-added.\n        See also :func:`~imgaug.augmenters.color.change_colorspace_` for\n        details.\n\n    to_colorspace : {\"Lab\", \"HLS\", \"HSV\"}, optional\n        Colorspace in which to perform CLAHE. For ``Lab``, CLAHE will only be\n        applied to the first channel (``L``), for ``HLS`` to the\n        second (``L``) and for ``HSV`` to the third (``V``). To apply CLAHE\n        to all channels of an input image (without colorspace conversion),\n        see ``imgaug.augmenters.contrast.AllChannelsCLAHE``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CLAHE()\n\n    Create a standard CLAHE augmenter.\n\n    >>> aug = iaa.CLAHE(clip_limit=(1, 10))\n\n    Create a CLAHE augmenter with a clip limit uniformly sampled from\n    ``[1..10]``, where ``1`` is rather low contrast and ``10`` is rather\n    high contrast.\n\n    >>> aug = iaa.CLAHE(tile_grid_size_px=(3, 21))\n\n    Create a CLAHE augmenter with kernel sizes of ``SxS``, where ``S`` is\n    uniformly sampled from ``[3..21]``. Sampling happens once per image.\n\n    >>> aug = iaa.CLAHE(\n    >>>     tile_grid_size_px=iap.Discretize(iap.Normal(loc=7, scale=2)),\n    >>>     tile_grid_size_px_min=3)\n\n    Create a CLAHE augmenter with kernel sizes of ``SxS``, where ``S`` is\n    sampled from ``N(7, 2)``, but does not go below ``3``.\n\n    >>> aug = iaa.CLAHE(tile_grid_size_px=((3, 21), [3, 5, 7]))\n\n    Create a CLAHE augmenter with kernel sizes of ``HxW``, where ``H`` is\n    uniformly sampled from ``[3..21]`` and ``W`` is randomly picked from the\n    list ``[3, 5, 7]``.\n\n    >>> aug = iaa.CLAHE(\n    >>>     from_colorspace=iaa.CSPACE_BGR,\n    >>>     to_colorspace=iaa.CSPACE_HSV)\n\n    Create a CLAHE augmenter that converts images from BGR colorspace to\n    HSV colorspace and then applies the local histogram equalization to the\n    ``V`` channel of the images (before converting back to ``BGR``).\n    Alternatively, ``Lab`` (default) or ``HLS`` can be used as the target\n    colorspace. Grayscale images (no channels / one channel) are never\n    converted and are instead directly normalized (i.e. `from_colorspace`\n    does not have to be changed for them).\n\n    \"\"\"\n    RGB = color_lib.CSPACE_RGB\n    BGR = color_lib.CSPACE_BGR\n    HSV = color_lib.CSPACE_HSV\n    HLS = color_lib.CSPACE_HLS\n    Lab = color_lib.CSPACE_Lab\n\n    def __init__(self, clip_limit=(0.1, 8), tile_grid_size_px=(3, 12),\n                 tile_grid_size_px_min=3,\n                 from_colorspace=color_lib.CSPACE_RGB,\n                 to_colorspace=color_lib.CSPACE_Lab,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CLAHE, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.all_channel_clahe = AllChannelsCLAHE(\n            clip_limit=clip_limit,\n            tile_grid_size_px=tile_grid_size_px,\n            tile_grid_size_px_min=tile_grid_size_px_min,\n            name=\"%s_AllChannelsCLAHE\" % (name,))\n\n        self.intensity_channel_based_applier = _IntensityChannelBasedApplier(\n            from_colorspace, to_colorspace)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        iadt.allow_only_uint8(images, augmenter=self)\n\n        def _augment_all_channels_clahe(images_normalized,\n                                        random_state_derived):\n            # pylint: disable=protected-access\n            # TODO would .augment_batch() be sufficient here?\n            batch_imgs = _BatchInAugmentation(\n                images=images_normalized)\n            return self.all_channel_clahe._augment_batch_(\n                batch_imgs, random_state_derived, parents + [self],\n                hooks\n            ).images\n\n        batch.images = self.intensity_channel_based_applier.apply(\n            images, random_state, parents + [self], hooks,\n            _augment_all_channels_clahe)\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        ac_clahe = self.all_channel_clahe\n        intb_applier = self.intensity_channel_based_applier\n        return [\n            ac_clahe.clip_limit,\n            ac_clahe.tile_grid_size_px,\n            ac_clahe.tile_grid_size_px_min\n        ] + intb_applier.get_parameters()\n\n\nclass AllChannelsHistogramEqualization(meta.Augmenter):\n    \"\"\"\n    Apply Histogram Eq. to all channels of images in their original colorspaces.\n\n    In contrast to ``imgaug.augmenters.contrast.HistogramEqualization``, this\n    augmenter operates directly on all channels of the input images. It does\n    not perform any colorspace transformations and does not focus on specific\n    channels (e.g. ``L`` in ``Lab`` colorspace).\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no (1)\n        * ``uint32``: no (2)\n        * ``uint64``: no (1)\n        * ``int8``: no (1)\n        * ``int16``: no (1)\n        * ``int32``: no (1)\n        * ``int64``: no (1)\n        * ``float16``: no (2)\n        * ``float32``: no (1)\n        * ``float64``: no (1)\n        * ``float128``: no (2)\n        * ``bool``: no (1)\n\n        - (1) causes cv2 error: ``cv2.error:\n              OpenCV(3.4.5) (...)/histogram.cpp:3345: error: (-215:Assertion\n              failed) src.type() == CV_8UC1 in function 'equalizeHist'``\n        - (2) rejected by cv2\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AllChannelsHistogramEqualization()\n\n    Create an augmenter that applies histogram equalization to all channels\n    of input images in the original colorspaces.\n\n    >>> aug = iaa.Alpha((0.0, 1.0), iaa.AllChannelsHistogramEqualization())\n\n    Same as in the previous example, but alpha-blends the contrast-enhanced\n    augmented images with the original input images using random blend\n    strengths. This leads to random strengths of the contrast adjustment.\n\n    \"\"\"\n    def __init__(self, seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(AllChannelsHistogramEqualization, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        iadt.allow_only_uint8(images, augmenter=self)\n\n        for i, image in enumerate(images):\n            if image.size == 0:\n                continue\n\n            image_warped = [\n                cv2.equalizeHist(_normalize_cv2_input_arr_(image[..., c]))\n                for c in sm.xrange(image.shape[2])]\n            image_warped = np.array(image_warped, dtype=image_warped[0].dtype)\n            image_warped = image_warped.transpose((1, 2, 0))\n\n            batch.images[i] = image_warped\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return []\n\n\nclass HistogramEqualization(meta.Augmenter):\n    \"\"\"\n    Apply Histogram Eq. to L/V/L channels of images in HLS/HSV/Lab colorspaces.\n\n    This augmenter is similar to ``imgaug.augmenters.contrast.CLAHE``.\n\n    The augmenter transforms input images to a target colorspace (e.g.\n    ``Lab``), extracts an intensity-related channel from the converted images\n    (e.g. ``L`` for ``Lab``), applies Histogram Equalization to the channel\n    and then converts the resulting image back to the original colorspace.\n\n    Grayscale images (images without channel axis or with only one channel\n    axis) are automatically handled, `from_colorspace` does not have to be\n    adjusted for them. For images with four channels (e.g. RGBA), the fourth\n    channel is ignored in the colorspace conversion (e.g. from an ``RGBA``\n    image, only the ``RGB`` part is converted, normalized, converted back and\n    concatenated with the input ``A`` channel). Images with unusual channel\n    numbers (2, 5 or more than 5) are normalized channel-by-channel (same\n    behaviour as ``AllChannelsHistogramEqualization``, though a warning will\n    be raised).\n\n    If you want to apply HistogramEqualization to each channel of the original\n    input image's colorspace (without any colorspace conversion), use\n    ``imgaug.augmenters.contrast.AllChannelsHistogramEqualization`` instead.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no (1)\n        * ``uint32``: no (1)\n        * ``uint64``: no (1)\n        * ``int8``: no (1)\n        * ``int16``: no (1)\n        * ``int32``: no (1)\n        * ``int64``: no (1)\n        * ``float16``: no (1)\n        * ``float32``: no (1)\n        * ``float64``: no (1)\n        * ``float128``: no (1)\n        * ``bool``: no (1)\n\n        - (1) This augmenter uses :class:`AllChannelsHistogramEqualization`,\n              which only supports ``uint8``.\n\n    Parameters\n    ----------\n    from_colorspace : {\"RGB\", \"BGR\", \"HSV\", \"HLS\", \"Lab\"}, optional\n        Colorspace of the input images.\n        If any input image has only one or zero channels, this setting will be\n        ignored and it will be assumed that the input is grayscale.\n        If a fourth channel is present in an input image, it will be removed\n        before the colorspace conversion and later re-added.\n        See also :func:`~imgaug.augmenters.color.change_colorspace_` for\n        details.\n\n    to_colorspace : {\"Lab\", \"HLS\", \"HSV\"}, optional\n        Colorspace in which to perform Histogram Equalization. For ``Lab``,\n        the equalization will only be applied to the first channel (``L``),\n        for ``HLS`` to the second (``L``) and for ``HSV`` to the third (``V``).\n        To apply histogram equalization to all channels of an input image\n        (without colorspace conversion), see\n        ``imgaug.augmenters.contrast.AllChannelsHistogramEqualization``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.HistogramEqualization()\n\n    Create an augmenter that converts images to ``HLS``/``HSV``/``Lab``\n    colorspaces, extracts intensity-related channels (i.e. ``L``/``V``/``L``),\n    applies histogram equalization to these channels and converts back to the\n    input colorspace.\n\n    >>> aug = iaa.Alpha((0.0, 1.0), iaa.HistogramEqualization())\n\n    Same as in the previous example, but alpha blends the result, leading\n    to various strengths of contrast normalization.\n\n    >>> aug = iaa.HistogramEqualization(\n    >>>     from_colorspace=iaa.CSPACE_BGR,\n    >>>     to_colorspace=iaa.CSPACE_HSV)\n\n    Same as in the first example, but the colorspace of input images has\n    to be ``BGR`` (instead of default ``RGB``) and the histogram equalization\n    is applied to the ``V`` channel in ``HSV`` colorspace.\n\n    \"\"\"\n    RGB = color_lib.CSPACE_RGB\n    BGR = color_lib.CSPACE_BGR\n    HSV = color_lib.CSPACE_HSV\n    HLS = color_lib.CSPACE_HLS\n    Lab = color_lib.CSPACE_Lab\n\n    def __init__(self, from_colorspace=color_lib.CSPACE_RGB,\n                 to_colorspace=color_lib.CSPACE_Lab,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(HistogramEqualization, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.all_channel_histogram_equalization = \\\n            AllChannelsHistogramEqualization(\n                name=\"%s_AllChannelsHistogramEqualization\" % (name,))\n\n        self.intensity_channel_based_applier = _IntensityChannelBasedApplier(\n            from_colorspace, to_colorspace)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        iadt.allow_only_uint8(images, augmenter=self)\n\n        def _augment_all_channels_histogram_equalization(images_normalized,\n                                                         random_state_derived):\n            # pylint: disable=protected-access\n            # TODO would .augment_batch() be sufficient here\n            batch_imgs = _BatchInAugmentation(\n                images=images_normalized)\n            return self.all_channel_histogram_equalization._augment_batch_(\n                batch_imgs, random_state_derived, parents + [self],\n                hooks\n            ).images\n\n        batch.images = self.intensity_channel_based_applier.apply(\n            images, random_state, parents + [self], hooks,\n            _augment_all_channels_histogram_equalization)\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        icb_applier = self.intensity_channel_based_applier\n        return icb_applier.get_parameters()\n"
  },
  {
    "path": "imgaug/augmenters/convolutional.py",
    "content": "\"\"\"\nAugmenters that are based on applying convolution kernels to images.\n\nList of augmenters:\n\n    * :class:`Convolve`\n    * :class:`Sharpen`\n    * :class:`Emboss`\n    * :class:`EdgeDetect`\n    * :class:`DirectedEdgeDetect`\n\nFor MotionBlur, see ``blur.py``.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport itertools\n\nimport numpy as np\nimport cv2\nimport six.moves as sm\n\nimport imgaug as ia\nfrom . import meta\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\n\n\ndef convolve(image, kernel):\n    \"\"\"Apply a convolution kernel (or one per channel) to an image.\n\n    See :func:`convolve_` for details.\n\n    Added in 0.5.0.\n\n    **Supported dtypes**:\n\n        See :func:`~imgaug.augmenters.convolutional.convolve_`.\n\n    Parameters\n    ----------\n    image : ndarray\n        ``(H,W)`` or ``(H,W,C)`` image array.\n\n    kernel : ndarray or list of ndarray\n        Either a single 2D kernel matrix (will be applied to all channels)\n        or a list of 2D matrices (one per image channel).\n\n    Returns\n    -------\n    image\n        Image of the same shape and dtype as the input array.\n\n    \"\"\"\n    return convolve_(np.copy(image), kernel)\n\n\ndef convolve_(image, kernel):\n    \"\"\"Apply a convolution kernel (or one per channel) in-place to an image.\n\n    Use a list of matrices to apply one kernel per channel.\n\n    Added in 0.5.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: no (1)\n        * ``uint64``: no (2)\n        * ``int8``: yes; tested (3)\n        * ``int16``: yes; tested\n        * ``int32``: no (2)\n        * ``int64``: no (2)\n        * ``float16``: yes; tested (4)\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no (1)\n        * ``bool``: yes; tested (4)\n\n        - (1) rejected by ``cv2.filter2D()``.\n        - (2) causes error: cv2.error: OpenCV(3.4.2) (...)/filter.cpp:4487:\n              error: (-213:The function/feature is not implemented)\n              Unsupported combination of source format (=1), and destination\n              format (=1) in function 'getLinearFilter'.\n        - (3) mapped internally to ``int16``.\n        - (4) mapped internally to ``float32``.\n\n    Parameters\n    ----------\n    image : ndarray\n        ``(H,W)`` or ``(H,W,C)`` image array.\n        May be modified in-place.\n\n    kernel : ndarray or list of ndarray\n        Either a single 2D kernel matrix (will be applied to all channels)\n        or a list of 2D matrices (one per image channel).\n\n    Returns\n    -------\n    image\n        Image of the same shape and dtype as the input array.\n        Might have been modified in-place.\n\n    \"\"\"\n    iadt.gate_dtypes_strs(\n        {image.dtype},\n        allowed=\"bool uint8 uint16 int8 int16 float16 float32 float64\",\n        disallowed=\"uint32 uint64 int32 int64 float128\"\n    )\n\n    # currently we don't have to worry here about alignemnt with\n    # non-image data and therefore can just place this before any\n    # sampling\n    if image.size == 0:\n        return image\n\n    input_shape = image.shape\n    nb_channels = 1 if len(input_shape) == 2 else input_shape[2]\n\n    input_dtype = image.dtype\n    if image.dtype in {iadt._BOOL_DTYPE, iadt._FLOAT16_DTYPE}:\n        image = image.astype(np.float32, copy=False)\n    elif image.dtype == iadt._INT8_DTYPE:\n        image = image.astype(np.int16, copy=False)\n\n    if ia.is_np_array(kernel):\n        assert kernel.ndim == 2, (\n            \"Expected kernel to be either a list of (H,W) arrays or a \"\n            \"single (H,W) array, got array of shape %s.\" % (kernel.shape,)\n        )\n        matrices = [kernel]\n    else:\n        assert isinstance(kernel, list), (\n            \"Expected kernel to be either a list of (H,W) arrays or a \"\n            \"single (H,W) array, got type %s.\" % (type(kernel).__name__,)\n        )\n        assert len(kernel) == nb_channels, (\n            \"Kernel was given as a list. Expected that list to contain as \"\n            \"many arrays as there are image channels. \"\n            \"Got %d, but expected %d for image of shape %s.\" % (\n                len(kernel), nb_channels, image.shape\n            )\n        )\n        matrices = kernel\n\n    if not image.flags[\"C_CONTIGUOUS\"]:\n        image = np.ascontiguousarray(image)\n\n    # force channelwise application for >512 channels\n    if nb_channels > 512 and len(matrices) == 1:\n        matrices = [matrices[0]] * nb_channels\n\n    if len(matrices) == 1:\n        if matrices[0] is not None:\n            if image.base is not None and image.base.shape[0] == 1:\n                image = np.copy(image)\n            image = cv2.filter2D(image, -1, matrices[0], dst=image)\n    else:\n        for channel in sm.xrange(nb_channels):\n            if matrices[channel] is not None:\n                arr_channel = np.copy(image[..., channel])\n                image[..., channel] = cv2.filter2D(\n                    arr_channel,\n                    -1,\n                    matrices[channel],\n                    dst=arr_channel\n                )\n\n    if input_dtype.kind == \"b\":\n        image = image > 0.5\n    elif input_dtype in {iadt._INT8_DTYPE, iadt._FLOAT16_DTYPE}:\n        image = iadt.restore_dtypes_(image, input_dtype)\n\n    if len(input_shape) == 3 and image.ndim == 2:\n        image = image[:, :, np.newaxis]\n\n    return image\n\n\n# TODO allow 3d matrices as input (not only 2D)\n# TODO add _augment_keypoints and other _augment funcs, as these should do\n#      something for e.g. [[0, 0, 1]]\nclass Convolve(meta.Augmenter):\n    \"\"\"\n    Apply a convolution to input images.\n\n    **Supported dtypes**:\n\n        See :func:`~imgaug.augmenters.convolutional.convolve_`.\n\n    Parameters\n    ----------\n    matrix : None or (H, W) ndarray or imgaug.parameters.StochasticParameter or callable, optional\n        The weight matrix of the convolution kernel to apply.\n\n            * If ``None``, the input images will not be changed.\n            * If a 2D numpy array, that array will always be used for all\n              images and channels as the kernel.\n            * If a callable, that method will be called for each image\n              via ``parameter(image, C, random_state)``. The function must\n              either return a list of ``C`` matrices (i.e. one per channel)\n              or a 2D numpy array (will be used for all channels) or a\n              3D ``HxWxC`` numpy array. If a list is returned, each entry may\n              be ``None``, which will result in no changes to the respective\n              channel.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> matrix = np.array([[0, -1, 0],\n    >>>                    [-1, 4, -1],\n    >>>                    [0, -1, 0]])\n    >>> aug = iaa.Convolve(matrix=matrix)\n\n    Convolves all input images with the kernel shown in the ``matrix``\n    variable.\n\n    >>> def gen_matrix(image, nb_channels, random_state):\n    >>>     matrix_A = np.array([[0, -1, 0],\n    >>>                          [-1, 4, -1],\n    >>>                          [0, -1, 0]])\n    >>>     matrix_B = np.array([[0, 1, 0],\n    >>>                          [1, -4, 1],\n    >>>                          [0, 1, 0]])\n    >>>     if image.shape[0] % 2 == 0:\n    >>>         return [matrix_A] * nb_channels\n    >>>     else:\n    >>>         return [matrix_B] * nb_channels\n    >>> aug = iaa.Convolve(matrix=gen_matrix)\n\n    Convolves images that have an even height with matrix A and images\n    having an odd height with matrix B.\n\n    \"\"\"\n\n    def __init__(self, matrix=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Convolve, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        if matrix is None:\n            self.matrix = None\n            self.matrix_type = \"None\"\n        elif ia.is_np_array(matrix):\n            assert matrix.ndim == 2, (\n                \"Expected convolution matrix to have exactly two dimensions, \"\n                \"got %d (shape %s).\" % (matrix.ndim, matrix.shape))\n            self.matrix = matrix\n            self.matrix_type = \"constant\"\n        elif ia.is_callable(matrix):\n            self.matrix = matrix\n            self.matrix_type = \"function\"\n        else:\n            raise Exception(\n                \"Expected float, int, tuple/list with 2 entries or \"\n                \"StochasticParameter. Got %s.\" % (\n                    type(matrix),))\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n        rss = random_state.duplicate(len(images))\n\n        for i, image in enumerate(images):\n            _height, _width, nb_channels = image.shape\n\n            if self.matrix_type == \"None\":\n                matrix = None\n            elif self.matrix_type == \"constant\":\n                matrix = self.matrix\n            else:\n                assert self.matrix_type == \"function\"\n                # TODO check if sampled matrices are identical over channels\n                #      and if so merge. (does that really help wrt speed?)\n                matrix = self.matrix(images[i], nb_channels, rss[i])\n\n            if matrix is not None:\n                batch.images[i] = convolve_(image, matrix)\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.matrix, self.matrix_type]\n\n\nclass Sharpen(Convolve):\n    \"\"\"\n    Sharpen images and alpha-blend the result with the original input images.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.convolutional.Convolve`.\n\n    Parameters\n    ----------\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Blending factor of the sharpened image. At ``0.0``, only the original\n        image is visible, at ``1.0`` only its sharpened version is visible.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value will be sampled from the\n              interval ``[a, b]`` per image.\n            * If a list, a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, a value will be sampled from that\n              parameter per image.\n\n    lightness : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Lightness/brightness of the sharped image.\n        Sane values are somewhere in the interval ``[0.5, 2.0]``.\n        The value ``0.0`` results in an edge map. Values higher than ``1.0``\n        create bright images. Default value is ``1.0``.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value will be sampled from the\n              interval ``[a, b]`` per image.\n            * If a list, a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, a value will be sampled from that\n              parameter per image.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Sharpen(alpha=(0.0, 1.0))\n\n    Sharpens input images and blends the sharpened image with the input image\n    using a random blending factor between ``0%`` and ``100%`` (uniformly\n    sampled).\n\n    >>> aug = iaa.Sharpen(alpha=(0.0, 1.0), lightness=(0.75, 2.0))\n\n    Sharpens input images with a variable `lightness` sampled uniformly from\n    the interval ``[0.75, 2.0]`` and with a fully random blending factor\n    (as in the above example).\n\n    \"\"\"\n    def __init__(self, alpha=(0.0, 0.2), lightness=(0.8, 1.2),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        alpha_param = iap.handle_continuous_param(\n            alpha, \"alpha\",\n            value_range=(0, 1.0), tuple_to_uniform=True, list_to_choice=True)\n        lightness_param = iap.handle_continuous_param(\n            lightness, \"lightness\",\n            value_range=(0, None), tuple_to_uniform=True, list_to_choice=True)\n\n        matrix_gen = _SharpeningMatrixGenerator(alpha_param, lightness_param)\n\n        super(Sharpen, self).__init__(\n            matrix=matrix_gen,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass _SharpeningMatrixGenerator(object):\n    def __init__(self, alpha, lightness):\n        self.alpha = alpha\n        self.lightness = lightness\n\n    def __call__(self, _image, nb_channels, random_state):\n        alpha_sample = self.alpha.draw_sample(random_state=random_state)\n        assert 0 <= alpha_sample <= 1.0, (\n            \"Expected 'alpha' to be in the interval [0.0, 1.0], \"\n            \"got %.4f.\" % (alpha_sample,))\n        lightness_sample = self.lightness.draw_sample(random_state=random_state)\n        matrix_nochange = np.array([\n            [0, 0, 0],\n            [0, 1, 0],\n            [0, 0, 0]\n        ], dtype=np.float32)\n        matrix_effect = np.array([\n            [-1, -1, -1],\n            [-1, 8+lightness_sample, -1],\n            [-1, -1, -1]\n        ], dtype=np.float32)\n        matrix = (\n            (1-alpha_sample) * matrix_nochange\n            + alpha_sample * matrix_effect\n        )\n        return matrix\n\n\nclass Emboss(Convolve):\n    \"\"\"\n    Emboss images and alpha-blend the result with the original input images.\n\n    The embossed version pronounces highlights and shadows,\n    letting the image look as if it was recreated on a metal plate (\"embossed\").\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.convolutional.Convolve`.\n\n    Parameters\n    ----------\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Blending factor of the embossed image. At ``0.0``, only the original\n        image is visible, at ``1.0`` only its embossed version is visible.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value will be sampled from the\n              interval ``[a, b]`` per image.\n            * If a list, a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, a value will be sampled from that\n              parameter per image.\n\n    strength : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Parameter that controls the strength of the embossing.\n        Sane values are somewhere in the interval ``[0.0, 2.0]`` with ``1.0``\n        being the standard embossing effect. Default value is ``1.0``.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value will be sampled from the\n              interval ``[a, b]`` per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, a value will be sampled from the\n              parameter per image.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Emboss(alpha=(0.0, 1.0), strength=(0.5, 1.5))\n\n    Emboss an image with a strength sampled uniformly from the interval\n    ``[0.5, 1.5]`` and alpha-blend the result with the original input image\n    using a random blending factor between ``0%`` and ``100%``.\n\n    \"\"\"\n    def __init__(self, alpha=(0.0, 1.0), strength=(0.25, 1.0),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        alpha_param = iap.handle_continuous_param(\n            alpha, \"alpha\",\n            value_range=(0, 1.0), tuple_to_uniform=True, list_to_choice=True)\n        strength_param = iap.handle_continuous_param(\n            strength, \"strength\",\n            value_range=(0, None), tuple_to_uniform=True, list_to_choice=True)\n\n        matrix_gen = _EmbossMatrixGenerator(alpha_param, strength_param)\n\n        super(Emboss, self).__init__(\n            matrix=matrix_gen,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass _EmbossMatrixGenerator(object):\n    def __init__(self, alpha, strength):\n        self.alpha = alpha\n        self.strength = strength\n\n    def __call__(self, _image, nb_channels, random_state):\n        alpha_sample = self.alpha.draw_sample(random_state=random_state)\n        assert 0 <= alpha_sample <= 1.0, (\n            \"Expected 'alpha' to be in the interval [0.0, 1.0], \"\n            \"got %.4f.\" % (alpha_sample,))\n        strength_sample = self.strength.draw_sample(random_state=random_state)\n        matrix_nochange = np.array([\n            [0, 0, 0],\n            [0, 1, 0],\n            [0, 0, 0]\n        ], dtype=np.float32)\n        matrix_effect = np.array([\n            [-1-strength_sample, 0-strength_sample, 0],\n            [0-strength_sample, 1, 0+strength_sample],\n            [0, 0+strength_sample, 1+strength_sample]\n        ], dtype=np.float32)\n        matrix = (\n            (1-alpha_sample) * matrix_nochange\n            + alpha_sample * matrix_effect\n        )\n        return matrix\n\n\n# TODO add tests\n# TODO move this to edges.py?\nclass EdgeDetect(Convolve):\n    \"\"\"\n    Generate a black & white edge image and alpha-blend it with the input image.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.convolutional.Convolve`.\n\n    Parameters\n    ----------\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Blending factor of the edge image. At ``0.0``, only the original\n        image is visible, at ``1.0`` only the edge image is visible.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value will be sampled from the\n              interval ``[a, b]`` per image.\n            * If a list, a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, a value will be sampled from that\n              parameter per image.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.EdgeDetect(alpha=(0.0, 1.0))\n\n    Detect edges in an image, mark them as black (non-edge) and white (edges)\n    and alpha-blend the result with the original input image using a random\n    blending factor between ``0%`` and ``100%``.\n\n    \"\"\"\n    def __init__(self, alpha=(0.0, 0.75),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        alpha_param = iap.handle_continuous_param(\n            alpha, \"alpha\",\n            value_range=(0, 1.0), tuple_to_uniform=True, list_to_choice=True)\n\n        matrix_gen = _EdgeDetectMatrixGenerator(alpha_param)\n\n        super(EdgeDetect, self).__init__(\n            matrix=matrix_gen,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass _EdgeDetectMatrixGenerator(object):\n    def __init__(self, alpha):\n        self.alpha = alpha\n\n    def __call__(self, _image, nb_channels, random_state):\n        alpha_sample = self.alpha.draw_sample(random_state=random_state)\n        assert 0 <= alpha_sample <= 1.0, (\n            \"Expected 'alpha' to be in the interval [0.0, 1.0], \"\n            \"got %.4f.\" % (alpha_sample,))\n        matrix_nochange = np.array([\n            [0, 0, 0],\n            [0, 1, 0],\n            [0, 0, 0]\n        ], dtype=np.float32)\n        matrix_effect = np.array([\n            [0, 1, 0],\n            [1, -4, 1],\n            [0, 1, 0]\n        ], dtype=np.float32)\n        matrix = (\n            (1-alpha_sample) * matrix_nochange\n            + alpha_sample * matrix_effect\n        )\n        return matrix\n\n\n# TODO add tests\n# TODO merge EdgeDetect and DirectedEdgeDetect?\n# TODO deprecate and rename to AngledEdgeDetect\n# TODO rename arg \"direction\" to \"angle\"\n# TODO change direction/angle value range to (0, 360)\n# TODO move this to edges.py?\nclass DirectedEdgeDetect(Convolve):\n    \"\"\"\n    Detect edges from specified angles and alpha-blend with the input image.\n\n    This augmenter first detects edges along a certain angle.\n    Usually, edges are detected in x- or y-direction, while here the edge\n    detection kernel is rotated to match a specified angle.\n    The result of applying the kernel is a black (non-edges) and white (edges)\n    image. That image is alpha-blended with the input image.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.convolutional.Convolve`.\n\n    Parameters\n    ----------\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Blending factor of the edge image. At ``0.0``, only the original\n        image is visible, at ``1.0`` only the edge image is visible.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value will be sampled from the\n              interval ``[a, b]`` per image.\n            * If a list, a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, a value will be sampled from that\n              parameter per image.\n\n    direction : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Angle (in degrees) of edges to pronounce, where ``0`` represents\n        ``0`` degrees and ``1.0`` represents 360 degrees (both clockwise,\n        starting at the top). Default value is ``(0.0, 1.0)``, i.e. pick a\n        random angle per image.\n\n            * If a number, exactly that value will always be used.\n            * If a tuple ``(a, b)``, a random value will be sampled from the\n              interval ``[a, b]`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, a value will be sampled from the\n              parameter per image.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.DirectedEdgeDetect(alpha=1.0, direction=0)\n\n    Turn input images into edge images in which edges are detected from\n    the top side of the image (i.e. the top sides of horizontal edges are\n    part of the edge image, while vertical edges are ignored).\n\n    >>> aug = iaa.DirectedEdgeDetect(alpha=1.0, direction=90/360)\n\n    Same as before, but edges are detected from the right. Horizontal edges\n    are now ignored.\n\n    >>> aug = iaa.DirectedEdgeDetect(alpha=1.0, direction=(0.0, 1.0))\n\n    Same as before, but edges are detected from a random angle sampled\n    uniformly from the interval ``[0deg, 360deg]``.\n\n    >>> aug = iaa.DirectedEdgeDetect(alpha=(0.0, 0.3), direction=0)\n\n    Similar to the previous examples, but here the edge image is alpha-blended\n    with the input image. The result is a mixture between the edge image and\n    the input image. The blending factor is randomly sampled between ``0%``\n    and ``30%``.\n\n    \"\"\"\n    def __init__(self, alpha=(0.0, 0.75), direction=(0.0, 1.0),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        alpha_param = iap.handle_continuous_param(\n            alpha, \"alpha\",\n            value_range=(0, 1.0), tuple_to_uniform=True, list_to_choice=True)\n        direction_param = iap.handle_continuous_param(\n            direction, \"direction\",\n            value_range=None, tuple_to_uniform=True, list_to_choice=True)\n\n        matrix_gen = _DirectedEdgeDetectMatrixGenerator(alpha_param,\n                                                        direction_param)\n\n        super(DirectedEdgeDetect, self).__init__(\n            matrix=matrix_gen,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass _DirectedEdgeDetectMatrixGenerator(object):\n    def __init__(self, alpha, direction):\n        self.alpha = alpha\n        self.direction = direction\n\n    def __call__(self, _image, nb_channels, random_state):\n        alpha_sample = self.alpha.draw_sample(random_state=random_state)\n        assert 0 <= alpha_sample <= 1.0, (\n            \"Expected 'alpha' to be in the interval [0.0, 1.0], \"\n            \"got %.4f.\" % (alpha_sample,))\n        direction_sample = self.direction.draw_sample(random_state=random_state)\n\n        deg = int(direction_sample * 360) % 360\n        rad = np.deg2rad(deg)\n        x = np.cos(rad - 0.5*np.pi)\n        y = np.sin(rad - 0.5*np.pi)\n        direction_vector = np.array([x, y])\n\n        matrix_effect = np.array([\n            [0, 0, 0],\n            [0, 0, 0],\n            [0, 0, 0]\n        ], dtype=np.float32)\n        for x, y in itertools.product([-1, 0, 1], [-1, 0, 1]):\n            if (x, y) != (0, 0):\n                cell_vector = np.array([x, y])\n                distance_deg = np.rad2deg(\n                    ia.angle_between_vectors(cell_vector,\n                                             direction_vector))\n                distance = distance_deg / 180\n                similarity = (1 - distance)**4\n                matrix_effect[y+1, x+1] = similarity\n        matrix_effect = matrix_effect / np.sum(matrix_effect)\n        matrix_effect = matrix_effect * (-1)\n        matrix_effect[1, 1] = 1\n\n        matrix_nochange = np.array([\n            [0, 0, 0],\n            [0, 1, 0],\n            [0, 0, 0]\n        ], dtype=np.float32)\n\n        matrix = (\n            (1-alpha_sample) * matrix_nochange\n            + alpha_sample * matrix_effect\n        )\n\n        return matrix\n"
  },
  {
    "path": "imgaug/augmenters/debug.py",
    "content": "\"\"\"Augmenters that help with debugging.\n\nList of augmenters:\n\n    * :class:`SaveDebugImageEveryNBatches`\n\nAdded in 0.4.0.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nfrom abc import ABCMeta, abstractmethod, abstractproperty\n\nimport os\nimport collections\n\nimport six\nimport numpy as np\nimport imageio\n\nimport imgaug as ia\nfrom .. import dtypes as iadt\nfrom . import meta\nfrom . import size as sizelib\nfrom . import blend as blendlib\n\n_COLOR_PINK = (255, 192, 203)\n_COLOR_GRID_BACKGROUND = _COLOR_PINK\n\n\ndef _resizepad_to_size(image, size, cval):\n    \"\"\"Resize and pad and image to given size.\n\n    This first resizes until one image size matches one size in `size` (while\n    retaining the aspect ratio).\n    Then it pads the other side until both sides match `size`.\n\n    Added in 0.4.0.\n\n    \"\"\"\n    # resize to height H and width W while keeping aspect ratio\n    height = size[0]\n    width = size[1]\n    height_im = image.shape[0]\n    width_im = image.shape[1]\n    aspect_ratio_im = width_im / height_im\n\n    # we know that height_im <= height and width_im <= width\n    height_diff = height - height_im\n    width_diff = width - width_im\n    if height_diff < width_diff:\n        height_im_rs = height\n        width_im_rs = height * aspect_ratio_im\n    else:\n        height_im_rs = width / aspect_ratio_im\n        width_im_rs = width\n\n    height_im_rs = max(int(np.round(height_im_rs)), 1)\n    width_im_rs = max(int(np.round(width_im_rs)), 1)\n\n    image_rs = ia.imresize_single_image(image, (height_im_rs, width_im_rs))\n\n    # pad to remaining size\n    pad_y = height - height_im_rs\n    pad_x = width - width_im_rs\n    pad_top = int(np.floor(pad_y / 2))\n    pad_right = int(np.ceil(pad_x / 2))\n    pad_bottom = int(np.ceil(pad_y / 2))\n    pad_left = int(np.floor(pad_x / 2))\n\n    image_rs_pad = sizelib.pad(image_rs,\n                               top=pad_top, right=pad_right,\n                               bottom=pad_bottom, left=pad_left,\n                               cval=cval)\n\n    paddings = (pad_top, pad_right, pad_bottom, pad_left)\n    return image_rs_pad, (height_im_rs, width_im_rs), paddings\n\n\n# TODO rename to Grid\n@six.add_metaclass(ABCMeta)\nclass _IDebugGridCell(object):\n    \"\"\"A single cell within a debug image's grid.\n\n    Usually corresponds to one image, but can also be e.g. a title/description.\n\n    Added in 0.4.0.\n\n    \"\"\"\n\n    @abstractproperty\n    def min_width(self):\n        \"\"\"Minimum width in pixels that the cell requires.\n\n        Added in 0.4.0.\n\n        \"\"\"\n\n    @abstractproperty\n    def min_height(self):\n        \"\"\"Minimum height in pixels that the cell requires.\n\n        Added in 0.4.0.\n\n        \"\"\"\n\n    @abstractmethod\n    def draw(self, height, width):\n        \"\"\"Draw the debug image grid cell's content.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        height : int\n            Expected height of the drawn cell image/array.\n\n        width : int\n            Expected width of the drawn cell image/array.\n\n        Returns\n        -------\n        ndarray\n            ``(H,W,3)`` Image.\n\n        \"\"\"\n\n\nclass _DebugGridBorderCell(_IDebugGridCell):\n    \"\"\"Helper to add a border around a cell within the debug image grid.\n\n    Added in 0.4.0.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, size, color, child):\n        self.size = size\n        self.color = color\n        self.child = child\n\n    # Added in 0.4.0.\n    @property\n    def min_height(self):\n        return self.child.min_height\n\n    # Added in 0.4.0.\n    @property\n    def min_width(self):\n        return self.child.min_width\n\n    # Added in 0.4.0.\n    def draw(self, height, width):\n        content = self.child.draw(height, width)\n        content = sizelib.pad(content,\n                              top=self.size, right=self.size,\n                              bottom=self.size, left=self.size,\n                              mode=\"constant\", cval=self.color)\n        return content\n\n\nclass _DebugGridTextCell(_IDebugGridCell):\n    \"\"\"Cell containing text.\n\n    Added in 0.4.0.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, text):\n        self.text = text\n\n    # Added in 0.4.0.\n    @property\n    def min_height(self):\n        return max(20, len(self.text.split(\"\\n\")) * 17)\n\n    # Added in 0.4.0.\n    @property\n    def min_width(self):\n        lines = self.text.split(\"\\n\")\n        if len(lines) == 0:\n            return 20\n        return max(20, int(7 * max([len(line) for line in lines])))\n\n    # Added in 0.4.0.\n    def draw(self, height, width):\n        image = np.full((height, width, 3), 255, dtype=np.uint8)\n        image = ia.draw_text(image, 0, 0, self.text, color=(0, 0, 0),\n                             size=12)\n        return image\n\n\nclass _DebugGridImageCell(_IDebugGridCell):\n    \"\"\"Cell containing an image, possibly with an different-shaped overlay.\n\n    Added in 0.4.0.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, image, overlay=None, overlay_alpha=0.75):\n        self.image = image\n        self.overlay = overlay\n        self.overlay_alpha = overlay_alpha\n\n    # Added in 0.4.0.\n    @property\n    def min_height(self):\n        return self.image.shape[0]\n\n    # Added in 0.4.0.\n    @property\n    def min_width(self):\n        return self.image.shape[1]\n\n    # Added in 0.4.0.\n    def draw(self, height, width):\n        image = self.image\n        kind = image.dtype.kind\n        if kind == \"b\":\n            image = image.astype(np.uint8) * 255\n        elif kind == \"u\":\n            min_value, _, max_value = iadt.get_value_range_of_dtype(image.dtype)\n            image = image.astype(np.float64) / max_value\n        elif kind == \"i\":\n            min_value, _, max_value = iadt.get_value_range_of_dtype(image.dtype)\n            dynamic_range = (max_value - min_value)\n            image = (min_value + image.astype(np.float64)) / dynamic_range\n\n        if image.dtype.kind == \"f\":\n            image = (np.clip(image, 0, 1.0) * 255).astype(np.uint8)\n\n        image_rsp, size_rs, paddings = _resizepad_to_size(\n            image, (height, width), cval=_COLOR_GRID_BACKGROUND)\n\n        blend = image_rsp\n        if self.overlay is not None:\n            overlay_rs = self._resize_overlay(self.overlay,\n                                              image.shape[0:2])\n            overlay_rsp = self._resize_overlay(overlay_rs, size_rs)\n            overlay_rsp = sizelib.pad(overlay_rsp,\n                                      top=paddings[0], right=paddings[1],\n                                      bottom=paddings[2], left=paddings[3],\n                                      cval=_COLOR_GRID_BACKGROUND)\n\n            blend = blendlib.blend_alpha(overlay_rsp, image_rsp,\n                                         alpha=self.overlay_alpha)\n\n        return blend\n\n    # Added in 0.4.0.\n    @classmethod\n    def _resize_overlay(cls, arr, size):\n        arr_rs = ia.imresize_single_image(arr, size, interpolation=\"nearest\")\n        return arr_rs\n\n\nclass _DebugGridCBAsOICell(_IDebugGridCell):\n    \"\"\"Cell visualizing a coordinate-based augmentable.\n\n    CBAsOI = coordinate-based augmentables on images,\n    e.g. ``KeypointsOnImage``.\n\n    Added in 0.4.0.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, cbasoi, image):\n        self.cbasoi = cbasoi\n        self.image = image\n\n    # Added in 0.4.0.\n    @property\n    def min_height(self):\n        return self.image.shape[0]\n\n    # Added in 0.4.0.\n    @property\n    def min_width(self):\n        return self.image.shape[1]\n\n    # Added in 0.4.0.\n    def draw(self, height, width):\n        image_rsp, size_rs, paddings = _resizepad_to_size(\n            self.image, (height, width), cval=_COLOR_GRID_BACKGROUND)\n\n        cbasoi = self.cbasoi.deepcopy()\n        cbasoi = cbasoi.on_(size_rs)\n        cbasoi = cbasoi.shift_(y=paddings[0], x=paddings[3])\n        cbasoi.shape = image_rsp.shape\n\n        return cbasoi.draw_on_image(image_rsp)\n\n\nclass _DebugGridColumn(object):\n    \"\"\"A single column within the debug image grid.\n\n    Added in 0.4.0.\n\n    \"\"\"\n\n    def __init__(self, cells):\n        self.cells = cells\n\n    @property\n    def nb_rows(self):\n        \"\"\"Number of rows in the column, i.e. examples in batch.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        return len(self.cells)\n\n    @property\n    def max_cell_width(self):\n        \"\"\"Width in pixels of the widest cell in the column.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        return max([cell.min_width for cell in self.cells])\n\n    @property\n    def max_cell_height(self):\n        \"\"\"Height in pixels of the tallest cell in the column.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        return max([cell.min_height for cell in self.cells])\n\n    def draw(self, heights):\n        \"\"\"Convert this column to an image array.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        width = self.max_cell_width\n        return np.vstack([cell.draw(height=height, width=width)\n                          for cell, height\n                          in zip(self.cells, heights)])\n\n\nclass _DebugGrid(object):\n    \"\"\"A debug image grid.\n\n    Columns correspond to the input datatypes (e.g. images, bounding boxes).\n    Rows correspond to the examples within a batch.\n\n    Added in 0.4.0.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, columns):\n        assert len(columns) > 0\n        self.columns = columns\n\n    def draw(self):\n        \"\"\"Convert this grid to an image array.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        nb_rows_by_col = [column.nb_rows for column in self.columns]\n        assert len(set(nb_rows_by_col)) == 1\n        rowwise_heights = np.zeros((self.columns[0].nb_rows,), dtype=np.int32)\n        for column in self.columns:\n            heights = [cell.min_height for cell in column.cells]\n            rowwise_heights = np.maximum(rowwise_heights, heights)\n        return np.hstack([column.draw(heights=rowwise_heights)\n                          for column in self.columns])\n\n\n# TODO image subtitles\n# TODO run start date\n# TODO main process id, process id\n# TODO warning if map aspect ratio is different from image aspect ratio\n# TODO error if non-image shapes differ from image shapes\ndef draw_debug_image(images, heatmaps=None, segmentation_maps=None,\n                     keypoints=None, bounding_boxes=None, polygons=None,\n                     line_strings=None):\n    \"\"\"Generate a debug image grid of a single batch and various datatypes.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; tested\n        * ``uint16``: ?\n        * ``uint32``: ?\n        * ``uint64``: ?\n        * ``int8``: ?\n        * ``int16``: ?\n        * ``int32``: ?\n        * ``int64``: ?\n        * ``float16``: ?\n        * ``float32``: ?\n        * ``float64``: ?\n        * ``float128``: ?\n        * ``bool``: ?\n\n    Parameters\n    ----------\n    images : ndarray or list of ndarray\n        Images in the batch. Must always be provided. Batches without images\n        cannot be visualized.\n\n    heatmaps : None or list of imgaug.augmentables.heatmaps.HeatmapsOnImage, optional\n        Heatmaps on the provided images.\n\n    segmentation_maps : None or list of imgaug.augmentables.segmaps.SegmentationMapsOnImage, optional\n        Segmentation maps on the provided images.\n\n    keypoints : None or list of imgaug.augmentables.kps.KeypointsOnImage, optional\n        Keypoints on the provided images.\n\n    bounding_boxes : None or list of imgaug.augmentables.bbs.BoundingBoxesOnImage, optional\n        Bounding boxes on the provided images.\n\n    polygons : None or list of imgaug.augmentables.polys.PolygonsOnImage, optional\n        Polygons on the provided images.\n\n    line_strings : None or list of imgaug.augmentables.lines.LineStringsOnImage, optional\n        Line strings on the provided images.\n\n    Returns\n    -------\n    ndarray\n        Visualized batch as RGB image.\n\n    Examples\n    --------\n    >>> import numpy as np\n    >>> import imgaug.augmenters as iaa\n    >>> image = np.zeros((64, 64, 3), dtype=np.uint8)\n    >>> debug_image = iaa.draw_debug_image(images=[image, image])\n\n    Generate a debug image for two empty images.\n\n    >>> from imgaug.augmentables.kps import KeypointsOnImage\n    >>> kpsoi = KeypointsOnImage.from_xy_array([(10.5, 20.5), (30.5, 30.5)],\n    >>>                                        shape=image.shape)\n    >>> debug_image = iaa.draw_debug_image(images=[image, image],\n    >>>                                    keypoints=[kpsoi, kpsoi])\n\n    Generate a debug image for two empty images, each having two keypoints\n    drawn on them.\n\n    >>> from imgaug.augmentables.batches import UnnormalizedBatch\n    >>> segmap_arr = np.zeros((32, 32, 1), dtype=np.int32)\n    >>> kp_tuples = [(10.5, 20.5), (30.5, 30.5)]\n    >>> batch = UnnormalizedBatch(images=[image, image],\n    >>>                           segmentation_maps=[segmap_arr, segmap_arr],\n    >>>                           keypoints=[kp_tuples, kp_tuples])\n    >>> batch = batch.to_normalized_batch()\n    >>> debug_image = iaa.draw_debug_image(\n    >>>     images=batch.images_unaug,\n    >>>     segmentation_maps=batch.segmentation_maps_unaug,\n    >>>     keypoints=batch.keypoints_unaug)\n\n    Generate a debug image for two empty images, each having an empty\n    segmentation map and two keypoints drawn on them. This example uses\n    ``UnnormalizedBatch`` to show how to mostly evade going through imgaug\n    classes.\n\n    \"\"\"\n    columns = [_create_images_column(images)]\n\n    if heatmaps is not None:\n        columns.extend(_create_heatmaps_columns(heatmaps, images))\n\n    if segmentation_maps is not None:\n        columns.extend(_create_segmap_columns(segmentation_maps, images))\n\n    if keypoints is not None:\n        columns.append(_create_cbasois_column(keypoints, images, \"Keypoints\"))\n\n    if bounding_boxes is not None:\n        columns.append(_create_cbasois_column(bounding_boxes, images,\n                                              \"Bounding Boxes\"))\n\n    if polygons is not None:\n        columns.append(_create_cbasois_column(polygons, images, \"Polygons\"))\n\n    if line_strings is not None:\n        columns.append(_create_cbasois_column(line_strings, images,\n                                              \"Line Strings\"))\n\n    result = _DebugGrid(columns)\n    result = result.draw()\n    result = sizelib.pad(result, top=1, right=1, bottom=1, left=1,\n                         mode=\"constant\", cval=_COLOR_GRID_BACKGROUND)\n    return result\n\n\n# Added in 0.4.0.\ndef _add_borders(cells):\n    \"\"\"Add a border (cell) around a cell.\"\"\"\n    return [_DebugGridBorderCell(1, _COLOR_GRID_BACKGROUND, cell)\n            for cell in cells]\n\n\n# Added in 0.4.0.\ndef _add_text_cell(title, cells):\n    \"\"\"Add a text cell before other cells.\"\"\"\n    return [_DebugGridTextCell(title)] + cells\n\n\n# Added in 0.4.0.\ndef _create_images_column(images):\n    \"\"\"Create columns for image data.\"\"\"\n    cells = [_DebugGridImageCell(image) for image in images]\n    images_descr = _generate_images_description(images)\n    column = _DebugGridColumn(\n        _add_borders(\n            _add_text_cell(\n                \"Images\",\n                _add_text_cell(\n                    images_descr,\n                    cells)\n            )\n        )\n    )\n    return column\n\n\n# Added in 0.4.0.\ndef _create_heatmaps_columns(heatmaps, images):\n    \"\"\"Create columns for heatmap data.\"\"\"\n    nb_map_channels = max([heatmap.arr_0to1.shape[2]\n                           for heatmap in heatmaps])\n    columns = [[] for _ in np.arange(nb_map_channels)]\n    for image, heatmap in zip(images, heatmaps):\n        heatmap_drawn = heatmap.draw()\n        for c, heatmap_drawn_c in enumerate(heatmap_drawn):\n            columns[c].append(\n                _DebugGridImageCell(image, overlay=heatmap_drawn_c))\n\n    columns = [\n        _DebugGridColumn(\n            _add_borders(\n                _add_text_cell(\n                    \"Heatmaps\",\n                    _add_text_cell(\n                        _generate_heatmaps_description(\n                            heatmaps,\n                            channel_idx=c,\n                            show_details=(c == 0)),\n                        cells)\n                )\n            )\n        )\n        for c, cells in enumerate(columns)\n    ]\n    return columns\n\n\n# Added in 0.4.0.\ndef _create_segmap_columns(segmentation_maps, images):\n    \"\"\"Create columns for segmentation map data.\"\"\"\n    nb_map_channels = max([segmap.arr.shape[2]\n                           for segmap in segmentation_maps])\n    columns = [[] for _ in np.arange(nb_map_channels)]\n    for image, segmap in zip(images, segmentation_maps):\n        # TODO this currently draws the background in black, hence the\n        #      resulting blended image is dark at class id 0\n        segmap_drawn = segmap.draw()\n        for c, segmap_drawn_c in enumerate(segmap_drawn):\n            columns[c].append(\n                _DebugGridImageCell(image, overlay=segmap_drawn_c))\n\n    columns = [\n        _DebugGridColumn(\n            _add_borders(\n                _add_text_cell(\n                    \"SegMaps\",\n                    _add_text_cell(\n                        _generate_segmaps_description(\n                            segmentation_maps,\n                            channel_idx=c,\n                            show_details=(c == 0)),\n                        cells\n                    )\n                )\n            )\n        )\n        for c, cells in enumerate(columns)\n    ]\n\n    return columns\n\n\n# Added in 0.4.0.\ndef _create_cbasois_column(cbasois, images, column_name):\n    \"\"\"Create a column for coordinate-based augmentables.\"\"\"\n    cells = [_DebugGridCBAsOICell(cbasoi, image)\n             for cbasoi, image\n             in zip(cbasois, images)]\n    descr = _generate_cbasois_description(cbasois, images)\n    column = _DebugGridColumn(\n        _add_borders(\n            _add_text_cell(\n                column_name,\n                _add_text_cell(descr, cells)\n            )\n        )\n    )\n    return column\n\n\n# Added in 0.4.0.\ndef _generate_images_description(images):\n    \"\"\"Generate description for image columns.\"\"\"\n    if ia.is_np_array(images):\n        shapes_str = \"array, shape %11s\" % (str(images.shape),)\n        dtypes_str = \"dtype %8s\" % (images.dtype.name,)\n        if len(images) == 0:\n            value_range_str = \"\"\n        elif images.dtype.kind in [\"u\", \"i\", \"b\"]:\n            value_range_str = \"value range: %3d to %3d\" % (\n                np.min(images), np.max(images))\n        else:\n            value_range_str = \"value range: %7.4f to %7.4f\" % (\n                np.min(images), np.max(images))\n    else:\n        stats = _ListOfArraysStats(images)\n\n        if stats.empty:\n            shapes_str = \"\"\n        elif stats.all_same_shape:\n            shapes_str = (\n                \"list of %3d arrays\\n\"\n                \"all shape %11s\"\n            ) % (len(images), stats.shapes[0],)\n        else:\n            shapes_str = (\n                \"list of %3d arrays\\n\"\n                \"varying shapes\\n\"\n                \"smallest image: %11s\\n\"\n                \"largest image: %11s\\n\"\n                \"height: %3d to %3d\\n\"\n                \"width: %3d to %3d\\n\"\n                \"channels: %1s to %1s\"\n            ) % (len(images),\n                 stats.smallest_shape, stats.largest_shape,\n                 stats.height_min, stats.height_max,\n                 stats.width_min, stats.width_max,\n                 stats.get_channels_min(\"None\"),\n                 stats.get_channels_max(\"None\"))\n\n        if stats.empty:\n            dtypes_str = \"\"\n        elif stats.all_same_dtype:\n            dtypes_str = \"all dtype %8s\" % (stats.dtypes[0],)\n        else:\n            dtypes_str = \"dtypes: %s\" % (\", \".join(stats.unique_dtype_names),)\n\n        if stats.empty:\n            value_range_str = \"\"\n        else:\n            value_range_str = \"value range: %3d to %3d\"\n            if not stats.all_dtypes_intlike:\n                value_range_str = \"value range: %6.4f to %6.4f\"\n            value_range_str = value_range_str % (stats.value_min,\n                                                 stats.value_max)\n\n    strs = [shapes_str, dtypes_str, value_range_str]\n    return _join_description_strs(strs)\n\n\n# Added in 0.4.0.\ndef _generate_segmaps_description(segmaps, channel_idx, show_details):\n    \"\"\"Generate description for segmap columns.\"\"\"\n    if len(segmaps) == 0:\n        return \"empty list\"\n\n    strs = _generate_sm_hm_description(segmaps, channel_idx, show_details)\n\n    arrs_channel = [segmap.arr[:, :, channel_idx] for segmap in segmaps]\n    stats_channel = _ListOfArraysStats(arrs_channel)\n    value_range_str = (\n        \"value range: %3d to %3d\\n\"\n        \"number of unique classes: %2d\"\n    ) % (stats_channel.value_min, stats_channel.value_max,\n         stats_channel.nb_unique_values)\n\n    return _join_description_strs(strs + [value_range_str])\n\n\n# Added in 0.4.0.\ndef _generate_heatmaps_description(heatmaps, channel_idx, show_details):\n    \"\"\"Generate description for heatmap columns.\"\"\"\n    if len(heatmaps) == 0:\n        return \"empty list\"\n\n    strs = _generate_sm_hm_description(heatmaps, channel_idx, show_details)\n\n    arrs_channel = [heatmap.arr_0to1[:, :, channel_idx] for heatmap in heatmaps]\n    stats_channel = _ListOfArraysStats(arrs_channel)\n    value_range_str = (\n        \"value range: %6.4f to %6.4f\\n\"\n        \"    (internal, max is [0.0, 1.0])\"\n    ) % (stats_channel.value_min, stats_channel.value_max)\n\n    return _join_description_strs(strs + [value_range_str])\n\n\n# Added in 0.4.0.\ndef _generate_sm_hm_description(augmentables, channel_idx, show_details):\n    \"\"\"Generate description for SegMap/Heatmap columns.\"\"\"\n    if augmentables is None:\n        return \"\"\n    if len(augmentables) == 0:\n        return \"empty list\"\n\n    arrs = [augmentable.get_arr() for augmentable in augmentables]\n    stats = _ListOfArraysStats(arrs)\n\n    if stats.get_channels_max(-1) > -1:\n        channel_str = \"Channel %1d of %1d\" % (channel_idx+1,\n                                              stats.get_channels_max(-1))\n    else:\n        channel_str = \"\"\n\n    if not show_details:\n        shapes_str = \"\"\n    elif stats.all_same_shape:\n        shapes_str = (\n            \"items for %3d images\\n\"\n            \"all arrays of shape %11s\"\n        ) % (len(augmentables), stats.shapes[0],)\n    else:\n        shapes_str = (\n            \"items for %3d images\\n\"\n            \"varying array shapes\\n\"\n            \"smallest: %11s\\n\"\n            \"largest: %11s\\n\"\n            \"height: %3d to %3d\\n\"\n            \"width: %3d to %3d\\n\"\n            \"channels: %1s to %1s\"\n        ) % (len(augmentables),\n             stats.smallest_shape, stats.largest_shape,\n             stats.height_min, stats.height_max,\n             stats.width_min, stats.width_max,\n             stats.get_channels_min(\"None\"),\n             stats.get_channels_max(\"None\"))\n\n    if not show_details:\n        on_shapes_str = \"\"\n    else:\n        on_shapes_str = _generate_on_image_shapes_descr(augmentables)\n\n    return [channel_str, shapes_str, on_shapes_str]\n\n\n# Added in 0.4.0.\ndef _generate_cbasois_description(cbasois, images):\n    \"\"\"Generate description for coordinate-based augmentable columns.\"\"\"\n    images_str = \"items for %d images\" % (len(cbasois),)\n\n    nb_items_lst = [len(cbasoi.items) for cbasoi in cbasois]\n    nb_items_lst = nb_items_lst if len(cbasois) > 0 else [-1]\n    nb_items = sum(nb_items_lst)\n    items_str = (\n        \"fewest items on image: %3d\\n\"\n        \"most items on image: %3d\\n\"\n        \"total items: %6d\"\n    ) % (min(nb_items_lst), max(nb_items_lst), nb_items)\n\n    areas = [\n        cba.area if hasattr(cba, \"area\") else -1\n        for cbasoi in cbasois\n        for cba in cbasoi.items]\n    areas = areas if len(cbasois) > 0 else [-1]\n    areas_str = (\n        \"smallest area: %7.4f\\n\"\n        \"largest area: %7.4f\"\n    ) % (min(areas), max(areas))\n\n    labels = list(ia.flatten([item.label if hasattr(item, \"label\") else None\n                              for cbasoi in cbasois\n                              for item in cbasoi.items]))\n    labels_ctr = collections.Counter(labels)\n    labels_most_common = []\n    for label, count in labels_ctr.most_common(10):\n        labels_most_common.append(\"\\n    - %s (%3d, %6.2f%%)\" % (\n            label, count, count/nb_items * 100))\n    labels_str = (\n        \"unique labels: %2d\\n\"\n        \"most common labels:\"\n        \"%s\"\n    ) % (len(labels_ctr.keys()), \"\".join(labels_most_common))\n\n    coords_ooi = []\n    dists = []\n    for cbasoi, image in zip(cbasois, images):\n        h, w = image.shape[0:2]\n        for cba in cbasoi.items:\n            coords = cba.coords\n            for coord in coords:\n                x, y = coord\n                dist = (x - w/2)**2 + (y - h/2) ** 2\n                coords_ooi.append(not (0 <= x < w and 0 <= y < h))\n                dists.append(((x, y), dist))\n\n    # use x_ and y_ because otherwise we get a 'redefines x' error in pylint\n    coords_extreme = [(x_, y_)\n                      for (x_, y_), _\n                      in sorted(dists, key=lambda t: t[1])]\n\n    nb_ooi = sum(coords_ooi)\n    ooi_str = (\n        \"coords out of image: %d (%6.2f%%)\\n\"\n        \"most extreme coord: (%5.1f, %5.1f)\"\n        # TODO \"items anyhow out of image: %d (%.2f%%)\\n\"\n        # TODO \"items fully out of image: %d (%.2f%%)\\n\"\n    ) % (nb_ooi, nb_ooi / len(coords_ooi) * 100,\n         coords_extreme[-1][0], coords_extreme[-1][1])\n\n    on_shapes_str = _generate_on_image_shapes_descr(cbasois)\n\n    return _join_description_strs([images_str, items_str, areas_str,\n                                   labels_str, ooi_str, on_shapes_str])\n\n\n# Added in 0.4.0.\ndef _generate_on_image_shapes_descr(augmentables):\n    \"\"\"Generate text block for non-image data describing their image shapes.\"\"\"\n    on_shapes = [augmentable.shape for augmentable in augmentables]\n    stats_imgs = _ListOfArraysStats([np.empty(on_shape)\n                                     for on_shape in on_shapes])\n    if stats_imgs.all_same_shape:\n        on_shapes_str = \"all on image shape %11s\" % (stats_imgs.shapes[0],)\n    else:\n        on_shapes_str = (\n            \"on varying image shapes\\n\"\n            \"smallest image: %11s\\n\"\n            \"largest image: %11s\"\n        ) % (stats_imgs.smallest_shape, stats_imgs.largest_shape)\n    return on_shapes_str\n\n\n# Added in 0.4.0.\ndef _join_description_strs(strs):\n    \"\"\"Join lines to a single string while removing empty lines.\"\"\"\n    strs = [str_i for str_i in strs if len(str_i) > 0]\n    return \"\\n\".join(strs)\n\n\nclass _ListOfArraysStats(object):\n    \"\"\"Class to derive aggregated values from a list of arrays.\n\n    E.g. shape of the largest array, number of unique dtypes etc.\n\n    Added in 0.4.0.\n\n    \"\"\"\n\n    def __init__(self, arrays):\n        self.arrays = arrays\n\n    # Added in 0.4.0.\n    @property\n    def empty(self):\n        return len(self.arrays) == 0\n\n    # Added in 0.4.0.\n    @property\n    def areas(self):\n        return [np.prod(arr.shape[0:2]) for arr in self.arrays]\n\n    # Added in 0.4.0.\n    @property\n    def arrays_by_area(self):\n        arrays_by_area = [\n            arr for arr, _\n            in sorted(zip(self.arrays, self.areas), key=lambda t: t[1])\n        ]\n        return arrays_by_area\n\n    # Added in 0.4.0.\n    @property\n    def shapes(self):\n        return [arr.shape for arr in self.arrays]\n\n    # Added in 0.4.0.\n    @property\n    def all_same_shape(self):\n        if self.empty:\n            return True\n        return len(set(self.shapes)) == 1\n\n    # Added in 0.4.0.\n    @property\n    def smallest_shape(self):\n        if self.empty:\n            return tuple()\n        return self.arrays_by_area[0].shape\n\n    # Added in 0.4.0.\n    @property\n    def largest_shape(self):\n        if self.empty:\n            return tuple()\n        return self.arrays_by_area[-1].shape\n\n    # Added in 0.4.0.\n    @property\n    def area_max(self):\n        if self.empty:\n            return tuple()\n        return np.prod(self.arrays_by_area[-1][0:2])\n\n    # Added in 0.4.0.\n    @property\n    def heights(self):\n        return [arr.shape[0] for arr in self.arrays]\n\n    # Added in 0.4.0.\n    @property\n    def height_min(self):\n        heights = self.heights\n        return min(heights) if len(heights) > 0 else 0\n\n    # Added in 0.4.0.\n    @property\n    def height_max(self):\n        heights = self.heights\n        return max(heights) if len(heights) > 0 else 0\n\n    # Added in 0.4.0.\n    @property\n    def widths(self):\n        return [arr.shape[1] for arr in self.arrays]\n\n    # Added in 0.4.0.\n    @property\n    def width_min(self):\n        widths = self.widths\n        return min(widths) if len(widths) > 0 else 0\n\n    # Added in 0.4.0.\n    @property\n    def width_max(self):\n        widths = self.widths\n        return max(widths) if len(widths) > 0 else 0\n\n    # Added in 0.4.0.\n    def get_channels_min(self, default):\n        if self.empty:\n            return -1\n        if any([arr.ndim == 2 for arr in self.arrays]):\n            return default\n        return min([arr.shape[2] for arr in self.arrays if arr.ndim > 2])\n\n    # Added in 0.4.0.\n    def get_channels_max(self, default):\n        if self.empty:\n            return -1\n        if not any([arr.ndim > 2 for arr in self.arrays]):\n            return default\n        return max([arr.shape[2] for arr in self.arrays if arr.ndim > 2])\n\n    # Added in 0.4.0.\n    @property\n    def dtypes(self):\n        return [arr.dtype for arr in self.arrays]\n\n    # Added in 0.4.0.\n    @property\n    def dtype_names(self):\n        return [dtype.name for dtype in self.dtypes]\n\n    # Added in 0.4.0.\n    @property\n    def all_same_dtype(self):\n        return len(set(self.dtype_names)) in [0, 1]\n\n    # Added in 0.4.0.\n    @property\n    def all_dtypes_intlike(self):\n        if self.empty:\n            return True\n        return all([arr.dtype.kind in [\"u\", \"i\", \"b\"] for arr in self.arrays])\n\n    # Added in 0.4.0.\n    @property\n    def unique_dtype_names(self):\n        return sorted(list({arr.dtype.name for arr in self.arrays}))\n\n    # Added in 0.4.0.\n    @property\n    def value_min(self):\n        return min([np.min(arr) for arr in self.arrays])\n\n    # Added in 0.4.0.\n    @property\n    def value_max(self):\n        return max([np.max(arr) for arr in self.arrays])\n\n    # Added in 0.4.0.\n    @property\n    def nb_unique_values(self):\n        values_uq = set()\n        for arr in self.arrays:\n            values_uq.update(np.unique(arr))\n        return len(values_uq)\n\n\n# Added in 0.4.0.\n@six.add_metaclass(ABCMeta)\nclass _IImageDestination(object):\n    \"\"\"A destination which receives images to save.\"\"\"\n\n    def on_batch(self, batch):\n        \"\"\"Signal to the destination that a new batch is processed.\n\n        This is intended to be used by the destination e.g. to count batches.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        batch : imgaug.augmentables.batches._BatchInAugmentation\n            A batch to which the next ``receive()`` call may correspond.\n\n        \"\"\"\n\n    def receive(self, image):\n        \"\"\"Receive and handle an image.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        image : ndarray\n            Image to be handled by the destination.\n\n        \"\"\"\n\n\n# Added in 0.4.0.\nclass _MultiDestination(_IImageDestination):\n    \"\"\"A list of multiple destinations behaving like a single one.\"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, destinations):\n        self.destinations = destinations\n\n    # Added in 0.4.0.\n    def on_batch(self, batch):\n        for destination in self.destinations:\n            destination.on_batch(batch)\n\n    # Added in 0.4.0.\n    def receive(self, image):\n        for destination in self.destinations:\n            destination.receive(image)\n\n\n# Added in 0.4.0.\nclass _FolderImageDestination(_IImageDestination):\n    \"\"\"A destination which saves images to a directory.\"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, folder_path,\n                 filename_pattern=\"batch_{batch_id:06d}.png\"):\n        super(_FolderImageDestination, self).__init__()\n        self.folder_path = folder_path\n        self.filename_pattern = filename_pattern\n        self._batch_id = -1\n        self._filepath = None\n\n    # Added in 0.4.0.\n    def on_batch(self, batch):\n        self._batch_id += 1\n        self._filepath = os.path.join(\n            self.folder_path,\n            self.filename_pattern.format(batch_id=self._batch_id))\n\n    # Added in 0.4.0.\n    def receive(self, image):\n        imageio.imwrite(self._filepath, image)\n\n\n# Added in 0.4.0.\n@six.add_metaclass(ABCMeta)\nclass _IBatchwiseSchedule(object):\n    \"\"\"A schedule determining per batch whether a condition is met.\"\"\"\n\n    def on_batch(self, batch):\n        \"\"\"Determine for the given batch whether the condition is met.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        batch : _BatchInAugmentation\n            Batch for which to evaluate the condition.\n\n        Returns\n        -------\n        bool\n            Signal whether the condition is met.\n\n        \"\"\"\n\n\n# Added in 0.4.0.\nclass _EveryNBatchesSchedule(_IBatchwiseSchedule):\n    \"\"\"A schedule that generates a signal at every ``N`` th batch.\n\n    This schedule must be called for *every* batch in order to count them.\n\n    Added in 0.4.0.\n\n    \"\"\"\n\n    def __init__(self, interval):\n        self.interval = interval\n        self._batch_id = -1\n\n    # Added in 0.4.0.\n    def on_batch(self, batch):\n        self._batch_id += 1\n        signal = (self._batch_id % self.interval == 0)\n        return signal\n\n\nclass _SaveDebugImage(meta.Augmenter):\n    \"\"\"Augmenter saving debug images to a destination according to a schedule.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    destination : _IImageDestination\n        The destination receiving debug images.\n\n    schedule : _IBatchwiseSchedule\n        The schedule to use to determine for which batches an image is\n        supposed to be generated.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, destination, schedule,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(_SaveDebugImage, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.destination = destination\n        self.schedule = schedule\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        save = self.schedule.on_batch(batch)\n        self.destination.on_batch(batch)\n\n        if save:\n            image = draw_debug_image(\n                images=batch.images,\n                heatmaps=batch.heatmaps,\n                segmentation_maps=batch.segmentation_maps,\n                keypoints=batch.keypoints,\n                bounding_boxes=batch.bounding_boxes,\n                polygons=batch.polygons,\n                line_strings=batch.line_strings)\n\n            self.destination.receive(image)\n\n        return batch\n\n\nclass SaveDebugImageEveryNBatches(_SaveDebugImage):\n    \"\"\"Visualize data in batches and save corresponding plots to a folder.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.debug.draw_debug_image`.\n\n    Parameters\n    ----------\n    destination : str or _IImageDestination\n        Path to a folder. The saved images will follow a filename pattern\n        of ``batch_<batch_id>.png``. The latest image will additionally be\n        saved to ``latest.png``.\n\n    interval : int\n        Interval in batches. If set to ``N``, every ``N`` th batch an\n        image will be generated and saved, starting with the first observed\n        batch.\n        Note that the augmenter only counts batches that it sees. If it is\n        executed conditionally or re-instantiated, it may not see all batches\n        or the counter may be wrong in other ways.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> import tempfile\n    >>> folder_path = tempfile.mkdtemp()\n    >>> seq = iaa.Sequential([\n    >>>     iaa.Sequential([\n    >>>         iaa.Fliplr(0.5),\n    >>>         iaa.Crop(px=(0, 16))\n    >>>     ], random_order=True),\n    >>>     iaa.SaveDebugImageEveryNBatches(folder_path, 100)\n    >>> ])\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, destination, interval,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        schedule = _EveryNBatchesSchedule(interval)\n        if not isinstance(destination, _IImageDestination):\n            assert os.path.isdir(destination), (\n                \"Expected 'destination' to be a string path to an existing \"\n                \"directory. Got path '%s'.\" % (destination,))\n            destination = _MultiDestination([\n                _FolderImageDestination(destination),\n                _FolderImageDestination(destination,\n                                        filename_pattern=\"batch_latest.png\")\n            ])\n        super(SaveDebugImageEveryNBatches, self).__init__(\n            destination=destination, schedule=schedule,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        dests = self.destination.destinations\n        return [\n            dests[0].folder_path,\n            dests[0].filename_pattern,\n            dests[1].folder_path,\n            dests[1].filename_pattern,\n            self.schedule.interval\n        ]\n"
  },
  {
    "path": "imgaug/augmenters/edges.py",
    "content": "\"\"\"\nAugmenters that deal with edge detection.\n\nList of augmenters:\n\n    * :class:`Canny`\n\n:class:`~imgaug.augmenters.convolutional.EdgeDetect` and\n:class:`~imgaug.augmenters.convolutional.DirectedEdgeDetect` are currently\nstill in ``convolutional.py``.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nfrom abc import ABCMeta, abstractmethod\n\nimport numpy as np\nimport cv2\nimport six\n\nimport imgaug as ia\nfrom imgaug.imgaug import _normalize_cv2_input_arr_\nfrom . import meta\nfrom . import blend\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\n\n\n# TODO this should be placed in some other file than edges.py as it could be\n#      re-used wherever a binary image is the result\n@six.add_metaclass(ABCMeta)\nclass IBinaryImageColorizer(object):\n    \"\"\"Interface for classes that convert binary masks to color images.\"\"\"\n\n    @abstractmethod\n    def colorize(self, image_binary, image_original, nth_image, random_state):\n        \"\"\"\n        Convert a binary image to a colorized one.\n\n        Parameters\n        ----------\n        image_binary : ndarray\n            Boolean ``(H,W)`` image.\n\n        image_original : ndarray\n            Original ``(H,W,C)`` input image.\n\n        nth_image : int\n            Index of the image in the batch.\n\n        random_state : imgaug.random.RNG\n            Random state to use.\n\n        Returns\n        -------\n        ndarray\n            Colorized form of `image_binary`.\n\n        \"\"\"\n\n\n# TODO see above, this should be moved to another file\nclass RandomColorsBinaryImageColorizer(IBinaryImageColorizer):\n    \"\"\"\n    Colorizer using two randomly sampled foreground/background colors.\n\n    Parameters\n    ----------\n    color_true : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Color of the foreground, i.e. all pixels in binary images that are\n        ``True``. This parameter will be queried once per image to\n        generate ``(3,)`` samples denoting the color. (Note that even for\n        grayscale images three values will be sampled and converted to\n        grayscale according to ``0.299*R + 0.587*G + 0.114*B``. This is the\n        same equation that is also used by OpenCV.)\n\n            * If an int, exactly that value will always be used, i.e. every\n              color will be ``(v, v, v)`` for value ``v``.\n            * If a tuple ``(a, b)``, three random values from the range\n              ``a <= x <= b`` will be sampled per image.\n            * If a list, then three random values will be sampled from that\n              list per image.\n            * If a StochasticParameter, three values will be sampled from the\n              parameter per image.\n\n    color_false : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Analogous to `color_true`, but denotes the color for all pixels that\n        are ``False`` in the binary input image.\n\n    \"\"\"\n\n    def __init__(self, color_true=(0, 255), color_false=(0, 255)):\n        self.color_true = iap.handle_discrete_param(\n            color_true,\n            \"color_true\",\n            value_range=(0, 255),\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            allow_floats=False)\n\n        self.color_false = iap.handle_discrete_param(\n            color_false,\n            \"color_false\",\n            value_range=(0, 255),\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            allow_floats=False)\n\n    def _draw_samples(self, random_state):\n        color_true = self.color_true.draw_samples((3,),\n                                                  random_state=random_state)\n        color_false = self.color_false.draw_samples((3,),\n                                                    random_state=random_state)\n        return color_true, color_false\n\n    def colorize(self, image_binary, image_original, nth_image, random_state):\n        assert image_binary.ndim == 2, (\n            \"Expected binary image to colorize to be 2-dimensional, \"\n            \"got %d dimensions.\" % (image_binary.ndim,))\n        assert image_binary.dtype.kind == \"b\", (\n            \"Expected binary image to colorize to be boolean, \"\n            \"got dtype kind %s.\" % (image_binary.dtype.kind,))\n        assert image_original.ndim == 3, (\n            \"Expected original image to be 3-dimensional, got %d \"\n            \"dimensions.\" % (image_original.ndim,))\n        assert image_original.shape[-1] in [1, 3, 4], (\n            \"Expected original image to have 1, 3 or 4 channels. \"\n            \"Got %d channels.\" % (image_original.shape[-1],))\n        assert image_original.dtype == iadt._UINT8_DTYPE, (\n            \"Expected original image to have dtype uint8, got dtype %s.\" % (\n                image_original.dtype.name))\n\n        color_true, color_false = self._draw_samples(random_state)\n\n        nb_channels = min(image_original.shape[-1], 3)\n        image_colorized = np.zeros(\n            (image_original.shape[0], image_original.shape[1], nb_channels),\n            dtype=image_original.dtype)\n\n        if nb_channels == 1:\n            # single channel input image, convert colors to grayscale\n            image_colorized[image_binary] = (\n                0.299*color_true[0]\n                + 0.587*color_true[1]\n                + 0.114*color_true[2])\n            image_colorized[~image_binary] = (\n                0.299*color_false[0]\n                + 0.587*color_false[1]\n                + 0.114*color_false[2])\n        else:\n            image_colorized[image_binary] = color_true\n            image_colorized[~image_binary] = color_false\n\n        # re-attach alpha channel if it was present in input image\n        if image_original.shape[-1] == 4:\n            image_colorized = np.dstack(\n                [image_colorized, image_original[:, :, 3:4]])\n\n        return image_colorized\n\n    def __str__(self):\n        return (\"RandomColorsBinaryImageColorizer(\"\n                \"color_true=%s, color_false=%s)\") % (\n                    self.color_true, self.color_false)\n\n\nclass Canny(meta.Augmenter):\n    \"\"\"\n    Apply a canny edge detector to input images.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no; not tested\n        * ``uint32``: no; not tested\n        * ``uint64``: no; not tested\n        * ``int8``: no; not tested\n        * ``int16``: no; not tested\n        * ``int32``: no; not tested\n        * ``int64``: no; not tested\n        * ``float16``: no; not tested\n        * ``float32``: no; not tested\n        * ``float64``: no; not tested\n        * ``float128``: no; not tested\n        * ``bool``: no; not tested\n\n    Parameters\n    ----------\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Blending factor to use in alpha blending.\n        A value close to 1.0 means that only the edge image is visible.\n        A value close to 0.0 means that only the original image is visible.\n        A value close to 0.5 means that the images are merged according to\n        `0.5*image + 0.5*edge_image`.\n        If a sample from this parameter is 0, no action will be performed for\n        the corresponding image.\n\n            * If an int or float, exactly that value will be used.\n            * If a tuple ``(a, b)``, a random value from the range\n              ``a <= x <= b`` will be sampled per image.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a StochasticParameter, a value will be sampled from the\n              parameter per image.\n\n    hysteresis_thresholds : number or tuple of number or list of number or imgaug.parameters.StochasticParameter or tuple of tuple of number or tuple of list of number or tuple of imgaug.parameters.StochasticParameter, optional\n        Min and max values to use in hysteresis thresholding.\n        (This parameter seems to have not very much effect on the results.)\n        Either a single parameter or a tuple of two parameters.\n        If a single parameter is provided, the sampling happens once for all\n        images with `(N,2)` samples being requested from the parameter,\n        where each first value denotes the hysteresis minimum and each second\n        the maximum.\n        If a tuple of two parameters is provided, one sampling of `(N,)` values\n        is independently performed per parameter (first parameter: hysteresis\n        minimum, second: hysteresis maximum).\n\n            * If this is a single number, both min and max value will always be\n              exactly that value.\n            * If this is a tuple of numbers ``(a, b)``, two random values from\n              the range ``a <= x <= b`` will be sampled per image.\n            * If this is a list, two random values will be sampled from that\n              list per image.\n            * If this is a StochasticParameter, two random values will be\n              sampled from that parameter per image.\n            * If this is a tuple ``(min, max)`` with ``min`` and ``max``\n              both *not* being numbers, they will be treated according to the\n              rules above (i.e. may be a number, tuple, list or\n              StochasticParameter). A single value will be sampled per image\n              and parameter.\n\n    sobel_kernel_size : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Kernel size of the sobel operator initially applied to each image.\n        This corresponds to ``apertureSize`` in ``cv2.Canny()``.\n        If a sample from this parameter is ``<=1``, no action will be performed\n        for the corresponding image.\n        The maximum for this parameter is ``7`` (inclusive). Higher values are\n        not accepted by OpenCV.\n        If an even value ``v`` is sampled, it is automatically changed to\n        ``v-1``.\n\n            * If this is a single integer, the kernel size always matches that\n              value.\n            * If this is a tuple of integers ``(a, b)``, a random discrete\n              value will be sampled from the range ``a <= x <= b`` per image.\n            * If this is a list, a random value will be sampled from that\n              list per image.\n            * If this is a StochasticParameter, a random value will be sampled\n              from that parameter per image.\n\n    colorizer : None or imgaug.augmenters.edges.IBinaryImageColorizer, optional\n        A strategy to convert binary edge images to color images.\n        If this is ``None``, an instance of ``RandomColorBinaryImageColorizer``\n        is created, which means that each edge image is converted into an\n        ``uint8`` image, where edge and non-edge pixels each have a different\n        color that was uniformly randomly sampled from the space of all\n        ``uint8`` colors.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Canny()\n\n    Create an augmenter that generates random blends between images and\n    their canny edge representations.\n\n    >>> aug = iaa.Canny(alpha=(0.0, 0.5))\n\n    Create a canny edge augmenter that generates edge images with a blending\n    factor of max ``50%``, i.e. the original (non-edge) image is always at\n    least partially visible.\n\n    >>> aug = iaa.Canny(\n    >>>     alpha=(0.0, 0.5),\n    >>>     colorizer=iaa.RandomColorsBinaryImageColorizer(\n    >>>         color_true=255,\n    >>>         color_false=0\n    >>>     )\n    >>> )\n\n    Same as in the previous example, but the edge image always uses the\n    color white for edges and black for the background.\n\n    >>> aug = iaa.Canny(alpha=(0.5, 1.0), sobel_kernel_size=[3, 7])\n\n    Create a canny edge augmenter that initially preprocesses images using\n    a sobel filter with kernel size of either ``3x3`` or ``13x13`` and\n    alpha-blends with result using a strength of ``50%`` (both images\n    equally visible) to ``100%`` (only edge image visible).\n\n    >>> aug = iaa.Alpha(\n    >>>     (0.0, 1.0),\n    >>>     iaa.Canny(alpha=1),\n    >>>     iaa.MedianBlur(13)\n    >>> )\n\n    Create an augmenter that blends a canny edge image with a median-blurred\n    version of the input image. The median blur uses a fixed kernel size\n    of ``13x13`` pixels.\n\n    \"\"\"\n\n    def __init__(self,\n                 alpha=(0.0, 1.0),\n                 hysteresis_thresholds=((100-40, 100+40), (200-40, 200+40)),\n                 sobel_kernel_size=(3, 7),\n                 colorizer=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Canny, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.alpha = iap.handle_continuous_param(\n            alpha, \"alpha\", value_range=(0, 1.0), tuple_to_uniform=True,\n            list_to_choice=True)\n\n        if isinstance(hysteresis_thresholds, tuple) \\\n                and len(hysteresis_thresholds) == 2 \\\n                and not ia.is_single_number(hysteresis_thresholds[0]) \\\n                and not ia.is_single_number(hysteresis_thresholds[1]):\n            self.hysteresis_thresholds = (\n                iap.handle_discrete_param(\n                    hysteresis_thresholds[0],\n                    \"hysteresis_thresholds[0]\",\n                    value_range=(0, 255),\n                    tuple_to_uniform=True,\n                    list_to_choice=True,\n                    allow_floats=True),\n                iap.handle_discrete_param(\n                    hysteresis_thresholds[1],\n                    \"hysteresis_thresholds[1]\",\n                    value_range=(0, 255),\n                    tuple_to_uniform=True,\n                    list_to_choice=True,\n                    allow_floats=True)\n            )\n        else:\n            self.hysteresis_thresholds = iap.handle_discrete_param(\n                hysteresis_thresholds,\n                \"hysteresis_thresholds\",\n                value_range=(0, 255),\n                tuple_to_uniform=True,\n                list_to_choice=True,\n                allow_floats=True)\n\n        # we don't use handle_discrete_kernel_size_param() here, because\n        # cv2.Canny() can't handle independent height/width values, only a\n        # single kernel size\n        self.sobel_kernel_size = iap.handle_discrete_param(\n            sobel_kernel_size,\n            \"sobel_kernel_size\",\n            value_range=(0, 7),  # OpenCV only accepts ksize up to 7\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            allow_floats=False)\n\n        self.colorizer = (\n            colorizer\n            if colorizer is not None\n            else RandomColorsBinaryImageColorizer()\n        )\n\n    def _draw_samples(self, augmentables, random_state):\n        nb_images = len(augmentables)\n        rss = random_state.duplicate(4)\n\n        alpha_samples = self.alpha.draw_samples((nb_images,), rss[0])\n\n        hthresh = self.hysteresis_thresholds\n        if isinstance(hthresh, tuple):\n            min_values = hthresh[0].draw_samples((nb_images,), rss[1])\n            max_values = hthresh[1].draw_samples((nb_images,), rss[2])\n            hthresh_samples = np.stack([min_values, max_values], axis=-1)\n        else:\n            hthresh_samples = hthresh.draw_samples((nb_images, 2), rss[1])\n\n        sobel_samples = self.sobel_kernel_size.draw_samples((nb_images,),\n                                                            rss[3])\n\n        # verify for hysteresis thresholds that min_value < max_value everywhere\n        invalid = (hthresh_samples[:, 0] > hthresh_samples[:, 1])\n        if np.any(invalid):\n            hthresh_samples[invalid, :] = hthresh_samples[invalid, :][:, [1, 0]]\n\n        # ensure that sobel kernel sizes are correct\n        # note that OpenCV accepts only kernel sizes that are (a) even\n        # and (b) <=7\n        assert not np.any(sobel_samples < 0), (\n            \"Sampled a sobel kernel size below 0 in Canny. \"\n            \"Allowed value range is 0 to 7.\")\n        assert not np.any(sobel_samples > 7), (\n            \"Sampled a sobel kernel size above 7 in Canny. \"\n            \"Allowed value range is 0 to 7.\")\n        even_idx = (np.mod(sobel_samples, 2) == 0)\n        sobel_samples[even_idx] -= 1\n\n        return alpha_samples, hthresh_samples, sobel_samples\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        iadt.allow_only_uint8(images, augmenter=self)\n\n        rss = random_state.duplicate(len(images))\n        samples = self._draw_samples(images, rss[-1])\n        alpha_samples = samples[0]\n        hthresh_samples = samples[1]\n        sobel_samples = samples[2]\n\n        gen = enumerate(zip(images, alpha_samples, hthresh_samples,\n                            sobel_samples))\n        for i, (image, alpha, hthreshs, sobel) in gen:\n            assert image.shape[-1] in [1, 3, 4], (\n                \"Canny edge detector can currently only handle images with \"\n                \"channel numbers that are 1, 3 or 4. Got %d.\") % (\n                    image.shape[-1],)\n\n            has_zero_sized_axes = (0 in image.shape[0:2])\n            if alpha > 0 and sobel > 1 and not has_zero_sized_axes:\n                image_canny = cv2.Canny(\n                    _normalize_cv2_input_arr_(image[:, :, 0:3]),\n                    threshold1=hthreshs[0],\n                    threshold2=hthreshs[1],\n                    apertureSize=sobel,\n                    L2gradient=True)\n                image_canny = (image_canny > 0)\n\n                # canny returns a boolean (H,W) image, so we change it to\n                # (H,W,C) and then uint8\n                image_canny_color = self.colorizer.colorize(\n                    image_canny, image, nth_image=i, random_state=rss[i])\n\n                batch.images[i] = blend.blend_alpha(image_canny_color, image,\n                                                    alpha)\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.alpha, self.hysteresis_thresholds, self.sobel_kernel_size,\n                self.colorizer]\n\n    def __str__(self):\n        return (\"Canny(\"\n                \"alpha=%s, \"\n                \"hysteresis_thresholds=%s, \"\n                \"sobel_kernel_size=%s, \"\n                \"colorizer=%s, \"\n                \"name=%s, \"\n                \"deterministic=%s)\" % (\n                    self.alpha, self.hysteresis_thresholds,\n                    self.sobel_kernel_size, self.colorizer,\n                    self.name, self.deterministic))\n"
  },
  {
    "path": "imgaug/augmenters/flip.py",
    "content": "\"\"\"\nAugmenters that apply mirroring/flipping operations to images.\n\nList of augmenters:\n\n    * :class:`Fliplr`\n    * :class:`Flipud`\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport numpy as np\nimport cv2\nimport six.moves as sm\n\nfrom imgaug.imgaug import _normalize_cv2_input_arr_\nfrom . import meta\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\n\n\n# pylint:disable=pointless-string-statement\n\"\"\"\nSpeed comparison by datatype and flip method.\n\nHORIZONTAL FLIPS.\n\n----------\nbool\n----------\n               slice 0.00052ms\n       slice, contig 0.21878ms\n              fliplr 0.00180ms\n       fliplr contig 0.22000ms\n                 cv2 Error: Expected cv::UMat for argument 'src'\n          cv2 contig Error: Expected cv::UMat for argument 'src'\n            fort cv2 Error: Expected cv::UMat for argument 'src'\n     fort cv2 contig Error: Expected cv::UMat for argument 'src'\n                cv2_ Error: Expected cv::UMat for argument 'src'\n         cv2_ contig Error: Expected cv::UMat for argument 'src'\n           fort cv2_ Error: Expected cv::UMat for argument 'src'\n    fort cv2_ contig Error: Expected cv::UMat for argument 'src'\n            cv2_ get Error: Expected cv::UMat for argument 'src'\n     cv2_ get contig Error: Expected cv::UMat for argument 'src'\n       fort cv2_ get Error: Expected cv::UMat for argument 'src'\nfort cv2_ get contig Error: Expected cv::UMat for argument 'src'\n\n----------\nuint8\n----------\n               slice 0.00052ms\n       slice, contig 0.21878ms\n              fliplr 0.00174ms\n       fliplr contig 0.21828ms\n                 cv2 0.07037ms (3.11x)\n          cv2 contig 0.07355ms (2.97x)\n            fort cv2 0.33900ms (0.65x)\n     fort cv2 contig 0.34198ms (0.64x)\n                cv2_ 0.08093ms (2.70x)\n         cv2_ contig 0.08554ms (2.56x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.25783ms (0.17x)\nfort cv2_ get contig 1.25868ms (0.17x)\n\n----------\nuint16\n----------\n               slice 0.00176ms\n       slice, contig 0.21250ms\n              fliplr 0.00489ms\n       fliplr contig 0.21438ms\n                 cv2 0.16964ms (1.25x)\n          cv2 contig 0.17314ms (1.23x)\n            fort cv2 0.50989ms (0.42x)\n     fort cv2 contig 0.51188ms (0.42x)\n                cv2_ 0.18803ms (1.13x)\n         cv2_ contig 0.19136ms (1.11x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.48429ms (0.14x)\nfort cv2_ get contig 1.48392ms (0.14x)\n\n----------\nuint32\n----------\n               slice 0.00181ms\n       slice, contig 0.22855ms\n              fliplr 0.00486ms\n       fliplr contig 0.23070ms\n                 cv2 Error: Expected cv::UMat for argument 'src'\n          cv2 contig Error: Expected cv::UMat for argument 'src'\n            fort cv2 Error: Expected cv::UMat for argument 'src'\n     fort cv2 contig Error: Expected cv::UMat for argument 'src'\n                cv2_ Error: Expected cv::UMat for argument 'src'\n         cv2_ contig Error: Expected cv::UMat for argument 'src'\n           fort cv2_ Error: Expected cv::UMat for argument 'src'\n    fort cv2_ contig Error: Expected cv::UMat for argument 'src'\n            cv2_ get Error: Expected cv::UMat for argument 'src'\n     cv2_ get contig Error: Expected cv::UMat for argument 'src'\n       fort cv2_ get Error: Expected cv::UMat for argument 'src'\nfort cv2_ get contig Error: Expected cv::UMat for argument 'src'\n\n----------\nuint64\n----------\n               slice 0.00175ms\n       slice, contig 0.28662ms\n              fliplr 0.00497ms\n       fliplr contig 0.28986ms\n                 cv2 Error: Got dtype int32\n          cv2 contig Error: Got dtype int32\n            fort cv2 Error: Got dtype int32\n     fort cv2 contig Error: Got dtype int32\n                cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n         cv2_ contig Error: Got dtype object\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: Got dtype int32\n     cv2_ get contig Error: Got dtype int32\n       fort cv2_ get Error: Got dtype int32\nfort cv2_ get contig Error: Got dtype int32\n\n----------\nint8\n----------\n               slice 0.00052ms\n       slice, contig 0.21802ms\n              fliplr 0.00183ms\n       fliplr contig 0.21866ms\n                 cv2 0.07026ms (3.10x)\n          cv2 contig 0.07234ms (3.01x)\n            fort cv2 0.34137ms (0.64x)\n     fort cv2 contig 0.35426ms (0.62x)\n                cv2_ 0.08145ms (2.68x)\n         cv2_ contig 0.08498ms (2.57x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.27798ms (0.17x)\nfort cv2_ get contig 1.26791ms (0.17x)\n\n----------\nint16\n----------\n               slice 0.00146ms\n       slice, contig 0.21083ms\n              fliplr 0.00443ms\n       fliplr contig 0.21287ms\n                 cv2 0.17461ms (1.21x)\n          cv2 contig 0.17523ms (1.20x)\n            fort cv2 0.51030ms (0.41x)\n     fort cv2 contig 0.50438ms (0.42x)\n                cv2_ 0.18627ms (1.13x)\n         cv2_ contig 0.19703ms (1.07x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.47157ms (0.14x)\nfort cv2_ get contig 1.48715ms (0.14x)\n\n----------\nint32\n----------\n               slice 0.00177ms\n       slice, contig 0.22641ms\n              fliplr 0.00505ms\n       fliplr contig 0.22934ms\n                 cv2 0.33548ms (0.67x)\n          cv2 contig 0.34303ms (0.66x)\n            fort cv2 0.77874ms (0.29x)\n     fort cv2 contig 0.78380ms (0.29x)\n                cv2_ 0.39785ms (0.57x)\n         cv2_ contig 0.39249ms (0.58x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.89115ms (0.12x)\nfort cv2_ get contig 1.89214ms (0.12x)\n\n----------\nint64\n----------\n               slice 0.00176ms\n       slice, contig 0.28347ms\n              fliplr 0.00484ms\n       fliplr contig 0.28658ms\n                 cv2 Error: Got dtype int32\n          cv2 contig Error: Got dtype int32\n            fort cv2 Error: Got dtype int32\n     fort cv2 contig Error: Got dtype int32\n                cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n         cv2_ contig Error: Got dtype object\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: Got dtype int32\n     cv2_ get contig Error: Got dtype int32\n       fort cv2_ get Error: Got dtype int32\nfort cv2_ get contig Error: Got dtype int32\n\n----------\nfloat16\n----------\n               slice 0.00155ms\n       slice, contig 0.21066ms\n              fliplr 0.00435ms\n       fliplr contig 0.21295ms\n                 cv2 Error: Expected cv::UMat for argument 'src'\n          cv2 contig Error: Expected cv::UMat for argument 'src'\n            fort cv2 Error: Expected cv::UMat for argument 'src'\n     fort cv2 contig Error: Expected cv::UMat for argument 'src'\n                cv2_ Error: Expected cv::UMat for argument 'src'\n         cv2_ contig Error: Expected cv::UMat for argument 'src'\n           fort cv2_ Error: Expected cv::UMat for argument 'src'\n    fort cv2_ contig Error: Expected cv::UMat for argument 'src'\n            cv2_ get Error: Expected cv::UMat for argument 'src'\n     cv2_ get contig Error: Expected cv::UMat for argument 'src'\n       fort cv2_ get Error: Expected cv::UMat for argument 'src'\nfort cv2_ get contig Error: Expected cv::UMat for argument 'src'\n\n----------\nfloat32\n----------\n               slice 0.00177ms\n       slice, contig 0.22798ms\n              fliplr 0.00495ms\n       fliplr contig 0.22916ms\n                 cv2 0.33821ms (0.67x)\n          cv2 contig 0.31665ms (0.72x)\n            fort cv2 0.78119ms (0.29x)\n     fort cv2 contig 0.77838ms (0.29x)\n                cv2_ 0.39515ms (0.58x)\n         cv2_ contig 0.41023ms (0.56x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.88737ms (0.12x)\nfort cv2_ get contig 1.89616ms (0.12x)\n\n----------\nfloat64\n----------\n               slice 0.00179ms\n       slice, contig 0.28810ms\n              fliplr 0.00495ms\n       fliplr contig 0.29130ms\n                 cv2 0.63258ms (0.46x)\n          cv2 contig 0.64089ms (0.45x)\n            fort cv2 1.64795ms (0.17x)\n     fort cv2 contig 1.65449ms (0.17x)\n                cv2_ 0.75202ms (0.38x)\n         cv2_ contig 0.74789ms (0.39x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 3.99237ms (0.07x)\nfort cv2_ get contig 3.97847ms (0.07x)\n\n----------\nfloat128\n----------\n               slice 0.00179ms\n       slice, contig 0.51371ms\n              fliplr 0.00545ms\n       fliplr contig 0.51618ms\n                 cv2 Error: Expected cv::UMat for argument 'src'\n          cv2 contig Error: Expected cv::UMat for argument 'src'\n            fort cv2 Error: Expected cv::UMat for argument 'src'\n     fort cv2 contig Error: Expected cv::UMat for argument 'src'\n                cv2_ Error: Expected cv::UMat for argument 'src'\n         cv2_ contig Error: Expected cv::UMat for argument 'src'\n           fort cv2_ Error: Expected cv::UMat for argument 'src'\n    fort cv2_ contig Error: Expected cv::UMat for argument 'src'\n            cv2_ get Error: Expected cv::UMat for argument 'src'\n     cv2_ get contig Error: Expected cv::UMat for argument 'src'\n       fort cv2_ get Error: Expected cv::UMat for argument 'src'\nfort cv2_ get contig Error: Expected cv::UMat for argument 'src'\n\n==============================\nflip method followed by Add\n==============================\n               slice 1.29597ms\n       slice, contig 1.32523ms\n              fliplr 1.29298ms\n       fliplr contig 1.33087ms\n                 cv2 1.17829ms\n          cv2 contig 1.18350ms\n            fort cv2 1.45182ms\n     fort cv2 contig 1.45762ms\n                cv2_ 1.19331ms\n         cv2_ contig 1.19364ms\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 2.43293ms\nfort cv2_ get contig 2.48836ms\n\n==============================\nflip method followed by Affine\n==============================\n               slice 2.83081ms\n       slice, contig 2.88243ms\n              fliplr 2.84253ms\n       fliplr contig 2.89106ms\n                 cv2 2.72900ms\n          cv2 contig 2.74500ms\n            fort cv2 2.99842ms\n     fort cv2 contig 3.03457ms\n                cv2_ 2.73629ms\n         cv2_ contig 2.77505ms\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 4.01583ms\nfort cv2_ get contig 4.03347ms\n\n==============================\nflip method followed by AverageBlur\n==============================\n               slice 0.77109ms\n       slice, contig 0.80603ms\n              fliplr 0.77666ms\n       fliplr contig 0.81088ms\n                 cv2 0.66065ms\n          cv2 contig 0.66496ms\n            fort cv2 0.94078ms\n     fort cv2 contig 0.92662ms\n                cv2_ 0.65560ms\n         cv2_ contig 0.66237ms\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.88932ms\nfort cv2_ get contig 1.89190ms\n\n\"\"\"\n\n\"\"\"\nSpeed comparison by datatype and flip method.\n\nVERTICAL FLIPS.\n\n----------\nbool\n----------\n               slice 0.00047ms\n       slice, contig 0.02332ms\n              flipud 0.00143ms\n       flipud contig 0.02502ms\n                 cv2 Error: Expected cv::UMat for argument 'src'\n          cv2 contig Error: Expected cv::UMat for argument 'src'\n            fort cv2 Error: Expected cv::UMat for argument 'src'\n     fort cv2 contig Error: Expected cv::UMat for argument 'src'\n                cv2_ Error: Expected cv::UMat for argument 'src'\n         cv2_ contig Error: Expected cv::UMat for argument 'src'\n           fort cv2_ Error: Expected cv::UMat for argument 'src'\n    fort cv2_ contig Error: Expected cv::UMat for argument 'src'\n            cv2_ get Error: Expected cv::UMat for argument 'src'\n     cv2_ get contig Error: Expected cv::UMat for argument 'src'\n       fort cv2_ get Error: Expected cv::UMat for argument 'src'\nfort cv2_ get contig Error: Expected cv::UMat for argument 'src'\n\n----------\nuint8\n----------\n               slice 0.00049ms\n       slice, contig 0.02311ms\n              flipud 0.00155ms\n       flipud contig 0.02506ms\n                 cv2 0.03030ms (0.76x)\n          cv2 contig 0.03401ms (0.68x)\n            fort cv2 0.31000ms (0.07x)\n     fort cv2 contig 0.33619ms (0.07x)\n                cv2_ 0.01753ms (1.32x)\n         cv2_ contig 0.01841ms (1.26x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.26680ms (0.02x)\nfort cv2_ get contig 1.27027ms (0.02x)\n\n----------\nuint16\n----------\n               slice 0.00147ms\n       slice, contig 0.04861ms\n              flipud 0.00406ms\n       flipud contig 0.05060ms\n                 cv2 0.06169ms (0.79x)\n          cv2 contig 0.06397ms (0.76x)\n            fort cv2 0.39190ms (0.12x)\n     fort cv2 contig 0.39098ms (0.12x)\n                cv2_ 0.03375ms (1.44x)\n         cv2_ contig 0.03619ms (1.34x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.51197ms (0.03x)\nfort cv2_ get contig 1.52977ms (0.03x)\n\n----------\nuint32\n----------\n               slice 0.00167ms\n       slice, contig 0.09415ms\n              flipud 0.00476ms\n       flipud contig 0.09660ms\n                 cv2 Error: Expected cv::UMat for argument 'src'\n          cv2 contig Error: Expected cv::UMat for argument 'src'\n            fort cv2 Error: Expected cv::UMat for argument 'src'\n     fort cv2 contig Error: Expected cv::UMat for argument 'src'\n                cv2_ Error: Expected cv::UMat for argument 'src'\n         cv2_ contig Error: Expected cv::UMat for argument 'src'\n           fort cv2_ Error: Expected cv::UMat for argument 'src'\n    fort cv2_ contig Error: Expected cv::UMat for argument 'src'\n            cv2_ get Error: Expected cv::UMat for argument 'src'\n     cv2_ get contig Error: Expected cv::UMat for argument 'src'\n       fort cv2_ get Error: Expected cv::UMat for argument 'src'\nfort cv2_ get contig Error: Expected cv::UMat for argument 'src'\n\n----------\nuint64\n----------\n               slice 0.00175ms\n       slice, contig 0.17413ms\n              flipud 0.00488ms\n       flipud contig 0.17508ms\n                 cv2 Error: Got dtype int32\n          cv2 contig Error: Got dtype int32\n            fort cv2 Error: Got dtype int32\n     fort cv2 contig Error: Got dtype int32\n                cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n         cv2_ contig Error: Got dtype object\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: Got dtype int32\n     cv2_ get contig Error: Got dtype int32\n       fort cv2_ get Error: Got dtype int32\nfort cv2_ get contig Error: Got dtype int32\n\n----------\nint8\n----------\n               slice 0.00057ms\n       slice, contig 0.02604ms\n              flipud 0.00146ms\n       flipud contig 0.02806ms\n                 cv2 0.03434ms (0.76x)\n          cv2 contig 0.03676ms (0.71x)\n            fort cv2 0.30920ms (0.08x)\n     fort cv2 contig 0.31123ms (0.08x)\n                cv2_ 0.01625ms (1.60x)\n         cv2_ contig 0.01799ms (1.45x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.29065ms (0.02x)\nfort cv2_ get contig 1.29596ms (0.02x)\n\n----------\nint16\n----------\n               slice 0.00142ms\n       slice, contig 0.05079ms\n              flipud 0.00403ms\n       flipud contig 0.05312ms\n                 cv2 0.06277ms (0.81x)\n          cv2 contig 0.06527ms (0.78x)\n            fort cv2 0.39710ms (0.13x)\n     fort cv2 contig 0.39851ms (0.13x)\n                cv2_ 0.03419ms (1.49x)\n         cv2_ contig 0.03668ms (1.38x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.52476ms (0.03x)\nfort cv2_ get contig 1.52594ms (0.03x)\n\n----------\nint32\n----------\n               slice 0.00172ms\n       slice, contig 0.09621ms\n              flipud 0.00469ms\n       flipud contig 0.09868ms\n                 cv2 0.12192ms (0.79x)\n          cv2 contig 0.12459ms (0.77x)\n            fort cv2 0.57915ms (0.17x)\n     fort cv2 contig 0.58571ms (0.16x)\n                cv2_ 0.07337ms (1.31x)\n         cv2_ contig 0.07595ms (1.27x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.97937ms (0.05x)\nfort cv2_ get contig 1.97919ms (0.05x)\n\n----------\nint64\n----------\n               slice 0.00167ms\n       slice, contig 0.17308ms\n              flipud 0.00470ms\n       flipud contig 0.17542ms\n                 cv2 Error: Got dtype int32\n          cv2 contig Error: Got dtype int32\n            fort cv2 Error: Got dtype int32\n     fort cv2 contig Error: Got dtype int32\n                cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n         cv2_ contig Error: Got dtype object\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: Got dtype int32\n     cv2_ get contig Error: Got dtype int32\n       fort cv2_ get Error: Got dtype int32\nfort cv2_ get contig Error: Got dtype int32\n\n----------\nfloat16\n----------\n               slice 0.00146ms\n       slice, contig 0.05094ms\n              flipud 0.00408ms\n       flipud contig 0.05359ms\n                 cv2 Error: Expected cv::UMat for argument 'src'\n          cv2 contig Error: Expected cv::UMat for argument 'src'\n            fort cv2 Error: Expected cv::UMat for argument 'src'\n     fort cv2 contig Error: Expected cv::UMat for argument 'src'\n                cv2_ Error: Expected cv::UMat for argument 'src'\n         cv2_ contig Error: Expected cv::UMat for argument 'src'\n           fort cv2_ Error: Expected cv::UMat for argument 'src'\n    fort cv2_ contig Error: Expected cv::UMat for argument 'src'\n            cv2_ get Error: Expected cv::UMat for argument 'src'\n     cv2_ get contig Error: Expected cv::UMat for argument 'src'\n       fort cv2_ get Error: Expected cv::UMat for argument 'src'\nfort cv2_ get contig Error: Expected cv::UMat for argument 'src'\n\n----------\nfloat32\n----------\n               slice 0.00164ms\n       slice, contig 0.09606ms\n              flipud 0.00461ms\n       flipud contig 0.09897ms\n                 cv2 0.12151ms (0.79x)\n          cv2 contig 0.12514ms (0.77x)\n            fort cv2 0.57887ms (0.17x)\n     fort cv2 contig 0.58280ms (0.16x)\n                cv2_ 0.07312ms (1.31x)\n         cv2_ contig 0.07581ms (1.27x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.97501ms (0.05x)\nfort cv2_ get contig 1.98010ms (0.05x)\n\n----------\nfloat64\n----------\n               slice 0.00170ms\n       slice, contig 0.17300ms\n              flipud 0.00483ms\n       flipud contig 0.17539ms\n                 cv2 0.23941ms (0.72x)\n          cv2 contig 0.24220ms (0.71x)\n            fort cv2 1.26645ms (0.14x)\n     fort cv2 contig 1.27246ms (0.14x)\n                cv2_ 0.14879ms (1.16x)\n         cv2_ contig 0.15185ms (1.14x)\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 4.02958ms (0.04x)\nfort cv2_ get contig 4.02822ms (0.04x)\n\n----------\nfloat128\n----------\n               slice 0.00168ms\n       slice, contig 0.35407ms\n              flipud 0.00513ms\n       flipud contig 0.35716ms\n                 cv2 Error: Expected cv::UMat for argument 'src'\n          cv2 contig Error: Expected cv::UMat for argument 'src'\n            fort cv2 Error: Expected cv::UMat for argument 'src'\n     fort cv2 contig Error: Expected cv::UMat for argument 'src'\n                cv2_ Error: Expected cv::UMat for argument 'src'\n         cv2_ contig Error: Expected cv::UMat for argument 'src'\n           fort cv2_ Error: Expected cv::UMat for argument 'src'\n    fort cv2_ contig Error: Expected cv::UMat for argument 'src'\n            cv2_ get Error: Expected cv::UMat for argument 'src'\n     cv2_ get contig Error: Expected cv::UMat for argument 'src'\n       fort cv2_ get Error: Expected cv::UMat for argument 'src'\nfort cv2_ get contig Error: Expected cv::UMat for argument 'src'\n\n==============================\nflip method followed by Add\n==============================\n               slice 1.11286ms\n       slice, contig 1.14822ms\n              flipud 1.11858ms\n       flipud contig 1.14703ms\n                 cv2 1.14892ms\n          cv2 contig 1.15923ms\n            fort cv2 1.43633ms\n     fort cv2 contig 1.44342ms\n                cv2_ 1.15194ms\n         cv2_ contig 1.14864ms\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 2.47966ms\nfort cv2_ get contig 2.48177ms\n\n==============================\nflip method followed by Affine\n==============================\n               slice 2.61450ms\n       slice, contig 2.67402ms\n              flipud 2.62069ms\n       flipud contig 2.67267ms\n                 cv2 2.66312ms\n          cv2 contig 2.68482ms\n            fort cv2 2.94520ms\n     fort cv2 contig 2.97767ms\n                cv2_ 2.64183ms\n         cv2_ contig 2.64857ms\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 3.94321ms\nfort cv2_ get contig 4.01652ms\n\n==============================\nflip method followed by AverageBlur\n==============================\n               slice 0.57750ms\n       slice, contig 0.60905ms\n              flipud 0.58156ms\n       flipud contig 0.61562ms\n                 cv2 0.61704ms\n          cv2 contig 0.62293ms\n            fort cv2 0.90804ms\n     fort cv2 contig 0.90495ms\n                cv2_ 0.60047ms\n         cv2_ contig 0.60876ms\n           fort cv2_ Error: 'cv2.UMat' object has no attribute 'ndim'\n    fort cv2_ contig Error: Got dtype object\n            cv2_ get Error: 'numpy.ndarray' object has no attribute 'get'\n     cv2_ get contig Error: 'numpy.ndarray' object has no attribute 'get'\n       fort cv2_ get 1.93064ms\nfort cv2_ get contig 1.95215ms\n\"\"\"\n# pylint:enable=pointless-string-statement\n\n_FLIPLR_DTYPES_CV2 = iadt._convert_dtype_strs_to_types(\n    \"uint8 uint16 int8 int16\"\n)\n\n\ndef fliplr(arr):\n    \"\"\"Flip an image-like array horizontally.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; fully tested\n        * ``uint32``: yes; fully tested\n        * ``uint64``: yes; fully tested\n        * ``int8``: yes; fully tested\n        * ``int16``: yes; fully tested\n        * ``int32``: yes; fully tested\n        * ``int64``: yes; fully tested\n        * ``float16``: yes; fully tested\n        * ``float32``: yes; fully tested\n        * ``float64``: yes; fully tested\n        * ``float128``: yes; fully tested\n        * ``bool``: yes; fully tested\n\n    Parameters\n    ----------\n    arr : ndarray\n        A 2D/3D `(H, W, [C])` image array.\n\n    Returns\n    -------\n    ndarray\n        Horizontally flipped array.\n\n    Examples\n    --------\n    >>> import numpy as np\n    >>> import imgaug.augmenters.flip as flip\n    >>> arr = np.arange(16).reshape((4, 4))\n    >>> arr_flipped = flip.fliplr(arr)\n\n    Create a ``4x4`` array and flip it horizontally.\n\n    \"\"\"\n    # we don't check here if #channels > 512, because the cv2 function also\n    # kinda works with that, it is very rare to happen and would induce an\n    # additional check (with significant relative impact on runtime considering\n    # flipping is already ultra fast)\n    if arr.dtype in _FLIPLR_DTYPES_CV2:\n        return _fliplr_cv2(arr)\n    return _fliplr_sliced(arr)\n\n\ndef _fliplr_sliced(arr):\n    return arr[:, ::-1, ...]\n\n\ndef _fliplr_cv2(arr):\n    # cv2.flip() returns None for arrays with zero height or width\n    # and turns channels=0 to channels=512\n    if arr.size == 0:\n        return np.copy(arr)\n\n    # cv2.flip() fails for more than 512 channels\n    if arr.ndim == 3 and arr.shape[-1] > 512:\n        # TODO this is quite inefficient right now\n        channels = [\n            cv2.flip(_normalize_cv2_input_arr_(arr[..., c]), 1)\n            for c\n            in sm.xrange(arr.shape[-1])\n        ]\n        result = np.stack(channels, axis=-1)\n    else:\n        # Normalization from imgaug.imgaug._normalize_cv2_input_arr_().\n        # Moved here for performance reasons. Keep this aligned.\n        # TODO recalculate timings, they were computed without this.\n        flags = arr.flags\n        if not flags[\"OWNDATA\"]:\n            arr = np.copy(arr)\n            flags = arr.flags\n        if not flags[\"C_CONTIGUOUS\"]:\n            arr = np.ascontiguousarray(arr)\n\n        result = cv2.flip(_normalize_cv2_input_arr_(arr), 1)\n\n    if result.ndim == 2 and arr.ndim == 3:\n        return result[..., np.newaxis]\n    return result\n\n\ndef flipud(arr):\n    \"\"\"Flip an image-like array vertically.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; fully tested\n        * ``uint32``: yes; fully tested\n        * ``uint64``: yes; fully tested\n        * ``int8``: yes; fully tested\n        * ``int16``: yes; fully tested\n        * ``int32``: yes; fully tested\n        * ``int64``: yes; fully tested\n        * ``float16``: yes; fully tested\n        * ``float32``: yes; fully tested\n        * ``float64``: yes; fully tested\n        * ``float128``: yes; fully tested\n        * ``bool``: yes; fully tested\n\n    Parameters\n    ----------\n    arr : ndarray\n        A 2D/3D `(H, W, [C])` image array.\n\n    Returns\n    -------\n    ndarray\n        Vertically flipped array.\n\n    Examples\n    --------\n    >>> import numpy as np\n    >>> import imgaug.augmenters.flip as flip\n    >>> arr = np.arange(16).reshape((4, 4))\n    >>> arr_flipped = flip.flipud(arr)\n\n    Create a ``4x4`` array and flip it vertically.\n\n    \"\"\"\n    # Note that this function is currently not called by Flipud for performance\n    # reasons. Changing this will therefore not affect Flipud.\n    return arr[::-1, ...]\n\n\ndef HorizontalFlip(*args, **kwargs):\n    \"\"\"Alias for Fliplr.\"\"\"\n    # pylint: disable=invalid-name\n    return Fliplr(*args, **kwargs)\n\n\ndef VerticalFlip(*args, **kwargs):\n    \"\"\"Alias for Flipud.\"\"\"\n    # pylint: disable=invalid-name\n    return Flipud(*args, **kwargs)\n\n\nclass Fliplr(meta.Augmenter):\n    \"\"\"Flip/mirror input images horizontally.\n\n    .. note::\n\n        The default value for the probability is ``0.0``.\n        So, to flip *all* input images use ``Fliplr(1.0)`` and *not* just\n        ``Fliplr()``.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.flip.fliplr`.\n\n    Parameters\n    ----------\n    p : number or imgaug.parameters.StochasticParameter, optional\n        Probability of each image to get flipped.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Fliplr(0.5)\n\n    Flip ``50`` percent of all images horizontally.\n\n\n    >>> aug = iaa.Fliplr(1.0)\n\n    Flip all images horizontally.\n\n    \"\"\"\n\n    def __init__(self, p=1,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Fliplr, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.p = iap.handle_probability_param(p, \"p\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        samples = self.p.draw_samples((batch.nb_rows,),\n                                      random_state=random_state)\n        for i, sample in enumerate(samples):\n            if sample >= 0.5:\n                if batch.images is not None:\n                    batch.images[i] = fliplr(batch.images[i])\n\n                if batch.heatmaps is not None:\n                    batch.heatmaps[i].arr_0to1 = fliplr(\n                        batch.heatmaps[i].arr_0to1)\n\n                if batch.segmentation_maps is not None:\n                    batch.segmentation_maps[i].arr = fliplr(\n                        batch.segmentation_maps[i].arr)\n\n                if batch.keypoints is not None:\n                    kpsoi = batch.keypoints[i]\n                    width = kpsoi.shape[1]\n                    for kp in kpsoi.keypoints:\n                        kp.x = width - float(kp.x)\n\n                if batch.bounding_boxes is not None:\n                    bbsoi = batch.bounding_boxes[i]\n                    width = bbsoi.shape[1]\n                    for bb in bbsoi.bounding_boxes:\n                        # after flip, x1 ends up right of x2\n                        x1, x2 = bb.x1, bb.x2\n                        bb.x1 = width - x2\n                        bb.x2 = width - x1\n\n                if batch.polygons is not None:\n                    psoi = batch.polygons[i]\n                    width = psoi.shape[1]\n                    for poly in psoi.polygons:\n                        # TODO maybe reverse the order of points afterwards?\n                        #      the flip probably inverts them\n                        poly.exterior[:, 0] = width - poly.exterior[:, 0]\n\n                if batch.line_strings is not None:\n                    lsoi = batch.line_strings[i]\n                    width = lsoi.shape[1]\n                    for ls in lsoi.line_strings:\n                        # TODO maybe reverse the order of points afterwards?\n                        #      the flip probably inverts them\n                        ls.coords[:, 0] = width - ls.coords[:, 0]\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.p]\n\n\n# TODO merge with Fliplr\nclass Flipud(meta.Augmenter):\n    \"\"\"Flip/mirror input images vertically.\n\n    .. note::\n\n        The default value for the probability is ``0.0``.\n        So, to flip *all* input images use ``Flipud(1.0)`` and *not* just\n        ``Flipud()``.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.flip.flipud`.\n\n    Parameters\n    ----------\n    p : number or imgaug.parameters.StochasticParameter, optional\n        Probability of each image to get flipped.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Flipud(0.5)\n\n    Flip ``50`` percent of all images vertically.\n\n    >>> aug = iaa.Flipud(1.0)\n\n    Flip all images vertically.\n\n    \"\"\"\n\n    def __init__(self, p=1,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Flipud, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.p = iap.handle_probability_param(p, \"p\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        samples = self.p.draw_samples((batch.nb_rows,),\n                                      random_state=random_state)\n        for i, sample in enumerate(samples):\n            if sample >= 0.5:\n                if batch.images is not None:\n                    # We currently do not use flip.flipud() here, because that\n                    # saves a function call.\n                    batch.images[i] = batch.images[i][::-1, ...]\n\n                if batch.heatmaps is not None:\n                    batch.heatmaps[i].arr_0to1 = \\\n                        batch.heatmaps[i].arr_0to1[::-1, ...]\n\n                if batch.segmentation_maps is not None:\n                    batch.segmentation_maps[i].arr = \\\n                        batch.segmentation_maps[i].arr[::-1, ...]\n\n                if batch.keypoints is not None:\n                    kpsoi = batch.keypoints[i]\n                    height = kpsoi.shape[0]\n                    for kp in kpsoi.keypoints:\n                        kp.y = height - float(kp.y)\n\n                if batch.bounding_boxes is not None:\n                    bbsoi = batch.bounding_boxes[i]\n                    height = bbsoi.shape[0]\n                    for bb in bbsoi.bounding_boxes:\n                        # after flip, y1 ends up right of y2\n                        y1, y2 = bb.y1, bb.y2\n                        bb.y1 = height - y2\n                        bb.y2 = height - y1\n\n                if batch.polygons is not None:\n                    psoi = batch.polygons[i]\n                    height = psoi.shape[0]\n                    for poly in psoi.polygons:\n                        # TODO maybe reverse the order of points afterwards?\n                        #      the flip probably inverts them\n                        poly.exterior[:, 1] = height - poly.exterior[:, 1]\n\n                if batch.line_strings is not None:\n                    lsoi = batch.line_strings[i]\n                    height = lsoi.shape[0]\n                    for ls in lsoi.line_strings:\n                        # TODO maybe reverse the order of points afterwards?\n                        #      the flip probably inverts them\n                        ls.coords[:, 1] = height - ls.coords[:, 1]\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.p]\n"
  },
  {
    "path": "imgaug/augmenters/geometric.py",
    "content": "\"\"\"Augmenters that apply affine or similar transformations.\n\nList of augmenters:\n\n    * :class:`Affine`\n    * :class:`ScaleX`\n    * :class:`ScaleY`\n    * :class:`TranslateX`\n    * :class:`TranslateY`\n    * :class:`Rotate`\n    * :class:`ShearX`\n    * :class:`ShearY`\n    * :class:`AffineCv2`\n    * :class:`PiecewiseAffine`\n    * :class:`PerspectiveTransform`\n    * :class:`ElasticTransformation`\n    * :class:`Rot90`\n    * :class:`WithPolarWarping`\n    * :class:`Jigsaw`\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport math\nimport functools\nimport itertools\n\nimport numpy as np\nfrom scipy import ndimage\nfrom skimage import transform as tf\nimport cv2\nimport six.moves as sm\n\nimport imgaug as ia\nfrom imgaug.imgaug import _normalize_cv2_input_arr_\nfrom imgaug.augmentables.polys import _ConcavePolygonRecoverer\nfrom . import meta\nfrom . import blur as blur_lib\nfrom . import size as size_lib\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\nfrom .. import random as iarandom\n\n\n_WARP_AFF_VALID_DTYPES_CV2_ORDER_0 = iadt._convert_dtype_strs_to_types(\n    \"uint8 uint16 int8 int16 int32 float16 float32 float64 bool\"\n)  # Added in 0.5.0.\n_WARP_AFF_VALID_DTYPES_CV2_ORDER_NOT_0 = iadt._convert_dtype_strs_to_types(\n    \"uint8 uint16 int8 int16 float16 float32 float64 bool\"\n)  # Added in 0.5.0.\n\n# skimage | cv2\n# 0       | cv2.INTER_NEAREST\n# 1       | cv2.INTER_LINEAR\n# 2       | -\n# 3       | cv2.INTER_CUBIC\n# 4       | -\n_AFFINE_INTERPOLATION_ORDER_SKIMAGE_TO_CV2 = {\n    0: cv2.INTER_NEAREST,\n    1: cv2.INTER_LINEAR,\n    2: cv2.INTER_CUBIC,\n    3: cv2.INTER_CUBIC,\n    4: cv2.INTER_CUBIC\n}\n\n# constant, edge, symmetric, reflect, wrap\n# skimage   | cv2\n# constant  | cv2.BORDER_CONSTANT\n# edge      | cv2.BORDER_REPLICATE\n# symmetric | cv2.BORDER_REFLECT\n# reflect   | cv2.BORDER_REFLECT_101\n# wrap      | cv2.BORDER_WRAP\n_AFFINE_MODE_SKIMAGE_TO_CV2 = {\n    \"constant\": cv2.BORDER_CONSTANT,\n    \"edge\": cv2.BORDER_REPLICATE,\n    \"symmetric\": cv2.BORDER_REFLECT,\n    \"reflect\": cv2.BORDER_REFLECT_101,\n    \"wrap\": cv2.BORDER_WRAP\n}\n\n_PI = 3.141592653589793\n_RAD_PER_DEGREE = _PI / 180\n\n\n@iap._prefetchable\ndef _handle_order_arg(order, backend):\n    # Peformance in skimage for Affine:\n    #  1.0x order 0\n    #  1.5x order 1\n    #  3.0x order 3\n    # 30.0x order 4\n    # 60.0x order 5\n    # measurement based on 256x256x3 batches, difference is smaller\n    # on smaller images (seems to grow more like exponentially with image\n    # size)\n    if order == ia.ALL:\n        if backend in [\"auto\", \"cv2\"]:\n            return iap.Choice([0, 1, 3])\n        # dont use order=2 (bi-quadratic) because that is apparently\n        # currently not recommended (and throws a warning)\n        return iap.Choice([0, 1, 3, 4, 5])\n    if ia.is_single_integer(order):\n        assert 0 <= order <= 5, (\n            \"Expected order's integer value to be in the interval [0, 5], \"\n            \"got %d.\" % (order,))\n        if backend == \"cv2\":\n            assert order in [0, 1, 3], (\n                \"Backend \\\"cv2\\\" and order=%d was chosen, but cv2 backend \"\n                \"can only handle order 0, 1 or 3.\" % (order,))\n        return iap.Deterministic(order)\n    if isinstance(order, list):\n        assert all([ia.is_single_integer(val) for val in order]), (\n            \"Expected order list to only contain integers, \"\n            \"got types %s.\" % (str([type(val) for val in order]),))\n        assert all([0 <= val <= 5 for val in order]), (\n            \"Expected all of order's integer values to be in range \"\n            \"0 <= x <= 5, got %s.\" % (str(order),))\n        if backend == \"cv2\":\n            assert all([val in [0, 1, 3] for val in order]), (\n                \"cv2 backend can only handle order 0, 1 or 3. Got order \"\n                \"list of %s.\" % (order,))\n        return iap.Choice(order)\n    if isinstance(order, iap.StochasticParameter):\n        return order\n    raise Exception(\n        \"Expected order to be imgaug.ALL, int, list of int or \"\n        \"StochasticParameter, got %s.\" % (type(order),))\n\n\n@iap._prefetchable\ndef _handle_cval_arg(cval):\n    if cval == ia.ALL:\n        # TODO change this so that it is dynamically created per image\n        #      (or once per dtype)\n        return iap.Uniform(0, 255)  # skimage transform expects float\n    return iap.handle_continuous_param(\n        cval,\n        \"cval\",\n        value_range=None,\n        tuple_to_uniform=True,\n        list_to_choice=True\n    )\n\n\n# currently used for Affine and PiecewiseAffine\n# TODO use iap.handle_categorical_string_param() here\n@iap._prefetchable_str\ndef _handle_mode_arg(mode):\n    if mode == ia.ALL:\n        return iap.Choice([\"constant\", \"edge\", \"symmetric\", \"reflect\", \"wrap\"])\n    if ia.is_string(mode):\n        return iap.Deterministic(mode)\n    if isinstance(mode, list):\n        assert all([ia.is_string(val) for val in mode]), (\n            \"Expected list of modes to only contain strings, got \"\n            \"types %s\" % (\", \".join([str(type(v)) for v in mode]))\n        )\n        return iap.Choice(mode)\n    if isinstance(mode, iap.StochasticParameter):\n        return mode\n    raise Exception(\n        \"Expected mode to be imgaug.ALL, a string, a list of strings \"\n        \"or StochasticParameter, got %s.\" % (type(mode),)\n    )\n\n\ndef _warp_affine_arr(arr, matrix, order=1, mode=\"constant\", cval=0,\n                     output_shape=None, backend=\"auto\"):\n    # no changes to zero-sized arrays\n    if arr.size == 0:\n        return arr\n\n    if ia.is_single_integer(cval) or ia.is_single_float(cval):\n        cval = [cval] * len(arr.shape[2])\n\n    min_value, _center_value, max_value = \\\n        iadt.get_value_range_of_dtype(arr.dtype)\n\n    cv2_bad_order = order not in [0, 1, 3]\n    if order == 0:\n        cv2_bad_dtype = (arr.dtype\n                         not in _WARP_AFF_VALID_DTYPES_CV2_ORDER_0)\n    else:\n        cv2_bad_dtype = (arr.dtype\n                         not in _WARP_AFF_VALID_DTYPES_CV2_ORDER_NOT_0)\n    cv2_impossible = cv2_bad_order or cv2_bad_dtype\n    use_skimage = (\n        backend == \"skimage\"\n        or (backend == \"auto\" and cv2_impossible)\n    )\n    if use_skimage:\n        # cval contains 3 values as cv2 can handle 3, but\n        # skimage only 1\n        cval = cval[0]\n        # skimage does not clip automatically\n        cval = max(min(cval, max_value), min_value)\n        image_warped = _warp_affine_arr_skimage(\n            arr,\n            matrix,\n            cval=cval,\n            mode=mode,\n            order=order,\n            output_shape=output_shape\n        )\n    else:\n        assert not cv2_bad_dtype, (\n            not cv2_bad_dtype,\n            \"cv2 backend in Affine got a dtype %s, which it \"\n            \"cannot handle. Try using a different dtype or set \"\n            \"order=0.\" % (\n                arr.dtype,))\n        cval_type = float if arr.dtype.kind == \"f\" else int\n        image_warped = _warp_affine_arr_cv2(\n            arr,\n            matrix,\n            cval=tuple([cval_type(v) for v in cval]),\n            mode=mode,\n            order=order,\n            output_shape=output_shape\n        )\n    return image_warped\n\n\ndef _warp_affine_arr_skimage(arr, matrix, cval, mode, order, output_shape):\n    iadt.gate_dtypes_strs(\n        {arr.dtype},\n        allowed=\"bool uint8 uint16 uint32 int8 int16 int32 \"\n                \"float16 float32 float64\",\n        disallowed=\"uint64 int64 float128\"\n    )\n\n    input_dtype = arr.dtype\n\n    # tf.warp() produces a deprecation warning for bool images with\n    # order!=0. We either need to convert them to float or use NN\n    # interpolation.\n    if input_dtype == iadt._BOOL_DTYPE and order != 0:\n        arr = arr.astype(np.float32)\n\n    image_warped = tf.warp(\n        arr,\n        np.linalg.inv(matrix),\n        order=order,\n        mode=mode,\n        cval=cval,\n        preserve_range=True,\n        output_shape=output_shape,\n    )\n\n    # tf.warp changes all dtypes to float64, including uint8\n    if input_dtype.kind == \"b\":\n        image_warped = image_warped > 0.5\n    else:\n        image_warped = iadt.restore_dtypes_(image_warped, input_dtype)\n\n    return image_warped\n\n\ndef _warp_affine_arr_cv2(arr, matrix, cval, mode, order, output_shape):\n    iadt.gate_dtypes_strs(\n        {arr.dtype},\n        allowed=\"bool uint8 uint16 int8 int16 int32 float16 float32 float64\",\n        disallowed=\"uint32 uint64 int64 float128\"\n    )\n\n    if order != 0:\n        assert arr.dtype != iadt._INT32_DTYPE, (\n            \"Affine only supports cv2-based transformations of int32 \"\n            \"arrays when using order=0, but order was set to %d.\" % (\n                order,))\n\n    input_dtype = arr.dtype\n    if input_dtype in {iadt._BOOL_DTYPE, iadt._FLOAT16_DTYPE}:\n        arr = arr.astype(np.float32)\n    elif input_dtype == iadt._INT8_DTYPE and order != 0:\n        arr = arr.astype(np.int16)\n\n    dsize = (\n        int(np.round(output_shape[1])),\n        int(np.round(output_shape[0]))\n    )\n\n    # map key X from skimage to cv2 or fall back to key X\n    mode = _AFFINE_MODE_SKIMAGE_TO_CV2.get(mode, mode)\n    order = _AFFINE_INTERPOLATION_ORDER_SKIMAGE_TO_CV2.get(order, order)\n\n    # TODO this uses always a tuple of 3 values for cval, even if\n    #      #chans != 3, works with 1d but what in other cases?\n    nb_channels = arr.shape[-1]\n    if nb_channels <= 3:\n        # TODO this block can also be when order==0 for any nb_channels,\n        #      but was deactivated for now, because cval would always\n        #      contain 3 values and not nb_channels values\n        image_warped = cv2.warpAffine(\n            _normalize_cv2_input_arr_(arr),\n            matrix[0:2, :],\n            dsize=dsize,\n            flags=order,\n            borderMode=mode,\n            borderValue=cval\n        )\n\n        # cv2 warp drops last axis if shape is (H, W, 1)\n        if image_warped.ndim == 2:\n            image_warped = image_warped[..., np.newaxis]\n    else:\n        # warp each channel on its own, re-add channel axis, then stack\n        # the result from a list of [H, W, 1] to (H, W, C).\n        image_warped = [\n            cv2.warpAffine(\n                _normalize_cv2_input_arr_(arr[:, :, c]),\n                matrix[0:2, :],\n                dsize=dsize,\n                flags=order,\n                borderMode=mode,\n                borderValue=tuple([cval[0]])\n            )\n            for c in sm.xrange(nb_channels)\n        ]\n        image_warped = np.stack(image_warped, axis=-1)\n\n    if input_dtype.kind == \"b\":\n        image_warped = image_warped > 0.5\n    elif input_dtype in {iadt._INT8_DTYPE, iadt._FLOAT16_DTYPE}:\n        image_warped = iadt.restore_dtypes_(image_warped, input_dtype)\n\n    return image_warped\n\n\n# Added in 0.5.0.\ndef _warp_affine_coords(coords, matrix):\n    if len(coords) == 0:\n        return coords\n    assert coords.shape[1] == 2\n    assert matrix.shape == (3, 3)\n\n    # this is the same as in scikit-image, _geometric.py -> _apply_mat()\n    x, y = np.transpose(coords)\n    src = np.vstack((x, y, np.ones_like(x)))\n    dst = np.dot(src.T, matrix.T)\n\n    # below, we will divide by the last dimension of the homogeneous\n    # coordinate matrix. In order to avoid division by zero,\n    # we replace exact zeros in this column with a very small number.\n    dst[dst[:, 2] == 0, 2] = np.finfo(float).eps\n    # rescale to homogeneous coordinates\n    dst[:, :2] /= dst[:, 2:3]\n\n    return dst[:, :2]\n\n\ndef _compute_affine_warp_output_shape(matrix, input_shape):\n    height, width = input_shape[:2]\n\n    if height == 0 or width == 0:\n        return matrix, input_shape\n\n    # determine shape of output image\n    corners = np.array([\n        [0, 0],\n        [0, height - 1],\n        [width - 1, height - 1],\n        [width - 1, 0]\n    ])\n    corners = _warp_affine_coords(corners, matrix)\n    minc = corners[:, 0].min()\n    minr = corners[:, 1].min()\n    maxc = corners[:, 0].max()\n    maxr = corners[:, 1].max()\n    out_height = maxr - minr + 1\n    out_width = maxc - minc + 1\n    if len(input_shape) == 3:\n        output_shape = np.ceil((out_height, out_width, input_shape[2]))\n    else:\n        output_shape = np.ceil((out_height, out_width))\n    output_shape = tuple([int(v) for v in output_shape.tolist()])\n    # fit output image in new shape\n    translation = (-minc, -minr)\n    matrix = _AffineMatrixGenerator(matrix).translate(\n        x_px=translation[0], y_px=translation[1]\n    ).matrix\n    return matrix, output_shape\n\n\n# TODO allow -1 destinations\ndef apply_jigsaw(arr, destinations):\n    \"\"\"Move cells of an image similar to a jigsaw puzzle.\n\n    This function will split the image into ``rows x cols`` cells and\n    move each cell to the target index given in `destinations`.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; fully tested\n        * ``uint32``: yes; fully tested\n        * ``uint64``: yes; fully tested\n        * ``int8``: yes; fully tested\n        * ``int16``: yes; fully tested\n        * ``int32``: yes; fully tested\n        * ``int64``: yes; fully tested\n        * ``float16``: yes; fully tested\n        * ``float32``: yes; fully tested\n        * ``float64``: yes; fully tested\n        * ``float128``: yes; fully tested\n        * ``bool``: yes; fully tested\n\n    Parameters\n    ----------\n    arr : ndarray\n        Array with at least two dimensions denoting height and width.\n\n    destinations : ndarray\n        2-dimensional array containing for each cell the id of the destination\n        cell. The order is expected to a flattened c-order, i.e. row by row.\n        The height of the image must be evenly divisible by the number of\n        rows in this array. Analogous for the width and columns.\n\n    Returns\n    -------\n    ndarray\n        Modified image with cells moved according to `destinations`.\n\n    \"\"\"\n    # pylint complains about unravel_index() here\n    # pylint: disable=unbalanced-tuple-unpacking\n\n    nb_rows, nb_cols = destinations.shape[0:2]\n\n    assert arr.ndim >= 2, (\n        \"Expected array with at least two dimensions, but got %d with \"\n        \"shape %s.\" % (arr.ndim, arr.shape))\n    assert (arr.shape[0] % nb_rows) == 0, (\n        \"Expected image height to by divisible by number of rows, but got \"\n        \"height %d and %d rows. Use cropping or padding to modify the image \"\n        \"height or change the number of rows.\" % (arr.shape[0], nb_rows)\n    )\n    assert (arr.shape[1] % nb_cols) == 0, (\n        \"Expected image width to by divisible by number of columns, but got \"\n        \"width %d and %d columns. Use cropping or padding to modify the image \"\n        \"width or change the number of columns.\" % (arr.shape[1], nb_cols)\n    )\n\n    cell_height = arr.shape[0] // nb_rows\n    cell_width = arr.shape[1] // nb_cols\n\n    dest_rows, dest_cols = np.unravel_index(\n        destinations.flatten(), (nb_rows, nb_cols))\n\n    result = np.zeros_like(arr)\n    i = 0\n    for source_row in np.arange(nb_rows):\n        for source_col in np.arange(nb_cols):\n            # TODO vectorize coords computation\n            dest_row, dest_col = dest_rows[i], dest_cols[i]\n\n            source_y1 = source_row * cell_height\n            source_y2 = source_y1 + cell_height\n            source_x1 = source_col * cell_width\n            source_x2 = source_x1 + cell_width\n\n            dest_y1 = dest_row * cell_height\n            dest_y2 = dest_y1 + cell_height\n            dest_x1 = dest_col * cell_width\n            dest_x2 = dest_x1 + cell_width\n\n            source = arr[source_y1:source_y2, source_x1:source_x2]\n            result[dest_y1:dest_y2, dest_x1:dest_x2] = source\n\n            i += 1\n\n    return result\n\n\ndef apply_jigsaw_to_coords(coords, destinations, image_shape):\n    \"\"\"Move coordinates on an image similar to a jigsaw puzzle.\n\n    This is the same as :func:`apply_jigsaw`, but moves coordinates within\n    the cells.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    coords : ndarray\n        ``(N, 2)`` array denoting xy-coordinates.\n\n    destinations : ndarray\n        See :func:`apply_jigsaw`.\n\n    image_shape : tuple of int\n        ``(height, width, ...)`` shape of the image on which the\n        coordinates are placed. Only height and width are required.\n\n    Returns\n    -------\n    ndarray\n        Moved coordinates.\n\n    \"\"\"\n    # pylint complains about unravel_index() here\n    # pylint: disable=unbalanced-tuple-unpacking\n\n    nb_rows, nb_cols = destinations.shape[0:2]\n\n    height, width = image_shape[0:2]\n    cell_height = height // nb_rows\n    cell_width = width // nb_cols\n\n    dest_rows, dest_cols = np.unravel_index(\n        destinations.flatten(), (nb_rows, nb_cols))\n\n    result = np.copy(coords)\n\n    # TODO vectorize this loop\n    for i, (x, y) in enumerate(coords):\n        ooi_x = (x < 0 or x >= width)\n        ooi_y = (y < 0 or y >= height)\n        if ooi_x or ooi_y:\n            continue\n\n        source_row = int(y // cell_height)\n        source_col = int(x // cell_width)\n        source_cell_idx = (source_row * nb_cols) + source_col\n        dest_row = dest_rows[source_cell_idx]\n        dest_col = dest_cols[source_cell_idx]\n\n        source_y1 = source_row * cell_height\n        source_x1 = source_col * cell_width\n\n        dest_y1 = dest_row * cell_height\n        dest_x1 = dest_col * cell_width\n\n        result[i, 0] = dest_x1 + (x - source_x1)\n        result[i, 1] = dest_y1 + (y - source_y1)\n\n    return result\n\n\ndef generate_jigsaw_destinations(nb_rows, nb_cols, max_steps, seed,\n                                 connectivity=4):\n    \"\"\"Generate a destination pattern for :func:`apply_jigsaw`.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    nb_rows : int\n        Number of rows to split the image into.\n\n    nb_cols : int\n        Number of columns to split the image into.\n\n    max_steps : int\n        Maximum number of cells that each cell may be moved.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState\n        Seed value or alternatively RNG to use.\n        If ``None`` the global RNG will be used.\n\n    connectivity : int, optional\n        Whether a diagonal move of a cell counts as one step\n        (``connectivity=8``) or two steps (``connectivity=4``).\n\n    Returns\n    -------\n    ndarray\n        2-dimensional array containing for each cell the id of the target\n        cell.\n\n    \"\"\"\n    assert connectivity in (4, 8), (\n        \"Expected connectivity of 4 or 8, got %d.\" % (connectivity,))\n    random_state = iarandom.RNG.create_if_not_rng_(seed)\n    steps = random_state.integers(0, max_steps, size=(nb_rows, nb_cols),\n                                  endpoint=True)\n    directions = random_state.integers(0, connectivity,\n                                       size=(nb_rows, nb_cols, max_steps),\n                                       endpoint=False)\n    destinations = np.arange(nb_rows*nb_cols).reshape((nb_rows, nb_cols))\n\n    for step in np.arange(max_steps):\n        directions_step = directions[:, :, step]\n\n        for y in np.arange(nb_rows):\n            for x in np.arange(nb_cols):\n                if steps[y, x] > 0:\n                    y_target, x_target = {\n                        0: (y-1, x+0),\n                        1: (y+0, x+1),\n                        2: (y+1, x+0),\n                        3: (y+0, x-1),\n                        4: (y-1, x-1),\n                        5: (y-1, x+1),\n                        6: (y+1, x+1),\n                        7: (y+1, x-1)\n                    }[directions_step[y, x]]\n                    y_target = max(min(y_target, nb_rows-1), 0)\n                    x_target = max(min(x_target, nb_cols-1), 0)\n\n                    target_steps = steps[y_target, x_target]\n                    if (y, x) != (y_target, x_target) and target_steps >= 1:\n                        source_dest = destinations[y, x]\n                        target_dest = destinations[y_target, x_target]\n                        destinations[y, x] = target_dest\n                        destinations[y_target, x_target] = source_dest\n\n                        steps[y, x] -= 1\n                        steps[y_target, x_target] -= 1\n\n    return destinations\n\n\n# Added in 0.5.0.\nclass _AffineMatrixGenerator(object):\n    # Added in 0.5.0.\n    def __init__(self, matrix=None):\n        if matrix is None:\n            matrix = np.eye(3, dtype=np.float32)\n        self.matrix = matrix\n\n    # Added in 0.5.0.\n    def centerize(self, image_shape):\n        height, width = image_shape[0:2]\n        self.translate(-width/2, -height/2)\n        return self\n\n    # Added in 0.5.0.\n    def invert_centerize(self, image_shape):\n        height, width = image_shape[0:2]\n        self.translate(width/2, height/2)\n        return self\n\n    # Added in 0.5.0.\n    def translate(self, x_px, y_px):\n        if x_px < 1e-4 or x_px > 1e-4 or y_px < 1e-4 or x_px > 1e-4:\n            matrix = np.array([\n                [1, 0, x_px],\n                [0, 1, y_px],\n                [0, 0, 1]\n            ], dtype=np.float32)\n            self._mul(matrix)\n        return self\n\n    # Added in 0.5.0.\n    def scale(self, x_frac, y_frac):\n        if (x_frac < 1.0-1e-4 or x_frac > 1.0+1e-4\n                or y_frac < 1.0-1e-4 or y_frac > 1.0+1e-4):\n            matrix = np.array([\n                [x_frac, 0, 0],\n                [0, y_frac, 0],\n                [0, 0, 1]\n            ], dtype=np.float32)\n            self._mul(matrix)\n        return self\n\n    # Added in 0.5.0.\n    def rotate(self, rad):\n        if rad < 1e-4 or rad > 1e-4:\n            rad = -rad\n            matrix = np.array([\n                [np.cos(rad), np.sin(rad), 0],\n                [-np.sin(rad), np.cos(rad), 0],\n                [0, 0, 1]\n            ], dtype=np.float32)\n            self._mul(matrix)\n        return self\n\n    # Added in 0.5.0.\n    def shear(self, x_rad, y_rad):\n        if x_rad < 1e-4 or x_rad > 1e-4 or y_rad < 1e-4 or y_rad > 1e-4:\n            matrix = np.array([\n                [1, np.tanh(-x_rad), 0],\n                [np.tanh(y_rad), 1, 0],\n                [0, 0, 1]\n            ], dtype=np.float32)\n            self._mul(matrix)\n        return self\n\n    # Added in 0.5.0.\n    def _mul(self, matrix):\n        self.matrix = np.matmul(matrix, self.matrix)\n\n\nclass _AffineSamplingResult(object):\n    def __init__(self, scale=None, translate=None, translate_mode=\"px\",\n                 rotate=None, shear=None, cval=None, mode=None, order=None):\n        self.scale = scale\n        self.translate = translate\n        self.translate_mode = translate_mode\n        self.rotate = rotate\n        self.shear = shear\n        self.cval = cval\n        self.mode = mode\n        self.order = order\n\n    # Added in 0.4.0.\n    def get_affine_parameters(self, idx, arr_shape, image_shape):\n        scale_y = self.scale[1][idx]  # TODO 1 and 0 should be inverted here\n        scale_x = self.scale[0][idx]\n\n        translate_y = self.translate[1][idx]  # TODO same as above\n        translate_x = self.translate[0][idx]\n        assert self.translate_mode in [\"px\", \"percent\"], (\n            \"Expected 'px' or 'percent', got '%s'.\" % (self.translate_mode,)\n        )\n\n        if self.translate_mode == \"percent\":\n            translate_y_px = translate_y * arr_shape[0]\n            translate_x_px = translate_x * arr_shape[1]\n        else:\n            translate_y_px = (translate_y / image_shape[0]) * arr_shape[0]\n            translate_x_px = (translate_x / image_shape[1]) * arr_shape[1]\n\n        rotate_deg = self.rotate[idx]\n        shear_x_deg = self.shear[0][idx]\n        shear_y_deg = self.shear[1][idx]\n\n        rotate_rad = rotate_deg * _RAD_PER_DEGREE\n        shear_x_rad = shear_x_deg * _RAD_PER_DEGREE\n        shear_y_rad = shear_y_deg * _RAD_PER_DEGREE\n\n        # we add the _deg versions of rotate and shear here for PILAffine,\n        # Affine itself only uses *_rad\n        return {\n            \"scale_y\": scale_y,\n            \"scale_x\": scale_x,\n            \"translate_y_px\": translate_y_px,\n            \"translate_x_px\": translate_x_px,\n            \"rotate_rad\": rotate_rad,\n            \"shear_y_rad\": shear_y_rad,\n            \"shear_x_rad\": shear_x_rad,\n            \"rotate_deg\": rotate_deg,\n            \"shear_y_deg\": shear_y_deg,\n            \"shear_x_deg\": shear_x_deg\n        }\n\n    # for images we use additional shifts of (0.5, 0.5) as otherwise\n    # we get an ugly black border for 90deg rotations\n    def to_matrix(\n            self,\n            idx,\n            arr_shape,\n            image_shape,\n            fit_output,\n            shift_add=(0.5, 0.5)\n    ):\n        if 0 in image_shape:\n            return np.eye(3, dtype=np.float32), arr_shape\n\n        params = self.get_affine_parameters(\n            idx, arr_shape=arr_shape, image_shape=image_shape\n        )\n\n        matrix_gen = _AffineMatrixGenerator()\n        matrix_gen.centerize(arr_shape)\n        matrix_gen.translate(x_px=shift_add[1], y_px=shift_add[0])\n        matrix_gen.rotate(params[\"rotate_rad\"])\n        matrix_gen.scale(x_frac=params[\"scale_x\"], y_frac=params[\"scale_y\"])\n        matrix_gen.shear(x_rad=params[\"shear_x_rad\"],\n                         y_rad=params[\"shear_y_rad\"])\n        matrix_gen.translate(x_px=params[\"translate_x_px\"],\n                             y_px=params[\"translate_y_px\"])\n        matrix_gen.translate(x_px=-shift_add[1], y_px=-shift_add[0])\n        matrix_gen.invert_centerize(arr_shape)\n\n        matrix = matrix_gen.matrix\n        if fit_output:\n            matrix, arr_shape = _compute_affine_warp_output_shape(\n                matrix, arr_shape\n            )\n        return matrix, arr_shape\n\n    # Added in 0.4.0.\n    def to_matrix_cba(self, idx, arr_shape, fit_output, shift_add=(0.0, 0.0)):\n        return self.to_matrix(idx, arr_shape, arr_shape, fit_output, shift_add)\n\n    # Added in 0.4.0.\n    def copy(self):\n        return _AffineSamplingResult(\n            scale=self.scale,\n            translate=self.translate,\n            translate_mode=self.translate_mode,\n            rotate=self.rotate,\n            shear=self.shear,\n            cval=self.cval,\n            mode=self.mode,\n            order=self.order\n        )\n\n\ndef _is_identity_matrix(matrix, eps=1e-4):\n    identity = np.eye(3, dtype=np.float32)\n    # about twice as fast as np.allclose()\n    return np.average(np.abs(matrix - identity)) <= eps\n\n\nclass Affine(meta.Augmenter):\n    \"\"\"\n    Augmenter to apply affine transformations to images.\n\n    This is mostly a wrapper around the corresponding classes and functions\n    in OpenCV and skimage.\n\n    Affine transformations involve:\n\n        - Translation (\"move\" image on the x-/y-axis)\n        - Rotation\n        - Scaling (\"zoom\" in/out)\n        - Shear (move one side of the image, turning a square into a trapezoid)\n\n    All such transformations can create \"new\" pixels in the image without a\n    defined content, e.g. if the image is translated to the left, pixels\n    are created on the right.\n    A method has to be defined to deal with these pixel values. The\n    parameters `cval` and `mode` of this class deal with this.\n\n    Some transformations involve interpolations between several pixels\n    of the input image to generate output pixel values. The parameter `order`\n    deals with the method of interpolation used for this.\n\n    .. note::\n\n        While this augmenter supports segmentation maps and heatmaps that\n        have a different size than the corresponding image, it is strongly\n        recommended to use the same aspect ratios. E.g. for an image of\n        shape ``(200, 100, 3)``, good segmap/heatmap array shapes also follow\n        a ``2:1`` ratio and ideally are ``(200, 100, C)``, ``(100, 50, C)`` or\n        ``(50, 25, C)``. Otherwise, transformations involving rotations or\n        shearing will produce unaligned outputs.\n        For performance reasons, there is no explicit validation of whether\n        the aspect ratios are similar.\n\n    **Supported dtypes**:\n\n    if (backend=\"skimage\", order in [0, 1]):\n\n        * ``uint8``: yes; tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested (1)\n        * ``uint64``: no (2)\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested  (1)\n        * ``int64``: no (2)\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no (2)\n        * ``bool``: yes; tested\n\n        - (1) scikit-image converts internally to float64, which might\n              affect the accuracy of large integers. In tests this seemed\n              to not be an issue.\n        - (2) results too inaccurate\n\n    if (backend=\"skimage\", order in [3, 4]):\n\n        * ``uint8``: yes; tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested (1)\n        * ``uint64``: no (2)\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested  (1)\n        * ``int64``: no (2)\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: limited; tested (3)\n        * ``float128``: no (2)\n        * ``bool``: yes; tested\n\n        - (1) scikit-image converts internally to float64, which might\n              affect the accuracy of large integers. In tests this seemed\n              to not be an issue.\n        - (2) results too inaccurate\n        - (3) ``NaN`` around minimum and maximum of float64 value range\n\n    if (backend=\"skimage\", order=5]):\n\n            * ``uint8``: yes; tested\n            * ``uint16``: yes; tested\n            * ``uint32``: yes; tested (1)\n            * ``uint64``: no (2)\n            * ``int8``: yes; tested\n            * ``int16``: yes; tested\n            * ``int32``: yes; tested  (1)\n            * ``int64``: no (2)\n            * ``float16``: yes; tested\n            * ``float32``: yes; tested\n            * ``float64``: limited; not tested (3)\n            * ``float128``: no (2)\n            * ``bool``: yes; tested\n\n            - (1) scikit-image converts internally to ``float64``, which\n                  might affect the accuracy of large integers. In tests\n                  this seemed to not be an issue.\n            - (2) results too inaccurate\n            - (3) ``NaN`` around minimum and maximum of float64 value range\n\n    if (backend=\"cv2\", order=0):\n\n        * ``uint8``: yes; tested\n        * ``uint16``: yes; tested\n        * ``uint32``: no (1)\n        * ``uint64``: no (2)\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: no (2)\n        * ``float16``: yes; tested (3)\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no (1)\n        * ``bool``: yes; tested (3)\n\n        - (1) rejected by cv2\n        - (2) changed to ``int32`` by cv2\n        - (3) mapped internally to ``float32``\n\n    if (backend=\"cv2\", order=1):\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: no (1)\n        * ``uint64``: no (2)\n        * ``int8``: yes; tested (3)\n        * ``int16``: yes; tested\n        * ``int32``: no (2)\n        * ``int64``: no (2)\n        * ``float16``: yes; tested (4)\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no (1)\n        * ``bool``: yes; tested (4)\n\n        - (1) rejected by cv2\n        - (2) causes cv2 error: ``cv2.error: OpenCV(3.4.4)\n              (...)imgwarp.cpp:1805: error:\n              (-215:Assertion failed) ifunc != 0 in function 'remap'``\n        - (3) mapped internally to ``int16``\n        - (4) mapped internally to ``float32``\n\n    if (backend=\"cv2\", order=3):\n\n        * ``uint8``: yes; tested\n        * ``uint16``: yes; tested\n        * ``uint32``: no (1)\n        * ``uint64``: no (2)\n        * ``int8``: yes; tested (3)\n        * ``int16``: yes; tested\n        * ``int32``: no (2)\n        * ``int64``: no (2)\n        * ``float16``: yes; tested (4)\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no (1)\n        * ``bool``: yes; tested (4)\n\n        - (1) rejected by cv2\n        - (2) causes cv2 error: ``cv2.error: OpenCV(3.4.4)\n              (...)imgwarp.cpp:1805: error:\n              (-215:Assertion failed) ifunc != 0 in function 'remap'``\n        - (3) mapped internally to ``int16``\n        - (4) mapped internally to ``float32``\n\n\n    Parameters\n    ----------\n    scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter or dict {\"x\": number/tuple/list/StochasticParameter, \"y\": number/tuple/list/StochasticParameter}, optional\n        Scaling factor to use, where ``1.0`` denotes \"no change\" and\n        ``0.5`` is zoomed out to ``50`` percent of the original size.\n\n            * If a single number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``. That value will be\n              used identically for both x- and y-axis.\n            * If a list, then a random value will be sampled from that list\n              per image (again, used for both x- and y-axis).\n            * If a ``StochasticParameter``, then from that parameter a value\n              will be sampled per image (again, used for both x- and y-axis).\n            * If a dictionary, then it is expected to have the keys ``x``\n              and/or ``y``. Each of these keys can have the same values as\n              described above. Using a dictionary allows to set different\n              values for the two axis and sampling will then happen\n              *independently* per axis, resulting in samples that differ\n              between the axes.\n\n    translate_percent : None or number or tuple of number or list of number or imgaug.parameters.StochasticParameter or dict {\"x\": number/tuple/list/StochasticParameter, \"y\": number/tuple/list/StochasticParameter}, optional\n        Translation as a fraction of the image height/width (x-translation,\n        y-translation), where ``0`` denotes \"no change\" and ``0.5`` denotes\n        \"half of the axis size\".\n\n            * If ``None`` then equivalent to ``0.0`` unless `translate_px` has\n              a value other than ``None``.\n            * If a single number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``. That sampled fraction\n              value will be used identically for both x- and y-axis.\n            * If a list, then a random value will be sampled from that list\n              per image (again, used for both x- and y-axis).\n            * If a ``StochasticParameter``, then from that parameter a value\n              will be sampled per image (again, used for both x- and y-axis).\n            * If a dictionary, then it is expected to have the keys ``x``\n              and/or ``y``. Each of these keys can have the same values as\n              described above. Using a dictionary allows to set different\n              values for the two axis and sampling will then happen\n              *independently* per axis, resulting in samples that differ\n              between the axes.\n\n    translate_px : None or int or tuple of int or list of int or imgaug.parameters.StochasticParameter or dict {\"x\": int/tuple/list/StochasticParameter, \"y\": int/tuple/list/StochasticParameter}, optional\n        Translation in pixels.\n\n            * If ``None`` then equivalent to ``0`` unless `translate_percent`\n              has a value other than ``None``.\n            * If a single int, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value will be uniformly sampled\n              per image from the discrete interval ``[a..b]``. That number\n              will be used identically for both x- and y-axis.\n            * If a list, then a random value will be sampled from that list\n              per image (again, used for both x- and y-axis).\n            * If a ``StochasticParameter``, then from that parameter a value\n              will be sampled per image (again, used for both x- and y-axis).\n            * If a dictionary, then it is expected to have the keys ``x``\n              and/or ``y``. Each of these keys can have the same values as\n              described above. Using a dictionary allows to set different\n              values for the two axis and sampling will then happen\n              *independently* per axis, resulting in samples that differ\n              between the axes.\n\n    rotate : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Rotation in degrees (**NOT** radians), i.e. expected value range is\n        around ``[-360, 360]``. Rotation happens around the *center* of the\n        image, not the top left corner as in some other frameworks.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]`` and used as the rotation\n              value.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              sample the rotation value per image.\n\n    shear : number or tuple of number or list of number or imgaug.parameters.StochasticParameter or dict {\"x\": int/tuple/list/StochasticParameter, \"y\": int/tuple/list/StochasticParameter}, optional\n        Shear in degrees (**NOT** radians), i.e. expected value range is\n        around ``[-360, 360]``, with reasonable values being in the range\n        of ``[-45, 45]``.\n\n            * If a number, then that value will be used for all images as\n              the shear on the x-axis (no shear on the y-axis will be done).\n            * If a tuple ``(a, b)``, then two value will be uniformly sampled\n              per image from the interval ``[a, b]`` and be used as the\n              x- and y-shear value.\n            * If a list, then two random values will be sampled from that list\n              per image, denoting x- and y-shear.\n            * If a ``StochasticParameter``, then this parameter will be used\n              to sample the x- and y-shear values per image.\n            * If a dictionary, then similar to `translate_percent`, i.e. one\n              ``x`` key and/or one ``y`` key are expected, denoting the\n              shearing on the x- and y-axis respectively. The allowed datatypes\n              are again ``number``, ``tuple`` ``(a, b)``, ``list`` or\n              ``StochasticParameter``.\n\n    order : int or iterable of int or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        Interpolation order to use. Same meaning as in ``skimage``:\n\n            * ``0``: ``Nearest-neighbor``\n            * ``1``: ``Bi-linear`` (default)\n            * ``2``: ``Bi-quadratic`` (not recommended by skimage)\n            * ``3``: ``Bi-cubic``\n            * ``4``: ``Bi-quartic``\n            * ``5``: ``Bi-quintic``\n\n        Method ``0`` and ``1`` are fast, ``3`` is a bit slower, ``4`` and\n        ``5`` are very slow. If the backend is ``cv2``, the mapping to\n        OpenCV's interpolation modes is as follows:\n\n            * ``0`` -> ``cv2.INTER_NEAREST``\n            * ``1`` -> ``cv2.INTER_LINEAR``\n            * ``2`` -> ``cv2.INTER_CUBIC``\n            * ``3`` -> ``cv2.INTER_CUBIC``\n            * ``4`` -> ``cv2.INTER_CUBIC``\n\n        As datatypes this parameter accepts:\n\n            * If a single ``int``, then that order will be used for all images.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If ``imgaug.ALL``, then equivalant to list ``[0, 1, 3, 4, 5]``\n              in case of ``backend=skimage`` and otherwise ``[0, 1, 3]``.\n            * If ``StochasticParameter``, then that parameter is queried per\n              image to sample the order value to use.\n\n    cval : number or tuple of number or list of number or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        The constant value to use when filling in newly created pixels.\n        (E.g. translating by 1px to the right will create a new 1px-wide\n        column of pixels on the left of the image).  The value is only used\n        when `mode=constant`. The expected value range is ``[0, 255]`` for\n        ``uint8`` images. It may be a float value.\n\n            * If this is a single number, then that value will be used\n              (e.g. 0 results in black pixels).\n            * If a tuple ``(a, b)``, then three values (for three image\n              channels) will be uniformly sampled per image from the\n              interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If ``imgaug.ALL`` then equivalent to tuple ``(0, 255)`.\n            * If a ``StochasticParameter``, a new value will be sampled from\n              the parameter per image.\n\n    mode : str or list of str or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        Method to use when filling in newly created pixels.\n        Same meaning as in ``skimage`` (and :func:`numpy.pad`):\n\n            * ``constant``: Pads with a constant value\n            * ``edge``: Pads with the edge values of array\n            * ``symmetric``: Pads with the reflection of the vector mirrored\n              along the edge of the array.\n            * ``reflect``: Pads with the reflection of the vector mirrored on\n              the first and last values of the vector along each axis.\n            * ``wrap``: Pads with the wrap of the vector along the axis.\n              The first values are used to pad the end and the end values\n              are used to pad the beginning.\n\n        If ``cv2`` is chosen as the backend the mapping is as follows:\n\n            * ``constant`` -> ``cv2.BORDER_CONSTANT``\n            * ``edge`` -> ``cv2.BORDER_REPLICATE``\n            * ``symmetric`` -> ``cv2.BORDER_REFLECT``\n            * ``reflect`` -> ``cv2.BORDER_REFLECT_101``\n            * ``wrap`` -> ``cv2.BORDER_WRAP``\n\n        The datatype of the parameter may be:\n\n            * If a single string, then that mode will be used for all images.\n            * If a list of strings, then a random mode will be picked\n              from that list per image.\n            * If ``imgaug.ALL``, then a random mode from all possible modes\n              will be picked.\n            * If ``StochasticParameter``, then the mode will be sampled from\n              that parameter per image, i.e. it must return only the above\n              mentioned strings.\n\n    fit_output : bool, optional\n        Whether to modify the affine transformation so that the whole output\n        image is always contained in the image plane (``True``) or accept\n        parts of the image being outside the image plane (``False``).\n        This can be thought of as first applying the affine transformation\n        and then applying a second transformation to \"zoom in\" on the new\n        image so that it fits the image plane,\n        This is useful to avoid corners of the image being outside of the image\n        plane after applying rotations. It will however negate translation\n        and scaling.\n        Note also that activating this may lead to image sizes differing from\n        the input image sizes. To avoid this, wrap ``Affine`` in\n        :class:`~imgaug.augmenters.size.KeepSizeByResize`,\n        e.g. ``KeepSizeByResize(Affine(...))``.\n\n    backend : str, optional\n        Framework to use as a backend. Valid values are ``auto``, ``skimage``\n        (scikit-image's warp) and ``cv2`` (OpenCV's warp).\n        If ``auto`` is used, the augmenter will automatically try\n        to use ``cv2`` whenever possible (order must be in ``[0, 1, 3]``). It\n        will silently fall back to skimage if order/dtype is not supported by\n        cv2. cv2 is generally faster than skimage. It also supports RGB cvals,\n        while skimage will resort to intensity cvals (i.e. 3x the same value\n        as RGB). If ``cv2`` is chosen and order is ``2`` or ``4``, it will\n        automatically fall back to order ``3``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Affine(scale=2.0)\n\n    Zoom in on all images by a factor of ``2``.\n\n    >>> aug = iaa.Affine(translate_px=16)\n\n    Translate all images on the x- and y-axis by 16 pixels (towards the\n    bottom right) and fill up any new pixels with zero (black values).\n\n    >>> aug = iaa.Affine(translate_percent=0.1)\n\n    Translate all images on the x- and y-axis by ``10`` percent of their\n    width/height (towards the bottom right). The pixel values are computed\n    per axis based on that axis' size. Fill up any new pixels with zero\n    (black values).\n\n    >>> aug = iaa.Affine(rotate=35)\n\n    Rotate all images by ``35`` *degrees*. Fill up any new pixels with zero\n    (black values).\n\n    >>> aug = iaa.Affine(shear=15)\n\n    Shear all images by ``15`` *degrees*. Fill up any new pixels with zero\n    (black values).\n\n    >>> aug = iaa.Affine(translate_px=(-16, 16))\n\n    Translate all images on the x- and y-axis by a random value\n    between ``-16`` and ``16`` pixels (to the bottom right) and fill up any new\n    pixels with zero (black values). The translation value is sampled once\n    per image and is the same for both axis.\n\n    >>> aug = iaa.Affine(translate_px={\"x\": (-16, 16), \"y\": (-4, 4)})\n\n    Translate all images on the x-axis by a random value\n    between ``-16`` and ``16`` pixels (to the right) and on the y-axis by a\n    random value between ``-4`` and ``4`` pixels to the bottom. The sampling\n    happens independently per axis, so even if both intervals were identical,\n    the sampled axis-wise values would likely be different.\n    This also fills up any new pixels with zero (black values).\n\n    >>> aug = iaa.Affine(scale=2.0, order=[0, 1])\n\n    Same as in the above `scale` example, but uses (randomly) either\n    nearest neighbour interpolation or linear interpolation. If `order` is\n    not specified, ``order=1`` would be used by default.\n\n    >>> aug = iaa.Affine(translate_px=16, cval=(0, 255))\n\n    Same as in the `translate_px` example above, but newly created pixels\n    are now filled with a random color (sampled once per image and the\n    same for all newly created pixels within that image).\n\n    >>> aug = iaa.Affine(translate_px=16, mode=[\"constant\", \"edge\"])\n\n    Similar to the previous example, but the newly created pixels are\n    filled with black pixels in half of all images (mode ``constant`` with\n    default `cval` being ``0``) and in the other half of all images using\n    ``edge`` mode, which repeats the color of the spatially closest pixel\n    of the corresponding image edge.\n\n    >>> aug = iaa.Affine(shear={\"y\": (-45, 45)})\n\n    Shear images only on the y-axis. Set `shear` to ``shear=(-45, 45)`` to\n    shear randomly on both axes, using for each image the same sample for\n    both the x- and y-axis. Use ``shear={\"x\": (-45, 45), \"y\": (-45, 45)}``\n    to get independent samples per axis.\n\n    \"\"\"\n\n    def __init__(self, scale=None, translate_percent=None, translate_px=None,\n                 rotate=None, shear=None, order=1, cval=0, mode=\"constant\",\n                 fit_output=False, backend=\"auto\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Affine, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        params = [scale, translate_percent, translate_px, rotate, shear]\n        if all([p is None for p in params]):\n            scale = {\"x\": (0.9, 1.1), \"y\": (0.9, 1.1)}\n            translate_percent = {\"x\": (-0.1, 0.1), \"y\": (-0.1, 0.1)}\n            rotate = (-15, 15)\n            shear = {\"x\": (-10, 10), \"y\": (-10, 10)}\n        else:\n            scale = scale if scale is not None else 1.0\n            rotate = rotate if rotate is not None else 0.0\n            shear = shear if shear is not None else 0.0\n\n        assert backend in [\"auto\", \"skimage\", \"cv2\"], (\n            \"Expected 'backend' to be \\\"auto\\\", \\\"skimage\\\" or \\\"cv2\\\", \"\n            \"got %s.\" % (backend,))\n        self.backend = backend\n        self.order = _handle_order_arg(order, backend)\n        self.cval = _handle_cval_arg(cval)\n        self.mode = _handle_mode_arg(mode)\n        self.scale = self._handle_scale_arg(scale)\n        self.translate = self._handle_translate_arg(\n            translate_px, translate_percent)\n        self.rotate = iap.handle_continuous_param(\n            rotate, \"rotate\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True)\n        self.shear, self._shear_param_type = self._handle_shear_arg(shear)\n        self.fit_output = fit_output\n\n        # Special order, mode and cval parameters for heatmaps and\n        # segmentation maps. These may either be None or a fixed value.\n        # Stochastic parameters are currently *not* supported.\n        # If set to None, the same values as for images will be used.\n        # That is really not recommended for the cval parameter.\n        #\n        # Segmentation map augmentation by default always pads with a\n        # constant value of 0 (background class id), and always uses nearest\n        # neighbour interpolation. While other pad modes and BG class ids\n        # could be used, the interpolation mode has to be NN as any other\n        # mode would lead to averaging class ids, which makes no sense to do.\n        self._order_heatmaps = 3\n        self._order_segmentation_maps = 0\n        self._mode_heatmaps = \"constant\"\n        self._mode_segmentation_maps = \"constant\"\n        self._cval_heatmaps = 0\n        self._cval_segmentation_maps = 0\n\n    @classmethod\n    def _handle_scale_arg(cls, scale):\n        if isinstance(scale, dict):\n            assert \"x\" in scale or \"y\" in scale, (\n                \"Expected scale dictionary to contain at least key \\\"x\\\" or \"\n                \"key \\\"y\\\". Found neither of them.\")\n            x = scale.get(\"x\", 1.0)\n            y = scale.get(\"y\", 1.0)\n            return (\n                iap.handle_continuous_param(\n                    x, \"scale['x']\", value_range=(0+1e-4, None),\n                    tuple_to_uniform=True, list_to_choice=True),\n                iap.handle_continuous_param(\n                    y, \"scale['y']\", value_range=(0+1e-4, None),\n                    tuple_to_uniform=True, list_to_choice=True)\n            )\n        return iap.handle_continuous_param(\n            scale, \"scale\", value_range=(0+1e-4, None),\n            tuple_to_uniform=True, list_to_choice=True)\n\n    @classmethod\n    def _handle_translate_arg(cls, translate_px, translate_percent):\n        # pylint: disable=no-else-return\n        if translate_percent is None and translate_px is None:\n            translate_px = 0\n\n        assert translate_percent is None or translate_px is None, (\n            \"Expected either translate_percent or translate_px to be \"\n            \"provided, but neither of them was.\")\n\n        if translate_percent is not None:\n            # translate by percent\n            if isinstance(translate_percent, dict):\n                assert \"x\" in translate_percent or \"y\" in translate_percent, (\n                    \"Expected translate_percent dictionary to contain at \"\n                    \"least key \\\"x\\\" or key \\\"y\\\". Found neither of them.\")\n                x = translate_percent.get(\"x\", 0)\n                y = translate_percent.get(\"y\", 0)\n                return (\n                    iap.handle_continuous_param(\n                        x, \"translate_percent['x']\", value_range=None,\n                        tuple_to_uniform=True, list_to_choice=True),\n                    iap.handle_continuous_param(\n                        y, \"translate_percent['y']\", value_range=None,\n                        tuple_to_uniform=True, list_to_choice=True),\n                    \"percent\"\n                )\n            return (\n                iap.handle_continuous_param(\n                    translate_percent, \"translate_percent\",\n                    value_range=None, tuple_to_uniform=True,\n                    list_to_choice=True),\n                None,\n                \"percent\"\n            )\n        else:\n            # translate by pixels\n            if isinstance(translate_px, dict):\n                assert \"x\" in translate_px or \"y\" in translate_px, (\n                    \"Expected translate_px dictionary to contain at \"\n                    \"least key \\\"x\\\" or key \\\"y\\\". Found neither of them.\")\n                x = translate_px.get(\"x\", 0)\n                y = translate_px.get(\"y\", 0)\n                return (\n                    iap.handle_discrete_param(\n                        x, \"translate_px['x']\", value_range=None,\n                        tuple_to_uniform=True, list_to_choice=True,\n                        allow_floats=False),\n                    iap.handle_discrete_param(\n                        y, \"translate_px['y']\", value_range=None,\n                        tuple_to_uniform=True, list_to_choice=True,\n                        allow_floats=False),\n                    \"px\"\n                )\n            return (\n                iap.handle_discrete_param(\n                    translate_px, \"translate_px\", value_range=None,\n                    tuple_to_uniform=True, list_to_choice=True,\n                    allow_floats=False),\n                None,\n                \"px\"\n            )\n\n    # Added in 0.4.0.\n    @classmethod\n    def _handle_shear_arg(cls, shear):\n        # pylint: disable=no-else-return\n        if isinstance(shear, dict):\n            assert \"x\" in shear or \"y\" in shear, (\n                \"Expected shear dictionary to contain at \"\n                \"least key \\\"x\\\" or key \\\"y\\\". Found neither of them.\")\n            x = shear.get(\"x\", 0)\n            y = shear.get(\"y\", 0)\n            return (\n                iap.handle_continuous_param(\n                    x, \"shear['x']\", value_range=None,\n                    tuple_to_uniform=True, list_to_choice=True),\n                iap.handle_continuous_param(\n                    y, \"shear['y']\", value_range=None,\n                    tuple_to_uniform=True, list_to_choice=True)\n            ), \"dict\"\n        else:\n            param_type = \"other\"\n            if ia.is_single_number(shear):\n                param_type = \"single-number\"\n            return iap.handle_continuous_param(\n                shear, \"shear\", value_range=None, tuple_to_uniform=True,\n                list_to_choice=True\n            ), param_type\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        samples = self._draw_samples(batch.nb_rows, random_state)\n\n        if batch.images is not None:\n            batch.images = self._augment_images_by_samples(batch.images,\n                                                           samples)\n\n        if batch.heatmaps is not None:\n            batch.heatmaps = self._augment_maps_by_samples(\n                batch.heatmaps, samples, \"arr_0to1\", self._cval_heatmaps,\n                self._mode_heatmaps, self._order_heatmaps, \"float32\")\n\n        if batch.segmentation_maps is not None:\n            batch.segmentation_maps = self._augment_maps_by_samples(\n                batch.segmentation_maps, samples, \"arr\",\n                self._cval_segmentation_maps, self._mode_segmentation_maps,\n                self._order_segmentation_maps, \"int32\")\n\n        for augm_name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                          \"line_strings\"]:\n            augm_value = getattr(batch, augm_name)\n            if augm_value is not None:\n                for i, cbaoi in enumerate(augm_value):\n                    matrix, output_shape = samples.to_matrix_cba(\n                        i, cbaoi.shape, self.fit_output)\n\n                    if (not _is_identity_matrix(matrix)\n                            and not cbaoi.empty\n                            and 0 not in cbaoi.shape[0:2]):\n                        # TODO this is hacky\n                        if augm_name == \"bounding_boxes\":\n                            # Ensure that 4 points are used for bbs.\n                            # to_keypoints_on_images() does return 4 points,\n                            # to_xy_array() does not.\n                            kpsoi = cbaoi.to_keypoints_on_image()\n                            coords = kpsoi.to_xy_array()\n                            coords_aug = tf.matrix_transform(coords, matrix)\n                            kpsoi = kpsoi.fill_from_xy_array_(coords_aug)\n                            cbaoi = cbaoi.invert_to_keypoints_on_image_(\n                                kpsoi)\n                        else:\n                            coords = cbaoi.to_xy_array()\n                            coords_aug = tf.matrix_transform(coords, matrix)\n                            cbaoi = cbaoi.fill_from_xy_array_(coords_aug)\n\n                    cbaoi.shape = output_shape\n                    augm_value[i] = cbaoi\n\n        return batch\n\n    def _augment_images_by_samples(self, images, samples,\n                                   image_shapes=None,\n                                   return_matrices=False):\n        if image_shapes is None:\n            image_shapes = [image.shape for image in images]\n\n        input_was_array = ia.is_np_array(images)\n        input_dtype = None if not input_was_array else images.dtype\n        result = []\n        matrices = []\n        gen = enumerate(\n            zip(\n                images, image_shapes, samples.cval, samples.mode, samples.order\n            )\n        )\n        for i, (image, image_shape, cval, mode, order) in gen:\n            matrix, output_shape = samples.to_matrix(\n                i, image.shape, image_shape, self.fit_output\n            )\n\n            image_warped = image\n            if not _is_identity_matrix(matrix):\n                image_warped = _warp_affine_arr(\n                    image,\n                    matrix,\n                    order=order,\n                    mode=mode,\n                    cval=cval,\n                    output_shape=output_shape,\n                    backend=self.backend\n                )\n\n            result.append(image_warped)\n\n            if return_matrices:\n                matrices.append(matrix)\n\n        # the shapes can change due to fit_output, then it may not be possible\n        # to return an array, even when the input was an array\n        if input_was_array:\n            nb_shapes = len({image.shape for image in result})\n            if nb_shapes == 1:\n                result = np.array(result, input_dtype)\n\n        if return_matrices:\n            result = (result, matrices)\n        return result\n\n    # Added in 0.4.0.\n    def _augment_maps_by_samples(self, augmentables, samples,\n                                 arr_attr_name, cval, mode, order, cval_dtype):\n        nb_images = len(augmentables)\n\n        samples = samples.copy()\n        if cval is not None:\n            samples.cval = np.full((nb_images, 1), cval, dtype=cval_dtype)\n        if mode is not None:\n            samples.mode = [mode] * nb_images\n        if order is not None:\n            samples.order = [order] * nb_images\n\n        arrs = [getattr(augmentable, arr_attr_name)\n                for augmentable in augmentables]\n        image_shapes = [augmentable.shape for augmentable in augmentables]\n        arrs_aug, matrices = self._augment_images_by_samples(\n            arrs, samples, image_shapes=image_shapes, return_matrices=True)\n\n        gen = zip(augmentables, arrs_aug, matrices, samples.order)\n        for augmentable_i, arr_aug, matrix, order_i in gen:\n            # skip augmented HM/SM arrs for which the images were not\n            # augmented due to being zero-sized\n            if 0 in augmentable_i.shape:\n                continue\n\n            # order=3 matches cubic interpolation and can cause values to go\n            # outside of the range [0.0, 1.0] not clear whether 4+ also do that\n            # We don't clip here for Segmentation Maps, because for these\n            # the value range isn't clearly limited to [0, 1] (and they should\n            # also never use order=3 to begin with).\n            # TODO add test for this\n            if order_i >= 3 and isinstance(augmentable_i, ia.HeatmapsOnImage):\n                arr_aug = np.clip(arr_aug, 0.0, 1.0, out=arr_aug)\n\n            setattr(augmentable_i, arr_attr_name, arr_aug)\n            if self.fit_output:\n                _, output_shape_i = _compute_affine_warp_output_shape(\n                    matrix, augmentable_i.shape\n                )\n            else:\n                output_shape_i = augmentable_i.shape\n            augmentable_i.shape = output_shape_i\n        return augmentables\n\n    def _draw_samples(self, nb_samples, random_state):\n        rngs = random_state.duplicate(12)\n\n        if isinstance(self.scale, tuple):\n            scale_samples = (\n                self.scale[0].draw_samples((nb_samples,), random_state=rngs[0]),\n                self.scale[1].draw_samples((nb_samples,), random_state=rngs[1]),\n            )\n        else:\n            scale_samples = self.scale.draw_samples((nb_samples,),\n                                                    random_state=rngs[2])\n            scale_samples = (scale_samples, scale_samples)\n\n        if self.translate[1] is not None:\n            translate_samples = (\n                self.translate[0].draw_samples((nb_samples,),\n                                               random_state=rngs[3]),\n                self.translate[1].draw_samples((nb_samples,),\n                                               random_state=rngs[4]),\n            )\n        else:\n            translate_samples = self.translate[0].draw_samples(\n                (nb_samples,), random_state=rngs[5])\n            translate_samples = (translate_samples, translate_samples)\n\n        rotate_samples = self.rotate.draw_samples((nb_samples,),\n                                                  random_state=rngs[6])\n        if self._shear_param_type == \"dict\":\n            shear_samples = (\n                self.shear[0].draw_samples((nb_samples,), random_state=rngs[7]),\n                self.shear[1].draw_samples((nb_samples,), random_state=rngs[8])\n            )\n        elif self._shear_param_type == \"single-number\":\n            # only shear on the x-axis if a single number was given\n            shear_samples = self.shear.draw_samples((nb_samples,),\n                                                    random_state=rngs[7])\n            shear_samples = (shear_samples, np.zeros_like(shear_samples))\n        else:\n            shear_samples = self.shear.draw_samples((nb_samples,),\n                                                    random_state=rngs[7])\n            shear_samples = (shear_samples, shear_samples)\n\n        cval_samples = self.cval.draw_samples((nb_samples, 3),\n                                              random_state=rngs[9])\n        mode_samples = self.mode.draw_samples((nb_samples,),\n                                              random_state=rngs[10])\n        order_samples = self.order.draw_samples((nb_samples,),\n                                                random_state=rngs[11])\n\n        return _AffineSamplingResult(\n            scale=scale_samples,\n            translate=translate_samples,\n            translate_mode=self.translate[2],\n            rotate=rotate_samples,\n            shear=shear_samples,\n            cval=cval_samples,\n            mode=mode_samples,\n            order=order_samples\n        )\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [\n            self.scale, self.translate, self.rotate, self.shear, self.order,\n            self.cval, self.mode, self.backend, self.fit_output\n        ]\n\n\nclass ScaleX(Affine):\n    \"\"\"Apply affine scaling on the x-axis to input data.\n\n    This is a wrapper around :class:`Affine`.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    Parameters\n    ----------\n    scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Analogous to ``scale`` in :class:`Affine`, except that this scale\n        value only affects the x-axis. No dictionary input is allowed.\n\n    order : int or iterable of int or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    cval : number or tuple of number or list of number or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    mode : str or list of str or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    fit_output : bool, optional\n        See :class:`Affine`.\n\n    backend : str, optional\n        See :class:`Affine`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.ScaleX((0.5, 1.5))\n\n    Create an augmenter that scales images along the width to sizes between\n    ``50%`` and ``150%``. This does not change the image shape (i.e. height\n    and width), only the pixels within the image are remapped and potentially\n    new ones are filled in.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, scale=(0.5, 1.5), order=1, cval=0, mode=\"constant\",\n                 fit_output=False, backend=\"auto\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ScaleX, self).__init__(\n            scale={\"x\": scale},\n            order=order,\n            cval=cval,\n            mode=mode,\n            fit_output=fit_output,\n            backend=backend,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass ScaleY(Affine):\n    \"\"\"Apply affine scaling on the y-axis to input data.\n\n    This is a wrapper around :class:`Affine`.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    Parameters\n    ----------\n    scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Analogous to ``scale`` in :class:`Affine`, except that this scale\n        value only affects the y-axis. No dictionary input is allowed.\n\n    order : int or iterable of int or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    cval : number or tuple of number or list of number or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    mode : str or list of str or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    fit_output : bool, optional\n        See :class:`Affine`.\n\n    backend : str, optional\n        See :class:`Affine`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.ScaleY((0.5, 1.5))\n\n    Create an augmenter that scales images along the height to sizes between\n    ``50%`` and ``150%``. This does not change the image shape (i.e. height\n    and width), only the pixels within the image are remapped and potentially\n    new ones are filled in.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, scale=(0.5, 1.5), order=1, cval=0, mode=\"constant\",\n                 fit_output=False, backend=\"auto\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ScaleY, self).__init__(\n            scale={\"y\": scale},\n            order=order,\n            cval=cval,\n            mode=mode,\n            fit_output=fit_output,\n            backend=backend,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO make Affine more efficient for translation-only transformations\nclass TranslateX(Affine):\n    \"\"\"Apply affine translation on the x-axis to input data.\n\n    This is a wrapper around :class:`Affine`.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    Parameters\n    ----------\n    percent : None or number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Analogous to ``translate_percent`` in :class:`Affine`, except that\n        this translation value only affects the x-axis. No dictionary input\n        is allowed.\n\n    px : None or int or tuple of int or list of int or imgaug.parameters.StochasticParameter or dict {\"x\": int/tuple/list/StochasticParameter, \"y\": int/tuple/list/StochasticParameter}, optional\n        Analogous to ``translate_px`` in :class:`Affine`, except that\n        this translation value only affects the x-axis. No dictionary input\n        is allowed.\n\n    order : int or iterable of int or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    cval : number or tuple of number or list of number or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    mode : str or list of str or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    fit_output : bool, optional\n        See :class:`Affine`.\n\n    backend : str, optional\n        See :class:`Affine`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.TranslateX(px=(-20, 20))\n\n    Create an augmenter that translates images along the x-axis by\n    ``-20`` to ``20`` pixels.\n\n    >>> aug = iaa.TranslateX(percent=(-0.1, 0.1))\n\n    Create an augmenter that translates images along the x-axis by\n    ``-10%`` to ``10%`` (relative to the x-axis size).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, percent=None, px=None, order=1,\n                 cval=0, mode=\"constant\", fit_output=False, backend=\"auto\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        if percent is None and px is None:\n            percent = (-0.25, 0.25)\n\n        super(TranslateX, self).__init__(\n            translate_percent=({\"x\": percent} if percent is not None else None),\n            translate_px=({\"x\": px} if px is not None else None),\n            order=order,\n            cval=cval,\n            mode=mode,\n            fit_output=fit_output,\n            backend=backend,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO make Affine more efficient for translation-only transformations\nclass TranslateY(Affine):\n    \"\"\"Apply affine translation on the y-axis to input data.\n\n    This is a wrapper around :class:`Affine`.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    Parameters\n    ----------\n    percent : None or number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Analogous to ``translate_percent`` in :class:`Affine`, except that\n        this translation value only affects the y-axis. No dictionary input\n        is allowed.\n\n    px : None or int or tuple of int or list of int or imgaug.parameters.StochasticParameter or dict {\"x\": int/tuple/list/StochasticParameter, \"y\": int/tuple/list/StochasticParameter}, optional\n        Analogous to ``translate_px`` in :class:`Affine`, except that\n        this translation value only affects the y-axis. No dictionary input\n        is allowed.\n\n    order : int or iterable of int or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    cval : number or tuple of number or list of number or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    mode : str or list of str or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    fit_output : bool, optional\n        See :class:`Affine`.\n\n    backend : str, optional\n        See :class:`Affine`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.TranslateY(px=(-20, 20))\n\n    Create an augmenter that translates images along the y-axis by\n    ``-20`` to ``20`` pixels.\n\n    >>> aug = iaa.TranslateY(percent=(-0.1, 0.1))\n\n    Create an augmenter that translates images along the y-axis by\n    ``-10%`` to ``10%`` (relative to the y-axis size).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, percent=None, px=None, order=1,\n                 cval=0, mode=\"constant\", fit_output=False, backend=\"auto\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        if percent is None and px is None:\n            percent = (-0.25, 0.25)\n\n        super(TranslateY, self).__init__(\n            translate_percent=({\"y\": percent} if percent is not None else None),\n            translate_px=({\"y\": px} if px is not None else None),\n            order=order,\n            cval=cval,\n            mode=mode,\n            fit_output=fit_output,\n            backend=backend,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Rotate(Affine):\n    \"\"\"Apply affine rotation on the y-axis to input data.\n\n    This is a wrapper around :class:`Affine`.\n    It is the same as ``Affine(rotate=<value>)``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    Parameters\n    ----------\n    rotate : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    order : int or iterable of int or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    cval : number or tuple of number or list of number or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    mode : str or list of str or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    fit_output : bool, optional\n        See :class:`Affine`.\n\n    backend : str, optional\n        See :class:`Affine`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Rotate((-45, 45))\n\n    Create an augmenter that rotates images by a random value between ``-45``\n    and ``45`` degress.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, rotate=(-30, 30), order=1, cval=0, mode=\"constant\",\n                 fit_output=False, backend=\"auto\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Rotate, self).__init__(\n            rotate=rotate,\n            order=order,\n            cval=cval,\n            mode=mode,\n            fit_output=fit_output,\n            backend=backend,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass ShearX(Affine):\n    \"\"\"Apply affine shear on the x-axis to input data.\n\n    This is a wrapper around :class:`Affine`.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    Parameters\n    ----------\n    shear : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Analogous to ``shear`` in :class:`Affine`, except that this shear\n        value only affects the x-axis. No dictionary input is allowed.\n\n    order : int or iterable of int or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    cval : number or tuple of number or list of number or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    mode : str or list of str or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    fit_output : bool, optional\n        See :class:`Affine`.\n\n    backend : str, optional\n        See :class:`Affine`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.ShearX((-20, 20))\n\n    Create an augmenter that shears images along the x-axis by random amounts\n    between ``-20`` and ``20`` degrees.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, shear=(-30, 30), order=1, cval=0, mode=\"constant\",\n                 fit_output=False, backend=\"auto\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ShearX, self).__init__(\n            shear={\"x\": shear},\n            order=order,\n            cval=cval,\n            mode=mode,\n            fit_output=fit_output,\n            backend=backend,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass ShearY(Affine):\n    \"\"\"Apply affine shear on the y-axis to input data.\n\n    This is a wrapper around :class:`Affine`.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    Parameters\n    ----------\n    shear : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Analogous to ``shear`` in :class:`Affine`, except that this shear\n        value only affects the y-axis. No dictionary input is allowed.\n\n    order : int or iterable of int or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    cval : number or tuple of number or list of number or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    mode : str or list of str or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :class:`Affine`.\n\n    fit_output : bool, optional\n        See :class:`Affine`.\n\n    backend : str, optional\n        See :class:`Affine`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.ShearY((-20, 20))\n\n    Create an augmenter that shears images along the y-axis by random amounts\n    between ``-20`` and ``20`` degrees.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, shear=(-30, 30), order=1, cval=0, mode=\"constant\",\n                 fit_output=False, backend=\"auto\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ShearY, self).__init__(\n            shear={\"y\": shear},\n            order=order,\n            cval=cval,\n            mode=mode,\n            fit_output=fit_output,\n            backend=backend,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass AffineCv2(meta.Augmenter):\n    \"\"\"\n    **Deprecated.** Augmenter to apply affine transformations to images using\n    cv2 (i.e. opencv) backend.\n\n    .. warning::\n\n        This augmenter is deprecated since 0.4.0.\n        Use ``Affine(..., backend='cv2')`` instead.\n\n    Affine transformations\n    involve:\n\n        - Translation (\"move\" image on the x-/y-axis)\n        - Rotation\n        - Scaling (\"zoom\" in/out)\n        - Shear (move one side of the image, turning a square into a trapezoid)\n\n    All such transformations can create \"new\" pixels in the image without a\n    defined content, e.g. if the image is translated to the left, pixels\n    are created on the right.\n    A method has to be defined to deal with these pixel values. The\n    parameters `cval` and `mode` of this class deal with this.\n\n    Some transformations involve interpolations between several pixels\n    of the input image to generate output pixel values. The parameter `order`\n    deals with the method of interpolation used for this.\n\n    Deprecated since 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: ?\n        * ``uint32``: ?\n        * ``uint64``: ?\n        * ``int8``: ?\n        * ``int16``: ?\n        * ``int32``: ?\n        * ``int64``: ?\n        * ``float16``: ?\n        * ``float32``: ?\n        * ``float64``: ?\n        * ``float128``: ?\n        * ``bool``: ?\n\n    Parameters\n    ----------\n    scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter or dict {\"x\": number/tuple/list/StochasticParameter, \"y\": number/tuple/list/StochasticParameter}, optional\n        Scaling factor to use, where ``1.0`` denotes \\\"no change\\\" and\n        ``0.5`` is zoomed out to ``50`` percent of the original size.\n\n            * If a single number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``. That value will be\n              used identically for both x- and y-axis.\n            * If a list, then a random value will be sampled from that list\n              per image (again, used for both x- and y-axis).\n            * If a ``StochasticParameter``, then from that parameter a value\n              will be sampled per image (again, used for both x- and y-axis).\n            * If a dictionary, then it is expected to have the keys ``x``\n              and/or ``y``. Each of these keys can have the same values as\n              described above. Using a dictionary allows to set different\n              values for the two axis and sampling will then happen\n              *independently* per axis, resulting in samples that differ\n              between the axes.\n\n    translate_percent : number or tuple of number or list of number or imgaug.parameters.StochasticParameter or dict {\"x\": number/tuple/list/StochasticParameter, \"y\": number/tuple/list/StochasticParameter}, optional\n        Translation as a fraction of the image height/width (x-translation,\n        y-translation), where ``0`` denotes \"no change\" and ``0.5`` denotes\n        \"half of the axis size\".\n\n            * If ``None`` then equivalent to ``0.0`` unless `translate_px` has\n              a value other than ``None``.\n            * If a single number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``. That sampled fraction\n              value will be used identically for both x- and y-axis.\n            * If a list, then a random value will be sampled from that list\n              per image (again, used for both x- and y-axis).\n            * If a ``StochasticParameter``, then from that parameter a value\n              will be sampled per image (again, used for both x- and y-axis).\n            * If a dictionary, then it is expected to have the keys ``x``\n              and/or ``y``. Each of these keys can have the same values as\n              described above. Using a dictionary allows to set different\n              values for the two axis and sampling will then happen\n              *independently* per axis, resulting in samples that differ\n              between the axes.\n\n    translate_px : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or dict {\"x\": int/tuple/list/StochasticParameter, \"y\": int/tuple/list/StochasticParameter}, optional\n        Translation in pixels.\n\n            * If ``None`` then equivalent to ``0`` unless `translate_percent`\n              has a value other than ``None``.\n            * If a single int, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value will be uniformly sampled\n              per image from the discrete interval ``[a..b]``. That number\n              will be used identically for both x- and y-axis.\n            * If a list, then a random value will be sampled from that list\n              per image (again, used for both x- and y-axis).\n            * If a ``StochasticParameter``, then from that parameter a value\n              will be sampled per image (again, used for both x- and y-axis).\n            * If a dictionary, then it is expected to have the keys ``x``\n              and/or ``y``. Each of these keys can have the same values as\n              described above. Using a dictionary allows to set different\n              values for the two axis and sampling will then happen\n              *independently* per axis, resulting in samples that differ\n              between the axes.\n\n    rotate : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Rotation in degrees (**NOT** radians), i.e. expected value range is\n        around ``[-360, 360]``. Rotation happens around the *center* of the\n        image, not the top left corner as in some other frameworks.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]`` and used as the rotation\n              value.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              sample the rotation value per image.\n\n    shear : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Shear in degrees (**NOT** radians), i.e. expected value range is\n        around ``[-360, 360]``.\n\n            * If a number, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]`` and be used as the\n              rotation value.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If a ``StochasticParameter``, then this parameter will be used\n              to sample the shear value per image.\n\n    order : int or list of int or str or list of str or imaug.ALL or imgaug.parameters.StochasticParameter, optional\n        Interpolation order to use. Allowed are:\n\n            * ``cv2.INTER_NEAREST`` (nearest-neighbor interpolation)\n            * ``cv2.INTER_LINEAR`` (bilinear interpolation, used by default)\n            * ``cv2.INTER_CUBIC`` (bicubic interpolation over ``4x4`` pixel\n                neighborhood)\n            * ``cv2.INTER_LANCZOS4``\n            * string ``nearest`` (same as ``cv2.INTER_NEAREST``)\n            * string ``linear`` (same as ``cv2.INTER_LINEAR``)\n            * string ``cubic`` (same as ``cv2.INTER_CUBIC``)\n            * string ``lanczos4`` (same as ``cv2.INTER_LANCZOS``)\n\n        ``INTER_NEAREST`` (nearest neighbour interpolation) and\n        ``INTER_NEAREST`` (linear interpolation) are the fastest.\n\n            * If a single ``int``, then that order will be used for all images.\n            * If a string, then it must be one of: ``nearest``, ``linear``,\n              ``cubic``, ``lanczos4``.\n            * If an iterable of ``int``/``str``, then for each image a random\n              value will be sampled from that iterable (i.e. list of allowed\n              order values).\n            * If ``imgaug.ALL``, then equivalant to list ``[cv2.INTER_NEAREST,\n              cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_LANCZOS4]``.\n            * If ``StochasticParameter``, then that parameter is queried per\n              image to sample the order value to use.\n\n    cval : number or tuple of number or list of number or imaug.ALL or imgaug.parameters.StochasticParameter, optional\n        The constant value to use when filling in newly created pixels.\n        (E.g. translating by 1px to the right will create a new 1px-wide\n        column of pixels on the left of the image).  The value is only used\n        when `mode=constant`. The expected value range is ``[0, 255]`` for\n        ``uint8`` images. It may be a float value.\n\n            * If this is a single number, then that value will be used\n              (e.g. 0 results in black pixels).\n            * If a tuple ``(a, b)``, then three values (for three image\n              channels) will be uniformly sampled per image from the\n              interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If ``imgaug.ALL`` then equivalent to tuple ``(0, 255)`.\n            * If a ``StochasticParameter``, a new value will be sampled from\n              the parameter per image.\n\n    mode : int or str or list of str or list of int or imgaug.ALL or imgaug.parameters.StochasticParameter,\n           optional\n        Method to use when filling in newly created pixels.\n        Same meaning as in OpenCV's border mode. Let ``abcdefgh`` be an image's\n        content and ``|`` be an image boundary after which new pixels are\n        filled in, then the valid modes and their behaviour are the following:\n\n            * ``cv2.BORDER_REPLICATE``: ``aaaaaa|abcdefgh|hhhhhhh``\n            * ``cv2.BORDER_REFLECT``: ``fedcba|abcdefgh|hgfedcb``\n            * ``cv2.BORDER_REFLECT_101``: ``gfedcb|abcdefgh|gfedcba``\n            * ``cv2.BORDER_WRAP``: ``cdefgh|abcdefgh|abcdefg``\n            * ``cv2.BORDER_CONSTANT``: ``iiiiii|abcdefgh|iiiiiii``,\n               where ``i`` is the defined cval.\n            * ``replicate``: Same as ``cv2.BORDER_REPLICATE``.\n            * ``reflect``: Same as ``cv2.BORDER_REFLECT``.\n            * ``reflect_101``: Same as ``cv2.BORDER_REFLECT_101``.\n            * ``wrap``: Same as ``cv2.BORDER_WRAP``.\n            * ``constant``: Same as ``cv2.BORDER_CONSTANT``.\n\n        The datatype of the parameter may be:\n\n            * If a single ``int``, then it must be one of the ``cv2.BORDER_*``\n              constants.\n            * If a single string, then it must be one of: ``replicate``,\n              ``reflect``, ``reflect_101``, ``wrap``, ``constant``.\n            * If a list of ``int``/``str``, then per image a random mode will\n              be picked from that list.\n            * If ``imgaug.ALL``, then a random mode from all possible modes\n              will be picked.\n            * If ``StochasticParameter``, then the mode will be sampled from\n              that parameter per image, i.e. it must return only the above\n              mentioned strings.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AffineCv2(scale=2.0)\n\n    Zoom in on all images by a factor of ``2``.\n\n    >>> aug = iaa.AffineCv2(translate_px=16)\n\n    Translate all images on the x- and y-axis by 16 pixels (towards the\n    bottom right) and fill up any new pixels with zero (black values).\n\n    >>> aug = iaa.AffineCv2(translate_percent=0.1)\n\n    Translate all images on the x- and y-axis by ``10`` percent of their\n    width/height (towards the bottom right). The pixel values are computed\n    per axis based on that axis' size. Fill up any new pixels with zero\n    (black values).\n\n    >>> aug = iaa.AffineCv2(rotate=35)\n\n    Rotate all images by ``35`` *degrees*. Fill up any new pixels with zero\n    (black values).\n\n    >>> aug = iaa.AffineCv2(shear=15)\n\n    Shear all images by ``15`` *degrees*. Fill up any new pixels with zero\n    (black values).\n\n    >>> aug = iaa.AffineCv2(translate_px=(-16, 16))\n\n    Translate all images on the x- and y-axis by a random value\n    between ``-16`` and ``16`` pixels (to the bottom right) and fill up any new\n    pixels with zero (black values). The translation value is sampled once\n    per image and is the same for both axis.\n\n    >>> aug = iaa.AffineCv2(translate_px={\"x\": (-16, 16), \"y\": (-4, 4)})\n\n    Translate all images on the x-axis by a random value\n    between ``-16`` and ``16`` pixels (to the right) and on the y-axis by a\n    random value between ``-4`` and ``4`` pixels to the bottom. The sampling\n    happens independently per axis, so even if both intervals were identical,\n    the sampled axis-wise values would likely be different.\n    This also fills up any new pixels with zero (black values).\n\n    >>> aug = iaa.AffineCv2(scale=2.0, order=[0, 1])\n\n    Same as in the above `scale` example, but uses (randomly) either\n    nearest neighbour interpolation or linear interpolation. If `order` is\n    not specified, ``order=1`` would be used by default.\n\n    >>> aug = iaa.AffineCv2(translate_px=16, cval=(0, 255))\n\n    Same as in the `translate_px` example above, but newly created pixels\n    are now filled with a random color (sampled once per image and the\n    same for all newly created pixels within that image).\n\n    >>> aug = iaa.AffineCv2(translate_px=16, mode=[\"constant\", \"replicate\"])\n\n    Similar to the previous example, but the newly created pixels are\n    filled with black pixels in half of all images (mode ``constant`` with\n    default `cval` being ``0``) and in the other half of all images using\n    ``replicate`` mode, which repeats the color of the spatially closest pixel\n    of the corresponding image edge.\n\n    \"\"\"\n\n    def __init__(self, scale=1.0, translate_percent=None, translate_px=None,\n                 rotate=0.0, shear=0.0, order=cv2.INTER_LINEAR, cval=0,\n                 mode=cv2.BORDER_CONSTANT,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(AffineCv2, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        # using a context on __init__ seems to produce no warning,\n        # so warn manually here\n        ia.warn_deprecated(\n            \"AffineCv2 is deprecated. \"\n            \"Use imgaug.augmenters.geometric.Affine(..., backend='cv2') \"\n            \"instead.\", stacklevel=4)\n\n        available_orders = [cv2.INTER_NEAREST, cv2.INTER_LINEAR,\n                            cv2.INTER_CUBIC, cv2.INTER_LANCZOS4]\n        available_orders_str = [\"nearest\", \"linear\", \"cubic\", \"lanczos4\"]\n\n        if order == ia.ALL:\n            self.order = iap.Choice(available_orders)\n        elif ia.is_single_integer(order):\n            assert order in available_orders, (\n                \"Expected order's integer value to be in %s, got %d.\" % (\n                    str(available_orders), order))\n            self.order = iap.Deterministic(order)\n        elif ia.is_string(order):\n            assert order in available_orders_str, (\n                \"Expected order to be in %s, got %s.\" % (\n                    str(available_orders_str), order))\n            self.order = iap.Deterministic(order)\n        elif isinstance(order, list):\n            valid_types = all(\n                [ia.is_single_integer(val) or ia.is_string(val)\n                 for val in order])\n            assert valid_types, (\n                \"Expected order list to only contain integers/strings, got \"\n                \"types %s.\" % (str([type(val) for val in order]),))\n            valid_orders = all(\n                [val in available_orders + available_orders_str\n                 for val in order])\n            assert valid_orders, (\n                \"Expected all order values to be in %s, got %s.\" % (\n                    available_orders + available_orders_str, str(order),))\n            self.order = iap.Choice(order)\n        elif isinstance(order, iap.StochasticParameter):\n            self.order = order\n        else:\n            raise Exception(\n                \"Expected order to be imgaug.ALL, int, string, a list of\"\n                \"int/string or StochasticParameter, got %s.\" % (type(order),))\n\n        if cval == ia.ALL:\n            self.cval = iap.DiscreteUniform(0, 255)\n        else:\n            self.cval = iap.handle_discrete_param(\n                cval, \"cval\", value_range=(0, 255), tuple_to_uniform=True,\n                list_to_choice=True, allow_floats=True)\n\n        available_modes = [cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT,\n                           cv2.BORDER_REFLECT_101, cv2.BORDER_WRAP,\n                           cv2.BORDER_CONSTANT]\n        available_modes_str = [\"replicate\", \"reflect\", \"reflect_101\",\n                               \"wrap\", \"constant\"]\n        if mode == ia.ALL:\n            self.mode = iap.Choice(available_modes)\n        elif ia.is_single_integer(mode):\n            assert mode in available_modes, (\n                \"Expected mode to be in %s, got %d.\" % (\n                    str(available_modes), mode))\n            self.mode = iap.Deterministic(mode)\n        elif ia.is_string(mode):\n            assert mode in available_modes_str, (\n                \"Expected mode to be in %s, got %s.\" % (\n                    str(available_modes_str), mode))\n            self.mode = iap.Deterministic(mode)\n        elif isinstance(mode, list):\n            all_valid_types = all([\n                ia.is_single_integer(val) or ia.is_string(val) for val in mode])\n            assert all_valid_types, (\n                \"Expected mode list to only contain integers/strings, \"\n                \"got types %s.\" % (str([type(val) for val in mode]),))\n            all_valid_modes = all([\n                val in available_modes + available_modes_str for val in mode])\n            assert all_valid_modes, (\n                \"Expected all mode values to be in %s, got %s.\" % (\n                    str(available_modes + available_modes_str), str(mode)))\n            self.mode = iap.Choice(mode)\n        elif isinstance(mode, iap.StochasticParameter):\n            self.mode = mode\n        else:\n            raise Exception(\n                \"Expected mode to be imgaug.ALL, an int, a string, a list of \"\n                \"int/strings or StochasticParameter, got %s.\" % (type(mode),))\n\n        # scale\n        if isinstance(scale, dict):\n            assert \"x\" in scale or \"y\" in scale, (\n                \"Expected scale dictionary to contain at \"\n                \"least key \\\"x\\\" or key \\\"y\\\". Found neither of them.\")\n            x = scale.get(\"x\", 1.0)\n            y = scale.get(\"y\", 1.0)\n            self.scale = (\n                iap.handle_continuous_param(\n                    x, \"scale['x']\", value_range=(0+1e-4, None),\n                    tuple_to_uniform=True, list_to_choice=True),\n                iap.handle_continuous_param(\n                    y, \"scale['y']\", value_range=(0+1e-4, None),\n                    tuple_to_uniform=True, list_to_choice=True)\n            )\n        else:\n            self.scale = iap.handle_continuous_param(\n                scale, \"scale\", value_range=(0+1e-4, None),\n                tuple_to_uniform=True, list_to_choice=True)\n\n        # translate\n        if translate_percent is None and translate_px is None:\n            translate_px = 0\n\n        assert translate_percent is None or translate_px is None, (\n            \"Expected either translate_percent or translate_px to be \"\n            \"provided, but neither of them was.\")\n\n        if translate_percent is not None:\n            # translate by percent\n            if isinstance(translate_percent, dict):\n                assert \"x\" in translate_percent or \"y\" in translate_percent, (\n                    \"Expected translate_percent dictionary to contain at \"\n                    \"least key \\\"x\\\" or key \\\"y\\\". Found neither of them.\")\n                x = translate_percent.get(\"x\", 0)\n                y = translate_percent.get(\"y\", 0)\n                self.translate = (\n                    iap.handle_continuous_param(\n                        x, \"translate_percent['x']\", value_range=None,\n                        tuple_to_uniform=True, list_to_choice=True),\n                    iap.handle_continuous_param(\n                        y, \"translate_percent['y']\", value_range=None,\n                        tuple_to_uniform=True, list_to_choice=True)\n                )\n            else:\n                self.translate = iap.handle_continuous_param(\n                    translate_percent, \"translate_percent\", value_range=None,\n                    tuple_to_uniform=True, list_to_choice=True)\n        else:\n            # translate by pixels\n            if isinstance(translate_px, dict):\n                assert \"x\" in translate_px or \"y\" in translate_px, (\n                    \"Expected translate_px dictionary to contain at \"\n                    \"least key \\\"x\\\" or key \\\"y\\\". Found neither of them.\")\n                x = translate_px.get(\"x\", 0)\n                y = translate_px.get(\"y\", 0)\n                self.translate = (\n                    iap.handle_discrete_param(\n                        x, \"translate_px['x']\", value_range=None,\n                        tuple_to_uniform=True, list_to_choice=True,\n                        allow_floats=False),\n                    iap.handle_discrete_param(\n                        y, \"translate_px['y']\", value_range=None,\n                        tuple_to_uniform=True, list_to_choice=True,\n                        allow_floats=False)\n                )\n            else:\n                self.translate = iap.handle_discrete_param(\n                    translate_px, \"translate_px\", value_range=None,\n                    tuple_to_uniform=True, list_to_choice=True,\n                    allow_floats=False)\n\n        self.rotate = iap.handle_continuous_param(\n            rotate, \"rotate\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True)\n        self.shear = iap.handle_continuous_param(\n            shear, \"shear\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True)\n\n    def _augment_images(self, images, random_state, parents, hooks):\n        nb_images = len(images)\n        scale_samples, translate_samples, rotate_samples, shear_samples, \\\n            cval_samples, mode_samples, order_samples = self._draw_samples(\n                nb_images, random_state)\n        result = self._augment_images_by_samples(\n            images, scale_samples, translate_samples, rotate_samples,\n            shear_samples, cval_samples, mode_samples, order_samples)\n        return result\n\n    @classmethod\n    def _augment_images_by_samples(cls, images, scale_samples,\n                                   translate_samples, rotate_samples,\n                                   shear_samples, cval_samples, mode_samples,\n                                   order_samples):\n        # TODO change these to class attributes\n        order_str_to_int = {\n            \"nearest\": cv2.INTER_NEAREST,\n            \"linear\": cv2.INTER_LINEAR,\n            \"cubic\": cv2.INTER_CUBIC,\n            \"lanczos4\": cv2.INTER_LANCZOS4\n        }\n        mode_str_to_int = {\n            \"replicate\": cv2.BORDER_REPLICATE,\n            \"reflect\": cv2.BORDER_REFLECT,\n            \"reflect_101\": cv2.BORDER_REFLECT_101,\n            \"wrap\": cv2.BORDER_WRAP,\n            \"constant\": cv2.BORDER_CONSTANT\n        }\n\n        nb_images = len(images)\n        result = images\n        for i in sm.xrange(nb_images):\n            height, width = images[i].shape[0], images[i].shape[1]\n            shift_x = width / 2.0 - 0.5\n            shift_y = height / 2.0 - 0.5\n            scale_x, scale_y = scale_samples[0][i], scale_samples[1][i]\n            translate_x = translate_samples[0][i]\n            translate_y = translate_samples[1][i]\n            if ia.is_single_float(translate_y):\n                translate_y_px = int(\n                    np.round(translate_y * images[i].shape[0]))\n            else:\n                translate_y_px = translate_y\n            if ia.is_single_float(translate_x):\n                translate_x_px = int(\n                    np.round(translate_x * images[i].shape[1]))\n            else:\n                translate_x_px = translate_x\n            rotate = rotate_samples[i]\n            shear = shear_samples[i]\n            cval = cval_samples[i]\n            mode = mode_samples[i]\n            order = order_samples[i]\n\n            mode = (mode\n                    if ia.is_single_integer(mode)\n                    else mode_str_to_int[mode])\n            order = (order\n                     if ia.is_single_integer(order)\n                     else order_str_to_int[order])\n\n            any_change = (\n                scale_x != 1.0 or scale_y != 1.0\n                or translate_x_px != 0 or translate_y_px != 0\n                or rotate != 0 or shear != 0\n            )\n\n            if any_change:\n                matrix_to_topleft = tf.SimilarityTransform(\n                    translation=[-shift_x, -shift_y])\n                matrix_transforms = tf.AffineTransform(\n                    scale=(scale_x, scale_y),\n                    translation=(translate_x_px, translate_y_px),\n                    rotation=math.radians(rotate),\n                    shear=math.radians(shear)\n                )\n                matrix_to_center = tf.SimilarityTransform(\n                    translation=[shift_x, shift_y])\n                matrix = (matrix_to_topleft\n                          + matrix_transforms\n                          + matrix_to_center)\n\n                image_warped = cv2.warpAffine(\n                    _normalize_cv2_input_arr_(images[i]),\n                    matrix.params[:2],\n                    dsize=(width, height),\n                    flags=order,\n                    borderMode=mode,\n                    borderValue=tuple([int(v) for v in cval])\n                )\n\n                # cv2 warp drops last axis if shape is (H, W, 1)\n                if image_warped.ndim == 2:\n                    image_warped = image_warped[..., np.newaxis]\n\n                # warp changes uint8 to float64, making this necessary\n                result[i] = image_warped\n            else:\n                result[i] = images[i]\n\n        return result\n\n    def _augment_heatmaps(self, heatmaps, random_state, parents, hooks):\n        nb_images = len(heatmaps)\n        scale_samples, translate_samples, rotate_samples, shear_samples, \\\n            cval_samples, mode_samples, order_samples = self._draw_samples(\n                nb_images, random_state)\n        cval_samples = np.zeros((cval_samples.shape[0], 1), dtype=np.float32)\n        mode_samples = [\"constant\"] * len(mode_samples)\n        arrs = [heatmap_i.arr_0to1 for heatmap_i in heatmaps]\n        arrs_aug = self._augment_images_by_samples(\n            arrs, scale_samples, translate_samples, rotate_samples,\n            shear_samples, cval_samples, mode_samples, order_samples)\n        for heatmap_i, arr_aug in zip(heatmaps, arrs_aug):\n            heatmap_i.arr_0to1 = arr_aug\n        return heatmaps\n\n    def _augment_segmentation_maps(self, segmaps, random_state, parents, hooks):\n        nb_images = len(segmaps)\n        scale_samples, translate_samples, rotate_samples, shear_samples, \\\n            cval_samples, mode_samples, order_samples = self._draw_samples(\n                nb_images, random_state)\n        cval_samples = np.zeros((cval_samples.shape[0], 1), dtype=np.float32)\n        mode_samples = [\"constant\"] * len(mode_samples)\n        order_samples = [0] * len(order_samples)\n        arrs = [segmaps_i.arr for segmaps_i in segmaps]\n        arrs_aug = self._augment_images_by_samples(\n            arrs, scale_samples, translate_samples, rotate_samples,\n            shear_samples, cval_samples, mode_samples, order_samples)\n        for segmaps_i, arr_aug in zip(segmaps, arrs_aug):\n            segmaps_i.arr = arr_aug\n        return segmaps\n\n    def _augment_keypoints(self, keypoints_on_images, random_state, parents,\n                           hooks):\n        result = []\n        nb_images = len(keypoints_on_images)\n        scale_samples, translate_samples, rotate_samples, shear_samples, \\\n            _cval_samples, _mode_samples, _order_samples = self._draw_samples(\n                nb_images, random_state)\n\n        for i, keypoints_on_image in enumerate(keypoints_on_images):\n            if not keypoints_on_image.keypoints:\n                # AffineCv2 does not change the image shape, hence we can skip\n                # all steps below if there are no keypoints\n                result.append(keypoints_on_image)\n                continue\n            height, width = keypoints_on_image.height, keypoints_on_image.width\n            shift_x = width / 2.0 - 0.5\n            shift_y = height / 2.0 - 0.5\n            scale_x, scale_y = scale_samples[0][i], scale_samples[1][i]\n            translate_x = translate_samples[0][i]\n            translate_y = translate_samples[1][i]\n            if ia.is_single_float(translate_y):\n                translate_y_px = int(\n                    np.round(translate_y * keypoints_on_image.shape[0]))\n            else:\n                translate_y_px = translate_y\n            if ia.is_single_float(translate_x):\n                translate_x_px = int(\n                    np.round(translate_x * keypoints_on_image.shape[1]))\n            else:\n                translate_x_px = translate_x\n            rotate = rotate_samples[i]\n            shear = shear_samples[i]\n\n            any_change = (\n                scale_x != 1.0 or scale_y != 1.0\n                or translate_x_px != 0 or translate_y_px != 0\n                or rotate != 0 or shear != 0\n            )\n\n            if any_change:\n                matrix_to_topleft = tf.SimilarityTransform(\n                    translation=[-shift_x, -shift_y])\n                matrix_transforms = tf.AffineTransform(\n                    scale=(scale_x, scale_y),\n                    translation=(translate_x_px, translate_y_px),\n                    rotation=math.radians(rotate),\n                    shear=math.radians(shear)\n                )\n                matrix_to_center = tf.SimilarityTransform(\n                    translation=[shift_x, shift_y])\n                matrix = (matrix_to_topleft\n                          + matrix_transforms\n                          + matrix_to_center)\n\n                coords = keypoints_on_image.to_xy_array()\n                coords_aug = tf.matrix_transform(coords, matrix.params)\n                kps_new = [kp.deepcopy(x=coords[0], y=coords[1])\n                           for kp, coords\n                           in zip(keypoints_on_image.keypoints, coords_aug)]\n                result.append(keypoints_on_image.deepcopy(\n                    keypoints=kps_new,\n                    shape=keypoints_on_image.shape\n                ))\n            else:\n                result.append(keypoints_on_image)\n        return result\n\n    def _augment_polygons(self, polygons_on_images, random_state, parents,\n                          hooks):\n        return self._augment_polygons_as_keypoints(\n            polygons_on_images, random_state, parents, hooks)\n\n    def _augment_line_strings(self, line_strings_on_images, random_state,\n                              parents, hooks):\n        return self._augment_line_strings_as_keypoints(\n            line_strings_on_images, random_state, parents, hooks)\n\n    def _augment_bounding_boxes(self, bounding_boxes_on_images, random_state,\n                                parents, hooks):\n        return self._augment_bounding_boxes_as_keypoints(\n            bounding_boxes_on_images, random_state, parents, hooks)\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.scale, self.translate, self.rotate, self.shear,\n                self.order, self.cval, self.mode]\n\n    def _draw_samples(self, nb_samples, random_state):\n        rngs = random_state.duplicate(11)\n\n        if isinstance(self.scale, tuple):\n            scale_samples = (\n                self.scale[0].draw_samples((nb_samples,),\n                                           random_state=rngs[0]),\n                self.scale[1].draw_samples((nb_samples,),\n                                           random_state=rngs[1]),\n            )\n        else:\n            scale_samples = self.scale.draw_samples((nb_samples,),\n                                                    random_state=rngs[2])\n            scale_samples = (scale_samples, scale_samples)\n\n        if isinstance(self.translate, tuple):\n            translate_samples = (\n                self.translate[0].draw_samples((nb_samples,),\n                                               random_state=rngs[3]),\n                self.translate[1].draw_samples((nb_samples,),\n                                               random_state=rngs[4]),\n            )\n        else:\n            translate_samples = self.translate.draw_samples(\n                (nb_samples,), random_state=rngs[5])\n            translate_samples = (translate_samples, translate_samples)\n\n        valid_dts = iadt._convert_dtype_strs_to_types(\n            \"int32 int64 float32 float64\"\n        )\n        for i in sm.xrange(2):\n            assert translate_samples[i].dtype in valid_dts, (\n                \"Expected translate_samples to have any dtype of %s. \"\n                \"Got %s.\" % (str(valid_dts), translate_samples[i].dtype.name,))\n\n        rotate_samples = self.rotate.draw_samples((nb_samples,),\n                                                  random_state=rngs[6])\n        shear_samples = self.shear.draw_samples((nb_samples,),\n                                                random_state=rngs[7])\n\n        cval_samples = self.cval.draw_samples((nb_samples, 3),\n                                              random_state=rngs[8])\n        mode_samples = self.mode.draw_samples((nb_samples,),\n                                              random_state=rngs[9])\n        order_samples = self.order.draw_samples((nb_samples,),\n                                                random_state=rngs[10])\n\n        return (\n            scale_samples, translate_samples, rotate_samples, shear_samples,\n            cval_samples, mode_samples, order_samples\n        )\n\n\nclass _PiecewiseAffineSamplingResult(object):\n    def __init__(self, nb_rows, nb_cols, jitter, order, cval, mode):\n        self.nb_rows = nb_rows\n        self.nb_cols = nb_cols\n        self.order = order\n        self.jitter = jitter\n        self.cval = cval\n        self.mode = mode\n\n    def get_clipped_cval(self, idx, dtype):\n        min_value, _, max_value = iadt.get_value_range_of_dtype(dtype)\n        cval = self.cval[idx]\n        cval = max(min(cval, max_value), min_value)\n        return cval\n\n\nclass PiecewiseAffine(meta.Augmenter):\n    \"\"\"\n    Apply affine transformations that differ between local neighbourhoods.\n\n    This augmenter places a regular grid of points on an image and randomly\n    moves the neighbourhood of these point around via affine transformations.\n    This leads to local distortions.\n\n    This is mostly a wrapper around scikit-image's ``PiecewiseAffine``.\n    See also ``Affine`` for a similar technique.\n\n    .. note::\n\n        This augmenter is very slow. See :ref:`performance`.\n        Try to use ``ElasticTransformation`` instead, which is at least 10x\n        faster.\n\n    .. note::\n\n        For coordinate-based inputs (keypoints, bounding boxes, polygons,\n        ...), this augmenter still has to perform an image-based augmentation,\n        which will make it significantly slower for such inputs than other\n        augmenters. See :ref:`performance`.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested (1)\n        * ``uint32``: yes; tested (1) (2)\n        * ``uint64``: no (3)\n        * ``int8``: yes; tested (1)\n        * ``int16``: yes; tested (1)\n        * ``int32``: yes; tested (1) (2)\n        * ``int64``: no (3)\n        * ``float16``: yes; tested (1)\n        * ``float32``: yes; tested (1)\n        * ``float64``: yes; tested (1)\n        * ``float128``: no (3)\n        * ``bool``: yes; tested (1) (4)\n\n        - (1) Only tested with `order` set to ``0``.\n        - (2) scikit-image converts internally to ``float64``, which might\n              introduce inaccuracies. Tests showed that these inaccuracies\n              seemed to not be an issue.\n        - (3) Results too inaccurate.\n        - (4) Mapped internally to ``float64``.\n\n    Parameters\n    ----------\n    scale : float or tuple of float or imgaug.parameters.StochasticParameter, optional\n        Each point on the regular grid is moved around via a normal\n        distribution. This scale factor is equivalent to the normal\n        distribution's sigma. Note that the jitter (how far each point is\n        moved in which direction) is multiplied by the height/width of the\n        image if ``absolute_scale=False`` (default), so this scale can be\n        the same for different sized images.\n        Recommended values are in the range ``0.01`` to ``0.05`` (weak to\n        strong augmentations).\n\n            * If a single ``float``, then that value will always be used as\n              the scale.\n            * If a tuple ``(a, b)`` of ``float`` s, then a random value will\n              be uniformly sampled per image from the interval ``[a, b]``.\n            * If a list, then a random value will be picked from that list\n              per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    nb_rows : int or tuple of int or imgaug.parameters.StochasticParameter, optional\n        Number of rows of points that the regular grid should have.\n        Must be at least ``2``. For large images, you might want to pick a\n        higher value than ``4``. You might have to then adjust scale to lower\n        values.\n\n            * If a single ``int``, then that value will always be used as the\n              number of rows.\n            * If a tuple ``(a, b)``, then a value from the discrete interval\n              ``[a..b]`` will be uniformly sampled per image.\n            * If a list, then a random value will be picked from that list\n              per image.\n            * If a StochasticParameter, then that parameter will be queried to\n              draw one value per image.\n\n    nb_cols : int or tuple of int or imgaug.parameters.StochasticParameter, optional\n        Number of columns. Analogous to `nb_rows`.\n\n    order : int or list of int or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.geometric.Affine.__init__`.\n\n    cval : int or float or tuple of float or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.geometric.Affine.__init__`.\n\n    mode : str or list of str or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.geometric.Affine.__init__`.\n\n    absolute_scale : bool, optional\n        Take `scale` as an absolute value rather than a relative value.\n\n    polygon_recoverer : 'auto' or None or imgaug.augmentables.polygons._ConcavePolygonRecoverer, optional\n        The class to use to repair invalid polygons.\n        If ``\"auto\"``, a new instance of\n        :class`imgaug.augmentables.polygons._ConcavePolygonRecoverer`\n        will be created.\n        If ``None``, no polygon recoverer will be used.\n        If an object, then that object will be used and must provide a\n        ``recover_from()`` method, similar to\n        :class:`~imgaug.augmentables.polygons._ConcavePolygonRecoverer`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.PiecewiseAffine(scale=(0.01, 0.05))\n\n    Place a regular grid of points on each image and then randomly move each\n    point around by ``1`` to ``5`` percent (with respect to the image\n    height/width). Pixels between these points will be moved accordingly.\n\n    >>> aug = iaa.PiecewiseAffine(scale=(0.01, 0.05), nb_rows=8, nb_cols=8)\n\n    Same as the previous example, but uses a denser grid of ``8x8`` points\n    (default is ``4x4``). This can be useful for large images.\n\n    \"\"\"\n\n    def __init__(self, scale=(0.0, 0.04), nb_rows=(2, 4), nb_cols=(2, 4),\n                 order=1, cval=0, mode=\"constant\", absolute_scale=False,\n                 polygon_recoverer=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(PiecewiseAffine, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.scale = iap.handle_continuous_param(\n            scale, \"scale\", value_range=(0, None), tuple_to_uniform=True,\n            list_to_choice=True)\n        self.jitter = iap.Normal(loc=0, scale=self.scale)\n        self.nb_rows = iap.handle_discrete_param(\n            nb_rows, \"nb_rows\", value_range=(2, None), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=False)\n        self.nb_cols = iap.handle_discrete_param(\n            nb_cols, \"nb_cols\", value_range=(2, None), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=False)\n\n        self.order = _handle_order_arg(order, backend=\"skimage\")\n        self.cval = _handle_cval_arg(cval)\n        self.mode = _handle_mode_arg(mode)\n\n        self.absolute_scale = absolute_scale\n        self.polygon_recoverer = polygon_recoverer\n        if polygon_recoverer == \"auto\":\n            self.polygon_recoverer = _ConcavePolygonRecoverer()\n\n        # Special order, mode and cval parameters for heatmaps and\n        # segmentation maps. These may either be None or a fixed value.\n        # Stochastic parameters are currently *not* supported.\n        # If set to None, the same values as for images will be used.\n        # That is really not recommended for the cval parameter.\n        self._order_heatmaps = 3\n        self._order_segmentation_maps = 0\n        self._mode_heatmaps = \"constant\"\n        self._mode_segmentation_maps = \"constant\"\n        self._cval_heatmaps = 0\n        self._cval_segmentation_maps = 0\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        samples = self._draw_samples(batch.nb_rows, random_state)\n\n        if batch.images is not None:\n            batch.images = self._augment_images_by_samples(batch.images,\n                                                           samples)\n\n        if batch.heatmaps is not None:\n            batch.heatmaps = self._augment_maps_by_samples(\n                batch.heatmaps, \"arr_0to1\", samples, self._cval_heatmaps,\n                self._mode_heatmaps, self._order_heatmaps)\n\n        if batch.segmentation_maps is not None:\n            batch.segmentation_maps = self._augment_maps_by_samples(\n                batch.segmentation_maps, \"arr\", samples,\n                self._cval_segmentation_maps, self._mode_segmentation_maps,\n                self._order_segmentation_maps)\n\n        # TODO add test for recoverer\n        if batch.polygons is not None:\n            func = functools.partial(\n                self._augment_keypoints_by_samples,\n                samples=samples)\n            batch.polygons = self._apply_to_polygons_as_keypoints(\n                batch.polygons, func, recoverer=self.polygon_recoverer)\n\n        for augm_name in [\"keypoints\", \"bounding_boxes\", \"line_strings\"]:\n            augm_value = getattr(batch, augm_name)\n            if augm_value is not None:\n                func = functools.partial(\n                    self._augment_keypoints_by_samples,\n                    samples=samples)\n                cbaois = self._apply_to_cbaois_as_keypoints(augm_value, func)\n                setattr(batch, augm_name, cbaois)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _augment_images_by_samples(self, images, samples):\n        iadt.gate_dtypes_strs(\n            images,\n            allowed=\"bool uint8 uint16 uint32 int8 int16 int32 \"\n                    \"float16 float32 float64\",\n            disallowed=\"uint64 int64 float128\",\n            augmenter=self\n        )\n\n        result = images\n\n        for i, image in enumerate(images):\n            transformer = self._get_transformer(\n                image.shape, image.shape, samples.nb_rows[i],\n                samples.nb_cols[i], samples.jitter[i])\n\n            if transformer is not None:\n                input_dtype = image.dtype\n                if image.dtype.kind == \"b\":\n                    image = image.astype(np.float64)\n\n                image_warped = tf.warp(\n                    image,\n                    transformer,\n                    order=samples.order[i],\n                    mode=samples.mode[i],\n                    cval=samples.get_clipped_cval(i, image.dtype),\n                    preserve_range=True,\n                    output_shape=images[i].shape\n                )\n\n                if input_dtype.kind == \"b\":\n                    image_warped = image_warped > 0.5\n                else:\n                    # warp seems to change everything to float64, including\n                    # uint8, making this necessary\n                    image_warped = iadt.restore_dtypes_(\n                        image_warped, input_dtype)\n\n                result[i] = image_warped\n\n        return result\n\n    # Added in 0.4.0.\n    def _augment_maps_by_samples(self, augmentables, arr_attr_name, samples,\n                                 cval, mode, order):\n        result = augmentables\n\n        for i, augmentable in enumerate(augmentables):\n            arr = getattr(augmentable, arr_attr_name)\n\n            transformer = self._get_transformer(\n                arr.shape, augmentable.shape, samples.nb_rows[i],\n                samples.nb_cols[i], samples.jitter[i])\n\n            if transformer is not None:\n                arr_warped = tf.warp(\n                    arr,\n                    transformer,\n                    order=order if order is not None else samples.order[i],\n                    mode=mode if mode is not None else samples.mode[i],\n                    cval=cval if cval is not None else samples.cval[i],\n                    preserve_range=True,\n                    output_shape=arr.shape\n                )\n\n                # skimage converts to float64\n                arr_warped = arr_warped.astype(arr.dtype)\n\n                # TODO not entirely clear whether this breaks the value\n                #      range -- Affine does\n                # TODO add test for this\n                # order=3 matches cubic interpolation and can cause values\n                # to go outside of the range [0.0, 1.0] not clear whether\n                # 4+ also do that\n                # We don't modify segmaps here, because they don't have a\n                # clear value range of [0, 1]\n                if order >= 3 and isinstance(augmentable, ia.HeatmapsOnImage):\n                    arr_warped = np.clip(arr_warped, 0.0, 1.0, out=arr_warped)\n\n                setattr(augmentable, arr_attr_name, arr_warped)\n\n        return result\n\n    # Added in 0.4.0.\n    def _augment_keypoints_by_samples(self, kpsois, samples):\n        # pylint: disable=pointless-string-statement\n        result = []\n\n        for i, kpsoi in enumerate(kpsois):\n            h, w = kpsoi.shape[0:2]\n            transformer = self._get_transformer(\n                kpsoi.shape, kpsoi.shape, samples.nb_rows[i],\n                samples.nb_cols[i], samples.jitter[i])\n\n            if transformer is None or len(kpsoi.keypoints) == 0:\n                result.append(kpsoi)\n            else:\n                # Augmentation routine that only modifies keypoint coordinates\n                # This is efficient (coordinates of all other locations in the\n                # image are ignored). The code below should usually work, but\n                # for some reason augmented coordinates are often wildly off\n                # for large scale parameters (lots of jitter/distortion).\n                # The reason for that is unknown.\n                \"\"\"\n                coords = keypoints_on_images[i].get_coords_array()\n                coords_aug = transformer.inverse(coords)\n                result.append(\n                    ia.KeypointsOnImage.from_coords_array(\n                        coords_aug,\n                        shape=keypoints_on_images[i].shape\n                    )\n                )\n                \"\"\"\n\n                # TODO this could be done a little bit more efficient by\n                #      removing first all KPs that are outside of the image\n                #      plane so that no corresponding distance map has to\n                #      be augmented\n                # Image based augmentation routine. Draws the keypoints on\n                # the image plane using distance maps (more accurate than\n                # just marking the points),  then augments these images, then\n                # searches for the new (visual) location of the keypoints.\n                # Much slower than directly augmenting the coordinates, but\n                # here the only method that reliably works.\n                dist_maps = kpsoi.to_distance_maps(inverted=True)\n                dist_maps_warped = tf.warp(\n                    dist_maps,\n                    transformer,\n                    order=1,\n                    preserve_range=True,\n                    output_shape=(kpsoi.shape[0], kpsoi.shape[1],\n                                  len(kpsoi.keypoints))\n                )\n\n                kps_aug = ia.KeypointsOnImage.from_distance_maps(\n                    dist_maps_warped,\n                    inverted=True,\n                    threshold=0.01,\n                    if_not_found_coords={\"x\": -1, \"y\": -1},\n                    nb_channels=(\n                        None if len(kpsoi.shape) < 3 else kpsoi.shape[2])\n                )\n\n                for kp, kp_aug in zip(kpsoi.keypoints, kps_aug.keypoints):\n                    # Keypoints that were outside of the image plane before the\n                    # augmentation were replaced with (-1, -1) by default (as\n                    # they can't be drawn on the keypoint images).\n                    within_image = (0 <= kp.x < w and 0 <= kp.y < h)\n                    if within_image:\n                        kp.x = kp_aug.x\n                        kp.y = kp_aug.y\n\n                result.append(kpsoi)\n\n        return result\n\n    def _draw_samples(self, nb_images, random_state):\n        rss = random_state.duplicate(6)\n\n        nb_rows_samples = self.nb_rows.draw_samples((nb_images,),\n                                                    random_state=rss[-6])\n        nb_cols_samples = self.nb_cols.draw_samples((nb_images,),\n                                                    random_state=rss[-5])\n        order_samples = self.order.draw_samples((nb_images,),\n                                                random_state=rss[-4])\n        cval_samples = self.cval.draw_samples((nb_images,),\n                                              random_state=rss[-3])\n        mode_samples = self.mode.draw_samples((nb_images,),\n                                              random_state=rss[-2])\n\n        nb_rows_samples = np.clip(nb_rows_samples, 2, None)\n        nb_cols_samples = np.clip(nb_cols_samples, 2, None)\n        nb_cells = nb_rows_samples * nb_cols_samples\n        jitter = self.jitter.draw_samples((int(np.sum(nb_cells)), 2),\n                                          random_state=rss[-1])\n\n        jitter_by_image = []\n        counter = 0\n        for nb_cells_i in nb_cells:\n            jitter_img = jitter[counter:counter+nb_cells_i, :]\n            jitter_by_image.append(jitter_img)\n            counter += nb_cells_i\n\n        return _PiecewiseAffineSamplingResult(\n            nb_rows=nb_rows_samples, nb_cols=nb_cols_samples,\n            jitter=jitter_by_image,\n            order=order_samples, cval=cval_samples, mode=mode_samples)\n\n    def _get_transformer(self, augmentable_shape, image_shape, nb_rows,\n                         nb_cols, jitter_img):\n        # get coords on y and x axis of points to move around\n        # these coordinates are supposed to be at the centers of each cell\n        # (otherwise the first coordinate would be at (0, 0) and could hardly\n        # be moved around before leaving the image),\n        # so we use here (half cell height/width to H/W minus half\n        # height/width) instead of (0, H/W)\n\n        # pylint: disable=no-else-return\n\n        y = np.linspace(0, augmentable_shape[0], nb_rows)\n        x = np.linspace(0, augmentable_shape[1], nb_cols)\n\n        # (H, W) and (H, W) for H=rows, W=cols\n        xx_src, yy_src = np.meshgrid(x, y)\n\n        # (1, HW, 2) => (HW, 2) for H=rows, W=cols\n        points_src = np.dstack([yy_src.flat, xx_src.flat])[0]\n\n        any_nonzero = np.any(jitter_img > 0)\n        if not any_nonzero:\n            return None\n        else:\n            # Without this, jitter gets changed between different augmentables.\n            # TODO if left out, only one test failed -- should be more\n            jitter_img = np.copy(jitter_img)\n\n            if self.absolute_scale:\n                if image_shape[0] > 0:\n                    jitter_img[:, 0] = jitter_img[:, 0] / image_shape[0]\n                else:\n                    jitter_img[:, 0] = 0.0\n\n                if image_shape[1] > 0:\n                    jitter_img[:, 1] = jitter_img[:, 1] / image_shape[1]\n                else:\n                    jitter_img[:, 1] = 0.0\n\n            jitter_img[:, 0] = jitter_img[:, 0] * augmentable_shape[0]\n            jitter_img[:, 1] = jitter_img[:, 1] * augmentable_shape[1]\n\n            points_dest = np.copy(points_src)\n            points_dest[:, 0] = points_dest[:, 0] + jitter_img[:, 0]\n            points_dest[:, 1] = points_dest[:, 1] + jitter_img[:, 1]\n\n            # Restrict all destination points to be inside the image plane.\n            # This is necessary, as otherwise keypoints could be augmented\n            # outside of the image plane and these would be replaced by\n            # (-1, -1), which would not conform with the behaviour of the\n            # other augmenters.\n            points_dest[:, 0] = np.clip(points_dest[:, 0],\n                                        0, augmentable_shape[0]-1)\n            points_dest[:, 1] = np.clip(points_dest[:, 1],\n                                        0, augmentable_shape[1]-1)\n\n            # tf.warp() results in qhull error if the points are identical,\n            # which is mainly the case if any axis is 0\n            has_low_axis = any([axis <= 1 for axis in augmentable_shape[0:2]])\n            has_zero_channels = (\n                (\n                    augmentable_shape is not None\n                    and len(augmentable_shape) == 3\n                    and augmentable_shape[-1] == 0\n                )\n                or\n                (\n                    image_shape is not None\n                    and len(image_shape) == 3\n                    and image_shape[-1] == 0\n                )\n            )\n\n            if has_low_axis or has_zero_channels:\n                return None\n            else:\n                matrix = tf.PiecewiseAffineTransform()\n                matrix.estimate(points_src[:, ::-1], points_dest[:, ::-1])\n                return matrix\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [\n            self.scale, self.nb_rows, self.nb_cols, self.order, self.cval,\n            self.mode, self.absolute_scale]\n\n\nclass _PerspectiveTransformSamplingResult(object):\n    def __init__(self, matrices, max_heights, max_widths, cvals, modes):\n        self.matrices = matrices\n        self.max_heights = max_heights\n        self.max_widths = max_widths\n        self.cvals = cvals\n        self.modes = modes\n\n\n# TODO add arg for image interpolation\nclass PerspectiveTransform(meta.Augmenter):\n    \"\"\"\n    Apply random four point perspective transformations to images.\n\n    Each of the four points is placed on the image using a random distance from\n    its respective corner. The distance is sampled from a normal distribution.\n    As a result, most transformations don't change the image very much, while\n    some \"focus\" on polygons far inside the image.\n\n    The results of this augmenter have some similarity with ``Crop``.\n\n    Code partially from\n    http://www.pyimagesearch.com/2014/08/25/4-point-opencv-getperspective-transform-example/\n\n    **Supported dtypes**:\n\n    if (keep_size=False):\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: no (1)\n        * ``uint64``: no (2)\n        * ``int8``: yes; tested (3)\n        * ``int16``: yes; tested\n        * ``int32``: no (2)\n        * ``int64``: no (2)\n        * ``float16``: yes; tested (4)\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no (1)\n        * ``bool``: yes; tested (4)\n\n        - (1) rejected by opencv\n        - (2) leads to opencv error: cv2.error: ``OpenCV(3.4.4)\n              (...)imgwarp.cpp:1805: error: (-215:Assertion failed)\n              ifunc != 0 in function 'remap'``.\n        - (3) mapped internally to ``int16``.\n        - (4) mapped intenally to ``float32``.\n\n    if (keep_size=True):\n\n        minimum of (\n            ``imgaug.augmenters.geometric.PerspectiveTransform(keep_size=False)``,\n            :func:`~imgaug.imgaug.imresize_many_images`\n        )\n\n    Parameters\n    ----------\n    scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Standard deviation of the normal distributions. These are used to\n        sample the random distances of the subimage's corners from the full\n        image's corners. The sampled values reflect percentage values (with\n        respect to image height/width). Recommended values are in the range\n        ``0.0`` to ``0.1``.\n\n            * If a single number, then that value will always be used as the\n              scale.\n            * If a tuple ``(a, b)`` of numbers, then a random value will be\n              uniformly sampled per image from the interval ``(a, b)``.\n            * If a list of values, a random value will be picked from the\n              list per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    keep_size : bool, optional\n        Whether to resize image's back to their original size after applying\n        the perspective transform. If set to ``False``, the resulting images\n        may end up having different shapes and will always be a list, never\n        an array.\n\n    cval : number or tuple of number or list of number or imaug.ALL or imgaug.parameters.StochasticParameter, optional\n        The constant value used to fill up pixels in the result image that\n        didn't exist in the input image (e.g. when translating to the left,\n        some new pixels are created at the right). Such a fill-up with a\n        constant value only happens, when `mode` is ``constant``.\n        The expected value range is ``[0, 255]`` for ``uint8`` images.\n        It may be a float value.\n\n            * If this is a single int or float, then that value will be used\n              (e.g. 0 results in black pixels).\n            * If a tuple ``(a, b)``, then a random value is uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a list, then a random value will be sampled from that list\n              per image.\n            * If ``imgaug.ALL``, then equivalent to tuple ``(0, 255)``.\n            * If a ``StochasticParameter``, a new value will be sampled from\n              the parameter per image.\n\n    mode : int or str or list of str or list of int or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        Parameter that defines the handling of newly created pixels.\n        Same meaning as in OpenCV's border mode. Let ``abcdefgh`` be an image's\n        content and ``|`` be an image boundary, then:\n\n            * ``cv2.BORDER_REPLICATE``: ``aaaaaa|abcdefgh|hhhhhhh``\n            * ``cv2.BORDER_CONSTANT``: ``iiiiii|abcdefgh|iiiiiii``, where\n              ``i`` is the defined cval.\n            * ``replicate``: Same as ``cv2.BORDER_REPLICATE``.\n            * ``constant``: Same as ``cv2.BORDER_CONSTANT``.\n\n        The datatype of the parameter may be:\n\n            * If a single ``int``, then it must be one of ``cv2.BORDER_*``.\n            * If a single string, then it must be one of: ``replicate``,\n              ``reflect``, ``reflect_101``, ``wrap``, ``constant``.\n            * If a list of ints/strings, then per image a random mode will be\n              picked from that list.\n            * If ``imgaug.ALL``, then a random mode from all possible modes\n              will be picked per image.\n            * If ``StochasticParameter``, then the mode will be sampled from\n              that parameter per image, i.e. it must return only the above\n              mentioned strings.\n\n    fit_output : bool, optional\n        If ``True``, the image plane size and position will be adjusted\n        to still capture the whole image after perspective transformation.\n        (Followed by image resizing if `keep_size` is set to ``True``.)\n        Otherwise, parts of the transformed image may be outside of the image\n        plane.\n        This setting should not be set to ``True`` when using large `scale`\n        values as it could lead to very large images.\n\n        Added in 0.4.0.\n\n    polygon_recoverer : 'auto' or None or imgaug.augmentables.polygons._ConcavePolygonRecoverer, optional\n        The class to use to repair invalid polygons.\n        If ``\"auto\"``, a new instance of\n        :class`imgaug.augmentables.polygons._ConcavePolygonRecoverer`\n        will be created.\n        If ``None``, no polygon recoverer will be used.\n        If an object, then that object will be used and must provide a\n        ``recover_from()`` method, similar to\n        :class:`~imgaug.augmentables.polygons._ConcavePolygonRecoverer`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.PerspectiveTransform(scale=(0.01, 0.15))\n\n    Apply perspective transformations using a random scale between ``0.01``\n    and ``0.15`` per image, where the scale is roughly a measure of how far\n    the perspective transformation's corner points may be distanced from the\n    image's corner points. Higher scale values lead to stronger \"zoom-in\"\n    effects (and thereby stronger distortions).\n\n    >>> aug = iaa.PerspectiveTransform(scale=(0.01, 0.15), keep_size=False)\n\n    Same as in the previous example, but images are not resized back to\n    the input image size after augmentation. This will lead to smaller\n    output images.\n\n    \"\"\"\n\n    _BORDER_MODE_STR_TO_INT = {\n        \"replicate\": cv2.BORDER_REPLICATE,\n        \"constant\": cv2.BORDER_CONSTANT\n    }\n\n    def __init__(self, scale=(0.0, 0.06), cval=0, mode=\"constant\",\n                 keep_size=True, fit_output=False, polygon_recoverer=\"auto\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(PerspectiveTransform, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.scale = iap.handle_continuous_param(\n            scale, \"scale\", value_range=(0, None), tuple_to_uniform=True,\n            list_to_choice=True)\n        self.jitter = iap.Normal(loc=0, scale=self.scale)\n\n        # setting these to 1x1 caused problems for large scales and polygon\n        # augmentation\n        # TODO there is now a recoverer for polygons - are these minima still\n        #      needed/sensible?\n        self.min_width = 2\n        self.min_height = 2\n\n        self.cval = _handle_cval_arg(cval)\n        self.mode = self._handle_mode_arg(mode)\n        self.keep_size = keep_size\n        self.fit_output = fit_output\n\n        self.polygon_recoverer = polygon_recoverer\n        if polygon_recoverer == \"auto\":\n            self.polygon_recoverer = _ConcavePolygonRecoverer()\n\n        # Special order, mode and cval parameters for heatmaps and\n        # segmentation maps. These may either be None or a fixed value.\n        # Stochastic parameters are currently *not* supported.\n        # If set to None, the same values as for images will be used.\n        # That is really not recommended for the cval parameter.\n        self._order_heatmaps = cv2.INTER_LINEAR\n        self._order_segmentation_maps = cv2.INTER_NEAREST\n        self._mode_heatmaps = cv2.BORDER_CONSTANT\n        self._mode_segmentation_maps = cv2.BORDER_CONSTANT\n        self._cval_heatmaps = 0\n        self._cval_segmentation_maps = 0\n\n    # TODO unify this somehow with the global _handle_mode_arg() that is\n    #      currently used for Affine and PiecewiseAffine\n    @classmethod\n    @iap._prefetchable_str\n    def _handle_mode_arg(cls, mode):\n        available_modes = [cv2.BORDER_REPLICATE, cv2.BORDER_CONSTANT]\n        available_modes_str = [\"replicate\", \"constant\"]\n        if mode == ia.ALL:\n            return iap.Choice(available_modes)\n        if ia.is_single_integer(mode):\n            assert mode in available_modes, (\n                \"Expected mode to be in %s, got %d.\" % (\n                    str(available_modes), mode))\n            return iap.Deterministic(mode)\n        if ia.is_string(mode):\n            assert mode in available_modes_str, (\n                \"Expected mode to be in %s, got %s.\" % (\n                    str(available_modes_str), mode))\n            return iap.Deterministic(mode)\n        if isinstance(mode, list):\n            valid_types = all([ia.is_single_integer(val) or ia.is_string(val)\n                               for val in mode])\n            assert valid_types, (\n                \"Expected mode list to only contain integers/strings, got \"\n                \"types %s.\" % (\n                    \", \".join([str(type(val)) for val in mode]),))\n            valid_modes = all([val in available_modes + available_modes_str\n                               for val in mode])\n            assert valid_modes, (\n                \"Expected all mode values to be in %s, got %s.\" % (\n                    str(available_modes + available_modes_str), str(mode)))\n            return iap.Choice(mode)\n        if isinstance(mode, iap.StochasticParameter):\n            return mode\n        raise Exception(\n            \"Expected mode to be imgaug.ALL, an int, a string, a list \"\n            \"of int/strings or StochasticParameter, got %s.\" % (\n                type(mode),))\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        # Advance once, because below we always use random_state.copy() and\n        # hence the sampling calls actually don't change random_state's state.\n        # Without this, every call of the augmenter would produce the same\n        # results.\n        random_state.advance_()\n\n        samples_images = self._draw_samples(batch.get_rowwise_shapes(),\n                                            random_state.copy())\n\n        if batch.images is not None:\n            batch.images = self._augment_images_by_samples(batch.images,\n                                                           samples_images)\n\n        if batch.heatmaps is not None:\n            samples = self._draw_samples(\n                [augmentable.arr_0to1.shape\n                 for augmentable in batch.heatmaps],\n                random_state.copy())\n\n            batch.heatmaps = self._augment_maps_by_samples(\n                batch.heatmaps, \"arr_0to1\", samples, samples_images,\n                self._cval_heatmaps, self._mode_heatmaps, self._order_heatmaps)\n\n        if batch.segmentation_maps is not None:\n            samples = self._draw_samples(\n                [augmentable.arr.shape\n                 for augmentable in batch.segmentation_maps],\n                random_state.copy())\n\n            batch.segmentation_maps = self._augment_maps_by_samples(\n                batch.segmentation_maps, \"arr\", samples, samples_images,\n                self._cval_segmentation_maps, self._mode_segmentation_maps,\n                self._order_segmentation_maps)\n\n        # large scale values cause invalid polygons (unclear why that happens),\n        # hence the recoverer\n        # TODO add test for recoverer\n        if batch.polygons is not None:\n            func = functools.partial(\n                self._augment_keypoints_by_samples,\n                samples_images=samples_images)\n            batch.polygons = self._apply_to_polygons_as_keypoints(\n                batch.polygons, func, recoverer=self.polygon_recoverer)\n\n        for augm_name in [\"keypoints\", \"bounding_boxes\", \"line_strings\"]:\n            augm_value = getattr(batch, augm_name)\n            if augm_value is not None:\n                func = functools.partial(\n                    self._augment_keypoints_by_samples,\n                    samples_images=samples_images)\n                cbaois = self._apply_to_cbaois_as_keypoints(augm_value, func)\n                setattr(batch, augm_name, cbaois)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _augment_images_by_samples(self, images, samples):\n        iadt.gate_dtypes_strs(\n            images,\n            allowed=\"bool uint8 uint16 int8 int16 float16 float32 float64\",\n            disallowed=\"uint32 uint64 int32 int64 float128\",\n            augmenter=self\n        )\n\n        result = images\n        if not self.keep_size:\n            result = list(result)\n\n        gen = enumerate(zip(images, samples.matrices, samples.max_heights,\n                            samples.max_widths, samples.cvals, samples.modes))\n\n        for i, (image, matrix, max_height, max_width, cval, mode) in gen:\n            input_dtype = image.dtype\n            if input_dtype == iadt._INT8_DTYPE:\n                image = image.astype(np.int16)\n            elif (input_dtype\n                  in {iadt._BOOL_DTYPE, iadt._FLOAT16_DTYPE}):\n                image = image.astype(np.float32)\n\n            # cv2.warpPerspective only supports <=4 channels and errors\n            # on axes with size zero\n            nb_channels = image.shape[2]\n            has_zero_sized_axis = (image.size == 0)\n            if has_zero_sized_axis:\n                warped = image\n            elif nb_channels <= 4:\n                warped = cv2.warpPerspective(\n                    _normalize_cv2_input_arr_(image),\n                    matrix,\n                    (max_width, max_height),\n                    borderValue=cval,\n                    borderMode=mode)\n                if warped.ndim == 2 and images[i].ndim == 3:\n                    warped = np.expand_dims(warped, 2)\n            else:\n                # warp each channel on its own\n                # note that cv2 removes the channel axis in case of (H,W,1)\n                # inputs\n                warped = [\n                    cv2.warpPerspective(\n                        _normalize_cv2_input_arr_(image[..., c]),\n                        matrix,\n                        (max_width, max_height),\n                        borderValue=cval[min(c, len(cval)-1)],\n                        borderMode=mode,\n                        flags=cv2.INTER_LINEAR\n                    )\n                    for c in sm.xrange(nb_channels)\n                ]\n                warped = np.stack(warped, axis=-1)\n\n            if self.keep_size and not has_zero_sized_axis:\n                h, w = image.shape[0:2]\n                warped = ia.imresize_single_image(warped, (h, w))\n\n            if input_dtype.kind == \"b\":\n                warped = warped > 0.5\n            elif warped.dtype != input_dtype:\n                warped = iadt.restore_dtypes_(warped, input_dtype)\n\n            result[i] = warped\n\n        return result\n\n    # Added in 0.4.0.\n    def _augment_maps_by_samples(self, augmentables, arr_attr_name,\n                                 samples, samples_images, cval, mode, flags):\n        result = augmentables\n\n        # estimate max_heights/max_widths for the underlying images\n        # this is only necessary if keep_size is False as then the underlying\n        # image sizes change and we need to update them here\n        # TODO this was re-used from before _augment_batch_() -- reoptimize\n        if self.keep_size:\n            max_heights_imgs = samples.max_heights\n            max_widths_imgs = samples.max_widths\n        else:\n            max_heights_imgs = samples_images.max_heights\n            max_widths_imgs = samples_images.max_widths\n\n        gen = enumerate(zip(augmentables, samples.matrices, samples.max_heights,\n                            samples.max_widths))\n\n        for i, (augmentable_i, matrix, max_height, max_width) in gen:\n            arr = getattr(augmentable_i, arr_attr_name)\n\n            mode_i = mode\n            if mode is None:\n                mode_i = samples.modes[i]\n\n            cval_i = cval\n            if cval is None:\n                cval_i = samples.cvals[i]\n\n            nb_channels = arr.shape[2]\n            image_has_zero_sized_axis = (0 in augmentable_i.shape)\n            map_has_zero_sized_axis = (arr.size == 0)\n\n            if not image_has_zero_sized_axis:\n                if not map_has_zero_sized_axis:\n                    warped = [\n                        cv2.warpPerspective(\n                            _normalize_cv2_input_arr_(arr[..., c]),\n                            matrix,\n                            (max_width, max_height),\n                            borderValue=cval_i,\n                            borderMode=mode_i,\n                            flags=flags\n                        )\n                        for c in sm.xrange(nb_channels)\n                    ]\n                    warped = np.stack(warped, axis=-1)\n\n                    setattr(augmentable_i, arr_attr_name, warped)\n\n                if self.keep_size:\n                    h, w = arr.shape[0:2]\n                    augmentable_i = augmentable_i.resize((h, w))\n                else:\n                    new_shape = (\n                        max_heights_imgs[i], max_widths_imgs[i]\n                    ) + augmentable_i.shape[2:]\n                    augmentable_i.shape = new_shape\n\n                result[i] = augmentable_i\n\n        return result\n\n    # Added in 0.4.0.\n    def _augment_keypoints_by_samples(self, kpsois, samples_images):\n        result = kpsois\n\n        gen = enumerate(zip(kpsois,\n                            samples_images.matrices,\n                            samples_images.max_heights,\n                            samples_images.max_widths))\n\n        for i, (kpsoi, matrix, max_height, max_width) in gen:\n            image_has_zero_sized_axis = (0 in kpsoi.shape)\n\n            if not image_has_zero_sized_axis:\n                shape_orig = kpsoi.shape\n                shape_new = (max_height, max_width) + kpsoi.shape[2:]\n                kpsoi.shape = shape_new\n                if not kpsoi.empty:\n                    kps_arr = kpsoi.to_xy_array()\n                    warped = cv2.perspectiveTransform(\n                        np.array([kps_arr], dtype=np.float32), matrix)\n                    warped = warped[0]\n                    for kp, coords in zip(kpsoi.keypoints, warped):\n                        kp.x = coords[0]\n                        kp.y = coords[1]\n                if self.keep_size:\n                    kpsoi = kpsoi.on_(shape_orig)\n                result[i] = kpsoi\n\n        return result\n\n    # Added in 0.4.0.\n    def _draw_samples(self, shapes, random_state):\n        # pylint: disable=invalid-name\n        matrices = []\n        max_heights = []\n        max_widths = []\n        nb_images = len(shapes)\n        rngs = random_state.duplicate(3)\n\n        cval_samples = self.cval.draw_samples((nb_images, 3),\n                                              random_state=rngs[0])\n        mode_samples = self.mode.draw_samples((nb_images,),\n                                              random_state=rngs[1])\n        jitter = self.jitter.draw_samples((nb_images, 4, 2),\n                                          random_state=rngs[2])\n\n        # cv2 perspectiveTransform doesn't accept numpy arrays as cval\n        cval_samples_cv2 = cval_samples.tolist()\n\n        # if border modes are represented by strings, convert them to cv2\n        # border mode integers\n        if mode_samples.dtype.kind not in [\"i\", \"u\"]:\n            for mode, mapped_mode in self._BORDER_MODE_STR_TO_INT.items():\n                mode_samples[mode_samples == mode] = mapped_mode\n\n        # modify jitter to the four corner point coordinates\n        # some x/y values have to be modified from `jitter` to `1-jtter`\n        # for that\n        # TODO remove the abs() here. it currently only allows to \"zoom-in\",\n        #      not to \"zoom-out\"\n        points = np.mod(np.abs(jitter), 1)\n\n        # top left -- no changes needed, just use jitter\n        # top right\n        points[:, 1, 0] = 1.0 - points[:, 1, 0]  # w = 1.0 - jitter\n        # bottom right\n        points[:, 2, 0] = 1.0 - points[:, 2, 0]  # w = 1.0 - jitter\n        points[:, 2, 1] = 1.0 - points[:, 2, 1]  # h = 1.0 - jitter\n        # bottom left\n        points[:, 3, 1] = 1.0 - points[:, 3, 1]  # h = 1.0 - jitter\n\n        for shape, points_i in zip(shapes, points):\n            h, w = shape[0:2]\n\n            points_i[:, 0] *= w\n            points_i[:, 1] *= h\n\n            # Obtain a consistent order of the points and unpack them\n            # individually.\n            # Warning: don't just do (tl, tr, br, bl) = _order_points(...)\n            # here, because the reordered points_i is used further below.\n            points_i = self._order_points(points_i)\n            (tl, tr, br, bl) = points_i\n\n            # compute the width of the new image, which will be the\n            # maximum distance between bottom-right and bottom-left\n            # x-coordiates or the top-right and top-left x-coordinates\n            min_width = None\n            max_width = None\n            while min_width is None or min_width < self.min_width:\n                width_top = np.sqrt(((tr[0]-tl[0])**2) + ((tr[1]-tl[1])**2))\n                width_bottom = np.sqrt(((br[0]-bl[0])**2) + ((br[1]-bl[1])**2))\n                max_width = int(max(width_top, width_bottom))\n                min_width = int(min(width_top, width_bottom))\n                if min_width < self.min_width:\n                    step_size = (self.min_width - min_width)/2\n                    tl[0] -= step_size\n                    tr[0] += step_size\n                    bl[0] -= step_size\n                    br[0] += step_size\n\n            # compute the height of the new image, which will be the\n            # maximum distance between the top-right and bottom-right\n            # y-coordinates or the top-left and bottom-left y-coordinates\n            min_height = None\n            max_height = None\n            while min_height is None or min_height < self.min_height:\n                height_right = np.sqrt(((tr[0]-br[0])**2) + ((tr[1]-br[1])**2))\n                height_left = np.sqrt(((tl[0]-bl[0])**2) + ((tl[1]-bl[1])**2))\n                max_height = int(max(height_right, height_left))\n                min_height = int(min(height_right, height_left))\n                if min_height < self.min_height:\n                    step_size = (self.min_height - min_height)/2\n                    tl[1] -= step_size\n                    tr[1] -= step_size\n                    bl[1] += step_size\n                    br[1] += step_size\n\n            # now that we have the dimensions of the new image, construct\n            # the set of destination points to obtain a \"birds eye view\",\n            # (i.e. top-down view) of the image, again specifying points\n            # in the top-left, top-right, bottom-right, and bottom-left\n            # order\n            # do not use width-1 or height-1 here, as for e.g. width=3, height=2\n            # the bottom right coordinate is at (3.0, 2.0) and not (2.0, 1.0)\n            dst = np.array([\n                [0, 0],\n                [max_width, 0],\n                [max_width, max_height],\n                [0, max_height]\n            ], dtype=np.float32)\n\n            # compute the perspective transform matrix and then apply it\n            m = cv2.getPerspectiveTransform(points_i, dst)\n\n            if self.fit_output:\n                m, max_width, max_height = self._expand_transform(m, (h, w))\n\n            matrices.append(m)\n            max_heights.append(max_height)\n            max_widths.append(max_width)\n\n        mode_samples = mode_samples.astype(np.int32)\n        return _PerspectiveTransformSamplingResult(\n            matrices, max_heights, max_widths, cval_samples_cv2,\n            mode_samples)\n\n    @classmethod\n    def _order_points(cls, pts):\n        # initialzie a list of coordinates that will be ordered\n        # such that the first entry in the list is the top-left,\n        # the second entry is the top-right, the third is the\n        # bottom-right, and the fourth is the bottom-left\n        pts_ordered = np.zeros((4, 2), dtype=np.float32)\n\n        # the top-left point will have the smallest sum, whereas\n        # the bottom-right point will have the largest sum\n        pointwise_sum = pts.sum(axis=1)\n        pts_ordered[0] = pts[np.argmin(pointwise_sum)]\n        pts_ordered[2] = pts[np.argmax(pointwise_sum)]\n\n        # now, compute the difference between the points, the\n        # top-right point will have the smallest difference,\n        # whereas the bottom-left will have the largest difference\n        diff = np.diff(pts, axis=1)\n        pts_ordered[1] = pts[np.argmin(diff)]\n        pts_ordered[3] = pts[np.argmax(diff)]\n\n        # return the ordered coordinates\n        return pts_ordered\n\n    # Added in 0.4.0.\n    @classmethod\n    def _expand_transform(cls, matrix, shape):\n        height, width = shape\n        # do not use width-1 or height-1 here, as for e.g. width=3, height=2\n        # the bottom right coordinate is at (3.0, 2.0) and not (2.0, 1.0)\n        rect = np.array([\n            [0, 0],\n            [width, 0],\n            [width, height],\n            [0, height]], dtype=np.float32)\n        dst = cv2.perspectiveTransform(np.array([rect]), matrix)[0]\n\n        # get min x, y over transformed 4 points\n        # then modify target points by subtracting these minima\n        # => shift to (0, 0)\n        dst -= dst.min(axis=0, keepdims=True)\n        dst = np.around(dst, decimals=0)\n\n        matrix_expanded = cv2.getPerspectiveTransform(rect, dst)\n        max_width, max_height = dst.max(axis=0)\n        return matrix_expanded, int(max_width), int(max_height)\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.jitter, self.keep_size, self.cval, self.mode,\n                self.fit_output]\n\n\n# TODO add independent sigmas for x/y\n# TODO add independent alphas for x/y\n# TODO add backend arg\nclass ElasticTransformation(meta.Augmenter):\n    \"\"\"\n    Transform images by moving pixels locally around using displacement fields.\n\n    The augmenter has the parameters ``alpha`` and ``sigma``. ``alpha``\n    controls the strength of the displacement: higher values mean that pixels\n    are moved further. ``sigma`` controls the smoothness of the displacement:\n    higher values lead to smoother patterns -- as if the image was below water\n    -- while low values will cause indivdual pixels to be moved very\n    differently from their neighbours, leading to noisy and pixelated images.\n\n    A relation of 10:1 seems to be good for ``alpha`` and ``sigma``, e.g.\n    ``alpha=10`` and ``sigma=1`` or ``alpha=50``, ``sigma=5``. For ``128x128``\n    a setting of ``alpha=(0, 70.0)``, ``sigma=(4.0, 6.0)`` may be a good\n    choice and will lead to a water-like effect.\n\n    Code here was initially inspired by\n    https://gist.github.com/chsasank/4d8f68caf01f041a6453e67fb30f8f5a\n\n    For a detailed explanation, see ::\n\n        Simard, Steinkraus and Platt\n        Best Practices for Convolutional Neural Networks applied to Visual\n        Document Analysis\n        in Proc. of the International Conference on Document Analysis and\n        Recognition, 2003\n\n    .. note::\n\n        For coordinate-based inputs (keypoints, bounding boxes, polygons,\n        ...), this augmenter still has to perform an image-based augmentation,\n        which will make it significantly slower for such inputs than other\n        augmenters. See :ref:`performance`.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested (1)\n        * ``uint16``: yes; tested (1)\n        * ``uint32``: yes; tested (2)\n        * ``uint64``: limited; tested (3)\n        * ``int8``: yes; tested (1) (4) (5)\n        * ``int16``: yes; tested (4) (6)\n        * ``int32``: yes; tested (4) (6)\n        * ``int64``: limited; tested (3)\n        * ``float16``: yes; tested (1)\n        * ``float32``: yes; tested (1)\n        * ``float64``: yes; tested (1)\n        * ``float128``: no\n        * ``bool``: yes; tested (1) (7)\n\n        - (1) Always handled by ``cv2``.\n        - (2) Always handled by ``scipy``.\n        - (3) Only supported for ``order != 0``. Will fail for ``order=0``.\n        - (4) Mapped internally to ``float64`` when ``order=1``.\n        - (5) Mapped internally to ``int16`` when ``order>=2``.\n        - (6) Handled by ``cv2`` when ``order=0`` or ``order=1``, otherwise by\n              ``scipy``.\n        - (7) Mapped internally to ``float32``.\n\n    Parameters\n    ----------\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Strength of the distortion field. Higher values mean that pixels are\n        moved further with respect to the distortion field's direction.\n        Should be a value from interval ``[1.0, inf]``. Set this to around\n        ``10 * sigma`` for visible effects.\n\n            * If number, then that value will be used for all images.\n            * If tuple ``(a, b)``, then a random value will be uniformly\n              sampled per image from the interval ``[a, b]``.\n            * If a list, then for each image a random value will be sampled\n              from that list.\n            * If ``StochasticParameter``, then that parameter will be used to\n              sample a value per image.\n\n    sigma : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Corresponds to the standard deviation of the gaussian kernel used\n        in the original algorithm. Here, for performance reasons, it denotes\n        half of an average blur kernel size. (Only for ``sigma<1.5`` is\n        a gaussian kernel actually used.)\n        Higher values (for ``128x128`` images around 5.0) lead to more\n        water-like effects, while lower values (for ``128x128`` images\n        around ``1.0`` and lower) lead to more noisy, pixelated images. Set\n        this to around 1/10th of `alpha` for visible effects.\n\n            * If number, then that value will be used for all images.\n            * If tuple ``(a, b)``, then a random value will be uniformly\n              sampled per image from the interval ``[a, b]``.\n            * If a list, then for each image a random value will be sampled\n              from that list.\n            * If ``StochasticParameter``, then that parameter will be used to\n              sample a value per image.\n\n    order : int or list of int or imaug.ALL or imgaug.parameters.StochasticParameter, optional\n        Interpolation order to use. Same meaning as in\n        :func:`scipy.ndimage.map_coordinates` and may take any integer value\n        in the range ``0`` to ``5``, where orders close to ``0`` are faster.\n\n            * If a single int, then that order will be used for all images.\n            * If a tuple ``(a, b)``, then a random value will be uniformly\n              sampled per image from the interval ``[a, b]``.\n            * If a list, then for each image a random value will be sampled\n              from that list.\n            * If ``imgaug.ALL``, then equivalant to list\n              ``[0, 1, 2, 3, 4, 5]``.\n            * If ``StochasticParameter``, then that parameter is queried per\n              image to sample the order value to use.\n\n    cval : number or tuple of number or list of number or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        The constant intensity value used to fill in new pixels.\n        This value is only used if `mode` is set to ``constant``.\n        For standard ``uint8`` images (value range ``0`` to ``255``), this\n        value may also should also be in the range ``0`` to ``255``. It may\n        be a ``float`` value, even for images with integer dtypes.\n\n            * If this is a single number, then that value will be used\n              (e.g. ``0`` results in black pixels).\n            * If a tuple ``(a, b)``, then a random value will be uniformly\n              sampled per image from the interval ``[a, b]``.\n            * If a list, then a random value will be picked from that list per\n              image.\n            * If ``imgaug.ALL``, a value from the discrete range ``[0..255]``\n              will be sampled per image.\n            * If a ``StochasticParameter``, a new value will be sampled from\n              the parameter per image.\n\n    mode : str or list of str or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        Parameter that defines the handling of newly created pixels.\n        May take the same values as in :func:`scipy.ndimage.map_coordinates`,\n        i.e. ``constant``, ``nearest``, ``reflect`` or ``wrap``.\n\n            * If a single string, then that mode will be used for all images.\n            * If a list of strings, then per image a random mode will be picked\n              from that list.\n            * If ``imgaug.ALL``, then a random mode from all possible modes\n              will be picked.\n            * If ``StochasticParameter``, then the mode will be sampled from\n              that parameter per image, i.e. it must return only the above\n              mentioned strings.\n\n    polygon_recoverer : 'auto' or None or imgaug.augmentables.polygons._ConcavePolygonRecoverer, optional\n        The class to use to repair invalid polygons.\n        If ``\"auto\"``, a new instance of\n        :class`imgaug.augmentables.polygons._ConcavePolygonRecoverer`\n        will be created.\n        If ``None``, no polygon recoverer will be used.\n        If an object, then that object will be used and must provide a\n        ``recover_from()`` method, similar to\n        :class:`~imgaug.augmentables.polygons._ConcavePolygonRecoverer`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.ElasticTransformation(alpha=50.0, sigma=5.0)\n\n    Apply elastic transformations with a strength/alpha of ``50.0`` and\n    smoothness of ``5.0`` to all images.\n\n    >>> aug = iaa.ElasticTransformation(alpha=(0.0, 70.0), sigma=5.0)\n\n    Apply elastic transformations with a strength/alpha that comes\n    from the interval ``[0.0, 70.0]`` (randomly picked per image) and\n    with a smoothness of ``5.0``.\n\n    \"\"\"\n\n    NB_NEIGHBOURING_KEYPOINTS = 3\n    NEIGHBOURING_KEYPOINTS_DISTANCE = 1.0\n    KEYPOINT_AUG_ALPHA_THRESH = 0.05\n    # even at high alphas we don't augment keypoints if the sigma is too low,\n    # because then the pixel movements are mostly gaussian noise anyways\n    KEYPOINT_AUG_SIGMA_THRESH = 1.0\n\n    _MAPPING_MODE_SCIPY_CV2 = {\n        \"constant\": cv2.BORDER_CONSTANT,\n        \"nearest\": cv2.BORDER_REPLICATE,\n        \"reflect\": cv2.BORDER_REFLECT_101,\n        \"wrap\": cv2.BORDER_WRAP\n    }\n\n    _MAPPING_ORDER_SCIPY_CV2 = {\n        0: cv2.INTER_NEAREST,\n        1: cv2.INTER_LINEAR,\n        2: cv2.INTER_CUBIC,\n        3: cv2.INTER_CUBIC,\n        4: cv2.INTER_CUBIC,\n        5: cv2.INTER_CUBIC\n    }\n\n    def __init__(self, alpha=(1.0, 40.0), sigma=(4.0, 8.0), order=0, cval=0,\n                 mode=\"constant\",\n                 polygon_recoverer=\"auto\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ElasticTransformation, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.alpha = iap.handle_continuous_param(\n            alpha, \"alpha\", value_range=(0, None), tuple_to_uniform=True,\n            list_to_choice=True)\n        self.sigma = iap.handle_continuous_param(\n            sigma, \"sigma\", value_range=(0, None), tuple_to_uniform=True,\n            list_to_choice=True)\n\n        self.order = self._handle_order_arg(order)\n        self.cval = _handle_cval_arg(cval)\n        self.mode = self._handle_mode_arg(mode)\n\n        self.polygon_recoverer = polygon_recoverer\n        if polygon_recoverer == \"auto\":\n            self.polygon_recoverer = _ConcavePolygonRecoverer()\n\n        # Special order, mode and cval parameters for heatmaps and\n        # segmentation maps. These may either be None or a fixed value.\n        # Stochastic parameters are currently *not* supported.\n        # If set to None, the same values as for images will be used.\n        # That is really not recommended for the cval parameter.\n        #\n        self._order_heatmaps = 3\n        self._order_segmentation_maps = 0\n        self._mode_heatmaps = \"constant\"\n        self._mode_segmentation_maps = \"constant\"\n        self._cval_heatmaps = 0.0\n        self._cval_segmentation_maps = 0\n\n        self._last_meshgrid = None\n\n    @classmethod\n    @iap._prefetchable\n    def _handle_order_arg(cls, order):\n        if order == ia.ALL:\n            return iap.Choice([0, 1, 2, 3, 4, 5])\n        return iap.handle_discrete_param(\n            order, \"order\", value_range=(0, 5), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=False)\n\n    @classmethod\n    @iap._prefetchable_str\n    def _handle_mode_arg(cls, mode):\n        if mode == ia.ALL:\n            return iap.Choice([\"constant\", \"nearest\", \"reflect\", \"wrap\"])\n        if ia.is_string(mode):\n            return iap.Deterministic(mode)\n        if ia.is_iterable(mode):\n            assert all([ia.is_string(val) for val in mode]), (\n                \"Expected mode list to only contain strings, got \"\n                \"types %s.\" % (\n                    \", \".join([str(type(val)) for val in mode]),))\n            return iap.Choice(mode)\n        if isinstance(mode, iap.StochasticParameter):\n            return mode\n        raise Exception(\n            \"Expected mode to be imgaug.ALL, a string, a list of strings \"\n            \"or StochasticParameter, got %s.\" % (type(mode),))\n\n    def _draw_samples(self, nb_images, random_state):\n        rss = random_state.duplicate(nb_images+5)\n        alphas = self.alpha.draw_samples((nb_images,), random_state=rss[-5])\n        sigmas = self.sigma.draw_samples((nb_images,), random_state=rss[-4])\n        orders = self.order.draw_samples((nb_images,), random_state=rss[-3])\n        cvals = self.cval.draw_samples((nb_images,), random_state=rss[-2])\n        modes = self.mode.draw_samples((nb_images,), random_state=rss[-1])\n        return _ElasticTransformationSamplingResult(\n            rss[0], alphas, sigmas, orders, cvals, modes)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        # pylint: disable=invalid-name\n        if batch.images is not None:\n            iadt.gate_dtypes_strs(\n                batch.images,\n                allowed=\"bool uint8 uint16 uint32 uint64 int8 int16 int32 \"\n                        \"int64 float16 float32 float64\",\n                disallowed=\"float128\",\n                augmenter=self\n            )\n\n        shapes = batch.get_rowwise_shapes()\n        samples = self._draw_samples(len(shapes), random_state)\n        smgen = _ElasticTfShiftMapGenerator()\n        shift_maps = smgen.generate(shapes, samples.alphas, samples.sigmas,\n                                    samples.random_state)\n\n        for i, (shape, (dx, dy)) in enumerate(zip(shapes, shift_maps)):\n            if batch.images is not None:\n                batch.images[i] = self._augment_image_by_samples(\n                    batch.images[i], i, samples, dx, dy)\n            if batch.heatmaps is not None:\n                batch.heatmaps[i] = self._augment_hm_or_sm_by_samples(\n                    batch.heatmaps[i], i, samples, dx, dy, \"arr_0to1\",\n                    self._cval_heatmaps, self._mode_heatmaps,\n                    self._order_heatmaps)\n            if batch.segmentation_maps is not None:\n                batch.segmentation_maps[i] = self._augment_hm_or_sm_by_samples(\n                    batch.segmentation_maps[i], i, samples, dx, dy, \"arr\",\n                    self._cval_segmentation_maps, self._mode_segmentation_maps,\n                    self._order_segmentation_maps)\n            if batch.keypoints is not None:\n                batch.keypoints[i] = self._augment_kpsoi_by_samples(\n                    batch.keypoints[i], i, samples, dx, dy)\n            if batch.bounding_boxes is not None:\n                batch.bounding_boxes[i] = self._augment_bbsoi_by_samples(\n                    batch.bounding_boxes[i], i, samples, dx, dy)\n            if batch.polygons is not None:\n                batch.polygons[i] = self._augment_psoi_by_samples(\n                    batch.polygons[i], i, samples, dx, dy)\n            if batch.line_strings is not None:\n                batch.line_strings[i] = self._augment_lsoi_by_samples(\n                    batch.line_strings[i], i, samples, dx, dy)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _augment_image_by_samples(self, image, row_idx, samples, dx, dy):\n        # pylint: disable=invalid-name\n        min_value, _center_value, max_value = \\\n            iadt.get_value_range_of_dtype(image.dtype)\n        cval = max(min(samples.cvals[row_idx], max_value), min_value)\n\n        input_dtype = image.dtype\n        if image.dtype == iadt._FLOAT16_DTYPE:\n            image = image.astype(np.float32)\n\n        image_aug = self._map_coordinates(\n            image, dx, dy,\n            order=samples.orders[row_idx],\n            cval=cval,\n            mode=samples.modes[row_idx])\n\n        if image.dtype != input_dtype:\n            image_aug = iadt.restore_dtypes_(image_aug, input_dtype)\n        return image_aug\n\n    # Added in 0.4.0.\n    def _augment_hm_or_sm_by_samples(self, augmentable, row_idx, samples,\n                                     dx, dy, arr_attr_name, cval, mode, order):\n        # pylint: disable=invalid-name\n        cval = cval if cval is not None else samples.cvals[row_idx]\n        mode = mode if mode is not None else samples.modes[row_idx]\n        order = order if order is not None else samples.orders[row_idx]\n\n        # note that we do not have to check for zero-sized axes here,\n        # because _generate_shift_maps(), _map_coordinates(), .resize()\n        # and np.clip() are all known to handle arrays with zero-sized axes\n\n        arr = getattr(augmentable, arr_attr_name)\n\n        if arr.shape[0:2] == augmentable.shape[0:2]:\n            arr_warped = self._map_coordinates(\n                arr, dx, dy, order=order, cval=cval, mode=mode)\n\n            # interpolation in map_coordinates() can cause some values to\n            # be below/above 1.0, so we clip here\n            if order >= 3 and isinstance(augmentable, ia.HeatmapsOnImage):\n                arr_warped = np.clip(arr_warped, 0.0, 1.0, out=arr_warped)\n\n            setattr(augmentable, arr_attr_name, arr_warped)\n        else:\n            # Heatmaps/Segmaps do not have the same size as augmented\n            # images. This may result in indices of moved pixels being\n            # different. To prevent this, we use the same image size as\n            # for the base images, but that requires resizing the heatmaps\n            # temporarily to the image sizes.\n            height_orig, width_orig = arr.shape[0:2]\n            augmentable = augmentable.resize(augmentable.shape[0:2])\n            arr = getattr(augmentable, arr_attr_name)\n\n            # TODO will it produce similar results to first downscale the\n            #      shift maps and then remap? That would make the remap\n            #      step take less operations and would also mean that the\n            #      heatmaps wouldnt have to be scaled up anymore. It would\n            #      also simplify the code as this branch could be merged\n            #      with the one above.\n            arr_warped = self._map_coordinates(\n                arr, dx, dy, order=order, cval=cval, mode=mode)\n\n            # interpolation in map_coordinates() can cause some values to\n            # be below/above 1.0, so we clip here\n            if order >= 3 and isinstance(augmentable, ia.HeatmapsOnImage):\n                arr_warped = np.clip(arr_warped, 0.0, 1.0, out=arr_warped)\n\n            setattr(augmentable, arr_attr_name, arr_warped)\n\n            augmentable = augmentable.resize((height_orig, width_orig))\n\n        return augmentable\n\n    # Added in 0.4.0.\n    def _augment_kpsoi_by_samples(self, kpsoi, row_idx, samples, dx, dy):\n        # pylint: disable=misplaced-comparison-constant, invalid-name\n        height, width = kpsoi.shape[0:2]\n        alpha = samples.alphas[row_idx]\n        sigma = samples.sigmas[row_idx]\n\n        # TODO add test for keypoint alignment when keypoints are empty\n        # Note: this block must be placed after _generate_shift_maps() to\n        # keep samples aligned\n        # Note: we should stop for zero-sized axes early here, event though\n        # there is a height/width check for each keypoint, because the\n        # channel number can also be zero\n        image_has_zero_sized_axes = (0 in kpsoi.shape)\n        params_below_thresh = (\n            alpha <= self.KEYPOINT_AUG_ALPHA_THRESH\n            or sigma <= self.KEYPOINT_AUG_SIGMA_THRESH)\n\n        if kpsoi.empty or image_has_zero_sized_axes or params_below_thresh:\n            # ElasticTransformation does not change the shape, hence we can\n            # skip the below steps\n            return kpsoi\n\n        for kp in kpsoi.keypoints:\n            within_image_plane = (0 <= kp.x < width and 0 <= kp.y < height)\n            if within_image_plane:\n                kp_neighborhood = kp.generate_similar_points_manhattan(\n                    self.NB_NEIGHBOURING_KEYPOINTS,\n                    self.NEIGHBOURING_KEYPOINTS_DISTANCE,\n                    return_array=True\n                )\n\n                # We can clip here, because we made sure above that the\n                # keypoint is inside the image plane. Keypoints at the\n                # bottom row or right columns might be rounded outside\n                # the image plane, which we prevent here. We reduce\n                # neighbours to only those within the image plane as only\n                # for such points we know where to move them.\n                xx = np.round(kp_neighborhood[:, 0]).astype(np.int32)\n                yy = np.round(kp_neighborhood[:, 1]).astype(np.int32)\n                inside_image_mask = np.logical_and(\n                    np.logical_and(0 <= xx, xx < width),\n                    np.logical_and(0 <= yy, yy < height)\n                )\n                xx = xx[inside_image_mask]\n                yy = yy[inside_image_mask]\n\n                xxyy = np.concatenate(\n                    [xx[:, np.newaxis], yy[:, np.newaxis]],\n                    axis=1)\n\n                xxyy_aug = np.copy(xxyy).astype(np.float32)\n                xxyy_aug[:, 0] += dx[yy, xx]\n                xxyy_aug[:, 1] += dy[yy, xx]\n\n                med = ia.compute_geometric_median(xxyy_aug)\n                # uncomment to use average instead of median\n                # med = np.average(xxyy_aug, 0)\n                kp.x = med[0]\n                kp.y = med[1]\n\n        return kpsoi\n\n    # Added in 0.4.0.\n    def _augment_psoi_by_samples(self, psoi, row_idx, samples, dx, dy):\n        # pylint: disable=invalid-name\n        func = functools.partial(self._augment_kpsoi_by_samples,\n                                 row_idx=row_idx, samples=samples, dx=dx, dy=dy)\n        return self._apply_to_polygons_as_keypoints(\n            psoi, func, recoverer=self.polygon_recoverer)\n\n    # Added in 0.4.0.\n    def _augment_lsoi_by_samples(self, lsoi, row_idx, samples, dx, dy):\n        # pylint: disable=invalid-name\n        func = functools.partial(self._augment_kpsoi_by_samples,\n                                 row_idx=row_idx, samples=samples, dx=dx, dy=dy)\n        return self._apply_to_cbaois_as_keypoints(lsoi, func)\n\n    # Added in 0.4.0.\n    def _augment_bbsoi_by_samples(self, bbsoi, row_idx, samples, dx, dy):\n        # pylint: disable=invalid-name\n        func = functools.partial(self._augment_kpsoi_by_samples,\n                                 row_idx=row_idx, samples=samples, dx=dx, dy=dy)\n        return self._apply_to_cbaois_as_keypoints(bbsoi, func)\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.alpha, self.sigma, self.order, self.cval, self.mode]\n\n    def _map_coordinates(self, image, dx, dy, order=1, cval=0, mode=\"constant\"):\n        \"\"\"Remap pixels in an image according to x/y shift maps.\n\n        **Supported dtypes**:\n\n        if (backend=\"scipy\" and order=0):\n\n            * ``uint8``: yes\n            * ``uint16``: yes\n            * ``uint32``: yes\n            * ``uint64``: no (1)\n            * ``int8``: yes\n            * ``int16``: yes\n            * ``int32``: yes\n            * ``int64``: no (2)\n            * ``float16``: yes\n            * ``float32``: yes\n            * ``float64``: yes\n            * ``float128``: no (3)\n            * ``bool``: yes\n\n            - (1) produces array filled with only 0\n            - (2) produces array filled with <min_value> when testing\n                  with <max_value>\n            - (3) causes: 'data type no supported'\n\n        if (backend=\"scipy\" and order>0):\n\n            * ``uint8``: yes (1)\n            * ``uint16``: yes (1)\n            * ``uint32``: yes (1)\n            * ``uint64``: yes (1)\n            * ``int8``: yes (1)\n            * ``int16``: yes (1)\n            * ``int32``: yes (1)\n            * ``int64``: yes (1)\n            * ``float16``: yes (1)\n            * ``float32``: yes (1)\n            * ``float64``: yes (1)\n            * ``float128``: no (2)\n            * ``bool``: yes\n\n            - (1) rather loose test, to avoid having to re-compute the\n                  interpolation\n            - (2) causes: 'data type no supported'\n\n        if (backend=\"cv2\" and order=0):\n\n            * ``uint8``: yes\n            * ``uint16``: yes\n            * ``uint32``: no (1)\n            * ``uint64``: no (2)\n            * ``int8``: yes\n            * ``int16``: yes\n            * ``int32``: yes\n            * ``int64``: no (2)\n            * ``float16``: yes\n            * ``float32``: yes\n            * ``float64``: yes\n            * ``float128``: no (3)\n            * ``bool``: no (4)\n\n            - (1) causes: src data type = 6 is not supported\n            - (2) silently converts to int32\n            - (3) causes: src data type = 13 is not supported\n            - (4) causes: src data type = 0 is not supported\n\n        if (backend=\"cv2\" and order=1):\n\n            * ``uint8``: yes\n            * ``uint16``: yes\n            * ``uint32``: no (1)\n            * ``uint64``: no (2)\n            * ``int8``: no (2)\n            * ``int16``: no (2)\n            * ``int32``: no (2)\n            * ``int64``: no (2)\n            * ``float16``: yes\n            * ``float32``: yes\n            * ``float64``: yes\n            * ``float128``: no (3)\n            * ``bool``: no (4)\n\n            - (1) causes: src data type = 6 is not supported\n            - (2) causes: OpenCV(3.4.5) (...)/imgwarp.cpp:1805:\n                  error: (-215:Assertion failed) ifunc != 0 in function\n                  'remap'\n            - (3) causes: src data type = 13 is not supported\n            - (4) causes: src data type = 0 is not supported\n\n        if (backend=\"cv2\" and order>=2):\n\n            * ``uint8``: yes\n            * ``uint16``: yes\n            * ``uint32``: no (1)\n            * ``uint64``: no (2)\n            * ``int8``: no (2)\n            * ``int16``: yes\n            * ``int32``: no (2)\n            * ``int64``: no (2)\n            * ``float16``: yes\n            * ``float32``: yes\n            * ``float64``: yes\n            * ``float128``: no (3)\n            * ``bool``: no (4)\n\n            - (1) causes: src data type = 6 is not supported\n            - (2) causes: OpenCV(3.4.5) (...)/imgwarp.cpp:1805:\n                  error: (-215:Assertion failed) ifunc != 0 in function\n                  'remap'\n            - (3) causes: src data type = 13 is not supported\n            - (4) causes: src data type = 0 is not supported\n\n        \"\"\"\n        # pylint: disable=invalid-name\n        if image.size == 0:\n            return np.copy(image)\n\n        if (order == 0\n                and image.dtype\n                in {iadt._UINT64_DTYPE, iadt._INT64_DTYPE}):\n            raise Exception(\n                \"dtypes uint64 and int64 are only supported in \"\n                \"ElasticTransformation for order=0, got order=%d with \"\n                \"dtype=%s.\" % (order, image.dtype.name))\n\n        input_dtype = image.dtype\n        if image.dtype.kind == \"b\":\n            image = image.astype(np.float32)\n        elif (order == 1\n              and image.dtype\n              in {iadt._INT8_DTYPE, iadt._INT16_DTYPE, iadt._INT32_DTYPE}):\n            image = image.astype(np.float64)\n        elif order >= 2 and image.dtype == iadt._INT8_DTYPE:\n            image = image.astype(np.int16)\n        elif order >= 2 and image.dtype == iadt._INT32_DTYPE:\n            image = image.astype(np.float64)\n\n        shrt_max = 32767  # maximum of datatype `short`\n        backend = \"cv2\"\n        if order == 0:\n            bad_dtype_cv2 = image.dtype in iadt._convert_dtype_strs_to_types(\n                \"uint32 uint64 int64 float128 bool\"\n            )\n        elif order == 1:\n            bad_dtype_cv2 = image.dtype in iadt._convert_dtype_strs_to_types(\n                \"uint32 uint64 int8 int16 int32 int64 float128 bool\"\n            )\n        else:\n            bad_dtype_cv2 = image.dtype in iadt._convert_dtype_strs_to_types(\n                \"uint32 uint64 int8 int32 int64 float128 bool\"\n            )\n\n        bad_dx_shape_cv2 = (dx.shape[0] >= shrt_max or dx.shape[1] >= shrt_max)\n        bad_dy_shape_cv2 = (dy.shape[0] >= shrt_max or dy.shape[1] >= shrt_max)\n        if bad_dtype_cv2 or bad_dx_shape_cv2 or bad_dy_shape_cv2:\n            backend = \"scipy\"\n\n        assert image.ndim == 3, (\n            \"Expected 3-dimensional image, got %d dimensions.\" % (image.ndim,))\n\n        h, w, nb_channels = image.shape\n        last = self._last_meshgrid\n        if last is not None and last[0].shape == (h, w):\n            y, x = self._last_meshgrid\n        else:\n            y, x = np.meshgrid(\n                np.arange(h).astype(np.float32),\n                np.arange(w).astype(np.float32),\n                indexing=\"ij\"\n            )\n            self._last_meshgrid = (y, x)\n        x_shifted = x - dx\n        y_shifted = y - dy\n\n        if backend == \"scipy\":\n            result = np.empty_like(image)\n\n            for c in sm.xrange(image.shape[2]):\n                remapped_flat = ndimage.interpolation.map_coordinates(\n                    image[..., c],\n                    (y_shifted.flatten(), x_shifted.flatten()),\n                    order=order,\n                    cval=cval,\n                    mode=mode\n                )\n                remapped = remapped_flat.reshape((h, w))\n                result[..., c] = remapped\n        else:\n            if image.dtype.kind == \"f\":\n                cval = float(cval)\n            else:\n                cval = int(cval)\n\n            border_mode = self._MAPPING_MODE_SCIPY_CV2[mode]\n            interpolation = self._MAPPING_ORDER_SCIPY_CV2[order]\n\n            is_nearest_neighbour = (interpolation == cv2.INTER_NEAREST)\n            map1, map2 = cv2.convertMaps(\n                x_shifted, y_shifted, cv2.CV_16SC2,\n                nninterpolation=is_nearest_neighbour)\n            # remap only supports up to 4 channels\n            if nb_channels <= 4:\n                # dst does not seem to improve performance here\n                result = cv2.remap(\n                    _normalize_cv2_input_arr_(image),\n                    map1,\n                    map2,\n                    interpolation=interpolation,\n                    borderMode=border_mode,\n                    borderValue=tuple([cval] * nb_channels)\n                )\n                if image.ndim == 3 and result.ndim == 2:\n                    result = result[..., np.newaxis]\n            else:\n                current_chan_idx = 0\n                result = []\n                while current_chan_idx < nb_channels:\n                    channels = image[..., current_chan_idx:current_chan_idx+4]\n                    result_c = cv2.remap(\n                        _normalize_cv2_input_arr_(channels),\n                        map1, map2, interpolation=interpolation,\n                        borderMode=border_mode, borderValue=(cval, cval, cval))\n                    if result_c.ndim == 2:\n                        result_c = result_c[..., np.newaxis]\n                    result.append(result_c)\n                    current_chan_idx += 4\n                result = np.concatenate(result, axis=2)\n\n        if result.dtype != input_dtype:\n            result = iadt.restore_dtypes_(result, input_dtype)\n\n        return result\n\n\nclass _ElasticTransformationSamplingResult(object):\n    def __init__(self, random_state, alphas, sigmas, orders, cvals, modes):\n        self.random_state = random_state\n        self.alphas = alphas\n        self.sigmas = sigmas\n        self.orders = orders\n        self.cvals = cvals\n        self.modes = modes\n\n\nclass _ElasticTfShiftMapGenerator(object):\n    \"\"\"Class to generate shift/displacement maps for ElasticTransformation.\n\n    This class re-uses samples for multiple examples. This minimizes the amount\n    of sampling that has to be done.\n\n    Added in 0.5.0.\n\n    \"\"\"\n\n    # Not really necessary to have this as a class, considering it has no\n    # attributes. But it makes things easier to read.\n    # Added in 0.5.0.\n    def __init__(self):\n        pass\n\n    # Added in 0.5.0.\n    def generate(self, shapes, alphas, sigmas, random_state):\n        # We will sample shift maps from [0.0, 1.0] and then shift by -0.5 to\n        # [-0.5, 0.5]. To bring these maps to [-1.0, 1.0], we have to multiply\n        # somewhere by 2. It is fastes to multiply the (fewer) alphas, which\n        # we will have to multiply the shift maps with anyways.\n        alphas *= 2\n\n        # Configuration for each chunk.\n        # switch dx / dy, flip dx lr, flip dx ud, flip dy lr, flip dy ud\n        switch = [False, True]\n        fliplr_dx = [False, True]\n        flipud_dx = [False, True]\n        fliplr_dy = [False, True]\n        flipud_dy = [False, True]\n        configs = list(\n            itertools.product(\n                switch, fliplr_dx, flipud_dx, fliplr_dy, flipud_dy\n            )\n        )\n\n        areas = [shape[0] * shape[1] for shape in shapes]\n        nb_chunks = len(configs)\n        gen = zip(\n            self._split_chunks(shapes, nb_chunks),\n            self._split_chunks(areas, nb_chunks),\n            self._split_chunks(alphas, nb_chunks),\n            self._split_chunks(sigmas, nb_chunks)\n        )\n        # \"_c\" denotes a chunk here\n        for shapes_c, areas_c, alphas_c, sigmas_c in gen:\n            area_max = max(areas_c)\n\n            dxdy = random_state.random((2, area_max))\n            dxdy -= 0.5\n            dx, dy = dxdy[0, :], dxdy[1, :]\n\n            # dx_lr = flip dx left-right, dx_ud = flip dx up-down\n            # dy_lr, dy_ud analogous\n            for i, (switch_i, dx_lr, dx_ud, dy_lr, dy_ud) in enumerate(configs):\n                if i >= len(shapes_c):\n                    break\n\n                dx_i, dy_i = (dx, dy) if not switch_i else (dy, dx)\n                shape_i = shapes_c[i][0:2]\n                area_i = shape_i[0] * shape_i[1]\n\n                if area_i == 0:\n                    yield (\n                        np.zeros(shape_i, dtype=np.float32),\n                        np.zeros(shape_i, dtype=np.float32)\n                    )\n                else:\n                    dx_i = dx_i[0:area_i].reshape(shape_i)\n                    dy_i = dy_i[0:area_i].reshape(shape_i)\n                    dx_i, dy_i = self._flip(dx_i, dy_i,\n                                            (dx_lr, dx_ud, dy_lr, dy_ud))\n                    dx_i, dy_i = self._mul_alpha(dx_i, dy_i, alphas_c[i])\n                    yield self._smoothen_(dx_i, dy_i, sigmas_c[i])\n\n    # Added in 0.5.0.\n    @classmethod\n    def _flip(cls, dx, dy, flips):\n        # no measureable benefit from using cv2 here\n        if flips[0]:\n            dx = np.fliplr(dx)\n        if flips[1]:\n            dx = np.flipud(dx)\n        if flips[2]:\n            dy = np.fliplr(dy)\n        if flips[3]:\n            dy = np.flipud(dy)\n        return dx, dy\n\n    # Added in 0.5.0.\n    @classmethod\n    def _mul_alpha(cls, dx, dy, alpha):\n        # performance drops for cv2.multiply here\n        dx = dx * alpha\n        dy = dy * alpha\n        return dx, dy\n\n    # Added in 0.5.0.\n    @classmethod\n    def _smoothen_(cls, dx, dy, sigma):\n        if sigma < 1.5:\n            dx = blur_lib.blur_gaussian_(dx, sigma)\n            dy = blur_lib.blur_gaussian_(dy, sigma)\n        else:\n            ksize = int(round(2*sigma))\n            dx = cv2.blur(dx, (ksize, ksize), dst=dx)\n            dy = cv2.blur(dy, (ksize, ksize), dst=dy)\n        return dx, dy\n\n    # Added in 0.5.0.\n    @classmethod\n    def _split_chunks(cls, iterable, chunk_size):\n        for i in sm.xrange(0, len(iterable), chunk_size):\n            yield iterable[i:i+chunk_size]\n\n\nclass Rot90(meta.Augmenter):\n    \"\"\"\n    Rotate images clockwise by multiples of 90 degrees.\n\n    This could also be achieved using ``Affine``, but ``Rot90`` is\n    significantly more efficient.\n\n    **Supported dtypes**:\n\n    if (keep_size=False):\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    if (keep_size=True):\n\n        minimum of (\n            ``imgaug.augmenters.geometric.Rot90(keep_size=False)``,\n            :func:`~imgaug.imgaug.imresize_many_images`\n        )\n\n    Parameters\n    ----------\n    k : int or list of int or tuple of int or imaug.ALL or imgaug.parameters.StochasticParameter, optional\n        How often to rotate clockwise by 90 degrees.\n\n            * If a single ``int``, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a random value will be uniformly\n              sampled per image from the discrete interval ``[a..b]``.\n            * If a list, then for each image a random value will be sampled\n              from that list.\n            * If ``imgaug.ALL``, then equivalant to list ``[0, 1, 2, 3]``.\n            * If ``StochasticParameter``, then that parameter is queried per\n              image to sample the value to use.\n\n    keep_size : bool, optional\n        After rotation by an odd-valued `k` (e.g. 1 or 3), the resulting image\n        may have a different height/width than the original image.\n        If this parameter is set to ``True``, then the rotated\n        image will be resized to the input image's size. Note that this might\n        also cause the augmented image to look distorted.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Rot90(1)\n\n    Rotate all images by 90 degrees.\n    Resize these images afterwards to keep the size that they had before\n    augmentation.\n    This may cause the images to look distorted.\n\n    >>> aug = iaa.Rot90([1, 3])\n\n    Rotate all images by 90 or 270 degrees.\n    Resize these images afterwards to keep the size that they had before\n    augmentation.\n    This may cause the images to look distorted.\n\n    >>> aug = iaa.Rot90((1, 3))\n\n    Rotate all images by 90, 180 or 270 degrees.\n    Resize these images afterwards to keep the size that they had before\n    augmentation.\n    This may cause the images to look distorted.\n\n    >>> aug = iaa.Rot90((1, 3), keep_size=False)\n\n    Rotate all images by 90, 180 or 270 degrees.\n    Does not resize to the original image size afterwards, i.e. each image's\n    size may change.\n\n    \"\"\"\n\n    def __init__(self, k=1, keep_size=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Rot90, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        if k == ia.ALL:\n            k = [0, 1, 2, 3]\n        self.k = iap.handle_discrete_param(\n            k, \"k\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=False)\n\n        self.keep_size = keep_size\n\n    def _draw_samples(self, nb_images, random_state):\n        return self.k.draw_samples((nb_images,), random_state=random_state)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        # pylint: disable=invalid-name\n        ks = self._draw_samples(batch.nb_rows, random_state)\n\n        if batch.images is not None:\n            batch.images = self._augment_arrays_by_samples(\n                batch.images, ks, self.keep_size, ia.imresize_single_image)\n\n        if batch.heatmaps is not None:\n            batch.heatmaps = self._augment_maps_by_samples(\n                batch.heatmaps, \"arr_0to1\", ks)\n\n        if batch.segmentation_maps is not None:\n            batch.segmentation_maps = self._augment_maps_by_samples(\n                batch.segmentation_maps, \"arr\", ks)\n\n        for augm_name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                          \"line_strings\"]:\n            augm_value = getattr(batch, augm_name)\n            if augm_value is not None:\n                func = functools.partial(\n                    self._augment_keypoints_by_samples,\n                    ks=ks)\n                cbaois = self._apply_to_cbaois_as_keypoints(augm_value, func)\n                setattr(batch, augm_name, cbaois)\n\n        return batch\n\n    @classmethod\n    def _augment_arrays_by_samples(cls, arrs, ks, keep_size, resize_func):\n        # pylint: disable=invalid-name\n        input_was_array = ia.is_np_array(arrs)\n        input_dtype = arrs.dtype if input_was_array else None\n        arrs_aug = []\n        for arr, k_i in zip(arrs, ks):\n            # adding axes here rotates clock-wise instead of ccw\n            arr_aug = np.rot90(arr, k_i, axes=(1, 0))\n\n            do_resize = (\n                keep_size\n                and arr.shape != arr_aug.shape\n                and resize_func is not None)\n            if do_resize:\n                arr_aug = resize_func(arr_aug, arr.shape[0:2])\n            arrs_aug.append(arr_aug)\n        if keep_size and input_was_array:\n            n_shapes = len({arr.shape for arr in arrs_aug})\n            if n_shapes == 1:\n                arrs_aug = np.array(arrs_aug, dtype=input_dtype)\n        return arrs_aug\n\n    # Added in 0.4.0.\n    def _augment_maps_by_samples(self, augmentables, arr_attr_name, ks):\n        # pylint: disable=invalid-name\n        arrs = [getattr(map_i, arr_attr_name) for map_i in augmentables]\n        arrs_aug = self._augment_arrays_by_samples(\n            arrs, ks, self.keep_size, None)\n\n        maps_aug = []\n        gen = zip(augmentables, arrs, arrs_aug, ks)\n        for augmentable_i, arr, arr_aug, k_i in gen:\n            shape_orig = arr.shape\n            setattr(augmentable_i, arr_attr_name, arr_aug)\n            if self.keep_size:\n                augmentable_i = augmentable_i.resize(shape_orig[0:2])\n            elif k_i % 2 == 1:\n                h, w = augmentable_i.shape[0:2]\n                augmentable_i.shape = tuple(\n                    [w, h] + list(augmentable_i.shape[2:]))\n            else:\n                # keep_size was False, but rotated by a multiple of 2,\n                # hence height and width do not change\n                pass\n            maps_aug.append(augmentable_i)\n        return maps_aug\n\n    # Added in 0.4.0.\n    def _augment_keypoints_by_samples(self, keypoints_on_images, ks):\n        # pylint: disable=invalid-name\n        result = []\n        for kpsoi_i, k_i in zip(keypoints_on_images, ks):\n            shape_orig = kpsoi_i.shape\n\n            if (k_i % 4) == 0:\n                result.append(kpsoi_i)\n            else:\n                k_i = int(k_i) % 4  # this is also correct when k_i is negative\n                h, w = kpsoi_i.shape[0:2]\n                h_aug, w_aug = (h, w) if (k_i % 2) == 0 else (w, h)\n\n                for kp in kpsoi_i.keypoints:\n                    y, x = kp.y, kp.x\n                    yr, xr = y, x\n                    wr, hr = w, h\n                    for _ in sm.xrange(k_i):\n                        # for int coordinates this would instead be\n                        #   xr, yr = (hr - 1) - yr, xr\n                        # here we assume that coordinates are always\n                        # subpixel-accurate\n                        xr, yr = hr - yr, xr\n                        wr, hr = hr, wr\n                    kp.x = xr\n                    kp.y = yr\n\n                shape_aug = tuple([h_aug, w_aug] + list(kpsoi_i.shape[2:]))\n                kpsoi_i.shape = shape_aug\n\n                if self.keep_size and (h, w) != (h_aug, w_aug):\n                    kpsoi_i = kpsoi_i.on_(shape_orig)\n                    kpsoi_i.shape = shape_orig\n\n                result.append(kpsoi_i)\n        return result\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.k, self.keep_size]\n\n\n# TODO semipolar\nclass WithPolarWarping(meta.Augmenter):\n    \"\"\"Augmenter that applies other augmenters in a polar-transformed space.\n\n    This augmenter first transforms an image into a polar representation,\n    then applies its child augmenter, then transforms back to cartesian\n    space. The polar representation is still in the image's input dtype\n    (i.e. ``uint8`` stays ``uint8``) and can be visualized. It can be thought\n    of as an \"unrolled\" version of the image, where previously circular lines\n    appear straight. Hence, applying child augmenters in that space can lead\n    to circular effects. E.g. replacing rectangular pixel areas in the polar\n    representation with black pixels will lead to curved black areas in\n    the cartesian result.\n\n    This augmenter can create new pixels in the image. It will fill these\n    with black pixels. For segmentation maps it will fill with class\n    id ``0``. For heatmaps it will fill with ``0.0``.\n\n    This augmenter is limited to arrays with a height and/or width of\n    ``32767`` or less.\n\n    .. warning::\n\n        When augmenting coordinates in polar representation, it is possible\n        that these are shifted outside of the polar image, but are inside the\n        image plane after transforming back to cartesian representation,\n        usually on newly created pixels (i.e. black backgrounds).\n        These coordinates are currently not removed. It is recommended to\n        not use very strong child transformations when also augmenting\n        coordinate-based augmentables.\n\n    .. warning::\n\n        For bounding boxes, this augmenter suffers from the same problem as\n        affine rotations applied to bounding boxes, i.e. the resulting\n        bounding boxes can have unintuitive (seemingly wrong) appearance.\n        This is due to coordinates being \"rotated\" that are inside the\n        bounding box, but do not fall on the object and actually are\n        background.\n        It is recommended to use this augmenter with caution when augmenting\n        bounding boxes.\n\n    .. warning::\n\n        For polygons, this augmenter should not be combined with\n        augmenters that perform automatic polygon recovery for invalid\n        polygons, as the polygons will frequently appear broken in polar\n        representation and their \"fixed\" version will be very broken in\n        cartesian representation. Augmenters that perform such polygon\n        recovery are currently ``PerspectiveTransform``, ``PiecewiseAffine``\n        and ``ElasticTransformation``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: no (1)\n        * ``uint64``: no (2)\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: no (2)\n        * ``float16``: yes; tested (3)\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no (1)\n        * ``bool``: yes; tested (4)\n\n        - (1) OpenCV produces error\n          ``TypeError: Expected cv::UMat for argument 'src'``\n        - (2) OpenCV produces array of nothing but zeros.\n        - (3) Mapepd to ``float32``.\n        - (4) Mapped to ``uint8``.\n\n    Parameters\n    ----------\n    children : imgaug.augmenters.meta.Augmenter or list of imgaug.augmenters.meta.Augmenter or None, optional\n        One or more augmenters to apply to images after they were transformed\n        to polar representation.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.WithPolarWarping(iaa.CropAndPad(percent=(-0.1, 0.1)))\n\n    Apply cropping and padding in polar representation, then warp back to\n    cartesian representation.\n\n    >>> aug = iaa.WithPolarWarping(\n    >>>     iaa.Affine(\n    >>>         translate_percent={\"x\": (-0.2, 0.2), \"y\": (-0.2, 0.2)},\n    >>>         rotate=(-35, 35),\n    >>>         scale=(0.8, 1.2),\n    >>>         shear={\"x\": (-15, 15), \"y\": (-15, 15)}\n    >>>     )\n    >>> )\n\n    Apply affine transformations in polar representation.\n\n    >>> aug = iaa.WithPolarWarping(iaa.AveragePooling((2, 8)))\n\n    Apply average pooling in polar representation. This leads to circular\n    bins.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, children,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(WithPolarWarping, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.children = meta.handle_children_list(children, self.name, \"then\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is not None:\n            iadt.gate_dtypes_strs(\n                batch.images,\n                allowed=\"bool uint8 uint16 int8 int16 int32 float16 float32 \"\n                        \"float64\",\n                disallowed=\"uint32 uint64 int64 float128\",\n                augmenter=self\n            )\n\n        with batch.propagation_hooks_ctx(self, hooks, parents):\n            batch, inv_data_bbs = self._convert_bbs_to_polygons_(batch)\n\n            inv_data = {}\n            for column in batch.columns:\n                func = getattr(self, \"_warp_%s_\" % (column.name,))\n                col_aug, inv_data_col = func(column.value)\n                setattr(batch, column.attr_name, col_aug)\n                inv_data[column.name] = inv_data_col\n\n            batch = self.children.augment_batch_(batch,\n                                                 parents=parents + [self],\n                                                 hooks=hooks)\n            for column in batch.columns:\n                func = getattr(self, \"_invert_warp_%s_\" % (column.name,))\n                col_unaug = func(column.value, inv_data[column.name])\n                setattr(batch, column.attr_name, col_unaug)\n\n            batch = self._invert_convert_bbs_to_polygons_(batch, inv_data_bbs)\n\n        return batch\n\n    # Added in 0.4.0.\n    @classmethod\n    def _convert_bbs_to_polygons_(cls, batch):\n        batch_contained_polygons = batch.polygons is not None\n        if batch.bounding_boxes is None:\n            return batch, (False, batch_contained_polygons)\n\n        psois = [bbsoi.to_polygons_on_image() for bbsoi in batch.bounding_boxes]\n        psois = [psoi.subdivide_(2) for psoi in psois]\n\n        # Mark Polygons that are really Bounding Boxes\n        for psoi in psois:\n            for polygon in psoi.polygons:\n                if polygon.label is None:\n                    polygon.label = \"$$IMGAUG_BB_AS_POLYGON\"\n                else:\n                    polygon.label = polygon.label + \";$$IMGAUG_BB_AS_POLYGON\"\n\n        # Merge Fake-Polygons into existing Polygons\n        if batch.polygons is None:\n            batch.polygons = psois\n        else:\n            for psoi, bbs_as_psoi in zip(batch.polygons, psois):\n                assert psoi.shape == bbs_as_psoi.shape, (\n                    \"Expected polygons and bounding boxes to have the same \"\n                    \".shape value, got %s and %s.\" % (psoi.shape,\n                                                      bbs_as_psoi.shape))\n\n                psoi.polygons.extend(bbs_as_psoi.polygons)\n\n        batch.bounding_boxes = None\n\n        return batch, (True, batch_contained_polygons)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_convert_bbs_to_polygons_(cls, batch, inv_data):\n        batch_contained_bbs, batch_contained_polygons = inv_data\n\n        if not batch_contained_bbs:\n            return batch\n\n        bbsois = []\n        for psoi in batch.polygons:\n            polygons = []\n            bbs = []\n            for polygon in psoi.polygons:\n                is_bb = False\n                if polygon.label is None:\n                    is_bb = False\n                elif polygon.label == \"$$IMGAUG_BB_AS_POLYGON\":\n                    polygon.label = None\n                    is_bb = True\n                elif polygon.label.endswith(\";$$IMGAUG_BB_AS_POLYGON\"):\n                    polygon.label = \\\n                        polygon.label[:-len(\";$$IMGAUG_BB_AS_POLYGON\")]\n                    is_bb = True\n\n                if is_bb:\n                    bbs.append(polygon.to_bounding_box())\n                else:\n                    polygons.append(polygon)\n\n            psoi.polygons = polygons\n            bbsoi = ia.BoundingBoxesOnImage(bbs, shape=psoi.shape)\n            bbsois.append(bbsoi)\n\n        batch.bounding_boxes = bbsois\n\n        if not batch_contained_polygons:\n            batch.polygons = None\n\n        return batch\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_images_(cls, images):\n        return cls._warp_arrays(images, False)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_warp_images_(cls, images_warped, inv_data):\n        return cls._invert_warp_arrays(images_warped, False, inv_data)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_heatmaps_(cls, heatmaps):\n        return cls._warp_maps_(heatmaps, \"arr_0to1\", False)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_warp_heatmaps_(cls, heatmaps_warped, inv_data):\n        return cls._invert_warp_maps_(heatmaps_warped, \"arr_0to1\", False,\n                                      inv_data)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_segmentation_maps_(cls, segmentation_maps):\n        return cls._warp_maps_(segmentation_maps, \"arr\", True)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_warp_segmentation_maps_(cls, segmentation_maps_warped,\n                                        inv_data):\n        return cls._invert_warp_maps_(segmentation_maps_warped, \"arr\", True,\n                                      inv_data)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_keypoints_(cls, kpsois):\n        return cls._warp_cbaois_(kpsois)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_warp_keypoints_(cls, kpsois_warped, image_shapes_orig):\n        return cls._invert_warp_cbaois_(kpsois_warped, image_shapes_orig)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_bounding_boxes_(cls, bbsois):  # pylint: disable=useless-return\n        assert bbsois is None, (\"Expected BBs to have been converted \"\n                                \"to polygons.\")\n        return None\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_warp_bounding_boxes_(cls, bbsois_warped, _image_shapes_orig):  # pylint: disable=useless-return\n        assert bbsois_warped is None, (\"Expected BBs to have been converted \"\n                                       \"to polygons.\")\n        return None\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_polygons_(cls, psois):\n        return cls._warp_cbaois_(psois)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_warp_polygons_(cls, psois_warped, image_shapes_orig):\n        return cls._invert_warp_cbaois_(psois_warped, image_shapes_orig)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_line_strings_(cls, lsois):\n        return cls._warp_cbaois_(lsois)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_warp_line_strings_(cls, lsois_warped, image_shapes_orig):\n        return cls._invert_warp_cbaois_(lsois_warped, image_shapes_orig)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_arrays(cls, arrays, interpolation_nearest):\n        if arrays is None:\n            return None, None\n\n        flags = cv2.WARP_FILL_OUTLIERS + cv2.WARP_POLAR_LINEAR\n        if interpolation_nearest:\n            flags += cv2.INTER_NEAREST\n\n        arrays_warped = []\n        shapes_orig = []\n        for arr in arrays:\n            if 0 in arr.shape:\n                arrays_warped.append(arr)\n                shapes_orig.append(arr.shape)\n                continue\n\n            input_dtype = arr.dtype\n            if input_dtype.kind == \"b\":\n                arr = arr.astype(np.uint8) * 255\n            elif input_dtype == iadt._FLOAT16_DTYPE:\n                arr = arr.astype(np.float32)\n\n            height, width = arr.shape[0:2]\n\n            # remap limitation, see docs for warpPolar()\n            assert height <= 32767 and width <= 32767, (\n                \"WithPolarWarping._warp_arrays() can currently only handle \"\n                \"arrays with axis sizes below 32767, but got shape %s. This \"\n                \"is an OpenCV limitation.\" % (arr.shape,))\n\n            dest_size = (0, 0)\n            center_xy = (width/2, height/2)\n            max_radius = np.sqrt((height/2.0)**2.0 + (width/2.0)**2.0)\n\n            if arr.ndim == 3 and arr.shape[-1] > 512:\n                arr_warped = np.stack(\n                    [cv2.warpPolar(_normalize_cv2_input_arr_(arr[..., c_idx]),\n                                   dest_size, center_xy, max_radius, flags)\n                     for c_idx in np.arange(arr.shape[-1])],\n                    axis=-1)\n            else:\n                arr_warped = cv2.warpPolar(_normalize_cv2_input_arr_(arr),\n                                           dest_size, center_xy, max_radius,\n                                           flags)\n                if arr_warped.ndim == 2 and arr.ndim == 3:\n                    arr_warped = arr_warped[:, :, np.newaxis]\n\n            if input_dtype.kind == \"b\":\n                arr_warped = (arr_warped > 128)\n            elif input_dtype == iadt._FLOAT16_DTYPE:\n                arr_warped = arr_warped.astype(np.float16)\n\n            arrays_warped.append(arr_warped)\n            shapes_orig.append(arr.shape)\n        return arrays_warped, shapes_orig\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_warp_arrays(cls, arrays_warped, interpolation_nearest,\n                            inv_data):\n        shapes_orig = inv_data\n        if arrays_warped is None:\n            return None\n\n        flags = (cv2.WARP_FILL_OUTLIERS + cv2.WARP_POLAR_LINEAR\n                 + cv2.WARP_INVERSE_MAP)\n        if interpolation_nearest:\n            flags += cv2.INTER_NEAREST\n\n        # TODO this does per iteration almost the same as _warp_arrays()\n        #      make DRY\n        arrays_inv = []\n        for arr_warped, shape_orig in zip(arrays_warped, shapes_orig):\n            if 0 in arr_warped.shape:\n                arrays_inv.append(arr_warped)\n                continue\n\n            input_dtype = arr_warped.dtype\n            if input_dtype.kind == \"b\":\n                arr_warped = arr_warped.astype(np.uint8) * 255\n            elif input_dtype == iadt._FLOAT16_DTYPE:\n                arr_warped = arr_warped.astype(np.float32)\n\n            height, width = shape_orig[0:2]\n\n            # remap limitation, see docs for warpPolar()\n            assert (arr_warped.shape[0] <= 32767\n                    and arr_warped.shape[1] <= 32767), (\n                        \"WithPolarWarping._warp_arrays() can currently only \"\n                        \"handle arrays with axis sizes below 32767, but got \"\n                        \"shape %s. This is an OpenCV limitation.\" % (\n                            arr_warped.shape,))\n\n            dest_size = (width, height)\n            center_xy = (width/2, height/2)\n            max_radius = np.sqrt((height/2.0)**2.0 + (width/2.0)**2.0)\n\n            if arr_warped.ndim == 3 and arr_warped.shape[-1] > 512:\n                arr_inv = np.stack(\n                    [cv2.warpPolar(\n                        _normalize_cv2_input_arr_(arr_warped[..., c_idx]),\n                        dest_size, center_xy, max_radius, flags)\n                     for c_idx in np.arange(arr_warped.shape[-1])],\n                    axis=-1)\n            else:\n                arr_inv = cv2.warpPolar(\n                    _normalize_cv2_input_arr_(arr_warped),\n                    dest_size, center_xy, max_radius, flags)\n                if arr_inv.ndim == 2 and arr_warped.ndim == 3:\n                    arr_inv = arr_inv[:, :, np.newaxis]\n\n            if input_dtype.kind == \"b\":\n                arr_inv = (arr_inv > 128)\n            elif input_dtype == iadt._FLOAT16_DTYPE:\n                arr_inv = arr_inv.astype(np.float16)\n\n            arrays_inv.append(arr_inv)\n        return arrays_inv\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_maps_(cls, maps, arr_attr_name, interpolation_nearest):\n        if maps is None:\n            return None, None\n\n        skipped = [False] * len(maps)\n        arrays = []\n        shapes_imgs_orig = []\n        for i, map_i in enumerate(maps):\n            if 0 in map_i.shape:\n                skipped[i] = True\n                arrays.append(np.zeros((0, 0), dtype=np.int32))\n                shapes_imgs_orig.append(map_i.shape)\n            else:\n                arrays.append(getattr(map_i, arr_attr_name))\n                shapes_imgs_orig.append(map_i.shape)\n\n        arrays_warped, warparr_inv_data = cls._warp_arrays(\n            arrays, interpolation_nearest)\n        shapes_imgs_warped = cls._warp_shape_tuples(shapes_imgs_orig)\n\n        for i, map_i in enumerate(maps):\n            if not skipped[i]:\n                map_i.shape = shapes_imgs_warped[i]\n                setattr(map_i, arr_attr_name, arrays_warped[i])\n\n        return maps, (shapes_imgs_orig, warparr_inv_data, skipped)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_warp_maps_(cls, maps_warped, arr_attr_name,\n                           interpolation_nearest, invert_data):\n        if maps_warped is None:\n            return None\n\n        shapes_imgs_orig, warparr_inv_data, skipped = invert_data\n\n        arrays_warped = []\n        for i, map_warped in enumerate(maps_warped):\n            if skipped[i]:\n                arrays_warped.append(np.zeros((0, 0), dtype=np.int32))\n            else:\n                arrays_warped.append(getattr(map_warped, arr_attr_name))\n\n        arrays_inv = cls._invert_warp_arrays(arrays_warped,\n                                             interpolation_nearest,\n                                             warparr_inv_data)\n\n        for i, map_i in enumerate(maps_warped):\n            if not skipped[i]:\n                map_i.shape = shapes_imgs_orig[i]\n                setattr(map_i, arr_attr_name, arrays_inv[i])\n\n        return maps_warped\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_coords(cls, coords, image_shapes):\n        if coords is None:\n            return None, None\n\n        image_shapes_warped = cls._warp_shape_tuples(image_shapes)\n\n        flags = cv2.WARP_POLAR_LINEAR\n\n        coords_warped = []\n        for coords_i, shape, shape_warped in zip(coords, image_shapes,\n                                                 image_shapes_warped):\n            if 0 in shape:\n                coords_warped.append(coords_i)\n                continue\n\n            height, width = shape[0:2]\n            dest_size = (shape_warped[1], shape_warped[0])\n            center_xy = (width/2, height/2)\n            max_radius = np.sqrt((height/2.0)**2.0 + (width/2.0)**2.0)\n\n            coords_i_warped = cls.warpPolarCoords(\n                coords_i, dest_size, center_xy, max_radius, flags)\n\n            coords_warped.append(coords_i_warped)\n        return coords_warped, image_shapes\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_warp_coords(cls, coords_warped, image_shapes_after_aug,\n                            inv_data):\n        image_shapes_orig = inv_data\n        if coords_warped is None:\n            return None\n\n        flags = cv2.WARP_POLAR_LINEAR + cv2.WARP_INVERSE_MAP\n        coords_inv = []\n        gen = enumerate(zip(coords_warped, image_shapes_orig))\n        for i, (coords_i_warped, shape_orig) in gen:\n            if 0 in shape_orig:\n                coords_inv.append(coords_i_warped)\n                continue\n\n            shape_warped = image_shapes_after_aug[i]\n            height, width = shape_orig[0:2]\n            dest_size = (shape_warped[1], shape_warped[0])\n            center_xy = (width/2, height/2)\n            max_radius = np.sqrt((height/2.0)**2.0 + (width/2.0)**2.0)\n\n            coords_i_inv = cls.warpPolarCoords(coords_i_warped,\n                                               dest_size, center_xy,\n                                               max_radius, flags)\n\n            coords_inv.append(coords_i_inv)\n        return coords_inv\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_cbaois_(cls, cbaois):\n        if cbaois is None:\n            return None, None\n\n        coords = [cbaoi.to_xy_array() for cbaoi in cbaois]\n        image_shapes = [cbaoi.shape for cbaoi in cbaois]\n        image_shapes_warped = cls._warp_shape_tuples(image_shapes)\n\n        coords_warped, inv_data = cls._warp_coords(coords, image_shapes)\n        for i, (cbaoi, coords_i_warped) in enumerate(zip(cbaois,\n                                                         coords_warped)):\n            cbaoi = cbaoi.fill_from_xy_array_(coords_i_warped)\n            cbaoi.shape = image_shapes_warped[i]\n            cbaois[i] = cbaoi\n\n        return cbaois, inv_data\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_warp_cbaois_(cls, cbaois_warped, image_shapes_orig):\n        if cbaois_warped is None:\n            return None\n\n        coords = [cbaoi.to_xy_array() for cbaoi in cbaois_warped]\n        image_shapes_after_aug = [cbaoi.shape for cbaoi in cbaois_warped]\n\n        coords_warped = cls._invert_warp_coords(coords, image_shapes_after_aug,\n                                                image_shapes_orig)\n\n        cbaois = cbaois_warped\n        for i, (cbaoi, coords_i_warped) in enumerate(zip(cbaois,\n                                                         coords_warped)):\n            cbaoi = cbaoi.fill_from_xy_array_(coords_i_warped)\n            cbaoi.shape = image_shapes_orig[i]\n            cbaois[i] = cbaoi\n\n        return cbaois\n\n    # Added in 0.4.0.\n    @classmethod\n    def _warp_shape_tuples(cls, shapes):\n        # pylint: disable=invalid-name\n        pi = np.pi\n        result = []\n        for shape in shapes:\n            if 0 in shape:\n                result.append(shape)\n                continue\n\n            height, width = shape[0:2]\n            max_radius = np.sqrt((height/2.0)**2.0 + (width/2.0)**2.0)\n            # np.round() is here a replacement for cvRound(). It is not fully\n            # clear whether the two functions behave exactly identical in all\n            # situations.\n            # See\n            # https://github.com/opencv/opencv/blob/master/\n            # modules/core/include/opencv2/core/fast_math.hpp\n            # for OpenCV's implementation.\n            width = int(np.round(max_radius))\n            height = int(np.round(max_radius * pi))\n            result.append(tuple([height, width] + list(shape[2:])))\n        return result\n\n    # Added in 0.4.0.\n    @classmethod\n    def warpPolarCoords(cls, src, dsize, center, maxRadius, flags):\n        # See\n        # https://docs.opencv.org/3.4.8/da/d54/group__imgproc__transform.html\n        # for the equations\n        # or also\n        # https://github.com/opencv/opencv/blob/master/modules/imgproc/src/\n        # imgwarp.cpp\n        #\n        # pylint: disable=invalid-name, no-else-return\n        assert dsize[0] > 0\n        assert dsize[1] > 0\n\n        dsize_width = dsize[0]\n        dsize_height = dsize[1]\n\n        center_x = center[0]\n        center_y = center[1]\n\n        if np.logical_and(flags, cv2.WARP_INVERSE_MAP):\n            rho = src[:, 0]\n            phi = src[:, 1]\n            Kangle = dsize_height / (2*np.pi)\n            angleRad = phi / Kangle\n            if np.bitwise_and(flags, cv2.WARP_POLAR_LOG):\n                Klog = dsize_width / np.log(maxRadius)\n                magnitude = np.exp(rho / Klog)\n            else:\n                Klin = dsize_width / maxRadius\n                magnitude = rho / Klin\n            x = center_x + magnitude * np.cos(angleRad)\n            y = center_y + magnitude * np.sin(angleRad)\n\n            x = x[:, np.newaxis]\n            y = y[:, np.newaxis]\n\n            return np.concatenate([x, y], axis=1)\n        else:\n            x = src[:, 0]\n            y = src[:, 1]\n\n            Kangle = dsize_height / (2*np.pi)\n            Klin = dsize_width / maxRadius\n\n            I_x, I_y = (x - center_x, y - center_y)\n            magnitude_I, angle_I = cv2.cartToPolar(I_x, I_y)\n            phi = Kangle * angle_I\n            # TODO add semilog support here\n            rho = Klin * magnitude_I\n\n            return np.concatenate([rho, phi], axis=1)\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return []\n\n    # Added in 0.4.0.\n    def get_children_lists(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists`.\"\"\"\n        return [self.children]\n\n    # Added in 0.4.0.\n    def _to_deterministic(self):\n        aug = self.copy()\n        aug.children = aug.children.to_deterministic()\n        aug.deterministic = True\n        aug.random_state = self.random_state.derive_rng_()\n        return aug\n\n    # Added in 0.4.0.\n    def __str__(self):\n        pattern = (\n            \"%s(\"\n            \"name=%s, children=%s, deterministic=%s\"\n            \")\")\n        return pattern % (self.__class__.__name__, self.name,\n                          self.children, self.deterministic)\n\n\nclass Jigsaw(meta.Augmenter):\n    \"\"\"Move cells within images similar to jigsaw patterns.\n\n    .. note::\n\n        This augmenter will by default pad images until their height is a\n        multiple of `nb_rows`. Analogous for `nb_cols`.\n\n    .. note::\n\n        This augmenter will resize heatmaps and segmentation maps to the\n        image size, then apply similar padding as for the corresponding images\n        and resize back to the original map size. That also means that images\n        may change in shape (due to padding), but heatmaps/segmaps will not\n        change. For heatmaps/segmaps, this deviates from pad augmenters that\n        will change images and heatmaps/segmaps in corresponding ways and then\n        keep the heatmaps/segmaps at the new size.\n\n    .. warning::\n\n        This augmenter currently only supports augmentation of images,\n        heatmaps, segmentation maps and keypoints. Other augmentables,\n        i.e. bounding boxes, polygons and line strings, will result in errors.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.geometric.apply_jigsaw`.\n\n    Parameters\n    ----------\n    nb_rows : int or list of int or tuple of int or imgaug.parameters.StochasticParameter, optional\n        How many rows the jigsaw pattern should have.\n\n            * If a single ``int``, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a random value will be uniformly\n              sampled per image from the discrete interval ``[a..b]``.\n            * If a list, then for each image a random value will be sampled\n              from that list.\n            * If ``StochasticParameter``, then that parameter is queried per\n              image to sample the value to use.\n\n    nb_cols : int or list of int or tuple of int or imgaug.parameters.StochasticParameter, optional\n        How many cols the jigsaw pattern should have.\n\n            * If a single ``int``, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a random value will be uniformly\n              sampled per image from the discrete interval ``[a..b]``.\n            * If a list, then for each image a random value will be sampled\n              from that list.\n            * If ``StochasticParameter``, then that parameter is queried per\n              image to sample the value to use.\n\n    max_steps : int or list of int or tuple of int or imgaug.parameters.StochasticParameter, optional\n        How many steps each jigsaw cell may be moved.\n\n            * If a single ``int``, then that value will be used for all images.\n            * If a tuple ``(a, b)``, then a random value will be uniformly\n              sampled per image from the discrete interval ``[a..b]``.\n            * If a list, then for each image a random value will be sampled\n              from that list.\n            * If ``StochasticParameter``, then that parameter is queried per\n              image to sample the value to use.\n\n    allow_pad : bool, optional\n        Whether to allow automatically padding images until they are evenly\n        divisible by ``nb_rows`` and ``nb_cols``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Jigsaw(nb_rows=10, nb_cols=10)\n\n    Create a jigsaw augmenter that splits images into ``10x10`` cells\n    and shifts them around by ``0`` to ``2`` steps (default setting).\n\n    >>> aug = iaa.Jigsaw(nb_rows=(1, 4), nb_cols=(1, 4))\n\n    Create a jigsaw augmenter that splits each image into ``1`` to ``4``\n    cells along each axis.\n\n    >>> aug = iaa.Jigsaw(nb_rows=10, nb_cols=10, max_steps=(1, 5))\n\n    Create a jigsaw augmenter that moves the cells in each image by a random\n    amount between ``1`` and ``5`` times (decided per image). Some images will\n    be barely changed, some will be fairly distorted.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, nb_rows=(3, 10), nb_cols=(3, 10), max_steps=1,\n                 allow_pad=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Jigsaw, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.nb_rows = iap.handle_discrete_param(\n            nb_rows, \"nb_rows\", value_range=(1, None), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=False)\n        self.nb_cols = iap.handle_discrete_param(\n            nb_cols, \"nb_cols\", value_range=(1, None), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=False)\n        self.max_steps = iap.handle_discrete_param(\n            max_steps, \"max_steps\", value_range=(0, None),\n            tuple_to_uniform=True, list_to_choice=True, allow_floats=False)\n        self.allow_pad = allow_pad\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        samples = self._draw_samples(batch, random_state)\n\n        # We resize here heatmaps/segmaps early to the image size in order to\n        # avoid problems where the jigsaw cells don't fit perfectly into\n        # the heatmap/segmap arrays or there are minor padding-related\n        # differences.\n        # TODO This step could most likely be avoided.\n        # TODO add something like\n        #      'with batch.maps_resized_to_image_sizes(): ...'\n        batch, maps_shapes_orig = self._resize_maps(batch)\n\n        if self.allow_pad:\n            # this is a bit more difficult than one might expect, because we\n            # (a) might have different numbers of rows/cols per image\n            # (b) might have different shapes per image\n            # (c) have non-image data that also requires padding\n            # TODO enable support for stochastic parameters in\n            #      PadToMultiplesOf, then we can simple use two\n            #      DeterministicLists here to generate rowwise values\n\n            for i in np.arange(len(samples.destinations)):\n                padder = size_lib.CenterPadToMultiplesOf(\n                    width_multiple=samples.nb_cols[i],\n                    height_multiple=samples.nb_rows[i],\n                    seed=random_state\n                )\n                row = batch.subselect_rows_by_indices([i])\n                row = padder.augment_batch_(row, parents=parents + [self],\n                                            hooks=hooks)\n                batch = batch.invert_subselect_rows_by_indices_([i], row)\n\n        if batch.images is not None:\n            for i, image in enumerate(batch.images):\n                image[...] = apply_jigsaw(image, samples.destinations[i])\n\n        if batch.heatmaps is not None:\n            for i, heatmap in enumerate(batch.heatmaps):\n                heatmap.arr_0to1 = apply_jigsaw(heatmap.arr_0to1,\n                                                samples.destinations[i])\n\n        if batch.segmentation_maps is not None:\n            for i, segmap in enumerate(batch.segmentation_maps):\n                segmap.arr = apply_jigsaw(segmap.arr, samples.destinations[i])\n\n        if batch.keypoints is not None:\n            for i, kpsoi in enumerate(batch.keypoints):\n                xy = kpsoi.to_xy_array()\n                xy[...] = apply_jigsaw_to_coords(xy,\n                                                 samples.destinations[i],\n                                                 image_shape=kpsoi.shape)\n                kpsoi.fill_from_xy_array_(xy)\n\n        has_other_cbaoi = any([getattr(batch, attr_name) is not None\n                               for attr_name\n                               in [\"bounding_boxes\", \"polygons\",\n                                   \"line_strings\"]])\n        if has_other_cbaoi:\n            raise NotImplementedError(\n                \"Jigsaw currently only supports augmentation of images, \"\n                \"heatmaps, segmentation maps and keypoints. \"\n                \"Explicitly not supported are: bounding boxes, polygons \"\n                \"and line strings.\")\n\n        # We don't crop back to the original size, partly because it is\n        # rather cumbersome to implement, partly because the padded\n        # borders might have been moved into the inner parts of the image\n\n        batch = self._invert_resize_maps(batch, maps_shapes_orig)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        nb_images = batch.nb_rows\n        nb_rows = self.nb_rows.draw_samples((nb_images,),\n                                            random_state=random_state)\n        nb_cols = self.nb_cols.draw_samples((nb_images,),\n                                            random_state=random_state)\n        max_steps = self.max_steps.draw_samples((nb_images,),\n                                                random_state=random_state)\n        destinations = []\n        for i in np.arange(nb_images):\n            destinations.append(\n                generate_jigsaw_destinations(\n                    nb_rows[i], nb_cols[i], max_steps[i],\n                    seed=random_state)\n            )\n\n        samples = _JigsawSamples(nb_rows, nb_cols, max_steps, destinations)\n        return samples\n\n    # Added in 0.4.0.\n    @classmethod\n    def _resize_maps(cls, batch):\n        # skip computation of rowwise shapes\n        if batch.heatmaps is None and batch.segmentation_maps is None:\n            return batch, (None, None)\n\n        image_shapes = batch.get_rowwise_shapes()\n        batch.heatmaps, heatmaps_shapes_orig = cls._resize_maps_single_list(\n            batch.heatmaps, \"arr_0to1\", image_shapes)\n        batch.segmentation_maps, sm_shapes_orig = cls._resize_maps_single_list(\n            batch.segmentation_maps, \"arr\", image_shapes)\n\n        return batch, (heatmaps_shapes_orig, sm_shapes_orig)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _resize_maps_single_list(cls, augmentables, arr_attr_name,\n                                 image_shapes):\n        if augmentables is None:\n            return None, None\n\n        shapes_orig = []\n        augms_resized = []\n        for augmentable, image_shape in zip(augmentables, image_shapes):\n            shape_orig = getattr(augmentable, arr_attr_name).shape\n            augm_rs = augmentable.resize(image_shape[0:2])\n            augms_resized.append(augm_rs)\n            shapes_orig.append(shape_orig)\n        return augms_resized, shapes_orig\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_resize_maps(cls, batch, shapes_orig):\n        batch.heatmaps = cls._invert_resize_maps_single_list(\n            batch.heatmaps, shapes_orig[0])\n        batch.segmentation_maps = cls._invert_resize_maps_single_list(\n            batch.segmentation_maps, shapes_orig[1])\n\n        return batch\n\n    # Added in 0.4.0.\n    @classmethod\n    def _invert_resize_maps_single_list(cls, augmentables, shapes_orig):\n        if shapes_orig is None:\n            return None\n\n        augms_resized = []\n        for augmentable, shape_orig in zip(augmentables, shapes_orig):\n            augms_resized.append(augmentable.resize(shape_orig[0:2]))\n        return augms_resized\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        return [self.nb_rows, self.nb_cols, self.max_steps, self.allow_pad]\n\n\n# Added in 0.4.0.\nclass _JigsawSamples(object):\n    # Added in 0.4.0.\n    def __init__(self, nb_rows, nb_cols, max_steps, destinations):\n        self.nb_rows = nb_rows\n        self.nb_cols = nb_cols\n        self.max_steps = max_steps\n        self.destinations = destinations\n"
  },
  {
    "path": "imgaug/augmenters/imgcorruptlike.py",
    "content": "\"\"\"\nAugmenters that wrap methods from ``imagecorruptions`` package.\n\nSee `https://github.com/bethgelab/imagecorruptions`_ for the package.\n\nThe package is derived from `https://github.com/hendrycks/robustness`_.\nThe corresponding `paper <https://arxiv.org/abs/1807.01697>`_ is::\n\n    Hendrycks, Dan and Dietterich, Thomas G.\n    Benchmarking Neural Network Robustness to Common Corruptions and\n    Surface Variations\n\nwith the `newer version <https://arxiv.org/abs/1903.12261>`_ being::\n\n    Hendrycks, Dan and Dietterich, Thomas G.\n    Benchmarking Neural Network Robustness to Common Corruptions and\n    Perturbations\n\nList of augmenters:\n\n    * :class:`GaussianNoise`\n    * :class:`ShotNoise`\n    * :class:`ImpulseNoise`\n    * :class:`SpeckleNoise`\n    * :class:`GaussianBlur`\n    * :class:`GlassBlur`\n    * :class:`DefocusBlur`\n    * :class:`MotionBlur`\n    * :class:`ZoomBlur`\n    * :class:`Fog`\n    * :class:`Frost`\n    * :class:`Snow`\n    * :class:`Spatter`\n    * :class:`Contrast`\n    * :class:`Brightness`\n    * :class:`Saturate`\n    * :class:`JpegCompression`\n    * :class:`Pixelate`\n    * :class:`ElasticTransform`\n\n.. note::\n\n    The functions provided here have identical outputs to the ones in\n    ``imagecorruptions`` when called using the ``corrupt()`` function of\n    that package. E.g. the outputs are always ``uint8`` and not\n    ``float32`` or ``float64``.\n\nExample usage::\n\n    >>> # Skip the doctests in this file as the imagecorruptions package is\n    >>> # not available in all python versions that are otherwise supported\n    >>> # by imgaug.\n    >>> # doctest: +SKIP\n    >>> import imgaug as ia\n    >>> import imgaug.augmenters as iaa\n    >>> import numpy as np\n    >>> image = np.zeros((64, 64, 3), dtype=np.uint8)\n    >>> names, funcs = iaa.imgcorruptlike.get_corruption_names(\"validation\")\n    >>> for name, func in zip(names, funcs):\n    >>>     image_aug = func(image, severity=5, seed=1)\n    >>>     image_aug = ia.draw_text(image_aug, x=20, y=20, text=name)\n    >>>     ia.imshow(image_aug)\n\n    Use e.g. ``iaa.imgcorruptlike.GaussianNoise(severity=2)(images=...)`` to\n    create and apply a specific augmenter.\n\nAdded in 0.4.0.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport warnings\n\nimport six.moves as sm\nimport numpy as np\nimport skimage.filters\n\nimport imgaug as ia\nfrom ..imgaug import _numbajit\nfrom .. import dtypes as iadt\nfrom .. import random as iarandom\nfrom .. import parameters as iap\nfrom . import meta\n\n# TODO add optional dependency\n\n_MISSING_PACKAGE_ERROR_MSG = (\n    \"Could not import package `imagecorruptions`. This is an optional \"\n    \"dependency of imgaug and must be installed manually in order \"\n    \"to use augmenters from `imgaug.augmenters.imgcorrupt`. \"\n    \"Use e.g. `pip install imagecorruptions` to install it. See also \"\n    \"https://github.com/bethgelab/imagecorruptions for the repository \"\n    \"of the package.\"\n)\n\n\n# Added in 0.4.0.\ndef _clipped_zoom_no_scipy_warning(img, zoom_factor):\n    from scipy.ndimage import zoom as scizoom\n\n    with warnings.catch_warnings():\n        warnings.filterwarnings(\"ignore\", \".*output shape of zoom.*\")\n\n        # clipping along the width dimension:\n        ch0 = int(np.ceil(img.shape[0] / float(zoom_factor)))\n        top0 = (img.shape[0] - ch0) // 2\n\n        # clipping along the height dimension:\n        ch1 = int(np.ceil(img.shape[1] / float(zoom_factor)))\n        top1 = (img.shape[1] - ch1) // 2\n\n        img = scizoom(img[top0:top0 + ch0, top1:top1 + ch1],\n                      (zoom_factor, zoom_factor, 1), order=1)\n\n        return img\n\n\ndef _call_imgcorrupt_func(fname, seed, convert_to_pil, *args, **kwargs):\n    \"\"\"Apply an ``imagecorruptions`` function.\n\n    The dtype support below is basically a placeholder to which the\n    augmentation functions can point to decrease the amount of documentation.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; indirectly tested (1)\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n        - (1) Tested by comparison with function in ``imagecorruptions``\n              package.\n\n    \"\"\"\n    # import imagecorruptions, note that it is an optional dependency\n    try:\n        # imagecorruptions sets its own warnings filter rule via\n        # warnings.simplefilter(). That rule is the in effect for the whole\n        # program and not just the module. So to prevent that here\n        # we use catch_warnings(), which uintuitively does not by default\n        # catch warnings but saves and restores the warnings filter settings.\n        with warnings.catch_warnings():\n            import imagecorruptions.corruptions as corruptions\n    except ImportError:\n        raise ImportError(_MISSING_PACKAGE_ERROR_MSG)\n\n    # Monkeypatch clip_zoom() as that causes warnings in some scipy versions,\n    # and the implementation here suppresses these warnings. They suppress\n    # all UserWarnings on a module level instead, which seems very exhaustive.\n    corruptions.clipped_zoom = _clipped_zoom_no_scipy_warning\n\n    image = args[0]\n\n    iadt.allow_only_uint8({image.dtype})\n\n    input_shape = image.shape\n\n    height, width = input_shape[0:2]\n    assert height >= 32 and width >= 32, (\n        \"Expected the provided image to have a width and height of at least \"\n        \"32 pixels, as that is the lower limit that the wrapped \"\n        \"imagecorruptions functions use. Got shape %s.\" % (image.shape,))\n\n    ndim = image.ndim\n    assert ndim == 2 or (ndim == 3 and (image.shape[2] in [1, 3])), (\n        \"Expected input image to have shape (height, width) or \"\n        \"(height, width, 1) or (height, width, 3). Got shape %s.\" % (\n            image.shape,))\n\n    if ndim == 2:\n        image = image[..., np.newaxis]\n    if image.shape[-1] == 1:\n        image = np.tile(image, (1, 1, 3))\n\n    if convert_to_pil:\n        import PIL.Image\n        image = PIL.Image.fromarray(image)\n\n    with iarandom.temporary_numpy_seed(seed):\n        if ia.is_callable(fname):\n            image_aug = fname(image, *args[1:], **kwargs)\n        else:\n            image_aug = getattr(corruptions, fname)(image, *args[1:], **kwargs)\n\n    if convert_to_pil:\n        image_aug = np.asarray(image_aug)\n\n    if ndim == 2:\n        image_aug = image_aug[:, :, 0]\n    elif input_shape[-1] == 1:\n        image_aug = image_aug[:, :, 0:1]\n\n    # this cast is done at the end of imagecorruptions.__init__.corrupt()\n    image_aug = np.uint8(image_aug)\n\n    return image_aug\n\n\ndef get_corruption_names(subset=\"common\"):\n    \"\"\"Get a named subset of image corruption functions.\n\n    .. note::\n\n        This function returns the augmentation names (as strings) *and* the\n        corresponding augmentation functions, while ``get_corruption_names()``\n        in ``imagecorruptions`` only returns the augmentation names.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    subset : {'common', 'validation', 'all'}, optional.\n        Name of the subset of image corruption functions.\n\n    Returns\n    -------\n    list of str\n        Names of the corruption methods, e.g. \"gaussian_noise\".\n\n    list of callable\n        Function corresponding to the name. Is one of the\n        ``apply_*()`` functions in this module. Apply e.g.\n        via ``func(image, severity=2, seed=123)``.\n\n    \"\"\"\n    # import imagecorruptions, note that it is an optional dependency\n    try:\n        # imagecorruptions sets its own warnings filter rule via\n        # warnings.simplefilter(). That rule is the in effect for the whole\n        # program and not just the module. So to prevent that here\n        # we use catch_warnings(), which uintuitively does not by default\n        # catch warnings but saves and restores the warnings filter settings.\n        with warnings.catch_warnings():\n            import imagecorruptions\n    except ImportError:\n        raise ImportError(_MISSING_PACKAGE_ERROR_MSG)\n\n    cnames = imagecorruptions.get_corruption_names(subset)\n    funcs = [globals()[\"apply_%s\" % (cname,)] for cname in cnames]\n\n    return cnames, funcs\n\n\n# ----------------------------------------------------------------------------\n# Corruption functions\n# ----------------------------------------------------------------------------\n# These functions could easily be created dynamically, especially templating\n# the docstrings would save many lines of code. It is intentionally not done\n# here for the same reasons as in case of the augmenters. See the comment\n# further below at the start of the augmenter section for details.\n\ndef apply_gaussian_noise(x, severity=1, seed=None):\n    \"\"\"Apply ``gaussian_noise`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"gaussian_noise\", seed, False, x, severity)\n\n\ndef apply_shot_noise(x, severity=1, seed=None):\n    \"\"\"Apply ``shot_noise`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"shot_noise\", seed, False, x, severity)\n\n\ndef apply_impulse_noise(x, severity=1, seed=None):\n    \"\"\"Apply ``impulse_noise`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"impulse_noise\", seed, False, x, severity)\n\n\ndef apply_speckle_noise(x, severity=1, seed=None):\n    \"\"\"Apply ``speckle_noise`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"speckle_noise\", seed, False, x, severity)\n\n\ndef apply_gaussian_blur(x, severity=1, seed=None):\n    \"\"\"Apply ``gaussian_blur`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"gaussian_blur\", seed, False, x, severity)\n\n\ndef apply_glass_blur(x, severity=1, seed=None):\n    \"\"\"Apply ``glass_blur`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(_apply_glass_blur_imgaug, seed, False, x,\n                                 severity)\n\n\n# Added in 0.4.0.\ndef _apply_glass_blur_imgaug(x, severity=1):\n    # false positive on x_shape[0]\n    # invalid name for dx, dy\n    # pylint: disable=unsubscriptable-object, invalid-name\n\n    # original function implementation from\n    # https://github.com/bethgelab/imagecorruptions/blob/master/imagecorruptions/corruptions.py\n    # this is an improved (i.e. faster) version\n    # sigma, max_delta, iterations\n    c = [\n        (0.7, 1, 2),\n        (0.9, 2, 1),\n        (1, 2, 3),\n        (1.1, 3, 2),\n        (1.5, 4, 2)\n    ][severity - 1]\n\n    sigma, max_delta, iterations = c\n\n    x = (\n        skimage.filters.gaussian(\n            np.array(x) / 255., sigma=sigma, multichannel=True\n        ) * 255\n    ).astype(np.uint)\n    x_shape = x.shape\n\n    dxxdyy = np.random.randint(\n        -max_delta,\n        max_delta,\n        size=(\n            iterations,\n            x_shape[0] - 2*max_delta,\n            x_shape[1] - 2*max_delta,\n            2\n        )\n    )\n\n    x = _apply_glass_blur_imgaug_loop(\n        x, iterations, max_delta, dxxdyy\n    )\n\n    return np.clip(\n        skimage.filters.gaussian(x / 255., sigma=sigma, multichannel=True),\n        0, 1\n    ) * 255\n\n\n# Added in 0.5.0.\n@_numbajit(nopython=True, nogil=True, cache=True)\ndef _apply_glass_blur_imgaug_loop(\n        x, iterations, max_delta, dxxdyy\n):\n    x_shape = x.shape\n    nb_height = x_shape[0] - 2 * max_delta\n    nb_width = x_shape[1] - 2 * max_delta\n\n    # locally shuffle pixels\n    for i in sm.xrange(iterations):\n        for j in sm.xrange(nb_height):\n            for k in sm.xrange(nb_width):\n                h = x_shape[0] - max_delta - j\n                w = x_shape[1] - max_delta - k\n                dx, dy = dxxdyy[i, j, k]\n                h_prime, w_prime = h + dy, w + dx\n                # swap\n                x[h, w], x[h_prime, w_prime] = x[h_prime, w_prime], x[h, w]\n\n    return x\n\n\ndef apply_defocus_blur(x, severity=1, seed=None):\n    \"\"\"Apply ``defocus_blur`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"defocus_blur\", seed, False, x, severity)\n\n\ndef apply_motion_blur(x, severity=1, seed=None):\n    \"\"\"Apply ``motion_blur`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"motion_blur\", seed, False, x, severity)\n\n\ndef apply_zoom_blur(x, severity=1, seed=None):\n    \"\"\"Apply ``zoom_blur`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"zoom_blur\", seed, False, x, severity)\n\n\ndef apply_fog(x, severity=1, seed=None):\n    \"\"\"Apply ``fog`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"fog\", seed, False, x, severity)\n\n\ndef apply_frost(x, severity=1, seed=None):\n    \"\"\"Apply ``frost`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"frost\", seed, False, x, severity)\n\n\ndef apply_snow(x, severity=1, seed=None):\n    \"\"\"Apply ``snow`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"snow\", seed, False, x, severity)\n\n\ndef apply_spatter(x, severity=1, seed=None):\n    \"\"\"Apply ``spatter`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"spatter\", seed, True, x, severity)\n\n\ndef apply_contrast(x, severity=1, seed=None):\n    \"\"\"Apply ``contrast`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"contrast\", seed, False, x, severity)\n\n\ndef apply_brightness(x, severity=1, seed=None):\n    \"\"\"Apply ``brightness`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"brightness\", seed, False, x, severity)\n\n\ndef apply_saturate(x, severity=1, seed=None):\n    \"\"\"Apply ``saturate`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"saturate\", seed, False, x, severity)\n\n\ndef apply_jpeg_compression(x, severity=1, seed=None):\n    \"\"\"Apply ``jpeg_compression`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"jpeg_compression\", seed, True, x, severity)\n\n\ndef apply_pixelate(x, severity=1, seed=None):\n    \"\"\"Apply ``pixelate`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    x : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"pixelate\", seed, True, x, severity)\n\n\ndef apply_elastic_transform(image, severity=1, seed=None):\n    \"\"\"Apply ``elastic_transform`` from ``imagecorruptions``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike._call_imgcorrupt_func`.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array.\n        Expected to have shape ``(H,W)``, ``(H,W,1)`` or ``(H,W,3)`` with\n        dtype ``uint8`` and a minimum height/width of ``32``.\n\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int, optional\n        Seed for the random number generation to use.\n\n    Returns\n    -------\n    ndarray\n        Corrupted image.\n\n    \"\"\"\n    return _call_imgcorrupt_func(\"elastic_transform\", seed, False, image,\n                                 severity)\n\n\n# ----------------------------------------------------------------------------\n# Augmenters\n# ----------------------------------------------------------------------------\n# The augmenter definitions below are almost identical and mainly differ in\n# the names and functions used. It would be fairly trivial to write a\n# function that would create these augmenters dynamically (and one is listed\n# below as a comment). The downside is that in these cases the documentation\n# would also be generated dynamically, which leads to numerous problems:\n# (1) users couldn't easily read the documentation while scrolling through\n# the code file, (2) IDEs might not be able to use it for code suggestions,\n# (3) tools like pylint can't detect and validate it, (4) the imgaug-doc\n# tools to parse dtype support don't work with dynamically generated\n# documentation (and neither with dynamically generated classes).\n# Even though it's by far more code, it seems like the better choice overall\n# to just write it out.\n\n# Example function to dynamically generate augmenters, kept for possible\n# future uses:\n# def _create_augmenter(class_name, func_name):\n#     func = globals()[\"apply_%s\" % (func_name,)]\n#\n#     def __init__(self, severity=1, name=None, deterministic=False,\n#                  random_state=None):\n#         super(self.__class__, self).__init__(\n#             func, severity, name=name, deterministic=deterministic,\n#             random_state=random_state)\n#\n#     augmenter_class = type(class_name,\n#                            (_ImgcorruptAugmenterBase,),\n#                            {\"__init__\": __init__})\n#\n#     augmenter_class.__doc__ = \"\"\"\n#     Wrapper around ``imagecorruptions.corruptions.%s``.\n#\n#     **Supported dtypes**:\n#\n#     See :func:`~imgaug.augmenters.imgcorruptlike.apply_%s`.\n#\n#     Parameters\n#     ----------\n#     severity : int, optional\n#         Strength of the corruption, with valid values being\n#         ``1 <= severity <= 5``.\n#\n#     name : None or str, optional\n#         See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n#\n#     deterministic : bool, optional\n#         See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n#\n#     random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n#         See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n#\n#     Examples\n#     --------\n#     >>> import imgaug.augmenters as iaa\n#     >>> aug = iaa.%s(severity=2)\n#\n#     Create an augmenter around ``imagecorruptions.corruptions.%s``. Apply it to\n#     images using e.g. ``aug(images=[image1, image2, ...])``.\n#\n#     \"\"\" % (func_name, func_name, class_name, func_name)\n#\n#     return augmenter_class\n\n\n# Added in 0.4.0.\nclass _ImgcorruptAugmenterBase(meta.Augmenter):\n    def __init__(self, func, severity=1,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(_ImgcorruptAugmenterBase, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.func = func\n        self.severity = iap.handle_discrete_param(\n            severity, \"severity\", value_range=(1, 5), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=False)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        severities, seeds = self._draw_samples(len(batch.images),\n                                               random_state=random_state)\n\n        for image, severity, seed in zip(batch.images, severities, seeds):\n            image[...] = self.func(image, severity=severity, seed=seed)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _draw_samples(self, nb_rows, random_state):\n        severities = self.severity.draw_samples((nb_rows,),\n                                                random_state=random_state)\n        seeds = random_state.generate_seeds_(nb_rows)\n\n        return severities, seeds\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.severity]\n\n\nclass GaussianNoise(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.gaussian_noise``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_gaussian_noise`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.GaussianNoise(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.gaussian_noise``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(GaussianNoise, self).__init__(\n            apply_gaussian_noise, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass ShotNoise(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.shot_noise``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_shot_noise`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.ShotNoise(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.shot_noise``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ShotNoise, self).__init__(\n            apply_shot_noise, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass ImpulseNoise(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.impulse_noise``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_impulse_noise`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.ImpulseNoise(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.impulse_noise``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ImpulseNoise, self).__init__(\n            apply_impulse_noise, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass SpeckleNoise(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.speckle_noise``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_speckle_noise`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.SpeckleNoise(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.speckle_noise``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(SpeckleNoise, self).__init__(\n            apply_speckle_noise, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass GaussianBlur(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.gaussian_blur``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_gaussian_blur`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.GaussianBlur(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.gaussian_blur``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(GaussianBlur, self).__init__(\n            apply_gaussian_blur, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass GlassBlur(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.glass_blur``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_glass_blur`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.GlassBlur(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.glass_blur``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(GlassBlur, self).__init__(\n            apply_glass_blur, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass DefocusBlur(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.defocus_blur``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_defocus_blur`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.DefocusBlur(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.defocus_blur``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(DefocusBlur, self).__init__(\n            apply_defocus_blur, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass MotionBlur(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.motion_blur``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_motion_blur`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.MotionBlur(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.motion_blur``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(MotionBlur, self).__init__(\n            apply_motion_blur, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass ZoomBlur(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.zoom_blur``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_zoom_blur`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.ZoomBlur(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.zoom_blur``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ZoomBlur, self).__init__(\n            apply_zoom_blur, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Fog(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.fog``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_fog`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.Fog(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.fog``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Fog, self).__init__(\n            apply_fog, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Frost(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.frost``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_frost`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.Frost(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.frost``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Frost, self).__init__(\n            apply_frost, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Snow(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.snow``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_snow`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.Snow(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.snow``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Snow, self).__init__(\n            apply_snow, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Spatter(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.spatter``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_spatter`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.Spatter(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.spatter``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Spatter, self).__init__(\n            apply_spatter, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Contrast(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.contrast``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_contrast`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.Contrast(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.contrast``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Contrast, self).__init__(\n            apply_contrast, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Brightness(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.brightness``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_brightness`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.Brightness(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.brightness``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Brightness, self).__init__(\n            apply_brightness, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Saturate(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.saturate``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_saturate`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.Saturate(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.saturate``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Saturate, self).__init__(\n            apply_saturate, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass JpegCompression(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.jpeg_compression``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_jpeg_compression`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.JpegCompression(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.jpeg_compression``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(JpegCompression, self).__init__(\n            apply_jpeg_compression, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Pixelate(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.pixelate``.\n\n    .. note::\n\n        This augmenter only affects images. Other data is not changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_pixelate`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.Pixelate(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.pixelate``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Pixelate, self).__init__(\n            apply_pixelate, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass ElasticTransform(_ImgcorruptAugmenterBase):\n    \"\"\"\n    Wrapper around ``imagecorruptions.corruptions.elastic_transform``.\n\n    .. warning::\n\n        This augmenter can currently only transform image-data.\n        Batches containing heatmaps, segmentation maps and\n        coordinate-based augmentables will be rejected with an error.\n        Use :class:`~imgaug.augmenters.geometric.ElasticTransformation` if\n        you have to transform such inputs.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.imgcorruptlike.apply_elastic_transform`.\n\n    Parameters\n    ----------\n    severity : int, optional\n        Strength of the corruption, with valid values being\n        ``1 <= severity <= 5``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> # doctest: +SKIP\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.imgcorruptlike.ElasticTransform(severity=2)\n\n    Create an augmenter around\n    ``imagecorruptions.corruptions.elastic_transform``.\n    Apply it to images using e.g. ``aug(images=[image1, image2, ...])``.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, severity=(1, 5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ElasticTransform, self).__init__(\n            apply_elastic_transform, severity,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        cols = batch.get_column_names()\n        assert len(cols) == 0 or (len(cols) == 1 and \"images\" in cols), (\n            \"imgcorruptlike.ElasticTransform can currently only process image \"\n            \"data. Got a batch containing: %s. Use \"\n            \"imgaug.augmenters.geometric.ElasticTransformation for \"\n            \"batches containing non-image data.\" % (\", \".join(cols),))\n        return super(ElasticTransform, self)._augment_batch_(\n            batch, random_state, parents, hooks)\n"
  },
  {
    "path": "imgaug/augmenters/meta.py",
    "content": "\"\"\"\nAugmenters that don't apply augmentations themselves, but are needed\nfor meta usage.\n\nList of augmenters:\n\n    * :class:`Augmenter` (base class for all augmenters)\n    * :class:`Sequential`\n    * :class:`SomeOf`\n    * :class:`OneOf`\n    * :class:`Sometimes`\n    * :class:`WithChannels`\n    * :class:`Identity`\n    * :class:`Noop`\n    * :class:`Lambda`\n    * :class:`AssertLambda`\n    * :class:`AssertShape`\n    * :class:`ChannelShuffle`\n\nNote: :class:`~imgaug.augmenters.color.WithColorspace` is in ``color.py``.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nfrom abc import ABCMeta, abstractmethod\nimport copy as copy_module\nimport re\nimport itertools\nimport functools\nimport sys\n\nimport numpy as np\nimport six\nimport six.moves as sm\n\nimport imgaug as ia\nfrom imgaug.augmentables.batches import (Batch, UnnormalizedBatch,\n                                         _BatchInAugmentation)\nfrom .. import parameters as iap\nfrom .. import random as iarandom\nfrom . import base as iabase\n\n\n@ia.deprecated(\"imgaug.dtypes.clip_\")\ndef clip_augmented_image_(image, min_value, max_value):\n    \"\"\"Clip image in-place.\"\"\"\n    return clip_augmented_images_(image, min_value, max_value)\n\n\n@ia.deprecated(\"imgaug.dtypes.clip_\")\ndef clip_augmented_image(image, min_value, max_value):\n    \"\"\"Clip image.\"\"\"\n    return clip_augmented_images(image, min_value, max_value)\n\n\n@ia.deprecated(\"imgaug.dtypes.clip_\")\ndef clip_augmented_images_(images, min_value, max_value):\n    \"\"\"Clip images in-place.\"\"\"\n    if ia.is_np_array(images):\n        return np.clip(images, min_value, max_value, out=images)\n    return [np.clip(image, min_value, max_value, out=image)\n            for image in images]\n\n\n@ia.deprecated(\"imgaug.dtypes.clip_\")\ndef clip_augmented_images(images, min_value, max_value):\n    \"\"\"Clip images.\"\"\"\n    if ia.is_np_array(images):\n        images = np.copy(images)\n    else:\n        images = [np.copy(image) for image in images]\n    return clip_augmented_images_(images, min_value, max_value)\n\n\ndef handle_children_list(lst, augmenter_name, lst_name, default=\"sequential\"):\n    \"\"\"Normalize an augmenter list provided by a user.\"\"\"\n    if lst is None:\n        if default == \"sequential\":\n            return Sequential([], name=\"%s-%s\" % (augmenter_name, lst_name))\n        return default\n    if isinstance(lst, Augmenter):\n        if ia.is_iterable(lst):\n            # TODO why was this assert added here? seems to make no sense\n            only_augmenters = all([isinstance(child, Augmenter)\n                                   for child in lst])\n            assert only_augmenters, (\n                \"Expected all children to be augmenters, got types %s.\" % (\n                    \", \".join([str(type(v)) for v in lst])))\n            return lst\n        return Sequential(lst, name=\"%s-%s\" % (augmenter_name, lst_name))\n    if ia.is_iterable(lst):\n        if len(lst) == 0 and default != \"sequential\":\n            return default\n        only_augmenters = all([isinstance(child, Augmenter)\n                               for child in lst])\n        assert only_augmenters, (\n            \"Expected all children to be augmenters, got types %s.\" % (\n                \", \".join([str(type(v)) for v in lst])))\n        return Sequential(lst, name=\"%s-%s\" % (augmenter_name, lst_name))\n    raise Exception(\n        \"Expected None, Augmenter or list/tuple as children list %s \"\n        \"for augmenter with name %s, got %s.\" % (\n            lst_name, augmenter_name, type(lst),))\n\n\ndef reduce_to_nonempty(objs):\n    \"\"\"Remove from a list all objects that don't follow ``obj.empty==True``.\"\"\"\n    objs_reduced = []\n    ids = []\n    for i, obj in enumerate(objs):\n        assert hasattr(obj, \"empty\"), (\n            \"Expected object with property 'empty'. Got type %s.\" % (\n                type(obj),))\n        if not obj.empty:\n            objs_reduced.append(obj)\n            ids.append(i)\n    return objs_reduced, ids\n\n\ndef invert_reduce_to_nonempty(objs, ids, objs_reduced):\n    \"\"\"Inverse of :func:`reduce_to_nonempty`.\"\"\"\n    objs_inv = list(objs)\n    for idx, obj_from_reduced in zip(ids, objs_reduced):\n        objs_inv[idx] = obj_from_reduced\n    return objs_inv\n\n\ndef estimate_max_number_of_channels(images):\n    \"\"\"Compute the maximum number of image channels among a list of images.\"\"\"\n    if ia.is_np_array(images):\n        assert images.ndim == 4, (\n            \"Expected 'images' to be 4-dimensional if provided as array. \"\n            \"Got %d dimensions.\" % (images.ndim,))\n        return images.shape[3]\n\n    assert ia.is_iterable(images), (\n        \"Expected 'images' to be an array or iterable, got %s.\" % (\n            type(images),))\n    if len(images) == 0:\n        return None\n    channels = [el.shape[2] if len(el.shape) >= 3 else 1 for el in images]\n    return max(channels)\n\n\ndef copy_arrays(arrays):\n    \"\"\"Copy the arrays of a single input array or list of input arrays.\"\"\"\n    if ia.is_np_array(arrays):\n        return np.copy(arrays)\n    return [np.copy(array) for array in arrays]\n\n\ndef _add_channel_axis(arrs):\n    if ia.is_np_array(arrs):\n        if arrs.ndim == 3:  # (N,H,W)\n            return arrs[..., np.newaxis]  # (N,H,W) -> (N,H,W,1)\n        return arrs\n    return [\n        arr[..., np.newaxis]  # (H,W) -> (H,W,1)\n        if arr.ndim == 2\n        else arr\n        for arr in arrs\n    ]\n\n\ndef _remove_added_channel_axis(arrs_added, arrs_orig):\n    if ia.is_np_array(arrs_orig):\n        if arrs_orig.ndim == 3:  # (N,H,W)\n            if ia.is_np_array(arrs_added):\n                return arrs_added[..., 0]  # (N,H,W,1) -> (N,H,W)\n            # (N,H,W) -> (N,H,W,1) -> <augmentation> -> list of (H,W,1)\n            return [arr[..., 0] for arr in arrs_added]\n        return arrs_added\n    return [\n        arr_added[..., 0]\n        if arr_orig.ndim == 2\n        else arr_added   # (H,W,1) -> (H,W)\n        for arr_added, arr_orig\n        in zip(arrs_added, arrs_orig)\n    ]\n\n\nclass _maybe_deterministic_ctx(object):  # pylint: disable=invalid-name\n    \"\"\"Context that resets an RNG to its initial state upon exit.\n\n    This allows to execute some sampling functions and leave the code block\n    with the used RNG in the same state as before.\n\n    Parameters\n    ----------\n    random_state : imgaug.random.RNG or imgaug.augmenters.meta.Augmenter\n        The RNG to reset. If this is an augmenter, then the augmenter's\n        RNG will be used.\n\n    deterministic : None or bool\n        Whether to reset the RNG upon exit (``True``) or not (``False``).\n        Allowed to be ``None`` iff `random_state` was an augmenter, in which\n        case that augmenter's ``deterministic`` attribute will be used.\n\n    \"\"\"\n\n    def __init__(self, random_state, deterministic=None):\n        if deterministic is None:\n            augmenter = random_state\n            self.random_state = augmenter.random_state\n            self.deterministic = augmenter.deterministic\n        else:\n            assert deterministic is not None, (\n                \"Expected boolean as `deterministic`, got None.\")\n            self.random_state = random_state\n            self.deterministic = deterministic\n        self.old_state = None\n\n    def __enter__(self):\n        if self.deterministic:\n            self.old_state = self.random_state.state\n\n    def __exit__(self, exception_type, exception_value, exception_traceback):\n        if self.old_state is not None:\n            self.random_state.state = self.old_state\n\n\n@six.add_metaclass(ABCMeta)\nclass Augmenter(object):\n    \"\"\"\n    Base class for Augmenter objects.\n    All augmenters derive from this class.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Seed to use for this augmenter's random number generator (RNG) or\n        alternatively an RNG itself. Setting this parameter allows to\n        control/influence the random number sampling of this specific\n        augmenter without affecting other augmenters. Usually, there is no\n        need to set this parameter.\n\n            * If ``None``: The global RNG is used (shared by all\n              augmenters).\n            * If ``int``: The value will be used as a seed for a new\n              :class:`~imgaug.random.RNG` instance.\n            * If :class:`~imgaug.random.RNG`: The ``RNG`` instance will be\n              used without changes.\n            * If :class:`~imgaug.random.Generator`: A new\n              :class:`~imgaug.random.RNG` instance will be\n              created, containing that generator.\n            * If :class:`~imgaug.random.bit_generator.BitGenerator`: Will\n              be wrapped in a :class:`~imgaug.random.Generator`. Then\n              similar behaviour to :class:`~imgaug.random.Generator`\n              parameters.\n            * If :class:`~imgaug.random.SeedSequence`: Will\n              be wrapped in a new bit generator and\n              :class:`~imgaug.random.Generator`. Then\n              similar behaviour to :class:`~imgaug.random.Generator`\n              parameters.\n            * If :class:`~imgaug.random.RandomState`: Similar behaviour to\n              :class:`~imgaug.random.Generator`. Outdated in numpy 1.17+.\n\n        If a new bit generator has to be created, it will be an instance\n        of :class:`numpy.random.SFC64`.\n\n        Added in 0.4.0.\n\n    name : None or str, optional\n        Name given to the Augmenter instance. This name is used when\n        converting the instance to a string, e.g. for ``print`` statements.\n        It is also used for ``find``, ``remove`` or similar operations\n        on augmenters with children.\n        If ``None``, ``UnnamedX`` will be used as the name, where ``X``\n        is the Augmenter's class name.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    \"\"\"\n\n    def __init__(self, seed=None, name=None,\n                 random_state=\"deprecated\",\n                 deterministic=\"deprecated\"):\n        \"\"\"Create a new Augmenter instance.\"\"\"\n        super(Augmenter, self).__init__()\n\n        assert name is None or ia.is_string(name), (\n            \"Expected name to be None or string-like, got %s.\" % (\n                type(name),))\n        if name is None:\n            self.name = \"Unnamed%s\" % (self.__class__.__name__,)\n        else:\n            self.name = name\n\n        if deterministic != \"deprecated\":\n            ia.warn_deprecated(\n                \"The parameter `deterministic` is deprecated \"\n                \"in `imgaug.augmenters.meta.Augmenter`. Use \"\n                \"`.to_deterministic()` to switch into deterministic mode.\",\n                stacklevel=4)\n            assert ia.is_single_bool(deterministic), (\n                \"Expected deterministic to be a boolean, got %s.\" % (\n                    type(deterministic),))\n        else:\n            deterministic = False\n\n        self.deterministic = deterministic\n\n        if random_state != \"deprecated\":\n            assert seed is None, \"Cannot set both `seed` and `random_state`.\"\n            seed = random_state\n\n        if deterministic and seed is None:\n            # Usually if None is provided, the global RNG will be used.\n            # In case of deterministic mode we most likely rather want a local\n            # RNG, which is here created.\n            self.random_state = iarandom.RNG.create_pseudo_random_()\n        else:\n            # self.random_state = iarandom.normalize_rng_(random_state)\n            self.random_state = iarandom.RNG.create_if_not_rng_(seed)\n\n        self.activated = True\n\n    def augment_batches(self, batches, hooks=None, background=False):\n        \"\"\"Augment multiple batches.\n\n        In contrast to other ``augment_*`` method, this one **yields**\n        batches instead of returning a full list. This is more suited\n        for most training loops.\n\n        This method also also supports augmentation on multiple cpu cores,\n        activated via the `background` flag. If the `background` flag\n        is activated, an instance of :class:`~imgaug.multicore.Pool` will\n        be spawned using all available logical CPU cores and an\n        ``output_buffer_size`` of ``C*10``, where ``C`` is the number of\n        logical CPU cores. I.e. a maximum of ``C*10`` batches will be somewhere\n        in the augmentation pipeline (or waiting to be retrieved by downstream\n        functions) before this method temporarily stops the loading of new\n        batches from `batches`.\n\n        Parameters\n        ----------\n        batches : imgaug.augmentables.batches.Batch or imgaug.augmentables.batches.UnnormalizedBatch or iterable of imgaug.augmentables.batches.Batch or iterable of imgaug.augmentables.batches.UnnormalizedBatch\n            A single batch or a list of batches to augment.\n\n        hooks : None or imgaug.HooksImages, optional\n            HooksImages object to dynamically interfere with the augmentation\n            process.\n\n        background : bool, optional\n            Whether to augment the batches in background processes.\n            If ``True``, hooks can currently not be used as that would require\n            pickling functions.\n            Note that multicore augmentation distributes the batches onto\n            different CPU cores. It does *not* split the data *within* batches.\n            It is therefore *not* sensible to use ``background=True`` to\n            augment a single batch. Only use it for multiple batches.\n            Note also that multicore augmentation needs some time to start. It\n            is therefore not recommended to use it for very few batches.\n\n        Yields\n        -------\n        imgaug.augmentables.batches.Batch or imgaug.augmentables.batches.UnnormalizedBatch or iterable of imgaug.augmentables.batches.Batch or iterable of imgaug.augmentables.batches.UnnormalizedBatch\n            Augmented batches.\n\n        \"\"\"\n        if isinstance(batches, (Batch, UnnormalizedBatch)):\n            batches = [batches]\n\n        assert (\n            (ia.is_iterable(batches)\n             and not ia.is_np_array(batches)\n             and not ia.is_string(batches))\n            or ia.is_generator(batches)), (\n                \"Expected either (a) an iterable that is not an array or a \"\n                \"string or (b) a generator. Got: %s\" % (type(batches),))\n\n        if background:\n            assert hooks is None, (\n                \"Hooks can not be used when background augmentation is \"\n                \"activated.\")\n\n        def _normalize_batch(idx, batch):\n            if isinstance(batch, Batch):\n                batch_copy = batch.deepcopy()\n                batch_copy.data = (idx, batch_copy.data)\n                batch_normalized = batch_copy\n                batch_orig_dt = \"imgaug.Batch\"\n            elif isinstance(batch, UnnormalizedBatch):\n                batch_copy = batch.to_normalized_batch()\n                batch_copy.data = (idx, batch_copy.data)\n                batch_normalized = batch_copy\n                batch_orig_dt = \"imgaug.UnnormalizedBatch\"\n            elif ia.is_np_array(batch):\n                assert batch.ndim in (3, 4), (\n                    \"Expected numpy array to have shape (N, H, W) or \"\n                    \"(N, H, W, C), got %s.\" % (batch.shape,))\n                batch_normalized = Batch(images=batch, data=(idx,))\n                batch_orig_dt = \"numpy_array\"\n            elif isinstance(batch, list):\n                if len(batch) == 0:\n                    batch_normalized = Batch(data=(idx,))\n                    batch_orig_dt = \"empty_list\"\n                elif ia.is_np_array(batch[0]):\n                    batch_normalized = Batch(images=batch, data=(idx,))\n                    batch_orig_dt = \"list_of_numpy_arrays\"\n                elif isinstance(batch[0], ia.HeatmapsOnImage):\n                    batch_normalized = Batch(heatmaps=batch, data=(idx,))\n                    batch_orig_dt = \"list_of_imgaug.HeatmapsOnImage\"\n                elif isinstance(batch[0], ia.SegmentationMapsOnImage):\n                    batch_normalized = Batch(segmentation_maps=batch,\n                                             data=(idx,))\n                    batch_orig_dt = \"list_of_imgaug.SegmentationMapsOnImage\"\n                elif isinstance(batch[0], ia.KeypointsOnImage):\n                    batch_normalized = Batch(keypoints=batch, data=(idx,))\n                    batch_orig_dt = \"list_of_imgaug.KeypointsOnImage\"\n                elif isinstance(batch[0], ia.BoundingBoxesOnImage):\n                    batch_normalized = Batch(bounding_boxes=batch, data=(idx,))\n                    batch_orig_dt = \"list_of_imgaug.BoundingBoxesOnImage\"\n                elif isinstance(batch[0], ia.PolygonsOnImage):\n                    batch_normalized = Batch(polygons=batch, data=(idx,))\n                    batch_orig_dt = \"list_of_imgaug.PolygonsOnImage\"\n                else:\n                    raise Exception(\n                        \"Unknown datatype in batch[0]. Expected numpy array \"\n                        \"or imgaug.HeatmapsOnImage or \"\n                        \"imgaug.SegmentationMapsOnImage or \"\n                        \"imgaug.KeypointsOnImage or \"\n                        \"imgaug.BoundingBoxesOnImage, \"\n                        \"or imgaug.PolygonsOnImage, \"\n                        \"got %s.\" % (type(batch[0]),))\n            else:\n                raise Exception(\n                    \"Unknown datatype of batch. Expected imgaug.Batch or \"\n                    \"imgaug.UnnormalizedBatch or \"\n                    \"numpy array or list of (numpy array or \"\n                    \"imgaug.HeatmapsOnImage or \"\n                    \"imgaug.SegmentationMapsOnImage \"\n                    \"or imgaug.KeypointsOnImage or \"\n                    \"imgaug.BoundingBoxesOnImage or \"\n                    \"imgaug.PolygonsOnImage). Got %s.\" % (type(batch),))\n\n            if batch_orig_dt not in [\"imgaug.Batch\",\n                                     \"imgaug.UnnormalizedBatch\"]:\n                ia.warn_deprecated(\n                    \"Received an input in augment_batches() that was not an \"\n                    \"instance of imgaug.augmentables.batches.Batch \"\n                    \"or imgaug.augmentables.batches.UnnormalizedBatch, but \"\n                    \"instead %s. This is deprecated. Use augment() for such \"\n                    \"data or wrap it in a Batch instance.\" % (\n                        batch_orig_dt,))\n            return batch_normalized, batch_orig_dt\n\n        # unnormalization of non-Batch/UnnormalizedBatch is for legacy support\n        def _unnormalize_batch(batch_aug, batch_orig, batch_orig_dt):\n            if batch_orig_dt == \"imgaug.Batch\":\n                batch_unnormalized = batch_aug\n                # change (i, .data) back to just .data\n                batch_unnormalized.data = batch_unnormalized.data[1]\n            elif batch_orig_dt == \"imgaug.UnnormalizedBatch\":\n                # change (i, .data) back to just .data\n                batch_aug.data = batch_aug.data[1]\n\n                batch_unnormalized = \\\n                    batch_orig.fill_from_augmented_normalized_batch(batch_aug)\n            elif batch_orig_dt == \"numpy_array\":\n                batch_unnormalized = batch_aug.images_aug\n            elif batch_orig_dt == \"empty_list\":\n                batch_unnormalized = []\n            elif batch_orig_dt == \"list_of_numpy_arrays\":\n                batch_unnormalized = batch_aug.images_aug\n            elif batch_orig_dt == \"list_of_imgaug.HeatmapsOnImage\":\n                batch_unnormalized = batch_aug.heatmaps_aug\n            elif batch_orig_dt == \"list_of_imgaug.SegmentationMapsOnImage\":\n                batch_unnormalized = batch_aug.segmentation_maps_aug\n            elif batch_orig_dt == \"list_of_imgaug.KeypointsOnImage\":\n                batch_unnormalized = batch_aug.keypoints_aug\n            elif batch_orig_dt == \"list_of_imgaug.BoundingBoxesOnImage\":\n                batch_unnormalized = batch_aug.bounding_boxes_aug\n            else:  # only option left\n                assert batch_orig_dt == \"list_of_imgaug.PolygonsOnImage\", (\n                    \"Got an unexpected type %s.\" % (type(batch_orig_dt),))\n                batch_unnormalized = batch_aug.polygons_aug\n            return batch_unnormalized\n\n        if not background:\n            # singlecore augmentation\n\n            for idx, batch in enumerate(batches):\n                batch_normalized, batch_orig_dt = _normalize_batch(idx, batch)\n                batch_normalized = self.augment_batch_(\n                    batch_normalized, hooks=hooks)\n                batch_unnormalized = _unnormalize_batch(\n                    batch_normalized, batch, batch_orig_dt)\n\n                yield batch_unnormalized\n        else:\n            # multicore augmentation\n            import imgaug.multicore as multicore\n\n            id_to_batch_orig = dict()\n\n            def load_batches():\n                for idx, batch in enumerate(batches):\n                    batch_normalized, batch_orig_dt = _normalize_batch(\n                        idx, batch)\n                    id_to_batch_orig[idx] = (batch, batch_orig_dt)\n                    yield batch_normalized\n\n            with multicore.Pool(self) as pool:\n                # pylint:disable=protected-access\n                # note that pool.processes is None here\n                output_buffer_size = pool.pool._processes * 10\n\n                for batch_aug in pool.imap_batches(\n                        load_batches(), output_buffer_size=output_buffer_size):\n                    idx = batch_aug.data[0]\n                    assert idx in id_to_batch_orig, (\n                        \"Got idx %d from Pool, which is not known.\" % (\n                            idx))\n                    batch_orig, batch_orig_dt = id_to_batch_orig[idx]\n                    batch_unnormalized = _unnormalize_batch(\n                        batch_aug, batch_orig, batch_orig_dt)\n                    del id_to_batch_orig[idx]\n                    yield batch_unnormalized\n\n    # we deprecate here so that users switch to `augment_batch_()` and in the\n    # future we can add a `parents` parameter here without having to consider\n    # that a breaking change\n    @ia.deprecated(\"augment_batch_()\",\n                   comment=\"`augment_batch()` was renamed to \"\n                           \"`augment_batch_()` as it changes all `*_unaug` \"\n                           \"attributes of batches in-place. Note that \"\n                           \"`augment_batch_()` has now a `parents` parameter. \"\n                           \"Calls of the style `augment_batch(batch, hooks)` \"\n                           \"must be changed to \"\n                           \"`augment_batch(batch, hooks=hooks)`.\")\n    def augment_batch(self, batch, hooks=None):\n        \"\"\"Augment a single batch.\n\n        Deprecated since 0.4.0.\n\n        \"\"\"\n        # We call augment_batch_() directly here without copy, because this\n        # method never copies. Would make sense to add a copy here if the\n        # method is un-deprecated at some point.\n        return self.augment_batch_(batch, hooks=hooks)\n\n    # TODO add more tests\n    def augment_batch_(self, batch, parents=None, hooks=None):\n        \"\"\"\n        Augment a single batch in-place.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        batch : imgaug.augmentables.batches.Batch or imgaug.augmentables.batches.UnnormalizedBatch or imgaug.augmentables.batch._BatchInAugmentation\n            A single batch to augment.\n\n            If :class:`imgaug.augmentables.batches.UnnormalizedBatch`\n            or :class:`imgaug.augmentables.batches.Batch`, then the ``*_aug``\n            attributes may be modified in-place, while the ``*_unaug``\n            attributes will not be modified.\n            If :class:`imgaug.augmentables.batches._BatchInAugmentation`,\n            then all attributes may be modified in-place.\n\n        parents : None or list of imgaug.augmenters.Augmenter, optional\n            Parent augmenters that have previously been called before the\n            call to this function. Usually you can leave this parameter as\n            ``None``. It is set automatically for child augmenters.\n\n        hooks : None or imgaug.HooksImages, optional\n            HooksImages object to dynamically interfere with the augmentation\n            process.\n\n        Returns\n        -------\n        imgaug.augmentables.batches.Batch or imgaug.augmentables.batches.UnnormalizedBatch\n            Augmented batch.\n\n        \"\"\"\n        # this chain of if/elses would be more beautiful if it was\n        # (1st) UnnormalizedBatch, (2nd) Batch, (3rd) BatchInAugmenation.\n        # We check for _BatchInAugmentation first as it is expected to be the\n        # most common input (due to child calls).\n        batch_unnorm = None\n        batch_norm = None\n        if isinstance(batch, _BatchInAugmentation):\n            batch_inaug = batch\n        elif isinstance(batch, UnnormalizedBatch):\n            batch_unnorm = batch\n            batch_norm = batch.to_normalized_batch()\n            batch_inaug = batch_norm.to_batch_in_augmentation()\n        elif isinstance(batch, Batch):\n            batch_norm = batch\n            batch_inaug = batch_norm.to_batch_in_augmentation()\n        else:\n            raise ValueError(\n                \"Expected UnnormalizedBatch, Batch or _BatchInAugmentation, \"\n                \"got %s.\" % (type(batch).__name__,))\n\n        columns = batch_inaug.columns\n\n        # hooks preprocess\n        if hooks is not None:\n            for column in columns:\n                value = hooks.preprocess(\n                    column.value, augmenter=self, parents=parents)\n                setattr(batch_inaug, column.attr_name, value)\n\n            # refresh so that values are updated for later functions\n            columns = batch_inaug.columns\n\n        # set augmentables to None if this augmenter is deactivated or hooks\n        # demands it\n        set_to_none = []\n        if not self.activated:\n            for column in columns:\n                set_to_none.append(column)\n                setattr(batch_inaug, column.attr_name, None)\n        elif hooks is not None:\n            for column in columns:\n                activated = hooks.is_activated(\n                    column.value, augmenter=self, parents=parents,\n                    default=self.activated)\n                if not activated:\n                    set_to_none.append(column)\n                    setattr(batch_inaug, column.attr_name, None)\n\n        # If _augment_batch_() follows legacy-style and ends up calling\n        # _augment_images() and similar methods, we don't need the\n        # deterministic context here. But if there is a custom implementation\n        # of _augment_batch_(), then we should have this here. It causes very\n        # little overhead.\n        with _maybe_deterministic_ctx(self):\n            if not batch_inaug.empty:\n                pf_enabled = not self.deterministic\n                with iap.toggled_prefetching(pf_enabled):\n                    batch_inaug = self._augment_batch_(\n                        batch_inaug,\n                        random_state=self.random_state,\n                        parents=parents if parents is not None else [],\n                        hooks=hooks)\n\n        # revert augmentables being set to None for non-activated augmenters\n        for column in set_to_none:\n            setattr(batch_inaug, column.attr_name, column.value)\n\n        # hooks postprocess\n        if hooks is not None:\n            # refresh as contents may have been changed in _augment_batch_()\n            columns = batch_inaug.columns\n\n            for column in columns:\n                augm_value = hooks.postprocess(\n                    column.value, augmenter=self, parents=parents)\n                setattr(batch_inaug, column.attr_name, augm_value)\n\n        if batch_unnorm is not None:\n            batch_norm = batch_norm.fill_from_batch_in_augmentation_(\n                batch_inaug)\n            batch_unnorm = batch_unnorm.fill_from_augmented_normalized_batch_(\n                batch_norm)\n            return batch_unnorm\n        if batch_norm is not None:\n            batch_norm = batch_norm.fill_from_batch_in_augmentation_(\n                batch_inaug)\n            return batch_norm\n        return batch_inaug\n\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        \"\"\"Augment a single batch in-place.\n\n        This is the internal version of :func:`Augmenter.augment_batch_`.\n        It is called from :func:`Augmenter.augment_batch_` and should usually\n        not be called directly.\n        This method may transform the batches in-place.\n        This method does not have to care about determinism or the\n        Augmenter instance's ``random_state`` variable. The parameter\n        ``random_state`` takes care of both of these.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        batch : imgaug.augmentables.batches._BatchInAugmentation\n            The normalized batch to augment. May be changed in-place.\n\n        random_state : imgaug.random.RNG\n            The random state to use for all sampling tasks during the\n            augmentation.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_batch_`.\n\n        hooks : imgaug.imgaug.HooksImages or None\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_batch_`.\n\n        Returns\n        -------\n        imgaug.augmentables.batches._BatchInAugmentation\n            The augmented batch.\n\n        \"\"\"\n        # The code below covers the case of older augmenters that still have\n        # _augment_images(), _augment_keypoints(), ... methods that augment\n        # each input type on its own (including re-sampling from random\n        # variables). The code block can be safely overwritten by a method\n        # augmenting a whole batch of data in one step.\n\n        columns = batch.columns\n        multiple_columns = len(columns) > 1\n\n        # For multi-column data (e.g. images + BBs) we need deterministic mode\n        # within this batch, otherwise the datatypes within this batch would\n        # get different samples.\n        deterministic = self.deterministic or multiple_columns\n\n        # set attribute batch.T_aug with result of self.augment_T() for each\n        # batch.T_unaug (that had any content)\n        for column in columns:\n            with _maybe_deterministic_ctx(random_state, deterministic):\n                pf_enabled = not self.deterministic\n                with iap.toggled_prefetching(pf_enabled):\n                    value = getattr(self, \"_augment_\" + column.name)(\n                        column.value, random_state=random_state,\n                        parents=parents, hooks=hooks)\n                    setattr(batch, column.attr_name, value)\n\n        # If the augmenter was alread in deterministic mode, we can expect\n        # that to_deterministic() was called, which advances the RNG. But\n        # if it wasn't and we had to auto-switch for the batch, there was not\n        # advancement yet.\n        if multiple_columns and not self.deterministic:\n            random_state.advance_()\n\n        return batch\n\n    def augment_image(self, image, hooks=None):\n        \"\"\"Augment a single image.\n\n        Parameters\n        ----------\n        image : (H,W,C) ndarray or (H,W) ndarray\n            The image to augment.\n            Channel-axis is optional, but expected to be the last axis if\n            present. In most cases, this array should be of dtype ``uint8``,\n            which is supported by all augmenters. Support for other dtypes\n            varies by augmenter -- see the respective augmenter-specific\n            documentation for more details.\n\n        hooks : None or imgaug.HooksImages, optional\n            HooksImages object to dynamically interfere with the augmentation\n            process.\n\n        Returns\n        -------\n        ndarray\n            The corresponding augmented image.\n\n        \"\"\"\n        assert ia.is_np_array(image), (\n            \"Expected to get a single numpy array of shape (H,W) or (H,W,C) \"\n            \"for `image`. Got instead type %d. Use `augment_images(images)` \"\n            \"to augment a list of multiple images.\" % (\n                type(image).__name__),)\n        assert image.ndim in [2, 3], (\n            \"Expected image to have shape (height, width, [channels]), \"\n            \"got shape %s.\" % (image.shape,))\n        iabase._warn_on_suspicious_single_image_shape(image)\n        return self.augment_images([image], hooks=hooks)[0]\n\n    def augment_images(self, images, parents=None, hooks=None):\n        \"\"\"Augment a batch of images.\n\n        Parameters\n        ----------\n        images : (N,H,W,C) ndarray or (N,H,W) ndarray or list of (H,W,C) ndarray or list of (H,W) ndarray\n            Images to augment.\n            The input can be a list of numpy arrays or a single array. Each\n            array is expected to have shape ``(H, W, C)`` or ``(H, W)``,\n            where ``H`` is the height, ``W`` is the width and ``C`` are the\n            channels. The number of channels may differ between images.\n            If a list is provided, the height, width and channels may differ\n            between images within the provided batch.\n            In most cases, the image array(s) should be of dtype ``uint8``,\n            which is supported by all augmenters. Support for other dtypes\n            varies by augmenter -- see the respective augmenter-specific\n            documentation for more details.\n\n        parents : None or list of imgaug.augmenters.Augmenter, optional\n            Parent augmenters that have previously been called before the\n            call to this function. Usually you can leave this parameter as\n            ``None``. It is set automatically for child augmenters.\n\n        hooks : None or imgaug.imgaug.HooksImages, optional\n            :class:`~imgaug.imgaug.HooksImages` object to dynamically\n            interfere with the augmentation process.\n\n        Returns\n        -------\n        ndarray or list\n            Corresponding augmented images.\n            If the input was an ``ndarray``, the output is also an ``ndarray``,\n            unless the used augmentations have led to different output image\n            sizes (as can happen in e.g. cropping).\n\n        Examples\n        --------\n        >>> import imgaug.augmenters as iaa\n        >>> import numpy as np\n        >>> aug = iaa.GaussianBlur((0.0, 3.0))\n        >>> # create empty example images\n        >>> images = np.zeros((2, 64, 64, 3), dtype=np.uint8)\n        >>> images_aug = aug.augment_images(images)\n\n        Create ``2`` empty (i.e. black) example numpy images and apply\n        gaussian blurring to them.\n\n        \"\"\"\n        iabase._warn_on_suspicious_multi_image_shapes(images)\n        return self.augment_batch_(\n            UnnormalizedBatch(images=images),\n            parents=parents,\n            hooks=hooks\n        ).images_aug\n\n    def _augment_images(self, images, random_state, parents, hooks):\n        \"\"\"Augment a batch of images in-place.\n\n        This is the internal version of :func:`Augmenter.augment_images`.\n        It is called from :func:`Augmenter.augment_images` and should usually\n        not be called directly.\n        It has to be implemented by every augmenter.\n        This method may transform the images in-place.\n        This method does not have to care about determinism or the\n        Augmenter instance's ``random_state`` variable. The parameter\n        ``random_state`` takes care of both of these.\n\n        .. note::\n\n            This method exists mostly for legacy-support.\n            Overwriting :func:`~imgaug.augmenters.meta.Augmenter._augment_batch`\n            is now the preferred way of implementing custom augmentation\n            routines.\n\n        Parameters\n        ----------\n        images : (N,H,W,C) ndarray or list of (H,W,C) ndarray\n            Images to augment.\n            They may be changed in-place.\n            Either a list of ``(H, W, C)`` arrays or a single ``(N, H, W, C)``\n            array, where ``N`` is the number of images, ``H`` is the height of\n            images, ``W`` is the width of images and ``C`` is the number of\n            channels of images. In the case of a list as input, ``H``, ``W``\n            and ``C`` may change per image.\n\n        random_state : imgaug.random.RNG\n            The random state to use for all sampling tasks during the\n            augmentation.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_images`.\n\n        hooks : imgaug.imgaug.HooksImages or None\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_images`.\n\n        Returns\n        ----------\n        (N,H,W,C) ndarray or list of (H,W,C) ndarray\n            The augmented images.\n\n        \"\"\"\n        return images\n\n    def augment_heatmaps(self, heatmaps, parents=None, hooks=None):\n        \"\"\"Augment a batch of heatmaps.\n\n        Parameters\n        ----------\n        heatmaps : imgaug.augmentables.heatmaps.HeatmapsOnImage or list of imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Heatmap(s) to augment. Either a single heatmap or a list of\n            heatmaps.\n\n        parents : None or list of imgaug.augmenters.meta.Augmenter, optional\n            Parent augmenters that have previously been called before the\n            call to this function. Usually you can leave this parameter as\n            ``None``.\n            It is set automatically for child augmenters.\n\n        hooks : None or imaug.imgaug.HooksHeatmaps, optional\n            :class:`~imgaug.imgaug.HooksHeatmaps` object to dynamically\n            interfere with the augmentation process.\n\n        Returns\n        -------\n        imgaug.augmentables.heatmaps.HeatmapsOnImage or list of imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Corresponding augmented heatmap(s).\n\n        \"\"\"\n        return self.augment_batch_(\n            UnnormalizedBatch(heatmaps=heatmaps), parents=parents, hooks=hooks\n        ).heatmaps_aug\n\n    def _augment_heatmaps(self, heatmaps, random_state, parents, hooks):\n        \"\"\"Augment a batch of heatmaps in-place.\n\n        This is the internal version of :func:`Augmenter.augment_heatmaps`.\n        It is called from :func:`Augmenter.augment_heatmaps` and should\n        usually not be called directly.\n        This method may augment heatmaps in-place.\n        This method does not have to care about determinism or the\n        Augmenter instance's ``random_state`` variable. The parameter\n        ``random_state`` takes care of both of these.\n\n        .. note::\n\n            This method exists mostly for legacy-support.\n            Overwriting :func:`~imgaug.augmenters.meta.Augmenter._augment_batch`\n            is now the preferred way of implementing custom augmentation\n            routines.\n\n        Parameters\n        ----------\n        heatmaps : list of imgaug.augmentables.heatmaps.HeatmapsOnImage\n            Heatmaps to augment. They may be changed in-place.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_heatmaps`.\n\n        hooks : imgaug.imgaug.HooksHeatmaps or None\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_heatmaps`.\n\n        Returns\n        ----------\n        images : list of imgaug.augmentables.heatmaps.HeatmapsOnImage\n            The augmented heatmaps.\n\n        \"\"\"\n        return heatmaps\n\n    def augment_segmentation_maps(self, segmaps, parents=None, hooks=None):\n        \"\"\"Augment a batch of segmentation maps.\n\n        Parameters\n        ----------\n        segmaps : imgaug.augmentables.segmaps.SegmentationMapsOnImage or list of imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            Segmentation map(s) to augment. Either a single segmentation map\n            or a list of segmentation maps.\n\n        parents : None or list of imgaug.augmenters.meta.Augmenter, optional\n            Parent augmenters that have previously been called before the\n            call to this function. Usually you can leave this parameter as\n            ``None``. It is set automatically for child augmenters.\n\n        hooks : None or imgaug.HooksHeatmaps, optional\n            :class:`~imgaug.imgaug.HooksHeatmaps` object to dynamically\n            interfere with the augmentation process.\n\n        Returns\n        -------\n        imgaug.augmentables.segmaps.SegmentationMapsOnImage or list of imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            Corresponding augmented segmentation map(s).\n\n        \"\"\"\n        return self.augment_batch_(\n            UnnormalizedBatch(segmentation_maps=segmaps),\n            parents=parents,\n            hooks=hooks\n        ).segmentation_maps_aug\n\n    def _augment_segmentation_maps(self, segmaps, random_state, parents, hooks):\n        \"\"\"Augment a batch of segmentation in-place.\n\n        This is the internal version of\n        :func:`Augmenter.augment_segmentation_maps`.\n        It is called from :func:`Augmenter.augment_segmentation_maps` and\n        should usually not be called directly.\n        This method may augment segmentation maps in-place.\n        This method does not have to care about determinism or the\n        Augmenter instance's ``random_state`` variable. The parameter\n        ``random_state`` takes care of both of these.\n\n        .. note::\n\n            This method exists mostly for legacy-support.\n            Overwriting :func:`~imgaug.augmenters.meta.Augmenter._augment_batch`\n            is now the preferred way of implementing custom augmentation\n            routines.\n\n        Parameters\n        ----------\n        segmaps : list of imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            Segmentation maps to augment. They may be changed in-place.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See\n            :func:`~imgaug.augmenters.meta.Augmenter.augment_segmentation_maps`.\n\n        hooks : imgaug.imgaug.HooksHeatmaps or None\n            See\n            :func:`~imgaug.augmenters.meta.Augmenter.augment_segmentation_maps`.\n\n        Returns\n        ----------\n        images : list of imgaug.augmentables.segmaps.SegmentationMapsOnImage\n            The augmented segmentation maps.\n\n        \"\"\"\n        return segmaps\n\n    def augment_keypoints(self, keypoints_on_images, parents=None, hooks=None):\n        \"\"\"Augment a batch of keypoints/landmarks.\n\n        This is the corresponding function to :func:`Augmenter.augment_images`,\n        just for keypoints/landmarks (i.e. points on images).\n        Usually you will want to call :func:`Augmenter.augment_images` with\n        a list of images, e.g. ``augment_images([A, B, C])`` and then\n        ``augment_keypoints()`` with the corresponding list of keypoints on\n        these images, e.g. ``augment_keypoints([Ak, Bk, Ck])``, where ``Ak``\n        are the keypoints on image ``A``.\n\n        Make sure to first convert the augmenter(s) to deterministic states\n        before augmenting images and their corresponding keypoints,\n        e.g. by\n\n        >>> import imgaug.augmenters as iaa\n        >>> from imgaug.augmentables.kps import Keypoint\n        >>> from imgaug.augmentables.kps import KeypointsOnImage\n        >>> A = B = C = np.zeros((10, 10), dtype=np.uint8)\n        >>> Ak = Bk = Ck = KeypointsOnImage([Keypoint(2, 2)], (10, 10))\n        >>> seq = iaa.Fliplr(0.5)\n        >>> seq_det = seq.to_deterministic()\n        >>> imgs_aug = seq_det.augment_images([A, B, C])\n        >>> kps_aug = seq_det.augment_keypoints([Ak, Bk, Ck])\n\n        Otherwise, different random values will be sampled for the image\n        and keypoint augmentations, resulting in different augmentations (e.g.\n        images might be rotated by ``30deg`` and keypoints by ``-10deg``).\n        Also make sure to call :func:`Augmenter.to_deterministic` again for\n        each new batch, otherwise you would augment all batches in the same\n        way.\n\n        Note that there is also :func:`Augmenter.augment`, which automatically\n        handles the random state alignment.\n\n        Parameters\n        ----------\n        keypoints_on_images : imgaug.augmentables.kps.KeypointsOnImage or list of imgaug.augmentables.kps.KeypointsOnImage\n            The keypoints/landmarks to augment.\n            Either a single instance of\n            :class:`~imgaug.augmentables.kps.KeypointsOnImage` or a list of\n            such instances. Each instance must contain the keypoints of a\n            single image.\n\n        parents : None or list of imgaug.augmenters.meta.Augmenter, optional\n            Parent augmenters that have previously been called before the\n            call to this function. Usually you can leave this parameter as\n            ``None``. It is set automatically for child augmenters.\n\n        hooks : None or imgaug.imgaug.HooksKeypoints, optional\n            :class:`~imgaug.imgaug.HooksKeypoints` object to dynamically\n            interfere with the augmentation process.\n\n        Returns\n        -------\n        imgaug.augmentables.kps.KeypointsOnImage or list of imgaug.augmentables.kps.KeypointsOnImage\n            Augmented keypoints.\n\n        \"\"\"\n        return self.augment_batch_(\n            UnnormalizedBatch(keypoints=keypoints_on_images),\n            parents=parents,\n            hooks=hooks\n        ).keypoints_aug\n\n    def _augment_keypoints(self, keypoints_on_images, random_state, parents,\n                           hooks):\n        \"\"\"Augment a batch of keypoints in-place.\n\n        This is the internal version of :func:`Augmenter.augment_keypoints`.\n        It is called from :func:`Augmenter.augment_keypoints` and should\n        usually not be called directly.\n        This method may transform the keypoints in-place.\n        This method does not have to care about determinism or the\n        Augmenter instance's ``random_state`` variable. The parameter\n        ``random_state`` takes care of both of these.\n\n        .. note::\n\n            This method exists mostly for legacy-support.\n            Overwriting :func:`~imgaug.augmenters.meta.Augmenter._augment_batch`\n            is now the preferred way of implementing custom augmentation\n            routines.\n\n        Parameters\n        ----------\n        keypoints_on_images : list of imgaug.augmentables.kps.KeypointsOnImage\n            Keypoints to augment. They may be changed in-place.\n\n        random_state : imgaug.random.RNG\n            The random state to use for all sampling tasks during the augmentation.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_keypoints`.\n\n        hooks : imgaug.imgaug.HooksKeypoints or None\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_keypoints`.\n\n        Returns\n        ----------\n        list of imgaug.augmentables.kps.KeypointsOnImage\n            The augmented keypoints.\n\n        \"\"\"\n        return keypoints_on_images\n\n    def augment_bounding_boxes(self, bounding_boxes_on_images, parents=None,\n                               hooks=None):\n        \"\"\"Augment a batch of bounding boxes.\n\n        This is the corresponding function to\n        :func:`Augmenter.augment_images`, just for bounding boxes.\n        Usually you will want to call :func:`Augmenter.augment_images` with\n        a list of images, e.g. ``augment_images([A, B, C])`` and then\n        ``augment_bounding_boxes()`` with the corresponding list of bounding\n        boxes on these images, e.g.\n        ``augment_bounding_boxes([Abb, Bbb, Cbb])``, where ``Abb`` are the\n        bounding boxes on image ``A``.\n\n        Make sure to first convert the augmenter(s) to deterministic states\n        before augmenting images and their corresponding bounding boxes,\n        e.g. by\n\n        >>> import imgaug.augmenters as iaa\n        >>> from imgaug.augmentables.bbs import BoundingBox\n        >>> from imgaug.augmentables.bbs import BoundingBoxesOnImage\n        >>> A = B = C = np.ones((10, 10), dtype=np.uint8)\n        >>> Abb = Bbb = Cbb = BoundingBoxesOnImage([\n        >>>     BoundingBox(1, 1, 9, 9)], (10, 10))\n        >>> seq = iaa.Fliplr(0.5)\n        >>> seq_det = seq.to_deterministic()\n        >>> imgs_aug = seq_det.augment_images([A, B, C])\n        >>> bbs_aug = seq_det.augment_bounding_boxes([Abb, Bbb, Cbb])\n\n        Otherwise, different random values will be sampled for the image\n        and bounding box augmentations, resulting in different augmentations\n        (e.g. images might be rotated by ``30deg`` and bounding boxes by\n        ``-10deg``). Also make sure to call :func:`Augmenter.to_deterministic`\n        again for each new batch, otherwise you would augment all batches in\n        the same way.\n\n        Note that there is also :func:`Augmenter.augment`, which automatically\n        handles the random state alignment.\n\n        Parameters\n        ----------\n        bounding_boxes_on_images : imgaug.augmentables.bbs.BoundingBoxesOnImage or list of imgaug.augmentables.bbs.BoundingBoxesOnImage\n            The bounding boxes to augment.\n            Either a single instance of\n            :class:`~imgaug.augmentables.bbs.BoundingBoxesOnImage` or a list of\n            such instances, with each one of them containing the bounding\n            boxes of a single image.\n\n        parents : None or list of imgaug.augmenters.meta.Augmenter, optional\n            Parent augmenters that have previously been called before the\n            call to this function. Usually you can leave this parameter as\n            ``None``. It is set automatically for child augmenters.\n\n        hooks : None or imgaug.imgaug.HooksKeypoints, optional\n            :class:`~imgaug.imgaug.HooksKeypoints` object to dynamically\n            interfere with the augmentation process.\n\n        Returns\n        -------\n        imgaug.augmentables.bbs.BoundingBoxesOnImage or list of imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Augmented bounding boxes.\n\n        \"\"\"\n        return self.augment_batch_(\n            UnnormalizedBatch(bounding_boxes=bounding_boxes_on_images),\n            parents=parents,\n            hooks=hooks\n        ).bounding_boxes_aug\n\n    def augment_polygons(self, polygons_on_images, parents=None, hooks=None):\n        \"\"\"Augment a batch of polygons.\n\n        This is the corresponding function to :func:`Augmenter.augment_images`,\n        just for polygons.\n        Usually you will want to call :func:`Augmenter.augment_images`` with\n        a list of images, e.g. ``augment_images([A, B, C])`` and then\n        ``augment_polygons()`` with the corresponding list of polygons on these\n        images, e.g. ``augment_polygons([A_poly, B_poly, C_poly])``, where\n        ``A_poly`` are the polygons on image ``A``.\n\n        Make sure to first convert the augmenter(s) to deterministic states\n        before augmenting images and their corresponding polygons,\n        e.g. by\n\n        >>> import imgaug.augmenters as iaa\n        >>> from imgaug.augmentables.polys import Polygon, PolygonsOnImage\n        >>> A = B = C = np.ones((10, 10), dtype=np.uint8)\n        >>> Apoly = Bpoly = Cpoly = PolygonsOnImage(\n        >>>     [Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])],\n        >>>     shape=(10, 10))\n        >>> seq = iaa.Fliplr(0.5)\n        >>> seq_det = seq.to_deterministic()\n        >>> imgs_aug = seq_det.augment_images([A, B, C])\n        >>> polys_aug = seq_det.augment_polygons([Apoly, Bpoly, Cpoly])\n\n        Otherwise, different random values will be sampled for the image\n        and polygon augmentations, resulting in different augmentations\n        (e.g. images might be rotated by ``30deg`` and polygons by\n        ``-10deg``). Also make sure to call ``to_deterministic()`` again for\n        each new batch, otherwise you would augment all batches in the same\n        way.\n\n        Note that there is also :func:`Augmenter.augment`, which automatically\n        handles the random state alignment.\n\n        Parameters\n        ----------\n        polygons_on_images : imgaug.augmentables.polys.PolygonsOnImage or list of imgaug.augmentables.polys.PolygonsOnImage\n            The polygons to augment.\n            Either a single instance of\n            :class:`~imgaug.augmentables.polys.PolygonsOnImage` or a list of\n            such instances, with each one of them containing the polygons of\n            a single image.\n\n        parents : None or list of imgaug.augmenters.meta.Augmenter, optional\n            Parent augmenters that have previously been called before the\n            call to this function. Usually you can leave this parameter as\n            ``None``. It is set automatically for child augmenters.\n\n        hooks : None or imgaug.imgaug.HooksKeypoints, optional\n            :class:`~imgaug.imgaug.HooksKeypoints` object to dynamically\n            interfere with the augmentation process.\n\n        Returns\n        -------\n        imgaug.augmentables.polys.PolygonsOnImage or list of imgaug.augmentables.polys.PolygonsOnImage\n            Augmented polygons.\n\n        \"\"\"\n        return self.augment_batch_(\n            UnnormalizedBatch(polygons=polygons_on_images),\n            parents=parents,\n            hooks=hooks\n        ).polygons_aug\n\n    def augment_line_strings(self, line_strings_on_images, parents=None,\n                             hooks=None):\n        \"\"\"Augment a batch of line strings.\n\n        This is the corresponding function to\n        :func:`Augmenter.augment_images``, just for line strings.\n        Usually you will want to call :func:`Augmenter.augment_images` with\n        a list of images, e.g. ``augment_images([A, B, C])`` and then\n        ``augment_line_strings()`` with the corresponding list of line\n        strings on these images, e.g.\n        ``augment_line_strings([A_line, B_line, C_line])``, where ``A_line``\n        are the line strings on image ``A``.\n\n        Make sure to first convert the augmenter(s) to deterministic states\n        before augmenting images and their corresponding line strings,\n        e.g. by\n\n        >>> import imgaug.augmenters as iaa\n        >>> from imgaug.augmentables.lines import LineString\n        >>> from imgaug.augmentables.lines import LineStringsOnImage\n        >>> A = B = C = np.ones((10, 10), dtype=np.uint8)\n        >>> A_line = B_line = C_line = LineStringsOnImage(\n        >>>     [LineString([(0, 0), (1, 0), (1, 1), (0, 1)])],\n        >>>     shape=(10, 10))\n        >>> seq = iaa.Fliplr(0.5)\n        >>> seq_det = seq.to_deterministic()\n        >>> imgs_aug = seq_det.augment_images([A, B, C])\n        >>> lines_aug = seq_det.augment_line_strings([A_line, B_line, C_line])\n\n        Otherwise, different random values will be sampled for the image\n        and line string augmentations, resulting in different augmentations\n        (e.g. images might be rotated by ``30deg`` and line strings by\n        ``-10deg``). Also make sure to call ``to_deterministic()`` again for\n        each new batch, otherwise you would augment all batches in the same\n        way.\n\n        Note that there is also :func:`Augmenter.augment`, which automatically\n        handles the random state alignment.\n\n        Parameters\n        ----------\n        line_strings_on_images : imgaug.augmentables.lines.LineStringsOnImage or list of imgaug.augmentables.lines.LineStringsOnImage\n            The line strings to augment.\n            Either a single instance of\n            :class:`~imgaug.augmentables.lines.LineStringsOnImage` or a list of\n            such instances, with each one of them containing the line strings\n            of a single image.\n\n        parents : None or list of imgaug.augmenters.meta.Augmenter, optional\n            Parent augmenters that have previously been called before the\n            call to this function. Usually you can leave this parameter as None.\n            It is set automatically for child augmenters.\n\n        hooks : None or imgaug.imgaug.HooksKeypoints, optional\n            :class:`~imgaug.imgaug.HooksKeypoints` object to dynamically\n            interfere with the augmentation process.\n\n        Returns\n        -------\n        imgaug.augmentables.lines.LineStringsOnImage or list of imgaug.augmentables.lines.LineStringsOnImage\n            Augmented line strings.\n\n        \"\"\"\n        return self.augment_batch_(\n            UnnormalizedBatch(line_strings=line_strings_on_images),\n            parents=parents,\n            hooks=hooks\n        ).line_strings_aug\n\n    def _augment_bounding_boxes(self, bounding_boxes_on_images, random_state,\n                                parents, hooks):\n        \"\"\"Augment a batch of bounding boxes on images in-place.\n\n        This is the internal version of\n        :func:`Augmenter.augment_bounding_boxes`.\n        It is called from :func:`Augmenter.augment_bounding_boxes` and should\n        usually not be called directly.\n        This method may transform the bounding boxes in-place.\n        This method does not have to care about determinism or the\n        Augmenter instance's ``random_state`` variable. The parameter\n        ``random_state`` takes care of both of these.\n\n        .. note::\n\n            This method exists mostly for legacy-support.\n            Overwriting :func:`~imgaug.augmenters.meta.Augmenter._augment_batch`\n            is now the preferred way of implementing custom augmentation\n            routines.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        bounding_boxes_on_images : list of imgaug.augmentables.bbs.BoundingBoxesOnImage\n            Polygons to augment. They may be changed in-place.\n\n        random_state : imgaug.random.RNG\n            The random state to use for all sampling tasks during the\n            augmentation.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_bounding_boxes`.\n\n        hooks : imgaug.imgaug.HooksKeypoints or None\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_bounding_boxes`.\n\n        Returns\n        -------\n        list of imgaug.augmentables.bbs.BoundingBoxesOnImage\n            The augmented bounding boxes.\n\n        \"\"\"\n        return self._augment_cbaois_as_keypoints(\n            bounding_boxes_on_images,\n            random_state=random_state,\n            parents=parents,\n            hooks=hooks\n        )\n\n    def _augment_polygons(self, polygons_on_images, random_state, parents,\n                          hooks):\n        \"\"\"Augment a batch of polygons on images in-place.\n\n        This is the internal version of :func:`Augmenter.augment_polygons`.\n        It is called from :func:`Augmenter.augment_polygons` and should\n        usually not be called directly.\n        This method may transform the polygons in-place.\n        This method does not have to care about determinism or the\n        Augmenter instance's ``random_state`` variable. The parameter\n        ``random_state`` takes care of both of these.\n\n        .. note::\n\n            This method exists mostly for legacy-support.\n            Overwriting :func:`~imgaug.augmenters.meta.Augmenter._augment_batch`\n            is now the preferred way of implementing custom augmentation\n            routines.\n\n        Parameters\n        ----------\n        polygons_on_images : list of imgaug.augmentables.polys.PolygonsOnImage\n            Polygons to augment. They may be changed in-place.\n\n        random_state : imgaug.random.RNG\n            The random state to use for all sampling tasks during the\n            augmentation.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_polygons`.\n\n        hooks : imgaug.imgaug.HooksKeypoints or None\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_polygons`.\n\n        Returns\n        -------\n        list of imgaug.augmentables.polys.PolygonsOnImage\n            The augmented polygons.\n\n        \"\"\"\n        return self._augment_cbaois_as_keypoints(\n            polygons_on_images,\n            random_state=random_state,\n            parents=parents,\n            hooks=hooks\n        )\n\n    def _augment_line_strings(self, line_strings_on_images, random_state,\n                              parents, hooks):\n        \"\"\"Augment a batch of line strings in-place.\n\n        This is the internal version of\n        :func:`Augmenter.augment_line_strings`.\n        It is called from :func:`Augmenter.augment_line_strings` and should\n        usually not be called directly.\n        This method may transform the line strings in-place.\n        This method does not have to care about determinism or the\n        Augmenter instance's ``random_state`` variable. The parameter\n        ``random_state`` takes care of both of these.\n\n        .. note::\n\n            This method exists mostly for legacy-support.\n            Overwriting :func:`~imgaug.augmenters.meta.Augmenter._augment_batch`\n            is now the preferred way of implementing custom augmentation\n            routines.\n\n        Parameters\n        ----------\n        line_strings_on_images : list of imgaug.augmentables.lines.LineStringsOnImage\n            Line strings to augment. They may be changed in-place.\n\n        random_state : imgaug.random.RNG\n            The random state to use for all sampling tasks during the\n            augmentation.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_line_strings`.\n\n        hooks : imgaug.imgaug.HooksKeypoints or None\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_line_strings`.\n\n        Returns\n        -------\n        list of imgaug.augmentables.lines.LineStringsOnImage\n            The augmented line strings.\n\n        \"\"\"\n        return self._augment_cbaois_as_keypoints(\n            line_strings_on_images,\n            random_state=random_state,\n            parents=parents,\n            hooks=hooks\n        )\n\n    def _augment_bounding_boxes_as_keypoints(self, bounding_boxes_on_images,\n                                             random_state, parents, hooks):\n        \"\"\"\n        Augment BBs by applying keypoint augmentation to their corners.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        bounding_boxes_on_images : list of imgaug.augmentables.bbs.BoundingBoxesOnImages or imgaug.augmentables.bbs.BoundingBoxesOnImages\n            Bounding boxes to augment. They may be changed in-place.\n\n        random_state : imgaug.random.RNG\n            The random state to use for all sampling tasks during the\n            augmentation.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_polygons`.\n\n        hooks : imgaug.imgaug.HooksKeypoints or None\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_polygons`.\n\n        Returns\n        -------\n        list of imgaug.augmentables.bbs.BoundingBoxesOnImage or imgaug.augmentables.bbs.BoundingBoxesOnImage\n            The augmented bounding boxes.\n\n        \"\"\"\n        return self._augment_cbaois_as_keypoints(bounding_boxes_on_images,\n                                                 random_state=random_state,\n                                                 parents=parents,\n                                                 hooks=hooks)\n\n    def _augment_polygons_as_keypoints(self, polygons_on_images, random_state,\n                                       parents, hooks, recoverer=None):\n        \"\"\"\n        Augment polygons by applying keypoint augmentation to their vertices.\n\n        .. warning::\n\n            This method calls\n            :func:`~imgaug.augmenters.meta.Augmenter._augment_keypoints` and\n            expects it to do keypoint augmentation. The default for that\n            method is to do nothing. It must therefore be overwritten,\n            otherwise the polygon augmentation will also do nothing.\n\n        Parameters\n        ----------\n        polygons_on_images : list of imgaug.augmentables.polys.PolygonsOnImage or imgaug.augmentables.polys.PolygonsOnImage\n            Polygons to augment. They may be changed in-place.\n\n        random_state : imgaug.random.RNG\n            The random state to use for all sampling tasks during the\n            augmentation.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_polygons`.\n\n        hooks : imgaug.imgaug.HooksKeypoints or None\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_polygons`.\n\n        recoverer : None or imgaug.augmentables.polys._ConcavePolygonRecoverer\n            An instance used to repair invalid polygons after augmentation.\n            Must offer the method\n            ``recover_from(new_exterior, old_polygon, random_state=0)``.\n            If ``None`` then invalid polygons are not repaired.\n\n        Returns\n        -------\n        list of imgaug.augmentables.polys.PolygonsOnImage or imgaug.augmentables.polys.PolygonsOnImage\n            The augmented polygons.\n\n        \"\"\"\n        func = functools.partial(self._augment_keypoints,\n                                 random_state=random_state,\n                                 parents=parents,\n                                 hooks=hooks)\n\n        return self._apply_to_polygons_as_keypoints(polygons_on_images, func,\n                                                    recoverer, random_state)\n\n    def _augment_line_strings_as_keypoints(self, line_strings_on_images,\n                                           random_state, parents, hooks):\n        \"\"\"\n        Augment BBs by applying keypoint augmentation to their corners.\n\n        Parameters\n        ----------\n        line_strings_on_images : list of imgaug.augmentables.lines.LineStringsOnImages or imgaug.augmentables.lines.LineStringsOnImages\n            Line strings to augment. They may be changed in-place.\n\n        random_state : imgaug.random.RNG\n            The random state to use for all sampling tasks during the\n            augmentation.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_polygons`.\n\n        hooks : imgaug.imgaug.HooksKeypoints or None\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_polygons`.\n\n        Returns\n        -------\n        list of imgaug.augmentables.lines.LineStringsOnImages or imgaug.augmentables.lines.LineStringsOnImages\n            The augmented line strings.\n\n        \"\"\"\n        return self._augment_cbaois_as_keypoints(line_strings_on_images,\n                                                 random_state=random_state,\n                                                 parents=parents,\n                                                 hooks=hooks)\n\n    def _augment_cbaois_as_keypoints(\n            self, cbaois, random_state, parents, hooks):\n        \"\"\"\n        Augment bounding boxes by applying KP augmentation to their corners.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        cbaois : list of imgaug.augmentables.bbs.BoundingBoxesOnImage or list of imgaug.augmentables.polys.PolygonsOnImage or list of imgaug.augmentables.lines.LineStringsOnImage or imgaug.augmentables.bbs.BoundingBoxesOnImage or imgaug.augmentables.polys.PolygonsOnImage or imgaug.augmentables.lines.LineStringsOnImage\n            Coordinate-based augmentables to augment. They may be changed\n            in-place.\n\n        random_state : imgaug.random.RNG\n            The random state to use for all sampling tasks during the\n            augmentation.\n\n        parents : list of imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_batch`.\n\n        hooks : imgaug.imgaug.HooksKeypoints or None\n            See :func:`~imgaug.augmenters.meta.Augmenter.augment_batch`.\n\n        Returns\n        -------\n        list of imgaug.augmentables.bbs.BoundingBoxesOnImage or list of imgaug.augmentables.polys.PolygonsOnImage or list of imgaug.augmentables.lines.LineStringsOnImage or imgaug.augmentables.bbs.BoundingBoxesOnImage or imgaug.augmentables.polys.PolygonsOnImage or imgaug.augmentables.lines.LineStringsOnImage\n            The augmented coordinate-based augmentables.\n\n        \"\"\"\n        func = functools.partial(self._augment_keypoints,\n                                 random_state=random_state,\n                                 parents=parents,\n                                 hooks=hooks)\n        return self._apply_to_cbaois_as_keypoints(cbaois, func)\n\n    @classmethod\n    def _apply_to_polygons_as_keypoints(cls, polygons_on_images, func,\n                                        recoverer=None, random_state=None):\n        \"\"\"\n        Apply a callback to polygons in keypoint-representation.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        polygons_on_images : list of imgaug.augmentables.polys.PolygonsOnImage or imgaug.augmentables.polys.PolygonsOnImage\n            Polygons to augment. They may be changed in-place.\n\n        func : callable\n            The function to apply. Receives a list of\n            :class:`~imgaug.augmentables.kps.KeypointsOnImage` instances as its\n            only parameter.\n\n        recoverer : None or imgaug.augmentables.polys._ConcavePolygonRecoverer\n            An instance used to repair invalid polygons after augmentation.\n            Must offer the method\n            ``recover_from(new_exterior, old_polygon, random_state=0)``.\n            If ``None`` then invalid polygons are not repaired.\n\n        random_state : None or imgaug.random.RNG\n            The random state to use for the recoverer.\n\n        Returns\n        -------\n        list of imgaug.augmentables.polys.PolygonsOnImage or imgaug.augmentables.polys.PolygonsOnImage\n            The augmented polygons.\n\n        \"\"\"\n        from ..augmentables.polys import recover_psois_\n\n        psois_orig = None\n        if recoverer is not None:\n            if isinstance(polygons_on_images, list):\n                psois_orig = [psoi.deepcopy() for psoi in polygons_on_images]\n            else:\n                psois_orig = polygons_on_images.deepcopy()\n\n        psois = cls._apply_to_cbaois_as_keypoints(polygons_on_images, func)\n\n        if recoverer is None:\n            return psois\n\n        # Its not really necessary to create an RNG copy for the recoverer\n        # here, as the augmentation of the polygons is already finished and\n        # used the same samples as the image augmentation. The recoverer might\n        # advance the RNG state, but the next call to e.g. augment() will then\n        # still use the same (advanced) RNG state for images and polygons.\n        # We copy here anyways as it seems cleaner.\n        random_state_recoverer = (random_state.copy()\n                                  if random_state is not None else None)\n        psois = recover_psois_(psois, psois_orig, recoverer,\n                               random_state_recoverer)\n\n        return psois\n\n    @classmethod\n    def _apply_to_cbaois_as_keypoints(cls, cbaois, func):\n        \"\"\"\n        Augment bounding boxes by applying KP augmentation to their corners.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        cbaois : list of imgaug.augmentables.bbs.BoundingBoxesOnImage or list of imgaug.augmentables.polys.PolygonsOnImage or list of imgaug.augmentables.lines.LineStringsOnImage or imgaug.augmentables.bbs.BoundingBoxesOnImage or imgaug.augmentables.polys.PolygonsOnImage or imgaug.augmentables.lines.LineStringsOnImage\n            Coordinate-based augmentables to augment. They may be changed\n            in-place.\n\n        func : callable\n            The function to apply. Receives a list of\n            :class:`~imgaug.augmentables.kps.KeypointsOnImage` instances as its\n            only parameter.\n\n        Returns\n        -------\n        list of imgaug.augmentables.bbs.BoundingBoxesOnImage or list of imgaug.augmentables.polys.PolygonsOnImage or list of imgaug.augmentables.lines.LineStringsOnImage or imgaug.augmentables.bbs.BoundingBoxesOnImage or imgaug.augmentables.polys.PolygonsOnImage or imgaug.augmentables.lines.LineStringsOnImage\n            The augmented coordinate-based augmentables.\n\n        \"\"\"\n        from ..augmentables.utils import (convert_cbaois_to_kpsois,\n                                          invert_convert_cbaois_to_kpsois_)\n\n        kpsois = convert_cbaois_to_kpsois(cbaois)\n        kpsois_aug = func(kpsois)\n        return invert_convert_cbaois_to_kpsois_(cbaois, kpsois_aug)\n\n    def augment(self, return_batch=False, hooks=None, **kwargs):\n        \"\"\"Augment a batch.\n\n        This method is a wrapper around\n        :class:`~imgaug.augmentables.batches.UnnormalizedBatch` and\n        :func:`~imgaug.augmenters.meta.Augmenter.augment_batch`. Hence, it\n        supports the same datatypes as\n        :class:`~imgaug.augmentables.batches.UnnormalizedBatch`.\n\n        If `return_batch` was set to ``False`` (the default), the method will\n        return a tuple of augmentables. It will return the same types of\n        augmentables (but in augmented form) as input into the method. This\n        behaviour is partly specific to the python version:\n\n        * In **python 3.6+** (if ``return_batch=False``):\n\n            * Any number of augmentables may be provided as input.\n            * None of the provided named arguments *has to be* `image` or\n              `images` (but of coarse you *may* provide them).\n            * The return order matches the order of the named arguments, e.g.\n              ``x_aug, y_aug, z_aug = augment(X=x, Y=y, Z=z)``.\n\n        * In **python <3.6** (if ``return_batch=False``):\n\n            * One or two augmentables may be used as input, not more than that.\n            * One of the input arguments has to be `image` or `images`.\n            * The augmented images are *always* returned first, independent\n              of the input argument order, e.g.\n              ``a_aug, b_aug = augment(b=b, images=a)``. This also means\n              that the output of the function can only be one of the\n              following three cases: a batch, list/array of images,\n              tuple of images and something (like images + segmentation maps).\n\n        If `return_batch` was set to ``True``, an instance of\n        :class:`~imgaug.augmentables.batches.UnnormalizedBatch` will be\n        returned. The output is the same for all python version and any\n        number or combination of augmentables may be provided.\n\n        So, to keep code downward compatible for python <3.6, use one of the\n        following three options:\n\n          * Use ``batch = augment(images=X, ..., return_batch=True)``.\n          * Call ``images = augment(images=X)``.\n          * Call ``images, other = augment(images=X, <something_else>=Y)``.\n\n        All augmentables must be provided as named arguments.\n        E.g. ``augment(<array>)`` will crash, but ``augment(images=<array>)``\n        will work.\n\n        Parameters\n        ----------\n        image : None or (H,W,C) ndarray or (H,W) ndarray, optional\n            The image to augment. Only this or `images` can be set, not both.\n            If `return_batch` is ``False`` and the python version is below 3.6,\n            either this or `images` **must** be provided.\n\n        images : None or (N,H,W,C) ndarray or (N,H,W) ndarray or iterable of (H,W,C) ndarray or iterable of (H,W) ndarray, optional\n            The images to augment. Only this or `image` can be set, not both.\n            If `return_batch` is ``False`` and the python version is below 3.6,\n            either this or `image` **must** be provided.\n\n        heatmaps : None or (N,H,W,C) ndarray or imgaug.augmentables.heatmaps.HeatmapsOnImage or iterable of (H,W,C) ndarray or iterable of imgaug.augmentables.heatmaps.HeatmapsOnImage, optional\n            The heatmaps to augment.\n            If anything else than\n            :class:`~imgaug.augmentables.heatmaps.HeatmapsOnImage`, then the\n            number of heatmaps must match the number of images provided via\n            parameter `images`. The number is contained either in ``N`` or the\n            first iterable's size.\n\n        segmentation_maps : None or (N,H,W) ndarray or imgaug.augmentables.segmaps.SegmentationMapsOnImage or iterable of (H,W) ndarray or iterable of imgaug.augmentables.segmaps.SegmentationMapsOnImage, optional\n            The segmentation maps to augment.\n            If anything else than\n            :class:`~imgaug.augmentables.segmaps.SegmentationMapsOnImage`, then\n            the number of segmaps must match the number of images provided via\n            parameter `images`. The number is contained either in ``N`` or the\n            first iterable's size.\n\n        keypoints : None or list of (N,K,2) ndarray or tuple of number or imgaug.augmentables.kps.Keypoint or iterable of (K,2) ndarray or iterable of tuple of number or iterable of imgaug.augmentables.kps.Keypoint or iterable of imgaug.augmentables.kps.KeypointOnImage or iterable of iterable of tuple of number or iterable of iterable of imgaug.augmentables.kps.Keypoint, optional\n            The keypoints to augment.\n            If a tuple (or iterable(s) of tuple), then iterpreted as ``(x,y)``\n            coordinates and must hence contain two numbers.\n            A single tuple represents a single coordinate on one image, an\n            iterable of tuples the coordinates on one image and an iterable of\n            iterable of tuples the coordinates on several images. Analogous if\n            :class:`~imgaug.augmentables.kps.Keypoint` instances are used\n            instead of tuples.\n            If an ndarray, then ``N`` denotes the number of images and ``K``\n            the number of keypoints on each image.\n            If anything else than\n            :class:`~imgaug.augmentables.kps.KeypointsOnImage` is provided, then\n            the number of keypoint groups must match the number of images\n            provided via parameter `images`. The number is contained e.g. in\n            ``N`` or in case of \"iterable of iterable of tuples\" in the first\n            iterable's size.\n\n        bounding_boxes : None or (N,B,4) ndarray or tuple of number or imgaug.augmentables.bbs.BoundingBox or imgaug.augmentables.bbs.BoundingBoxesOnImage or iterable of (B,4) ndarray or iterable of tuple of number or iterable of imgaug.augmentables.bbs.BoundingBox or iterable of imgaug.augmentables.bbs.BoundingBoxesOnImage or iterable of iterable of tuple of number or iterable of iterable imgaug.augmentables.bbs.BoundingBox, optional\n            The bounding boxes to augment.\n            This is analogous to the `keypoints` parameter. However, each\n            tuple -- and also the last index in case of arrays -- has size\n            ``4``, denoting the bounding box coordinates ``x1``, ``y1``,\n            ``x2`` and ``y2``.\n\n        polygons : None or (N,#polys,#points,2) ndarray or imgaug.augmentables.polys.Polygon or imgaug.augmentables.polys.PolygonsOnImage or iterable of (#polys,#points,2) ndarray or iterable of tuple of number or iterable of imgaug.augmentables.kps.Keypoint or iterable of imgaug.augmentables.polys.Polygon or iterable of imgaug.augmentables.polys.PolygonsOnImage or iterable of iterable of (#points,2) ndarray or iterable of iterable of tuple of number or iterable of iterable of imgaug.augmentables.kps.Keypoint or iterable of iterable of imgaug.augmentables.polys.Polygon or iterable of iterable of iterable of tuple of number or iterable of iterable of iterable of tuple of imgaug.augmentables.kps.Keypoint, optional\n            The polygons to augment.\n            This is similar to the `keypoints` parameter. However, each polygon\n            may be made up of several ``(x,y) ``coordinates (three or more are\n            required for valid polygons).\n            The following datatypes will be interpreted as a single polygon on\n            a single image:\n\n              * ``imgaug.augmentables.polys.Polygon``\n              * ``iterable of tuple of number``\n              * ``iterable of imgaug.augmentables.kps.Keypoint``\n\n            The following datatypes will be interpreted as multiple polygons\n            on a single image:\n\n              * ``imgaug.augmentables.polys.PolygonsOnImage``\n              * ``iterable of imgaug.augmentables.polys.Polygon``\n              * ``iterable of iterable of tuple of number``\n              * ``iterable of iterable of imgaug.augmentables.kps.Keypoint``\n              * ``iterable of iterable of imgaug.augmentables.polys.Polygon``\n\n            The following datatypes will be interpreted as multiple polygons on\n            multiple images:\n\n              * ``(N,#polys,#points,2) ndarray``\n              * ``iterable of (#polys,#points,2) ndarray``\n              * ``iterable of iterable of (#points,2) ndarray``\n              * ``iterable of iterable of iterable of tuple of number``\n              * ``iterable of iterable of iterable of tuple of imgaug.augmentables.kps.Keypoint``\n\n        line_strings : None or (N,#lines,#points,2) ndarray or imgaug.augmentables.lines.LineString or imgaug.augmentables.lines.LineStringOnImage or iterable of (#polys,#points,2) ndarray or iterable of tuple of number or iterable of imgaug.augmentables.kps.Keypoint or iterable of imgaug.augmentables.lines.LineString or iterable of imgaug.augmentables.lines.LineStringOnImage or iterable of iterable of (#points,2) ndarray or iterable of iterable of tuple of number or iterable of iterable of imgaug.augmentables.kps.Keypoint or iterable of iterable of imgaug.augmentables.lines.LineString or iterable of iterable of iterable of tuple of number or iterable of iterable of iterable of tuple of imgaug.augmentables.kps.Keypoint, optional\n            The line strings to augment.\n            See `polygons`, which behaves similarly.\n\n        return_batch : bool, optional\n            Whether to return an instance of\n            :class:`~imgaug.augmentables.batches.UnnormalizedBatch`. If the\n            python version is below 3.6 and more than two augmentables were\n            provided (e.g. images, keypoints and polygons), then this must be\n            set to ``True``. Otherwise an error will be raised.\n\n        hooks : None or imgaug.imgaug.HooksImages, optional\n            Hooks object to dynamically interfere with the augmentation process.\n\n        Returns\n        -------\n        tuple or imgaug.augmentables.batches.UnnormalizedBatch\n            If `return_batch` was set to ``True``, a instance of\n            ``UnnormalizedBatch`` will be returned.\n            If `return_batch` was set to ``False``, a tuple of augmentables\n            will be returned, e.g. ``(augmented images, augmented keypoints)``.\n            The datatypes match the input datatypes of the corresponding named\n            arguments. In python <3.6, augmented images are always the first\n            entry in the returned tuple. In python 3.6+ the order matches the\n            order of the named arguments.\n\n        Examples\n        --------\n        >>> import numpy as np\n        >>> import imgaug as ia\n        >>> import imgaug.augmenters as iaa\n        >>> aug = iaa.Affine(rotate=(-25, 25))\n        >>> image = np.zeros((64, 64, 3), dtype=np.uint8)\n        >>> keypoints = [(10, 20), (30, 32)]  # (x,y) coordinates\n        >>> images_aug, keypoints_aug = aug.augment(\n        >>>     image=image, keypoints=keypoints)\n\n        Create a single image and a set of two keypoints on it, then\n        augment both by applying a random rotation between ``-25`` deg and\n        ``+25`` deg. The sampled rotation value is automatically aligned\n        between image and keypoints. Note that in python <3.6, augmented\n        images will always be returned first, independent of the order of\n        the named input arguments. So\n        ``keypoints_aug, images_aug = aug.augment(keypoints=keypoints,\n        image=image)`` would **not** be correct (but in python 3.6+ it would\n        be).\n\n        >>> import numpy as np\n        >>> import imgaug as ia\n        >>> import imgaug.augmenters as iaa\n        >>> from imgaug.augmentables.bbs import BoundingBox\n        >>> aug = iaa.Affine(rotate=(-25, 25))\n        >>> images = [np.zeros((64, 64, 3), dtype=np.uint8),\n        >>>           np.zeros((32, 32, 3), dtype=np.uint8)]\n        >>> keypoints = [[(10, 20), (30, 32)],  # KPs on first image\n        >>>              [(22, 10), (12, 14)]]  # KPs on second image\n        >>> bbs = [\n        >>>           [BoundingBox(x1=5, y1=5, x2=50, y2=45)],\n        >>>           [BoundingBox(x1=4, y1=6, x2=10, y2=15),\n        >>>            BoundingBox(x1=8, y1=9, x2=16, y2=30)]\n        >>>       ]  # one BB on first image, two BBs on second image\n        >>> batch_aug = aug.augment(\n        >>>     images=images, keypoints=keypoints, bounding_boxes=bbs,\n        >>>     return_batch=True)\n\n        Create two images of size ``64x64`` and ``32x32``, two sets of\n        keypoints (each containing two keypoints) and two sets of bounding\n        boxes (the first containing one bounding box, the second two bounding\n        boxes). These augmentables are then augmented by applying random\n        rotations between ``-25`` deg and ``+25`` deg to them. The rotation\n        values are sampled by image and aligned between all augmentables on\n        the same image. The method finally returns an instance of\n        :class:`~imgaug.augmentables.batches.UnnormalizedBatch` from which the\n        augmented data can be retrieved via ``batch_aug.images_aug``,\n        ``batch_aug.keypoints_aug``, and ``batch_aug.bounding_boxes_aug``.\n        In python 3.6+, `return_batch` can be kept at ``False`` and the\n        augmented data can be retrieved as\n        ``images_aug, keypoints_aug, bbs_aug = augment(...)``.\n\n        \"\"\"\n        assert ia.is_single_bool(return_batch), (\n            \"Expected boolean as argument for 'return_batch', got type %s. \"\n            \"Call augment() only with named arguments, e.g. \"\n            \"augment(images=<array>).\" % (str(type(return_batch)),))\n\n        expected_keys = [\"images\", \"heatmaps\", \"segmentation_maps\",\n                         \"keypoints\", \"bounding_boxes\", \"polygons\",\n                         \"line_strings\"]\n        expected_keys_call = [\"image\"] + expected_keys\n\n        # at least one augmentable provided?\n        assert any([key in kwargs for key in expected_keys_call]), (\n            \"Expected augment() to be called with one of the following named \"\n            \"arguments: %s. Got none of these.\" % (\n                \", \".join(expected_keys_call),))\n\n        # all keys in kwargs actually known?\n        unknown_args = [key for key in kwargs if key not in expected_keys_call]\n        assert len(unknown_args) == 0, (\n            \"Got the following unknown keyword argument(s) in augment(): %s\" % (\n                \", \".join(unknown_args)\n            ))\n\n        # normalize image=... input to images=...\n        # this is not done by Batch.to_normalized_batch()\n        if \"image\" in kwargs:\n            assert \"images\" not in kwargs, (\n                \"You may only provide the argument 'image' OR 'images' to \"\n                \"augment(), not both of them.\")\n            images = [kwargs[\"image\"]]\n            iabase._warn_on_suspicious_single_image_shape(images[0])\n        else:\n            images = kwargs.get(\"images\", None)\n            iabase._warn_on_suspicious_multi_image_shapes(images)\n\n        # Decide whether to return the final tuple in the order of the kwargs\n        # keys or the default order based on python version. Only 3.6+ uses\n        # an ordered dict implementation for kwargs.\n        order = \"standard\"\n        nb_keys = len(list(kwargs.keys()))\n        vinfo = sys.version_info\n        is_py36_or_newer = vinfo[0] > 3 or (vinfo[0] == 3 and vinfo[1] >= 6)\n        if is_py36_or_newer:\n            order = \"kwargs_keys\"\n        elif not return_batch and nb_keys > 2:\n            raise ValueError(\n                \"Requested more than two outputs in augment(), but detected \"\n                \"python version is below 3.6. More than two outputs are only \"\n                \"supported for 3.6+ as earlier python versions offer no way \"\n                \"to retrieve the order of the provided named arguments. To \"\n                \"still use more than two outputs, add 'return_batch=True' as \"\n                \"an argument and retrieve the outputs manually from the \"\n                \"returned UnnormalizedBatch instance, e.g. via \"\n                \"'batch.images_aug' to get augmented images.\"\n            )\n        elif not return_batch and nb_keys == 2 and images is None:\n            raise ValueError(\n                \"Requested two outputs from augment() that were not 'images', \"\n                \"but detected python version is below 3.6. For security \"\n                \"reasons, only single-output requests or requests with two \"\n                \"outputs of which one is 'images' are allowed in <3.6. \"\n                \"'images' will then always be returned first. If you don't \"\n                \"want this, use 'return_batch=True' mode in augment(), which \"\n                \"returns a single UnnormalizedBatch instance instead and \"\n                \"supports any combination of outputs.\"\n            )\n\n        # augment batch\n        batch = UnnormalizedBatch(\n            images=images,\n            heatmaps=kwargs.get(\"heatmaps\", None),\n            segmentation_maps=kwargs.get(\"segmentation_maps\", None),\n            keypoints=kwargs.get(\"keypoints\", None),\n            bounding_boxes=kwargs.get(\"bounding_boxes\", None),\n            polygons=kwargs.get(\"polygons\", None),\n            line_strings=kwargs.get(\"line_strings\", None)\n        )\n\n        batch_aug = self.augment_batch_(batch, hooks=hooks)\n\n        # return either batch or tuple of augmentables, depending on what\n        # was requested by user\n        if return_batch:\n            return batch_aug\n\n        result = []\n        if order == \"kwargs_keys\":\n            for key in kwargs:\n                if key == \"image\":\n                    attr = getattr(batch_aug, \"images_aug\")\n                    result.append(attr[0])\n                else:\n                    result.append(getattr(batch_aug, \"%s_aug\" % (key,)))\n        else:\n            for key in expected_keys:\n                if key == \"images\" and \"image\" in kwargs:\n                    attr = getattr(batch_aug, \"images_aug\")\n                    result.append(attr[0])\n                elif key in kwargs:\n                    result.append(getattr(batch_aug, \"%s_aug\" % (key,)))\n\n        if len(result) == 1:\n            return result[0]\n        return tuple(result)\n\n    def __call__(self, *args, **kwargs):\n        \"\"\"Alias for :func:`~imgaug.augmenters.meta.Augmenter.augment`.\"\"\"\n        return self.augment(*args, **kwargs)\n\n    def pool(self, processes=None, maxtasksperchild=None, seed=None):\n        \"\"\"Create a pool used for multicore augmentation.\n\n        Parameters\n        ----------\n        processes : None or int, optional\n            Same as in :func:`~imgaug.multicore.Pool.__init__`.\n            The number of background workers. If ``None``, the number of the\n            machine's CPU cores will be used (this counts hyperthreads as CPU\n            cores). If this is set to a negative value ``p``, then\n            ``P - abs(p)`` will be used, where ``P`` is the number of CPU\n            cores. E.g. ``-1`` would use all cores except one (this is useful\n            to e.g. reserve one core to feed batches to the GPU).\n\n        maxtasksperchild : None or int, optional\n            Same as for :func:`~imgaug.multicore.Pool.__init__`.\n            The number of tasks done per worker process before the process\n            is killed and restarted. If ``None``, worker processes will not\n            be automatically restarted.\n\n        seed : None or int, optional\n            Same as for :func:`~imgaug.multicore.Pool.__init__`.\n            The seed to use for child processes. If ``None``, a random seed\n            will be used.\n\n        Returns\n        -------\n        imgaug.multicore.Pool\n            Pool for multicore augmentation.\n\n        Examples\n        --------\n        >>> import numpy as np\n        >>> import imgaug as ia\n        >>> import imgaug.augmenters as iaa\n        >>> from imgaug.augmentables.batches import Batch\n        >>>\n        >>> aug = iaa.Add(1)\n        >>> images = np.zeros((16, 128, 128, 3), dtype=np.uint8)\n        >>> batches = [Batch(images=np.copy(images)) for _ in range(100)]\n        >>> with aug.pool(processes=-1, seed=2) as pool:\n        >>>     batches_aug = pool.map_batches(batches, chunksize=8)\n        >>> print(np.sum(batches_aug[0].images_aug[0]))\n        49152\n\n        Create ``100`` batches of empty images. Each batch contains\n        ``16`` images of size ``128x128``. The batches are then augmented on\n        all CPU cores except one (``processes=-1``). After augmentation, the\n        sum of pixel values from the first augmented image is printed.\n\n        >>> import numpy as np\n        >>> import imgaug as ia\n        >>> import imgaug.augmenters as iaa\n        >>> from imgaug.augmentables.batches import Batch\n        >>>\n        >>> aug = iaa.Add(1)\n        >>> images = np.zeros((16, 128, 128, 3), dtype=np.uint8)\n        >>> def generate_batches():\n        >>>     for _ in range(100):\n        >>>         yield Batch(images=np.copy(images))\n        >>>\n        >>> with aug.pool(processes=-1, seed=2) as pool:\n        >>>     batches_aug = pool.imap_batches(generate_batches(), chunksize=8)\n        >>>     batch_aug = next(batches_aug)\n        >>>     print(np.sum(batch_aug.images_aug[0]))\n        49152\n\n        Same as above. This time, a generator is used to generate batches\n        of images. Again, the first augmented image's sum of pixels is printed.\n\n        \"\"\"\n        import imgaug.multicore as multicore\n        return multicore.Pool(self, processes=processes,\n                              maxtasksperchild=maxtasksperchild, seed=seed)\n\n    # TODO most of the code of this function could be replaced with\n    #      ia.draw_grid()\n    # TODO add parameter for handling multiple images ((a) next to each other\n    #      in each row or (b) multiply row count by number of images and put\n    #      each one in a new row)\n    # TODO \"images\" parameter deviates from augment_images (3d array is here\n    #      treated as one 3d image, in augment_images as (N, H, W))\n    # TODO according to the docstring, this can handle (H,W) images, but not\n    #      (H,W,1)\n    def draw_grid(self, images, rows, cols):\n        \"\"\"Augment images and draw the results as a single grid-like image.\n\n        This method applies this augmenter to the provided images and returns\n        a grid image of the results. Each cell in the grid contains a single\n        augmented version of an input image.\n\n        If multiple input images are provided, the row count is multiplied by\n        the number of images and each image gets its own row.\n        E.g. for ``images = [A, B]``, ``rows=2``, ``cols=3``::\n\n            A A A\n            B B B\n            A A A\n            B B B\n\n        for ``images = [A]``, ``rows=2``, ``cols=3``::\n\n            A A A\n            A A A\n\n        Parameters\n        -------\n        images : (N,H,W,3) ndarray or (H,W,3) ndarray or (H,W) ndarray or list of (H,W,3) ndarray or list of (H,W) ndarray\n            List of images to augment and draw in the grid.\n            If a list, then each element is expected to have shape ``(H, W)``\n            or ``(H, W, 3)``. If a single array, then it is expected to have\n            shape ``(N, H, W, 3)`` or ``(H, W, 3)`` or ``(H, W)``.\n\n        rows : int\n            Number of rows in the grid.\n            If ``N`` input images are given, this value will automatically be\n            multiplied by ``N`` to create rows for each image.\n\n        cols : int\n            Number of columns in the grid.\n\n        Returns\n        -------\n        (Hg, Wg, 3) ndarray\n            The generated grid image with augmented versions of the input\n            images. Here, ``Hg`` and ``Wg`` reference the output size of the\n            grid, and *not* the sizes of the input images.\n\n        \"\"\"\n        if ia.is_np_array(images):\n            if len(images.shape) == 4:\n                images = [images[i] for i in range(images.shape[0])]\n            elif len(images.shape) == 3:\n                images = [images]\n            elif len(images.shape) == 2:\n                images = [images[:, :, np.newaxis]]\n            else:\n                raise Exception(\n                    \"Unexpected images shape, expected 2-, 3- or \"\n                    \"4-dimensional array, got shape %s.\" % (images.shape,))\n        else:\n            assert isinstance(images, list), (\n                \"Expected 'images' to be an ndarray or list of ndarrays. \"\n                \"Got %s.\" % (type(images),))\n            for i, image in enumerate(images):\n                if len(image.shape) == 3:\n                    continue\n                if len(image.shape) == 2:\n                    images[i] = image[:, :, np.newaxis]\n                else:\n                    raise Exception(\n                        \"Unexpected image shape at index %d, expected 2- or \"\n                        \"3-dimensional array, got shape %s.\" % (\n                            i, image.shape,))\n\n        det = self if self.deterministic else self.to_deterministic()\n        augs = []\n        for image in images:\n            augs.append(det.augment_images([image] * (rows * cols)))\n\n        augs_flat = list(itertools.chain(*augs))\n        cell_height = max([image.shape[0] for image in augs_flat])\n        cell_width = max([image.shape[1] for image in augs_flat])\n        width = cell_width * cols\n        height = cell_height * (rows * len(images))\n        grid = np.zeros((height, width, 3), dtype=augs[0][0].dtype)\n        for row_idx in range(rows):\n            for img_idx, image in enumerate(images):\n                for col_idx in range(cols):\n                    image_aug = augs[img_idx][(row_idx * cols) + col_idx]\n                    cell_y1 = cell_height * (row_idx * len(images) + img_idx)\n                    cell_y2 = cell_y1 + image_aug.shape[0]\n                    cell_x1 = cell_width * col_idx\n                    cell_x2 = cell_x1 + image_aug.shape[1]\n                    grid[cell_y1:cell_y2, cell_x1:cell_x2, :] = image_aug\n\n        return grid\n\n    # TODO test for 2D images\n    # TODO test with C = 1\n    def show_grid(self, images, rows, cols):\n        \"\"\"Augment images and plot the results as a single grid-like image.\n\n        This calls :func:`~imgaug.augmenters.meta.Augmenter.draw_grid` and\n        simply shows the results. See that method for details.\n\n        Parameters\n        ----------\n        images : (N,H,W,3) ndarray or (H,W,3) ndarray or (H,W) ndarray or list of (H,W,3) ndarray or list of (H,W) ndarray\n            List of images to augment and draw in the grid.\n            If a list, then each element is expected to have shape ``(H, W)``\n            or ``(H, W, 3)``. If a single array, then it is expected to have\n            shape ``(N, H, W, 3)`` or ``(H, W, 3)`` or ``(H, W)``.\n\n        rows : int\n            Number of rows in the grid.\n            If ``N`` input images are given, this value will automatically be\n            multiplied by ``N`` to create rows for each image.\n\n        cols : int\n            Number of columns in the grid.\n\n        \"\"\"\n        grid = self.draw_grid(images, rows, cols)\n        ia.imshow(grid)\n\n    def to_deterministic(self, n=None):\n        \"\"\"Convert this augmenter from a stochastic to a deterministic one.\n\n        A stochastic augmenter samples pseudo-random values for each parameter,\n        image and batch.\n        A deterministic augmenter also samples new values for each parameter\n        and image, but not batch. Instead, for consecutive batches it will\n        sample the same values (provided the number of images and their sizes\n        don't change).\n        From a technical perspective this means that a deterministic augmenter\n        starts each batch's augmentation with a random number generator in\n        the same state (i.e. same seed), instead of advancing that state from\n        batch to batch.\n\n        Using determinism is useful to (a) get the same augmentations for\n        two or more image batches (e.g. for stereo cameras), (b) to augment\n        images and corresponding data on them (e.g. segmentation maps or\n        bounding boxes) in the same way.\n\n        Parameters\n        ----------\n        n : None or int, optional\n            Number of deterministic augmenters to return.\n            If ``None`` then only one :class:`~imgaug.augmenters.meta.Augmenter`\n            instance will be returned.\n            If ``1`` or higher, a list containing ``n``\n            :class:`~imgaug.augmenters.meta.Augmenter` instances will be\n            returned.\n\n        Returns\n        -------\n        imgaug.augmenters.meta.Augmenter or list of imgaug.augmenters.meta.Augmenter\n            A single Augmenter object if `n` was None,\n            otherwise a list of Augmenter objects (even if `n` was ``1``).\n\n        \"\"\"\n        assert n is None or n >= 1, (\n            \"Expected 'n' to be None or >=1, got %s.\" % (n,))\n        if n is None:\n            return self.to_deterministic(1)[0]\n        return [self._to_deterministic() for _ in sm.xrange(n)]\n\n    def _to_deterministic(self):\n        \"\"\"Convert this augmenter from a stochastic to a deterministic one.\n\n        Augmenter-specific implementation of\n        :func:`~imgaug.augmenters.meta.to_deterministic`. This function is\n        expected to return a single new deterministic\n        :class:`~imgaug.augmenters.meta.Augmenter` instance of this augmenter.\n\n        Returns\n        -------\n        det : imgaug.augmenters.meta.Augmenter\n            Deterministic variation of this Augmenter object.\n\n        \"\"\"\n        aug = self.copy()\n\n        # This was changed for 0.2.8 from deriving a new random state based on\n        # the global random state to deriving it from the augmenter's local\n        # random state. This should reduce the risk that re-runs of scripts\n        # lead to different results upon small changes somewhere. It also\n        # decreases the likelihood of problems when using multiprocessing\n        # (the child processes might use the same global random state as the\n        # parent process). Note for the latter point that augment_batches()\n        # might call to_deterministic() if the batch contains multiply types\n        # of augmentables.\n        # aug.random_state = iarandom.create_random_rng()\n        aug.random_state = self.random_state.derive_rng_()\n\n        aug.deterministic = True\n        return aug\n\n    @ia.deprecated(\"imgaug.augmenters.meta.Augmenter.seed_\")\n    def reseed(self, random_state=None, deterministic_too=False):\n        \"\"\"Old name of :func:`~imgaug.augmenters.meta.Augmenter.seed_`.\n\n        Deprecated since 0.4.0.\n\n        \"\"\"\n        self.seed_(entropy=random_state, deterministic_too=deterministic_too)\n\n    # TODO mark this as in-place\n    def seed_(self, entropy=None, deterministic_too=False):\n        \"\"\"Seed this augmenter and all of its children.\n\n        This method assigns a new random number generator to the\n        augmenter and all of its children (if it has any). The new random\n        number generator is *derived* from the provided seed or RNG -- or from\n        the global random number generator if ``None`` was provided.\n        Note that as child RNGs are *derived*, they do not all use the same\n        seed.\n\n        If this augmenter or any child augmenter had a random number generator\n        that pointed to the global random state, it will automatically be\n        replaced with a local random state. This is similar to what\n        :func:`~imgaug.augmenters.meta.Augmenter.localize_random_state`\n        does.\n\n        This method is useful when augmentations are run in the\n        background (i.e. on multiple cores).\n        It should be called before sending this\n        :class:`~imgaug.augmenters.meta.Augmenter` instance to a\n        background worker or once within each worker with different seeds\n        (i.e., if ``N`` workers are used, the function should be called\n        ``N`` times). Otherwise, all background workers will\n        use the same seeds and therefore apply the same augmentations.\n        Note that :func:`Augmenter.augment_batches` and :func:`Augmenter.pool`\n        already do this automatically.\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        entropy : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n            A seed or random number generator that is used to derive new\n            random number generators for this augmenter and its children.\n            If an ``int`` is provided, it will be interpreted as a seed.\n            If ``None`` is provided, the global random number generator will\n            be used.\n\n        deterministic_too : bool, optional\n            Whether to also change the seed of an augmenter ``A``, if ``A``\n            is deterministic. This is the case both when this augmenter\n            object is ``A`` or one of its children is ``A``.\n\n        Examples\n        --------\n        >>> import imgaug.augmenters as iaa\n        >>> aug = iaa.Sequential([\n        >>>     iaa.Crop(px=(0, 10)),\n        >>>     iaa.Crop(px=(0, 10))\n        >>> ])\n        >>> aug.seed_(1)\n\n        Seed an augmentation sequence containing two crop operations. Even\n        though the same seed was used, the two operations will still sample\n        different pixel amounts to crop as the child-specific seed is merely\n        derived from the provided seed.\n\n        \"\"\"\n        assert isinstance(deterministic_too, bool), (\n            \"Expected 'deterministic_too' to be a boolean, got type %s.\" % (\n                deterministic_too))\n\n        if entropy is None:\n            random_state = iarandom.RNG.create_pseudo_random_()\n        else:\n            random_state = iarandom.RNG.create_if_not_rng_(entropy)\n\n        if not self.deterministic or deterministic_too:\n            # note that derive_rng_() (used below) advances the RNG, so\n            # child augmenters get a different RNG state\n            self.random_state = random_state.copy()\n\n        for lst in self.get_children_lists():\n            for aug in lst:\n                aug.seed_(entropy=random_state.derive_rng_(),\n                          deterministic_too=deterministic_too)\n\n    def localize_random_state(self, recursive=True):\n        \"\"\"Assign augmenter-specific RNGs to this augmenter and its children.\n\n        See :func:`Augmenter.localize_random_state_` for more details.\n\n        Parameters\n        ----------\n        recursive : bool, optional\n            See\n            :func:`~imgaug.augmenters.meta.Augmenter.localize_random_state_`.\n\n        Returns\n        -------\n        imgaug.augmenters.meta.Augmenter\n            Copy of the augmenter and its children, with localized RNGs.\n\n        \"\"\"\n        aug = self.deepcopy()\n        aug.localize_random_state_(\n            recursive=recursive\n        )\n        return aug\n\n    # TODO rename random_state -> rng\n    def localize_random_state_(self, recursive=True):\n        \"\"\"Assign augmenter-specific RNGs to this augmenter and its children.\n\n        This method iterates over this augmenter and all of its children and\n        replaces any pointer to the global RNG with a new local (i.e.\n        augmenter-specific) RNG.\n\n        A random number generator (RNG) is used for the sampling of random\n        values.\n        The global random number generator exists exactly once throughout\n        the library and is shared by many augmenters.\n        A local RNG (usually) exists within exactly one augmenter and is\n        only used by that augmenter.\n\n        Usually there is no need to change global into local RNGs.\n        The only noteworthy exceptions are\n\n            * Whenever you want to use determinism (so that the global RNG is\n              not accidentally reverted).\n            * Whenever you want to copy RNGs from one augmenter to\n              another. (Copying the global RNG would usually not be useful.\n              Copying the global RNG from augmenter A to B, then executing A\n              and then B would result in B's (global) RNG's state having\n              already changed because of A's sampling. So the samples of\n              A and B would differ.)\n\n        The case of determinism is handled automatically by\n        :func:`~imgaug.augmenters.meta.Augmenter.to_deterministic`.\n        Only when you copy RNGs (via\n        :func:`~imgaug.augmenters.meta.Augmenter.copy_random_state`),\n        you need to call this function first.\n\n        Parameters\n        ----------\n        recursive : bool, optional\n            Whether to localize the RNGs of the augmenter's children too.\n\n        Returns\n        -------\n        imgaug.augmenters.meta.Augmenter\n            Returns itself (with localized RNGs).\n\n        \"\"\"\n        if self.random_state.is_global_rng():\n            self.random_state = self.random_state.derive_rng_()\n        if recursive:\n            for lst in self.get_children_lists():\n                for child in lst:\n                    child.localize_random_state_(recursive=recursive)\n        return self\n\n    # TODO adapt random_state -> rng\n    def copy_random_state(self, source, recursive=True, matching=\"position\",\n                          matching_tolerant=True, copy_determinism=False):\n        \"\"\"Copy the RNGs from a source augmenter sequence.\n\n        Parameters\n        ----------\n        source : imgaug.augmenters.meta.Augmenter\n            See :func:`~imgaug.augmenters.meta.Augmenter.copy_random_state_`.\n\n        recursive : bool, optional\n            See :func:`~imgaug.augmenters.meta.Augmenter.copy_random_state_`.\n\n        matching : {'position', 'name'}, optional\n            See :func:`~imgaug.augmenters.meta.Augmenter.copy_random_state_`.\n\n        matching_tolerant : bool, optional\n            See :func:`~imgaug.augmenters.meta.Augmenter.copy_random_state_`.\n\n        copy_determinism : bool, optional\n            See :func:`~imgaug.augmenters.meta.Augmenter.copy_random_state_`.\n\n        Returns\n        -------\n        imgaug.augmenters.meta.Augmenter\n            Copy of the augmenter itself (with copied RNGs).\n\n        \"\"\"\n        aug = self.deepcopy()\n        aug.copy_random_state_(\n            source,\n            recursive=recursive,\n            matching=matching,\n            matching_tolerant=matching_tolerant,\n            copy_determinism=copy_determinism\n        )\n        return aug\n\n    def copy_random_state_(self, source, recursive=True, matching=\"position\",\n                           matching_tolerant=True, copy_determinism=False):\n        \"\"\"Copy the RNGs from a source augmenter sequence (in-place).\n\n        .. note::\n\n            The source augmenters are not allowed to use the global RNG.\n            Call\n            :func:`~imgaug.augmenters.meta.Augmenter.localize_random_state_`\n            once on the source to localize all random states.\n\n        Parameters\n        ----------\n        source : imgaug.augmenters.meta.Augmenter\n            The source augmenter(s) from where to copy the RNG(s).\n            The source may have children (e.g. the source can be a\n            :class:`~imgaug.augmenters.meta.Sequential`).\n\n        recursive : bool, optional\n            Whether to copy the RNGs of the source augmenter *and*\n            all of its children (``True``) or just the source\n            augmenter (``False``).\n\n        matching : {'position', 'name'}, optional\n            Defines the matching mode to use during recursive copy.\n            This is used to associate source augmenters with target augmenters.\n            If ``position`` then the target and source sequences of augmenters\n            are turned into flattened lists and are associated based on\n            their list indices. If ``name`` then the target and source\n            augmenters are matched based on their names (i.e.\n            ``augmenter.name``).\n\n        matching_tolerant : bool, optional\n            Whether to use tolerant matching between source and target\n            augmenters. If set to ``False``: Name matching will raise an\n            exception for any target augmenter which's name does not appear\n            among the source augmenters. Position matching will raise an\n            exception if source and target augmenter have an unequal number\n            of children.\n\n        copy_determinism : bool, optional\n            Whether to copy the ``deterministic`` attributes from source to\n            target augmenters too.\n\n        Returns\n        -------\n        imgaug.augmenters.meta.Augmenter\n            The augmenter itself.\n\n        \"\"\"\n        # Note: the target random states are localized, but the source random\n        # states don't have to be localized. That means that they can be\n        # the global random state. Worse, if copy_random_state() was called,\n        # the target random states would have different identities, but\n        # same states. If multiple target random states were the global random\n        # state, then after deepcopying them, they would all share the same\n        # identity that is different to the global random state. I.e., if the\n        # state of any random state of them is set in-place, it modifies the\n        # state of all other target random states (that were once global),\n        # but not the global random state.\n        # Summary: Use target = source.copy() here, instead of\n        # target.use_state_of_(source).\n\n        source_augs = (\n            [source] + source.get_all_children(flat=True)\n            if recursive\n            else [source])\n        target_augs = (\n            [self] + self.get_all_children(flat=True)\n            if recursive\n            else [self])\n\n        global_rs_exc_msg = (\n            \"You called copy_random_state_() with a source that uses global \"\n            \"RNGs. Call localize_random_state_() on the source \"\n            \"first or initialize your augmenters with local random states, \"\n            \"e.g. via Dropout(..., random_state=1234).\")\n\n        if matching == \"name\":\n            source_augs_dict = {aug.name: aug for aug in source_augs}\n            target_augs_dict = {aug.name: aug for aug in target_augs}\n\n            different_lengths = (\n                len(source_augs_dict) < len(source_augs)\n                or len(target_augs_dict) < len(target_augs))\n            if different_lengths:\n                ia.warn(\n                    \"Matching mode 'name' with recursive=True was chosen in \"\n                    \"copy_random_state_, but either the source or target \"\n                    \"augmentation sequence contains multiple augmenters with \"\n                    \"the same name.\"\n                )\n\n            for name in target_augs_dict:\n                if name in source_augs_dict:\n                    if source_augs_dict[name].random_state.is_global_rng():\n                        raise Exception(global_rs_exc_msg)\n                    # has to be copy(), see above\n                    target_augs_dict[name].random_state = \\\n                        source_augs_dict[name].random_state.copy()\n                    if copy_determinism:\n                        target_augs_dict[name].deterministic = \\\n                            source_augs_dict[name].deterministic\n                elif not matching_tolerant:\n                    raise Exception(\n                        \"Augmenter name '%s' not found among source \"\n                        \"augmenters.\" % (name,))\n        elif matching == \"position\":\n            if len(source_augs) != len(target_augs) and not matching_tolerant:\n                raise Exception(\n                    \"Source and target augmentation sequences have different \"\n                    \"lengths.\")\n            for source_aug, target_aug in zip(source_augs, target_augs):\n                if source_aug.random_state.is_global_rng():\n                    raise Exception(global_rs_exc_msg)\n                # has to be copy(), see above\n                target_aug.random_state = source_aug.random_state.copy()\n                if copy_determinism:\n                    target_aug.deterministic = source_aug.deterministic\n        else:\n            raise Exception(\n                \"Unknown matching method '%s'. Valid options are 'name' \"\n                \"and 'position'.\" % (matching,))\n\n        return self\n\n    @abstractmethod\n    def get_parameters(self):\n        \"\"\"Get the parameters of this augmenter.\n\n        Returns\n        -------\n        list\n            List of parameters of arbitrary types (usually child class\n            of :class:`~imgaug.parameters.StochasticParameter`, but not\n            guaranteed to be).\n\n        \"\"\"\n        raise NotImplementedError()\n\n    def get_children_lists(self):\n        \"\"\"Get a list of lists of children of this augmenter.\n\n        For most augmenters, the result will be a single empty list.\n        For augmenters with children it will often be a list with one\n        sublist containing all children. In some cases the augmenter will\n        contain multiple distinct lists of children, e.g. an if-list and an\n        else-list. This will lead to a result consisting of a single list\n        with multiple sublists, each representing the respective sublist of\n        children.\n\n        E.g. for an if/else-augmenter that executes the children ``A1``,\n        ``A2`` if a condition is met and otherwise executes the children\n        ``B1``, ``B2``, ``B3`` the result will be\n        ``[[A1, A2], [B1, B2, B3]]``.\n\n        IMPORTANT: While the topmost list may be newly created, each of the\n        sublist must be editable inplace resulting in a changed children list\n        of the augmenter. E.g. if an Augmenter\n        ``IfElse(condition, [A1, A2], [B1, B2, B3])`` returns\n        ``[[A1, A2], [B1, B2, B3]]``\n        for a call to\n        :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists` and\n        ``A2`` is removed inplace from ``[A1, A2]``, then the children lists\n        of ``IfElse(...)`` must also change to ``[A1], [B1, B2, B3]``. This\n        is used in\n        :func:`~imgaug.augmeneters.meta.Augmenter.remove_augmenters_`.\n\n        Returns\n        -------\n        list of list of imgaug.augmenters.meta.Augmenter\n            One or more lists of child augmenter.\n            Can also be a single empty list.\n\n        \"\"\"\n        return []\n\n    # TODO why does this exist? it seems to be identical to\n    #      get_children_lists() for flat=False, aside from returning list\n    #      copies instead of the same instances as used by the augmenters.\n    # TODO this can be simplified using imgaug.imgaug.flatten()?\n    def get_all_children(self, flat=False):\n        \"\"\"Get all children of this augmenter as a list.\n\n        If the augmenter has no children, the returned list is empty.\n\n        Parameters\n        ----------\n        flat : bool\n            If set to ``True``, the returned list will be flat.\n\n        Returns\n        -------\n        list of imgaug.augmenters.meta.Augmenter\n            The children as a nested or flat list.\n\n        \"\"\"\n        result = []\n        for lst in self.get_children_lists():\n            for aug in lst:\n                result.append(aug)\n                children = aug.get_all_children(flat=flat)\n                if len(children) > 0:\n                    if flat:\n                        result.extend(children)\n                    else:\n                        result.append(children)\n        return result\n\n    def find_augmenters(self, func, parents=None, flat=True):\n        \"\"\"Find augmenters that match a condition.\n\n        This function will compare this augmenter and all of its children\n        with a condition. The condition is a lambda function.\n\n        Parameters\n        ----------\n        func : callable\n            A function that receives a\n            :class:`~imgaug.augmenters.meta.Augmenter` instance and a list of\n            parent :class:`~imgaug.augmenters.meta.Augmenter` instances and\n            must return ``True``, if that augmenter is valid match or\n            ``False`` otherwise.\n\n        parents : None or list of imgaug.augmenters.meta.Augmenter, optional\n            List of parent augmenters.\n            Intended for nested calls and can usually be left as ``None``.\n\n        flat : bool, optional\n            Whether to return the result as a flat list (``True``)\n            or a nested list (``False``). In the latter case, the nesting\n            matches each augmenters position among the children.\n\n        Returns\n        ----------\n        list of imgaug.augmenters.meta.Augmenter\n            Nested list if `flat` was set to ``False``.\n            Flat list if `flat` was set to ``True``.\n\n        Examples\n        --------\n        >>> import imgaug.augmenters as iaa\n        >>> aug = iaa.Sequential([\n        >>>     iaa.Fliplr(0.5, name=\"fliplr\"),\n        >>>     iaa.Flipud(0.5, name=\"flipud\")\n        >>> ])\n        >>> print(aug.find_augmenters(lambda a, parents: a.name == \"fliplr\"))\n\n        Return the first child augmenter (``Fliplr`` instance).\n\n        \"\"\"\n        if parents is None:\n            parents = []\n\n        result = []\n        if func(self, parents):\n            result.append(self)\n\n        subparents = parents + [self]\n        for lst in self.get_children_lists():\n            for aug in lst:\n                found = aug.find_augmenters(func, parents=subparents,\n                                            flat=flat)\n                if len(found) > 0:\n                    if flat:\n                        result.extend(found)\n                    else:\n                        result.append(found)\n        return result\n\n    def find_augmenters_by_name(self, name, regex=False, flat=True):\n        \"\"\"Find augmenter(s) by name.\n\n        Parameters\n        ----------\n        name : str\n            Name of the augmenter(s) to search for.\n\n        regex : bool, optional\n            Whether `name` parameter is a regular expression.\n\n        flat : bool, optional\n            See :func:`~imgaug.augmenters.meta.Augmenter.find_augmenters`.\n\n        Returns\n        -------\n        augmenters : list of imgaug.augmenters.meta.Augmenter\n            Nested list if `flat` was set to ``False``.\n            Flat list if `flat` was set to ``True``.\n\n        \"\"\"\n        return self.find_augmenters_by_names([name], regex=regex, flat=flat)\n\n    def find_augmenters_by_names(self, names, regex=False, flat=True):\n        \"\"\"Find augmenter(s) by names.\n\n        Parameters\n        ----------\n        names : list of str\n            Names of the augmenter(s) to search for.\n\n        regex : bool, optional\n            Whether `names` is a list of regular expressions.\n            If it is, an augmenter is considered a match if *at least* one\n            of these expressions is a match.\n\n        flat : boolean, optional\n            See :func:`~imgaug.augmenters.meta.Augmenter.find_augmenters`.\n\n        Returns\n        -------\n        augmenters : list of imgaug.augmenters.meta.Augmenter\n            Nested list if `flat` was set to ``False``.\n            Flat list if `flat` was set to ``True``.\n\n        \"\"\"\n        if regex:\n            def comparer(aug, _parents):\n                for pattern in names:\n                    if re.match(pattern, aug.name):\n                        return True\n                return False\n\n            return self.find_augmenters(comparer, flat=flat)\n        return self.find_augmenters(\n            lambda aug, parents: aug.name in names, flat=flat)\n\n    # TODO remove copy arg\n    # TODO allow first arg to be string name, class type or func\n    def remove_augmenters(self, func, copy=True, identity_if_topmost=True,\n                          noop_if_topmost=None):\n        \"\"\"Remove this augmenter or children that match a condition.\n\n        Parameters\n        ----------\n        func : callable\n            Condition to match per augmenter.\n            The function must expect the augmenter itself and a list of parent\n            augmenters and returns ``True`` if that augmenter is supposed to\n            be removed, or ``False`` otherwise.\n            E.g. ``lambda a, parents: a.name == \"fliplr\" and len(parents) == 1``\n            removes an augmenter with name ``fliplr`` if it is the direct child\n            of the augmenter upon which ``remove_augmenters()`` was initially\n            called.\n\n        copy : bool, optional\n            Whether to copy this augmenter and all if its children before\n            removing. If ``False``, removal is performed in-place.\n\n        identity_if_topmost : bool, optional\n            If ``True`` and the condition (lambda function) leads to the\n            removal of the topmost augmenter (the one this function is called\n            on initially), then that topmost augmenter will be replaced by an\n            instance of :class:`~imgaug.augmenters.meta.Noop` (i.e. an\n            augmenter that doesn't change its inputs). If ``False``, ``None``\n            will be returned in these cases.\n            This can only be ``False`` if copy is set to ``True``.\n\n        noop_if_topmost : bool, optional\n            Deprecated since 0.4.0.\n\n        Returns\n        -------\n        imgaug.augmenters.meta.Augmenter or None\n            This augmenter after the removal was performed.\n            ``None`` is returned if the condition was matched for the\n            topmost augmenter, `copy` was set to ``True`` and `noop_if_topmost`\n            was set to ``False``.\n\n        Examples\n        --------\n        >>> import imgaug.augmenters as iaa\n        >>> seq = iaa.Sequential([\n        >>>     iaa.Fliplr(0.5, name=\"fliplr\"),\n        >>>     iaa.Flipud(0.5, name=\"flipud\"),\n        >>> ])\n        >>> seq = seq.remove_augmenters(lambda a, parents: a.name == \"fliplr\")\n\n        This removes the augmenter ``Fliplr`` from the ``Sequential``\n        object's children.\n\n        \"\"\"\n        if noop_if_topmost is not None:\n            ia.warn_deprecated(\"Parameter 'noop_if_topmost' is deprecated. \"\n                               \"Use 'identity_if_topmost' instead.\")\n            identity_if_topmost = noop_if_topmost\n\n        if func(self, []):\n            if not copy:\n                raise Exception(\n                    \"Inplace removal of topmost augmenter requested, \"\n                    \"which is currently not possible. Set 'copy' to True.\")\n\n            if identity_if_topmost:\n                return Identity()\n            return None\n\n        aug = self if not copy else self.deepcopy()\n        aug.remove_augmenters_(func, parents=[])\n        return aug\n\n    @ia.deprecated(\"remove_augmenters_\")\n    def remove_augmenters_inplace(self, func, parents=None):\n        \"\"\"Old name for :func:`~imgaug.meta.Augmenter.remove_augmenters_`.\n\n        Deprecated since 0.4.0.\n\n        \"\"\"\n        self.remove_augmenters_(func=func, parents=parents)\n\n    # TODO allow first arg to be string name, class type or func\n    # TODO remove parents arg + add _remove_augmenters_() with parents arg\n    def remove_augmenters_(self, func, parents=None):\n        \"\"\"Remove in-place children of this augmenter that match a condition.\n\n        This is functionally identical to\n        :func:`~imgaug.augmenters.meta.remove_augmenters` with\n        ``copy=False``, except that it does not affect the topmost augmenter\n        (the one on which this function is initially called on).\n\n        Added in 0.4.0.\n\n        Parameters\n        ----------\n        func : callable\n            See :func:`~imgaug.augmenters.meta.Augmenter.remove_augmenters`.\n\n        parents : None or list of imgaug.augmenters.meta.Augmenter, optional\n            List of parent :class:`~imgaug.augmenters.meta.Augmenter` instances\n            that lead to this augmenter. If ``None``, an empty list will be\n            used. This parameter can usually be left empty and will be set\n            automatically for children.\n\n        Examples\n        --------\n        >>> import imgaug.augmenters as iaa\n        >>> seq = iaa.Sequential([\n        >>>     iaa.Fliplr(0.5, name=\"fliplr\"),\n        >>>    iaa.Flipud(0.5, name=\"flipud\"),\n        >>> ])\n        >>> seq.remove_augmenters_(lambda a, parents: a.name == \"fliplr\")\n\n        This removes the augmenter ``Fliplr`` from the ``Sequential``\n        object's children.\n\n        \"\"\"\n        parents = [] if parents is None else parents\n        subparents = parents + [self]\n        for lst in self.get_children_lists():\n            to_remove = []\n            for i, aug in enumerate(lst):\n                if func(aug, subparents):\n                    to_remove.append((i, aug))\n\n            for count_removed, (i, aug) in enumerate(to_remove):\n                del lst[i - count_removed]\n\n            for aug in lst:\n                aug.remove_augmenters_(func, subparents)\n\n    def copy(self):\n        \"\"\"Create a shallow copy of this Augmenter instance.\n\n        Returns\n        -------\n        imgaug.augmenters.meta.Augmenter\n            Shallow copy of this Augmenter instance.\n\n        \"\"\"\n        return copy_module.copy(self)\n\n    def deepcopy(self):\n        \"\"\"Create a deep copy of this Augmenter instance.\n\n        Returns\n        -------\n        imgaug.augmenters.meta.Augmenter\n            Deep copy of this Augmenter instance.\n\n        \"\"\"\n        # TODO if this augmenter has child augmenters and multiple of them\n        #      use the global random state, then after copying, these\n        #      augmenters share a single new random state that is a copy of\n        #      the global random state (i.e. all use the same *instance*,\n        #      not just state). This can lead to confusing bugs.\n        # TODO write a custom copying routine?\n        return copy_module.deepcopy(self)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        params = self.get_parameters()\n        params_str = \", \".join([param.__str__() for param in params])\n        return \"%s(name=%s, parameters=[%s], deterministic=%s)\" % (\n            self.__class__.__name__, self.name, params_str, self.deterministic)\n\n\nclass Sequential(Augmenter, list):\n    \"\"\"List augmenter containing child augmenters to apply to inputs.\n\n    This augmenter is simply a list of other augmenters. To augment an image\n    or any other data, it iterates over its children and applies each one\n    of them independently to the data. (This also means that the second\n    applied augmenter will already receive augmented input data and augment\n    it further.)\n\n    This augmenter offers the option to apply its children in random order\n    using the `random_order` parameter. This should often be activated as\n    it greatly increases the space of possible augmentations.\n\n    .. note::\n\n        You are *not* forced to use :class:`~imgaug.augmenters.meta.Sequential`\n        in order to use other augmenters. Each augmenter can be used on its\n        own, e.g the following defines an augmenter for horizontal flips and\n        then augments a single image:\n\n        >>> import numpy as np\n        >>> import imgaug.augmenters as iaa\n        >>> image = np.zeros((32, 32, 3), dtype=np.uint8)\n        >>> aug = iaa.Fliplr(0.5)\n        >>> image_aug = aug.augment_image(image)\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    children : imgaug.augmenters.meta.Augmenter or list of imgaug.augmenters.meta.Augmenter or None, optional\n        The augmenters to apply to images.\n\n    random_order : bool, optional\n        Whether to apply the child augmenters in random order.\n        If ``True``, the order will be randomly sampled once per batch.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import numpy as np\n    >>> import imgaug.augmenters as iaa\n    >>> imgs = [np.random.rand(10, 10)]\n    >>> seq = iaa.Sequential([\n    >>>     iaa.Fliplr(0.5),\n    >>>     iaa.Flipud(0.5)\n    >>> ])\n    >>> imgs_aug = seq.augment_images(imgs)\n\n    Create a :class:`~imgaug.augmenters.meta.Sequential` that always first\n    applies a horizontal flip augmenter and then a vertical flip augmenter.\n    Each of these two augmenters has a ``50%`` probability of actually\n    flipping the image.\n\n    >>> seq = iaa.Sequential([\n    >>>     iaa.Fliplr(0.5),\n    >>>     iaa.Flipud(0.5)\n    >>> ], random_order=True)\n    >>> imgs_aug = seq.augment_images(imgs)\n\n    Create a :class:`~imgaug.augmenters.meta.Sequential` that sometimes first\n    applies a horizontal flip augmenter (followed by a vertical flip\n    augmenter) and sometimes first a vertical flip augmenter (followed by a\n    horizontal flip augmenter). Again, each of them has a ``50%`` probability\n    of actually flipping the image.\n\n    \"\"\"\n\n    def __init__(self, children=None, random_order=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        Augmenter.__init__(\n            self,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        if children is None:\n            list.__init__(self, [])\n        elif isinstance(children, Augmenter):\n            # this must be separate from `list.__init__(self, children)`,\n            # otherwise in `Sequential(OneOf(...))` the OneOf(...) is\n            # interpreted as a list and OneOf's children become Sequential's\n            # children\n            list.__init__(self, [children])\n        elif ia.is_iterable(children):\n            assert all([isinstance(child, Augmenter) for child in children]), (\n                \"Expected all children to be augmenters, got types %s.\" % (\n                    \", \".join([str(type(v)) for v in children])))\n            list.__init__(self, children)\n        else:\n            raise Exception(\"Expected None or Augmenter or list of Augmenter, \"\n                            \"got %s.\" % (type(children),))\n\n        assert ia.is_single_bool(random_order), (\n            \"Expected random_order to be boolean, got %s.\" % (\n                type(random_order),))\n        self.random_order = random_order\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        with batch.propagation_hooks_ctx(self, hooks, parents):\n            if self.random_order:\n                order = random_state.permutation(len(self))\n            else:\n                order = sm.xrange(len(self))\n\n            for index in order:\n                batch = self[index].augment_batch_(\n                    batch,\n                    parents=parents + [self],\n                    hooks=hooks\n                )\n        return batch\n\n    def _to_deterministic(self):\n        augs = [aug.to_deterministic() for aug in self]\n        seq = self.copy()\n        seq[:] = augs\n        seq.random_state = self.random_state.derive_rng_()\n        seq.deterministic = True\n        return seq\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.random_order]\n\n    def add(self, augmenter):\n        \"\"\"Add an augmenter to the list of child augmenters.\n\n        Parameters\n        ----------\n        imgaug.augmenters.meta.Augmenter\n            The augmenter to add.\n\n        \"\"\"\n        self.append(augmenter)\n\n    def get_children_lists(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists`.\"\"\"\n        return [self]\n\n    def __str__(self):\n        augs_str = \", \".join([aug.__str__() for aug in self])\n        pattern = (\n            \"%s(\"\n            \"name=%s, random_order=%s, children=[%s], deterministic=%s\"\n            \")\")\n        return pattern % (\n            self.__class__.__name__, self.name, self.random_order, augs_str,\n            self.deterministic)\n\n\nclass SomeOf(Augmenter, list):\n    \"\"\"List augmenter that applies only some of its children to inputs.\n\n    This augmenter is similar to :class:`~imgaug.augmenters.meta.Sequential`,\n    but may apply only a fixed or random subset of its child augmenters to\n    inputs. E.g. the augmenter could be initialized with a list of 20 child\n    augmenters and then apply 5 randomly chosen child augmenters to images.\n\n    The subset of augmenters to apply (and their order) is sampled once\n    *per image*. If `random_order` is ``True``, the order will be sampled once\n    *per batch* (similar to :class:`~imgaug.augmenters.meta.Sequential`).\n\n    This augmenter currently does not support replacing (i.e. picking the same\n    child multiple times) due to implementation difficulties in connection\n    with deterministic augmenters.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    n : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or None, optional\n        Count of augmenters to apply.\n\n            * If ``int``, then exactly `n` of the child augmenters are applied\n              to every image.\n            * If tuple of two ``int`` s ``(a, b)``, then a random value will\n              be uniformly sampled per image from the discrete interval\n              ``[a..b]`` and denote the number of child augmenters to pick\n              and apply. ``b`` may be set to ``None``, which is then equivalent\n              to ``(a..C)`` with ``C`` denoting the number of children that\n              the augmenter has.\n            * If ``StochasticParameter``, then ``N`` numbers will be sampled\n              for ``N`` images. The parameter is expected to be discrete.\n            * If ``None``, then the total number of available children will be\n              used (i.e. all children will be applied).\n\n    children : imgaug.augmenters.meta.Augmenter or list of imgaug.augmenters.meta.Augmenter or None, optional\n        The augmenters to apply to images.\n        If this is a list of augmenters, it will be converted to a\n        :class:`~imgaug.augmenters.meta.Sequential`.\n\n    random_order : boolean, optional\n        Whether to apply the child augmenters in random order.\n        If ``True``, the order will be randomly sampled once per batch.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> imgs = [np.random.rand(10, 10)]\n    >>> seq = iaa.SomeOf(1, [\n    >>>     iaa.Fliplr(1.0),\n    >>>     iaa.Flipud(1.0)\n    >>> ])\n    >>> imgs_aug = seq.augment_images(imgs)\n\n    Apply either ``Fliplr`` or ``Flipud`` to images.\n\n    >>> seq = iaa.SomeOf((1, 3), [\n    >>>     iaa.Fliplr(1.0),\n    >>>     iaa.Flipud(1.0),\n    >>>     iaa.GaussianBlur(1.0)\n    >>> ])\n    >>> imgs_aug = seq.augment_images(imgs)\n\n    Apply one to three of the listed augmenters (``Fliplr``, ``Flipud``,\n    ``GaussianBlur``) to images. They are always applied in the\n    provided order, i.e. first ``Fliplr``, second ``Flipud``, third\n    ``GaussianBlur``.\n\n    >>> seq = iaa.SomeOf((1, None), [\n    >>>     iaa.Fliplr(1.0),\n    >>>     iaa.Flipud(1.0),\n    >>>     iaa.GaussianBlur(1.0)\n    >>> ], random_order=True)\n    >>> imgs_aug = seq.augment_images(imgs)\n\n    Apply one to all of the listed augmenters (``Fliplr``, ``Flipud``,\n    ``GaussianBlur``) to images. They are applied in random order, i.e.\n    sometimes ``GaussianBlur`` first, followed by ``Fliplr``, sometimes\n    ``Fliplr`` followed by ``Flipud`` followed by ``Blur`` etc.\n    The order is sampled once per batch.\n\n    \"\"\"\n\n    def __init__(self, n=None, children=None, random_order=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        Augmenter.__init__(\n            self,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        # TODO use handle_children_list() here?\n        if children is None:\n            list.__init__(self, [])\n        elif isinstance(children, Augmenter):\n            # this must be separate from `list.__init__(self, children)`,\n            # otherwise in `SomeOf(OneOf(...))` the OneOf(...) is\n            # interpreted as a list and OneOf's children become SomeOf's\n            # children\n            list.__init__(self, [children])\n        elif ia.is_iterable(children):\n            assert all([isinstance(child, Augmenter) for child in children]), (\n                \"Expected all children to be augmenters, got types %s.\" % (\n                    \", \".join([str(type(v)) for v in children])))\n            list.__init__(self, children)\n        else:\n            raise Exception(\"Expected None or Augmenter or list of Augmenter, \"\n                            \"got %s.\" % (type(children),))\n\n        self.n, self.n_mode = self._handle_arg_n(n)\n\n        assert ia.is_single_bool(random_order), (\n            \"Expected random_order to be boolean, got %s.\" % (\n                type(random_order),))\n        self.random_order = random_order\n\n    @classmethod\n    def _handle_arg_n(cls, n):\n        if ia.is_single_number(n):\n            n = int(n)\n            n_mode = \"deterministic\"\n        elif n is None:\n            n = None\n            n_mode = \"None\"\n        elif ia.is_iterable(n):\n            assert len(n) == 2, (\n                \"Expected iterable 'n' to contain exactly two values, \"\n                \"got %d.\" % (len(n),))\n            if ia.is_single_number(n[0]) and n[1] is None:\n                n = (int(n[0]), None)\n                n_mode = \"(int,None)\"\n            elif ia.is_single_number(n[0]) and ia.is_single_number(n[1]):\n                n = iap.DiscreteUniform(int(n[0]), int(n[1]))\n                n_mode = \"stochastic\"\n            else:\n                raise Exception(\"Expected tuple of (int, None) or (int, int), \"\n                                \"got %s\" % ([type(el) for el in n],))\n        elif isinstance(n, iap.StochasticParameter):\n            n_mode = \"stochastic\"\n        else:\n            raise Exception(\"Expected int, (int, None), (int, int) or \"\n                            \"StochasticParameter, got %s\" % (type(n),))\n        return n, n_mode\n\n    def _get_n(self, nb_images, random_state):\n        if self.n_mode == \"deterministic\":\n            return [self.n] * nb_images\n        if self.n_mode == \"None\":\n            return [len(self)] * nb_images\n        if self.n_mode == \"(int,None)\":\n            param = iap.DiscreteUniform(self.n[0], len(self))\n            return param.draw_samples((nb_images,), random_state=random_state)\n        if self.n_mode == \"stochastic\":\n            return self.n.draw_samples((nb_images,), random_state=random_state)\n        raise Exception(\"Invalid n_mode: %s\" % (self.n_mode,))\n\n    def _get_augmenter_order(self, random_state):\n        if not self.random_order:\n            augmenter_order = np.arange(len(self))\n        else:\n            augmenter_order = random_state.permutation(len(self))\n        return augmenter_order\n\n    def _get_augmenter_active(self, nb_rows, random_state):\n        # pylint: disable=invalid-name\n        nn = self._get_n(nb_rows, random_state)\n        nn = [min(n, len(self)) for n in nn]\n        augmenter_active = np.zeros((nb_rows, len(self)), dtype=np.bool)\n        for row_idx, n_true in enumerate(nn):\n            if n_true > 0:\n                augmenter_active[row_idx, 0:n_true] = 1\n        for row in augmenter_active:\n            random_state.shuffle(row)\n        return augmenter_active\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        with batch.propagation_hooks_ctx(self, hooks, parents):\n            # This must happen before creating the augmenter_active array,\n            # otherwise in case of determinism the number of augmented images\n            # would change the random_state's state, resulting in the order\n            # being dependent on the number of augmented images (and not be\n            # constant). By doing this first, the random state is always the\n            # same (when determinism is active), so the order is always the\n            # same.\n            augmenter_order = self._get_augmenter_order(random_state)\n\n            # create an array of active augmenters per image\n            # e.g.\n            #  [[0, 0, 1],\n            #   [1, 0, 1],\n            #   [1, 0, 0]]\n            # would signal, that augmenter 3 is active for the first image,\n            # augmenter 1 and 3 for the 2nd image and augmenter 1 for the 3rd.\n            augmenter_active = self._get_augmenter_active(batch.nb_rows,\n                                                          random_state)\n\n            for augmenter_index in augmenter_order:\n                active = augmenter_active[:, augmenter_index].nonzero()[0]\n\n                if len(active) > 0:\n                    batch_sub = batch.subselect_rows_by_indices(active)\n                    batch_sub = self[augmenter_index].augment_batch_(\n                        batch_sub,\n                        parents=parents + [self],\n                        hooks=hooks\n                    )\n                    batch = batch.invert_subselect_rows_by_indices_(active,\n                                                                    batch_sub)\n\n            return batch\n\n    def _to_deterministic(self):\n        augs = [aug.to_deterministic() for aug in self]\n        seq = self.copy()\n        seq[:] = augs\n        seq.random_state = self.random_state.derive_rng_()\n        seq.deterministic = True\n        return seq\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.n]\n\n    def add(self, augmenter):\n        \"\"\"Add an augmenter to the list of child augmenters.\n\n        Parameters\n        ----------\n        augmenter : imgaug.augmenters.meta.Augmenter\n            The augmenter to add.\n\n        \"\"\"\n        self.append(augmenter)\n\n    def get_children_lists(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists`.\"\"\"\n        return [self]\n\n    def __str__(self):\n        augs_str = \", \".join([aug.__str__() for aug in self])\n        pattern = (\n            \"%s(\"\n            \"name=%s, n=%s, random_order=%s, augmenters=[%s], deterministic=%s\"\n            \")\")\n        return pattern % (\n            self.__class__.__name__, self.name, str(self.n),\n            str(self.random_order), augs_str, self.deterministic)\n\n\nclass OneOf(SomeOf):\n    \"\"\"Augmenter that always executes exactly one of its children.\n\n    **Supported dtypes**:\n\n    See :class:`imgaug.augmenters.meta.SomeOf`.\n\n    Parameters\n    ----------\n    children : imgaug.augmenters.meta.Augmenter or list of imgaug.augmenters.meta.Augmenter\n        The choices of augmenters to apply.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> images = [np.ones((10, 10), dtype=np.uint8)]  # dummy example images\n    >>> seq = iaa.OneOf([\n    >>>     iaa.Fliplr(1.0),\n    >>>     iaa.Flipud(1.0)\n    >>> ])\n    >>> images_aug = seq.augment_images(images)\n\n    Flip each image either horizontally or vertically.\n\n    >>> images = [np.ones((10, 10), dtype=np.uint8)]  # dummy example images\n    >>> seq = iaa.OneOf([\n    >>>     iaa.Fliplr(1.0),\n    >>>     iaa.Sequential([\n    >>>         iaa.GaussianBlur(1.0),\n    >>>         iaa.Dropout(0.05),\n    >>>         iaa.AdditiveGaussianNoise(0.1*255)\n    >>>     ]),\n    >>>     iaa.Noop()\n    >>> ])\n    >>> images_aug = seq.augment_images(images)\n\n    Either flip each image horizontally, or add blur+dropout+noise or do\n    nothing.\n\n    \"\"\"\n\n    def __init__(self, children,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(OneOf, self).__init__(\n            n=1,\n            children=children,\n            random_order=False,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Sometimes(Augmenter):\n    \"\"\"Apply child augmenter(s) with a probability of `p`.\n\n    Let ``C`` be one or more child augmenters given to\n    :class:`~imgaug.augmenters.meta.Sometimes`.\n    Let ``p`` be the fraction of images (or other data) to augment.\n    Let ``I`` be the input images (or other data).\n    Let ``N`` be the number of input images (or other entities).\n    Then (on average) ``p*N`` images of ``I`` will be augmented using ``C``.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    p : float or imgaug.parameters.StochasticParameter, optional\n        Sets the probability with which the given augmenters will be applied to\n        input images/data. E.g. a value of ``0.5`` will result in ``50%`` of\n        all input images (or other augmentables) being augmented.\n\n    then_list : None or imgaug.augmenters.meta.Augmenter or list of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) to apply to `p%` percent of all images.\n        If this is a list of augmenters, it will be converted to a\n        :class:`~imgaug.augmenters.meta.Sequential`.\n\n    else_list : None or imgaug.augmenters.meta.Augmenter or list of imgaug.augmenters.meta.Augmenter, optional\n        Augmenter(s) to apply to ``(1-p)`` percent of all images.\n        These augmenters will be applied only when the ones in `then_list`\n        are *not* applied (either-or-relationship).\n        If this is a list of augmenters, it will be converted to a\n        :class:`~imgaug.augmenters.meta.Sequential`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Sometimes(0.5, iaa.GaussianBlur(0.3))\n\n    Apply ``GaussianBlur`` to ``50%`` of all input images.\n\n    >>> aug = iaa.Sometimes(0.5, iaa.GaussianBlur(0.3), iaa.Fliplr(1.0))\n\n    Apply ``GaussianBlur`` to ``50%`` of all input images. Apply ``Fliplr``\n    to the other ``50%`` of all input images.\n\n    \"\"\"\n\n    def __init__(self, p=0.5, then_list=None, else_list=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Sometimes, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.p = iap.handle_probability_param(p, \"p\")\n\n        self.then_list = handle_children_list(then_list, self.name, \"then\",\n                                              default=None)\n        self.else_list = handle_children_list(else_list, self.name, \"else\",\n                                              default=None)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        with batch.propagation_hooks_ctx(self, hooks, parents):\n            samples = self.p.draw_samples((batch.nb_rows,),\n                                          random_state=random_state)\n\n            # create lists/arrays of images for if and else lists (one for each)\n            # note that np.where returns tuple(array([0, 5, 9, ...])) or\n            # tuple(array([]))\n            indices_then_list = np.where(samples == 1)[0]\n            indices_else_list = np.where(samples == 0)[0]\n\n            indice_lists = [indices_then_list, indices_else_list]\n            augmenter_lists = [self.then_list, self.else_list]\n\n            # For then_list: collect augmentables to be processed by then_list\n            # augmenters, apply them to the list, then map back to the output\n            # list. Analogous for else_list.\n            # TODO maybe this would be easier if augment_*() accepted a list\n            #      that can contain Nones\n            for indices, augmenters in zip(indice_lists, augmenter_lists):\n                if augmenters is not None and len(augmenters) > 0:\n                    batch_sub = batch.subselect_rows_by_indices(indices)\n                    batch_sub = augmenters.augment_batch_(\n                        batch_sub,\n                        parents=parents + [self],\n                        hooks=hooks\n                    )\n                    batch = batch.invert_subselect_rows_by_indices_(indices,\n                                                                    batch_sub)\n\n            return batch\n\n    def _to_deterministic(self):\n        aug = self.copy()\n        aug.then_list = (aug.then_list.to_deterministic()\n                         if aug.then_list is not None\n                         else aug.then_list)\n        aug.else_list = (aug.else_list.to_deterministic()\n                         if aug.else_list is not None\n                         else aug.else_list)\n        aug.deterministic = True\n        aug.random_state = self.random_state.derive_rng_()\n        return aug\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.p]\n\n    def get_children_lists(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists`.\"\"\"\n        result = []\n        if self.then_list is not None:\n            result.append(self.then_list)\n        if self.else_list is not None:\n            result.append(self.else_list)\n        return result\n\n    def __str__(self):\n        pattern = (\n            \"%s(\"\n            \"p=%s, name=%s, then_list=%s, else_list=%s, deterministic=%s\"\n            \")\")\n        return pattern % (\n            self.__class__.__name__, self.p, self.name, self.then_list,\n            self.else_list, self.deterministic)\n\n\nclass WithChannels(Augmenter):\n    \"\"\"Apply child augmenters to specific channels.\n\n    Let ``C`` be one or more child augmenters given to this augmenter.\n    Let ``H`` be a list of channels.\n    Let ``I`` be the input images.\n    Then this augmenter will pick the channels ``H`` from each image\n    in ``I`` (resulting in new images) and apply ``C`` to them.\n    The result of the augmentation will be merged back into the original\n    images.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    channels : None or int or list of int, optional\n        Sets the channels to be extracted from each image.\n        If ``None``, all channels will be used. Note that this is not\n        stochastic - the extracted channels are always the same ones.\n\n    children : imgaug.augmenters.meta.Augmenter or list of imgaug.augmenters.meta.Augmenter or None, optional\n        One or more augmenters to apply to images, after the channels\n        are extracted.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.WithChannels([0], iaa.Add(10))\n\n    Assuming input images are RGB, then this augmenter will add ``10`` only to\n    the first channel, i.e. it will make images appear more red.\n\n    \"\"\"\n\n    def __init__(self, channels=None, children=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(WithChannels, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        # TODO change this to a stochastic parameter\n        if channels is None:\n            self.channels = None\n        elif ia.is_single_integer(channels):\n            self.channels = [channels]\n        elif ia.is_iterable(channels):\n            only_ints = all([\n                ia.is_single_integer(channel) for channel in channels])\n            assert only_ints, (\n                \"Expected integers as channels, got %s.\" % (\n                    [type(channel) for channel in channels],))\n            self.channels = channels\n        else:\n            raise Exception(\"Expected None, int or list of ints as channels, \"\n                            \"got %s.\" % (type(channels),))\n\n        self.children = handle_children_list(children, self.name, \"then\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if self.channels is not None and len(self.channels) == 0:\n            return batch\n\n        with batch.propagation_hooks_ctx(self, hooks, parents):\n            batch_cp = batch.deepcopy()\n\n            if batch.images is not None:\n                batch.images = self._reduce_images_to_channels(batch.images)\n\n            # Note that we augment here all data, including non-image data\n            # for which less than 50% of the corresponding image channels\n            # were augmented. This is because (a) the system does not yet\n            # understand None as cell values and (b) decreasing the length\n            # of columns leads to potential RNG misalignments.\n            # We replace non-image data that was not supposed to be augmented\n            # further below.\n            batch = self.children.augment_batch_(\n                batch, parents=parents + [self], hooks=hooks)\n\n            # If the shapes changed we cannot insert the augmented channels\n            # into the existing ones as the shapes of the non-augmented\n            # channels are still the same.\n            if batch.images is not None:\n                self._assert_lengths_not_changed(batch.images, batch_cp.images)\n                self._assert_shapes_not_changed(batch.images, batch_cp.images)\n                self._assert_dtypes_not_changed(batch.images, batch_cp.images)\n\n                batch.images = self._recover_images_array(batch.images,\n                                                          batch_cp.images)\n\n            for column in batch.columns:\n                if column.name != \"images\":\n                    value_old = getattr(batch_cp, column.attr_name)\n                    value = self._replace_unaugmented_cells(column.value,\n                                                            value_old)\n                    setattr(batch, column.attr_name, value)\n\n            if batch.images is not None:\n                batch.images = self._invert_reduce_images_to_channels(\n                    batch.images, batch_cp.images)\n\n        return batch\n\n    # Added in 0.4.0.\n    @classmethod\n    def _assert_lengths_not_changed(cls, images_aug, images):\n        assert len(images_aug) == len(images), (\n            \"Expected that number of images does not change during \"\n            \"augmentation, but got %d vs. originally %d images.\" % (\n                len(images_aug), len(images)))\n\n    # Added in 0.4.0.\n    @classmethod\n    def _assert_shapes_not_changed(cls, images_aug, images):\n        if ia.is_np_array(images_aug) and ia.is_np_array(images):\n            shapes_same = (images_aug.shape[1:3] == images.shape[1:3])\n        else:\n            shapes_same = all(\n                [image_aug.shape[0:2] == image.shape[0:2]\n                 for image_aug, image\n                 in zip(images_aug, images)])\n        assert shapes_same, (\n            \"Heights/widths of images changed in WithChannels from \"\n            \"%s to %s, but expected to be the same.\" % (\n                str([image.shape[0:2] for image in images]),\n                str([image_aug.shape[0:2] for image_aug in images_aug]),\n            ))\n\n    # Added in 0.4.0.\n    @classmethod\n    def _assert_dtypes_not_changed(cls, images_aug, images):\n        if ia.is_np_array(images_aug) and ia.is_np_array(images):\n            dtypes_same = (images_aug.dtype == images.dtype)\n        else:\n            dtypes_same = all([\n                image_aug.dtype == image.dtype\n                for image_aug, image\n                in zip(images_aug, images)\n            ])\n\n        assert dtypes_same, (\n            \"dtypes of images changed in WithChannels from \"\n            \"%s to %s, but expected to be the same.\" % (\n                str([image.dtype.name for image in images]),\n                str([image_aug.dtype.name for image_aug in images_aug]),\n            ))\n\n    # Added in 0.4.0.\n    @classmethod\n    def _recover_images_array(cls, images_aug, images):\n        if ia.is_np_array(images):\n            return np.array(images_aug)\n        return images_aug\n\n    # Added in 0.4.0.\n    def _reduce_images_to_channels(self, images):\n        if self.channels is None:\n            return images\n        if ia.is_np_array(images):\n            return images[..., self.channels]\n        return [image[..., self.channels] for image in images]\n\n    # Added in 0.4.0.\n    def _invert_reduce_images_to_channels(self, images_aug, images):\n        if self.channels is None:\n            return images_aug\n\n        for image, image_aug in zip(images, images_aug):\n            image[..., self.channels] = image_aug\n        return images\n\n    # Added in 0.4.0.\n    def _replace_unaugmented_cells(self, augmentables_aug, augmentables):\n        if self.channels is None:\n            return augmentables_aug\n\n        nb_channels_to_aug = len(self.channels)\n        nb_channels_lst = [augm.shape[2] if len(augm.shape) > 2 else 1\n                           for augm in augmentables]\n\n        # We use the augmented form of a non-image if at least 50% of the\n        # corresponding image's channels were augmented. Otherwise we use\n        # the unaugmented form.\n        fraction_augmented_lst = [nb_channels_to_aug/nb_channels\n                                  for nb_channels in nb_channels_lst]\n        result = [\n            (augmentable_aug if fraction_augmented >= 0.5 else augmentable)\n            for augmentable_aug, augmentable, fraction_augmented\n            in zip(augmentables_aug, augmentables, fraction_augmented_lst)]\n        return result\n\n    def _to_deterministic(self):\n        aug = self.copy()\n        aug.children = aug.children.to_deterministic()\n        aug.deterministic = True\n        aug.random_state = self.random_state.derive_rng_()\n        return aug\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.channels]\n\n    def get_children_lists(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists`.\"\"\"\n        return [self.children]\n\n    def __str__(self):\n        pattern = (\n            \"%s(\"\n            \"channels=%s, name=%s, children=%s, deterministic=%s\"\n            \")\")\n        return pattern % (self.__class__.__name__, self.channels, self.name,\n                          self.children, self.deterministic)\n\n\nclass Identity(Augmenter):\n    \"\"\"Augmenter that does not change the input data.\n\n    This augmenter is useful e.g. during validation/testing as it allows\n    to re-use the training code without actually performing any augmentation.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Identity()\n\n    Create an augmenter that does not change inputs.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Identity, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        return batch\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return []\n\n\nclass Noop(Identity):\n    \"\"\"Alias for augmenter :class:`Identity`.\n\n    It is recommended to now use :class:`Identity`. :class:`Noop` might be\n    deprecated in the future.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.meta.Identity`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Noop()\n\n    Create an augmenter that does not change inputs.\n\n    \"\"\"\n\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Noop, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Lambda(Augmenter):\n    \"\"\"Augmenter that calls a lambda function for each input batch.\n\n    This is useful to add missing functions to a list of augmenters.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    func_images : None or callable, optional\n        The function to call for each batch of images.\n        It must follow the form::\n\n            function(images, random_state, parents, hooks)\n\n        and return the changed images (may be transformed in-place).\n        This is essentially the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_images`.\n        If this is ``None`` instead of a function, the images will not be\n        altered.\n\n    func_heatmaps : None or callable, optional\n        The function to call for each batch of heatmaps.\n        It must follow the form::\n\n            function(heatmaps, random_state, parents, hooks)\n\n        and return the changed heatmaps (may be transformed in-place).\n        This is essentially the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_heatmaps`.\n        If this is ``None`` instead of a function, the heatmaps will not be\n        altered.\n\n    func_segmentation_maps : None or callable, optional\n        The function to call for each batch of segmentation maps.\n        It must follow the form::\n\n            function(segmaps, random_state, parents, hooks)\n\n        and return the changed segmaps (may be transformed in-place).\n        This is essentially the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_segmentation_maps`.\n        If this is ``None`` instead of a function, the segmentatio maps will\n        not be altered.\n\n    func_keypoints : None or callable, optional\n        The function to call for each batch of keypoints.\n        It must follow the form::\n\n            function(keypoints_on_images, random_state, parents, hooks)\n\n        and return the changed keypoints (may be transformed in-place).\n        This is essentially the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_keypoints`.\n        If this is ``None`` instead of a function, the keypoints will not be\n        altered.\n\n    func_bounding_boxes : \"keypoints\" or None or callable, optional\n        The function to call for each batch of bounding boxes.\n        It must follow the form::\n\n            function(bounding_boxes_on_images, random_state, parents, hooks)\n\n        and return the changed bounding boxes (may be transformed in-place).\n        This is essentially the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_bounding_boxes`.\n        If this is ``None`` instead of a function, the bounding boxes will not\n        be altered.\n        If this is the string ``\"keypoints\"`` instead of a function, the\n        bounding boxes will automatically be augmented by transforming their\n        corner vertices to keypoints and calling `func_keypoints`.\n\n        Added in 0.4.0.\n\n    func_polygons : \"keypoints\" or None or callable, optional\n        The function to call for each batch of polygons.\n        It must follow the form::\n\n            function(polygons_on_images, random_state, parents, hooks)\n\n        and return the changed polygons (may be transformed in-place).\n        This is essentially the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_polygons`.\n        If this is ``None`` instead of a function, the polygons will not\n        be altered.\n        If this is the string ``\"keypoints\"`` instead of a function, the\n        polygons will automatically be augmented by transforming their\n        corner vertices to keypoints and calling `func_keypoints`.\n\n    func_line_strings : \"keypoints\" or None or callable, optional\n        The function to call for each batch of line strings.\n        It must follow the form::\n\n            function(line_strings_on_images, random_state, parents, hooks)\n\n        and return the changed line strings (may be transformed in-place).\n        This is essentially the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_line_strings`.\n        If this is ``None`` instead of a function, the line strings will not\n        be altered.\n        If this is the string ``\"keypoints\"`` instead of a function, the\n        line strings will automatically be augmented by transforming their\n        corner vertices to keypoints and calling `func_keypoints`.\n\n        Added in 0.4.0.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>>\n    >>> def func_images(images, random_state, parents, hooks):\n    >>>     images[:, ::2, :, :] = 0\n    >>>     return images\n    >>>\n    >>> aug = iaa.Lambda(\n    >>>     func_images=func_images\n    >>> )\n\n    Replace every second row in input images with black pixels. Leave\n    other data (e.g. heatmaps, keypoints) unchanged.\n\n    >>> def func_images(images, random_state, parents, hooks):\n    >>>     images[:, ::2, :, :] = 0\n    >>>     return images\n    >>>\n    >>> def func_heatmaps(heatmaps, random_state, parents, hooks):\n    >>>     for heatmaps_i in heatmaps:\n    >>>         heatmaps.arr_0to1[::2, :, :] = 0\n    >>>     return heatmaps\n    >>>\n    >>> def func_keypoints(keypoints_on_images, random_state, parents, hooks):\n    >>>     return keypoints_on_images\n    >>>\n    >>> aug = iaa.Lambda(\n    >>>     func_images=func_images,\n    >>>     func_heatmaps=func_heatmaps,\n    >>>     func_keypoints=func_keypoints\n    >>> )\n\n    Replace every second row in images with black pixels, set every second\n    row in heatmaps to zero and leave other data (e.g. keypoints)\n    unchanged.\n\n    \"\"\"\n\n    def __init__(self, func_images=None, func_heatmaps=None,\n                 func_segmentation_maps=None, func_keypoints=None,\n                 func_bounding_boxes=\"keypoints\", func_polygons=\"keypoints\",\n                 func_line_strings=\"keypoints\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Lambda, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.func_images = func_images\n        self.func_heatmaps = func_heatmaps\n        self.func_segmentation_maps = func_segmentation_maps\n        self.func_keypoints = func_keypoints\n        self.func_bounding_boxes = func_bounding_boxes\n        self.func_polygons = func_polygons\n        self.func_line_strings = func_line_strings\n\n    def _augment_images(self, images, random_state, parents, hooks):\n        if self.func_images is not None:\n            return self.func_images(images, random_state, parents, hooks)\n        return images\n\n    def _augment_heatmaps(self, heatmaps, random_state, parents, hooks):\n        if self.func_heatmaps is not None:\n            result = self.func_heatmaps(heatmaps, random_state, parents, hooks)\n            assert ia.is_iterable(result), (\n                \"Expected callback function for heatmaps to return list of \"\n                \"imgaug.HeatmapsOnImage instances, got %s.\" % (\n                    type(result),))\n            only_heatmaps = all([\n                isinstance(el, ia.HeatmapsOnImage) for el in result])\n            assert only_heatmaps, (\n                \"Expected callback function for heatmaps to return list of \"\n                \"imgaug.HeatmapsOnImage instances, got %s.\" % (\n                    [type(el) for el in result],))\n            return result\n        return heatmaps\n\n    def _augment_segmentation_maps(self, segmaps, random_state, parents, hooks):\n        if self.func_segmentation_maps is not None:\n            result = self.func_segmentation_maps(segmaps, random_state,\n                                                 parents, hooks)\n            assert ia.is_iterable(result), (\n                \"Expected callback function for segmentation maps to return \"\n                \"list of imgaug.SegmentationMapsOnImage() instances, \"\n                \"got %s.\" % (type(result),))\n            only_segmaps = all([\n                isinstance(el, ia.SegmentationMapsOnImage) for el in result])\n            assert only_segmaps, (\n                \"Expected callback function for segmentation maps to return \"\n                \"list of imgaug.SegmentationMapsOnImage() instances, \"\n                \"got %s.\" % ([type(el) for el in result],))\n            return result\n        return segmaps\n\n    def _augment_keypoints(self, keypoints_on_images, random_state, parents,\n                           hooks):\n        if self.func_keypoints is not None:\n            result = self.func_keypoints(keypoints_on_images, random_state,\n                                         parents, hooks)\n            assert ia.is_iterable(result), (\n                \"Expected callback function for keypoints to return list of \"\n                \"imgaug.augmentables.kps.KeypointsOnImage instances, \"\n                \"got %s.\" % (type(result),))\n            only_keypoints = all([\n                isinstance(el, ia.KeypointsOnImage) for el in result])\n            assert only_keypoints, (\n                \"Expected callback function for keypoints to return list of \"\n                \"imgaug.augmentables.kps.KeypointsOnImage instances, \"\n                \"got %s.\" % ([type(el) for el in result],))\n            return result\n        return keypoints_on_images\n\n    def _augment_polygons(self, polygons_on_images, random_state, parents,\n                          hooks):\n        from imgaug.augmentables.polys import _ConcavePolygonRecoverer\n\n        if self.func_polygons == \"keypoints\":\n            return self._augment_polygons_as_keypoints(\n                polygons_on_images, random_state, parents, hooks,\n                recoverer=_ConcavePolygonRecoverer())\n        if self.func_polygons is not None:\n            result = self.func_polygons(polygons_on_images, random_state,\n                                        parents, hooks)\n            assert ia.is_iterable(result), (\n                \"Expected callback function for polygons to return list of \"\n                \"imgaug.augmentables.polys.PolygonsOnImage instances, \"\n                \"got %s.\" % (type(result),))\n            only_polygons = all([\n                isinstance(el, ia.PolygonsOnImage) for el in result])\n            assert only_polygons, (\n                \"Expected callback function for polygons to return list of \"\n                \"imgaug.augmentables.polys.PolygonsOnImage instances, \"\n                \"got %s.\" % ([type(el) for el in result],))\n            return result\n        return polygons_on_images\n\n    # Added in 0.4.0.\n    def _augment_line_strings(self, line_strings_on_images, random_state,\n                              parents, hooks):\n        if self.func_line_strings == \"keypoints\":\n            return self._augment_line_strings_as_keypoints(\n                line_strings_on_images, random_state, parents, hooks)\n        if self.func_line_strings is not None:\n            result = self.func_line_strings(line_strings_on_images,\n                                            random_state, parents, hooks)\n            assert ia.is_iterable(result), (\n                \"Expected callback function for line strings to return list of \"\n                \"imgaug.augmentables.lines.LineStringsOnImage instances, \"\n                \"got %s.\" % (type(result),))\n            only_ls = all([\n                isinstance(el, ia.LineStringsOnImage) for el in result])\n            assert only_ls, (\n                \"Expected callback function for line strings to return list of \"\n                \"imgaug.augmentables.lines.LineStringsOnImages instances, \"\n                \"got %s.\" % ([type(el) for el in result],))\n            return result\n        return line_strings_on_images\n\n    # Added in 0.4.0.\n    def _augment_bounding_boxes(self, bounding_boxes_on_images, random_state,\n                                parents, hooks):\n        if self.func_bounding_boxes == \"keypoints\":\n            return self._augment_bounding_boxes_as_keypoints(\n                bounding_boxes_on_images, random_state, parents, hooks)\n        if self.func_bounding_boxes is not None:\n            result = self.func_bounding_boxes(\n                bounding_boxes_on_images, random_state, parents, hooks)\n            assert ia.is_iterable(result), (\n                \"Expected callback function for bounding boxes to return list \"\n                \"of imgaug.augmentables.bbs.BoundingBoxesOnImage instances, \"\n                \"got %s.\" % (type(result),))\n            only_bbs = all([\n                isinstance(el, ia.BoundingBoxesOnImage) for el in result])\n            assert only_bbs, (\n                \"Expected callback function for polygons to return list of \"\n                \"imgaug.augmentables.polys.PolygonsOnImage instances, \"\n                \"got %s.\" % ([type(el) for el in result],))\n\n            for bboi in bounding_boxes_on_images:\n                for bb in bboi.bounding_boxes:\n                    if bb.x1 > bb.x2:\n                        bb.x1, bb.x2 = bb.x2, bb.x1\n                    if bb.y1 > bb.y2:\n                        bb.y1, bb.y2 = bb.y2, bb.y1\n\n            return result\n        return bounding_boxes_on_images\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return []\n\n\nclass AssertLambda(Lambda):\n    \"\"\"Assert conditions based on lambda-function to be the case for input data.\n\n    This augmenter applies a lambda function to each image or other input.\n    The lambda function must return ``True`` or ``False``. If ``False`` is\n    returned, an assertion error is produced.\n\n    This is useful to ensure that generic assumption about the input data\n    are actually the case and error out early otherwise.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    func_images : None or callable, optional\n        The function to call for each batch of images.\n        It must follow the form::\n\n            function(images, random_state, parents, hooks)\n\n        and return either ``True`` (valid input) or ``False`` (invalid input).\n        It essentially re-uses the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_images`.\n\n    func_heatmaps : None or callable, optional\n        The function to call for each batch of heatmaps.\n        It must follow the form::\n\n            function(heatmaps, random_state, parents, hooks)\n\n        and return either ``True`` (valid input) or ``False`` (invalid input).\n        It essentially re-uses the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_heatmaps`.\n\n    func_segmentation_maps : None or callable, optional\n        The function to call for each batch of segmentation maps.\n        It must follow the form::\n\n            function(segmaps, random_state, parents, hooks)\n\n        and return either ``True`` (valid input) or ``False`` (invalid input).\n        It essentially re-uses the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_segmentation_maps`.\n\n    func_keypoints : None or callable, optional\n        The function to call for each batch of keypoints.\n        It must follow the form::\n\n            function(keypoints_on_images, random_state, parents, hooks)\n\n        and return either ``True`` (valid input) or ``False`` (invalid input).\n        It essentially re-uses the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_keypoints`.\n\n    func_bounding_boxes : None or callable, optional\n        The function to call for each batch of bounding boxes.\n        It must follow the form::\n\n            function(bounding_boxes_on_images, random_state, parents, hooks)\n\n        and return either ``True`` (valid input) or ``False`` (invalid input).\n        It essentially re-uses the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_bounding_boxes`.\n\n        Added in 0.4.0.\n\n    func_polygons : None or callable, optional\n        The function to call for each batch of polygons.\n        It must follow the form::\n\n            function(polygons_on_images, random_state, parents, hooks)\n\n        and return either ``True`` (valid input) or ``False`` (invalid input).\n        It essentially re-uses the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_polygons`.\n\n    func_line_strings : None or callable, optional\n        The function to call for each batch of line strings.\n        It must follow the form::\n\n            function(line_strings_on_images, random_state, parents, hooks)\n\n        and return either ``True`` (valid input) or ``False`` (invalid input).\n        It essentially re-uses the interface of\n        :func:`~imgaug.augmenters.meta.Augmenter._augment_line_strings`.\n\n        Added in 0.4.0.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    \"\"\"\n\n    def __init__(self, func_images=None, func_heatmaps=None,\n                 func_segmentation_maps=None, func_keypoints=None,\n                 func_bounding_boxes=None, func_polygons=None,\n                 func_line_strings=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        def _default(var, augmentable_name):\n            return (\n                _AssertLambdaCallback(var, augmentable_name=augmentable_name)\n                if var is not None\n                else None\n            )\n\n        super(AssertLambda, self).__init__(\n            func_images=_default(func_images, \"images\"),\n            func_heatmaps=_default(func_heatmaps, \"heatmaps\"),\n            func_segmentation_maps=_default(func_segmentation_maps,\n                                            \"segmentation_maps\"),\n            func_keypoints=_default(func_keypoints, \"keypoints\"),\n            func_bounding_boxes=_default(func_bounding_boxes, \"bounding_boxes\"),\n            func_polygons=_default(func_polygons, \"polygons\"),\n            func_line_strings=_default(func_line_strings, \"line_strings\"),\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# Added in 0.4.0.\nclass _AssertLambdaCallback(object):\n    # Added in 0.4.0.\n    def __init__(self, func, augmentable_name):\n        self.func = func\n        self.augmentable_name = augmentable_name\n\n    # Added in 0.4.0.\n    def __call__(self, augmentables, random_state, parents, hooks):\n        assert self.func(augmentables, random_state, parents, hooks), (\n            \"Input %s did not fulfill user-defined assertion in \"\n            \"AssertLambda.\" % (self.augmentable_name,))\n        return augmentables\n\n\n# TODO add tests for segmaps\n# TODO This evaluates .shape for kps/polys, but the array shape for\n#      heatmaps/segmaps. Not very consistent.\nclass AssertShape(Lambda):\n    \"\"\"Assert that inputs have a specified shape.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    shape : tuple\n        The expected shape, given as a ``tuple``. The number of entries in\n        the ``tuple`` must match the number of dimensions, i.e. it must\n        contain four entries for ``(N, H, W, C)``. If only a single entity\n        is augmented, e.g. via\n        :func:`~imgaug.augmenters.meta.Augmenter.augment_image`, then ``N`` is\n        ``1`` in the input to this augmenter. Images that don't have\n        a channel axis will automatically have one assigned, i.e. ``C`` is\n        at least ``1``.\n        For each component of the ``tuple`` one of the following datatypes\n        may be used:\n\n            * If a component is ``None``, any value for that dimensions is\n              accepted.\n            * If a component is ``int``, exactly that value (and no other one)\n              will be accepted for that dimension.\n            * If a component is a ``tuple`` of two ``int`` s with values ``a``\n              and ``b``, only a value within the interval ``[a, b)`` will be\n              accepted for that dimension.\n            * If an entry is a ``list`` of ``int`` s, only a value from that\n              ``list`` will be accepted for that dimension.\n\n    check_images : bool, optional\n        Whether to validate input images via the given shape.\n\n    check_heatmaps : bool, optional\n        Whether to validate input heatmaps via the given shape.\n        The number of heatmaps will be verified as ``N``. For each\n        :class:`~imgaug.augmentables.heatmaps.HeatmapsOnImage` instance\n        its array's height and width will be verified as ``H`` and ``W``,\n        but not the channel count.\n\n    check_segmentation_maps : bool, optional\n        Whether to validate input segmentation maps via the given shape.\n        The number of segmentation maps will be verified as ``N``. For each\n        :class:`~imgaug.augmentables.segmaps.SegmentationMapOnImage` instance\n        its array's height and width will be verified as ``H`` and ``W``,\n        but not the channel count.\n\n    check_keypoints : bool, optional\n        Whether to validate input keypoints via the given shape.\n        This will check (a) the number of keypoints and (b) for each\n        :class:`~imgaug.augmentables.kps.KeypointsOnImage` instance the\n        ``.shape`` attribute, i.e. the shape of the corresponding image.\n\n    check_bounding_boxes : bool, optional\n        Whether to validate input bounding boxes via the given shape.\n        This will check (a) the number of bounding boxes and (b) for each\n        :class:`~imgaug.augmentables.bbs.BoundingBoxesOnImage` instance the\n        ``.shape`` attribute, i.e. the shape of the corresponding image.\n\n        Added in 0.4.0.\n\n    check_polygons : bool, optional\n        Whether to validate input polygons via the given shape.\n        This will check (a) the number of polygons and (b) for each\n        :class:`~imgaug.augmentables.polys.PolygonsOnImage` instance the\n        ``.shape`` attribute, i.e. the shape of the corresponding image.\n\n    check_line_strings : bool, optional\n        Whether to validate input line strings via the given shape.\n        This will check (a) the number of line strings and (b) for each\n        :class:`~imgaug.augmentables.lines.LineStringsOnImage` instance the\n        ``.shape`` attribute, i.e. the shape of the corresponding image.\n\n        Added in 0.4.0.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> seq = iaa.Sequential([\n    >>>     iaa.AssertShape((None, 32, 32, 3)),\n    >>>     iaa.Fliplr(0.5)\n    >>> ])\n\n    Verify first for each image batch if it contains a variable number of\n    ``32x32`` images with ``3`` channels each. Only if that check succeeds, the\n    horizontal flip will be executed. Otherwise an assertion error will be\n    raised.\n\n    >>> seq = iaa.Sequential([\n    >>>     iaa.AssertShape((None, (32, 64), 32, [1, 3])),\n    >>>     iaa.Fliplr(0.5)\n    >>> ])\n\n    Similar to the above example, but now the height may be in the interval\n    ``[32, 64)`` and the number of channels may be either ``1`` or ``3``.\n\n    \"\"\"\n\n    def __init__(self, shape, check_images=True, check_heatmaps=True,\n                 check_segmentation_maps=True, check_keypoints=True,\n                 check_bounding_boxes=True, check_polygons=True,\n                 check_line_strings=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        assert len(shape) == 4, (\n            \"Expected shape to have length 4, got %d with shape: %s.\" % (\n                len(shape), str(shape)))\n        self.shape = shape\n\n        def _default(func, do_use):\n            return func if do_use else None\n\n        super(AssertShape, self).__init__(\n            func_images=_default(_AssertShapeImagesCheck(shape),\n                                 check_images),\n            func_heatmaps=_default(_AssertShapeHeatmapsCheck(shape),\n                                   check_heatmaps),\n            func_segmentation_maps=_default(_AssertShapeSegmapCheck(shape),\n                                            check_segmentation_maps),\n            func_keypoints=_default(_AssertShapeKeypointsCheck(shape),\n                                    check_keypoints),\n            func_bounding_boxes=_default(_AssertShapeBoundingBoxesCheck(shape),\n                                         check_bounding_boxes),\n            func_polygons=_default(_AssertShapePolygonsCheck(shape),\n                                   check_polygons),\n            func_line_strings=_default(_AssertShapeLineStringsCheck(shape),\n                                       check_line_strings),\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    @classmethod\n    def _compare(cls, observed, expected, dimension, image_index):\n        if expected is not None:\n            if ia.is_single_integer(expected):\n                assert observed == expected, (\n                    \"Expected dim %d (entry index: %s) to have value %d, \"\n                    \"got %d.\" % (dimension, image_index, expected,\n                                 observed))\n            elif isinstance(expected, tuple):\n                assert len(expected) == 2, (\n                    \"Expected tuple argument 'expected' to contain \"\n                    \"exactly 2 entries, got %d.\" % (len(expected),))\n                assert expected[0] <= observed < expected[1], (\n                    \"Expected dim %d (entry index: %s) to have value in \"\n                    \"interval [%d, %d), got %d.\" % (\n                        dimension, image_index, expected[0], expected[1],\n                        observed))\n            elif isinstance(expected, list):\n                assert any([observed == val for val in expected]), (\n                    \"Expected dim %d (entry index: %s) to have any value \"\n                    \"of %s, got %d.\" % (\n                        dimension, image_index, str(expected), observed))\n            else:\n                raise Exception(\n                    \"Invalid datatype for shape entry %d, expected each \"\n                    \"entry to be an integer, a tuple (with two entries) \"\n                    \"or a list, got %s.\" % (dimension, type(expected),))\n\n    @classmethod\n    def _check_shapes(cls, shapes, shape_target):\n        if shape_target[0] is not None:\n            cls._compare(len(shapes), shape_target[0], 0, \"ALL\")\n\n        for augm_idx, shape in enumerate(shapes):\n            # note that dim_idx is here per object, dim 0 of shape target\n            # denotes \"number of all objects\" and was checked above\n            for dim_idx, expected in enumerate(shape_target[1:]):\n                observed = shape[dim_idx]\n                cls._compare(observed, expected, dim_idx, augm_idx)\n\n\n# turning these checks below into classmethods of AssertShape breaks pickling\n# in python 2.7\n# Added in 0.4.0.\nclass _AssertShapeImagesCheck(object):\n    def __init__(self, shape):\n        self.shape = shape\n\n    def __call__(self, images, _random_state, _parents, _hooks):\n        # set shape_target so that we check all target dimensions,\n        # including C, which isn't checked for the other methods\n        AssertShape._check_shapes([obj.shape for obj in images],\n                                  self.shape)\n        return images\n\n\n# Added in 0.4.0.\nclass _AssertShapeHeatmapsCheck(object):\n    def __init__(self, shape):\n        self.shape = shape\n\n    def __call__(self, heatmaps, _random_state, _parents, _hooks):\n        AssertShape._check_shapes([obj.arr_0to1.shape for obj in heatmaps],\n                                  self.shape[0:3])\n        return heatmaps\n\n\n# Added in 0.4.0.\nclass _AssertShapeSegmapCheck(object):\n    def __init__(self, shape):\n        self.shape = shape\n\n    def __call__(self, segmaps, _random_state, _parents, _hooks):\n        AssertShape._check_shapes([obj.arr.shape for obj in segmaps],\n                                  self.shape[0:3])\n        return segmaps\n\n\n# Added in 0.4.0.\nclass _AssertShapeKeypointsCheck(object):\n    def __init__(self, shape):\n        self.shape = shape\n\n    def __call__(self, keypoints_on_images, _random_state, _parents, _hooks):\n        AssertShape._check_shapes([obj.shape for obj in keypoints_on_images],\n                                  self.shape[0:3])\n        return keypoints_on_images\n\n\n# Added in 0.4.0.\nclass _AssertShapeBoundingBoxesCheck(object):\n    def __init__(self, shape):\n        self.shape = shape\n\n    def __call__(self, bounding_boxes_on_images, _random_state, _parents,\n                 _hooks):\n        AssertShape._check_shapes([obj.shape for obj\n                                   in bounding_boxes_on_images],\n                                  self.shape[0:3])\n        return bounding_boxes_on_images\n\n\n# Added in 0.4.0.\nclass _AssertShapePolygonsCheck(object):\n    def __init__(self, shape):\n        self.shape = shape\n\n    def __call__(self, polygons_on_images, _random_state, _parents, _hooks):\n        AssertShape._check_shapes([obj.shape for obj in polygons_on_images],\n                                  self.shape[0:3])\n        return polygons_on_images\n\n\n# Added in 0.4.0.\nclass _AssertShapeLineStringsCheck(object):\n    def __init__(self, shape):\n        self.shape = shape\n\n    def __call__(self, line_strings_on_images, _random_state, _parents,\n                 _hooks):\n        AssertShape._check_shapes([obj.shape for obj\n                                   in line_strings_on_images],\n                                  self.shape[0:3])\n        return line_strings_on_images\n\n\nclass ChannelShuffle(Augmenter):\n    \"\"\"Randomize the order of channels in input images.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    p : float or imgaug.parameters.StochasticParameter, optional\n        Probability of shuffling channels in any given image.\n        May be a fixed probability as a ``float``, or a\n        :class:`~imgaug.parameters.StochasticParameter` that returns ``0`` s\n        and ``1`` s.\n\n    channels : None or imgaug.ALL or list of int, optional\n        Which channels are allowed to be shuffled with each other.\n        If this is ``None`` or ``imgaug.ALL``, then all channels may be\n        shuffled. If it is a ``list`` of ``int`` s,\n        then only the channels with indices in that list may be shuffled.\n        (Values start at ``0``. All channel indices in the list must exist in\n        each image.)\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.ChannelShuffle(0.35)\n\n    Shuffle all channels of ``35%`` of all images.\n\n    >>> aug = iaa.ChannelShuffle(0.35, channels=[0, 1])\n\n    Shuffle only channels ``0`` and ``1`` of ``35%`` of all images. As the new\n    channel orders ``0, 1`` and ``1, 0`` are both valid outcomes of the\n    shuffling, it means that for ``0.35 * 0.5 = 0.175`` or ``17.5%`` of all\n    images the order of channels ``0`` and ``1`` is inverted.\n\n    \"\"\"\n\n    def __init__(self, p=1.0, channels=None,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ChannelShuffle, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.p = iap.handle_probability_param(p, \"p\")\n        valid_channels = (\n            channels is None\n            or channels == ia.ALL\n            or (\n                isinstance(channels, list)\n                and all([ia.is_single_integer(v) for v in channels])\n            ))\n        assert valid_channels, (\n            \"Expected None or imgaug.ALL or list of int, got %s.\" % (\n                type(channels),))\n        self.channels = channels\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        nb_images = len(images)\n        p_samples = self.p.draw_samples((nb_images,),\n                                        random_state=random_state)\n        rss = random_state.duplicate(nb_images)\n        for i, (image, p_i, rs) in enumerate(zip(images, p_samples, rss)):\n            if p_i >= 1-1e-4:\n                batch.images[i] = shuffle_channels(image, rs, self.channels)\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.p, self.channels]\n\n\ndef shuffle_channels(image, random_state, channels=None):\n    \"\"\"Randomize the order of (color) channels in an image.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; indirectly tested (1)\n        * ``uint32``: yes; indirectly tested (1)\n        * ``uint64``: yes; indirectly tested (1)\n        * ``int8``: yes; indirectly tested (1)\n        * ``int16``: yes; indirectly tested (1)\n        * ``int32``: yes; indirectly tested (1)\n        * ``int64``: yes; indirectly tested (1)\n        * ``float16``: yes; indirectly tested (1)\n        * ``float32``: yes; indirectly tested (1)\n        * ``float64``: yes; indirectly tested (1)\n        * ``float128``: yes; indirectly tested (1)\n        * ``bool``: yes; indirectly tested (1)\n\n        - (1) Indirectly tested via :class:`ChannelShuffle`.\n\n    Parameters\n    ----------\n    image : (H,W,[C]) ndarray\n        Image of any dtype for which to shuffle the channels.\n\n    random_state : imgaug.random.RNG\n        The random state to use for this shuffling operation.\n\n    channels : None or imgaug.ALL or list of int, optional\n        Which channels are allowed to be shuffled with each other.\n        If this is ``None`` or ``imgaug.ALL``, then all channels may be\n        shuffled. If it is a ``list`` of ``int`` s,\n        then only the channels with indices in that list may be shuffled.\n        (Values start at ``0``. All channel indices in the list must exist in\n        the image.)\n\n    Returns\n    -------\n    ndarray\n        The input image with shuffled channels.\n\n    \"\"\"\n    if image.ndim < 3 or image.shape[2] == 1:\n        return image\n    nb_channels = image.shape[2]\n    all_channels = np.arange(nb_channels)\n    is_all_channels = (\n        channels is None\n        or channels == ia.ALL\n        or len(set(all_channels).difference(set(channels))) == 0\n    )\n    if is_all_channels:\n        # note that if this is the case, then 'channels' may be None or\n        # imgaug.ALL, so don't simply move the assignment outside of the\n        # if/else\n        channels_perm = random_state.permutation(all_channels)\n        return image[..., channels_perm]\n\n    channels_perm = random_state.permutation(channels)\n    channels_perm_full = all_channels\n    for channel_source, channel_target in zip(channels, channels_perm):\n        channels_perm_full[channel_source] = channel_target\n    return image[..., channels_perm_full]\n\n\nclass RemoveCBAsByOutOfImageFraction(Augmenter):\n    \"\"\"Remove coordinate-based augmentables exceeding an out of image fraction.\n\n    This augmenter inspects all coordinate-based augmentables (e.g.\n    bounding boxes, line strings) within a given batch and removes any such\n    augmentable which's out of image fraction is exactly a given value or\n    greater than that. The out of image fraction denotes the fraction of the\n    augmentable's area that is outside of the image, e.g. for a bounding box\n    that has half of its area outside of the image it would be ``0.5``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; fully tested\n        * ``uint32``: yes; fully tested\n        * ``uint64``: yes; fully tested\n        * ``int8``: yes; fully tested\n        * ``int16``: yes; fully tested\n        * ``int32``: yes; fully tested\n        * ``int64``: yes; fully tested\n        * ``float16``: yes; fully tested\n        * ``float32``: yes; fully tested\n        * ``float64``: yes; fully tested\n        * ``float128``: yes; fully tested\n        * ``bool``: yes; fully tested\n\n    Parameters\n    ----------\n    fraction : number\n        Remove any augmentable for which ``fraction_{actual} >= fraction``,\n        where ``fraction_{actual}`` denotes the estimated out of image\n        fraction.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Sequential([\n    >>>     iaa.Affine(translate_px={\"x\": (-100, 100)}),\n    >>>     iaa.RemoveCBAsByOutOfImageFraction(0.5)\n    >>> ])\n\n    Translate all inputs by ``-100`` to ``100`` pixels on the x-axis, then\n    remove any coordinate-based augmentable (e.g. bounding boxes) which has\n    at least ``50%`` of its area outside of the image plane.\n\n    >>> import imgaug as ia\n    >>> import imgaug.augmenters as iaa\n    >>> image = ia.quokka_square((100, 100))\n    >>> bb = ia.BoundingBox(x1=50-25, y1=0, x2=50+25, y2=100)\n    >>> bbsoi = ia.BoundingBoxesOnImage([bb], shape=image.shape)\n    >>> aug_without = iaa.Affine(translate_px={\"x\": 51})\n    >>> aug_with = iaa.Sequential([\n    >>>     iaa.Affine(translate_px={\"x\": 51}),\n    >>>     iaa.RemoveCBAsByOutOfImageFraction(0.5)\n    >>> ])\n    >>>\n    >>> image_without, bbsoi_without = aug_without(\n    >>>     image=image, bounding_boxes=bbsoi)\n    >>> image_with, bbsoi_with = aug_with(\n    >>>     image=image, bounding_boxes=bbsoi)\n    >>>\n    >>> assert len(bbsoi_without.bounding_boxes) == 1\n    >>> assert len(bbsoi_with.bounding_boxes) == 0\n\n    Create a bounding box on an example image, then translate the image so that\n    ``50%`` of the bounding box's area is outside of the image and compare\n    the effects and using ``RemoveCBAsByOutOfImageFraction`` with not using it.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, fraction,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(RemoveCBAsByOutOfImageFraction, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.fraction = fraction\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        for column in batch.columns:\n            if column.name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                               \"line_strings\"]:\n                for i, cbaoi in enumerate(column.value):\n                    column.value[i] = cbaoi.remove_out_of_image_fraction_(\n                        self.fraction)\n\n        return batch\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.fraction]\n\n\nclass ClipCBAsToImagePlanes(Augmenter):\n    \"\"\"Clip coordinate-based augmentables to areas within the image plane.\n\n    This augmenter inspects all coordinate-based augmentables (e.g.\n    bounding boxes, line strings) within a given batch and from each of them\n    parts that are outside of the image plane. Parts within the image plane\n    will be retained. This may e.g. shrink down bounding boxes. For keypoints,\n    it removes any single points outside of the image plane. Any augmentable\n    that is completely outside of the image plane will be removed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; fully tested\n        * ``uint32``: yes; fully tested\n        * ``uint64``: yes; fully tested\n        * ``int8``: yes; fully tested\n        * ``int16``: yes; fully tested\n        * ``int32``: yes; fully tested\n        * ``int64``: yes; fully tested\n        * ``float16``: yes; fully tested\n        * ``float32``: yes; fully tested\n        * ``float64``: yes; fully tested\n        * ``float128``: yes; fully tested\n        * ``bool``: yes; fully tested\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Sequential([\n    >>>     iaa.Affine(translate_px={\"x\": (-100, 100)}),\n    >>>     iaa.ClipCBAsToImagePlanes()\n    >>> ])\n\n    Translate input data on the x-axis by ``-100`` to ``100`` pixels,\n    then cut all coordinate-based augmentables (e.g. bounding boxes) down\n    to areas that are within the image planes of their corresponding images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(ClipCBAsToImagePlanes, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        for column in batch.columns:\n            if column.name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                               \"line_strings\"]:\n                for i, cbaoi in enumerate(column.value):\n                    column.value[i] = cbaoi.clip_out_of_image_()\n\n        return batch\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return []\n"
  },
  {
    "path": "imgaug/augmenters/overlay.py",
    "content": "\"\"\"Alias for module blend.\n\nDeprecated module. Original name for module blend.py. Was changed in 0.2.8.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport imgaug as ia\nfrom . import blend\n\n\n_DEPRECATION_COMMENT = (\n    \"It has the same interface, except that the parameter \"\n    \"`first` was renamed to `foreground` and the parameter \"\n    \"`second` to `background`.\"\n)\n\n\n@ia.deprecated(alt_func=\"imgaug.augmenters.blend.blend_alpha()\",\n               comment=_DEPRECATION_COMMENT)\ndef blend_alpha(*args, **kwargs):\n    \"\"\"See :func:`~imgaug.augmenters.blend.blend_alpha`.\"\"\"\n    # pylint: disable=invalid-name\n    return blend.blend_alpha(*args, **kwargs)\n\n\n@ia.deprecated(alt_func=\"imgaug.augmenters.blend.BlendAlpha\",\n               comment=_DEPRECATION_COMMENT)\ndef Alpha(*args, **kwargs):\n    \"\"\"See :class:`~imgaug.augmenters.blend.BlendAlpha`.\"\"\"\n    # pylint: disable=invalid-name\n    return blend.Alpha(*args, **kwargs)\n\n\n@ia.deprecated(alt_func=\"imgaug.augmenters.blend.BlendAlphaElementwise\",\n               comment=_DEPRECATION_COMMENT)\ndef AlphaElementwise(*args, **kwargs):\n    \"\"\"See :class:`~imgaug.augmenters.blend.BlendAlphaElementwise`.\"\"\"\n    # pylint: disable=invalid-name\n    return blend.AlphaElementwise(*args, **kwargs)\n\n\n@ia.deprecated(alt_func=\"imgaug.augmenters.blend.BlendAlphaSimplexNoise\",\n               comment=_DEPRECATION_COMMENT)\ndef SimplexNoiseAlpha(*args, **kwargs):\n    \"\"\"See :class:`~imgaug.augmenters.blend.BlendAlphaSimplexNoise`.\"\"\"\n    # pylint: disable=invalid-name\n    return blend.SimplexNoiseAlpha(*args, **kwargs)\n\n\n@ia.deprecated(alt_func=\"imgaug.augmenters.blend.BlendAlphaFrequencyNoise\",\n               comment=_DEPRECATION_COMMENT)\ndef FrequencyNoiseAlpha(*args, **kwargs):\n    \"\"\"See :class:`~imgaug.augmenters.blend.BlendAlphaFrequencyNoise`.\"\"\"\n    # pylint: disable=invalid-name\n    return blend.FrequencyNoiseAlpha(*args, **kwargs)\n"
  },
  {
    "path": "imgaug/augmenters/pillike.py",
    "content": "\"\"\"\nAugmenters that have identical outputs to well-known PIL functions.\n\nThe ``like`` in ``pillike`` indicates that the augmenters in this module\nhave identical outputs and mostly identical inputs to corresponding PIL\nfunctions, but do not *have to* wrap these functions internally. They may\nuse internally different (e.g. faster) techniques to produce these outputs.\n\nSome of the augmenters in this module may also exist in other modules\nunder similar name. These other augmenters may currently have the same\noutputs as the corresponding PIL functions, but that is not guaranteed\nfor the future. Use the augmenters in this module if identical outputs\nto PIL are required.\n\nList of augmenters:\n\n    * :class:`Solarize`\n    * :class:`Posterize`\n    * :class:`Equalize`\n    * :class:`Autocontrast`\n    * :class:`EnhanceColor`\n    * :class:`EnhanceContrast`\n    * :class:`EnhanceBrightness`\n    * :class:`EnhanceSharpness`\n    * :class:`FilterBlur`\n    * :class:`FilterSmooth`\n    * :class:`FilterSmoothMore`\n    * :class:`FilterEdgeEnhance`\n    * :class:`FilterEdgeEnhanceMore`\n    * :class:`FilterFindEdges`\n    * :class:`FilterContour`\n    * :class:`FilterEmboss`\n    * :class:`FilterSharpen`\n    * :class:`FilterDetail`\n    * :class:`Affine`\n\nStandard usage of these augmenters follows roughly the schema::\n\n    import numpy as np\n    import imgaug.augmenters as iaa\n\n    aug = iaa.pillike.Affine(translate_px={\"x\": (-5, 5)})\n    image = np.full((32, 32, 3), 255, dtype=np.uint8)\n\n    images_aug = aug(images=[image, image, image])\n\nAdded in 0.4.0.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport six.moves as sm\nimport numpy as np\nimport cv2\nimport PIL.Image\nimport PIL.ImageOps\nimport PIL.ImageEnhance\nimport PIL.ImageFilter\n\nimport imgaug as ia\nfrom imgaug.imgaug import _normalize_cv2_input_arr_\nfrom . import meta\nfrom . import arithmetic\nfrom . import color as colorlib\nfrom . import contrast as contrastlib\nfrom . import geometric\nfrom . import size as sizelib\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\n\n\n# TODO some of the augmenters in this module broke on numpy arrays as\n#      image inputs (as opposed to lists of arrays) without any test failing\n#      add appropriate tests for that\n\n_EQUALIZE_USE_PIL_BELOW = 64*64  # H*W\n\n\n# Added in 0.4.0.\ndef _ensure_valid_shape(image, func_name):\n    is_hw1 = image.ndim == 3 and image.shape[-1] == 1\n    if is_hw1:\n        image = image[:, :, 0]\n    assert (\n        image.ndim == 2\n        or (image.ndim == 3 and image.shape[-1] in [3, 4])\n    ), (\n        \"Can apply %s only to images of \"\n        \"shape (H, W) or (H, W, 1) or (H, W, 3) or (H, W, 4). \"\n        \"Got shape %s.\" % (func_name, image.shape,))\n    return image, is_hw1\n\n\ndef solarize_(image, threshold=128):\n    \"\"\"Invert all array components above a threshold in-place.\n\n    This function has identical outputs to ``PIL.ImageOps.solarize``.\n    It does however work in-place.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See ``~imgaug.augmenters.arithmetic.invert_(min_value=None and max_value=None)``.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n        The array *might* be modified in-place.\n\n    threshold : int, optional\n        A threshold to use in order to invert only numbers above or below\n        the threshold.\n\n    Returns\n    -------\n    ndarray\n        Inverted image.\n        This *can* be the same array as input in `image`, modified in-place.\n\n    \"\"\"\n    return arithmetic.invert_(image, threshold=threshold)\n\n\ndef solarize(image, threshold=128):\n    \"\"\"Invert all array components above a threshold.\n\n    This function has identical outputs to ``PIL.ImageOps.solarize``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See ``~imgaug.augmenters.arithmetic.invert_(min_value=None and max_value=None)``.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n\n    threshold : int, optional\n        A threshold to use in order to invert only numbers above or below\n        the threshold.\n\n    Returns\n    -------\n    ndarray\n        Inverted image.\n\n    \"\"\"\n    return arithmetic.invert(image, threshold=threshold)\n\n\ndef posterize_(image, bits):\n    \"\"\"Reduce the number of bits for each color channel in-place.\n\n    This function has identical outputs to ``PIL.ImageOps.posterize``.\n    It does however work in-place.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.quantize_uniform_to_n_bits_`.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n\n    bits : int\n        The number of bits to keep per component.\n        Values in the interval ``[1, 8]`` are valid.\n\n    Returns\n    -------\n    ndarray\n        Posterized image.\n        This *can* be the same array as input in `image`, modified in-place.\n\n    \"\"\"\n    return colorlib.posterize(image, bits)\n\n\ndef posterize(image, bits):\n    \"\"\"Reduce the number of bits for each color channel.\n\n    This function has identical outputs to ``PIL.ImageOps.posterize``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.color.quantize_uniform_to_n_bits`.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image array of shape ``(H,W,[C])``.\n\n    bits : int\n        The number of bits to keep per component.\n        Values in the interval ``[1, 8]`` are valid.\n\n    Returns\n    -------\n    ndarray\n        Posterized image.\n\n    \"\"\"\n    return colorlib.posterize(image, bits)\n\n\ndef equalize(image, mask=None):\n    \"\"\"Equalize the image histogram.\n\n    See :func:`~imgaug.augmenters.pillike.equalize_` for details.\n\n    This function is identical in inputs and outputs to\n    ``PIL.ImageOps.equalize``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.equalize_`.\n\n    Parameters\n    ----------\n    image : ndarray\n        ``uint8`` ``(H,W,[C])`` image to equalize.\n\n    mask : None or ndarray, optional\n        An optional mask. If given, only the pixels selected by the mask are\n        included in the analysis.\n\n    Returns\n    -------\n    ndarray\n        Equalized image.\n\n    \"\"\"\n    # internally used method works in-place by default and hence needs a copy\n    size = image.size\n    if size == 0:\n        return np.copy(image)\n    if size >= _EQUALIZE_USE_PIL_BELOW:\n        image = np.copy(image)\n    return equalize_(image, mask)\n\n\ndef equalize_(image, mask=None):\n    \"\"\"Equalize the image histogram in-place.\n\n    This function applies a non-linear mapping to the input image, in order\n    to create a uniform distribution of grayscale values in the output image.\n\n    This function has identical outputs to ``PIL.ImageOps.equalize``.\n    It does however work in-place.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        ``uint8`` ``(H,W,[C])`` image to equalize.\n\n    mask : None or ndarray, optional\n        An optional mask. If given, only the pixels selected by the mask are\n        included in the analysis.\n\n    Returns\n    -------\n    ndarray\n        Equalized image. *Might* have been modified in-place.\n\n    \"\"\"\n    nb_channels = 1 if image.ndim == 2 else image.shape[-1]\n    if nb_channels not in [1, 3]:\n        result = [equalize_(image[:, :, c])\n                  for c in np.arange(nb_channels)]\n        return np.stack(result, axis=-1)\n\n    iadt.allow_only_uint8({image.dtype})\n\n    if mask is not None:\n        assert mask.ndim == 2, (\n            \"Expected 2-dimensional mask, got shape %s.\" % (mask.shape,))\n        assert mask.dtype == iadt._UINT8_DTYPE, (\n            \"Expected mask of dtype uint8, got dtype %s.\" % (mask.dtype.name,))\n\n    size = image.size\n    if size == 0:\n        return image\n    if nb_channels == 3 and size < _EQUALIZE_USE_PIL_BELOW:\n        return _equalize_pil_(image, mask)\n    return _equalize_no_pil_(image, mask)\n\n\n# note that this is supposed to be a non-PIL reimplementation of PIL's\n# equalize, which produces slightly different results from cv2.equalizeHist()\n# Added in 0.4.0.\ndef _equalize_no_pil_(image, mask=None):\n    nb_channels = 1 if image.ndim == 2 else image.shape[-1]\n    # TODO remove the first axis, no longer needed\n    lut = np.empty((1, 256, nb_channels), dtype=np.int32)\n\n    for c_idx in range(nb_channels):\n        if image.ndim == 2:\n            image_c = image[:, :, np.newaxis]\n        else:\n            image_c = image[:, :, c_idx:c_idx+1]\n        histo = cv2.calcHist(\n            [_normalize_cv2_input_arr_(image_c)], [0], mask, [256], [0, 256])\n        if len(histo.nonzero()[0]) <= 1:\n            lut[0, :, c_idx] = np.arange(256).astype(np.int32)\n            continue\n\n        step = np.sum(histo[:-1]) // 255\n        if not step:\n            lut[0, :, c_idx] = np.arange(256).astype(np.int32)\n            continue\n\n        n = step // 2\n        cumsum = np.cumsum(histo)\n        lut[0, 0, c_idx] = n\n        lut[0, 1:, c_idx] = n + cumsum[0:-1]\n        lut[0, :, c_idx] //= int(step)\n    lut = np.clip(lut, None, 255, out=lut).astype(np.uint8)\n    image = ia.apply_lut_(image, lut)\n    return image\n\n\n# Added in 0.4.0.\ndef _equalize_pil_(image, mask=None):\n    if mask is not None:\n        mask = PIL.Image.fromarray(mask).convert(\"L\")\n\n    # don't return np.asarray(...) directly as its results are read-only\n    image[...] = np.asarray(\n        PIL.ImageOps.equalize(\n            PIL.Image.fromarray(image),\n            mask=mask\n        )\n    )\n    return image\n\n\ndef autocontrast(image, cutoff=0, ignore=None):\n    \"\"\"Maximize (normalize) image contrast.\n\n    This function calculates a histogram of the input image, removes\n    **cutoff** percent of the lightest and darkest pixels from the histogram,\n    and remaps the image so that the darkest pixel becomes black (``0``), and\n    the lightest becomes white (``255``).\n\n    This function has identical outputs to ``PIL.ImageOps.autocontrast``.\n    The speed is almost identical.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image for which to enhance the contrast.\n\n    cutoff : number\n        How many percent to cut off at the low and high end of the\n        histogram. E.g. ``20`` will cut off the lowest and highest ``20%``\n        of values. Expected value range is ``[0, 100]``.\n\n    ignore : None or int or iterable of int\n        Intensity values to ignore, i.e. to treat as background. If ``None``,\n        no pixels will be ignored. Otherwise exactly the given intensity\n        value(s) will be ignored.\n\n    Returns\n    -------\n    ndarray\n        Contrast-enhanced image.\n\n    \"\"\"\n    iadt.allow_only_uint8({image.dtype})\n\n    if 0 in image.shape:\n        return np.copy(image)\n\n    standard_channels = (image.ndim == 2 or image.shape[2] == 3)\n\n    if cutoff and standard_channels:\n        return _autocontrast_pil(image, cutoff, ignore)\n    return _autocontrast_no_pil(image, cutoff, ignore)\n\n\n# Added in 0.4.0.\ndef _autocontrast_pil(image, cutoff, ignore):\n    # don't return np.asarray(...) as its results are read-only\n    return np.array(\n        PIL.ImageOps.autocontrast(\n            PIL.Image.fromarray(image),\n            cutoff=cutoff, ignore=ignore\n        )\n    )\n\n\n# This function is only faster than the corresponding PIL function if no\n# cutoff is used.\n# C901 is \"<functionname> is too complex\"\n# Added in 0.4.0.\ndef _autocontrast_no_pil(image, cutoff, ignore):  # noqa: C901\n    # pylint: disable=invalid-name\n    if ignore is not None and not ia.is_iterable(ignore):\n        ignore = [ignore]\n\n    result = np.empty_like(image)\n    if result.ndim == 2:\n        result = result[..., np.newaxis]\n    nb_channels = image.shape[2] if image.ndim >= 3 else 1\n    for c_idx in sm.xrange(nb_channels):\n        # using [0] instead of [int(c_idx)] allows this to work with >4\n        # channels\n        if image.ndim == 2:\n            image_c = image[:, :, np.newaxis]\n        else:\n            image_c = image[:, :, c_idx:c_idx+1]\n        h = cv2.calcHist(\n            [_normalize_cv2_input_arr_(image_c)], [0], None, [256], [0, 256])\n        if ignore is not None:\n            h[ignore] = 0\n\n        if cutoff:\n            cs = np.cumsum(h)\n            n = cs[-1]\n            cut = n * cutoff // 100\n\n            # remove cutoff% pixels from the low end\n            lo_cut = cut - cs\n            lo_cut_nz = np.nonzero(lo_cut <= 0.0)[0]\n            if len(lo_cut_nz) == 0:\n                lo = 255\n            else:\n                lo = lo_cut_nz[0]\n            if lo > 0:\n                h[:lo] = 0\n            h[lo] = lo_cut[lo]\n\n            # remove cutoff% samples from the hi end\n            cs_rev = np.cumsum(h[::-1])\n            hi_cut = cs_rev - cut\n            hi_cut_nz = np.nonzero(hi_cut > 0.0)[0]\n            if len(hi_cut_nz) == 0:\n                hi = -1\n            else:\n                hi = 255 - hi_cut_nz[0]\n            h[hi+1:] = 0\n            if hi > -1:\n                h[hi] = hi_cut[255-hi]\n\n        # find lowest/highest samples after preprocessing\n        for lo, lo_val in enumerate(h):\n            if lo_val:\n                break\n        for hi in range(255, -1, -1):\n            if h[hi]:\n                break\n        if hi <= lo:\n            # don't bother\n            lut = np.arange(256)\n        else:\n            scale = 255.0 / (hi - lo)\n            offset = -lo * scale\n            ix = np.arange(256).astype(np.float64) * scale + offset\n            ix = np.clip(ix, 0, 255).astype(np.uint8)\n            lut = ix\n        lut = np.array(lut, dtype=np.uint8)\n\n        # Vectorized implementation of above block.\n        # This is overall slower.\n        # h_nz = np.nonzero(h)[0]\n        # if len(h_nz) <= 1:\n        #     lut = np.arange(256).astype(np.uint8)\n        # else:\n        #     lo = h_nz[0]\n        #     hi = h_nz[-1]\n        #\n        #     scale = 255.0 / (hi - lo)\n        #     offset = -lo * scale\n        #     ix = np.arange(256).astype(np.float64) * scale + offset\n        #     ix = np.clip(ix, 0, 255).astype(np.uint8)\n        #     lut = ix\n\n        # TODO change to a single call instead of one per channel\n        image_c_aug = ia.apply_lut(image_c, lut)\n        result[:, :, c_idx:c_idx+1] = image_c_aug\n    if image.ndim == 2:\n        return result[..., 0]\n    return result\n\n\n# Added in 0.4.0.\ndef _apply_enhance_func(image, cls, factor):\n    iadt.allow_only_uint8({image.dtype})\n\n    if 0 in image.shape:\n        return np.copy(image)\n\n    image, is_hw1 = _ensure_valid_shape(\n        image, \"imgaug.augmenters.pillike.enhance_*()\")\n\n    # don't return np.asarray(...) as its results are read-only\n    result = np.array(\n        cls(\n            PIL.Image.fromarray(image)\n        ).enhance(factor)\n    )\n    if is_hw1:\n        result = result[:, :, np.newaxis]\n    return result\n\n\ndef enhance_color(image, factor):\n    \"\"\"Change the strength of colors in an image.\n\n    This function has identical outputs to\n    ``PIL.ImageEnhance.Color``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    factor : number\n        Colorfulness of the output image. Values close to ``0.0`` lead\n        to grayscale images, values above ``1.0`` increase the strength of\n        colors. Sane values are roughly in ``[0.0, 3.0]``.\n\n    Returns\n    -------\n    ndarray\n        Color-modified image.\n\n    \"\"\"\n    return _apply_enhance_func(image, PIL.ImageEnhance.Color, factor)\n\n\ndef enhance_contrast(image, factor):\n    \"\"\"Change the contrast of an image.\n\n    This function has identical outputs to\n    ``PIL.ImageEnhance.Contrast``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    factor : number\n        Strength of contrast in the image. Values below ``1.0`` decrease the\n        contrast, leading to a gray image around ``0.0``. Values\n        above ``1.0`` increase the contrast. Sane values are roughly in\n        ``[0.5, 1.5]``.\n\n    Returns\n    -------\n    ndarray\n        Contrast-modified image.\n\n    \"\"\"\n    return _apply_enhance_func(image, PIL.ImageEnhance.Contrast, factor)\n\n\ndef enhance_brightness(image, factor):\n    \"\"\"Change the brightness of images.\n\n    This function has identical outputs to\n    ``PIL.ImageEnhance.Brightness``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    factor : number\n        Brightness of the image. Values below ``1.0`` decrease the brightness,\n        leading to a black image around ``0.0``. Values above ``1.0`` increase\n        the brightness. Sane values are roughly in ``[0.5, 1.5]``.\n\n    Returns\n    -------\n    ndarray\n        Brightness-modified image.\n\n    \"\"\"\n    return _apply_enhance_func(image, PIL.ImageEnhance.Brightness, factor)\n\n\ndef enhance_sharpness(image, factor):\n    \"\"\"Change the sharpness of an image.\n\n    This function has identical outputs to\n    ``PIL.ImageEnhance.Sharpness``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    factor : number\n        Sharpness of the image. Values below ``1.0`` decrease the sharpness,\n        values above ``1.0`` increase it. Sane values are roughly in\n        ``[0.0, 2.0]``.\n\n    Returns\n    -------\n    ndarray\n        Sharpness-modified image.\n\n    \"\"\"\n    return _apply_enhance_func(image, PIL.ImageEnhance.Sharpness, factor)\n\n\n# Added in 0.4.0.\ndef _filter_by_kernel(image, kernel):\n    iadt.allow_only_uint8({image.dtype})\n\n    if 0 in image.shape:\n        return np.copy(image)\n\n    image, is_hw1 = _ensure_valid_shape(\n        image, \"imgaug.augmenters.pillike.filter_*()\")\n\n    image_pil = PIL.Image.fromarray(image)\n\n    image_filtered = image_pil.filter(kernel)\n\n    # don't return np.asarray(...) as its results are read-only\n    result = np.array(image_filtered)\n    if is_hw1:\n        result = result[:, :, np.newaxis]\n    return result\n\n\ndef filter_blur(image):\n    \"\"\"Apply a blur filter kernel to the image.\n\n    This is the same as using PIL's ``PIL.ImageFilter.BLUR`` kernel.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    Returns\n    -------\n    ndarray\n        Blurred image.\n\n    \"\"\"\n    return _filter_by_kernel(image, PIL.ImageFilter.BLUR)\n\n\ndef filter_smooth(image):\n    \"\"\"Apply a smoothness filter kernel to the image.\n\n    This is the same as using PIL's ``PIL.ImageFilter.SMOOTH`` kernel.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    Returns\n    -------\n    ndarray\n        Smoothened image.\n\n    \"\"\"\n    return _filter_by_kernel(image, PIL.ImageFilter.SMOOTH)\n\n\ndef filter_smooth_more(image):\n    \"\"\"Apply a strong smoothness filter kernel to the image.\n\n    This is the same as using PIL's ``PIL.ImageFilter.SMOOTH_MORE`` kernel.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    Returns\n    -------\n    ndarray\n        Smoothened image.\n\n    \"\"\"\n    return _filter_by_kernel(image, PIL.ImageFilter.SMOOTH_MORE)\n\n\ndef filter_edge_enhance(image):\n    \"\"\"Apply an edge enhancement filter kernel to the image.\n\n    This is the same as using PIL's ``PIL.ImageFilter.EDGE_ENHANCE`` kernel.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    Returns\n    -------\n    ndarray\n        Image with enhanced edges.\n\n    \"\"\"\n    return _filter_by_kernel(image, PIL.ImageFilter.EDGE_ENHANCE)\n\n\ndef filter_edge_enhance_more(image):\n    \"\"\"Apply a stronger edge enhancement filter kernel to the image.\n\n    This is the same as using PIL's ``PIL.ImageFilter.EDGE_ENHANCE_MORE``\n    kernel.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    Returns\n    -------\n    ndarray\n        Smoothened image.\n\n    \"\"\"\n    return _filter_by_kernel(image, PIL.ImageFilter.EDGE_ENHANCE_MORE)\n\n\ndef filter_find_edges(image):\n    \"\"\"Apply an edge detection filter kernel to the image.\n\n    This is the same as using PIL's ``PIL.ImageFilter.FIND_EDGES`` kernel.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    Returns\n    -------\n    ndarray\n        Image with detected edges.\n\n    \"\"\"\n    return _filter_by_kernel(image, PIL.ImageFilter.FIND_EDGES)\n\n\ndef filter_contour(image):\n    \"\"\"Apply a contour filter kernel to the image.\n\n    This is the same as using PIL's ``PIL.ImageFilter.CONTOUR`` kernel.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    Returns\n    -------\n    ndarray\n        Image with pronounced contours.\n\n    \"\"\"\n    return _filter_by_kernel(image, PIL.ImageFilter.CONTOUR)\n\n\ndef filter_emboss(image):\n    \"\"\"Apply an emboss filter kernel to the image.\n\n    This is the same as using PIL's ``PIL.ImageFilter.EMBOSS`` kernel.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    Returns\n    -------\n    ndarray\n        Embossed image.\n\n    \"\"\"\n    return _filter_by_kernel(image, PIL.ImageFilter.EMBOSS)\n\n\ndef filter_sharpen(image):\n    \"\"\"Apply a sharpening filter kernel to the image.\n\n    This is the same as using PIL's ``PIL.ImageFilter.SHARPEN`` kernel.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    Returns\n    -------\n    ndarray\n        Sharpened image.\n\n    \"\"\"\n    return _filter_by_kernel(image, PIL.ImageFilter.SHARPEN)\n\n\ndef filter_detail(image):\n    \"\"\"Apply a detail enhancement filter kernel to the image.\n\n    This is the same as using PIL's ``PIL.ImageFilter.DETAIL`` kernel.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify.\n\n    Returns\n    -------\n    ndarray\n        Image with enhanced details.\n\n    \"\"\"\n    return _filter_by_kernel(image, PIL.ImageFilter.DETAIL)\n\n\n# TODO unify this with the matrix generation for Affine,\n#      there is probably no need to keep these separate\n# Added in 0.4.0.\ndef _create_affine_matrix(scale_x=1.0, scale_y=1.0,\n                          translate_x_px=0, translate_y_px=0,\n                          rotate_deg=0,\n                          shear_x_deg=0, shear_y_deg=0,\n                          center_px=(0, 0)):\n    from .geometric import _AffineMatrixGenerator, _RAD_PER_DEGREE\n\n    scale_x = max(scale_x, 0.0001)\n    scale_y = max(scale_y, 0.0001)\n\n    rotate_rad = rotate_deg * _RAD_PER_DEGREE\n    shear_x_rad = shear_x_deg * _RAD_PER_DEGREE\n    shear_y_rad = shear_y_deg * _RAD_PER_DEGREE\n\n    matrix_gen = _AffineMatrixGenerator()\n    matrix_gen.translate(x_px=-center_px[0], y_px=-center_px[1])\n    matrix_gen.scale(x_frac=scale_x, y_frac=scale_y)\n    matrix_gen.translate(x_px=translate_x_px, y_px=translate_y_px)\n    matrix_gen.shear(x_rad=-shear_x_rad, y_rad=shear_y_rad)\n    matrix_gen.rotate(rotate_rad)\n    matrix_gen.translate(x_px=center_px[0], y_px=center_px[1])\n\n    matrix = matrix_gen.matrix\n    matrix = np.linalg.inv(matrix)\n\n    return matrix\n\n\ndef warp_affine(image,\n                scale_x=1.0, scale_y=1.0,\n                translate_x_px=0, translate_y_px=0,\n                rotate_deg=0,\n                shear_x_deg=0, shear_y_deg=0,\n                fillcolor=None,\n                center=(0.5, 0.5)):\n    \"\"\"Apply an affine transformation to an image.\n\n    This function has identical outputs to\n    ``PIL.Image.transform`` with ``method=PIL.Image.AFFINE``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to modify. Expected to be ``uint8`` with shape ``(H,W)``\n        or ``(H,W,C)`` with ``C`` being ``3`` or ``4``.\n\n    scale_x : number, optional\n        Affine scale factor along the x-axis, where ``1.0`` denotes an\n        identity transform and ``2.0`` is a strong zoom-in effect.\n\n    scale_y : number, optional\n        Affine scale factor along the y-axis, where ``1.0`` denotes an\n        identity transform and ``2.0`` is a strong zoom-in effect.\n\n    translate_x_px : number, optional\n        Affine translation along the x-axis in pixels.\n        Positive values translate the image towards the right.\n\n    translate_y_px : number, optional\n        Affine translation along the y-axis in pixels.\n        Positive values translate the image towards the bottom.\n\n    rotate_deg : number, optional\n        Affine rotation in degrees *around the top left* of the image.\n\n    shear_x_deg : number, optional\n        Affine shearing in degrees along the x-axis with center point\n        being the top-left of the image.\n\n    shear_y_deg : number, optional\n        Affine shearing in degrees along the y-axis with center point\n        being the top-left of the image.\n\n    fillcolor : None or int or tuple of int, optional\n        Color tuple or intensity value to use when filling up newly\n        created pixels. ``None`` fills with zeros. ``int`` will only fill\n        the ``0`` th channel with that intensity value and all other channels\n        with ``0`` (this is the default behaviour of PIL, use a tuple to\n        fill all channels).\n\n    center : tuple of number, optional\n        Center xy-coordinate of the affine transformation, given as *relative*\n        values, i.e. ``(0.0, 0.0)`` sets the transformation center to the\n        top-left image corner, ``(1.0, 0.0)`` sets it to the the top-right\n        image corner and ``(0.5, 0.5)`` sets it to the image center.\n        The transformation center is relevant e.g. for rotations (\"rotate\n        around this center point\"). PIL uses the image top-left corner\n        as the transformation center if no centerization is included in the\n        affine transformation matrix.\n\n    Returns\n    -------\n    ndarray\n        Image after affine transformation.\n\n    \"\"\"\n    iadt.allow_only_uint8({image.dtype})\n\n    if 0 in image.shape:\n        return np.copy(image)\n\n    fillcolor = fillcolor if fillcolor is not None else 0\n    if ia.is_iterable(fillcolor):\n        # make sure that iterable fillcolor contains only ints\n        # otherwise we get a deprecation warning in py3.8\n        fillcolor = tuple(map(int, fillcolor))\n\n    image, is_hw1 = _ensure_valid_shape(\n        image, \"imgaug.augmenters.pillike.warp_affine()\")\n\n    image_pil = PIL.Image.fromarray(image)\n\n    height, width = image.shape[0:2]\n    center_px = (width * center[0], height * center[1])\n    matrix = _create_affine_matrix(scale_x=scale_x,\n                                   scale_y=scale_y,\n                                   translate_x_px=translate_x_px,\n                                   translate_y_px=translate_y_px,\n                                   rotate_deg=rotate_deg,\n                                   shear_x_deg=shear_x_deg,\n                                   shear_y_deg=shear_y_deg,\n                                   center_px=center_px)\n    matrix = matrix[:2, :].flat\n\n    # don't return np.asarray(...) as its results are read-only\n    result = np.array(\n        image_pil.transform(image_pil.size, PIL.Image.AFFINE, matrix,\n                            fillcolor=fillcolor)\n    )\n\n    if is_hw1:\n        result = result[:, :, np.newaxis]\n    return result\n\n\n# we don't use pil_solarize() here. but instead just subclass Invert,\n# which is easier and comes down to the same\nclass Solarize(arithmetic.Invert):\n    \"\"\"Augmenter with identical outputs to PIL's ``solarize()`` function.\n\n    This augmenter inverts all pixel values above a threshold.\n\n    The outputs are identical to PIL's ``solarize()``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See ``~imgaug.augmenters.arithmetic.invert_(min_value=None and max_value=None)``.\n\n    Parameters\n    ----------\n    p : float or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.arithmetic.Invert`.\n\n    threshold : None or number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.arithmetic.Invert`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Solarize(0.5, threshold=(32, 128))\n\n    Invert the colors in ``50`` percent of all images for pixels with a\n    value between ``32`` and ``128`` or more. The threshold is sampled once\n    per image. The thresholding operation happens per channel.\n\n    \"\"\"\n\n    def __init__(self, p=1.0, threshold=128,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Solarize, self).__init__(\n            p=p, per_channel=False,\n            min_value=None, max_value=None,\n            threshold=threshold, invert_above_threshold=True,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Posterize(colorlib.Posterize):\n    \"\"\"Augmenter with identical outputs to PIL's ``posterize()`` function.\n\n    This augmenter quantizes each array component to ``N`` bits.\n\n    This class is currently an alias for\n    :class:`~imgaug.augmenters.color.Posterize`, which again is an alias\n    for :class:`~imgaug.augmenters.color.UniformColorQuantizationToNBits`,\n    i.e. all three classes are right now guarantueed to have the same\n    outputs as PIL's function.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.color.Posterize`.\n\n    \"\"\"\n\n\nclass Equalize(meta.Augmenter):\n    \"\"\"Equalize the image histogram.\n\n    This augmenter has identical outputs to ``PIL.ImageOps.equalize``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.equalize_`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.Equalize()\n\n    Equalize the histograms of all input images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Equalize, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        # pylint: disable=no-self-use\n        if batch.images is not None:\n            for image in batch.images:\n                image[...] = equalize_(image)\n        return batch\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return []\n\n\nclass Autocontrast(contrastlib._ContrastFuncWrapper):\n    \"\"\"Adjust contrast by cutting off ``p%`` of lowest/highest histogram values.\n\n    This augmenter has identical outputs to ``PIL.ImageOps.autocontrast``.\n\n    See :func:`~imgaug.augmenters.pillike.autocontrast` for more details.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.autocontrast`.\n\n    Parameters\n    ----------\n    cutoff : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Percentage of values to cut off from the low and high end of each\n        image's histogram, before stretching it to ``[0, 255]``.\n\n            * If ``int``: The value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A value will be uniformly sampled from\n              the discrete interval ``[a..b]`` per image.\n            * If ``list``: A random value will be sampled from the list\n              per image.\n            * If ``StochasticParameter``: A value will be sampled from that\n              parameter per image.\n\n    per_channel :  bool or float, optional\n        Whether to use the same value for all channels (``False``) or to\n        sample a new value for each channel (``True``). If this value is a\n        float ``p``, then for ``p`` percent of all images `per_channel` will\n        be treated as ``True``, otherwise as ``False``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.Autocontrast()\n\n    Modify the contrast of images by cutting off the ``0`` to ``20%`` lowest\n    and highest values from the histogram, then stretching it to full length.\n\n    >>> aug = iaa.pillike.Autocontrast((10, 20), per_channel=True)\n\n    Modify the contrast of images by cutting off the ``10`` to ``20%`` lowest\n    and highest values from the histogram, then stretching it to full length.\n    The cutoff value is sampled per *channel* instead of per *image*.\n\n    \"\"\"\n    # pylint: disable=protected-access\n\n    # Added in 0.4.0.\n    def __init__(self, cutoff=(0, 20), per_channel=False,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        params1d = [\n            iap.handle_discrete_param(\n                cutoff, \"cutoff\", value_range=(0, 49), tuple_to_uniform=True,\n                list_to_choice=True)\n        ]\n        func = autocontrast\n\n        super(Autocontrast, self).__init__(\n            func, params1d, per_channel,\n            dtypes_allowed=\"uint8\",\n            dtypes_disallowed=\"uint16 uint32 uint64 int8 int16 int32 int64 \"\n                              \"float16 float32 float64 float128 \"\n                              \"bool\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic\n        )\n\n\n# Added in 0.4.0.\nclass _EnhanceBase(meta.Augmenter):\n    # Added in 0.4.0.\n    def __init__(self, func, factor, factor_value_range,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(_EnhanceBase, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.func = func\n        self.factor = iap.handle_continuous_param(\n            factor, \"factor\", value_range=factor_value_range,\n            tuple_to_uniform=True, list_to_choice=True)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        factors = self._draw_samples(len(batch.images), random_state)\n        for image, factor in zip(batch.images, factors):\n            image[...] = self.func(image, factor)\n        return batch\n\n    # Added in 0.4.0.\n    def _draw_samples(self, nb_rows, random_state):\n        return self.factor.draw_samples((nb_rows,), random_state=random_state)\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.factor]\n\n\nclass EnhanceColor(_EnhanceBase):\n    \"\"\"Convert images to grayscale.\n\n    This augmenter has identical outputs to ``PIL.ImageEnhance.Color``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.enhance_color`.\n\n    Parameters\n    ----------\n    factor : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Colorfulness of the output image. Values close to ``0.0`` lead\n        to grayscale images, values above ``1.0`` increase the strength of\n        colors. Sane values are roughly in ``[0.0, 3.0]``.\n\n            * If ``number``: The value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A value will be uniformly sampled per\n              image from the interval ``[a, b)``.\n            * If ``list``: A random value will be picked from the list per\n              image.\n            * If ``StochasticParameter``: Per batch of size ``N``, the\n              parameter will be queried once to return ``(N,)`` samples.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.EnhanceColor()\n\n    Create an augmenter to remove a random fraction of color from\n    input images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, factor=(0.0, 3.0),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(EnhanceColor, self).__init__(\n            func=enhance_color,\n            factor=factor,\n            factor_value_range=(0.0, None),\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass EnhanceContrast(_EnhanceBase):\n    \"\"\"Change the contrast of images.\n\n    This augmenter has identical outputs to ``PIL.ImageEnhance.Contrast``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.enhance_contrast`.\n\n    Parameters\n    ----------\n    factor : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Strength of contrast in the image. Values below ``1.0`` decrease the\n        contrast, leading to a gray image around ``0.0``. Values\n        above ``1.0`` increase the contrast. Sane values are roughly in\n        ``[0.5, 1.5]``.\n\n            * If ``number``: The value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A value will be uniformly sampled per\n              image from the interval ``[a, b)``.\n            * If ``list``: A random value will be picked from the list per\n              image.\n            * If ``StochasticParameter``: Per batch of size ``N``, the\n              parameter will be queried once to return ``(N,)`` samples.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.EnhanceContrast()\n\n    Create an augmenter that worsens the contrast of an image by a random\n    factor.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, factor=(0.5, 1.5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(EnhanceContrast, self).__init__(\n            func=enhance_contrast,\n            factor=factor,\n            factor_value_range=(0.0, None),\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass EnhanceBrightness(_EnhanceBase):\n    \"\"\"Change the brightness of images.\n\n    This augmenter has identical outputs to\n    ``PIL.ImageEnhance.Brightness``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.enhance_brightness`.\n\n    Parameters\n    ----------\n    factor : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Brightness of the image. Values below ``1.0`` decrease the brightness,\n        leading to a black image around ``0.0``. Values above ``1.0`` increase\n        the brightness. Sane values are roughly in ``[0.5, 1.5]``.\n\n            * If ``number``: The value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A value will be uniformly sampled per\n              image from the interval ``[a, b)``.\n            * If ``list``: A random value will be picked from the list per\n              image.\n            * If ``StochasticParameter``: Per batch of size ``N``, the\n              parameter will be queried once to return ``(N,)`` samples.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.EnhanceBrightness()\n\n    Create an augmenter that worsens the brightness of an image by a random\n    factor.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, factor=(0.5, 1.5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(EnhanceBrightness, self).__init__(\n            func=enhance_brightness,\n            factor=factor,\n            factor_value_range=(0.0, None),\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass EnhanceSharpness(_EnhanceBase):\n    \"\"\"Change the sharpness of images.\n\n    This augmenter has identical outputs to\n    ``PIL.ImageEnhance.Sharpness``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.enhance_sharpness`.\n\n    Parameters\n    ----------\n    factor : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Sharpness of the image. Values below ``1.0`` decrease the sharpness,\n        values above ``1.0`` increase it. Sane values are roughly in\n        ``[0.0, 2.0]``.\n\n            * If ``number``: The value will be used for all images.\n            * If ``tuple`` ``(a, b)``: A value will be uniformly sampled per\n              image from the interval ``[a, b)``.\n            * If ``list``: A random value will be picked from the list per\n              image.\n            * If ``StochasticParameter``: Per batch of size ``N``, the\n              parameter will be queried once to return ``(N,)`` samples.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.EnhanceSharpness()\n\n    Create an augmenter that randomly decreases or increases the sharpness\n    of an image.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, factor=(0.0, 2.0),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(EnhanceSharpness, self).__init__(\n            func=enhance_sharpness,\n            factor=factor,\n            factor_value_range=(0.0, None),\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# Added in 0.4.0.\nclass _FilterBase(meta.Augmenter):\n    # Added in 0.4.0.\n    def __init__(self, func,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(_FilterBase, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.func = func\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is not None:\n            for image in batch.images:\n                image[...] = self.func(image)\n        return batch\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return []\n\n\nclass FilterBlur(_FilterBase):\n    \"\"\"Apply a blur filter kernel to images.\n\n    This augmenter has identical outputs to\n    calling ``PIL.Image.filter`` with kernel ``PIL.ImageFilter.BLUR``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.filter_blur`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.FilterBlur()\n\n    Create an augmenter that applies a blur filter kernel to images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(FilterBlur, self).__init__(\n            func=filter_blur,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass FilterSmooth(_FilterBase):\n    \"\"\"Apply a smoothening filter kernel to images.\n\n    This augmenter has identical outputs to\n    calling ``PIL.Image.filter`` with kernel ``PIL.ImageFilter.SMOOTH``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.filter_smooth`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.FilterSmooth()\n\n    Create an augmenter that applies a smoothening filter kernel to images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(FilterSmooth, self).__init__(\n            func=filter_smooth,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass FilterSmoothMore(_FilterBase):\n    \"\"\"Apply a strong smoothening filter kernel to images.\n\n    This augmenter has identical outputs to\n    calling ``PIL.Image.filter`` with kernel ``PIL.ImageFilter.BLUR``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.filter_smooth_more`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.FilterSmoothMore()\n\n    Create an augmenter that applies a strong smoothening filter kernel to\n    images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(FilterSmoothMore, self).__init__(\n            func=filter_smooth_more,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass FilterEdgeEnhance(_FilterBase):\n    \"\"\"Apply an edge enhance filter kernel to images.\n\n    This augmenter has identical outputs to\n    calling ``PIL.Image.filter`` with kernel\n    ``PIL.ImageFilter.EDGE_ENHANCE``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.filter_edge_enhance`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.FilterEdgeEnhance()\n\n    Create an augmenter that applies a edge enhancement filter kernel to\n    images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(FilterEdgeEnhance, self).__init__(\n            func=filter_edge_enhance,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass FilterEdgeEnhanceMore(_FilterBase):\n    \"\"\"Apply a strong edge enhancement filter kernel to images.\n\n    This augmenter has identical outputs to\n    calling ``PIL.Image.filter`` with kernel\n    ``PIL.ImageFilter.EDGE_ENHANCE_MORE``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.filter_edge_enhance_more`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.FilterEdgeEnhanceMore()\n\n    Create an augmenter that applies a strong edge enhancement filter kernel\n    to images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(FilterEdgeEnhanceMore, self).__init__(\n            func=filter_edge_enhance_more,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass FilterFindEdges(_FilterBase):\n    \"\"\"Apply a edge detection kernel to images.\n\n    This augmenter has identical outputs to\n    calling ``PIL.Image.filter`` with kernel\n    ``PIL.ImageFilter.FIND_EDGES``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.filter_find_edges`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.FilterFindEdges()\n\n    Create an augmenter that applies an edge detection filter kernel to images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(FilterFindEdges, self).__init__(\n            func=filter_find_edges,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass FilterContour(_FilterBase):\n    \"\"\"Apply a contour detection filter kernel to images.\n\n    This augmenter has identical outputs to\n    calling ``PIL.Image.filter`` with kernel ``PIL.ImageFilter.CONTOUR``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.filter_contour`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.FilterContour()\n\n    Create an augmenter that applies a contour detection filter kernel to\n    images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(FilterContour, self).__init__(\n            func=filter_contour,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass FilterEmboss(_FilterBase):\n    \"\"\"Apply an emboss filter kernel to images.\n\n    This augmenter has identical outputs to\n    calling ``PIL.Image.filter`` with kernel ``PIL.ImageFilter.EMBOSS``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.filter_emboss`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.FilterEmboss()\n\n    Create an augmenter that applies an emboss filter kernel to images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(FilterEmboss, self).__init__(\n            func=filter_emboss,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass FilterSharpen(_FilterBase):\n    \"\"\"Apply a sharpening filter kernel to images.\n\n    This augmenter has identical outputs to\n    calling ``PIL.Image.filter`` with kernel ``PIL.ImageFilter.SHARPEN``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.filter_sharpen`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.FilterSharpen()\n\n    Create an augmenter that applies a sharpening filter kernel to images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(FilterSharpen, self).__init__(\n            func=filter_sharpen,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass FilterDetail(_FilterBase):\n    \"\"\"Apply a detail enhancement filter kernel to images.\n\n    This augmenter has identical outputs to\n    calling ``PIL.Image.filter`` with kernel ``PIL.ImageFilter.DETAIL``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.filter_detail`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.FilterDetail()\n\n    Create an augmenter that applies a detail enhancement filter kernel to\n    images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(FilterDetail, self).__init__(\n            func=filter_detail,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Affine(geometric.Affine):\n    \"\"\"Apply PIL-like affine transformations to images.\n\n    This augmenter has identical outputs to\n    ``PIL.Image.transform`` with parameter ``method=PIL.Image.AFFINE``.\n\n    .. warning::\n\n        This augmenter can currently only transform image-data.\n        Batches containing heatmaps, segmentation maps and\n        coordinate-based augmentables will be rejected with an error.\n        Use :class:`~imgaug.augmenters.geometric.Affine` if you have to\n        transform such inputs.\n\n    .. note::\n\n        This augmenter uses the image center as the transformation center.\n        This has to be explicitly enforced in PIL using corresponding\n        translation matrices. Without such translation, PIL uses the image\n        top left corner as the transformation center. To mirror that\n        behaviour, use ``center=(0.0, 0.0)``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.pillike.warp_affine`.\n\n    Parameters\n    ----------\n    scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter or dict {\"x\": number/tuple/list/StochasticParameter, \"y\": number/tuple/list/StochasticParameter}, optional\n        See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    translate_percent : None or number or tuple of number or list of number or imgaug.parameters.StochasticParameter or dict {\"x\": number/tuple/list/StochasticParameter, \"y\": number/tuple/list/StochasticParameter}, optional\n        See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    translate_px : None or int or tuple of int or list of int or imgaug.parameters.StochasticParameter or dict {\"x\": int/tuple/list/StochasticParameter, \"y\": int/tuple/list/StochasticParameter}, optional\n        See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    rotate : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    shear : number or tuple of number or list of number or imgaug.parameters.StochasticParameter or dict {\"x\": int/tuple/list/StochasticParameter, \"y\": int/tuple/list/StochasticParameter}, optional\n        See :class:`~imgaug.augmenters.geometric.Affine`.\n\n    fillcolor : number or tuple of number or list of number or imgaug.ALL or imgaug.parameters.StochasticParameter, optional\n        See parameter ``cval`` in :class:`~imgaug.augmenters.geometric.Affine`.\n\n    center : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n        The center point of the affine transformation, given as relative\n        xy-coordinates.\n        Set this to ``(0.0, 0.0)`` or ``left-top`` to use the top left image\n        corner as the transformation center.\n        Set this to ``(0.5, 0.5)`` or ``center-center`` to use the image\n        center as the transformation center.\n        See also paramerer ``position`` in\n        :class:`~imgaug.augmenters.size.PadToFixedSize` for details\n        about valid datatypes of this parameter.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.pillike.Affine(scale={\"x\": (0.8, 1.2), \"y\": (0.5, 1.5)})\n\n    Create an augmenter that applies affine scaling (zoom in/out) to images.\n    Along the x-axis they are scaled to 80-120% of their size, along\n    the y-axis to 50-150% (both values randomly and uniformly chosen per\n    image).\n\n    >>> aug = iaa.pillike.Affine(translate_px={\"x\": 0, \"y\": [-10, 10]},\n    >>>                          fillcolor=128)\n\n    Create an augmenter that translates images along the y-axis by either\n    ``-10px`` or ``10px``. Newly created pixels are always filled with\n    the value ``128`` (along all channels).\n\n    >>> aug = iaa.pillike.Affine(rotate=(-20, 20), fillcolor=(0, 256))\n\n    Rotate an image by ``-20`` to ``20`` degress and fill up all newly\n    created pixels with a random RGB color.\n\n    See the similar augmenter :class:`~imgaug.augmenters.geometric.Affine`\n    for more examples.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, scale=1.0, translate_percent=None, translate_px=None,\n                 rotate=0.0, shear=0.0, fillcolor=0, center=(0.5, 0.5),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Affine, self).__init__(\n            scale=scale,\n            translate_percent=translate_percent,\n            translate_px=translate_px,\n            rotate=rotate,\n            shear=shear,\n            order=1,\n            cval=fillcolor,\n            mode=\"constant\",\n            fit_output=False,\n            backend=\"auto\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        # TODO move that func to iap\n        self.center = sizelib._handle_position_parameter(center)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        cols = batch.get_column_names()\n        assert len(cols) == 0 or (len(cols) == 1 and \"images\" in cols), (\n            \"pillike.Affine can currently only process image data. Got a \"\n            \"batch containing: %s. Use imgaug.augmenters.geometric.Affine for \"\n            \"batches containing non-image data.\" % (\", \".join(cols),))\n\n        return super(Affine, self)._augment_batch_(\n            batch, random_state, parents, hooks)\n\n    # Added in 0.4.0.\n    def _augment_images_by_samples(self, images, samples,\n                                   image_shapes=None,\n                                   return_matrices=False):\n        assert return_matrices is False, (\n            \"Got unexpectedly return_matrices=True. pillike.Affine does not \"\n            \"yet produce that output.\")\n\n        for i, image in enumerate(images):\n            image_shape = (image.shape if image_shapes is None\n                           else image_shapes[i])\n\n            params = samples.get_affine_parameters(\n                i, arr_shape=image_shape, image_shape=image_shape)\n\n            image[...] = warp_affine(\n                image,\n                scale_x=params[\"scale_x\"],\n                scale_y=params[\"scale_y\"],\n                translate_x_px=params[\"translate_x_px\"],\n                translate_y_px=params[\"translate_y_px\"],\n                rotate_deg=params[\"rotate_deg\"],\n                shear_x_deg=params[\"shear_x_deg\"],\n                shear_y_deg=params[\"shear_y_deg\"],\n                fillcolor=tuple(samples.cval[i]),\n                center=(samples.center_x[i], samples.center_y[i])\n            )\n\n        return images\n\n    # Added in 0.4.0.\n    def _draw_samples(self, nb_samples, random_state):\n        # standard affine samples\n        samples = super(Affine, self)._draw_samples(nb_samples,\n                                                    random_state)\n\n        # add samples for 'center' parameter, which is not yet a part of\n        # Affine\n        if isinstance(self.center, tuple):\n            xx = self.center[0].draw_samples(nb_samples,\n                                             random_state=random_state)\n            yy = self.center[1].draw_samples(nb_samples,\n                                             random_state=random_state)\n        else:\n            xy = self.center.draw_samples((nb_samples, 2),\n                                          random_state=random_state)\n            xx = xy[:, 0]\n            yy = xy[:, 1]\n\n        samples.center_x = xx\n        samples.center_y = yy\n        return samples\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [\n            self.scale, self.translate, self.rotate, self.shear, self.cval,\n            self.center]\n"
  },
  {
    "path": "imgaug/augmenters/pooling.py",
    "content": "\"\"\"\nAugmenters that apply pooling operations to images.\n\nList of augmenters:\n\n    * :class:`AveragePooling`\n    * :class:`MaxPooling`\n    * :class:`MinPooling`\n    * :class:`MedianPooling`\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nfrom abc import ABCMeta, abstractmethod\nimport functools\n\nimport six\nimport numpy as np\n\nimport imgaug as ia\nfrom . import meta\nfrom .. import parameters as iap\n\n\ndef _compute_shape_after_pooling(image_shape, ksize_h, ksize_w):\n    if any([axis == 0 for axis in image_shape]):\n        return image_shape\n\n    height, width = image_shape[0:2]\n\n    if height % ksize_h > 0:\n        height += ksize_h - (height % ksize_h)\n    if width % ksize_w > 0:\n        width += ksize_w - (width % ksize_w)\n\n    return tuple([\n        height//ksize_h,\n        width//ksize_w,\n    ] + list(image_shape[2:]))\n\n\n@six.add_metaclass(ABCMeta)\nclass _AbstractPoolingBase(meta.Augmenter):\n    # TODO add floats as ksize denoting fractions of image sizes\n    #      (note possible overlap with fractional kernel sizes here)\n    def __init__(self, kernel_size, keep_size=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(_AbstractPoolingBase, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.kernel_size = iap.handle_discrete_kernel_size_param(\n            kernel_size,\n            \"kernel_size\",\n            value_range=(0, None),\n            allow_floats=False)\n        self.keep_size = keep_size\n\n        self._resize_hm_and_sm_arrays = True\n\n    @abstractmethod\n    def _pool_image(self, image, kernel_size_h, kernel_size_w):\n        \"\"\"Apply pooling method with given kernel height/width to an image.\"\"\"\n\n    def _draw_samples(self, nb_rows, random_state):\n        rss = random_state.duplicate(2)\n        mode = \"single\" if self.kernel_size[1] is None else \"two\"\n        kernel_sizes_h = self.kernel_size[0].draw_samples(\n            (nb_rows,),\n            random_state=rss[0])\n        if mode == \"single\":\n            kernel_sizes_w = kernel_sizes_h\n        else:\n            kernel_sizes_w = self.kernel_size[1].draw_samples(\n                (nb_rows,), random_state=rss[1])\n        return (\n            np.clip(kernel_sizes_h, 1, None),\n            np.clip(kernel_sizes_w, 1, None)\n        )\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None and self.keep_size:\n            return batch\n\n        samples = self._draw_samples(batch.nb_rows, random_state)\n        for column in batch.columns:\n            value_aug = getattr(\n                self, \"_augment_%s_by_samples\" % (column.name,)\n            )(column.value, samples)\n            setattr(batch, column.attr_name, value_aug)\n        return batch\n\n    # Added in 0.4.0.\n    def _augment_images_by_samples(self, images, samples):\n        if not self.keep_size:\n            images = list(images)\n\n        kernel_sizes_h, kernel_sizes_w = samples\n\n        gen = enumerate(zip(images, kernel_sizes_h, kernel_sizes_w))\n        for i, (image, ksize_h, ksize_w) in gen:\n            if ksize_h >= 2 or ksize_w >= 2:\n                image_pooled = self._pool_image(\n                    image, ksize_h, ksize_w)\n                if self.keep_size:\n                    image_pooled = ia.imresize_single_image(\n                        image_pooled, image.shape[0:2])\n                images[i] = image_pooled\n\n        return images\n\n    # Added in 0.4.0.\n    def _augment_heatmaps_by_samples(self, heatmaps, samples):\n        return self._augment_hms_and_segmaps_by_samples(heatmaps, samples,\n                                                        \"arr_0to1\")\n\n    # Added in 0.4.0.\n    def _augment_segmentation_maps_by_samples(self, segmaps, samples):\n        return self._augment_hms_and_segmaps_by_samples(segmaps, samples,\n                                                        \"arr\")\n\n    # Added in 0.4.0.\n    def _augment_hms_and_segmaps_by_samples(self, augmentables, samples,\n                                            arr_attr_name):\n        if self.keep_size:\n            return augmentables\n\n        kernel_sizes_h, kernel_sizes_w = samples\n\n        gen = enumerate(zip(augmentables, kernel_sizes_h, kernel_sizes_w))\n        for i, (augmentable, ksize_h, ksize_w) in gen:\n            if ksize_h >= 2 or ksize_w >= 2:\n                # We could also keep the size of the HM/SM array unchanged\n                # here as the library can handle HMs/SMs that are larger\n                # than the image. This might be inintuitive however and\n                # could lead to unnecessary performance degredation.\n                if self._resize_hm_and_sm_arrays:\n                    new_shape_arr = _compute_shape_after_pooling(\n                        getattr(augmentable, arr_attr_name).shape,\n                        ksize_h, ksize_w)\n                    augmentable = augmentable.resize(new_shape_arr[0:2])\n\n                new_shape = _compute_shape_after_pooling(\n                    augmentable.shape, ksize_h, ksize_w)\n                augmentable.shape = new_shape\n\n                augmentables[i] = augmentable\n\n        return augmentables\n\n    # Added in 0.4.0.\n    def _augment_keypoints_by_samples(self, keypoints_on_images, samples):\n        if self.keep_size:\n            return keypoints_on_images\n\n        kernel_sizes_h, kernel_sizes_w = samples\n\n        gen = enumerate(zip(keypoints_on_images, kernel_sizes_h,\n                            kernel_sizes_w))\n        for i, (kpsoi, ksize_h, ksize_w) in gen:\n            if ksize_h >= 2 or ksize_w >= 2:\n                new_shape = _compute_shape_after_pooling(\n                    kpsoi.shape, ksize_h, ksize_w)\n\n                keypoints_on_images[i] = kpsoi.on_(new_shape)\n\n        return keypoints_on_images\n\n    # Added in 0.4.0.\n    def _augment_polygons_by_samples(self, polygons_on_images, samples):\n        func = functools.partial(self._augment_keypoints_by_samples,\n                                 samples=samples)\n        return self._apply_to_polygons_as_keypoints(polygons_on_images, func,\n                                                    recoverer=None)\n\n    # Added in 0.4.0.\n    def _augment_line_strings_by_samples(self, line_strings_on_images, samples):\n        func = functools.partial(self._augment_keypoints_by_samples,\n                                 samples=samples)\n        return self._apply_to_cbaois_as_keypoints(line_strings_on_images, func)\n\n    # Added in 0.4.0.\n    def _augment_bounding_boxes_by_samples(self, bounding_boxes_on_images,\n                                           samples):\n        func = functools.partial(self._augment_keypoints_by_samples,\n                                 samples=samples)\n        return self._apply_to_cbaois_as_keypoints(bounding_boxes_on_images,\n                                                  func)\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.kernel_size, self.keep_size]\n\n\n# TODO rename kernel size parameters in all augmenters to kernel_size\n# TODO add per_channel\n# TODO add upscaling interpolation mode?\nclass AveragePooling(_AbstractPoolingBase):\n    \"\"\"\n    Apply average pooling to images.\n\n    This augmenter pools images with kernel sizes ``H x W`` by averaging the\n    pixel values within these windows. For e.g. ``2 x 2`` this halves the image\n    size. Optionally, the augmenter will automatically re-upscale the image\n    to the input size (by default this is activated).\n\n    Note that this augmenter is very similar to ``AverageBlur``.\n    ``AverageBlur`` applies averaging within windows of given kernel size\n    *without* striding, while ``AveragePooling`` applies striding corresponding\n    to the kernel size, with optional upscaling afterwards. The upscaling\n    is configured to create \"pixelated\"/\"blocky\" images by default.\n\n    .. note::\n\n        During heatmap or segmentation map augmentation, the respective\n        arrays are not changed, only the shapes of the underlying images\n        are updated. This is because imgaug can handle maps/maks that are\n        larger/smaller than their corresponding image.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.imgaug.avg_pool`.\n\n    Attributes\n    ----------\n    kernel_size : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or tuple of tuple of int or tuple of list of int or tuple of imgaug.parameters.StochasticParameter, optional\n        The kernel size of the pooling operation.\n\n        * If an int, then that value will be used for all images for both\n          kernel height and width.\n        * If a tuple ``(a, b)``, then a value from the discrete range\n          ``[a..b]`` will be sampled per image.\n        * If a list, then a random value will be sampled from that list per\n          image and used for both kernel height and width.\n        * If a StochasticParameter, then a value will be sampled per image\n          from that parameter per image and used for both kernel height and\n          width.\n        * If a tuple of tuple of int given as ``((a, b), (c, d))``, then two\n          values will be sampled independently from the discrete ranges\n          ``[a..b]`` and ``[c..d]`` per image and used as the kernel height\n          and width.\n        * If a tuple of lists of int, then two values will be sampled\n          independently per image, one from the first list and one from the\n          second, and used as the kernel height and width.\n        * If a tuple of StochasticParameter, then two values will be sampled\n          indepdently per image, one from the first parameter and one from the\n          second, and used as the kernel height and width.\n\n    keep_size : bool, optional\n        After pooling, the result image will usually have a different\n        height/width compared to the original input image. If this\n        parameter is set to True, then the pooled image will be resized\n        to the input image's size, i.e. the augmenter's output shape is always\n        identical to the input shape.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.AveragePooling(2)\n\n    Create an augmenter that always pools with a kernel size of ``2 x 2``.\n\n    >>> aug = iaa.AveragePooling(2, keep_size=False)\n\n    Create an augmenter that always pools with a kernel size of ``2 x 2``\n    and does *not* resize back to the input image size, i.e. the resulting\n    images have half the resolution.\n\n    >>> aug = iaa.AveragePooling([2, 8])\n\n    Create an augmenter that always pools either with a kernel size\n    of ``2 x 2`` or ``8 x 8``.\n\n    >>> aug = iaa.AveragePooling((1, 7))\n\n    Create an augmenter that always pools with a kernel size of\n    ``1 x 1`` (does nothing) to ``7 x 7``. The kernel sizes are always\n    symmetric.\n\n    >>> aug = iaa.AveragePooling(((1, 7), (1, 7)))\n\n    Create an augmenter that always pools with a kernel size of\n    ``H x W`` where ``H`` and ``W`` are both sampled independently from the\n    range ``[1..7]``. E.g. resulting kernel sizes could be ``3 x 7``\n    or ``5 x 1``.\n\n    \"\"\"\n\n    # TODO add floats as ksize denoting fractions of image sizes\n    #      (note possible overlap with fractional kernel sizes here)\n    def __init__(self, kernel_size=(1, 5), keep_size=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(AveragePooling, self).__init__(\n            kernel_size=kernel_size, keep_size=keep_size,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    def _pool_image(self, image, kernel_size_h, kernel_size_w):\n        return ia.avg_pool(\n            image,\n            (kernel_size_h, kernel_size_w)\n        )\n\n\nclass MaxPooling(_AbstractPoolingBase):\n    \"\"\"\n    Apply max pooling to images.\n\n    This augmenter pools images with kernel sizes ``H x W`` by taking the\n    maximum pixel value over windows. For e.g. ``2 x 2`` this halves the image\n    size. Optionally, the augmenter will automatically re-upscale the image\n    to the input size (by default this is activated).\n\n    The maximum within each pixel window is always taken channelwise..\n\n    .. note::\n\n        During heatmap or segmentation map augmentation, the respective\n        arrays are not changed, only the shapes of the underlying images\n        are updated. This is because imgaug can handle maps/maks that are\n        larger/smaller than their corresponding image.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.imgaug.max_pool`.\n\n    Attributes\n    ----------\n    kernel_size : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or tuple of tuple of int or tuple of list of int or tuple of imgaug.parameters.StochasticParameter, optional\n        The kernel size of the pooling operation.\n\n        * If an int, then that value will be used for all images for both\n          kernel height and width.\n        * If a tuple ``(a, b)``, then a value from the discrete range\n          ``[a..b]`` will be sampled per image.\n        * If a list, then a random value will be sampled from that list per\n          image and used for both kernel height and width.\n        * If a StochasticParameter, then a value will be sampled per image\n          from that parameter per image and used for both kernel height and\n          width.\n        * If a tuple of tuple of int given as ``((a, b), (c, d))``, then two\n          values will be sampled independently from the discrete ranges\n          ``[a..b]`` and ``[c..d]`` per image and used as the kernel height\n          and width.\n        * If a tuple of lists of int, then two values will be sampled\n          independently per image, one from the first list and one from the\n          second, and used as the kernel height and width.\n        * If a tuple of StochasticParameter, then two values will be sampled\n          indepdently per image, one from the first parameter and one from the\n          second, and used as the kernel height and width.\n\n    keep_size : bool, optional\n        After pooling, the result image will usually have a different\n        height/width compared to the original input image. If this\n        parameter is set to True, then the pooled image will be resized\n        to the input image's size, i.e. the augmenter's output shape is always\n        identical to the input shape.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MaxPooling(2)\n\n    Create an augmenter that always pools with a kernel size of ``2 x 2``.\n\n    >>> aug = iaa.MaxPooling(2, keep_size=False)\n\n    Create an augmenter that always pools with a kernel size of ``2 x 2``\n    and does *not* resize back to the input image size, i.e. the resulting\n    images have half the resolution.\n\n    >>> aug = iaa.MaxPooling([2, 8])\n\n    Create an augmenter that always pools either with a kernel size\n    of ``2 x 2`` or ``8 x 8``.\n\n    >>> aug = iaa.MaxPooling((1, 7))\n\n    Create an augmenter that always pools with a kernel size of\n    ``1 x 1`` (does nothing) to ``7 x 7``. The kernel sizes are always\n    symmetric.\n\n    >>> aug = iaa.MaxPooling(((1, 7), (1, 7)))\n\n    Create an augmenter that always pools with a kernel size of\n    ``H x W`` where ``H`` and ``W`` are both sampled independently from the\n    range ``[1..7]``. E.g. resulting kernel sizes could be ``3 x 7``\n    or ``5 x 1``.\n\n    \"\"\"\n\n    # TODO add floats as ksize denoting fractions of image sizes\n    #      (note possible overlap with fractional kernel sizes here)\n    def __init__(self, kernel_size=(1, 5), keep_size=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(MaxPooling, self).__init__(\n            kernel_size=kernel_size, keep_size=keep_size,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    def _pool_image(self, image, kernel_size_h, kernel_size_w):\n        return ia.max_pool_(\n            image,\n            (kernel_size_h, kernel_size_w)\n        )\n\n\nclass MinPooling(_AbstractPoolingBase):\n    \"\"\"\n    Apply minimum pooling to images.\n\n    This augmenter pools images with kernel sizes ``H x W`` by taking the\n    minimum pixel value over windows. For e.g. ``2 x 2`` this halves the image\n    size. Optionally, the augmenter will automatically re-upscale the image\n    to the input size (by default this is activated).\n\n    The minimum within each pixel window is always taken channelwise.\n\n    .. note::\n\n        During heatmap or segmentation map augmentation, the respective\n        arrays are not changed, only the shapes of the underlying images\n        are updated. This is because imgaug can handle maps/maks that are\n        larger/smaller than their corresponding image.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.imgaug.min_pool`.\n\n    Attributes\n    ----------\n    kernel_size : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or tuple of tuple of int or tuple of list of int or tuple of imgaug.parameters.StochasticParameter, optional\n        The kernel size of the pooling operation.\n\n        * If an int, then that value will be used for all images for both\n          kernel height and width.\n        * If a tuple ``(a, b)``, then a value from the discrete range\n          ``[a..b]`` will be sampled per image.\n        * If a list, then a random value will be sampled from that list per\n          image and used for both kernel height and width.\n        * If a StochasticParameter, then a value will be sampled per image\n          from that parameter per image and used for both kernel height and\n          width.\n        * If a tuple of tuple of int given as ``((a, b), (c, d))``, then two\n          values will be sampled independently from the discrete ranges\n          ``[a..b]`` and ``[c..d]`` per image and used as the kernel height\n          and width.\n        * If a tuple of lists of int, then two values will be sampled\n          independently per image, one from the first list and one from the\n          second, and used as the kernel height and width.\n        * If a tuple of StochasticParameter, then two values will be sampled\n          indepdently per image, one from the first parameter and one from the\n          second, and used as the kernel height and width.\n\n    keep_size : bool, optional\n        After pooling, the result image will usually have a different\n        height/width compared to the original input image. If this\n        parameter is set to True, then the pooled image will be resized\n        to the input image's size, i.e. the augmenter's output shape is always\n        identical to the input shape.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MinPooling(2)\n\n    Create an augmenter that always pools with a kernel size of ``2 x 2``.\n\n    >>> aug = iaa.MinPooling(2, keep_size=False)\n\n    Create an augmenter that always pools with a kernel size of ``2 x 2``\n    and does *not* resize back to the input image size, i.e. the resulting\n    images have half the resolution.\n\n    >>> aug = iaa.MinPooling([2, 8])\n\n    Create an augmenter that always pools either with a kernel size\n    of ``2 x 2`` or ``8 x 8``.\n\n    >>> aug = iaa.MinPooling((1, 7))\n\n    Create an augmenter that always pools with a kernel size of\n    ``1 x 1`` (does nothing) to ``7 x 7``. The kernel sizes are always\n    symmetric.\n\n    >>> aug = iaa.MinPooling(((1, 7), (1, 7)))\n\n    Create an augmenter that always pools with a kernel size of\n    ``H x W`` where ``H`` and ``W`` are both sampled independently from the\n    range ``[1..7]``. E.g. resulting kernel sizes could be ``3 x 7``\n    or ``5 x 1``.\n\n    \"\"\"\n\n    # TODO add floats as ksize denoting fractions of image sizes\n    #      (note possible overlap with fractional kernel sizes here)\n    def __init__(self, kernel_size=(1, 5), keep_size=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(MinPooling, self).__init__(\n            kernel_size=kernel_size, keep_size=keep_size,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    def _pool_image(self, image, kernel_size_h, kernel_size_w):\n        return ia.min_pool_(\n            image,\n            (kernel_size_h, kernel_size_w)\n        )\n\n\nclass MedianPooling(_AbstractPoolingBase):\n    \"\"\"\n    Apply median pooling to images.\n\n    This augmenter pools images with kernel sizes ``H x W`` by taking the\n    median pixel value over windows. For e.g. ``2 x 2`` this halves the image\n    size. Optionally, the augmenter will automatically re-upscale the image\n    to the input size (by default this is activated).\n\n    The median within each pixel window is always taken channelwise.\n\n    .. note::\n\n        During heatmap or segmentation map augmentation, the respective\n        arrays are not changed, only the shapes of the underlying images\n        are updated. This is because imgaug can handle maps/maks that are\n        larger/smaller than their corresponding image.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.imgaug.median_pool`.\n\n    Attributes\n    ----------\n    kernel_size : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or tuple of tuple of int or tuple of list of int or tuple of imgaug.parameters.StochasticParameter, optional\n        The kernel size of the pooling operation.\n\n        * If an int, then that value will be used for all images for both\n          kernel height and width.\n        * If a tuple ``(a, b)``, then a value from the discrete range\n          ``[a..b]`` will be sampled per image.\n        * If a list, then a random value will be sampled from that list per\n          image and used for both kernel height and width.\n        * If a StochasticParameter, then a value will be sampled per image\n          from that parameter per image and used for both kernel height and\n          width.\n        * If a tuple of tuple of int given as ``((a, b), (c, d))``, then two\n          values will be sampled independently from the discrete ranges\n          ``[a..b]`` and ``[c..d]`` per image and used as the kernel height\n          and width.\n        * If a tuple of lists of int, then two values will be sampled\n          independently per image, one from the first list and one from the\n          second, and used as the kernel height and width.\n        * If a tuple of StochasticParameter, then two values will be sampled\n          indepdently per image, one from the first parameter and one from the\n          second, and used as the kernel height and width.\n\n    keep_size : bool, optional\n        After pooling, the result image will usually have a different\n        height/width compared to the original input image. If this\n        parameter is set to True, then the pooled image will be resized\n        to the input image's size, i.e. the augmenter's output shape is always\n        identical to the input shape.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.MedianPooling(2)\n\n    Create an augmenter that always pools with a kernel size of ``2 x 2``.\n\n    >>> aug = iaa.MedianPooling(2, keep_size=False)\n\n    Create an augmenter that always pools with a kernel size of ``2 x 2``\n    and does *not* resize back to the input image size, i.e. the resulting\n    images have half the resolution.\n\n    >>> aug = iaa.MedianPooling([2, 8])\n\n    Create an augmenter that always pools either with a kernel size\n    of ``2 x 2`` or ``8 x 8``.\n\n    >>> aug = iaa.MedianPooling((1, 7))\n\n    Create an augmenter that always pools with a kernel size of\n    ``1 x 1`` (does nothing) to ``7 x 7``. The kernel sizes are always\n    symmetric.\n\n    >>> aug = iaa.MedianPooling(((1, 7), (1, 7)))\n\n    Create an augmenter that always pools with a kernel size of\n    ``H x W`` where ``H`` and ``W`` are both sampled independently from the\n    range ``[1..7]``. E.g. resulting kernel sizes could be ``3 x 7``\n    or ``5 x 1``.\n\n    \"\"\"\n\n    # TODO add floats as ksize denoting fractions of image sizes\n    #      (note possible overlap with fractional kernel sizes here)\n    def __init__(self, kernel_size=(1, 5), keep_size=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(MedianPooling, self).__init__(\n            kernel_size=kernel_size, keep_size=keep_size,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    def _pool_image(self, image, kernel_size_h, kernel_size_w):\n        # TODO extend pool to support pad_mode and set it here\n        #      to reflection padding\n        return ia.median_pool(\n            image,\n            (kernel_size_h, kernel_size_w)\n        )\n"
  },
  {
    "path": "imgaug/augmenters/segmentation.py",
    "content": "\"\"\"\nAugmenters that apply changes to images based on segmentation methods.\n\nList of augmenters:\n\n    * :class:`Superpixels`\n    * :class:`Voronoi`\n    * :class:`UniformVoronoi`\n    * :class:`RegularGridVoronoi`\n    * :class:`RelativeRegularGridVoronoi`\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nfrom abc import ABCMeta, abstractmethod\n\nimport numpy as np\n# use skimage.segmentation instead `from skimage import segmentation` here,\n# because otherwise unittest seems to mix up imgaug.augmenters.segmentation\n# with skimage.segmentation for whatever reason\nimport skimage.segmentation\nimport skimage.measure\nimport six\nimport six.moves as sm\n\nimport imgaug as ia\nfrom . import meta\nfrom .. import random as iarandom\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\nfrom ..imgaug import _NUMBA_INSTALLED, _numbajit\n\n\n_SLIC_SUPPORTS_START_LABEL = (\n    tuple(map(int, skimage.__version__.split(\".\")[0:2]))\n    >= (0, 17)\n)  # Added in 0.5.0.\n\n\n# TODO merge this into imresize?\ndef _ensure_image_max_size(image, max_size, interpolation):\n    \"\"\"Ensure that images do not exceed a required maximum sidelength.\n\n    This downscales to `max_size` if any side violates that maximum.\n    The other side is downscaled too so that the aspect ratio is maintained.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.imgaug.imresize_single_image`.\n\n    Parameters\n    ----------\n    image : ndarray\n        Image to potentially downscale.\n\n    max_size : int\n        Maximum length of any side of the image.\n\n    interpolation : string or int\n        See :func:`~imgaug.imgaug.imresize_single_image`.\n\n    \"\"\"\n    if max_size is not None:\n        size = max(image.shape[0], image.shape[1])\n        if size > max_size:\n            resize_factor = max_size / size\n            new_height = int(image.shape[0] * resize_factor)\n            new_width = int(image.shape[1] * resize_factor)\n            image = ia.imresize_single_image(\n                image,\n                (new_height, new_width),\n                interpolation=interpolation)\n    return image\n\n\n# TODO add compactness parameter\nclass Superpixels(meta.Augmenter):\n    \"\"\"Transform images parially/completely to their superpixel representation.\n\n    This implementation uses skimage's version of the SLIC algorithm.\n\n    .. note::\n\n        This augmenter is fairly slow. See :ref:`performance`.\n\n    **Supported dtypes**:\n\n    if (image size <= max_size):\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: limited (1)\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: limited (1)\n        * ``float16``: no (2)\n        * ``float32``: no (2)\n        * ``float64``: no (3)\n        * ``float128``: no (2)\n        * ``bool``: yes; tested\n\n        - (1) Superpixel mean intensity replacement requires computing\n              these means as ``float64`` s. This can cause inaccuracies for\n              large integer values.\n        - (2) Error in scikit-image.\n        - (3) Loss of resolution in scikit-image.\n\n    if (image size > max_size):\n\n        minimum of (\n            ``imgaug.augmenters.segmentation.Superpixels(image size <= max_size)``,\n            :func:`~imgaug.augmenters.segmentation._ensure_image_max_size`\n        )\n\n    Parameters\n    ----------\n    p_replace : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Defines for any segment the probability that the pixels within that\n        segment are replaced by their average color (otherwise, the pixels\n        are not changed).\n        Examples:\n\n            * A probability of ``0.0`` would mean, that the pixels in no\n              segment are replaced by their average color (image is not\n              changed at all).\n            * A probability of ``0.5`` would mean, that around half of all\n              segments are replaced by their average color.\n            * A probability of ``1.0`` would mean, that all segments are\n              replaced by their average color (resulting in a voronoi\n              image).\n\n        Behaviour based on chosen datatypes for this parameter:\n\n            * If a ``number``, then that ``number`` will always be used.\n            * If ``tuple`` ``(a, b)``, then a random probability will be\n              sampled from the interval ``[a, b]`` per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, it is expected to return\n              values between ``0.0`` and ``1.0`` and will be queried *for each\n              individual segment* to determine whether it is supposed to\n              be averaged (``>0.5``) or not (``<=0.5``).\n              Recommended to be some form of ``Binomial(...)``.\n\n    n_segments : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Rough target number of how many superpixels to generate (the algorithm\n        may deviate from this number). Lower value will lead to coarser\n        superpixels. Higher values are computationally more intensive and\n        will hence lead to a slowdown.\n\n            * If a single ``int``, then that value will always be used as the\n              number of segments.\n            * If a ``tuple`` ``(a, b)``, then a value from the discrete\n              interval ``[a..b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    max_size : int or None, optional\n        Maximum image size at which the augmentation is performed.\n        If the width or height of an image exceeds this value, it will be\n        downscaled before the augmentation so that the longest side\n        matches `max_size`.\n        This is done to speed up the process. The final output image has the\n        same size as the input image. Note that in case `p_replace` is below\n        ``1.0``, the down-/upscaling will affect the not-replaced pixels too.\n        Use ``None`` to apply no down-/upscaling.\n\n    interpolation : int or str, optional\n        Interpolation method to use during downscaling when `max_size` is\n        exceeded. Valid methods are the same as in\n        :func:`~imgaug.imgaug.imresize_single_image`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Superpixels(p_replace=1.0, n_segments=64)\n\n    Generate around ``64`` superpixels per image and replace all of them with\n    their average color (standard superpixel image).\n\n    >>> aug = iaa.Superpixels(p_replace=0.5, n_segments=64)\n\n    Generate around ``64`` superpixels per image and replace half of them\n    with their average color, while the other half are left unchanged (i.e.\n    they still show the input image's content).\n\n    >>> aug = iaa.Superpixels(p_replace=(0.25, 1.0), n_segments=(16, 128))\n\n    Generate between ``16`` and ``128`` superpixels per image and replace\n    ``25`` to ``100`` percent of them with their average color.\n\n    \"\"\"\n\n    def __init__(self, p_replace=(0.5, 1.0), n_segments=(50, 120),\n                 max_size=128, interpolation=\"linear\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Superpixels, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.p_replace = iap.handle_probability_param(\n            p_replace, \"p_replace\", tuple_to_uniform=True, list_to_choice=True)\n        self.n_segments = iap.handle_discrete_param(\n            n_segments, \"n_segments\", value_range=(1, None),\n            tuple_to_uniform=True, list_to_choice=True, allow_floats=False)\n        self.max_size = max_size\n        self.interpolation = interpolation\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        iadt.gate_dtypes_strs(\n            images,\n            allowed=\"bool uint8 uint16 uint32 uint64 int8 int16 int32 int64\",\n            disallowed=\"float16 float32 float64 float128\",\n            augmenter=self\n        )\n\n        nb_images = len(images)\n        rss = random_state.duplicate(1+nb_images)\n        n_segments_samples = self.n_segments.draw_samples(\n            (nb_images,), random_state=rss[0])\n\n        # We cant reduce images to 0 or less segments, hence we pick the\n        # lowest possible value in these cases (i.e. 1). The alternative\n        # would be to not perform superpixel detection in these cases\n        # (akin to n_segments=#pixels).\n        # TODO add test for this\n        n_segments_samples = np.clip(n_segments_samples, 1, None)\n\n        for i, (image, rs) in enumerate(zip(images, rss[1:])):\n            if image.size == 0:\n                # Image with 0-sized axis, nothing to change.\n                # Placing this before the sampling step should be fine.\n                continue\n\n            replace_samples = self.p_replace.draw_samples(\n                (n_segments_samples[i],), random_state=rs)\n\n            if np.max(replace_samples) == 0:\n                # not a single superpixel would be replaced by its average\n                # color, i.e. the image would not be changed, so just keep it\n                continue\n\n            orig_shape = image.shape\n            image = _ensure_image_max_size(image, self.max_size,\n                                           self.interpolation)\n\n            # skimage 0.17+ introduces the start_label arg and produces a\n            # warning if it is not provided. We use start_label=0 here\n            # (old skimage style) (not entirely sure if =0 is required or =1\n            # could be used here too, but *seems* like both could work),\n            # but skimage will change the default start_label to 1 in the\n            # future.\n            kwargs = (\n                {\"start_label\": 0}\n                if _SLIC_SUPPORTS_START_LABEL\n                else {}\n            )\n\n            segments = skimage.segmentation.slic(\n                image,\n                n_segments=n_segments_samples[i],\n                compactness=10,\n                **kwargs\n            )\n\n            image_aug = replace_segments_(\n                image, segments, replace_samples > 0.5\n            )\n\n            if orig_shape != image_aug.shape:\n                image_aug = ia.imresize_single_image(\n                    image_aug,\n                    orig_shape[0:2],\n                    interpolation=self.interpolation)\n\n            batch.images[i] = image_aug\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.p_replace, self.n_segments, self.max_size,\n                self.interpolation]\n\n\n# TODO add the old skimage method here for 512x512+ images as it starts to\n#      be faster for these areas\n# TODO incorporate this dtype support in the dtype sections of docstrings for\n#      Superpixels and segment_voronoi()\ndef replace_segments_(image, segments, replace_flags):\n    \"\"\"Replace segments in images by their average colors in-place.\n\n    This expects an image ``(H,W,[C])`` and an integer segmentation\n    map ``(H,W)``. The segmentation map must contain the same id for pixels\n    that are supposed to be replaced by the same color (\"segments\").\n    For each segement, the average color is computed and used as the\n    replacement.\n\n    Added in 0.5.0.\n\n    **Supported dtypes**:\n\n    * ``uint8``: yes; indirectly tested\n    * ``uint16``: yes; indirectly tested\n    * ``uint32``: yes; indirectly tested\n    * ``uint64``: no; not tested\n    * ``int8``: yes; indirectly tested\n    * ``int16``: yes; indirectly tested\n    * ``int32``: yes; indirectly tested\n    * ``int64``: no; not tested\n    * ``float16``: ?; not tested\n    * ``float32``: ?; not tested\n    * ``float64``: ?; not tested\n    * ``float128``: ?; not tested\n    * ``bool``: yes; indirectly tested\n\n    Parameters\n    ----------\n    image : ndarray\n        An image of shape ``(H,W,[C])``.\n        This image may be changed in-place.\n        The function is currently not tested for float dtypes.\n\n    segments : ndarray\n        A ``(H,W)`` integer array containing the same ids for pixels belonging\n        to the same segment.\n\n    replace_flags : ndarray or None\n        A boolean array containing at the ``i`` th index a flag denoting\n        whether the segment with id ``i`` should be replaced by its average\n        color. If the flag is ``False``, the original image pixels will be\n        kept unchanged for that flag.\n        If this is ``None``, all segments will be replaced.\n\n    Returns\n    -------\n    ndarray\n        The image with replaced pixels.\n        Might be the same image as was provided via `image`.\n\n    \"\"\"\n    assert replace_flags is None or replace_flags.dtype.kind == \"b\"\n\n    input_shape = image.shape\n    if 0 in image.shape:\n        return image\n\n    if len(input_shape) == 2:\n        image = image[:, :, np.newaxis]\n\n    nb_segments = None\n    bad_dtype = (\n        image.dtype not in {iadt._UINT8_DTYPE, iadt._INT8_DTYPE}\n    )\n    if bad_dtype or not _NUMBA_INSTALLED:\n        func = _replace_segments_np_\n    else:\n        max_id = np.max(segments)\n        nb_segments = 1 + max_id\n        func = _replace_segments_numba_dispatcher_\n\n    result = func(image, segments, replace_flags, nb_segments)\n\n    if len(input_shape) == 2:\n        return result[:, :, 0]\n    return result\n\n\n# Added in 0.5.0.\ndef _replace_segments_np_(image, segments, replace_flags, _nb_segments):\n    seg_ids = np.unique(segments)\n    if replace_flags is None:\n        replace_flags = np.ones((len(seg_ids),), dtype=bool)\n    for i, seg_id in enumerate(seg_ids):\n        if replace_flags[i % len(replace_flags)]:\n            mask = (segments == seg_id)\n            mean_color = np.average(image[mask, :], axis=(0,))\n            image[mask] = mean_color\n    return image\n\n\n# Added in 0.5.0.\ndef _replace_segments_numba_dispatcher_(\n        image, segments, replace_flags, nb_segments\n):\n    if replace_flags is None:\n        replace_flags = np.ones((nb_segments,), dtype=bool)\n    elif not np.any(replace_flags[:nb_segments]):\n        return image\n\n    average_colors = _replace_segments_numba_collect_avg_colors(\n        image,\n        segments,\n        replace_flags,\n        nb_segments,\n        image.dtype\n    )\n    image = _replace_segments_numba_apply_avg_cols_(\n        image, segments, replace_flags, average_colors\n    )\n    return image\n\n\n# Added in 0.5.0.\n@_numbajit(nopython=True, nogil=True, cache=True)\ndef _replace_segments_numba_collect_avg_colors(\n        image, segments, replace_flags, nb_segments, output_dtype\n):\n    height, width, nb_channels = image.shape\n    nb_flags = len(replace_flags)\n\n    average_colors = np.zeros((nb_segments, nb_channels), dtype=np.float64)\n\n    counters = np.zeros((nb_segments,), dtype=np.int32)\n    for seg_id in sm.xrange(nb_segments):\n        if not replace_flags[seg_id % nb_flags]:\n            counters[seg_id] = -1\n\n    for y in sm.xrange(height):\n        for x in sm.xrange(width):\n            seg_id = segments[y, x]\n            count = counters[seg_id]\n\n            if count != -1:\n                col = image[y, x, :]\n                average_colors[seg_id] += col\n                counters[seg_id] += 1\n\n    counters = np.maximum(counters, 1)\n    counters = counters.reshape((-1, 1))\n    average_colors /= counters\n\n    average_colors = average_colors.astype(output_dtype)\n    return average_colors\n\n\n# Added in 0.5.0.\n@_numbajit(nopython=True, nogil=True, cache=True)\ndef _replace_segments_numba_apply_avg_cols_(\n        image, segments, replace_flags, average_colors\n):\n    height, width = image.shape[0:2]\n    nb_flags = len(replace_flags)\n\n    for y in sm.xrange(height):\n        for x in sm.xrange(width):\n            seg_id = segments[y, x]\n            if replace_flags[seg_id % nb_flags]:\n                image[y, x, :] = average_colors[seg_id]\n\n    return image\n\n\n# TODO don't average the alpha channel for RGBA?\ndef segment_voronoi(image, cell_coordinates, replace_mask=None):\n    \"\"\"Average colors within voronoi cells of an image.\n\n    **Supported dtypes**:\n\n    if (image size <= max_size):\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no; not tested\n        * ``uint32``: no; not tested\n        * ``uint64``: no; not tested\n        * ``int8``: no; not tested\n        * ``int16``: no; not tested\n        * ``int32``: no; not tested\n        * ``int64``: no; not tested\n        * ``float16``: no; not tested\n        * ``float32``: no; not tested\n        * ``float64``: no; not tested\n        * ``float128``: no; not tested\n        * ``bool``: no; not tested\n\n    if (image size > max_size):\n\n        minimum of (\n            ``imgaug.augmenters.segmentation.Voronoi(image size <= max_size)``,\n            :func:`~imgaug.augmenters.segmentation._ensure_image_max_size`\n        )\n\n    Parameters\n    ----------\n    image : ndarray\n        The image to convert to a voronoi image. May be ``HxW`` or\n        ``HxWxC``. Note that for ``RGBA`` images the alpha channel\n        will currently also by averaged.\n\n    cell_coordinates : ndarray\n        A ``Nx2`` float array containing the center coordinates of voronoi\n        cells on the image. Values are expected to be in the interval\n        ``[0.0, height-1.0]`` for the y-axis (x-axis analogous).\n        If this array contains no coordinate, the image will not be\n        changed.\n\n    replace_mask : None or ndarray, optional\n        Boolean mask of the same length as `cell_coordinates`, denoting\n        for each cell whether its pixels are supposed to be replaced\n        by the cell's average color (``True``) or left untouched (``False``).\n        If this is set to ``None``, all cells will be replaced.\n\n    Returns\n    -------\n    ndarray\n        Voronoi image.\n\n    \"\"\"\n    input_dims = image.ndim\n    if input_dims == 2:\n        image = image[..., np.newaxis]\n\n    if len(cell_coordinates) <= 0:\n        if input_dims == 2:\n            return image[..., 0]\n        return image\n\n    height, width = image.shape[0:2]\n    ids_of_nearest_cells = \\\n        _match_pixels_with_voronoi_cells(height, width, cell_coordinates)\n    image_aug = replace_segments_(\n        image,\n        ids_of_nearest_cells.reshape(image.shape[0:2]),\n        replace_mask\n    )\n\n    if input_dims == 2:\n        return image_aug[..., 0]\n    return image_aug\n\n\ndef _match_pixels_with_voronoi_cells(height, width, cell_coordinates):\n    # deferred import so that scipy is an optional dependency\n    from scipy.spatial import cKDTree as KDTree  # TODO add scipy for reqs\n    tree = KDTree(cell_coordinates)\n    pixel_coords = _generate_pixel_coords(height, width)\n    pixel_coords_subpixel = pixel_coords.astype(np.float32) + 0.5\n    ids_of_nearest_cells = tree.query(pixel_coords_subpixel)[1]\n    return ids_of_nearest_cells\n\n\ndef _generate_pixel_coords(height, width):\n    xx, yy = np.meshgrid(np.arange(width), np.arange(height))\n    return np.c_[xx.ravel(), yy.ravel()]\n\n\n# TODO this can be reduced down to a similar problem as Superpixels:\n#      generate an integer-based class id map of segments, then replace all\n#      segments with the same class id by the average color within that\n#      segment\nclass Voronoi(meta.Augmenter):\n    \"\"\"Average colors of an image within Voronoi cells.\n\n    This augmenter performs the following steps:\n\n        1. Query `points_sampler` to sample random coordinates of cell\n           centers. On the image.\n        2. Estimate for each pixel to which voronoi cell (i.e. segment)\n           it belongs. Each pixel belongs to the cell with the closest center\n           coordinate (euclidean distance).\n        3. Compute for each cell the average color of the pixels within it.\n        4. Replace the pixels of `p_replace` percent of all cells by their\n           average color. Do not change the pixels of ``(1 - p_replace)``\n           percent of all cells. (The percentages are average values over\n           many images. Some images may get more/less cells replaced by\n           their average color.)\n\n    This code is very loosely based on\n    https://codegolf.stackexchange.com/questions/50299/draw-an-image-as-a-voronoi-map/50345#50345\n\n    **Supported dtypes**:\n\n    See :func:`imgaug.augmenters.segmentation.segment_voronoi`.\n\n    Parameters\n    ----------\n    points_sampler : IPointsSampler\n        A points sampler which will be queried per image to generate the\n        coordinates of the centers of voronoi cells.\n\n    p_replace : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Defines for any segment the probability that the pixels within that\n        segment are replaced by their average color (otherwise, the pixels\n        are not changed).\n        Examples:\n\n            * A probability of ``0.0`` would mean, that the pixels in no\n              segment are replaced by their average color (image is not\n              changed at all).\n            * A probability of ``0.5`` would mean, that around half of all\n              segments are replaced by their average color.\n            * A probability of ``1.0`` would mean, that all segments are\n              replaced by their average color (resulting in a voronoi\n              image).\n\n        Behaviour based on chosen datatypes for this parameter:\n\n            * If a ``number``, then that ``number`` will always be used.\n            * If ``tuple`` ``(a, b)``, then a random probability will be\n              sampled from the interval ``[a, b]`` per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, it is expected to return\n              values between ``0.0`` and ``1.0`` and will be queried *for each\n              individual segment* to determine whether it is supposed to\n              be averaged (``>0.5``) or not (``<=0.5``).\n              Recommended to be some form of ``Binomial(...)``.\n\n    max_size : int or None, optional\n        Maximum image size at which the augmentation is performed.\n        If the width or height of an image exceeds this value, it will be\n        downscaled before the augmentation so that the longest side\n        matches `max_size`.\n        This is done to speed up the process. The final output image has the\n        same size as the input image. Note that in case `p_replace` is below\n        ``1.0``, the down-/upscaling will affect the not-replaced pixels too.\n        Use ``None`` to apply no down-/upscaling.\n\n    interpolation : int or str, optional\n        Interpolation method to use during downscaling when `max_size` is\n        exceeded. Valid methods are the same as in\n        :func:`~imgaug.imgaug.imresize_single_image`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> points_sampler = iaa.RegularGridPointsSampler(n_cols=20, n_rows=40)\n    >>> aug = iaa.Voronoi(points_sampler)\n\n    Create an augmenter that places a ``20x40`` (``HxW``) grid of cells on\n    the image and replaces all pixels within each cell by the cell's average\n    color. The process is performed at an image size not exceeding ``128`` px\n    on any side (default). If necessary, the downscaling is performed using\n    ``linear`` interpolation (default).\n\n    >>> points_sampler = iaa.DropoutPointsSampler(\n    >>>     iaa.RelativeRegularGridPointsSampler(\n    >>>         n_cols_frac=(0.05, 0.2),\n    >>>         n_rows_frac=0.1),\n    >>>     0.2)\n    >>> aug = iaa.Voronoi(points_sampler, p_replace=0.9, max_size=None)\n\n    Create a voronoi augmenter that generates a grid of cells dynamically\n    adapted to the image size. Larger images get more cells. On the x-axis,\n    the distance between two cells is ``w * W`` pixels, where ``W`` is the\n    width of the image and ``w`` is always ``0.1``. On the y-axis,\n    the distance between two cells is ``h * H`` pixels, where ``H`` is the\n    height of the image and ``h`` is sampled uniformly from the interval\n    ``[0.05, 0.2]``. To make the voronoi pattern less regular, about ``20``\n    percent of the cell coordinates are randomly dropped (i.e. the remaining\n    cells grow in size). In contrast to the first example, the image is not\n    resized (if it was, the sampling would happen *after* the resizing,\n    which would affect ``W`` and ``H``). Not all voronoi cells are replaced\n    by their average color, only around ``90`` percent of them. The\n    remaining ``10`` percent's pixels remain unchanged.\n\n    \"\"\"\n\n    def __init__(self, points_sampler, p_replace=1.0, max_size=128,\n                 interpolation=\"linear\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Voronoi, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        assert isinstance(points_sampler, IPointsSampler), (\n            \"Expected 'points_sampler' to be an instance of IPointsSampler, \"\n            \"got %s.\" % (type(points_sampler),))\n        self.points_sampler = points_sampler\n\n        self.p_replace = iap.handle_probability_param(\n            p_replace, \"p_replace\", tuple_to_uniform=True, list_to_choice=True)\n\n        self.max_size = max_size\n        self.interpolation = interpolation\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        iadt.allow_only_uint8(images, augmenter=self)\n\n        rss = random_state.duplicate(len(images))\n        for i, (image, rs) in enumerate(zip(images, rss)):\n            batch.images[i] = self._augment_single_image(image, rs)\n        return batch\n\n    def _augment_single_image(self, image, random_state):\n        rss = random_state.duplicate(2)\n        orig_shape = image.shape\n        image = _ensure_image_max_size(image, self.max_size, self.interpolation)\n\n        cell_coordinates = self.points_sampler.sample_points([image], rss[0])[0]\n        p_replace = self.p_replace.draw_samples((len(cell_coordinates),),\n                                                rss[1])\n        replace_mask = (p_replace > 0.5)\n\n        image_aug = segment_voronoi(image, cell_coordinates, replace_mask)\n\n        if orig_shape != image_aug.shape:\n            image_aug = ia.imresize_single_image(\n                image_aug,\n                orig_shape[0:2],\n                interpolation=self.interpolation)\n\n        return image_aug\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.points_sampler, self.p_replace, self.max_size,\n                self.interpolation]\n\n\nclass UniformVoronoi(Voronoi):\n    \"\"\"Uniformly sample Voronoi cells on images and average colors within them.\n\n    This augmenter is a shortcut for the combination of\n    :class:`~imgaug.augmenters.segmentation.Voronoi` with\n    :class:`~imgaug.augmenters.segmentation.UniformPointsSampler`. Hence, it\n    generates a fixed amount of ``N`` random coordinates of voronoi cells on\n    each image. The cell coordinates are sampled uniformly using the image\n    height and width as maxima.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.segmentation.Voronoi`.\n\n    Parameters\n    ----------\n    n_points : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of points to sample on each image.\n\n            * If a single ``int``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value from the discrete\n              interval ``[a..b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    p_replace : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Defines for any segment the probability that the pixels within that\n        segment are replaced by their average color (otherwise, the pixels\n        are not changed).\n        Examples:\n\n            * A probability of ``0.0`` would mean, that the pixels in no\n              segment are replaced by their average color (image is not\n              changed at all).\n            * A probability of ``0.5`` would mean, that around half of all\n              segments are replaced by their average color.\n            * A probability of ``1.0`` would mean, that all segments are\n              replaced by their average color (resulting in a voronoi\n              image).\n\n        Behaviour based on chosen datatypes for this parameter:\n\n            * If a ``number``, then that ``number`` will always be used.\n            * If ``tuple`` ``(a, b)``, then a random probability will be\n              sampled from the interval ``[a, b]`` per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, it is expected to return\n              values between ``0.0`` and ``1.0`` and will be queried *for each\n              individual segment* to determine whether it is supposed to\n              be averaged (``>0.5``) or not (``<=0.5``).\n              Recommended to be some form of ``Binomial(...)``.\n\n    max_size : int or None, optional\n        Maximum image size at which the augmentation is performed.\n        If the width or height of an image exceeds this value, it will be\n        downscaled before the augmentation so that the longest side\n        matches `max_size`.\n        This is done to speed up the process. The final output image has the\n        same size as the input image. Note that in case `p_replace` is below\n        ``1.0``, the down-/upscaling will affect the not-replaced pixels too.\n        Use ``None`` to apply no down-/upscaling.\n\n    interpolation : int or str, optional\n        Interpolation method to use during downscaling when `max_size` is\n        exceeded. Valid methods are the same as in\n        :func:`~imgaug.imgaug.imresize_single_image`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.UniformVoronoi((100, 500))\n\n    Sample for each image uniformly the number of voronoi cells ``N`` from the\n    interval ``[100, 500]``. Then generate ``N`` coordinates by sampling\n    uniformly the x-coordinates from ``[0, W]`` and the y-coordinates from\n    ``[0, H]``, where ``H`` is the image height and ``W`` the image width.\n    Then use these coordinates to group the image pixels into voronoi\n    cells and average the colors within them. The process is performed at an\n    image size not exceeding ``128`` px on any side (default). If necessary,\n    the downscaling is performed using ``linear`` interpolation (default).\n\n    >>> aug = iaa.UniformVoronoi(250, p_replace=0.9, max_size=None)\n\n    Same as above, but always samples ``N=250`` cells, replaces only\n    ``90`` percent of them with their average color (the pixels of the\n    remaining ``10`` percent are not changed) and performs the transformation\n    at the original image size (``max_size=None``).\n\n    \"\"\"\n\n    def __init__(self, n_points=(50, 500), p_replace=(0.5, 1.0), max_size=128,\n                 interpolation=\"linear\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(UniformVoronoi, self).__init__(\n            points_sampler=UniformPointsSampler(n_points),\n            p_replace=p_replace,\n            max_size=max_size,\n            interpolation=interpolation,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass RegularGridVoronoi(Voronoi):\n    \"\"\"Sample Voronoi cells from regular grids and color-average them.\n\n    This augmenter is a shortcut for the combination of\n    :class:`~imgaug.augmenters.segmentation.Voronoi`,\n    :class:`~imgaug.augmenters.segmentation.RegularGridPointsSampler` and\n    :class:`~imgaug.augmenters.segmentation.DropoutPointsSampler`. Hence, it\n    generates a regular grid with ``R`` rows and ``C`` columns of coordinates\n    on each image. Then, it drops ``p`` percent of the ``R*C`` coordinates\n    to randomize the grid. Each image pixel then belongs to the voronoi\n    cell with the closest coordinate.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.segmentation.Voronoi`.\n\n    Parameters\n    ----------\n    n_rows : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of rows of coordinates to place on each image, i.e. the number\n        of coordinates on the y-axis. Note that for each image, the sampled\n        value is clipped to the interval ``[1..H]``, where ``H`` is the image\n        height.\n\n            * If a single ``int``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value from the discrete\n              interval ``[a..b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    n_cols : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of columns of coordinates to place on each image, i.e. the\n        number of coordinates on the x-axis. Note that for each image, the\n        sampled value is clipped to the interval ``[1..W]``, where ``W`` is\n        the image width.\n\n            * If a single ``int``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value from the discrete\n              interval ``[a..b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    p_drop_points : number or tuple of number or imgaug.parameters.StochasticParameter, optional\n        The probability that a coordinate will be removed from the list\n        of all sampled coordinates. A value of ``1.0`` would mean that (on\n        average) ``100`` percent of all coordinates will be dropped,\n        while ``0.0`` denotes ``0`` percent. Note that this sampler will\n        always ensure that at least one coordinate is left after the dropout\n        operation, i.e. even ``1.0`` will only drop all *except one*\n        coordinate.\n\n            * If a ``float``, then that value will be used for all images.\n            * If a ``tuple`` ``(a, b)``, then a value ``p`` will be sampled\n              from the interval ``[a, b]`` per image.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              determine per coordinate whether it should be *kept* (sampled\n              value of ``>0.5``) or shouldn't be kept (sampled value of\n              ``<=0.5``). If you instead want to provide the probability as\n              a stochastic parameter, you can usually do\n              ``imgaug.parameters.Binomial(1-p)`` to convert parameter `p` to\n              a 0/1 representation.\n\n    p_replace : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Defines for any segment the probability that the pixels within that\n        segment are replaced by their average color (otherwise, the pixels\n        are not changed).\n        Examples:\n\n            * A probability of ``0.0`` would mean, that the pixels in no\n              segment are replaced by their average color (image is not\n              changed at all).\n            * A probability of ``0.5`` would mean, that around half of all\n              segments are replaced by their average color.\n            * A probability of ``1.0`` would mean, that all segments are\n              replaced by their average color (resulting in a voronoi\n              image).\n\n        Behaviour based on chosen datatypes for this parameter:\n\n            * If a ``number``, then that number will always be used.\n            * If ``tuple`` ``(a, b)``, then a random probability will be\n              sampled from the interval ``[a, b]`` per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, it is expected to return\n              values between ``0.0`` and ``1.0`` and will be queried *for each\n              individual segment* to determine whether it is supposed to\n              be averaged (``>0.5``) or not (``<=0.5``).\n              Recommended to be some form of ``Binomial(...)``.\n\n    max_size : int or None, optional\n        Maximum image size at which the augmentation is performed.\n        If the width or height of an image exceeds this value, it will be\n        downscaled before the augmentation so that the longest side\n        matches `max_size`.\n        This is done to speed up the process. The final output image has the\n        same size as the input image. Note that in case `p_replace` is below\n        ``1.0``, the down-/upscaling will affect the not-replaced pixels too.\n        Use ``None`` to apply no down-/upscaling.\n\n    interpolation : int or str, optional\n        Interpolation method to use during downscaling when `max_size` is\n        exceeded. Valid methods are the same as in\n        :func:`~imgaug.imgaug.imresize_single_image`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.RegularGridVoronoi(10, 20)\n\n    Place a regular grid of ``10x20`` (``height x width``) coordinates on\n    each image. Randomly drop on average ``20`` percent of these points\n    to create a less regular pattern. Then use the remaining coordinates\n    to group the image pixels into voronoi cells and average the colors\n    within them. The process is performed at an image size not exceeding\n    ``128`` px on any side (default). If necessary, the downscaling is\n    performed using ``linear`` interpolation (default).\n\n    >>> aug = iaa.RegularGridVoronoi(\n    >>>     (10, 30), 20, p_drop_points=0.0, p_replace=0.9, max_size=None)\n\n    Same as above, generates a grid with randomly ``10`` to ``30`` rows,\n    drops none of the generates points, replaces only ``90`` percent of\n    the voronoi cells with their average color (the pixels of the remaining\n    ``10`` percent are not changed) and performs the transformation\n    at the original image size (``max_size=None``).\n\n    \"\"\"\n\n    def __init__(self, n_rows=(10, 30), n_cols=(10, 30),\n                 p_drop_points=(0.0, 0.5), p_replace=(0.5, 1.0),\n                 max_size=128, interpolation=\"linear\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(RegularGridVoronoi, self).__init__(\n            points_sampler=DropoutPointsSampler(\n                RegularGridPointsSampler(n_rows, n_cols),\n                p_drop_points\n            ),\n            p_replace=p_replace,\n            max_size=max_size,\n            interpolation=interpolation,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass RelativeRegularGridVoronoi(Voronoi):\n    \"\"\"Sample Voronoi cells from image-dependent grids and color-average them.\n\n    This augmenter is a shortcut for the combination of\n    :class:`~imgaug.augmenters.segmentation.Voronoi`,\n    :class:`~imgaug.augmenters.segmentation.RegularGridPointsSampler` and\n    :class:`~imgaug.augmenters.segmentation.DropoutPointsSampler`. Hence, it\n    generates a regular grid with ``R`` rows and ``C`` columns of coordinates\n    on each image. Then, it drops ``p`` percent of the ``R*C`` coordinates\n    to randomize the grid. Each image pixel then belongs to the voronoi\n    cell with the closest coordinate.\n\n    .. note::\n\n        In contrast to the other voronoi augmenters, this one uses\n        ``None`` as the default value for `max_size`, i.e. the color averaging\n        is always performed at full resolution. This enables the augmenter to\n        make use of the additional points on larger images. It does\n        however slow down the augmentation process.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.segmentation.Voronoi`.\n\n    Parameters\n    ----------\n    n_rows_frac : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Relative number of coordinates to place on the y-axis. For a value\n        ``y`` and image height ``H`` the number of actually placed coordinates\n        (i.e. computed rows) is given by ``int(round(y*H))``.\n        Note that for each image, the number of coordinates is clipped to the\n        interval ``[1,H]``, where ``H`` is the image height.\n\n            * If a single ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value from the interval\n              ``[a, b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    n_cols_frac : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Relative number of coordinates to place on the x-axis. For a value\n        ``x`` and image height ``W`` the number of actually placed coordinates\n        (i.e. computed columns) is given by ``int(round(x*W))``.\n        Note that for each image, the number of coordinates is clipped to the\n        interval ``[1,W]``, where ``W`` is the image width.\n\n            * If a single ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value from the interval\n              ``[a, b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    p_drop_points : number or tuple of number or imgaug.parameters.StochasticParameter, optional\n        The probability that a coordinate will be removed from the list\n        of all sampled coordinates. A value of ``1.0`` would mean that (on\n        average) ``100`` percent of all coordinates will be dropped,\n        while ``0.0`` denotes ``0`` percent. Note that this sampler will\n        always ensure that at least one coordinate is left after the dropout\n        operation, i.e. even ``1.0`` will only drop all *except one*\n        coordinate.\n\n            * If a ``float``, then that value will be used for all images.\n            * If a ``tuple`` ``(a, b)``, then a value ``p`` will be sampled\n              from the interval ``[a, b]`` per image.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              determine per coordinate whether it should be *kept* (sampled\n              value of ``>0.5``) or shouldn't be kept (sampled value of\n              ``<=0.5``). If you instead want to provide the probability as\n              a stochastic parameter, you can usually do\n              ``imgaug.parameters.Binomial(1-p)`` to convert parameter `p` to\n              a 0/1 representation.\n\n    p_replace : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Defines for any segment the probability that the pixels within that\n        segment are replaced by their average color (otherwise, the pixels\n        are not changed).\n        Examples:\n\n            * A probability of ``0.0`` would mean, that the pixels in no\n              segment are replaced by their average color (image is not\n              changed at all).\n            * A probability of ``0.5`` would mean, that around half of all\n              segments are replaced by their average color.\n            * A probability of ``1.0`` would mean, that all segments are\n              replaced by their average color (resulting in a voronoi\n              image).\n\n        Behaviour based on chosen datatypes for this parameter:\n\n            * If a ``number``, then that ``number`` will always be used.\n            * If ``tuple`` ``(a, b)``, then a random probability will be\n              sampled from the interval ``[a, b]`` per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, it is expected to return\n              values between ``0.0`` and ``1.0`` and will be queried *for each\n              individual segment* to determine whether it is supposed to\n              be averaged (``>0.5``) or not (``<=0.5``).\n              Recommended to be some form of ``Binomial(...)``.\n\n    max_size : int or None, optional\n        Maximum image size at which the augmentation is performed.\n        If the width or height of an image exceeds this value, it will be\n        downscaled before the augmentation so that the longest side\n        matches `max_size`.\n        This is done to speed up the process. The final output image has the\n        same size as the input image. Note that in case `p_replace` is below\n        ``1.0``, the down-/upscaling will affect the not-replaced pixels too.\n        Use ``None`` to apply no down-/upscaling.\n\n    interpolation : int or str, optional\n        Interpolation method to use during downscaling when `max_size` is\n        exceeded. Valid methods are the same as in\n        :func:`~imgaug.imgaug.imresize_single_image`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.RelativeRegularGridVoronoi(0.1, 0.25)\n\n    Place a regular grid of ``R x C`` coordinates on each image, where\n    ``R`` is the number of rows and computed as ``R=0.1*H`` with ``H`` being\n    the height of the input image. ``C`` is the number of columns and\n    analogously estimated from the image width ``W`` as ``C=0.25*W``.\n    Larger images will lead to larger ``R`` and ``C`` values.\n    On average, ``20`` percent of these grid coordinates are randomly\n    dropped to create a less regular pattern. Then, the remaining coordinates\n    are used to group the image pixels into voronoi cells and the colors\n    within them are averaged.\n\n    >>> aug = iaa.RelativeRegularGridVoronoi(\n    >>>     (0.03, 0.1), 0.1, p_drop_points=0.0, p_replace=0.9, max_size=512)\n\n    Same as above, generates a grid with randomly ``R=r*H`` rows, where\n    ``r`` is sampled uniformly from the interval ``[0.03, 0.1]`` and\n    ``C=0.1*W`` rows. No points are dropped. The augmenter replaces only\n    ``90`` percent of the voronoi cells with their average color (the pixels\n    of the remaining ``10`` percent are not changed). Images larger than\n    ``512`` px are temporarily downscaled (*before* sampling the grid points)\n    so that no side exceeds ``512`` px. This improves performance, but\n    degrades the quality of the resulting image.\n\n    \"\"\"\n\n    def __init__(self, n_rows_frac=(0.05, 0.15), n_cols_frac=(0.05, 0.15),\n                 p_drop_points=(0.0, 0.5), p_replace=(0.5, 1.0),\n                 max_size=None, interpolation=\"linear\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(RelativeRegularGridVoronoi, self).__init__(\n            points_sampler=DropoutPointsSampler(\n                RelativeRegularGridPointsSampler(n_rows_frac, n_cols_frac),\n                p_drop_points\n            ),\n            p_replace=p_replace,\n            max_size=max_size,\n            interpolation=interpolation,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n@six.add_metaclass(ABCMeta)\nclass IPointsSampler(object):\n    \"\"\"Interface for all point samplers.\n\n    Point samplers return coordinate arrays of shape ``Nx2``.\n    These coordinates can be used in other augmenters, see e.g.\n    :class:`~imgaug.augmenters.segmentation.Voronoi`.\n\n    \"\"\"\n\n    @abstractmethod\n    def sample_points(self, images, random_state):\n        \"\"\"Generate coordinates of points on images.\n\n        Parameters\n        ----------\n        images : ndarray or list of ndarray\n            One or more images for which to generate points.\n            If this is a ``list`` of arrays, each one of them is expected to\n            have three dimensions.\n            If this is an array, it must be four-dimensional and the first\n            axis is expected to denote the image index. For ``RGB`` images\n            the array would hence have to be of shape ``(N, H, W, 3)``.\n\n        random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState\n            A random state to use for any probabilistic function required\n            during the point sampling.\n            See :func:`~imgaug.random.RNG` for details.\n\n        Returns\n        -------\n        ndarray\n            An ``(N,2)`` ``float32`` array containing ``(x,y)`` subpixel\n            coordinates, all of which being within the intervals\n            ``[0.0, width]`` and ``[0.0, height]``.\n\n        \"\"\"\n\n\ndef _verify_sample_points_images(images):\n    assert len(images) > 0, \"Expected at least one image, got zero.\"\n    if isinstance(images, list):\n        assert all([ia.is_np_array(image) for image in images]), (\n            \"Expected list of numpy arrays, got list of types %s.\" % (\n                \", \".join([str(type(image)) for image in images]),))\n        assert all([image.ndim == 3 for image in images]), (\n            \"Expected each image to have three dimensions, \"\n            \"got dimensions %s.\" % (\n                \", \".join([str(image.ndim) for image in images]),))\n    else:\n        assert ia.is_np_array(images), (\n            \"Expected either a list of numpy arrays or a single numpy \"\n            \"array of shape NxHxWxC. Got type %s.\" % (type(images),))\n        assert images.ndim == 4, (\n            \"Expected a four-dimensional array of shape NxHxWxC. \"\n            \"Got shape %d dimensions (shape: %s).\" % (\n                images.ndim, images.shape))\n\n\nclass RegularGridPointsSampler(IPointsSampler):\n    \"\"\"Sampler that generates a regular grid of coordinates on an image.\n\n    'Regular grid' here means that on each axis all coordinates have the\n    same distance from each other. Note that the distance may change between\n    axis.\n\n    Parameters\n    ----------\n    n_rows : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of rows of coordinates to place on each image, i.e. the number\n        of coordinates on the y-axis. Note that for each image, the sampled\n        value is clipped to the interval ``[1..H]``, where ``H`` is the image\n        height.\n\n            * If a single ``int``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value from the discrete\n              interval ``[a..b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    n_cols : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of columns of coordinates to place on each image, i.e. the\n        number of coordinates on the x-axis. Note that for each image, the\n        sampled value is clipped to the interval ``[1..W]``, where ``W`` is\n        the image width.\n\n            * If a single ``int``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value from the discrete\n              interval ``[a..b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> sampler = iaa.RegularGridPointsSampler(\n    >>>     n_rows=(5, 20),\n    >>>     n_cols=50)\n\n    Create a point sampler that generates regular grids of points. These grids\n    contain ``r`` points on the y-axis, where ``r`` is sampled\n    uniformly from the discrete interval ``[5..20]`` per image.\n    On the x-axis, the grids always contain ``50`` points.\n\n    \"\"\"\n\n    def __init__(self, n_rows, n_cols):\n        self.n_rows = iap.handle_discrete_param(\n            n_rows, \"n_rows\", value_range=(1, None),\n            tuple_to_uniform=True, list_to_choice=True, allow_floats=False)\n        self.n_cols = iap.handle_discrete_param(\n            n_cols, \"n_cols\", value_range=(1, None),\n            tuple_to_uniform=True, list_to_choice=True, allow_floats=False)\n\n    def sample_points(self, images, random_state):\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        _verify_sample_points_images(images)\n\n        n_rows_lst, n_cols_lst = self._draw_samples(images, random_state)\n        return self._generate_point_grids(images, n_rows_lst, n_cols_lst)\n\n    def _draw_samples(self, images, random_state):\n        rss = random_state.duplicate(2)\n        n_rows_lst = self.n_rows.draw_samples(len(images), random_state=rss[0])\n        n_cols_lst = self.n_cols.draw_samples(len(images), random_state=rss[1])\n        return self._clip_rows_and_cols(n_rows_lst, n_cols_lst, images)\n\n    @classmethod\n    def _clip_rows_and_cols(cls, n_rows_lst, n_cols_lst, images):\n        heights = np.int32([image.shape[0] for image in images])\n        widths = np.int32([image.shape[1] for image in images])\n        # We clip intentionally not to H-1 or W-1 here. If e.g. an image has\n        # a width of 1, we want to get a maximum of 1 column of coordinates.\n        # Note that we use two clips here instead of e.g. clip(., 1, height),\n        # because images can have height/width zero and in these cases numpy\n        # prefers the smaller value in clip(). But currently we want to get\n        # at least 1 point for such images.\n        n_rows_lst = np.clip(n_rows_lst, None, heights)\n        n_cols_lst = np.clip(n_cols_lst, None, widths)\n        n_rows_lst = np.clip(n_rows_lst, 1, None)\n        n_cols_lst = np.clip(n_cols_lst, 1, None)\n        return n_rows_lst, n_cols_lst\n\n    @classmethod\n    def _generate_point_grids(cls, images, n_rows_lst, n_cols_lst):\n        grids = []\n        for image, n_rows_i, n_cols_i in zip(images, n_rows_lst, n_cols_lst):\n            grids.append(cls._generate_point_grid(image, n_rows_i, n_cols_i))\n        return grids\n\n    @classmethod\n    def _generate_point_grid(cls, image, n_rows, n_cols):\n        height, width = image.shape[0:2]\n\n        # We do not have to subtract 1 here from height/width as these are\n        # subpixel coordinates. Technically, we could also place the cell\n        # centers outside of the image plane.\n        y_spacing = height / n_rows\n        y_start = 0.0 + y_spacing/2\n        y_end = height - y_spacing/2\n        if y_start - 1e-4 <= y_end <= y_start + 1e-4:\n            yy = np.float32([y_start])\n        else:\n            yy = np.linspace(y_start, y_end, num=n_rows)\n\n        x_spacing = width / n_cols\n        x_start = 0.0 + x_spacing/2\n        x_end = width - x_spacing/2\n        if x_start - 1e-4 <= x_end <= x_start + 1e-4:\n            xx = np.float32([x_start])\n        else:\n            xx = np.linspace(x_start, x_end, num=n_cols)\n\n        xx, yy = np.meshgrid(xx, yy)\n        grid = np.vstack([xx.ravel(), yy.ravel()]).T\n        return grid\n\n    def __repr__(self):\n        return \"RegularGridPointsSampler(%s, %s)\" % (self.n_rows, self.n_cols)\n\n    def __str__(self):\n        return self.__repr__()\n\n\nclass RelativeRegularGridPointsSampler(IPointsSampler):\n    \"\"\"Regular grid coordinate sampler; places more points on larger images.\n\n    This is similar to ``RegularGridPointsSampler``, but the number of rows\n    and columns is given as fractions of each image's height and width.\n    Hence, more coordinates are generated for larger images.\n\n    Parameters\n    ----------\n    n_rows_frac : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Relative number of coordinates to place on the y-axis. For a value\n        ``y`` and image height ``H`` the number of actually placed coordinates\n        (i.e. computed rows) is given by ``int(round(y*H))``.\n        Note that for each image, the number of coordinates is clipped to the\n        interval ``[1,H]``, where ``H`` is the image height.\n\n            * If a single ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value from the interval\n              ``[a, b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    n_cols_frac : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Relative number of coordinates to place on the x-axis. For a value\n        ``x`` and image height ``W`` the number of actually placed coordinates\n        (i.e. computed columns) is given by ``int(round(x*W))``.\n        Note that for each image, the number of coordinates is clipped to the\n        interval ``[1,W]``, where ``W`` is the image width.\n\n            * If a single ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value from the interval\n              ``[a, b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> sampler = iaa.RelativeRegularGridPointsSampler(\n    >>>     n_rows_frac=(0.01, 0.1),\n    >>>     n_cols_frac=0.2)\n\n    Create a point sampler that generates regular grids of points. These grids\n    contain ``round(y*H)`` points on the y-axis, where ``y`` is sampled\n    uniformly from the interval ``[0.01, 0.1]`` per image and ``H`` is the\n    image height. On the x-axis, the grids always contain ``0.2*W`` points,\n    where ``W`` is the image width.\n\n    \"\"\"\n\n    def __init__(self, n_rows_frac, n_cols_frac):\n        eps = 1e-4\n        self.n_rows_frac = iap.handle_continuous_param(\n            n_rows_frac, \"n_rows_frac\", value_range=(0.0+eps, 1.0),\n            tuple_to_uniform=True, list_to_choice=True)\n        self.n_cols_frac = iap.handle_continuous_param(\n            n_cols_frac, \"n_cols_frac\", value_range=(0.0+eps, 1.0),\n            tuple_to_uniform=True, list_to_choice=True)\n\n    def sample_points(self, images, random_state):\n        # pylint: disable=protected-access\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        _verify_sample_points_images(images)\n\n        n_rows, n_cols = self._draw_samples(images, random_state)\n        return RegularGridPointsSampler._generate_point_grids(images,\n                                                              n_rows, n_cols)\n\n    def _draw_samples(self, images, random_state):\n        # pylint: disable=protected-access\n        n_augmentables = len(images)\n        rss = random_state.duplicate(2)\n        n_rows_frac = self.n_rows_frac.draw_samples(n_augmentables,\n                                                    random_state=rss[0])\n        n_cols_frac = self.n_cols_frac.draw_samples(n_augmentables,\n                                                    random_state=rss[1])\n        heights = np.int32([image.shape[0] for image in images])\n        widths = np.int32([image.shape[1] for image in images])\n\n        n_rows = np.round(n_rows_frac * heights)\n        n_cols = np.round(n_cols_frac * widths)\n        n_rows, n_cols = RegularGridPointsSampler._clip_rows_and_cols(\n            n_rows, n_cols, images)\n\n        return n_rows.astype(np.int32), n_cols.astype(np.int32)\n\n    def __repr__(self):\n        return \"RelativeRegularGridPointsSampler(%s, %s)\" % (\n            self.n_rows_frac, self.n_cols_frac)\n\n    def __str__(self):\n        return self.__repr__()\n\n\nclass DropoutPointsSampler(IPointsSampler):\n    \"\"\"Remove a defined fraction of sampled points.\n\n    Parameters\n    ----------\n    other_points_sampler : IPointsSampler\n        Another point sampler that is queried to generate a list of points.\n        The dropout operation will be applied to that list.\n\n    p_drop : number or tuple of number or imgaug.parameters.StochasticParameter\n        The probability that a coordinate will be removed from the list\n        of all sampled coordinates. A value of ``1.0`` would mean that (on\n        average) ``100`` percent of all coordinates will be dropped,\n        while ``0.0`` denotes ``0`` percent. Note that this sampler will\n        always ensure that at least one coordinate is left after the dropout\n        operation, i.e. even ``1.0`` will only drop all *except one*\n        coordinate.\n\n            * If a ``float``, then that value will be used for all images.\n            * If a ``tuple`` ``(a, b)``, then a value ``p`` will be sampled\n              from the interval ``[a, b]`` per image.\n            * If a ``StochasticParameter``, then this parameter will be used to\n              determine per coordinate whether it should be *kept* (sampled\n              value of ``>0.5``) or shouldn't be kept (sampled value of\n              ``<=0.5``). If you instead want to provide the probability as\n              a stochastic parameter, you can usually do\n              ``imgaug.parameters.Binomial(1-p)`` to convert parameter `p` to\n              a 0/1 representation.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> sampler = iaa.DropoutPointsSampler(\n    >>>     iaa.RegularGridPointsSampler(10, 20),\n    >>>     0.2)\n\n    Create a point sampler that first generates points following a regular\n    grid of ``10`` rows and ``20`` columns, then randomly drops ``20`` percent\n    of these points.\n\n    \"\"\"\n\n    def __init__(self, other_points_sampler, p_drop):\n        assert isinstance(other_points_sampler, IPointsSampler), (\n            \"Expected to get an instance of IPointsSampler as argument \"\n            \"'other_points_sampler', got type %s.\" % (\n                type(other_points_sampler),))\n        self.other_points_sampler = other_points_sampler\n        self.p_drop = self._convert_p_drop_to_inverted_mask_param(p_drop)\n\n    @classmethod\n    def _convert_p_drop_to_inverted_mask_param(cls, p_drop):\n        # TODO this is the same as in Dropout, make DRY\n        # TODO add list as an option\n        if ia.is_single_number(p_drop):\n            p_drop = iap.Binomial(1 - p_drop)\n        elif ia.is_iterable(p_drop):\n            assert len(p_drop) == 2, (\n                \"Expected 'p_drop' given as an iterable to contain exactly \"\n                \"2 values, got %d.\" % (len(p_drop),))\n            assert p_drop[0] < p_drop[1], (\n                \"Expected 'p_drop' given as iterable to contain exactly 2 \"\n                \"values (a, b) with a < b. Got %.4f and %.4f.\" % (\n                    p_drop[0], p_drop[1]))\n            assert 0 <= p_drop[0] <= 1.0 and 0 <= p_drop[1] <= 1.0, (\n                \"Expected 'p_drop' given as iterable to only contain values \"\n                \"in the interval [0.0, 1.0], got %.4f and %.4f.\" % (\n                    p_drop[0], p_drop[1]))\n            p_drop = iap.Binomial(iap.Uniform(1 - p_drop[1], 1 - p_drop[0]))\n        elif isinstance(p_drop, iap.StochasticParameter):\n            pass\n        else:\n            raise Exception(\n                \"Expected p_drop to be float or int or StochasticParameter, \"\n                \"got %s.\" % (type(p_drop),))\n        return p_drop\n\n    def sample_points(self, images, random_state):\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        _verify_sample_points_images(images)\n\n        rss = random_state.duplicate(2)\n        points_on_images = self.other_points_sampler.sample_points(images,\n                                                                   rss[0])\n        drop_masks = self._draw_samples(points_on_images, rss[1])\n        return self._apply_dropout_masks(points_on_images, drop_masks)\n\n    def _draw_samples(self, points_on_images, random_state):\n        rss = random_state.duplicate(len(points_on_images))\n        drop_masks = [self._draw_samples_for_image(points_on_image, rs)\n                      for points_on_image, rs\n                      in zip(points_on_images, rss)]\n        return drop_masks\n\n    def _draw_samples_for_image(self, points_on_image, random_state):\n        drop_samples = self.p_drop.draw_samples((len(points_on_image),),\n                                                random_state)\n        keep_mask = (drop_samples > 0.5)\n        return keep_mask\n\n    @classmethod\n    def _apply_dropout_masks(cls, points_on_images, keep_masks):\n        points_on_images_dropped = []\n        for points_on_image, keep_mask in zip(points_on_images, keep_masks):\n            if len(points_on_image) == 0:\n                # other sampler didn't provide any points\n                poi_dropped = points_on_image\n            else:\n                if not np.any(keep_mask):\n                    # keep at least one point if all were supposed to be\n                    # dropped\n                    # TODO this could also be moved into its own point sampler,\n                    #      like AtLeastOnePoint(...)\n                    idx = (len(points_on_image) - 1) // 2\n                    keep_mask = np.copy(keep_mask)\n                    keep_mask[idx] = True\n                poi_dropped = points_on_image[keep_mask, :]\n            points_on_images_dropped.append(poi_dropped)\n        return points_on_images_dropped\n\n    def __repr__(self):\n        return \"DropoutPointsSampler(%s, %s)\" % (self.other_points_sampler,\n                                                 self.p_drop)\n\n    def __str__(self):\n        return self.__repr__()\n\n\nclass UniformPointsSampler(IPointsSampler):\n    \"\"\"Sample points uniformly on images.\n\n    This point sampler generates `n_points` points per image. The x- and\n    y-coordinates are both sampled from uniform distributions matching the\n    respective image width and height.\n\n    Parameters\n    ----------\n    n_points : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Number of points to sample on each image.\n\n            * If a single ``int``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value from the discrete\n              interval ``[a..b]`` will be sampled per image.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then that parameter will be\n              queried to draw one value per image.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> sampler = iaa.UniformPointsSampler(500)\n\n    Create a point sampler that generates an array of ``500`` random points for\n    each input image. The x- and y-coordinates of each point are sampled\n    from uniform distributions.\n\n    \"\"\"\n\n    def __init__(self, n_points):\n        self.n_points = iap.handle_discrete_param(\n            n_points, \"n_points\", value_range=(1, None),\n            tuple_to_uniform=True, list_to_choice=True, allow_floats=False)\n\n    def sample_points(self, images, random_state):\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        _verify_sample_points_images(images)\n\n        rss = random_state.duplicate(2)\n        n_points_imagewise = self._draw_samples(len(images), rss[0])\n\n        n_points_total = np.sum(n_points_imagewise)\n        n_components_total = 2 * n_points_total\n        coords_relative = rss[1].uniform(0.0, 1.0, n_components_total)\n        coords_relative_xy = coords_relative.reshape(n_points_total, 2)\n\n        return self._convert_relative_coords_to_absolute(\n            coords_relative_xy, n_points_imagewise, images)\n\n    def _draw_samples(self, n_augmentables, random_state):\n        n_points = self.n_points.draw_samples((n_augmentables,),\n                                              random_state=random_state)\n        n_points_clipped = np.clip(n_points, 1, None)\n        return n_points_clipped\n\n    @classmethod\n    def _convert_relative_coords_to_absolute(cls, coords_rel_xy,\n                                             n_points_imagewise, images):\n        coords_absolute = []\n        i = 0\n        for image, n_points_image in zip(images, n_points_imagewise):\n            height, width = image.shape[0:2]\n            xx = coords_rel_xy[i:i+n_points_image, 0]\n            yy = coords_rel_xy[i:i+n_points_image, 1]\n\n            xx_int = np.clip(np.round(xx * width), 0, width)\n            yy_int = np.clip(np.round(yy * height), 0, height)\n\n            coords_absolute.append(np.stack([xx_int, yy_int], axis=-1))\n            i += n_points_image\n        return coords_absolute\n\n    def __repr__(self):\n        return \"UniformPointsSampler(%s)\" % (self.n_points,)\n\n    def __str__(self):\n        return self.__repr__()\n\n\nclass SubsamplingPointsSampler(IPointsSampler):\n    \"\"\"Ensure that the number of sampled points is below a maximum.\n\n    This point sampler will sample points from another sampler and\n    then -- in case more points were generated than an allowed maximum --\n    will randomly pick `n_points_max` of these.\n\n    Parameters\n    ----------\n    other_points_sampler : IPointsSampler\n        Another point sampler that is queried to generate a ``list`` of points.\n        The dropout operation will be applied to that ``list``.\n\n    n_points_max : int\n        Maximum number of allowed points. If `other_points_sampler` generates\n        more points than this maximum, a random subset of size `n_points_max`\n        will be selected.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> sampler = iaa.SubsamplingPointsSampler(\n    >>>     iaa.RelativeRegularGridPointsSampler(0.1, 0.2),\n    >>>     50\n    >>> )\n\n    Create a points sampler that places ``y*H`` points on the y-axis (with\n    ``y`` being ``0.1`` and ``H`` being an image's height) and ``x*W`` on\n    the x-axis (analogous). Then, if that number of placed points exceeds\n    ``50`` (can easily happen for larger images), a random subset of ``50``\n    points will be picked and returned.\n\n    \"\"\"\n\n    def __init__(self, other_points_sampler, n_points_max):\n        assert isinstance(other_points_sampler, IPointsSampler), (\n            \"Expected to get an instance of IPointsSampler as argument \"\n            \"'other_points_sampler', got type %s.\" % (\n                type(other_points_sampler),))\n        self.other_points_sampler = other_points_sampler\n        self.n_points_max = np.clip(n_points_max, -1, None)\n        if self.n_points_max == 0:\n            ia.warn(\"Got n_points_max=0 in SubsamplingPointsSampler. \"\n                    \"This will result in no points ever getting \"\n                    \"returned.\")\n\n    def sample_points(self, images, random_state):\n        random_state = iarandom.RNG.create_if_not_rng_(random_state)\n        _verify_sample_points_images(images)\n\n        rss = random_state.duplicate(len(images) + 1)\n        points_on_images = self.other_points_sampler.sample_points(\n            images, rss[-1])\n        return [self._subsample(points_on_image, self.n_points_max, rs)\n                for points_on_image, rs\n                in zip(points_on_images, rss[:-1])]\n\n    @classmethod\n    def _subsample(cls, points_on_image, n_points_max, random_state):\n        if len(points_on_image) <= n_points_max:\n            return points_on_image\n        indices = np.arange(len(points_on_image))\n        indices_to_keep = random_state.permutation(indices)[0:n_points_max]\n        return points_on_image[indices_to_keep]\n\n    def __repr__(self):\n        return \"SubsamplingPointsSampler(%s, %d)\" % (self.other_points_sampler,\n                                                     self.n_points_max)\n\n    def __str__(self):\n        return self.__repr__()\n\n\n# TODO Add points subsampler that drops points close to each other first\n# TODO Add poisson points sampler\n# TODO Add jitter points sampler that moves points around\n# for both see https://codegolf.stackexchange.com/questions/50299/draw-an-image-as-a-voronoi-map/50345#50345\n"
  },
  {
    "path": "imgaug/augmenters/size.py",
    "content": "\"\"\"\nAugmenters that somehow change the size of the images.\n\nList of augmenters:\n\n    * :class:`Resize`\n    * :class:`CropAndPad`\n    * :class:`Crop`\n    * :class:`Pad`\n    * :class:`PadToFixedSize`\n    * :class:`CenterPadToFixedSize`\n    * :class:`CropToFixedSize`\n    * :class:`CenterCropToFixedSize`\n    * :class:`CropToMultiplesOf`\n    * :class:`CenterCropToMultiplesOf`\n    * :class:`PadToMultiplesOf`\n    * :class:`CenterPadToMultiplesOf`\n    * :class:`CropToPowersOf`\n    * :class:`CenterCropToPowersOf`\n    * :class:`PadToPowersOf`\n    * :class:`CenterPadToPowersOf`\n    * :class:`CropToAspectRatio`\n    * :class:`CenterCropToAspectRatio`\n    * :class:`PadToAspectRatio`\n    * :class:`CenterPadToAspectRatio`\n    * :class:`CropToSquare`\n    * :class:`CenterCropToSquare`\n    * :class:`PadToSquare`\n    * :class:`CenterPadToSquare`\n    * :class:`KeepSizeByResize`\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport re\nimport functools\n\nimport numpy as np\nimport cv2\n\nimport imgaug as ia\nfrom imgaug.imgaug import _normalize_cv2_input_arr_\nfrom . import meta\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\n\n\ndef _crop_trbl_to_xyxy(shape, top, right, bottom, left, prevent_zero_size=True):\n    if prevent_zero_size:\n        top, bottom = _prevent_zero_size_after_crop_(shape[0], top, bottom)\n        left, right = _prevent_zero_size_after_crop_(shape[1], left, right)\n\n    height, width = shape[0:2]\n    x1 = left\n    x2 = width - right\n    y1 = top\n    y2 = height - bottom\n\n    # these steps prevent negative sizes\n    # if x2==x1 or y2==y1 then the output arr has size 0 for the respective axis\n    # note that if height/width of arr is zero, then y2==y1 or x2==x1, which\n    # is still valid, even if height/width is zero and results in a zero-sized\n    # axis\n    x2 = max(x2, x1)\n    y2 = max(y2, y1)\n\n    return x1, y1, x2, y2\n\n\ndef _crop_arr_(arr, top, right, bottom, left, prevent_zero_size=True):\n    x1, y1, x2, y2 = _crop_trbl_to_xyxy(arr.shape, top, right, bottom, left,\n                                        prevent_zero_size=prevent_zero_size)\n    return arr[y1:y2, x1:x2, ...]\n\n\ndef _crop_and_pad_arr(arr, croppings, paddings, pad_mode=\"constant\",\n                      pad_cval=0, keep_size=False):\n    height, width = arr.shape[0:2]\n\n    image_cr = _crop_arr_(arr, *croppings)\n\n    image_cr_pa = pad(\n        image_cr,\n        top=paddings[0], right=paddings[1],\n        bottom=paddings[2], left=paddings[3],\n        mode=pad_mode, cval=pad_cval)\n\n    if keep_size:\n        image_cr_pa = ia.imresize_single_image(image_cr_pa, (height, width))\n\n    return image_cr_pa\n\n\ndef _crop_and_pad_heatmap_(heatmap, croppings_img, paddings_img,\n                           pad_mode=\"constant\", pad_cval=0.0, keep_size=False):\n    return _crop_and_pad_hms_or_segmaps_(heatmap, croppings_img,\n                                         paddings_img, pad_mode, pad_cval,\n                                         keep_size)\n\n\ndef _crop_and_pad_segmap_(segmap, croppings_img, paddings_img,\n                          pad_mode=\"constant\", pad_cval=0, keep_size=False):\n    return _crop_and_pad_hms_or_segmaps_(segmap, croppings_img,\n                                         paddings_img, pad_mode, pad_cval,\n                                         keep_size)\n\n\ndef _crop_and_pad_hms_or_segmaps_(augmentable, croppings_img,\n                                  paddings_img, pad_mode=\"constant\",\n                                  pad_cval=None, keep_size=False):\n    if isinstance(augmentable, ia.HeatmapsOnImage):\n        arr_attr_name = \"arr_0to1\"\n        pad_cval = pad_cval if pad_cval is not None else 0.0\n    else:\n        assert isinstance(augmentable, ia.SegmentationMapsOnImage), (\n            \"Expected HeatmapsOnImage or SegmentationMapsOnImage, got %s.\" % (\n                type(augmentable)))\n        arr_attr_name = \"arr\"\n        pad_cval = pad_cval if pad_cval is not None else 0\n\n    arr = getattr(augmentable, arr_attr_name)\n    arr_shape_orig = arr.shape\n    augm_shape = augmentable.shape\n\n    croppings_proj = _project_size_changes(croppings_img, augm_shape, arr.shape)\n    paddings_proj = _project_size_changes(paddings_img, augm_shape, arr.shape)\n\n    croppings_proj = _prevent_zero_size_after_crop_trbl_(arr.shape[0],\n                                                         arr.shape[1],\n                                                         croppings_proj)\n\n    arr_cr = _crop_arr_(arr,\n                        croppings_proj[0], croppings_proj[1],\n                        croppings_proj[2], croppings_proj[3])\n    arr_cr_pa = pad(\n        arr_cr,\n        top=paddings_proj[0], right=paddings_proj[1],\n        bottom=paddings_proj[2], left=paddings_proj[3],\n        mode=pad_mode,\n        cval=pad_cval)\n\n    setattr(augmentable, arr_attr_name, arr_cr_pa)\n\n    if keep_size:\n        augmentable = augmentable.resize(arr_shape_orig[0:2])\n    else:\n        augmentable.shape = _compute_shape_after_crop_and_pad(\n            augmentable.shape, croppings_img, paddings_img)\n    return augmentable\n\n\ndef _crop_and_pad_kpsoi_(kpsoi, croppings_img, paddings_img, keep_size):\n    # using the trbl function instead of croppings_img has the advantage\n    # of incorporating prevent_zero_size, dealing with zero-sized input image\n    # axis and dealing the negative crop amounts\n    x1, y1, _x2, _y2 = _crop_trbl_to_xyxy(kpsoi.shape, *croppings_img)\n    crop_left = x1\n    crop_top = y1\n\n    shape_orig = kpsoi.shape\n    shifted = kpsoi.shift_(\n        x=-crop_left+paddings_img[3],\n        y=-crop_top+paddings_img[0])\n    shifted.shape = _compute_shape_after_crop_and_pad(\n        shape_orig, croppings_img, paddings_img)\n    if keep_size:\n        shifted = shifted.on_(shape_orig)\n    return shifted\n\n\ndef _compute_shape_after_crop_and_pad(old_shape, croppings, paddings):\n    x1, y1, x2, y2 = _crop_trbl_to_xyxy(old_shape, *croppings)\n    new_shape = list(old_shape)\n    new_shape[0] = y2 - y1 + paddings[0] + paddings[2]\n    new_shape[1] = x2 - x1 + paddings[1] + paddings[3]\n    return tuple(new_shape)\n\n\ndef _prevent_zero_size_after_crop_trbl_(height, width, crop_trbl):\n    crop_top = crop_trbl[0]\n    crop_right = crop_trbl[1]\n    crop_bottom = crop_trbl[2]\n    crop_left = crop_trbl[3]\n\n    crop_top, crop_bottom = _prevent_zero_size_after_crop_(height, crop_top,\n                                                           crop_bottom)\n    crop_left, crop_right = _prevent_zero_size_after_crop_(width, crop_left,\n                                                           crop_right)\n    return (\n        crop_top, crop_right, crop_bottom, crop_left\n    )\n\n\ndef _prevent_zero_size_after_crop_(axis_size, crop_start, crop_end):\n    return map(\n        int,\n        _prevent_zero_sizes_after_crops_(\n            np.array([axis_size], dtype=np.int32),\n            np.array([crop_start], dtype=np.int32),\n            np.array([crop_end], dtype=np.int32)\n        )\n    )\n\n\ndef _prevent_zero_sizes_after_crops_(axis_sizes, crops_start, crops_end):\n    remaining_sizes = axis_sizes - (crops_start + crops_end)\n\n    mask_bad_sizes = (remaining_sizes < 1)\n    regains = mask_bad_sizes * (np.abs(remaining_sizes) + 1)\n    regains_half = regains.astype(np.float32) / 2\n    regains_start = np.ceil(regains_half).astype(np.int32)\n    regains_end = np.floor(regains_half).astype(np.int32)\n\n    crops_start -= regains_start\n    crops_end -= regains_end\n\n    mask_too_much_start = (crops_start < 0)\n    crops_end[mask_too_much_start] += crops_start[mask_too_much_start]\n    crops_start = np.maximum(crops_start, 0)\n\n    mask_too_much_end = (crops_end < 0)\n    crops_start[mask_too_much_end] += crops_end[mask_too_much_end]\n    crops_end = np.maximum(crops_end, 0)\n\n    crops_start = np.maximum(crops_start, 0)\n\n    return crops_start, crops_end\n\n\ndef _project_size_changes(trbl, from_shape, to_shape):\n    if from_shape[0:2] == to_shape[0:2]:\n        return trbl\n\n    height_to = to_shape[0]\n    width_to = to_shape[1]\n    height_from = from_shape[0]\n    width_from = from_shape[1]\n\n    top = trbl[0]\n    right = trbl[1]\n    bottom = trbl[2]\n    left = trbl[3]\n\n    # Adding/subtracting 1e-4 here helps for the case where a heatmap/segmap\n    # is exactly half the size of an image and the size change on an axis is\n    # an odd value. Then the projected value would end up being <something>.5\n    # and the rounding would always round up to the next integer. If both\n    # sides then have the same change, they are both rounded up, resulting\n    # in more change than expected.\n    # E.g. image height is 8, map height is 4, change is 3 at the top and 3 at\n    # the bottom. The changes are projected to 4*(3/8) = 1.5 and both rounded\n    # up to 2.0. Hence, the maps are changed by 4 (100% of the map height,\n    # vs. 6 for images, which is 75% of the image height).\n    top = _int_r(height_to * (top/height_from) - 1e-4)\n    right = _int_r(width_to * (right/width_from) + 1e-4)\n    bottom = _int_r(height_to * (bottom/height_from) + 1e-4)\n    left = _int_r(width_to * (left/width_from) - 1e-4)\n\n    return top, right, bottom, left\n\n\ndef _int_r(value):\n    return int(np.round(value))\n\n\n# TODO somehow integrate this with pad()\n@iap._prefetchable_str\ndef _handle_pad_mode_param(pad_mode):\n    pad_modes_available = {\n        \"constant\", \"edge\", \"linear_ramp\", \"maximum\", \"mean\", \"median\",\n        \"minimum\", \"reflect\", \"symmetric\", \"wrap\"}\n    if pad_mode == ia.ALL:\n        return iap.Choice(list(pad_modes_available))\n    if ia.is_string(pad_mode):\n        assert pad_mode in pad_modes_available, (\n            \"Value '%s' is not a valid pad mode. Valid pad modes are: %s.\" % (\n                pad_mode, \", \".join(pad_modes_available)))\n        return iap.Deterministic(pad_mode)\n    if isinstance(pad_mode, list):\n        assert all([v in pad_modes_available for v in pad_mode]), (\n            \"At least one in list %s is not a valid pad mode. Valid pad \"\n            \"modes are: %s.\" % (str(pad_mode), \", \".join(pad_modes_available)))\n        return iap.Choice(pad_mode)\n    if isinstance(pad_mode, iap.StochasticParameter):\n        return pad_mode\n    raise Exception(\n        \"Expected pad_mode to be ia.ALL or string or list of strings or \"\n        \"StochasticParameter, got %s.\" % (type(pad_mode),))\n\n\n@iap._prefetchable\ndef _handle_position_parameter(position):\n    if position == \"uniform\":\n        return iap.Uniform(0.0, 1.0), iap.Uniform(0.0, 1.0)\n    if position == \"normal\":\n        return (\n            iap.Clip(iap.Normal(loc=0.5, scale=0.35 / 2),\n                     minval=0.0, maxval=1.0),\n            iap.Clip(iap.Normal(loc=0.5, scale=0.35 / 2),\n                     minval=0.0, maxval=1.0)\n        )\n    if position == \"center\":\n        return iap.Deterministic(0.5), iap.Deterministic(0.5)\n    if (ia.is_string(position)\n            and re.match(r\"^(left|center|right)-(top|center|bottom)$\",\n                         position)):\n        mapping = {\"top\": 0.0, \"center\": 0.5, \"bottom\": 1.0, \"left\": 0.0,\n                   \"right\": 1.0}\n        return (\n            iap.Deterministic(mapping[position.split(\"-\")[0]]),\n            iap.Deterministic(mapping[position.split(\"-\")[1]])\n        )\n    if isinstance(position, iap.StochasticParameter):\n        return position\n    if isinstance(position, tuple):\n        assert len(position) == 2, (\n            \"Expected tuple with two entries as position parameter. \"\n            \"Got %d entries with types %s..\" % (\n                len(position), str([type(item) for item in position])))\n        for item in position:\n            if ia.is_single_number(item) and (item < 0 or item > 1.0):\n                raise Exception(\n                    \"Both position values must be within the value range \"\n                    \"[0.0, 1.0]. Got type %s with value %.8f.\" % (\n                        type(item), item,))\n        position = [iap.Deterministic(item)\n                    if ia.is_single_number(item)\n                    else item for item in position]\n\n        only_sparams = all([isinstance(item, iap.StochasticParameter)\n                            for item in position])\n        assert only_sparams, (\n            \"Expected tuple with two entries that are both either \"\n            \"StochasticParameter or float/int. Got types %s.\" % (\n                str([type(item) for item in position])\n            ))\n        return tuple(position)\n    raise Exception(\n        \"Expected one of the following as position parameter: string \"\n        \"'uniform', string 'normal', string 'center', a string matching \"\n        \"regex ^(left|center|right)-(top|center|bottom)$, a single \"\n        \"StochasticParameter or a tuple of two entries, both being either \"\n        \"StochasticParameter or floats or int. Got instead type %s with \"\n        \"content '%s'.\" % (\n            type(position),\n            (str(position)\n             if len(str(position)) < 20\n             else str(position)[0:20] + \"...\")\n        )\n    )\n\n\n# TODO this is the same as in imgaug.py, make DRY\n# Added in 0.4.0.\ndef _assert_two_or_three_dims(shape):\n    if hasattr(shape, \"shape\"):\n        shape = shape.shape\n    assert len(shape) in [2, 3], (\n        \"Expected image with two or three dimensions, but got %d dimensions \"\n        \"and shape %s.\" % (len(shape), shape))\n\n\ndef pad(arr, top=0, right=0, bottom=0, left=0, mode=\"constant\", cval=0):\n    \"\"\"Pad an image-like array on its top/right/bottom/left side.\n\n    This function is a wrapper around :func:`numpy.pad`.\n\n    Added in 0.4.0. (Previously named ``imgaug.imgaug.pad()``.)\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested (1)\n        * ``uint16``: yes; fully tested (1)\n        * ``uint32``: yes; fully tested (2) (3)\n        * ``uint64``: yes; fully tested (2) (3)\n        * ``int8``: yes; fully tested (1)\n        * ``int16``: yes; fully tested (1)\n        * ``int32``: yes; fully tested (1)\n        * ``int64``: yes; fully tested (2) (3)\n        * ``float16``: yes; fully tested (2) (3)\n        * ``float32``: yes; fully tested (1)\n        * ``float64``: yes; fully tested (1)\n        * ``float128``: yes; fully tested (2) (3)\n        * ``bool``: yes; tested (2) (3)\n\n        - (1) Uses ``cv2`` if `mode` is one of: ``\"constant\"``, ``\"edge\"``,\n              ``\"reflect\"``, ``\"symmetric\"``. Otherwise uses ``numpy``.\n        - (2) Uses ``numpy``.\n        - (3) Rejected by ``cv2``.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Image-like array to pad.\n\n    top : int, optional\n        Amount of pixels to add to the top side of the image.\n        Must be ``0`` or greater.\n\n    right : int, optional\n        Amount of pixels to add to the right side of the image.\n        Must be ``0`` or greater.\n\n    bottom : int, optional\n        Amount of pixels to add to the bottom side of the image.\n        Must be ``0`` or greater.\n\n    left : int, optional\n        Amount of pixels to add to the left side of the image.\n        Must be ``0`` or greater.\n\n    mode : str, optional\n        Padding mode to use. See :func:`numpy.pad` for details.\n        In case of mode ``constant``, the parameter `cval` will be used as\n        the ``constant_values`` parameter to :func:`numpy.pad`.\n        In case of mode ``linear_ramp``, the parameter `cval` will be used as\n        the ``end_values`` parameter to :func:`numpy.pad`.\n\n    cval : number or iterable of number, optional\n        Value to use for padding if `mode` is ``constant``.\n        See :func:`numpy.pad` for details. The cval is expected to match the\n        input array's dtype and value range. If an iterable is used, it is\n        expected to contain one value per channel. The number of values\n        and number of channels are expected to match.\n\n    Returns\n    -------\n    (H',W') ndarray or (H',W',C) ndarray\n        Padded array with height ``H'=H+top+bottom`` and width\n        ``W'=W+left+right``.\n\n    \"\"\"\n\n    _assert_two_or_three_dims(arr)\n    assert all([v >= 0 for v in [top, right, bottom, left]]), (\n        \"Expected padding amounts that are >=0, but got %d, %d, %d, %d \"\n        \"(top, right, bottom, left)\" % (top, right, bottom, left))\n\n    is_multi_cval = ia.is_iterable(cval)\n\n    if top > 0 or right > 0 or bottom > 0 or left > 0:\n        min_value, _, max_value = iadt.get_value_range_of_dtype(arr.dtype)\n\n        # Without the if here there are crashes for float128, e.g. if\n        # cval is an int (just using float(cval) seems to not be accurate\n        # enough).\n        # Note: If float128 is not available on the system, _FLOAT128_DTYPE is\n        # None, but 'np.dtype(\"float64\") == None' actually equates to True\n        # for whatever reason, so we check first if the constant is not None\n        # (i.e. if float128 exists).\n        if (iadt._FLOAT128_DTYPE is not None\n                and arr.dtype == iadt._FLOAT128_DTYPE):\n            cval = np.float128(cval)  # pylint: disable=no-member\n\n        if is_multi_cval:\n            cval = np.clip(cval, min_value, max_value)\n        else:\n            cval = max(min(cval, max_value), min_value)\n\n        # Note that copyMakeBorder() hangs/runs endlessly if arr has an\n        # axis of size 0 and mode is \"reflect\".\n        # Numpy also complains in these cases if mode is not \"constant\".\n        has_zero_sized_axis = any([axis == 0 for axis in arr.shape])\n        if has_zero_sized_axis:\n            mode = \"constant\"\n\n        mapping_mode_np_to_cv2 = {\n            \"constant\": cv2.BORDER_CONSTANT,\n            \"edge\": cv2.BORDER_REPLICATE,\n            \"linear_ramp\": None,\n            \"maximum\": None,\n            \"mean\": None,\n            \"median\": None,\n            \"minimum\": None,\n            \"reflect\": cv2.BORDER_REFLECT_101,\n            \"symmetric\": cv2.BORDER_REFLECT,\n            \"wrap\": None,\n            cv2.BORDER_CONSTANT: cv2.BORDER_CONSTANT,\n            cv2.BORDER_REPLICATE: cv2.BORDER_REPLICATE,\n            cv2.BORDER_REFLECT_101: cv2.BORDER_REFLECT_101,\n            cv2.BORDER_REFLECT: cv2.BORDER_REFLECT\n        }\n        bad_mode_cv2 = mapping_mode_np_to_cv2.get(mode, None) is None\n\n        # these datatypes all simply generate a \"TypeError: src data type = X\n        # is not supported\" error\n        bad_datatype_cv2 = (\n            arr.dtype in iadt._convert_dtype_strs_to_types(\n                \"uint32 uint64 int64 float16 float128 bool\"\n            )\n        )\n\n        # OpenCV turns the channel axis for arrays with 0 channels to 512\n        # TODO add direct test for this. indirectly tested via Pad\n        bad_shape_cv2 = (arr.ndim == 3 and arr.shape[-1] == 0)\n\n        if not bad_datatype_cv2 and not bad_mode_cv2 and not bad_shape_cv2:\n            # convert cval to expected type, as otherwise we get TypeError\n            # for np inputs\n            kind = arr.dtype.kind\n            if is_multi_cval:\n                cval = [float(cval_c) if kind == \"f\" else int(cval_c)\n                        for cval_c in cval]\n            else:\n                cval = float(cval) if kind == \"f\" else int(cval)\n\n            if arr.ndim == 2 or arr.shape[2] <= 4:\n                # without this, only the first channel is padded with the cval,\n                # all following channels with 0\n                if arr.ndim == 3 and not is_multi_cval:\n                    cval = tuple([cval] * arr.shape[2])\n\n                arr = _normalize_cv2_input_arr_(arr)\n                arr_pad = cv2.copyMakeBorder(\n                    arr,\n                    top=int(top), bottom=int(bottom),\n                    left=int(left), right=int(right),\n                    borderType=mapping_mode_np_to_cv2[mode], value=cval)\n                if arr.ndim == 3 and arr_pad.ndim == 2:\n                    arr_pad = arr_pad[..., np.newaxis]\n            else:\n                result = []\n                channel_start_idx = 0\n                cval = cval if is_multi_cval else tuple([cval] * arr.shape[2])\n                while channel_start_idx < arr.shape[2]:\n                    arr_c = arr[..., channel_start_idx:channel_start_idx+4]\n                    cval_c = cval[channel_start_idx:channel_start_idx+4]\n                    arr_pad_c = cv2.copyMakeBorder(\n                        _normalize_cv2_input_arr_(arr_c),\n                        top=top, bottom=bottom, left=left, right=right,\n                        borderType=mapping_mode_np_to_cv2[mode], value=cval_c)\n                    arr_pad_c = np.atleast_3d(arr_pad_c)\n                    result.append(arr_pad_c)\n                    channel_start_idx += 4\n                arr_pad = np.concatenate(result, axis=2)\n        else:\n            # paddings for 2d case\n            paddings_np = [(top, bottom), (left, right)]\n\n            # add paddings for 3d case\n            if arr.ndim == 3:\n                paddings_np.append((0, 0))\n\n            if mode == \"constant\":\n                if arr.ndim > 2 and is_multi_cval:\n                    arr_pad_chans = [\n                        np.pad(arr[..., c], paddings_np[0:2], mode=mode,\n                               constant_values=cval[c])\n                        for c in np.arange(arr.shape[2])]\n                    arr_pad = np.stack(arr_pad_chans, axis=-1)\n                else:\n                    arr_pad = np.pad(arr, paddings_np, mode=mode,\n                                     constant_values=cval)\n            elif mode == \"linear_ramp\":\n                if arr.ndim > 2 and is_multi_cval:\n                    arr_pad_chans = [\n                        np.pad(arr[..., c], paddings_np[0:2], mode=mode,\n                               end_values=cval[c])\n                        for c in np.arange(arr.shape[2])]\n                    arr_pad = np.stack(arr_pad_chans, axis=-1)\n                else:\n                    arr_pad = np.pad(arr, paddings_np, mode=mode,\n                                     end_values=cval)\n            else:\n                arr_pad = np.pad(arr, paddings_np, mode=mode)\n\n        return arr_pad\n    return np.copy(arr)\n\n\ndef pad_to_aspect_ratio(arr, aspect_ratio, mode=\"constant\", cval=0,\n                        return_pad_amounts=False):\n    \"\"\"Pad an image array on its sides so that it matches a target aspect ratio.\n\n    See :func:`~imgaug.imgaug.compute_paddings_for_aspect_ratio` for an\n    explanation of how the required padding amounts are distributed per\n    image axis.\n\n    Added in 0.4.0. (Previously named ``imgaug.imgaug.pad_to_aspect_ratio()``.)\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.size.pad`.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Image-like array to pad.\n\n    aspect_ratio : float\n        Target aspect ratio, given as width/height. E.g. ``2.0`` denotes the\n        image having twice as much width as height.\n\n    mode : str, optional\n        Padding mode to use. See :func:`~imgaug.imgaug.pad` for details.\n\n    cval : number, optional\n        Value to use for padding if `mode` is ``constant``.\n        See :func:`numpy.pad` for details.\n\n    return_pad_amounts : bool, optional\n        If ``False``, then only the padded image will be returned. If\n        ``True``, a ``tuple`` with two entries will be returned, where the\n        first entry is the padded image and the second entry are the amounts\n        by which each image side was padded. These amounts are again a\n        ``tuple`` of the form ``(top, right, bottom, left)``, with each value\n        being an ``int``.\n\n    Returns\n    -------\n    (H',W') ndarray or (H',W',C) ndarray\n        Padded image as ``(H',W')`` or ``(H',W',C)`` ndarray, fulfilling the\n        given `aspect_ratio`.\n\n    tuple of int\n        Amounts by which the image was padded on each side, given as a\n        ``tuple`` ``(top, right, bottom, left)``.\n        This ``tuple`` is only returned if `return_pad_amounts` was set to\n        ``True``.\n\n    \"\"\"\n    pad_top, pad_right, pad_bottom, pad_left = \\\n        compute_paddings_to_reach_aspect_ratio(arr, aspect_ratio)\n    arr_padded = pad(\n        arr,\n        top=pad_top,\n        right=pad_right,\n        bottom=pad_bottom,\n        left=pad_left,\n        mode=mode,\n        cval=cval\n    )\n\n    if return_pad_amounts:\n        return arr_padded, (pad_top, pad_right, pad_bottom, pad_left)\n    return arr_padded\n\n\ndef pad_to_multiples_of(arr, height_multiple, width_multiple, mode=\"constant\",\n                        cval=0, return_pad_amounts=False):\n    \"\"\"Pad an image array until its side lengths are multiples of given values.\n\n    See :func:`~imgaug.imgaug.compute_paddings_for_aspect_ratio` for an\n    explanation of how the required padding amounts are distributed per\n    image axis.\n\n    Added in 0.4.0. (Previously named ``imgaug.imgaug.pad_to_multiples_of()``.)\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.size.pad`.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Image-like array to pad.\n\n    height_multiple : None or int\n        The desired multiple of the height. The computed padding amount will\n        reflect a padding that increases the y axis size until it is a multiple\n        of this value.\n\n    width_multiple : None or int\n        The desired multiple of the width. The computed padding amount will\n        reflect a padding that increases the x axis size until it is a multiple\n        of this value.\n\n    mode : str, optional\n        Padding mode to use. See :func:`~imgaug.imgaug.pad` for details.\n\n    cval : number, optional\n        Value to use for padding if `mode` is ``constant``.\n        See :func:`numpy.pad` for details.\n\n    return_pad_amounts : bool, optional\n        If ``False``, then only the padded image will be returned. If\n        ``True``, a ``tuple`` with two entries will be returned, where the\n        first entry is the padded image and the second entry are the amounts\n        by which each image side was padded. These amounts are again a\n        ``tuple`` of the form ``(top, right, bottom, left)``, with each value\n        being an integer.\n\n    Returns\n    -------\n    (H',W') ndarray or (H',W',C) ndarray\n        Padded image as ``(H',W')`` or ``(H',W',C)`` ndarray.\n\n    tuple of int\n        Amounts by which the image was padded on each side, given as a\n        ``tuple`` ``(top, right, bottom, left)``.\n        This ``tuple`` is only returned if `return_pad_amounts` was set to\n        ``True``.\n\n    \"\"\"\n    pad_top, pad_right, pad_bottom, pad_left = \\\n        compute_paddings_to_reach_multiples_of(\n            arr, height_multiple, width_multiple)\n    arr_padded = pad(\n        arr,\n        top=pad_top,\n        right=pad_right,\n        bottom=pad_bottom,\n        left=pad_left,\n        mode=mode,\n        cval=cval\n    )\n\n    if return_pad_amounts:\n        return arr_padded, (pad_top, pad_right, pad_bottom, pad_left)\n    return arr_padded\n\n\ndef compute_paddings_to_reach_aspect_ratio(arr, aspect_ratio):\n    \"\"\"Compute pad amounts required to fulfill an aspect ratio.\n\n    \"Pad amounts\" here denotes the number of pixels that have to be added to\n    each side to fulfill the desired constraint.\n\n    The aspect ratio is given as ``ratio = width / height``.\n    Depending on which dimension is smaller (height or width), only the\n    corresponding sides (top/bottom or left/right) will be padded.\n\n    The axis-wise padding amounts are always distributed equally over the\n    sides of the respective axis (i.e. left and right, top and bottom). For\n    odd pixel amounts, one pixel will be left over after the equal\n    distribution and could be added to either side of the axis. This function\n    will always add such a left over pixel to the bottom (y-axis) or\n    right (x-axis) side.\n\n    Added in 0.4.0. (Previously named\n    ``imgaug.imgaug.compute_paddings_to_reach_aspect_ratio()``.)\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray or tuple of int\n        Image-like array or shape tuple for which to compute pad amounts.\n\n    aspect_ratio : float\n        Target aspect ratio, given as width/height. E.g. ``2.0`` denotes the\n        image having twice as much width as height.\n\n    Returns\n    -------\n    tuple of int\n        Required padding amounts to reach the target aspect ratio, given as a\n        ``tuple`` of the form ``(top, right, bottom, left)``.\n\n    \"\"\"\n    _assert_two_or_three_dims(arr)\n    assert aspect_ratio > 0, (\n        \"Expected to get an aspect ratio >0, got %.4f.\" % (aspect_ratio,))\n\n    pad_top = 0\n    pad_right = 0\n    pad_bottom = 0\n    pad_left = 0\n\n    shape = arr.shape if hasattr(arr, \"shape\") else arr\n    height, width = shape[0:2]\n\n    if height == 0:\n        height = 1\n        pad_bottom += 1\n    if width == 0:\n        width = 1\n        pad_right += 1\n\n    aspect_ratio_current = width / height\n\n    if aspect_ratio_current < aspect_ratio:\n        # image is more vertical than desired, width needs to be increased\n        diff = (aspect_ratio * height) - width\n        pad_right += int(np.ceil(diff / 2))\n        pad_left += int(np.floor(diff / 2))\n    elif aspect_ratio_current > aspect_ratio:\n        # image is more horizontal than desired, height needs to be increased\n        diff = ((1/aspect_ratio) * width) - height\n        pad_top += int(np.floor(diff / 2))\n        pad_bottom += int(np.ceil(diff / 2))\n\n    return pad_top, pad_right, pad_bottom, pad_left\n\n\ndef compute_croppings_to_reach_aspect_ratio(arr, aspect_ratio):\n    \"\"\"Compute crop amounts required to fulfill an aspect ratio.\n\n    \"Crop amounts\" here denotes the number of pixels that have to be removed\n    from each side to fulfill the desired constraint.\n\n    The aspect ratio is given as ``ratio = width / height``.\n    Depending on which dimension is smaller (height or width), only the\n    corresponding sides (top/bottom or left/right) will be cropped.\n\n    The axis-wise padding amounts are always distributed equally over the\n    sides of the respective axis (i.e. left and right, top and bottom). For\n    odd pixel amounts, one pixel will be left over after the equal\n    distribution and could be added to either side of the axis. This function\n    will always add such a left over pixel to the bottom (y-axis) or\n    right (x-axis) side.\n\n    If an aspect ratio cannot be reached exactly, this function will return\n    rather one pixel too few than one pixel too many.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray or tuple of int\n        Image-like array or shape tuple for which to compute crop amounts.\n\n    aspect_ratio : float\n        Target aspect ratio, given as width/height. E.g. ``2.0`` denotes the\n        image having twice as much width as height.\n\n    Returns\n    -------\n    tuple of int\n        Required cropping amounts to reach the target aspect ratio, given as a\n        ``tuple`` of the form ``(top, right, bottom, left)``.\n\n    \"\"\"\n    _assert_two_or_three_dims(arr)\n    assert aspect_ratio > 0, (\n        \"Expected to get an aspect ratio >0, got %.4f.\" % (aspect_ratio,))\n\n    shape = arr.shape if hasattr(arr, \"shape\") else arr\n    assert shape[0] > 0, (\n        \"Expected to get an array with height >0, got shape %s.\" % (shape,))\n\n    height, width = shape[0:2]\n    aspect_ratio_current = width / height\n\n    top = 0\n    right = 0\n    bottom = 0\n    left = 0\n\n    if aspect_ratio_current < aspect_ratio:\n        # image is more vertical than desired, height needs to be reduced\n        # c = H - W/r\n        crop_amount = height - (width / aspect_ratio)\n        crop_amount = min(crop_amount, height - 1)\n        top = int(np.floor(crop_amount / 2))\n        bottom = int(np.ceil(crop_amount / 2))\n    elif aspect_ratio_current > aspect_ratio:\n        # image is more horizontal than desired, width needs to be reduced\n        # c = W - Hr\n        crop_amount = width - height * aspect_ratio\n        crop_amount = min(crop_amount, width - 1)\n        left = int(np.floor(crop_amount / 2))\n        right = int(np.ceil(crop_amount / 2))\n\n    return top, right, bottom, left\n\n\ndef compute_paddings_to_reach_multiples_of(arr, height_multiple,\n                                           width_multiple):\n    \"\"\"Compute pad amounts until img height/width are multiples of given values.\n\n    See :func:`~imgaug.imgaug.compute_paddings_for_aspect_ratio` for an\n    explanation of how the required padding amounts are distributed per\n    image axis.\n\n    Added in 0.4.0. (Previously named\n    ``imgaug.imgaug.compute_paddings_to_reach_multiples_of()``.)\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray or tuple of int\n        Image-like array or shape tuple for which to compute pad amounts.\n\n    height_multiple : None or int\n        The desired multiple of the height. The computed padding amount will\n        reflect a padding that increases the y axis size until it is a multiple\n        of this value.\n\n    width_multiple : None or int\n        The desired multiple of the width. The computed padding amount will\n        reflect a padding that increases the x axis size until it is a multiple\n        of this value.\n\n    Returns\n    -------\n    tuple of int\n        Required padding amounts to reach multiples of the provided values,\n        given as a ``tuple`` of the form ``(top, right, bottom, left)``.\n\n    \"\"\"\n    def _compute_axis_value(axis_size, multiple):\n        if multiple is None:\n            return 0, 0\n        if axis_size == 0:\n            to_pad = multiple\n        elif axis_size % multiple == 0:\n            to_pad = 0\n        else:\n            to_pad = multiple - (axis_size % multiple)\n        return int(np.floor(to_pad/2)), int(np.ceil(to_pad/2))\n\n    _assert_two_or_three_dims(arr)\n\n    if height_multiple is not None:\n        assert height_multiple > 0, (\n            \"Can only pad to multiples of 1 or larger, got %d.\" % (\n                height_multiple,))\n    if width_multiple is not None:\n        assert width_multiple > 0, (\n            \"Can only pad to multiples of 1 or larger, got %d.\" % (\n                width_multiple,))\n\n    shape = arr.shape if hasattr(arr, \"shape\") else arr\n    height, width = shape[0:2]\n\n    top, bottom = _compute_axis_value(height, height_multiple)\n    left, right = _compute_axis_value(width, width_multiple)\n\n    return top, right, bottom, left\n\n\ndef compute_croppings_to_reach_multiples_of(arr, height_multiple,\n                                            width_multiple):\n    \"\"\"Compute croppings to reach multiples of given heights/widths.\n\n    See :func:`~imgaug.imgaug.compute_paddings_for_aspect_ratio` for an\n    explanation of how the required cropping amounts are distributed per\n    image axis.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray or tuple of int\n        Image-like array or shape tuple for which to compute crop amounts.\n\n    height_multiple : None or int\n        The desired multiple of the height. The computed croppings will\n        reflect a crop operation that decreases the y axis size until it is\n        a multiple of this value.\n\n    width_multiple : None or int\n        The desired multiple of the width. The computed croppings amount will\n        reflect a crop operation that decreases the x axis size until it is\n        a multiple of this value.\n\n    Returns\n    -------\n    tuple of int\n        Required cropping amounts to reach multiples of the provided values,\n        given as a ``tuple`` of the form ``(top, right, bottom, left)``.\n\n    \"\"\"\n    def _compute_axis_value(axis_size, multiple):\n        if multiple is None:\n            return 0, 0\n        if axis_size == 0:\n            to_crop = 0\n        elif axis_size % multiple == 0:\n            to_crop = 0\n        else:\n            to_crop = axis_size % multiple\n        return int(np.floor(to_crop/2)), int(np.ceil(to_crop/2))\n\n    _assert_two_or_three_dims(arr)\n\n    if height_multiple is not None:\n        assert height_multiple > 0, (\n            \"Can only crop to multiples of 1 or larger, got %d.\" % (\n                height_multiple,))\n    if width_multiple is not None:\n        assert width_multiple > 0, (\n            \"Can only crop to multiples of 1 or larger, got %d.\" % (\n                width_multiple,))\n\n    shape = arr.shape if hasattr(arr, \"shape\") else arr\n    height, width = shape[0:2]\n\n    top, bottom = _compute_axis_value(height, height_multiple)\n    left, right = _compute_axis_value(width, width_multiple)\n\n    return top, right, bottom, left\n\n\ndef compute_paddings_to_reach_powers_of(arr, height_base, width_base,\n                                        allow_zero_exponent=False):\n    \"\"\"Compute paddings to reach powers of given base values.\n\n    For given axis size ``S``, padded size ``S'`` (``S' >= S``) and base ``B``\n    this function computes paddings that fulfill ``S' = B^E``, where ``E``\n    is any exponent from the discrete interval ``[0 .. inf)``.\n\n    See :func:`~imgaug.imgaug.compute_paddings_for_aspect_ratio` for an\n    explanation of how the required padding amounts are distributed per\n    image axis.\n\n    Added in 0.4.0. (Previously named\n    ``imgaug.imgaug.compute_paddings_to_reach_exponents_of()``.)\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray or tuple of int\n        Image-like array or shape tuple for which to compute pad amounts.\n\n    height_base : None or int\n        The desired base of the height.\n\n    width_base : None or int\n        The desired base of the width.\n\n    allow_zero_exponent : bool, optional\n        Whether ``E=0`` in ``S'=B^E`` is a valid value. If ``True``, axes\n        with size ``0`` or ``1`` will be padded up to size ``B^0=1`` and\n        axes with size ``1 < S <= B`` will be padded up to ``B^1=B``.\n        If ``False``, the minimum output axis size is always at least ``B``.\n\n    Returns\n    -------\n    tuple of int\n        Required padding amounts to fulfill ``S' = B^E`` given as a\n        ``tuple`` of the form ``(top, right, bottom, left)``.\n\n    \"\"\"\n    def _compute_axis_value(axis_size, base):\n        if base is None:\n            return 0, 0\n        if axis_size == 0:\n            to_pad = 1 if allow_zero_exponent else base\n        elif axis_size <= base:\n            to_pad = base - axis_size\n        else:\n            # log_{base}(axis_size) in numpy\n            exponent = np.log(axis_size) / np.log(base)\n\n            to_pad = (base ** int(np.ceil(exponent))) - axis_size\n\n        return int(np.floor(to_pad/2)), int(np.ceil(to_pad/2))\n\n    _assert_two_or_three_dims(arr)\n\n    if height_base is not None:\n        assert height_base > 1, (\n            \"Can only pad to base larger than 1, got %d.\" % (height_base,))\n    if width_base is not None:\n        assert width_base > 1, (\n            \"Can only pad to base larger than 1, got %d.\" % (width_base,))\n\n    shape = arr.shape if hasattr(arr, \"shape\") else arr\n    height, width = shape[0:2]\n\n    top, bottom = _compute_axis_value(height, height_base)\n    left, right = _compute_axis_value(width, width_base)\n\n    return top, right, bottom, left\n\n\ndef compute_croppings_to_reach_powers_of(arr, height_base, width_base,\n                                         allow_zero_exponent=False):\n    \"\"\"Compute croppings to reach powers of given base values.\n\n    For given axis size ``S``, cropped size ``S'`` (``S' <= S``) and base ``B``\n    this function computes croppings that fulfill ``S' = B^E``, where ``E``\n    is any exponent from the discrete interval ``[0 .. inf)``.\n\n    See :func:`~imgaug.imgaug.compute_paddings_for_aspect_ratio` for an\n    explanation of how the required cropping amounts are distributed per\n    image axis.\n\n    .. note::\n\n        For axes where ``S == 0``, this function alwayws returns zeros as\n        croppings.\n\n        For axes where ``1 <= S < B`` see parameter `allow_zero_exponent`.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray or tuple of int\n        Image-like array or shape tuple for which to compute crop amounts.\n\n    height_base : None or int\n        The desired base of the height.\n\n    width_base : None or int\n        The desired base of the width.\n\n    allow_zero_exponent : bool\n        Whether ``E=0`` in ``S'=B^E`` is a valid value. If ``True``, axes\n        with size ``1 <= S < B`` will be cropped to size ``B^0=1``.\n        If ``False``, axes with sizes ``S < B`` will not be changed.\n\n    Returns\n    -------\n    tuple of int\n        Required cropping amounts to fulfill ``S' = B^E`` given as a\n        ``tuple`` of the form ``(top, right, bottom, left)``.\n\n    \"\"\"\n    def _compute_axis_value(axis_size, base):\n        if base is None:\n            return 0, 0\n        if axis_size == 0:\n            to_crop = 0\n        elif axis_size < base:\n            # crop down to B^0 = 1\n            to_crop = axis_size - 1 if allow_zero_exponent else 0\n        else:\n            # log_{base}(axis_size) in numpy\n            exponent = np.log(axis_size) / np.log(base)\n\n            to_crop = axis_size - (base ** int(exponent))\n\n        return int(np.floor(to_crop/2)), int(np.ceil(to_crop/2))\n\n    _assert_two_or_three_dims(arr)\n\n    if height_base is not None:\n        assert height_base > 1, (\n            \"Can only crop to base larger than 1, got %d.\" % (height_base,))\n    if width_base is not None:\n        assert width_base > 1, (\n            \"Can only crop to base larger than 1, got %d.\" % (width_base,))\n\n    shape = arr.shape if hasattr(arr, \"shape\") else arr\n    height, width = shape[0:2]\n\n    top, bottom = _compute_axis_value(height, height_base)\n    left, right = _compute_axis_value(width, width_base)\n\n    return top, right, bottom, left\n\n\n@ia.deprecated(alt_func=\"Resize\",\n               comment=\"Resize has the exactly same interface as Scale.\")\ndef Scale(*args, **kwargs):\n    \"\"\"Augmenter that resizes images to specified heights and widths.\"\"\"\n    # pylint: disable=invalid-name\n    return Resize(*args, **kwargs)\n\n\nclass Resize(meta.Augmenter):\n    \"\"\"Augmenter that resizes images to specified heights and widths.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.imgaug.imresize_many_images`.\n\n    Parameters\n    ----------\n    size : 'keep' or int or float or tuple of int or tuple of float or list of int or list of float or imgaug.parameters.StochasticParameter or dict\n        The new size of the images.\n\n            * If this has the string value ``keep``, the original height and\n              width values will be kept (image is not resized).\n            * If this is an ``int``, this value will always be used as the new\n              height and width of the images.\n            * If this is a ``float`` ``v``, then per image the image's height\n              ``H`` and width ``W`` will be changed to ``H*v`` and ``W*v``.\n            * If this is a ``tuple``, it is expected to have two entries\n              ``(a, b)``. If at least one of these are ``float`` s, a value\n              will be sampled from range ``[a, b]`` and used as the ``float``\n              value to resize the image (see above). If both are ``int`` s, a\n              value will be sampled from the discrete range ``[a..b]`` and\n              used as the integer value to resize the image (see above).\n            * If this is a ``list``, a random value from the ``list`` will be\n              picked to resize the image. All values in the ``list`` must be\n              ``int`` s or ``float`` s (no mixture is possible).\n            * If this is a ``StochasticParameter``, then this parameter will\n              first be queried once per image. The resulting value will be used\n              for both height and width.\n            * If this is a ``dict``, it may contain the keys ``height`` and\n              ``width`` or the keys ``shorter-side`` and ``longer-side``. Each\n              key may have the same datatypes as above and describes the\n              scaling on x and y-axis or the shorter and longer axis,\n              respectively. Both axis are sampled independently. Additionally,\n              one of the keys may have the value ``keep-aspect-ratio``, which\n              means that the respective side of the image will be resized so\n              that the original aspect ratio is kept. This is useful when only\n              resizing one image size by a pixel value (e.g. resize images to\n              a height of ``64`` pixels and resize the width so that the\n              overall aspect ratio is maintained).\n\n    interpolation : imgaug.ALL or int or str or list of int or list of str or imgaug.parameters.StochasticParameter, optional\n        Interpolation to use.\n\n            * If ``imgaug.ALL``, then a random interpolation from ``nearest``,\n              ``linear``, ``area`` or ``cubic`` will be picked (per image).\n            * If ``int``, then this interpolation will always be used.\n              Expected to be any of the following:\n              ``cv2.INTER_NEAREST``, ``cv2.INTER_LINEAR``, ``cv2.INTER_AREA``,\n              ``cv2.INTER_CUBIC``\n            * If string, then this interpolation will always be used.\n              Expected to be any of the following:\n              ``nearest``, ``linear``, ``area``, ``cubic``\n            * If ``list`` of ``int`` / ``str``, then a random one of the values\n              will be picked per image as the interpolation.\n            * If a ``StochasticParameter``, then this parameter will be\n              queried per image and is expected to return an ``int`` or\n              ``str``.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Resize(32)\n\n    Resize all images to ``32x32`` pixels.\n\n    >>> aug = iaa.Resize(0.5)\n\n    Resize all images to ``50`` percent of their original size.\n\n    >>> aug = iaa.Resize((16, 22))\n\n    Resize all images to a random height and width within the discrete\n    interval ``[16..22]`` (uniformly sampled per image).\n\n    >>> aug = iaa.Resize((0.5, 0.75))\n\n    Resize all any input image so that its height (``H``) and width (``W``)\n    become ``H*v`` and ``W*v``, where ``v`` is uniformly sampled from the\n    interval ``[0.5, 0.75]``.\n\n    >>> aug = iaa.Resize([16, 32, 64])\n\n    Resize all images either to ``16x16``, ``32x32`` or ``64x64`` pixels.\n\n    >>> aug = iaa.Resize({\"height\": 32})\n\n    Resize all images to a height of ``32`` pixels and keeps the original\n    width.\n\n    >>> aug = iaa.Resize({\"height\": 32, \"width\": 48})\n\n    Resize all images to a height of ``32`` pixels and a width of ``48``.\n\n    >>> aug = iaa.Resize({\"height\": 32, \"width\": \"keep-aspect-ratio\"})\n\n    Resize all images to a height of ``32`` pixels and resizes the\n    x-axis (width) so that the aspect ratio is maintained.\n\n    >>> aug = iaa.Resize(\n    >>>     {\"shorter-side\": 224, \"longer-side\": \"keep-aspect-ratio\"})\n\n    Resize all images to a height/width of ``224`` pixels, depending on which\n    axis is shorter and resize the other axis so that the aspect ratio is\n    maintained.\n\n    >>> aug = iaa.Resize({\"height\": (0.5, 0.75), \"width\": [16, 32, 64]})\n\n    Resize all images to a height of ``H*v``, where ``H`` is the original\n    height and ``v`` is a random value sampled from the interval\n    ``[0.5, 0.75]``. The width/x-axis of each image is resized to either\n    ``16`` or ``32`` or ``64`` pixels.\n\n    >>> aug = iaa.Resize(32, interpolation=[\"linear\", \"cubic\"])\n\n    Resize all images to ``32x32`` pixels. Randomly use either ``linear``\n    or ``cubic`` interpolation.\n\n    \"\"\"\n\n    def __init__(self, size, interpolation=\"cubic\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Resize, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.size, self.size_order = self._handle_size_arg(size, False)\n        self.interpolation = self._handle_interpolation_arg(interpolation)\n\n    @classmethod\n    @iap._prefetchable_str\n    def _handle_size_arg(cls, size, subcall):\n        def _dict_to_size_tuple(val1, val2):\n            kaa = \"keep-aspect-ratio\"\n            not_both_kaa = (val1 != kaa or val2 != kaa)\n            assert not_both_kaa, (\n                \"Expected at least one value to not be \\\"keep-aspect-ratio\\\", \"\n                \"but got it two times.\")\n\n            size_tuple = []\n            for k in [val1, val2]:\n                if k in [\"keep-aspect-ratio\", \"keep\"]:\n                    entry = iap.Deterministic(k)\n                else:\n                    entry = cls._handle_size_arg(k, True)\n                size_tuple.append(entry)\n            return tuple(size_tuple)\n\n        def _contains_any_key(dict_, keys):\n            return any([key in dict_ for key in keys])\n\n        # HW = height, width\n        # SL = shorter, longer\n        size_order = \"HW\"\n\n        if size == \"keep\":\n            result = iap.Deterministic(\"keep\")\n        elif ia.is_single_number(size):\n            assert size > 0, \"Expected only values > 0, got %s\" % (size,)\n            result = iap.Deterministic(size)\n        elif not subcall and isinstance(size, dict):\n            if len(size.keys()) == 0:\n                result = iap.Deterministic(\"keep\")\n            elif _contains_any_key(size, [\"height\", \"width\"]):\n                height = size.get(\"height\", \"keep\")\n                width = size.get(\"width\", \"keep\")\n                result = _dict_to_size_tuple(height, width)\n            elif _contains_any_key(size, [\"shorter-side\", \"longer-side\"]):\n                shorter = size.get(\"shorter-side\", \"keep\")\n                longer = size.get(\"longer-side\", \"keep\")\n                result = _dict_to_size_tuple(shorter, longer)\n                size_order = \"SL\"\n            else:\n                raise ValueError(\n                    \"Expected dictionary containing no keys, \"\n                    \"the keys \\\"height\\\" and/or \\\"width\\\", \"\n                    \"or the keys \\\"shorter-side\\\" and/or \\\"longer-side\\\". \"\n                    \"Got keys: %s.\" % (str(size.keys()),))\n        elif isinstance(size, tuple):\n            assert len(size) == 2, (\n                \"Expected size tuple to contain exactly 2 values, \"\n                \"got %d.\" % (len(size),))\n            assert size[0] > 0 and size[1] > 0, (\n                \"Expected size tuple to only contain values >0, \"\n                \"got %d and %d.\" % (size[0], size[1]))\n            if ia.is_single_float(size[0]) or ia.is_single_float(size[1]):\n                result = iap.Uniform(size[0], size[1])\n            else:\n                result = iap.DiscreteUniform(size[0], size[1])\n        elif isinstance(size, list):\n            if len(size) == 0:\n                result = iap.Deterministic(\"keep\")\n            else:\n                all_int = all([ia.is_single_integer(v) for v in size])\n                all_float = all([ia.is_single_float(v) for v in size])\n                assert all_int or all_float, (\n                    \"Expected to get only integers or floats.\")\n                assert all([v > 0 for v in size]), (\n                    \"Expected all values to be >0.\")\n                result = iap.Choice(size)\n        elif isinstance(size, iap.StochasticParameter):\n            result = size\n        else:\n            raise ValueError(\n                \"Expected number, tuple of two numbers, list of numbers, \"\n                \"dictionary of form \"\n                \"{'height': number/tuple/list/'keep-aspect-ratio'/'keep', \"\n                \"'width': <analogous>}, dictionary of form \"\n                \"{'shorter-side': number/tuple/list/'keep-aspect-ratio'/\"\n                \"'keep', 'longer-side': <analogous>} \"\n                \"or StochasticParameter, got %s.\" % (type(size),)\n            )\n\n        if subcall:\n            return result\n        return result, size_order\n\n    @classmethod\n    def _handle_interpolation_arg(cls, interpolation):\n        if interpolation == ia.ALL:\n            interpolation = iap.Choice(\n                [\"nearest\", \"linear\", \"area\", \"cubic\"])\n        elif ia.is_single_integer(interpolation):\n            interpolation = iap.Deterministic(interpolation)\n        elif ia.is_string(interpolation):\n            interpolation = iap.Deterministic(interpolation)\n        elif ia.is_iterable(interpolation):\n            interpolation = iap.Choice(interpolation)\n        elif isinstance(interpolation, iap.StochasticParameter):\n            pass\n        else:\n            raise Exception(\n                \"Expected int or string or iterable or StochasticParameter, \"\n                \"got %s.\" % (type(interpolation),))\n        return interpolation\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        nb_rows = batch.nb_rows\n        samples = self._draw_samples(nb_rows, random_state)\n\n        if batch.images is not None:\n            batch.images = self._augment_images_by_samples(batch.images,\n                                                           samples)\n\n        if batch.heatmaps is not None:\n            # TODO this uses the same interpolation as for images for heatmaps\n            #      while other augmenters resort to cubic\n            batch.heatmaps = self._augment_maps_by_samples(\n                batch.heatmaps, \"arr_0to1\", samples)\n\n        if batch.segmentation_maps is not None:\n            batch.segmentation_maps = self._augment_maps_by_samples(\n                batch.segmentation_maps, \"arr\",\n                (samples[0], samples[1], [None] * nb_rows))\n\n        for augm_name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                          \"line_strings\"]:\n            augm_value = getattr(batch, augm_name)\n            if augm_value is not None:\n                func = functools.partial(\n                    self._augment_keypoints_by_samples,\n                    samples=samples)\n                cbaois = self._apply_to_cbaois_as_keypoints(augm_value, func)\n                setattr(batch, augm_name, cbaois)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _augment_images_by_samples(self, images, samples):\n        input_was_array = False\n        input_dtype = None\n        if ia.is_np_array(images):\n            input_was_array = True\n            input_dtype = images.dtype\n\n        samples_a, samples_b, samples_ip = samples\n        result = []\n        for i, image in enumerate(images):\n            h, w = self._compute_height_width(image.shape, samples_a[i],\n                                              samples_b[i], self.size_order)\n            image_rs = ia.imresize_single_image(image, (h, w),\n                                                interpolation=samples_ip[i])\n            result.append(image_rs)\n\n        if input_was_array:\n            all_same_size = (len({image.shape for image in result}) == 1)\n            if all_same_size:\n                result = np.array(result, dtype=input_dtype)\n\n        return result\n\n    # Added in 0.4.0.\n    def _augment_maps_by_samples(self, augmentables, arr_attr_name, samples):\n        result = []\n        samples_h, samples_w, samples_ip = samples\n\n        for i, augmentable in enumerate(augmentables):\n            arr = getattr(augmentable, arr_attr_name)\n            arr_shape = arr.shape\n            img_shape = augmentable.shape\n            h_img, w_img = self._compute_height_width(\n                img_shape, samples_h[i], samples_w[i], self.size_order)\n            h = int(np.round(h_img * (arr_shape[0] / img_shape[0])))\n            w = int(np.round(w_img * (arr_shape[1] / img_shape[1])))\n            h = max(h, 1)\n            w = max(w, 1)\n            if samples_ip[0] is not None:\n                # TODO change this for heatmaps to always have cubic or\n                #      automatic interpolation?\n                augmentable_resize = augmentable.resize(\n                    (h, w), interpolation=samples_ip[i])\n            else:\n                augmentable_resize = augmentable.resize((h, w))\n            augmentable_resize.shape = (h_img, w_img) + img_shape[2:]\n            result.append(augmentable_resize)\n\n        return result\n\n    # Added in 0.4.0.\n    def _augment_keypoints_by_samples(self, kpsois, samples):\n        result = []\n        samples_a, samples_b, _samples_ip = samples\n        for i, kpsoi in enumerate(kpsois):\n            h, w = self._compute_height_width(\n                kpsoi.shape, samples_a[i], samples_b[i], self.size_order)\n            new_shape = (h, w) + kpsoi.shape[2:]\n            keypoints_on_image_rs = kpsoi.on_(new_shape)\n\n            result.append(keypoints_on_image_rs)\n\n        return result\n\n    def _draw_samples(self, nb_images, random_state):\n        rngs = random_state.duplicate(3)\n        if isinstance(self.size, tuple):\n            samples_h = self.size[0].draw_samples(nb_images,\n                                                  random_state=rngs[0])\n            samples_w = self.size[1].draw_samples(nb_images,\n                                                  random_state=rngs[1])\n        else:\n            samples_h = self.size.draw_samples(nb_images, random_state=rngs[0])\n            samples_w = samples_h\n\n        samples_ip = self.interpolation.draw_samples(nb_images,\n                                                     random_state=rngs[2])\n        return samples_h, samples_w, samples_ip\n\n    @classmethod\n    def _compute_height_width(cls, image_shape, sample_a, sample_b, size_order):\n        imh, imw = image_shape[0:2]\n\n        if size_order == 'SL':\n            # size order: short, long\n            if imh < imw:\n                h, w = sample_a, sample_b\n            else:\n                w, h = sample_a, sample_b\n        else:\n            # size order: height, width\n            h, w = sample_a, sample_b\n\n        if ia.is_single_float(h):\n            assert h > 0, \"Expected 'h' to be >0, got %.4f\" % (h,)\n            h = int(np.round(imh * h))\n            h = h if h > 0 else 1\n        elif h == \"keep\":\n            h = imh\n        if ia.is_single_float(w):\n            assert w > 0, \"Expected 'w' to be >0, got %.4f\" % (w,)\n            w = int(np.round(imw * w))\n            w = w if w > 0 else 1\n        elif w == \"keep\":\n            w = imw\n\n        # at least the checks for keep-aspect-ratio must come after\n        # the float checks, as they are dependent on the results\n        # this is also why these are not written as elifs\n        if h == \"keep-aspect-ratio\":\n            h_per_w_orig = imh / imw\n            h = int(np.round(w * h_per_w_orig))\n        if w == \"keep-aspect-ratio\":\n            w_per_h_orig = imw / imh\n            w = int(np.round(h * w_per_h_orig))\n\n        return h, w\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.size, self.interpolation, self.size_order]\n\n\nclass _CropAndPadSamplingResult(object):\n    def __init__(self, crop_top, crop_right, crop_bottom, crop_left,\n                 pad_top, pad_right, pad_bottom, pad_left, pad_mode, pad_cval):\n        self.crop_top = crop_top\n        self.crop_right = crop_right\n        self.crop_bottom = crop_bottom\n        self.crop_left = crop_left\n        self.pad_top = pad_top\n        self.pad_right = pad_right\n        self.pad_bottom = pad_bottom\n        self.pad_left = pad_left\n        self.pad_mode = pad_mode\n        self.pad_cval = pad_cval\n\n    def croppings(self, i):\n        \"\"\"Get absolute pixel amounts of croppings as a TRBL tuple.\"\"\"\n        return (\n            self.crop_top[i],\n            self.crop_right[i],\n            self.crop_bottom[i],\n            self.crop_left[i]\n        )\n\n    def paddings(self, i):\n        \"\"\"Get absolute pixel amounts of paddings as a TRBL tuple.\"\"\"\n        return (\n            self.pad_top[i],\n            self.pad_right[i],\n            self.pad_bottom[i],\n            self.pad_left[i]\n        )\n\n\nclass CropAndPad(meta.Augmenter):\n    \"\"\"Crop/pad images by pixel amounts or fractions of image sizes.\n\n    Cropping removes pixels at the sides (i.e. extracts a subimage from\n    a given full image). Padding adds pixels to the sides (e.g. black pixels).\n\n    This augmenter will never crop images below a height or width of ``1``.\n\n    .. note::\n\n        This augmenter automatically resizes images back to their original size\n        after it has augmented them. To deactivate this, add the\n        parameter ``keep_size=False``.\n\n    **Supported dtypes**:\n\n    if (keep_size=False):\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    if (keep_size=True):\n\n        minimum of (\n            ``imgaug.augmenters.size.CropAndPad(keep_size=False)``,\n            :func:`~imgaug.imgaug.imresize_many_images`\n        )\n\n    Parameters\n    ----------\n    px : None or int or imgaug.parameters.StochasticParameter or tuple, optional\n        The number of pixels to crop (negative values) or pad (positive values)\n        on each side of the image. Either this or the parameter `percent` may\n        be set, not both at the same time.\n\n            * If ``None``, then pixel-based cropping/padding will not be used.\n            * If ``int``, then that exact number of pixels will always be\n              cropped/padded.\n            * If ``StochasticParameter``, then that parameter will be used for\n              each image. Four samples will be drawn per image (top, right,\n              bottom, left), unless `sample_independently` is set to ``False``,\n              as then only one value will be sampled per image and used for\n              all sides.\n            * If a ``tuple`` of two ``int`` s with values ``a`` and ``b``,\n              then each side will be cropped/padded by a random amount sampled\n              uniformly per image and side from the inteval ``[a, b]``. If\n              however `sample_independently` is set to ``False``, only one\n              value will be sampled per image and used for all sides.\n            * If a ``tuple`` of four entries, then the entries represent top,\n              right, bottom, left. Each entry may be a single ``int`` (always\n              crop/pad by exactly that value), a ``tuple`` of two ``int`` s\n              ``a`` and ``b`` (crop/pad by an amount within ``[a, b]``), a\n              ``list`` of ``int`` s (crop/pad by a random value that is\n              contained in the ``list``) or a ``StochasticParameter`` (sample\n              the amount to crop/pad from that parameter).\n\n    percent : None or number or imgaug.parameters.StochasticParameter or tuple, optional\n        The number of pixels to crop (negative values) or pad (positive values)\n        on each side of the image given as a *fraction* of the image\n        height/width. E.g. if this is set to ``-0.1``, the augmenter will\n        always crop away ``10%`` of the image's height at both the top and the\n        bottom (both ``10%`` each), as well as ``10%`` of the width at the\n        right and left.\n        Expected value range is ``(-1.0, inf)``.\n        Either this or the parameter `px` may be set, not both\n        at the same time.\n\n            * If ``None``, then fraction-based cropping/padding will not be\n              used.\n            * If ``number``, then that fraction will always be cropped/padded.\n            * If ``StochasticParameter``, then that parameter will be used for\n              each image. Four samples will be drawn per image (top, right,\n              bottom, left). If however `sample_independently` is set to\n              ``False``, only one value will be sampled per image and used for\n              all sides.\n            * If a ``tuple`` of two ``float`` s with values ``a`` and ``b``,\n              then each side will be cropped/padded by a random fraction\n              sampled uniformly per image and side from the interval\n              ``[a, b]``. If however `sample_independently` is set to\n              ``False``, only one value will be sampled per image and used for\n              all sides.\n            * If a ``tuple`` of four entries, then the entries represent top,\n              right, bottom, left. Each entry may be a single ``float``\n              (always crop/pad by exactly that percent value), a ``tuple`` of\n              two ``float`` s ``a`` and ``b`` (crop/pad by a fraction from\n              ``[a, b]``), a ``list`` of ``float`` s (crop/pad by a random\n              value that is contained in the list) or a ``StochasticParameter``\n              (sample the percentage to crop/pad from that parameter).\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        Padding mode to use. The available modes match the numpy padding modes,\n        i.e. ``constant``, ``edge``, ``linear_ramp``, ``maximum``, ``median``,\n        ``minimum``, ``reflect``, ``symmetric``, ``wrap``. The modes\n        ``constant`` and ``linear_ramp`` use extra values, which are provided\n        by ``pad_cval`` when necessary. See :func:`~imgaug.imgaug.pad` for\n        more details.\n\n            * If ``imgaug.ALL``, then a random mode from all available modes\n              will be sampled per image.\n            * If a ``str``, it will be used as the pad mode for all images.\n            * If a ``list`` of ``str``, a random one of these will be sampled\n              per image and used as the mode.\n            * If ``StochasticParameter``, a random mode will be sampled from\n              this parameter per image.\n\n    pad_cval : number or tuple of number list of number or imgaug.parameters.StochasticParameter, optional\n        The constant value to use if the pad mode is ``constant`` or the end\n        value to use if the mode is ``linear_ramp``.\n        See :func:`~imgaug.imgaug.pad` for more details.\n\n            * If ``number``, then that value will be used.\n            * If a ``tuple`` of two ``number`` s and at least one of them is\n              a ``float``, then a random number will be uniformly sampled per\n              image from the continuous interval ``[a, b]`` and used as the\n              value. If both ``number`` s are ``int`` s, the interval is\n              discrete.\n            * If a ``list`` of ``number``, then a random value will be chosen\n              from the elements of the ``list`` and used as the value.\n            * If ``StochasticParameter``, a random value will be sampled from\n              that parameter per image.\n\n    keep_size : bool, optional\n        After cropping and padding, the result image will usually have a\n        different height/width compared to the original input image. If this\n        parameter is set to ``True``, then the cropped/padded image will be\n        resized to the input image's size, i.e. the augmenter's output shape\n        is always identical to the input shape.\n\n    sample_independently : bool, optional\n        If ``False`` *and* the values for `px`/`percent` result in exactly\n        *one* probability distribution for all image sides, only one single\n        value will be sampled from that probability distribution and used for\n        all sides. I.e. the crop/pad amount then is the same for all sides.\n        If ``True``, four values will be sampled independently, one per side.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CropAndPad(px=(-10, 0))\n\n    Crop each side by a random pixel value sampled uniformly per image and\n    side from the discrete interval ``[-10..0]``.\n\n    >>> aug = iaa.CropAndPad(px=(0, 10))\n\n    Pad each side by a random pixel value sampled uniformly per image and\n    side from the discrete interval ``[0..10]``. The padding happens by\n    zero-padding, i.e. it adds black pixels (default setting).\n\n    >>> aug = iaa.CropAndPad(px=(0, 10), pad_mode=\"edge\")\n\n    Pad each side by a random pixel value sampled uniformly per image and\n    side from the discrete interval ``[0..10]``. The padding uses the\n    ``edge`` mode from numpy's pad function, i.e. the pixel colors around\n    the image sides are repeated.\n\n    >>> aug = iaa.CropAndPad(px=(0, 10), pad_mode=[\"constant\", \"edge\"])\n\n    Similar to the previous example, but uses zero-padding (``constant``) for\n    half of the images and ``edge`` padding for the other half.\n\n    >>> aug = iaa.CropAndPad(px=(0, 10), pad_mode=ia.ALL, pad_cval=(0, 255))\n\n    Similar to the previous example, but uses any available padding mode.\n    In case the padding mode ends up being ``constant`` or ``linear_ramp``,\n    and random intensity is uniformly sampled (once per image) from the\n    discrete interval ``[0..255]`` and used as the intensity of the new\n    pixels.\n\n    >>> aug = iaa.CropAndPad(px=(0, 10), sample_independently=False)\n\n    Pad each side by a random pixel value sampled uniformly once per image\n    from the discrete interval ``[0..10]``. Each sampled value is used\n    for *all* sides of the corresponding image.\n\n    >>> aug = iaa.CropAndPad(px=(0, 10), keep_size=False)\n\n    Pad each side by a random pixel value sampled uniformly per image and\n    side from the discrete interval ``[0..10]``. Afterwards, do **not**\n    resize the padded image back to the input image's size. This will increase\n    the image's height and width by a maximum of ``20`` pixels.\n\n    >>> aug = iaa.CropAndPad(px=((0, 10), (0, 5), (0, 10), (0, 5)))\n\n    Pad the top and bottom by a random pixel value sampled uniformly from the\n    discrete interval ``[0..10]``. Pad the left and right analogously by\n    a random value sampled from ``[0..5]``. Each value is always sampled\n    independently.\n\n    >>> aug = iaa.CropAndPad(percent=(0, 0.1))\n\n    Pad each side by a random fraction sampled uniformly from the continuous\n    interval ``[0.0, 0.10]``. The fraction is sampled once per image and\n    side. E.g. a sampled fraction of ``0.1`` for the top side would pad by\n    ``0.1*H``, where ``H`` is the height of the input image.\n\n    >>> aug = iaa.CropAndPad(\n    >>>     percent=([0.05, 0.1], [0.05, 0.1], [0.05, 0.1], [0.05, 0.1]))\n\n    Pads each side by either ``5%`` or ``10%``. The values are sampled\n    once per side and image.\n\n    >>> aug = iaa.CropAndPad(px=(-10, 10))\n\n    Sample uniformly per image and side a value ``v`` from the discrete range\n    ``[-10..10]``. Then either crop (negative sample) or pad (positive sample)\n    the side by ``v`` pixels.\n\n    \"\"\"\n\n    def __init__(self, px=None, percent=None, pad_mode=\"constant\", pad_cval=0,\n                 keep_size=True, sample_independently=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        # pylint: disable=invalid-name\n        super(CropAndPad, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        if px is None and percent is None:\n            percent = (-0.1, 0.1)\n\n        self.mode, self.all_sides, self.top, self.right, self.bottom, \\\n            self.left = self._handle_px_and_percent_args(px, percent)\n\n        self.pad_mode = _handle_pad_mode_param(pad_mode)\n        # TODO enable ALL here, like in e.g. Affine\n        self.pad_cval = iap.handle_discrete_param(\n            pad_cval, \"pad_cval\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=True)\n\n        self.keep_size = keep_size\n        self.sample_independently = sample_independently\n\n        # set these to None to use the same values as sampled for the\n        # images (not tested)\n        self._pad_mode_heatmaps = \"constant\"\n        self._pad_mode_segmentation_maps = \"constant\"\n        self._pad_cval_heatmaps = 0.0\n        self._pad_cval_segmentation_maps = 0\n\n    @classmethod\n    @iap._prefetchable\n    def _handle_px_and_percent_args(cls, px, percent):\n        # pylint: disable=invalid-name\n        all_sides = None\n        top, right, bottom, left = None, None, None, None\n\n        if px is None and percent is None:\n            mode = \"noop\"\n        elif px is not None and percent is not None:\n            raise Exception(\"Can only pad by pixels or percent, not both.\")\n        elif px is not None:\n            mode = \"px\"\n            all_sides, top, right, bottom, left = cls._handle_px_arg(px)\n        else:  # = elif percent is not None:\n            mode = \"percent\"\n            all_sides, top, right, bottom, left = cls._handle_percent_arg(\n                percent)\n        return mode, all_sides, top, right, bottom, left\n\n    @classmethod\n    def _handle_px_arg(cls, px):\n        # pylint: disable=invalid-name\n        all_sides = None\n        top, right, bottom, left = None, None, None, None\n\n        if ia.is_single_integer(px):\n            all_sides = iap.Deterministic(px)\n        elif isinstance(px, tuple):\n            assert len(px) in [2, 4], (\n                \"Expected 'px' given as a tuple to contain 2 or 4 \"\n                \"entries, got %d.\" % (len(px),))\n\n            def handle_param(p):\n                if ia.is_single_integer(p):\n                    return iap.Deterministic(p)\n                if isinstance(p, tuple):\n                    assert len(p) == 2, (\n                        \"Expected tuple of 2 values, got %d.\" % (len(p)))\n                    only_ints = (\n                        ia.is_single_integer(p[0])\n                        and ia.is_single_integer(p[1]))\n                    assert only_ints, (\n                        \"Expected tuple of integers, got %s and %s.\" % (\n                            type(p[0]), type(p[1])))\n                    return iap.DiscreteUniform(p[0], p[1])\n                if isinstance(p, list):\n                    assert len(p) > 0, (\n                        \"Expected non-empty list, but got empty one.\")\n                    assert all([ia.is_single_integer(val) for val in p]), (\n                        \"Expected list of ints, got types %s.\" % (\n                            \", \".join([str(type(v)) for v in p])))\n                    return iap.Choice(p)\n                if isinstance(p, iap.StochasticParameter):\n                    return p\n                raise Exception(\n                    \"Expected int, tuple of two ints, list of ints or \"\n                    \"StochasticParameter, got type %s.\" % (type(p),))\n\n            if len(px) == 2:\n                all_sides = handle_param(px)\n            else:  # len == 4\n                top = handle_param(px[0])\n                right = handle_param(px[1])\n                bottom = handle_param(px[2])\n                left = handle_param(px[3])\n        elif isinstance(px, iap.StochasticParameter):\n            top = right = bottom = left = px\n        else:\n            raise Exception(\n                \"Expected int, tuple of 4 \"\n                \"ints/tuples/lists/StochasticParameters or \"\n                \"StochasticParameter, got type %s.\" % (type(px),))\n        return all_sides, top, right, bottom, left\n\n    @classmethod\n    def _handle_percent_arg(cls, percent):\n        all_sides = None\n        top, right, bottom, left = None, None, None, None\n\n        if ia.is_single_number(percent):\n            assert percent > -1.0, (\n                \"Expected 'percent' to be >-1.0, got %.4f.\" % (percent,))\n            all_sides = iap.Deterministic(percent)\n        elif isinstance(percent, tuple):\n            assert len(percent) in [2, 4], (\n                \"Expected 'percent' given as a tuple to contain 2 or 4 \"\n                \"entries, got %d.\" % (len(percent),))\n\n            def handle_param(p):\n                if ia.is_single_number(p):\n                    return iap.Deterministic(p)\n                if isinstance(p, tuple):\n                    assert len(p) == 2, (\n                        \"Expected tuple of 2 values, got %d.\" % (len(p),))\n                    only_numbers = (\n                        ia.is_single_number(p[0])\n                        and ia.is_single_number(p[1]))\n                    assert only_numbers, (\n                        \"Expected tuple of numbers, got %s and %s.\" % (\n                            type(p[0]), type(p[1])))\n                    assert p[0] > -1.0 and p[1] > -1.0, (\n                        \"Expected tuple of values >-1.0, got %.4f and \"\n                        \"%.4f.\" % (p[0], p[1]))\n                    return iap.Uniform(p[0], p[1])\n                if isinstance(p, list):\n                    assert len(p) > 0, (\n                        \"Expected non-empty list, but got empty one.\")\n                    assert all([ia.is_single_number(val) for val in p]), (\n                        \"Expected list of numbers, got types %s.\" % (\n                            \", \".join([str(type(v)) for v in p])))\n                    assert all([val > -1.0 for val in p]), (\n                        \"Expected list of values >-1.0, got values %s.\" % (\n                            \", \".join([\"%.4f\" % (v,) for v in p])))\n                    return iap.Choice(p)\n                if isinstance(p, iap.StochasticParameter):\n                    return p\n                raise Exception(\n                    \"Expected int, tuple of two ints, list of ints or \"\n                    \"StochasticParameter, got type %s.\" % (type(p),))\n\n            if len(percent) == 2:\n                all_sides = handle_param(percent)\n            else:  # len == 4\n                top = handle_param(percent[0])\n                right = handle_param(percent[1])\n                bottom = handle_param(percent[2])\n                left = handle_param(percent[3])\n        elif isinstance(percent, iap.StochasticParameter):\n            top = right = bottom = left = percent\n        else:\n            raise Exception(\n                \"Expected number, tuple of 4 \"\n                \"numbers/tuples/lists/StochasticParameters or \"\n                \"StochasticParameter, got type %s.\" % (type(percent),))\n        return all_sides, top, right, bottom, left\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        shapes = batch.get_rowwise_shapes()\n        samples = self._draw_samples(random_state, shapes)\n\n        if batch.images is not None:\n            batch.images = self._augment_images_by_samples(batch.images,\n                                                           samples)\n\n        if batch.heatmaps is not None:\n            batch.heatmaps = self._augment_maps_by_samples(\n                batch.heatmaps,\n                self._pad_mode_heatmaps, self._pad_cval_heatmaps,\n                samples)\n\n        if batch.segmentation_maps is not None:\n            batch.segmentation_maps = self._augment_maps_by_samples(\n                batch.segmentation_maps,\n                self._pad_mode_segmentation_maps,\n                self._pad_cval_segmentation_maps, samples)\n\n        for augm_name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                          \"line_strings\"]:\n            augm_value = getattr(batch, augm_name)\n            if augm_value is not None:\n                func = functools.partial(\n                    self._augment_keypoints_by_samples,\n                    samples=samples)\n                cbaois = self._apply_to_cbaois_as_keypoints(augm_value, func)\n                setattr(batch, augm_name, cbaois)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _augment_images_by_samples(self, images, samples):\n        result = []\n        for i, image in enumerate(images):\n            image_cr_pa = _crop_and_pad_arr(\n                image, samples.croppings(i), samples.paddings(i),\n                samples.pad_mode[i], samples.pad_cval[i], self.keep_size)\n\n            result.append(image_cr_pa)\n\n        if ia.is_np_array(images):\n            if self.keep_size:\n                result = np.array(result, dtype=images.dtype)\n            else:\n                nb_shapes = len({image.shape for image in result})\n                if nb_shapes == 1:\n                    result = np.array(result, dtype=images.dtype)\n\n        return result\n\n    # Added in 0.4.0.\n    def _augment_maps_by_samples(self, augmentables, pad_mode, pad_cval,\n                                 samples):\n        result = []\n        for i, augmentable in enumerate(augmentables):\n            augmentable = _crop_and_pad_hms_or_segmaps_(\n                augmentable,\n                croppings_img=samples.croppings(i),\n                paddings_img=samples.paddings(i),\n                pad_mode=(pad_mode\n                          if pad_mode is not None\n                          else samples.pad_mode[i]),\n                pad_cval=(pad_cval\n                          if pad_cval is not None\n                          else samples.pad_cval[i]),\n                keep_size=self.keep_size\n            )\n\n            result.append(augmentable)\n\n        return result\n\n    # Added in 0.4.0.\n    def _augment_keypoints_by_samples(self, keypoints_on_images, samples):\n        result = []\n        for i, keypoints_on_image in enumerate(keypoints_on_images):\n            kpsoi_aug = _crop_and_pad_kpsoi_(\n                keypoints_on_image, croppings_img=samples.croppings(i),\n                paddings_img=samples.paddings(i), keep_size=self.keep_size)\n            result.append(kpsoi_aug)\n\n        return result\n\n    def _draw_samples(self, random_state, shapes):\n        nb_rows = len(shapes)\n\n        shapes_arr = np.array([shape[0:2] for shape in shapes],\n                              dtype=np.int32)\n        heights = shapes_arr[:, 0]\n        widths = shapes_arr[:, 1]\n\n        if self.mode == \"noop\":\n            top = right = bottom = left = np.full((nb_rows,), 0,\n                                                  dtype=np.int32)\n        else:\n            if self.all_sides is not None:\n                if self.sample_independently:\n                    samples = self.all_sides.draw_samples(\n                        (nb_rows, 4), random_state=random_state)\n                    top = samples[:, 0]\n                    right = samples[:, 1]\n                    bottom = samples[:, 2]\n                    left = samples[:, 3]\n                else:\n                    sample = self.all_sides.draw_samples(\n                        (nb_rows,), random_state=random_state)\n                    top = right = bottom = left = sample\n            else:\n                top = self.top.draw_samples(\n                    (nb_rows,), random_state=random_state)\n                right = self.right.draw_samples(\n                    (nb_rows,), random_state=random_state)\n                bottom = self.bottom.draw_samples(\n                    (nb_rows,), random_state=random_state)\n                left = self.left.draw_samples(\n                    (nb_rows,), random_state=random_state)\n\n            if self.mode == \"px\":\n                # no change necessary for pixel values\n                pass\n            elif self.mode == \"percent\":\n                # percentage values have to be transformed to pixel values\n                heights_f = heights.astype(np.float32)\n                widths_f = widths.astype(np.float32)\n                top = np.round(heights_f * top).astype(np.int32)\n                right = np.round(widths_f * right).astype(np.int32)\n                bottom = np.round(heights_f * bottom).astype(np.int32)\n                left = np.round(widths_f * left).astype(np.int32)\n            else:\n                raise Exception(\"Invalid mode\")\n\n        # np.maximum(., 0) is a bit faster than arr[arr < 0] = 0 and\n        # significantly faster than clip. The masks could be computed once\n        # along each side, but it doesn't look like that would improve things\n        # very much.\n        crop_top = np.maximum((-1) * top, 0)\n        crop_right = np.maximum((-1) * right, 0)\n        crop_bottom = np.maximum((-1) * bottom, 0)\n        crop_left = np.maximum((-1) * left, 0)\n\n        crop_top, crop_bottom = _prevent_zero_sizes_after_crops_(heights, crop_top,\n                                                                 crop_bottom)\n        crop_left, crop_right = _prevent_zero_sizes_after_crops_(widths, crop_left,\n                                                                 crop_right)\n\n        pad_top = np.maximum(top, 0)\n        pad_right = np.maximum(right, 0)\n        pad_bottom = np.maximum(bottom, 0)\n        pad_left = np.maximum(left, 0)\n\n        pad_mode = self.pad_mode.draw_samples((nb_rows,),\n                                              random_state=random_state)\n        pad_cval = self.pad_cval.draw_samples((nb_rows,),\n                                              random_state=random_state)\n\n        return _CropAndPadSamplingResult(\n            crop_top=crop_top,\n            crop_right=crop_right,\n            crop_bottom=crop_bottom,\n            crop_left=crop_left,\n            pad_top=pad_top,\n            pad_right=pad_right,\n            pad_bottom=pad_bottom,\n            pad_left=pad_left,\n            pad_mode=pad_mode,\n            pad_cval=pad_cval\n        )\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.all_sides, self.top, self.right, self.bottom, self.left,\n                self.pad_mode, self.pad_cval]\n\n\nclass Pad(CropAndPad):\n    \"\"\"Pad images, i.e. adds columns/rows of pixels to them.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.CropAndPad`.\n\n    Parameters\n    ----------\n    px : None or int or imgaug.parameters.StochasticParameter or tuple, optional\n        The number of pixels to pad on each side of the image.\n        Expected value range is ``[0, inf)``.\n        Either this or the parameter `percent` may be set, not both at the same\n        time.\n\n            * If ``None``, then pixel-based padding will not be used.\n            * If ``int``, then that exact number of pixels will always be\n              padded.\n            * If ``StochasticParameter``, then that parameter will be used for\n              each image. Four samples will be drawn per image (top, right,\n              bottom, left), unless `sample_independently` is set to ``False``,\n              as then only one value will be sampled per image and used for\n              all sides.\n            * If a ``tuple`` of two ``int`` s with values ``a`` and ``b``,\n              then each side will be padded by a random amount sampled\n              uniformly per image and side from the inteval ``[a, b]``. If\n              however `sample_independently` is set to ``False``, only one\n              value will be sampled per image and used for all sides.\n            * If a ``tuple`` of four entries, then the entries represent top,\n              right, bottom, left. Each entry may be a single ``int`` (always\n              pad by exactly that value), a ``tuple`` of two ``int`` s\n              ``a`` and ``b`` (pad by an amount within ``[a, b]``), a\n              ``list`` of ``int`` s (pad by a random value that is\n              contained in the ``list``) or a ``StochasticParameter`` (sample\n              the amount to pad from that parameter).\n\n    percent : None or int or float or imgaug.parameters.StochasticParameter or tuple, optional\n        The number of pixels to pad\n        on each side of the image given as a *fraction* of the image\n        height/width. E.g. if this is set to ``0.1``, the augmenter will\n        always pad ``10%`` of the image's height at both the top and the\n        bottom (both ``10%`` each), as well as ``10%`` of the width at the\n        right and left.\n        Expected value range is ``[0.0, inf)``.\n        Either this or the parameter `px` may be set, not both\n        at the same time.\n\n            * If ``None``, then fraction-based padding will not be\n              used.\n            * If ``number``, then that fraction will always be padded.\n            * If ``StochasticParameter``, then that parameter will be used for\n              each image. Four samples will be drawn per image (top, right,\n              bottom, left). If however `sample_independently` is set to\n              ``False``, only one value will be sampled per image and used for\n              all sides.\n            * If a ``tuple`` of two ``float`` s with values ``a`` and ``b``,\n              then each side will be padded by a random fraction\n              sampled uniformly per image and side from the interval\n              ``[a, b]``. If however `sample_independently` is set to\n              ``False``, only one value will be sampled per image and used for\n              all sides.\n            * If a ``tuple`` of four entries, then the entries represent top,\n              right, bottom, left. Each entry may be a single ``float``\n              (always pad by exactly that fraction), a ``tuple`` of\n              two ``float`` s ``a`` and ``b`` (pad by a fraction from\n              ``[a, b]``), a ``list`` of ``float`` s (pad by a random\n              value that is contained in the list) or a ``StochasticParameter``\n              (sample the percentage to pad from that parameter).\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        Padding mode to use. The available modes match the numpy padding modes,\n        i.e. ``constant``, ``edge``, ``linear_ramp``, ``maximum``, ``median``,\n        ``minimum``, ``reflect``, ``symmetric``, ``wrap``. The modes\n        ``constant`` and ``linear_ramp`` use extra values, which are provided\n        by ``pad_cval`` when necessary. See :func:`~imgaug.imgaug.pad` for\n        more details.\n\n            * If ``imgaug.ALL``, then a random mode from all available modes\n              will be sampled per image.\n            * If a ``str``, it will be used as the pad mode for all images.\n            * If a ``list`` of ``str``, a random one of these will be sampled\n              per image and used as the mode.\n            * If ``StochasticParameter``, a random mode will be sampled from\n              this parameter per image.\n\n    pad_cval : number or tuple of number list of number or imgaug.parameters.StochasticParameter, optional\n        The constant value to use if the pad mode is ``constant`` or the end\n        value to use if the mode is ``linear_ramp``.\n        See :func:`~imgaug.imgaug.pad` for more details.\n\n            * If ``number``, then that value will be used.\n            * If a ``tuple`` of two ``number`` s and at least one of them is\n              a ``float``, then a random number will be uniformly sampled per\n              image from the continuous interval ``[a, b]`` and used as the\n              value. If both ``number`` s are ``int`` s, the interval is\n              discrete.\n            * If a ``list`` of ``number``, then a random value will be chosen\n              from the elements of the ``list`` and used as the value.\n            * If ``StochasticParameter``, a random value will be sampled from\n              that parameter per image.\n\n    keep_size : bool, optional\n        After padding, the result image will usually have a\n        different height/width compared to the original input image. If this\n        parameter is set to ``True``, then the padded image will be\n        resized to the input image's size, i.e. the augmenter's output shape\n        is always identical to the input shape.\n\n    sample_independently : bool, optional\n        If ``False`` *and* the values for `px`/`percent` result in exactly\n        *one* probability distribution for all image sides, only one single\n        value will be sampled from that probability distribution and used for\n        all sides. I.e. the pad amount then is the same for all sides.\n        If ``True``, four values will be sampled independently, one per side.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Pad(px=(0, 10))\n\n    Pad each side by a random pixel value sampled uniformly per image and\n    side from the discrete interval ``[0..10]``. The padding happens by\n    zero-padding, i.e. it adds black pixels (default setting).\n\n    >>> aug = iaa.Pad(px=(0, 10), pad_mode=\"edge\")\n\n    Pad each side by a random pixel value sampled uniformly per image and\n    side from the discrete interval ``[0..10]``. The padding uses the\n    ``edge`` mode from numpy's pad function, i.e. the pixel colors around\n    the image sides are repeated.\n\n    >>> aug = iaa.Pad(px=(0, 10), pad_mode=[\"constant\", \"edge\"])\n\n    Similar to the previous example, but uses zero-padding (``constant``) for\n    half of the images and ``edge`` padding for the other half.\n\n    >>> aug = iaa.Pad(px=(0, 10), pad_mode=ia.ALL, pad_cval=(0, 255))\n\n    Similar to the previous example, but uses any available padding mode.\n    In case the padding mode ends up being ``constant`` or ``linear_ramp``,\n    and random intensity is uniformly sampled (once per image) from the\n    discrete interval ``[0..255]`` and used as the intensity of the new\n    pixels.\n\n    >>> aug = iaa.Pad(px=(0, 10), sample_independently=False)\n\n    Pad each side by a random pixel value sampled uniformly once per image\n    from the discrete interval ``[0..10]``. Each sampled value is used\n    for *all* sides of the corresponding image.\n\n    >>> aug = iaa.Pad(px=(0, 10), keep_size=False)\n\n    Pad each side by a random pixel value sampled uniformly per image and\n    side from the discrete interval ``[0..10]``. Afterwards, do **not**\n    resize the padded image back to the input image's size. This will increase\n    the image's height and width by a maximum of ``20`` pixels.\n\n    >>> aug = iaa.Pad(px=((0, 10), (0, 5), (0, 10), (0, 5)))\n\n    Pad the top and bottom by a random pixel value sampled uniformly from the\n    discrete interval ``[0..10]``. Pad the left and right analogously by\n    a random value sampled from ``[0..5]``. Each value is always sampled\n    independently.\n\n    >>> aug = iaa.Pad(percent=(0, 0.1))\n\n    Pad each side by a random fraction sampled uniformly from the continuous\n    interval ``[0.0, 0.10]``. The fraction is sampled once per image and\n    side. E.g. a sampled fraction of ``0.1`` for the top side would pad by\n    ``0.1*H``, where ``H`` is the height of the input image.\n\n    >>> aug = iaa.Pad(\n    >>>     percent=([0.05, 0.1], [0.05, 0.1], [0.05, 0.1], [0.05, 0.1]))\n\n    Pads each side by either ``5%`` or ``10%``. The values are sampled\n    once per side and image.\n\n    \"\"\"\n\n    def __init__(self, px=None, percent=None, pad_mode=\"constant\", pad_cval=0,\n                 keep_size=True, sample_independently=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        def recursive_validate(value):\n            if value is None:\n                return value\n            if ia.is_single_number(value):\n                assert value >= 0, \"Expected value >0, got %.4f\" % (value,)\n                return value\n            if isinstance(value, iap.StochasticParameter):\n                return value\n            if isinstance(value, tuple):\n                return tuple([recursive_validate(v_) for v_ in value])\n            if isinstance(value, list):\n                return [recursive_validate(v_) for v_ in value]\n            raise Exception(\n                \"Expected None or int or float or StochasticParameter or \"\n                \"list or tuple, got %s.\" % (type(value),))\n\n        if px is None and percent is None:\n            percent = (0.0, 0.1)\n\n        px = recursive_validate(px)\n        percent = recursive_validate(percent)\n\n        super(Pad, self).__init__(\n            px=px,\n            percent=percent,\n            pad_mode=pad_mode,\n            pad_cval=pad_cval,\n            keep_size=keep_size,\n            sample_independently=sample_independently,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass Crop(CropAndPad):\n    \"\"\"Crop images, i.e. remove columns/rows of pixels at the sides of images.\n\n    This augmenter allows to extract smaller-sized subimages from given\n    full-sized input images. The number of pixels to cut off may be defined\n    in absolute values or as fractions of the image sizes.\n\n    This augmenter will never crop images below a height or width of ``1``.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.CropAndPad`.\n\n    Parameters\n    ----------\n    px : None or int or imgaug.parameters.StochasticParameter or tuple, optional\n        The number of pixels to crop on each side of the image.\n        Expected value range is ``[0, inf)``.\n        Either this or the parameter `percent` may be set, not both at the same\n        time.\n\n            * If ``None``, then pixel-based cropping will not be used.\n            * If ``int``, then that exact number of pixels will always be\n              cropped.\n            * If ``StochasticParameter``, then that parameter will be used for\n              each image. Four samples will be drawn per image (top, right,\n              bottom, left), unless `sample_independently` is set to ``False``,\n              as then only one value will be sampled per image and used for\n              all sides.\n            * If a ``tuple`` of two ``int`` s with values ``a`` and ``b``,\n              then each side will be cropped by a random amount sampled\n              uniformly per image and side from the inteval ``[a, b]``. If\n              however `sample_independently` is set to ``False``, only one\n              value will be sampled per image and used for all sides.\n            * If a ``tuple`` of four entries, then the entries represent top,\n              right, bottom, left. Each entry may be a single ``int`` (always\n              crop by exactly that value), a ``tuple`` of two ``int`` s\n              ``a`` and ``b`` (crop by an amount within ``[a, b]``), a\n              ``list`` of ``int`` s (crop by a random value that is\n              contained in the ``list``) or a ``StochasticParameter`` (sample\n              the amount to crop from that parameter).\n\n    percent : None or int or float or imgaug.parameters.StochasticParameter or tuple, optional\n        The number of pixels to crop\n        on each side of the image given as a *fraction* of the image\n        height/width. E.g. if this is set to ``0.1``, the augmenter will\n        always crop ``10%`` of the image's height at both the top and the\n        bottom (both ``10%`` each), as well as ``10%`` of the width at the\n        right and left.\n        Expected value range is ``[0.0, 1.0)``.\n        Either this or the parameter `px` may be set, not both\n        at the same time.\n\n            * If ``None``, then fraction-based cropping will not be\n              used.\n            * If ``number``, then that fraction will always be cropped.\n            * If ``StochasticParameter``, then that parameter will be used for\n              each image. Four samples will be drawn per image (top, right,\n              bottom, left). If however `sample_independently` is set to\n              ``False``, only one value will be sampled per image and used for\n              all sides.\n            * If a ``tuple`` of two ``float`` s with values ``a`` and ``b``,\n              then each side will be cropped by a random fraction\n              sampled uniformly per image and side from the interval\n              ``[a, b]``. If however `sample_independently` is set to\n              ``False``, only one value will be sampled per image and used for\n              all sides.\n            * If a ``tuple`` of four entries, then the entries represent top,\n              right, bottom, left. Each entry may be a single ``float``\n              (always crop by exactly that fraction), a ``tuple`` of\n              two ``float`` s ``a`` and ``b`` (crop by a fraction from\n              ``[a, b]``), a ``list`` of ``float`` s (crop by a random\n              value that is contained in the list) or a ``StochasticParameter``\n              (sample the percentage to crop from that parameter).\n\n    keep_size : bool, optional\n        After cropping, the result image will usually have a\n        different height/width compared to the original input image. If this\n        parameter is set to ``True``, then the cropped image will be\n        resized to the input image's size, i.e. the augmenter's output shape\n        is always identical to the input shape.\n\n    sample_independently : bool, optional\n        If ``False`` *and* the values for `px`/`percent` result in exactly\n        *one* probability distribution for all image sides, only one single\n        value will be sampled from that probability distribution and used for\n        all sides. I.e. the crop amount then is the same for all sides.\n        If ``True``, four values will be sampled independently, one per side.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Crop(px=(0, 10))\n\n    Crop each side by a random pixel value sampled uniformly per image and\n    side from the discrete interval ``[0..10]``.\n\n    >>> aug = iaa.Crop(px=(0, 10), sample_independently=False)\n\n    Crop each side by a random pixel value sampled uniformly once per image\n    from the discrete interval ``[0..10]``. Each sampled value is used\n    for *all* sides of the corresponding image.\n\n    >>> aug = iaa.Crop(px=(0, 10), keep_size=False)\n\n    Crop each side by a random pixel value sampled uniformly per image and\n    side from the discrete interval ``[0..10]``. Afterwards, do **not**\n    resize the cropped image back to the input image's size. This will decrease\n    the image's height and width by a maximum of ``20`` pixels.\n\n    >>> aug = iaa.Crop(px=((0, 10), (0, 5), (0, 10), (0, 5)))\n\n    Crop the top and bottom by a random pixel value sampled uniformly from the\n    discrete interval ``[0..10]``. Crop the left and right analogously by\n    a random value sampled from ``[0..5]``. Each value is always sampled\n    independently.\n\n    >>> aug = iaa.Crop(percent=(0, 0.1))\n\n    Crop each side by a random fraction sampled uniformly from the continuous\n    interval ``[0.0, 0.10]``. The fraction is sampled once per image and\n    side. E.g. a sampled fraction of ``0.1`` for the top side would crop by\n    ``0.1*H``, where ``H`` is the height of the input image.\n\n    >>> aug = iaa.Crop(\n    >>>     percent=([0.05, 0.1], [0.05, 0.1], [0.05, 0.1], [0.05, 0.1]))\n\n    Crops each side by either ``5%`` or ``10%``. The values are sampled\n    once per side and image.\n\n    \"\"\"\n\n    def __init__(self, px=None, percent=None, keep_size=True,\n                 sample_independently=True,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        def recursive_negate(value):\n            if value is None:\n                return value\n            if ia.is_single_number(value):\n                assert value >= 0, \"Expected value >0, got %.4f.\" % (value,)\n                return -value\n            if isinstance(value, iap.StochasticParameter):\n                return iap.Multiply(value, -1)\n            if isinstance(value, tuple):\n                return tuple([recursive_negate(v_) for v_ in value])\n            if isinstance(value, list):\n                return [recursive_negate(v_) for v_ in value]\n            raise Exception(\n                \"Expected None or int or float or StochasticParameter or \"\n                \"list or tuple, got %s.\" % (type(value),))\n\n        if px is None and percent is None:\n            percent = (0.0, 0.1)\n\n        px = recursive_negate(px)\n        percent = recursive_negate(percent)\n\n        super(Crop, self).__init__(\n            px=px,\n            percent=percent,\n            keep_size=keep_size,\n            sample_independently=sample_independently,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO maybe rename this to PadToMinimumSize?\n# TODO this is very similar to CropAndPad, maybe add a way to generate crop\n#      values imagewise via a callback in in CropAndPad?\n# TODO why is padding mode and cval here called pad_mode, pad_cval but in other\n#      cases mode/cval?\nclass PadToFixedSize(meta.Augmenter):\n    \"\"\"Pad images to a predefined minimum width and/or height.\n\n    If images are already at the minimum width/height or are larger, they will\n    not be padded. Note that this also means that images will not be cropped if\n    they exceed the required width/height.\n\n    The augmenter randomly decides per image how to distribute the required\n    padding amounts over the image axis. E.g. if 2px have to be padded on the\n    left or right to reach the required width, the augmenter will sometimes\n    add 2px to the left and 0px to the right, sometimes add 2px to the right\n    and 0px to the left and sometimes add 1px to both sides. Set `position`\n    to ``center`` to prevent that.\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.augmenters.size.pad`.\n\n    Parameters\n    ----------\n    width : int or None\n        Pad images up to this minimum width.\n        If ``None``, image widths will not be altered.\n\n    height : int or None\n        Pad images up to this minimum height.\n        If ``None``, image heights will not be altered.\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.CropAndPad.__init__`.\n\n    pad_cval : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.CropAndPad.__init__`.\n\n    position : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n        Sets the center point of the padding, which determines how the\n        required padding amounts are distributed to each side. For a ``tuple``\n        ``(a, b)``, both ``a`` and ``b`` are expected to be in range\n        ``[0.0, 1.0]`` and describe the fraction of padding applied to the\n        left/right (low/high values for ``a``) and the fraction of padding\n        applied to the top/bottom (low/high values for ``b``). A padding\n        position at ``(0.5, 0.5)`` would be the center of the image and\n        distribute the padding equally to all sides. A padding position at\n        ``(0.0, 1.0)`` would be the left-bottom and would apply 100% of the\n        required padding to the bottom and left sides of the image so that\n        the bottom left corner becomes more and more the new image\n        center (depending on how much is padded).\n\n            * If string ``uniform`` then the share of padding is randomly and\n              uniformly distributed over each side.\n              Equivalent to ``(Uniform(0.0, 1.0), Uniform(0.0, 1.0))``.\n            * If string ``normal`` then the share of padding is distributed\n              based on a normal distribution, leading to a focus on the\n              center of the images.\n              Equivalent to\n              ``(Clip(Normal(0.5, 0.45/2), 0, 1),\n              Clip(Normal(0.5, 0.45/2), 0, 1))``.\n            * If string ``center`` then center point of the padding is\n              identical to the image center.\n              Equivalent to ``(0.5, 0.5)``.\n            * If a string matching regex\n              ``^(left|center|right)-(top|center|bottom)$``, e.g. ``left-top``\n              or ``center-bottom`` then sets the center point of the padding\n              to the X-Y position matching that description.\n            * If a tuple of float, then expected to have exactly two entries\n              between ``0.0`` and ``1.0``, which will always be used as the\n              combination the position matching (x, y) form.\n            * If a ``StochasticParameter``, then that parameter will be queried\n              once per call to ``augment_*()`` to get ``Nx2`` center positions\n              in ``(x, y)`` form (with ``N`` the number of images).\n            * If a ``tuple`` of ``StochasticParameter``, then expected to have\n              exactly two entries that will both be queried per call to\n              ``augment_*()``, each for ``(N,)`` values, to get the center\n              positions. First parameter is used for ``x`` coordinates,\n              second for ``y`` coordinates.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.PadToFixedSize(width=100, height=100)\n\n    For image sides smaller than ``100`` pixels, pad to ``100`` pixels. Do\n    nothing for the other edges. The padding is randomly (uniformly)\n    distributed over the sides, so that e.g. sometimes most of the required\n    padding is applied to the left, sometimes to the right (analogous\n    top/bottom).\n\n    >>> aug = iaa.PadToFixedSize(width=100, height=100, position=\"center\")\n\n    For image sides smaller than ``100`` pixels, pad to ``100`` pixels. Do\n    nothing for the other image sides. The padding is always equally\n    distributed over the left/right and top/bottom sides.\n\n    >>> aug = iaa.PadToFixedSize(width=100, height=100, pad_mode=ia.ALL)\n\n    For image sides smaller than ``100`` pixels, pad to ``100`` pixels and\n    use any possible padding mode for that. Do nothing for the other image\n    sides. The padding is always equally distributed over the left/right and\n    top/bottom sides.\n\n    >>> aug = iaa.Sequential([\n    >>>     iaa.PadToFixedSize(width=100, height=100),\n    >>>     iaa.CropToFixedSize(width=100, height=100)\n    >>> ])\n\n    Pad images smaller than ``100x100`` until they reach ``100x100``.\n    Analogously, crop images larger than ``100x100`` until they reach\n    ``100x100``. The output images therefore have a fixed size of ``100x100``.\n\n    \"\"\"\n\n    def __init__(self, width, height, pad_mode=\"constant\", pad_cval=0,\n                 position=\"uniform\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(PadToFixedSize, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.size = (width, height)\n\n        # Position of where to pad. The further to the top left this is, the\n        # larger the share of pixels that will be added to the top and left\n        # sides. I.e. set to (Deterministic(0.0), Deterministic(0.0)) to only\n        # add at the top and left, (Deterministic(1.0), Deterministic(1.0))\n        # to only add at the bottom right. Analogously (0.5, 0.5) pads equally\n        # on both axis, (0.0, 1.0) pads left and bottom, (1.0, 0.0) pads right\n        # and top.\n        self.position = _handle_position_parameter(position)\n\n        self.pad_mode = _handle_pad_mode_param(pad_mode)\n        # TODO enable ALL here like in eg Affine\n        self.pad_cval = iap.handle_discrete_param(\n            pad_cval, \"pad_cval\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=True)\n\n        # set these to None to use the same values as sampled for the\n        # images (not tested)\n        self._pad_mode_heatmaps = \"constant\"\n        self._pad_mode_segmentation_maps = \"constant\"\n        self._pad_cval_heatmaps = 0.0\n        self._pad_cval_segmentation_maps = 0\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        # Providing the whole batch to _draw_samples() would not be necessary\n        # for this augmenter. The number of rows would be sufficient. This\n        # formulation however enables derived augmenters to use rowwise shapes\n        # without having to compute them here for this augmenter.\n        samples = self._draw_samples(batch, random_state)\n\n        if batch.images is not None:\n            batch.images = self._augment_images_by_samples(batch.images,\n                                                           samples)\n\n        if batch.heatmaps is not None:\n            batch.heatmaps = self._augment_maps_by_samples(\n                batch.heatmaps, samples, self._pad_mode_heatmaps,\n                self._pad_cval_heatmaps)\n\n        if batch.segmentation_maps is not None:\n            batch.segmentation_maps = self._augment_maps_by_samples(\n                batch.segmentation_maps, samples, self._pad_mode_heatmaps,\n                self._pad_cval_heatmaps)\n\n        for augm_name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                          \"line_strings\"]:\n            augm_value = getattr(batch, augm_name)\n            if augm_value is not None:\n                func = functools.partial(\n                    self._augment_keypoints_by_samples,\n                    samples=samples)\n                cbaois = self._apply_to_cbaois_as_keypoints(augm_value, func)\n                setattr(batch, augm_name, cbaois)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _augment_images_by_samples(self, images, samples):\n        result = []\n        sizes, pad_xs, pad_ys, pad_modes, pad_cvals = samples\n        for i, (image, size) in enumerate(zip(images, sizes)):\n            width_min, height_min = size\n            height_image, width_image = image.shape[:2]\n            paddings = self._calculate_paddings(height_image, width_image,\n                                                height_min, width_min,\n                                                pad_xs[i], pad_ys[i])\n\n            image = _crop_and_pad_arr(\n                image, (0, 0, 0, 0), paddings, pad_modes[i], pad_cvals[i],\n                keep_size=False)\n\n            result.append(image)\n\n        # TODO result is always a list. Should this be converted to an array\n        #      if possible (not guaranteed that all images have same size,\n        #      some might have been larger than desired height/width)\n        return result\n\n    # Added in 0.4.0.\n    def _augment_keypoints_by_samples(self, keypoints_on_images, samples):\n        result = []\n        sizes, pad_xs, pad_ys, _, _ = samples\n        for i, (kpsoi, size) in enumerate(zip(keypoints_on_images, sizes)):\n            width_min, height_min = size\n            height_image, width_image = kpsoi.shape[:2]\n            paddings_img = self._calculate_paddings(height_image, width_image,\n                                                    height_min, width_min,\n                                                    pad_xs[i], pad_ys[i])\n\n            keypoints_padded = _crop_and_pad_kpsoi_(\n                kpsoi, (0, 0, 0, 0), paddings_img,\n                keep_size=False)\n\n            result.append(keypoints_padded)\n\n        return result\n\n    # Added in 0.4.0.\n    def _augment_maps_by_samples(self, augmentables, samples, pad_mode,\n                                 pad_cval):\n        sizes, pad_xs, pad_ys, pad_modes, pad_cvals = samples\n\n        for i, (augmentable, size) in enumerate(zip(augmentables, sizes)):\n            width_min, height_min = size\n            height_img, width_img = augmentable.shape[:2]\n            paddings_img = self._calculate_paddings(\n                height_img, width_img, height_min, width_min,\n                pad_xs[i], pad_ys[i])\n\n            # TODO for the previous method (and likely the new/current one\n            #      too):\n            #      for 30x30 padded to 32x32 with 15x15 heatmaps this results\n            #      in paddings of 1 on each side (assuming\n            #      position=(0.5, 0.5)) giving 17x17 heatmaps when they should\n            #      be 16x16. Error is due to each side getting projected 0.5\n            #      padding which is rounded to 1. This doesn't seem right.\n            augmentables[i] = _crop_and_pad_hms_or_segmaps_(\n                augmentables[i],\n                (0, 0, 0, 0),\n                paddings_img,\n                pad_mode=pad_mode if pad_mode is not None else pad_modes[i],\n                pad_cval=pad_cval if pad_cval is not None else pad_cvals[i],\n                keep_size=False)\n\n        return augmentables\n\n    def _draw_samples(self, batch, random_state):\n        nb_images = batch.nb_rows\n        rngs = random_state.duplicate(4)\n\n        if isinstance(self.position, tuple):\n            pad_xs = self.position[0].draw_samples(nb_images,\n                                                   random_state=rngs[0])\n            pad_ys = self.position[1].draw_samples(nb_images,\n                                                   random_state=rngs[1])\n        else:\n            pads = self.position.draw_samples((nb_images, 2),\n                                              random_state=rngs[0])\n            pad_xs = pads[:, 0]\n            pad_ys = pads[:, 1]\n\n        pad_modes = self.pad_mode.draw_samples(nb_images,\n                                               random_state=rngs[2])\n        pad_cvals = self.pad_cval.draw_samples(nb_images,\n                                               random_state=rngs[3])\n\n        # We return here the sizes even though they are static as it allows\n        # derived augmenters to define image-specific heights/widths.\n        return [self.size] * nb_images, pad_xs, pad_ys, pad_modes, pad_cvals\n\n    @classmethod\n    def _calculate_paddings(cls, height_image, width_image,\n                            height_min, width_min, pad_xs_i, pad_ys_i):\n        pad_top = 0\n        pad_right = 0\n        pad_bottom = 0\n        pad_left = 0\n\n        if width_min is not None and width_image < width_min:\n            pad_total_x = width_min - width_image\n            pad_left = int((1-pad_xs_i) * pad_total_x)\n            pad_right = pad_total_x - pad_left\n\n        if height_min is not None and height_image < height_min:\n            pad_total_y = height_min - height_image\n            pad_top = int((1-pad_ys_i) * pad_total_y)\n            pad_bottom = pad_total_y - pad_top\n\n        return pad_top, pad_right, pad_bottom, pad_left\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.size[0], self.size[1], self.pad_mode, self.pad_cval,\n                self.position]\n\n\nclass CenterPadToFixedSize(PadToFixedSize):\n    \"\"\"Pad images equally on all sides up to given minimum heights/widths.\n\n    This is an alias for :class:`~imgaug.augmenters.size.PadToFixedSize`\n    with ``position=\"center\"``. It spreads the pad amounts equally over\n    all image sides, while :class:`~imgaug.augmenters.size.PadToFixedSize`\n    by defaults spreads them randomly.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.PadToFixedSize`.\n\n    Parameters\n    ----------\n    width : int or None\n        See :func:`PadToFixedSize.__init__`.\n\n    height : int or None\n        See :func:`PadToFixedSize.__init__`.\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :func:`PadToFixedSize.__init__`.\n\n    pad_cval : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :func:`PadToFixedSize.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CenterPadToFixedSize(height=20, width=30)\n\n    Create an augmenter that pads images up to ``20x30``, with the padded\n    rows added *equally* on the top and bottom (analogous for the padded\n    columns).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, width, height, pad_mode=\"constant\", pad_cval=0,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CenterPadToFixedSize, self).__init__(\n            width=width, height=height, pad_mode=pad_mode, pad_cval=pad_cval,\n            position=\"center\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO maybe rename this to CropToMaximumSize ?\n# TODO this is very similar to CropAndPad, maybe add a way to generate crop\n#      values imagewise via a callback in in CropAndPad?\n# TODO add crop() function in imgaug, similar to pad\nclass CropToFixedSize(meta.Augmenter):\n    \"\"\"Crop images down to a predefined maximum width and/or height.\n\n    If images are already at the maximum width/height or are smaller, they\n    will not be cropped. Note that this also means that images will not be\n    padded if they are below the required width/height.\n\n    The augmenter randomly decides per image how to distribute the required\n    cropping amounts over the image axis. E.g. if 2px have to be cropped on\n    the left or right to reach the required width, the augmenter will\n    sometimes remove 2px from the left and 0px from the right, sometimes\n    remove 2px from the right and 0px from the left and sometimes remove 1px\n    from both sides. Set `position` to ``center`` to prevent that.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested\n        * ``uint64``: yes; tested\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested\n        * ``int64``: yes; tested\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested\n        * ``bool``: yes; tested\n\n    Parameters\n    ----------\n    width : int or None\n        Crop images down to this maximum width.\n        If ``None``, image widths will not be altered.\n\n    height : int or None\n        Crop images down to this maximum height.\n        If ``None``, image heights will not be altered.\n\n    position : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n         Sets the center point of the cropping, which determines how the\n         required cropping amounts are distributed to each side. For a\n         ``tuple`` ``(a, b)``, both ``a`` and ``b`` are expected to be in\n         range ``[0.0, 1.0]`` and describe the fraction of cropping applied\n         to the left/right (low/high values for ``a``) and the fraction\n         of cropping applied to the top/bottom (low/high values for ``b``).\n         A cropping position at ``(0.5, 0.5)`` would be the center of the\n         image and distribute the cropping equally over all sides. A cropping\n         position at ``(1.0, 0.0)`` would be the right-top and would apply\n         100% of the required cropping to the right and top sides of the image.\n\n            * If string ``uniform`` then the share of cropping is randomly\n              and uniformly distributed over each side.\n              Equivalent to ``(Uniform(0.0, 1.0), Uniform(0.0, 1.0))``.\n            * If string ``normal`` then the share of cropping is distributed\n              based on a normal distribution, leading to a focus on the center\n              of the images.\n              Equivalent to\n              ``(Clip(Normal(0.5, 0.45/2), 0, 1),\n              Clip(Normal(0.5, 0.45/2), 0, 1))``.\n            * If string ``center`` then center point of the cropping is\n              identical to the image center.\n              Equivalent to ``(0.5, 0.5)``.\n            * If a string matching regex\n              ``^(left|center|right)-(top|center|bottom)$``, e.g.\n              ``left-top`` or ``center-bottom`` then sets the center point of\n              the cropping to the X-Y position matching that description.\n            * If a tuple of float, then expected to have exactly two entries\n              between ``0.0`` and ``1.0``, which will always be used as the\n              combination the position matching (x, y) form.\n            * If a ``StochasticParameter``, then that parameter will be queried\n              once per call to ``augment_*()`` to get ``Nx2`` center positions\n              in ``(x, y)`` form (with ``N`` the number of images).\n            * If a ``tuple`` of ``StochasticParameter``, then expected to have\n              exactly two entries that will both be queried per call to\n              ``augment_*()``, each for ``(N,)`` values, to get the center\n              positions. First parameter is used for ``x`` coordinates,\n              second for ``y`` coordinates.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CropToFixedSize(width=100, height=100)\n\n    For image sides larger than ``100`` pixels, crop to ``100`` pixels. Do\n    nothing for the other sides. The cropping amounts are randomly (and\n    uniformly) distributed over the sides of the image.\n\n    >>> aug = iaa.CropToFixedSize(width=100, height=100, position=\"center\")\n\n    For sides larger than ``100`` pixels, crop to ``100`` pixels. Do nothing\n    for the other sides. The cropping amounts are always equally distributed\n    over the left/right sides of the image (and analogously for top/bottom).\n\n    >>> aug = iaa.Sequential([\n    >>>     iaa.PadToFixedSize(width=100, height=100),\n    >>>     iaa.CropToFixedSize(width=100, height=100)\n    >>> ])\n\n    Pad images smaller than ``100x100`` until they reach ``100x100``.\n    Analogously, crop images larger than ``100x100`` until they reach\n    ``100x100``. The output images therefore have a fixed size of ``100x100``.\n\n    \"\"\"\n\n    def __init__(self, width, height, position=\"uniform\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CropToFixedSize, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.size = (width, height)\n\n        # Position of where to crop. The further to the top left this is,\n        # the larger the share of pixels that will be cropped from the top\n        # and left sides. I.e. set to (Deterministic(0.0), Deterministic(0.0))\n        # to only crop at the top and left,\n        # (Deterministic(1.0), Deterministic(1.0)) to only crop at the bottom\n        # right. Analogously (0.5, 0.5) crops equally on both axis,\n        # (0.0, 1.0) crops left and bottom, (1.0, 0.0) crops right and top.\n        self.position = _handle_position_parameter(position)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        # Providing the whole batch to _draw_samples() would not be necessary\n        # for this augmenter. The number of rows would be sufficient. This\n        # formulation however enables derived augmenters to use rowwise shapes\n        # without having to compute them here for this augmenter.\n        samples = self._draw_samples(batch, random_state)\n\n        if batch.images is not None:\n            batch.images = self._augment_images_by_samples(batch.images,\n                                                           samples)\n\n        if batch.heatmaps is not None:\n            batch.heatmaps = self._augment_maps_by_samples(\n                batch.heatmaps, samples)\n\n        if batch.segmentation_maps is not None:\n            batch.segmentation_maps = self._augment_maps_by_samples(\n                batch.segmentation_maps, samples)\n\n        for augm_name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                          \"line_strings\"]:\n            augm_value = getattr(batch, augm_name)\n            if augm_value is not None:\n                func = functools.partial(\n                    self._augment_keypoints_by_samples,\n                    samples=samples)\n                cbaois = self._apply_to_cbaois_as_keypoints(augm_value, func)\n                setattr(batch, augm_name, cbaois)\n\n        return batch\n\n    # Added in 0.4.0.\n    def _augment_images_by_samples(self, images, samples):\n        result = []\n        sizes, offset_xs, offset_ys = samples\n        for i, (image, size) in enumerate(zip(images, sizes)):\n            w, h = size\n            height_image, width_image = image.shape[0:2]\n\n            croppings = self._calculate_crop_amounts(\n                height_image, width_image, h, w, offset_ys[i], offset_xs[i])\n\n            image_cropped = _crop_and_pad_arr(image, croppings, (0, 0, 0, 0),\n                                              keep_size=False)\n\n            result.append(image_cropped)\n\n        return result\n\n    # Added in 0.4.0.\n    def _augment_keypoints_by_samples(self, kpsois, samples):\n        result = []\n        sizes, offset_xs, offset_ys = samples\n        for i, (kpsoi, size) in enumerate(zip(kpsois, sizes)):\n            w, h = size\n            height_image, width_image = kpsoi.shape[0:2]\n\n            croppings_img = self._calculate_crop_amounts(\n                height_image, width_image, h, w, offset_ys[i], offset_xs[i])\n\n            kpsoi_cropped = _crop_and_pad_kpsoi_(\n                kpsoi, croppings_img, (0, 0, 0, 0), keep_size=False)\n\n            result.append(kpsoi_cropped)\n\n        return result\n\n    # Added in 0.4.0.\n    def _augment_maps_by_samples(self, augmentables, samples):\n        sizes, offset_xs, offset_ys = samples\n        for i, (augmentable, size) in enumerate(zip(augmentables, sizes)):\n            w, h = size\n            height_image, width_image = augmentable.shape[0:2]\n\n            croppings_img = self._calculate_crop_amounts(\n                height_image, width_image, h, w, offset_ys[i], offset_xs[i])\n\n            augmentables[i] = _crop_and_pad_hms_or_segmaps_(\n                augmentable, croppings_img, (0, 0, 0, 0), keep_size=False)\n\n        return augmentables\n\n    @classmethod\n    def _calculate_crop_amounts(cls, height_image, width_image,\n                                height_max, width_max,\n                                offset_y, offset_x):\n        crop_top = 0\n        crop_right = 0\n        crop_bottom = 0\n        crop_left = 0\n\n        if height_max is not None and height_image > height_max:\n            crop_top = int(offset_y * (height_image - height_max))\n            crop_bottom = height_image - height_max - crop_top\n\n        if width_max is not None and width_image > width_max:\n            crop_left = int(offset_x * (width_image - width_max))\n            crop_right = width_image - width_max - crop_left\n\n        return crop_top, crop_right, crop_bottom, crop_left\n\n    def _draw_samples(self, batch, random_state):\n        nb_images = batch.nb_rows\n        rngs = random_state.duplicate(2)\n\n        if isinstance(self.position, tuple):\n            offset_xs = self.position[0].draw_samples(nb_images,\n                                                      random_state=rngs[0])\n            offset_ys = self.position[1].draw_samples(nb_images,\n                                                      random_state=rngs[1])\n        else:\n            offsets = self.position.draw_samples((nb_images, 2),\n                                                 random_state=rngs[0])\n            offset_xs = offsets[:, 0]\n            offset_ys = offsets[:, 1]\n\n        offset_xs = 1.0 - offset_xs\n        offset_ys = 1.0 - offset_ys\n\n        # We return here the sizes even though they are static as it allows\n        # derived augmenters to define image-specific heights/widths.\n        return [self.size] * nb_images, offset_xs, offset_ys\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.size[0], self.size[1], self.position]\n\n\nclass CenterCropToFixedSize(CropToFixedSize):\n    \"\"\"Take a crop from the center of each image.\n\n    This is an alias for :class:`~imgaug.augmenters.size.CropToFixedSize` with\n    ``position=\"center\"``.\n\n    .. note::\n\n        If images already have a width and/or height below the provided\n        width and/or height then this augmenter will do nothing for the\n        respective axis. Hence, resulting images can be smaller than the\n        provided axis sizes.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.CropToFixedSize`.\n\n    Parameters\n    ----------\n    width : int or None\n        See :func:`CropToFixedSize.__init__`.\n\n    height : int or None\n        See :func:`CropToFixedSize.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> crop = iaa.CenterCropToFixedSize(height=20, width=10)\n\n    Create an augmenter that takes ``20x10`` sized crops from the center of\n    images.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, width, height,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CenterCropToFixedSize, self).__init__(\n            width=width, height=height, position=\"center\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass CropToMultiplesOf(CropToFixedSize):\n    \"\"\"Crop images down until their height/width is a multiple of a value.\n\n    .. note::\n\n        For a given axis size ``A`` and multiple ``M``, if ``A`` is in the\n        interval ``[0 .. M]``, the axis will not be changed.\n        As a result, this augmenter can still produce axis sizes that are\n        not multiples of the given values.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.CropToFixedSize`.\n\n    Parameters\n    ----------\n    width_multiple : int or None\n        Multiple for the width. Images will be cropped down until their\n        width is a multiple of this value.\n        If ``None``, image widths will not be altered.\n\n    height_multiple : int or None\n        Multiple for the height. Images will be cropped down until their\n        height is a multiple of this value.\n        If ``None``, image heights will not be altered.\n\n    position : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n        See :func:`CropToFixedSize.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CropToMultiplesOf(height_multiple=10, width_multiple=6)\n\n    Create an augmenter that crops images to multiples of ``10`` along\n    the y-axis (i.e. 10, 20, 30, ...) and to multiples of ``6`` along the\n    x-axis (i.e. 6, 12, 18, ...).\n    The rows to be cropped will be spread *randomly* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, width_multiple, height_multiple, position=\"uniform\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CropToMultiplesOf, self).__init__(\n            width=None, height=None, position=position,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.width_multiple = width_multiple\n        self.height_multiple = height_multiple\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        _sizes, offset_xs, offset_ys = super(\n            CropToMultiplesOf, self\n        )._draw_samples(batch, random_state)\n\n        shapes = batch.get_rowwise_shapes()\n        sizes = []\n        for shape in shapes:\n            height, width = shape[0:2]\n            croppings = compute_croppings_to_reach_multiples_of(\n                shape,\n                height_multiple=self.height_multiple,\n                width_multiple=self.width_multiple)\n\n            # TODO change that\n            # note that these are not in the same order as shape tuples\n            # in CropToFixedSize\n            new_size = (\n                width - croppings[1] - croppings[3],\n                height - croppings[0] - croppings[2]\n            )\n            sizes.append(new_size)\n\n        return sizes, offset_xs, offset_ys\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.width_multiple, self.height_multiple, self.position]\n\n\nclass CenterCropToMultiplesOf(CropToMultiplesOf):\n    \"\"\"Crop images equally on all sides until H/W are multiples of given values.\n\n    This is the same as :class:`~imgaug.augmenters.size.CropToMultiplesOf`,\n    but uses ``position=\"center\"`` by default, which spreads the crop amounts\n    equally over all image sides, while\n    :class:`~imgaug.augmenters.size.CropToMultiplesOf` by default spreads\n    them randomly.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.CropToFixedSize`.\n\n    Parameters\n    ----------\n    width_multiple : int or None\n        See :func:`CropToMultiplesOf.__init__`.\n\n    height_multiple : int or None\n        See :func:`CropToMultiplesOf.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CenterCropToMultiplesOf(height_multiple=10, width_multiple=6)\n\n    Create an augmenter that crops images to multiples of ``10`` along\n    the y-axis (i.e. 10, 20, 30, ...) and to multiples of ``6`` along the\n    x-axis (i.e. 6, 12, 18, ...).\n    The rows to be cropped will be spread *equally* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, width_multiple, height_multiple,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CenterCropToMultiplesOf, self).__init__(\n            width_multiple=width_multiple,\n            height_multiple=height_multiple,\n            position=\"center\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass PadToMultiplesOf(PadToFixedSize):\n    \"\"\"Pad images until their height/width is a multiple of a value.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.PadToFixedSize`.\n\n    Parameters\n    ----------\n    width_multiple : int or None\n        Multiple for the width. Images will be padded until their\n        width is a multiple of this value.\n        If ``None``, image widths will not be altered.\n\n    height_multiple : int or None\n        Multiple for the height. Images will be padded until their\n        height is a multiple of this value.\n        If ``None``, image heights will not be altered.\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToFixedSize.__init__`.\n\n    pad_cval : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToFixedSize.__init__`.\n\n    position : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n        See :func:`PadToFixedSize.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.PadToMultiplesOf(height_multiple=10, width_multiple=6)\n\n    Create an augmenter that pads images to multiples of ``10`` along\n    the y-axis (i.e. 10, 20, 30, ...) and to multiples of ``6`` along the\n    x-axis (i.e. 6, 12, 18, ...).\n    The rows to be padded will be spread *randomly* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, width_multiple, height_multiple,\n                 pad_mode=\"constant\", pad_cval=0,\n                 position=\"uniform\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(PadToMultiplesOf, self).__init__(\n            width=None, height=None, pad_mode=pad_mode, pad_cval=pad_cval,\n            position=position,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.width_multiple = width_multiple\n        self.height_multiple = height_multiple\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        _sizes, pad_xs, pad_ys, pad_modes, pad_cvals = super(\n            PadToMultiplesOf, self\n        )._draw_samples(batch, random_state)\n\n        shapes = batch.get_rowwise_shapes()\n        sizes = []\n        for shape in shapes:\n            height, width = shape[0:2]\n            paddings = compute_paddings_to_reach_multiples_of(\n                shape,\n                height_multiple=self.height_multiple,\n                width_multiple=self.width_multiple)\n\n            # TODO change that\n            # note that these are not in the same order as shape tuples\n            # in PadToFixedSize\n            new_size = (\n                width + paddings[1] + paddings[3],\n                height + paddings[0] + paddings[2]\n            )\n            sizes.append(new_size)\n\n        return sizes, pad_xs, pad_ys, pad_modes, pad_cvals\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.width_multiple, self.height_multiple,\n                self.pad_mode, self.pad_cval,\n                self.position]\n\n\nclass CenterPadToMultiplesOf(PadToMultiplesOf):\n    \"\"\"Pad images equally on all sides until H/W are multiples of given values.\n\n    This is the same as :class:`~imgaug.augmenters.size.PadToMultiplesOf`, but\n    uses ``position=\"center\"`` by default, which spreads the pad amounts\n    equally over all image sides, while\n    :class:`~imgaug.augmenters.size.PadToMultiplesOf` by default spreads them\n    randomly.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.PadToFixedSize`.\n\n    Parameters\n    ----------\n    width_multiple : int or None\n        See :func:`PadToMultiplesOf.__init__`.\n\n    height_multiple : int or None\n        See :func:`PadToMultiplesOf.__init__`.\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToMultiplesOf.__init__`.\n\n    pad_cval : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToMultiplesOf.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CenterPadToMultiplesOf(height_multiple=10, width_multiple=6)\n\n    Create an augmenter that pads images to multiples of ``10`` along\n    the y-axis (i.e. 10, 20, 30, ...) and to multiples of ``6`` along the\n    x-axis (i.e. 6, 12, 18, ...).\n    The rows to be padded will be spread *equally* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, width_multiple, height_multiple,\n                 pad_mode=\"constant\", pad_cval=0,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CenterPadToMultiplesOf, self).__init__(\n            width_multiple=width_multiple,\n            height_multiple=height_multiple,\n            pad_mode=pad_mode,\n            pad_cval=pad_cval,\n            position=\"center\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass CropToPowersOf(CropToFixedSize):\n    \"\"\"Crop images until their height/width is a power of a base.\n\n    This augmenter removes pixels from an axis with size ``S`` leading to the\n    new size ``S'`` until ``S' = B^E`` is fulfilled, where ``B`` is a\n    provided base (e.g. ``2``) and ``E`` is an exponent from the discrete\n    interval ``[1 .. inf)``.\n\n    .. note::\n\n        This augmenter does nothing for axes with size less than ``B^1 = B``.\n        If you have images with ``S < B^1``, it is recommended\n        to combine this augmenter with a padding augmenter that pads each\n        axis up to ``B``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.CropToFixedSize`.\n\n    Parameters\n    ----------\n    width_base : int or None\n        Base for the width. Images will be cropped down until their\n        width fulfills ``width' = width_base ^ E`` with ``E`` being any\n        natural number.\n        If ``None``, image widths will not be altered.\n\n    height_base : int or None\n        Base for the height. Images will be cropped down until their\n        height fulfills ``height' = height_base ^ E`` with ``E`` being any\n        natural number.\n        If ``None``, image heights will not be altered.\n\n    position : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n        See :func:`CropToFixedSize.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CropToPowersOf(height_base=3, width_base=2)\n\n    Create an augmenter that crops each image down to powers of ``3`` along\n    the y-axis (i.e. 3, 9, 27, ...) and powers of ``2`` along the x-axis (i.e.\n    2, 4, 8, 16, ...).\n    The rows to be cropped will be spread *randomly* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, width_base, height_base, position=\"uniform\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CropToPowersOf, self).__init__(\n            width=None, height=None, position=position,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.width_base = width_base\n        self.height_base = height_base\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        _sizes, offset_xs, offset_ys = super(\n            CropToPowersOf, self\n        )._draw_samples(batch, random_state)\n\n        shapes = batch.get_rowwise_shapes()\n        sizes = []\n        for shape in shapes:\n            height, width = shape[0:2]\n            croppings = compute_croppings_to_reach_powers_of(\n                shape,\n                height_base=self.height_base,\n                width_base=self.width_base)\n\n            # TODO change that\n            # note that these are not in the same order as shape tuples\n            # in CropToFixedSize\n            new_size = (\n                width - croppings[1] - croppings[3],\n                height - croppings[0] - croppings[2]\n            )\n            sizes.append(new_size)\n\n        return sizes, offset_xs, offset_ys\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.width_base, self.height_base, self.position]\n\n\nclass CenterCropToPowersOf(CropToPowersOf):\n    \"\"\"Crop images equally on all sides until H/W is a power of a base.\n\n    This is the same as :class:`~imgaug.augmenters.size.CropToPowersOf`, but\n    uses ``position=\"center\"`` by default, which spreads the crop amounts\n    equally over all image sides, while\n    :class:`~imgaug.augmenters.size.CropToPowersOf` by default spreads them\n    randomly.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.CropToFixedSize`.\n\n    Parameters\n    ----------\n    width_base : int or None\n        See :func:`CropToPowersOf.__init__`.\n\n    height_base : int or None\n        See :func:`CropToPowersOf.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CropToPowersOf(height_base=3, width_base=2)\n\n    Create an augmenter that crops each image down to powers of ``3`` along\n    the y-axis (i.e. 3, 9, 27, ...) and powers of ``2`` along the x-axis (i.e.\n    2, 4, 8, 16, ...).\n    The rows to be cropped will be spread *equally* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, width_base, height_base,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CenterCropToPowersOf, self).__init__(\n            width_base=width_base, height_base=height_base, position=\"center\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass PadToPowersOf(PadToFixedSize):\n    \"\"\"Pad images until their height/width is a power of a base.\n\n    This augmenter adds pixels to an axis with size ``S`` leading to the\n    new size ``S'`` until ``S' = B^E`` is fulfilled, where ``B`` is a\n    provided base (e.g. ``2``) and ``E`` is an exponent from the discrete\n    interval ``[1 .. inf)``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.PadToFixedSize`.\n\n    Parameters\n    ----------\n    width_base : int or None\n        Base for the width. Images will be padded down until their\n        width fulfills ``width' = width_base ^ E`` with ``E`` being any\n        natural number.\n        If ``None``, image widths will not be altered.\n\n    height_base : int or None\n        Base for the height. Images will be padded until their\n        height fulfills ``height' = height_base ^ E`` with ``E`` being any\n        natural number.\n        If ``None``, image heights will not be altered.\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToFixedSize.__init__`.\n\n    pad_cval : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToFixedSize.__init__`.\n\n    position : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n        See :func:`PadToFixedSize.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.PadToPowersOf(height_base=3, width_base=2)\n\n    Create an augmenter that pads each image to powers of ``3`` along the\n    y-axis (i.e. 3, 9, 27, ...) and powers of ``2`` along the x-axis (i.e. 2,\n    4, 8, 16, ...).\n    The rows to be padded will be spread *randomly* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, width_base, height_base,\n                 pad_mode=\"constant\", pad_cval=0,\n                 position=\"uniform\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(PadToPowersOf, self).__init__(\n            width=None, height=None, pad_mode=pad_mode, pad_cval=pad_cval,\n            position=position,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.width_base = width_base\n        self.height_base = height_base\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        _sizes, pad_xs, pad_ys, pad_modes, pad_cvals = super(\n            PadToPowersOf, self\n        )._draw_samples(batch, random_state)\n\n        shapes = batch.get_rowwise_shapes()\n        sizes = []\n        for shape in shapes:\n            height, width = shape[0:2]\n            paddings = compute_paddings_to_reach_powers_of(\n                shape,\n                height_base=self.height_base,\n                width_base=self.width_base)\n\n            # TODO change that\n            # note that these are not in the same order as shape tuples\n            # in PadToFixedSize\n            new_size = (\n                width + paddings[1] + paddings[3],\n                height + paddings[0] + paddings[2]\n            )\n            sizes.append(new_size)\n\n        return sizes, pad_xs, pad_ys, pad_modes, pad_cvals\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.width_base, self.height_base,\n                self.pad_mode, self.pad_cval,\n                self.position]\n\n\nclass CenterPadToPowersOf(PadToPowersOf):\n    \"\"\"Pad images equally on all sides until H/W is a power of a base.\n\n    This is the same as :class:`~imgaug.augmenters.size.PadToPowersOf`, but uses\n    ``position=\"center\"`` by default, which spreads the pad amounts equally\n    over all image sides, while :class:`~imgaug.augmenters.size.PadToPowersOf`\n    by default spreads them randomly.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.PadToFixedSize`.\n\n    Parameters\n    ----------\n    width_base : int or None\n        See :func:`PadToPowersOf.__init__`.\n\n    height_base : int or None\n        See :func:`PadToPowersOf.__init__`.\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToPowersOf.__init__`.\n\n    pad_cval : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToPowersOf.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CenterPadToPowersOf(height_base=5, width_base=2)\n\n    Create an augmenter that pads each image to powers of ``3`` along the\n    y-axis (i.e. 3, 9, 27, ...) and powers of ``2`` along the x-axis (i.e. 2,\n    4, 8, 16, ...).\n    The rows to be padded will be spread *equally* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, width_base, height_base,\n                 pad_mode=\"constant\", pad_cval=0,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CenterPadToPowersOf, self).__init__(\n            width_base=width_base, height_base=height_base,\n            pad_mode=pad_mode, pad_cval=pad_cval,\n            position=\"center\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass CropToAspectRatio(CropToFixedSize):\n    \"\"\"Crop images until their width/height matches an aspect ratio.\n\n    This augmenter removes either rows or columns until the image reaches\n    the desired aspect ratio given in ``width / height``. The cropping\n    operation is stopped once the desired aspect ratio is reached or the image\n    side to crop reaches a size of ``1``. If any side of the image starts\n    with a size of ``0``, the image will not be changed.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.CropToFixedSize`.\n\n    Parameters\n    ----------\n    aspect_ratio : number\n        The desired aspect ratio, given as ``width/height``. E.g. a ratio\n        of ``2.0`` denotes an image that is twice as wide as it is high.\n\n    position : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n        See :func:`CropToFixedSize.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CropToAspectRatio(2.0)\n\n    Create an augmenter that crops each image until its aspect ratio is as\n    close as possible to ``2.0`` (i.e. two times as many pixels along the\n    x-axis than the y-axis).\n    The rows to be cropped will be spread *randomly* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, aspect_ratio, position=\"uniform\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CropToAspectRatio, self).__init__(\n            width=None, height=None, position=position,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.aspect_ratio = aspect_ratio\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        _sizes, offset_xs, offset_ys = super(\n            CropToAspectRatio, self\n        )._draw_samples(batch, random_state)\n\n        shapes = batch.get_rowwise_shapes()\n        sizes = []\n        for shape in shapes:\n            height, width = shape[0:2]\n\n            if height == 0 or width == 0:\n                croppings = (0, 0, 0, 0)\n            else:\n                croppings = compute_croppings_to_reach_aspect_ratio(\n                    shape,\n                    aspect_ratio=self.aspect_ratio)\n\n            # TODO change that\n            # note that these are not in the same order as shape tuples\n            # in CropToFixedSize\n            new_size = (\n                width - croppings[1] - croppings[3],\n                height - croppings[0] - croppings[2]\n            )\n            sizes.append(new_size)\n\n        return sizes, offset_xs, offset_ys\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.aspect_ratio, self.position]\n\n\nclass CenterCropToAspectRatio(CropToAspectRatio):\n    \"\"\"Crop images equally on all sides until they reach an aspect ratio.\n\n    This is the same as :class:`~imgaug.augmenters.size.CropToAspectRatio`, but\n    uses ``position=\"center\"`` by default, which spreads the crop amounts\n    equally over all image sides, while\n    :class:`~imgaug.augmenters.size.CropToAspectRatio` by default spreads\n    them randomly.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.CropToFixedSize`.\n\n    Parameters\n    ----------\n    aspect_ratio : number\n        See :func:`CropToAspectRatio.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CenterCropToAspectRatio(2.0)\n\n    Create an augmenter that crops each image until its aspect ratio is as\n    close as possible to ``2.0`` (i.e. two times as many pixels along the\n    x-axis than the y-axis).\n    The rows to be cropped will be spread *equally* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, aspect_ratio,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CenterCropToAspectRatio, self).__init__(\n            aspect_ratio=aspect_ratio, position=\"center\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass PadToAspectRatio(PadToFixedSize):\n    \"\"\"Pad images until their width/height matches an aspect ratio.\n\n    This augmenter adds either rows or columns until the image reaches\n    the desired aspect ratio given in ``width / height``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.PadToFixedSize`.\n\n    Parameters\n    ----------\n    aspect_ratio : number\n        The desired aspect ratio, given as ``width/height``. E.g. a ratio\n        of ``2.0`` denotes an image that is twice as wide as it is high.\n\n    position : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n        See :func:`PadToFixedSize.__init__`.\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToFixedSize.__init__`.\n\n    pad_cval : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToFixedSize.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.PadToAspectRatio(2.0)\n\n    Create an augmenter that pads each image until its aspect ratio is as\n    close as possible to ``2.0`` (i.e. two times as many pixels along the\n    x-axis than the y-axis).\n    The rows to be padded will be spread *randomly* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, aspect_ratio, pad_mode=\"constant\", pad_cval=0,\n                 position=\"uniform\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(PadToAspectRatio, self).__init__(\n            width=None, height=None, pad_mode=pad_mode, pad_cval=pad_cval,\n            position=position,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.aspect_ratio = aspect_ratio\n\n    # Added in 0.4.0.\n    def _draw_samples(self, batch, random_state):\n        _sizes, pad_xs, pad_ys, pad_modes, pad_cvals = super(\n            PadToAspectRatio, self\n        )._draw_samples(batch, random_state)\n\n        shapes = batch.get_rowwise_shapes()\n        sizes = []\n        for shape in shapes:\n            height, width = shape[0:2]\n\n            paddings = compute_paddings_to_reach_aspect_ratio(\n                shape,\n                aspect_ratio=self.aspect_ratio)\n\n            # TODO change that\n            # note that these are not in the same order as shape tuples\n            # in PadToFixedSize\n            new_size = (\n                width + paddings[1] + paddings[3],\n                height + paddings[0] + paddings[2]\n            )\n            sizes.append(new_size)\n\n        return sizes, pad_xs, pad_ys, pad_modes, pad_cvals\n\n    # Added in 0.4.0.\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.aspect_ratio, self.pad_mode, self.pad_cval,\n                self.position]\n\n\nclass CenterPadToAspectRatio(PadToAspectRatio):\n    \"\"\"Pad images equally on all sides until H/W matches an aspect ratio.\n\n    This is the same as :class:`~imgaug.augmenters.size.PadToAspectRatio`, but\n    uses ``position=\"center\"`` by default, which spreads the pad amounts\n    equally over all image sides, while\n    :class:`~imgaug.augmenters.size.PadToAspectRatio` by default spreads them\n    randomly.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.PadToFixedSize`.\n\n    Parameters\n    ----------\n    aspect_ratio : number\n        See :func:`PadToAspectRatio.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToAspectRatio.__init__`.\n\n    pad_cval : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToAspectRatio.__init__`.\n\n    deterministic : bool, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.PadToAspectRatio(2.0)\n\n    Create am augmenter that pads each image until its aspect ratio is as\n    close as possible to ``2.0`` (i.e. two times as many pixels along the\n    x-axis than the y-axis).\n    The rows to be padded will be spread *equally* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, aspect_ratio, pad_mode=\"constant\", pad_cval=0,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CenterPadToAspectRatio, self).__init__(\n            aspect_ratio=aspect_ratio, position=\"center\",\n            pad_mode=pad_mode, pad_cval=pad_cval,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass CropToSquare(CropToAspectRatio):\n    \"\"\"Crop images until their width and height are identical.\n\n    This is identical to :class:`~imgaug.augmenters.size.CropToAspectRatio`\n    with ``aspect_ratio=1.0``.\n\n    Images with axis sizes of ``0`` will not be altered.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.CropToFixedSize`.\n\n    Parameters\n    ----------\n    position : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n        See :func:`CropToFixedSize.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CropToSquare()\n\n    Create an augmenter that crops each image until its square, i.e. height\n    and width match.\n    The rows to be cropped will be spread *randomly* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, position=\"uniform\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CropToSquare, self).__init__(\n            aspect_ratio=1.0, position=position,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass CenterCropToSquare(CropToSquare):\n    \"\"\"Crop images equally on all sides until their height/width are identical.\n\n    In contrast to :class:`~imgaug.augmenters.size.CropToSquare`, this\n    augmenter always tries to spread the columns/rows to remove equally over\n    both sides of the respective axis to be cropped.\n    :class:`~imgaug.augmenters.size.CropToAspectRatio` by default spreads the\n    croppings randomly.\n\n    This augmenter is identical to :class:`~imgaug.augmenters.size.CropToSquare`\n    with ``position=\"center\"``, and thereby the same as\n    :class:`~imgaug.augmenters.size.CropToAspectRatio` with\n    ``aspect_ratio=1.0, position=\"center\"``.\n\n    Images with axis sizes of ``0`` will not be altered.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.CropToFixedSize`.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CenterCropToSquare()\n\n    Create an augmenter that crops each image until its square, i.e. height\n    and width match.\n    The rows to be cropped will be spread *equally* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CenterCropToSquare, self).__init__(\n            position=\"center\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass PadToSquare(PadToAspectRatio):\n    \"\"\"Pad images until their height and width are identical.\n\n    This augmenter is identical to\n    :class:`~imgaug.augmenters.size.PadToAspectRatio` with ``aspect_ratio=1.0``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.PadToFixedSize`.\n\n    Parameters\n    ----------\n    position : {'uniform', 'normal', 'center', 'left-top', 'left-center', 'left-bottom', 'center-top', 'center-center', 'center-bottom', 'right-top', 'right-center', 'right-bottom'} or tuple of float or StochasticParameter or tuple of StochasticParameter, optional\n        See :func:`PadToFixedSize.__init__`.\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToFixedSize.__init__`.\n\n    pad_cval : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToFixedSize.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.PadToSquare()\n\n    Create an augmenter that pads each image until its square, i.e. height\n    and width match.\n    The rows to be padded will be spread *randomly* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, pad_mode=\"constant\", pad_cval=0, position=\"uniform\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(PadToSquare, self).__init__(\n            aspect_ratio=1.0, pad_mode=pad_mode, pad_cval=pad_cval,\n            position=position,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass CenterPadToSquare(PadToSquare):\n    \"\"\"Pad images equally on all sides until their height & width are identical.\n\n    This is the same as :class:`~imgaug.augmenters.size.PadToSquare`, but uses\n    ``position=\"center\"`` by default, which spreads the pad amounts equally\n    over all image sides, while :class:`~imgaug.augmenters.size.PadToSquare`\n    by default spreads them randomly. This augmenter is thus also identical to\n    :class:`~imgaug.augmenters.size.PadToAspectRatio` with\n    ``aspect_ratio=1.0, position=\"center\"``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n    See :class:`~imgaug.augmenters.size.PadToFixedSize`.\n\n    Parameters\n    ----------\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    pad_mode : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToAspectRatio.__init__`.\n\n    pad_cval : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        See :func:`~imgaug.augmenters.size.PadToAspectRatio.__init__`.\n\n    deterministic : bool, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.CenterPadToSquare()\n\n    Create an augmenter that pads each image until its square, i.e. height\n    and width match.\n    The rows to be padded will be spread *equally* over the top and bottom\n    sides (analogous for the left/right sides).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, pad_mode=\"constant\", pad_cval=0,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CenterPadToSquare, self).__init__(\n            pad_mode=pad_mode, pad_cval=pad_cval, position=\"center\",\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass KeepSizeByResize(meta.Augmenter):\n    \"\"\"Resize images back to their input sizes after applying child augmenters.\n\n    Combining this with e.g. a cropping augmenter as the child will lead to\n    images being resized back to the input size after the crop operation was\n    applied. Some augmenters have a ``keep_size`` argument that achieves the\n    same goal (if set to ``True``), though this augmenter offers control over\n    the interpolation mode and which augmentables to resize (images, heatmaps,\n    segmentation maps).\n\n    **Supported dtypes**:\n\n    See :func:`~imgaug.imgaug.imresize_many_images`.\n\n    Parameters\n    ----------\n    children : Augmenter or list of imgaug.augmenters.meta.Augmenter or None, optional\n        One or more augmenters to apply to images. These augmenters may change\n        the image size.\n\n    interpolation : KeepSizeByResize.NO_RESIZE or {'nearest', 'linear', 'area', 'cubic'} or {cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_AREA, cv2.INTER_CUBIC} or list of str or list of int or StochasticParameter, optional\n        The interpolation mode to use when resizing images.\n        Can take any value that :func:`~imgaug.imgaug.imresize_single_image`\n        accepts, e.g. ``cubic``.\n\n            * If this is ``KeepSizeByResize.NO_RESIZE`` then images will not\n              be resized.\n            * If this is a single ``str``, it is expected to have one of the\n              following values: ``nearest``, ``linear``, ``area``, ``cubic``.\n            * If this is a single integer, it is expected to have a value\n              identical to one of: ``cv2.INTER_NEAREST``,\n              ``cv2.INTER_LINEAR``, ``cv2.INTER_AREA``, ``cv2.INTER_CUBIC``.\n            * If this is a ``list`` of ``str`` or ``int``, it is expected that\n              each ``str``/``int`` is one of the above mentioned valid ones.\n              A random one of these values will be sampled per image.\n            * If this is a ``StochasticParameter``, it will be queried once per\n              call to ``_augment_images()`` and must return ``N`` ``str`` s or\n              ``int`` s (matching the above mentioned ones) for ``N`` images.\n\n    interpolation_heatmaps : KeepSizeByResize.SAME_AS_IMAGES or KeepSizeByResize.NO_RESIZE or {'nearest', 'linear', 'area', 'cubic'} or {cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_AREA, cv2.INTER_CUBIC} or list of str or list of int or StochasticParameter, optional\n        The interpolation mode to use when resizing heatmaps.\n        Meaning and valid values are similar to `interpolation`. This\n        parameter may also take the value ``KeepSizeByResize.SAME_AS_IMAGES``,\n        which will lead to copying the interpolation modes used for the\n        corresponding images. The value may also be returned on a per-image\n        basis if `interpolation_heatmaps` is provided as a\n        ``StochasticParameter`` or may be one possible value if it is\n        provided as a ``list`` of ``str``.\n\n    interpolation_segmaps : KeepSizeByResize.SAME_AS_IMAGES or KeepSizeByResize.NO_RESIZE or {'nearest', 'linear', 'area', 'cubic'} or {cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_AREA, cv2.INTER_CUBIC} or list of str or list of int or StochasticParameter, optional\n        The interpolation mode to use when resizing segmentation maps.\n        Similar to `interpolation_heatmaps`.\n        **Note**: For segmentation maps, only ``NO_RESIZE`` or nearest\n        neighbour interpolation (i.e. ``nearest``) make sense in the vast\n        majority of all cases.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.KeepSizeByResize(\n    >>>     iaa.Crop((20, 40), keep_size=False)\n    >>> )\n\n    Apply random cropping to input images, then resize them back to their\n    original input sizes. The resizing is done using this augmenter instead\n    of the corresponding internal resizing operation in ``Crop``.\n\n    >>> aug = iaa.KeepSizeByResize(\n    >>>     iaa.Crop((20, 40), keep_size=False),\n    >>>     interpolation=\"nearest\"\n    >>> )\n\n    Same as in the previous example, but images are now always resized using\n    nearest neighbour interpolation.\n\n    >>> aug = iaa.KeepSizeByResize(\n    >>>     iaa.Crop((20, 40), keep_size=False),\n    >>>     interpolation=[\"nearest\", \"cubic\"],\n    >>>     interpolation_heatmaps=iaa.KeepSizeByResize.SAME_AS_IMAGES,\n    >>>     interpolation_segmaps=iaa.KeepSizeByResize.NO_RESIZE\n    >>> )\n\n    Similar to the previous example, but images are now sometimes resized\n    using linear interpolation and sometimes using nearest neighbour\n    interpolation. Heatmaps are resized using the same interpolation as was\n    used for the corresponding image. Segmentation maps are not resized and\n    will therefore remain at their size after cropping.\n\n    \"\"\"\n\n    NO_RESIZE = \"NO_RESIZE\"\n    SAME_AS_IMAGES = \"SAME_AS_IMAGES\"\n\n    def __init__(self, children,\n                 interpolation=\"cubic\",\n                 interpolation_heatmaps=SAME_AS_IMAGES,\n                 interpolation_segmaps=\"nearest\",\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(KeepSizeByResize, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.children = children\n\n        @iap._prefetchable_str\n        def _validate_param(val, allow_same_as_images):\n            valid_ips_and_resize = ia.IMRESIZE_VALID_INTERPOLATIONS \\\n                                  + [KeepSizeByResize.NO_RESIZE]\n            if allow_same_as_images and val == self.SAME_AS_IMAGES:\n                return self.SAME_AS_IMAGES\n            if val in valid_ips_and_resize:\n                return iap.Deterministic(val)\n            if isinstance(val, list):\n                assert len(val) > 0, (\n                    \"Expected a list of at least one interpolation method. \"\n                    \"Got an empty list.\")\n                valid_ips_here = valid_ips_and_resize\n                if allow_same_as_images:\n                    valid_ips_here = valid_ips_here \\\n                                     + [KeepSizeByResize.SAME_AS_IMAGES]\n                only_valid_ips = all([ip in valid_ips_here for ip in val])\n                assert only_valid_ips, (\n                    \"Expected each interpolations to be one of '%s', got \"\n                    \"'%s'.\" % (str(valid_ips_here), str(val)))\n                return iap.Choice(val)\n            if isinstance(val, iap.StochasticParameter):\n                return val\n            raise Exception(\n                \"Expected interpolation to be one of '%s' or a list of \"\n                \"these values or a StochasticParameter. Got type %s.\" % (\n                    str(ia.IMRESIZE_VALID_INTERPOLATIONS), type(val)))\n\n        self.children = meta.handle_children_list(children, self.name, \"then\")\n        self.interpolation = _validate_param(interpolation, False)\n        self.interpolation_heatmaps = _validate_param(interpolation_heatmaps,\n                                                      True)\n        self.interpolation_segmaps = _validate_param(interpolation_segmaps,\n                                                     True)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        with batch.propagation_hooks_ctx(self, hooks, parents):\n            images_were_array = None\n            if batch.images is not None:\n                images_were_array = ia.is_np_array(batch.images)\n            shapes_orig = self._get_shapes(batch)\n\n            samples = self._draw_samples(batch.nb_rows, random_state)\n\n            batch = self.children.augment_batch_(\n                batch, parents=parents + [self], hooks=hooks)\n\n            if batch.images is not None:\n                batch.images = self._keep_size_images(\n                    batch.images, shapes_orig[\"images\"], images_were_array,\n                    samples)\n\n            if batch.heatmaps is not None:\n                # dont use shapes_orig[\"images\"] because they might be None\n                batch.heatmaps = self._keep_size_maps(\n                    batch.heatmaps, shapes_orig[\"heatmaps\"],\n                    shapes_orig[\"heatmaps_arr\"], samples[1])\n\n            if batch.segmentation_maps is not None:\n                # dont use shapes_orig[\"images\"] because they might be None\n                batch.segmentation_maps = self._keep_size_maps(\n                    batch.segmentation_maps, shapes_orig[\"segmentation_maps\"],\n                    shapes_orig[\"segmentation_maps_arr\"], samples[2])\n\n            for augm_name in [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                              \"line_strings\"]:\n                augm_value = getattr(batch, augm_name)\n                if augm_value is not None:\n                    func = functools.partial(\n                        self._keep_size_keypoints,\n                        shapes_orig=shapes_orig[augm_name],\n                        interpolations=samples[0])\n                    cbaois = self._apply_to_cbaois_as_keypoints(augm_value,\n                                                                func)\n                    setattr(batch, augm_name, cbaois)\n        return batch\n\n    # Added in 0.4.0.\n    @classmethod\n    def _keep_size_images(cls, images, shapes_orig, images_were_array,\n                          samples):\n        interpolations, _, _ = samples\n\n        gen = zip(images, interpolations, shapes_orig)\n        result = []\n        for image, interpolation, input_shape in gen:\n            if interpolation == KeepSizeByResize.NO_RESIZE:\n                result.append(image)\n            else:\n                result.append(\n                    ia.imresize_single_image(image, input_shape[0:2],\n                                             interpolation))\n\n        if images_were_array:\n            # note here that NO_RESIZE can have led to different shapes\n            nb_shapes = len({image.shape for image in result})\n            if nb_shapes == 1:\n                # images.dtype does not necessarily work anymore, children\n                # might have turned 'images' into list\n                result = np.array(result, dtype=result[0].dtype)\n\n        return result\n\n    # Added in 0.4.0.\n    @classmethod\n    def _keep_size_maps(cls, augmentables, shapes_orig_images,\n                        shapes_orig_arrs, interpolations):\n        result = []\n        gen = zip(augmentables, interpolations,\n                  shapes_orig_arrs, shapes_orig_images)\n        for augmentable, interpolation, arr_shape_orig, img_shape_orig in gen:\n            if interpolation == \"NO_RESIZE\":\n                result.append(augmentable)\n            else:\n                augmentable = augmentable.resize(\n                    arr_shape_orig[0:2], interpolation=interpolation)\n                augmentable.shape = img_shape_orig\n                result.append(augmentable)\n\n        return result\n\n    # Added in 0.4.0.\n    @classmethod\n    def _keep_size_keypoints(cls, kpsois_aug, shapes_orig, interpolations):\n        result = []\n        gen = zip(kpsois_aug, interpolations, shapes_orig)\n        for kpsoi_aug, interpolation, input_shape in gen:\n            if interpolation == KeepSizeByResize.NO_RESIZE:\n                result.append(kpsoi_aug)\n            else:\n                result.append(kpsoi_aug.on_(input_shape))\n\n        return result\n\n    # Added in 0.4.0.\n    @classmethod\n    def _get_shapes(cls, batch):\n        result = dict()\n        for column in batch.columns:\n            result[column.name] = [cell.shape for cell in column.value]\n\n        if batch.heatmaps is not None:\n            result[\"heatmaps_arr\"] = [\n                cell.arr_0to1.shape for cell in batch.heatmaps]\n\n        if batch.segmentation_maps is not None:\n            result[\"segmentation_maps_arr\"] = [\n                cell.arr.shape for cell in batch.segmentation_maps]\n\n        return result\n\n    def _draw_samples(self, nb_images, random_state):\n        rngs = random_state.duplicate(3)\n        interpolations = self.interpolation.draw_samples((nb_images,),\n                                                         random_state=rngs[0])\n\n        if self.interpolation_heatmaps == KeepSizeByResize.SAME_AS_IMAGES:\n            interpolations_heatmaps = np.copy(interpolations)\n        else:\n            interpolations_heatmaps = self.interpolation_heatmaps.draw_samples(\n                (nb_images,), random_state=rngs[1]\n            )\n\n            # Note that `interpolations_heatmaps == self.SAME_AS_IMAGES`\n            # works here only if the datatype of the array is such that it\n            # may contain strings. It does not work properly for e.g.\n            # integer arrays and will produce a single bool output, even\n            # for arrays with more than one entry.\n            same_as_imgs_idx = [ip == self.SAME_AS_IMAGES\n                                for ip in interpolations_heatmaps]\n\n            interpolations_heatmaps[same_as_imgs_idx] = \\\n                interpolations[same_as_imgs_idx]\n\n        if self.interpolation_segmaps == KeepSizeByResize.SAME_AS_IMAGES:\n            interpolations_segmaps = np.copy(interpolations)\n        else:\n            # TODO This used previously the same seed as the heatmaps part\n            #      leading to the same sampled values. Was that intentional?\n            #      Doesn't look like it should be that way.\n            interpolations_segmaps = self.interpolation_segmaps.draw_samples(\n                (nb_images,), random_state=rngs[2]\n            )\n\n            # Note that `interpolations_heatmaps == self.SAME_AS_IMAGES`\n            # works here only if the datatype of the array is such that it\n            # may contain strings. It does not work properly for e.g.\n            # integer arrays and will produce a single bool output, even\n            # for arrays with more than one entry.\n            same_as_imgs_idx = [ip == self.SAME_AS_IMAGES\n                                for ip in interpolations_segmaps]\n\n            interpolations_segmaps[same_as_imgs_idx] = \\\n                interpolations[same_as_imgs_idx]\n\n        return interpolations, interpolations_heatmaps, interpolations_segmaps\n\n    def _to_deterministic(self):\n        aug = self.copy()\n        aug.children = aug.children.to_deterministic()\n        aug.deterministic = True\n        aug.random_state = self.random_state.derive_rng_()\n        return aug\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.interpolation, self.interpolation_heatmaps]\n\n    def get_children_lists(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_children_lists`.\"\"\"\n        return [self.children]\n\n    def __str__(self):\n        pattern = (\n            \"%s(\"\n            \"interpolation=%s, \"\n            \"interpolation_heatmaps=%s, \"\n            \"name=%s, \"\n            \"children=%s, \"\n            \"deterministic=%s\"\n            \")\")\n        return pattern % (\n            self.__class__.__name__, self.interpolation,\n            self.interpolation_heatmaps, self.name, self.children,\n            self.deterministic)\n"
  },
  {
    "path": "imgaug/augmenters/weather.py",
    "content": "\"\"\"\nAugmenters that create weather effects.\n\nList of augmenters:\n\n    * :class:`FastSnowyLandscape`\n    * :class:`CloudLayer`\n    * :class:`Clouds`\n    * :class:`Fog`\n    * :class:`SnowflakesLayer`\n    * :class:`Snowflakes`\n    * :class:`RainLayer`\n    * :class:`Rain`\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport numpy as np\n\nimport imgaug as ia\nfrom . import meta, arithmetic, blur, contrast, color as colorlib\nfrom .. import parameters as iap\nfrom .. import dtypes as iadt\n\n\nclass FastSnowyLandscape(meta.Augmenter):\n    \"\"\"Convert non-snowy landscapes to snowy ones.\n\n    This augmenter expects to get an image that roughly shows a landscape.\n\n    This augmenter is based on the method proposed in\n    https://medium.freecodecamp.org/image-augmentation-make-it-rain-make-it-snow-how-to-modify-a-photo-with-machine-learning-163c0cb3843f?gi=bca4a13e634c\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no (1)\n        * ``uint32``: no (1)\n        * ``uint64``: no (1)\n        * ``int8``: no (1)\n        * ``int16``: no (1)\n        * ``int32``: no (1)\n        * ``int64``: no (1)\n        * ``float16``: no (1)\n        * ``float32``: no (1)\n        * ``float64``: no (1)\n        * ``float128``: no (1)\n        * ``bool``: no (1)\n\n        - (1) This augmenter is based on a colorspace conversion to HLS.\n              Hence, only RGB ``uint8`` inputs are sensible.\n\n    Parameters\n    ----------\n    lightness_threshold : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        All pixels with lightness in HLS colorspace that is below this value\n        will have their lightness increased by `lightness_multiplier`.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the discrete interval ``[a..b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    lightness_multiplier : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Multiplier for pixel's lightness value in HLS colorspace.\n        Affects all pixels selected via `lightness_threshold`.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the discrete interval ``[a..b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    from_colorspace : str, optional\n        The source colorspace of the input images.\n        See :func:`~imgaug.augmenters.color.ChangeColorspace.__init__`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.FastSnowyLandscape(\n    >>>     lightness_threshold=140,\n    >>>     lightness_multiplier=2.5\n    >>> )\n\n    Search for all pixels in the image with a lightness value in HLS\n    colorspace of less than ``140`` and increase their lightness by a factor\n    of ``2.5``.\n\n    >>> aug = iaa.FastSnowyLandscape(\n    >>>     lightness_threshold=[128, 200],\n    >>>     lightness_multiplier=(1.5, 3.5)\n    >>> )\n\n    Search for all pixels in the image with a lightness value in HLS\n    colorspace of less than ``128`` or less than ``200`` (one of these\n    values is picked per image) and multiply their lightness by a factor\n    of ``x`` with ``x`` being sampled from ``uniform(1.5, 3.5)`` (once per\n    image).\n\n    >>> aug = iaa.FastSnowyLandscape(\n    >>>     lightness_threshold=(100, 255),\n    >>>     lightness_multiplier=(1.0, 4.0)\n    >>> )\n\n    Similar to the previous example, but the lightness threshold is sampled\n    from ``uniform(100, 255)`` (per image) and the multiplier\n    from ``uniform(1.0, 4.0)`` (per image). This seems to produce good and\n    varied results.\n\n    \"\"\"\n\n    def __init__(self, lightness_threshold=(100, 255),\n                 lightness_multiplier=(1.0, 4.0),\n                 from_colorspace=colorlib.CSPACE_RGB,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(FastSnowyLandscape, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n        self.lightness_threshold = iap.handle_continuous_param(\n            lightness_threshold, \"lightness_threshold\",\n            value_range=(0, 255), tuple_to_uniform=True, list_to_choice=True)\n        self.lightness_multiplier = iap.handle_continuous_param(\n            lightness_multiplier, \"lightness_multiplier\",\n            value_range=(0, None), tuple_to_uniform=True, list_to_choice=True)\n        self.from_colorspace = from_colorspace\n\n    def _draw_samples(self, augmentables, random_state):\n        nb_augmentables = len(augmentables)\n        rss = random_state.duplicate(2)\n        thresh_samples = self.lightness_threshold.draw_samples(\n            (nb_augmentables,), rss[1])\n        lmul_samples = self.lightness_multiplier.draw_samples(\n            (nb_augmentables,), rss[0])\n        return thresh_samples, lmul_samples\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        thresh_samples, lmul_samples = self._draw_samples(images, random_state)\n\n        gen = enumerate(zip(images, thresh_samples, lmul_samples))\n        for i, (image, thresh, lmul) in gen:\n            image_hls = colorlib.change_colorspace_(\n                image, colorlib.CSPACE_HLS, self.from_colorspace)\n            cvt_dtype = image_hls.dtype\n            image_hls = image_hls.astype(np.float64)\n            lightness = image_hls[..., 1]\n\n            lightness[lightness < thresh] *= lmul\n\n            image_hls = iadt.restore_dtypes_(image_hls, cvt_dtype)\n            image_rgb = colorlib.change_colorspace_(\n                image_hls, self.from_colorspace, colorlib.CSPACE_HLS)\n\n            batch.images[i] = image_rgb\n\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.lightness_threshold, self.lightness_multiplier]\n\n\n# TODO add examples and add these to the overview docs\n# TODO add perspective transform to each cloud layer to make them look more\n#      distant?\n# TODO alpha_mean and density overlap - remove one of them\nclass CloudLayer(meta.Augmenter):\n    \"\"\"Add a single layer of clouds to an image.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; indirectly tested (1)\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: yes; not tested\n        * ``float32``: yes; not tested\n        * ``float64``: yes; not tested\n        * ``float128``: yes; not tested (2)\n        * ``bool``: no\n\n        - (1) Indirectly tested via tests for :class:`Clouds`` and :class:`Fog`\n        - (2) Note that random values are usually sampled as ``int64`` or\n              ``float64``, which ``float128`` images would exceed. Note also\n              that random values might have to upscaled, which is done\n              via :func:`~imgaug.imgaug.imresize_many_images` and has its own\n              limited dtype support (includes however floats up to ``64bit``).\n\n    Parameters\n    ----------\n    intensity_mean : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Mean intensity of the clouds (i.e. mean color).\n        Recommended to be in the interval ``[190, 255]``.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly\n              sampled per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    intensity_freq_exponent : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Exponent of the frequency noise used to add fine intensity to the\n        mean intensity.\n        Recommended to be in the interval ``[-2.5, -1.5]``.\n        See :func:`~imgaug.parameters.FrequencyNoise.__init__` for details.\n\n    intensity_coarse_scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Standard deviation of the gaussian distribution used to add more\n        localized intensity to the mean intensity. Sampled in low resolution\n        space, i.e. affects final intensity on a coarse level.\n        Recommended to be in the interval ``(0, 10]``.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    alpha_min : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Minimum alpha when blending cloud noise with the image.\n        High values will lead to clouds being \"everywhere\".\n        Recommended to usually be at around ``0.0`` for clouds and ``>0`` for\n        fog.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    alpha_multiplier : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Multiplier for the sampled alpha values. High values will lead to\n        denser clouds wherever they are visible.\n        Recommended to be in the interval ``[0.3, 1.0]``.\n        Note that this parameter currently overlaps with `density_multiplier`,\n        which is applied a bit later to the alpha mask.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    alpha_size_px_max : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Controls the image size at which the alpha mask is sampled.\n        Lower values will lead to coarser alpha masks and hence larger\n        clouds (and empty areas).\n        See :func:`~imgaug.parameters.FrequencyNoise.__init__` for details.\n\n    alpha_freq_exponent : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Exponent of the frequency noise used to sample the alpha mask.\n        Similarly to `alpha_size_max_px`, lower values will lead to coarser\n        alpha patterns.\n        Recommended to be in the interval ``[-4.0, -1.5]``.\n        See :func:`~imgaug.parameters.FrequencyNoise.__init__` for details.\n\n    sparsity : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Exponent applied late to the alpha mask. Lower values will lead to\n        coarser cloud patterns, higher values to finer patterns.\n        Recommended to be somewhere around ``1.0``.\n        Do not deviate far from that value, otherwise the alpha mask might\n        get weird patterns with sudden fall-offs to zero that look very\n        unnatural.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    density_multiplier : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Late multiplier for the alpha mask, similar to `alpha_multiplier`.\n        Set this higher to get \"denser\" clouds wherever they are visible.\n        Recommended to be around ``[0.5, 1.5]``.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    \"\"\"\n\n    def __init__(self, intensity_mean, intensity_freq_exponent,\n                 intensity_coarse_scale, alpha_min, alpha_multiplier,\n                 alpha_size_px_max, alpha_freq_exponent, sparsity,\n                 density_multiplier,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(CloudLayer, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.intensity_mean = iap.handle_continuous_param(\n            intensity_mean, \"intensity_mean\")\n        self.intensity_freq_exponent = intensity_freq_exponent\n        self.intensity_coarse_scale = intensity_coarse_scale\n        self.alpha_min = iap.handle_continuous_param(alpha_min, \"alpha_min\")\n        self.alpha_multiplier = iap.handle_continuous_param(\n            alpha_multiplier, \"alpha_multiplier\")\n        self.alpha_size_px_max = alpha_size_px_max\n        self.alpha_freq_exponent = alpha_freq_exponent\n        self.sparsity = iap.handle_continuous_param(sparsity, \"sparsity\")\n        self.density_multiplier = iap.handle_continuous_param(\n            density_multiplier, \"density_multiplier\")\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        rss = random_state.duplicate(len(images))\n        for i, (image, rs) in enumerate(zip(images, rss)):\n            batch.images[i] = self.draw_on_image(image, rs)\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.intensity_mean,\n                self.alpha_min,\n                self.alpha_multiplier,\n                self.alpha_size_px_max,\n                self.alpha_freq_exponent,\n                self.intensity_freq_exponent,\n                self.sparsity,\n                self.density_multiplier,\n                self.intensity_coarse_scale]\n\n    def draw_on_image(self, image, random_state):\n        iadt.gate_dtypes_strs(\n            image,\n            allowed=\"uint8 float16 float32 float64 float128\",\n            disallowed=\"bool uint16 uint32 uint64 int8 int16 int32 int64\",\n            augmenter=self\n        )\n\n        alpha, intensity = self.generate_maps(image, random_state)\n        alpha = alpha[..., np.newaxis]\n        intensity = intensity[..., np.newaxis]\n\n        if image.dtype.kind == \"f\":\n            intensity = intensity.astype(image.dtype)\n            return (1 - alpha) * image + alpha * intensity\n\n        intensity = np.clip(intensity, 0, 255)\n        # TODO use blend_alpha_() here\n        return np.clip(\n            (1 - alpha) * image.astype(alpha.dtype)\n            + alpha * intensity.astype(alpha.dtype),\n            0,\n            255\n        ).astype(np.uint8)\n\n    def generate_maps(self, image, random_state):\n        intensity_mean_sample = self.intensity_mean.draw_sample(random_state)\n        alpha_min_sample = self.alpha_min.draw_sample(random_state)\n        alpha_multiplier_sample = \\\n            self.alpha_multiplier.draw_sample(random_state)\n        alpha_size_px_max = self.alpha_size_px_max\n        intensity_freq_exponent = self.intensity_freq_exponent\n        alpha_freq_exponent = self.alpha_freq_exponent\n        sparsity_sample = self.sparsity.draw_sample(random_state)\n        density_multiplier_sample = \\\n            self.density_multiplier.draw_sample(random_state)\n\n        height, width = image.shape[0:2]\n        rss_alpha, rss_intensity = random_state.duplicate(2)\n\n        intensity_coarse = self._generate_intensity_map_coarse(\n            height, width, intensity_mean_sample,\n            iap.Normal(0, scale=self.intensity_coarse_scale),\n            rss_intensity\n        )\n        intensity_fine = self._generate_intensity_map_fine(\n            height, width, intensity_mean_sample, intensity_freq_exponent,\n            rss_intensity)\n        intensity = intensity_coarse + intensity_fine\n\n        alpha = self._generate_alpha_mask(\n            height, width, alpha_min_sample, alpha_multiplier_sample,\n            alpha_freq_exponent, alpha_size_px_max, sparsity_sample,\n            density_multiplier_sample, rss_alpha)\n\n        return alpha, intensity\n\n    @classmethod\n    def _generate_intensity_map_coarse(cls, height, width, intensity_mean,\n                                       intensity_local_offset, random_state):\n        # TODO (8, 8) might be too simplistic for some image sizes\n        height_intensity, width_intensity = (8, 8)\n        intensity = (\n            intensity_mean\n            + intensity_local_offset.draw_samples(\n                (height_intensity, width_intensity), random_state)\n        )\n        intensity = ia.imresize_single_image(\n            intensity, (height, width), interpolation=\"cubic\")\n\n        return intensity\n\n    @classmethod\n    def _generate_intensity_map_fine(cls, height, width, intensity_mean,\n                                     exponent, random_state):\n        intensity_details_generator = iap.FrequencyNoise(\n            exponent=exponent,\n            size_px_max=max(height, width, 1),  # 1 here for case H, W being 0\n            upscale_method=\"cubic\"\n        )\n        intensity_details = intensity_details_generator.draw_samples(\n            (height, width), random_state)\n        return intensity_mean * ((2*intensity_details - 1.0)/5.0)\n\n    @classmethod\n    def _generate_alpha_mask(cls, height, width, alpha_min, alpha_multiplier,\n                             exponent, alpha_size_px_max, sparsity,\n                             density_multiplier, random_state):\n        alpha_generator = iap.FrequencyNoise(\n            exponent=exponent,\n            size_px_max=alpha_size_px_max,\n            upscale_method=\"cubic\"\n        )\n        alpha_local = alpha_generator.draw_samples(\n            (height, width), random_state)\n        alpha = alpha_min + (alpha_multiplier * alpha_local)\n        alpha = (alpha ** sparsity) * density_multiplier\n        alpha = np.clip(alpha, 0.0, 1.0)\n\n        return alpha\n\n\n# TODO add vertical gradient alpha to have clouds only at skylevel/groundlevel\n# TODO add configurable parameters\nclass Clouds(meta.SomeOf):\n    \"\"\"\n    Add clouds to images.\n\n    This is a wrapper around :class:`~imgaug.augmenters.weather.CloudLayer`.\n    It executes 1 to 2 layers per image, leading to varying densities and\n    frequency patterns of clouds.\n\n    This augmenter seems to be fairly robust w.r.t. the image size. Tested\n    with ``96x128``, ``192x256`` and ``960x1280``.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; tested\n        * ``uint16``: no (1)\n        * ``uint32``: no (1)\n        * ``uint64``: no (1)\n        * ``int8``: no (1)\n        * ``int16``: no (1)\n        * ``int32``: no (1)\n        * ``int64``: no (1)\n        * ``float16``: no (1)\n        * ``float32``: no (1)\n        * ``float64``: no (1)\n        * ``float128``: no (1)\n        * ``bool``: no (1)\n\n        - (1) Parameters of this augmenter are optimized for the value range\n              of ``uint8``. While other dtypes may be accepted, they will lead\n              to images augmented in ways inappropriate for the respective\n              dtype.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Clouds()\n\n    Create an augmenter that adds clouds to images.\n\n    \"\"\"\n\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        layers = [\n            CloudLayer(\n                intensity_mean=(196, 255),\n                intensity_freq_exponent=(-2.5, -2.0),\n                intensity_coarse_scale=10,\n                alpha_min=0,\n                alpha_multiplier=(0.25, 0.75),\n                alpha_size_px_max=(2, 8),\n                alpha_freq_exponent=(-2.5, -2.0),\n                sparsity=(0.8, 1.0),\n                density_multiplier=(0.5, 1.0),\n                seed=seed,\n                random_state=random_state,\n                deterministic=deterministic\n            ),\n            CloudLayer(\n                intensity_mean=(196, 255),\n                intensity_freq_exponent=(-2.0, -1.0),\n                intensity_coarse_scale=10,\n                alpha_min=0,\n                alpha_multiplier=(0.5, 1.0),\n                alpha_size_px_max=(64, 128),\n                alpha_freq_exponent=(-2.0, -1.0),\n                sparsity=(1.0, 1.4),\n                density_multiplier=(0.8, 1.5),\n                seed=seed,\n                random_state=random_state,\n                deterministic=deterministic\n            )\n        ]\n\n        super(Clouds, self).__init__(\n            (1, 2),\n            children=layers,\n            random_order=False,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO add vertical gradient alpha to have fog only at skylevel/groundlevel\n# TODO add configurable parameters\nclass Fog(CloudLayer):\n    \"\"\"Add fog to images.\n\n    This is a wrapper around :class:`~imgaug.augmenters.weather.CloudLayer`.\n    It executes a single layer per image with a configuration leading to\n    fairly dense clouds with low-frequency patterns.\n\n    This augmenter seems to be fairly robust w.r.t. the image size. Tested\n    with ``96x128``, ``192x256`` and ``960x1280``.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; tested\n        * ``uint16``: no (1)\n        * ``uint32``: no (1)\n        * ``uint64``: no (1)\n        * ``int8``: no (1)\n        * ``int16``: no (1)\n        * ``int32``: no (1)\n        * ``int64``: no (1)\n        * ``float16``: no (1)\n        * ``float32``: no (1)\n        * ``float64``: no (1)\n        * ``float128``: no (1)\n        * ``bool``: no (1)\n\n        - (1) Parameters of this augmenter are optimized for the value range\n              of ``uint8``. While other dtypes may be accepted, they will lead\n              to images augmented in ways inappropriate for the respective\n              dtype.\n\n    Parameters\n    ----------\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Fog()\n\n    Create an augmenter that adds fog to images.\n\n    \"\"\"\n\n    def __init__(self,\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(Fog, self).__init__(\n            intensity_mean=(220, 255),\n            intensity_freq_exponent=(-2.0, -1.5),\n            intensity_coarse_scale=2,\n            alpha_min=(0.7, 0.9),\n            alpha_multiplier=0.3,\n            alpha_size_px_max=(2, 8),\n            alpha_freq_exponent=(-4.0, -2.0),\n            sparsity=0.9,\n            density_multiplier=(0.4, 0.9),\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\n# TODO add examples and add these to the overview docs\n# TODO snowflakes are all almost 100% white, add some grayish tones and\n#      maybe color to them\nclass SnowflakesLayer(meta.Augmenter):\n    \"\"\"Add a single layer of falling snowflakes to images.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; indirectly tested (1)\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n        - (1) indirectly tested via tests for :class:`Snowflakes`\n\n    Parameters\n    ----------\n    density : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Density of the snowflake layer, as a probability of each pixel in\n        low resolution space to be a snowflake.\n        Valid values are in the interval ``[0.0, 1.0]``.\n        Recommended to be in the interval ``[0.01, 0.075]``.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    density_uniformity : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Size uniformity of the snowflakes. Higher values denote more\n        similarly sized snowflakes.\n        Valid values are in the interval ``[0.0, 1.0]``.\n        Recommended to be around ``0.5``.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    flake_size : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Size of the snowflakes. This parameter controls the resolution at\n        which snowflakes are sampled. Higher values mean that the resolution\n        is closer to the input image's resolution and hence each sampled\n        snowflake will be smaller (because of the smaller pixel size).\n\n        Valid values are in the interval ``(0.0, 1.0]``.\n        Recommended values:\n\n            * On 96x128 a value of ``(0.1, 0.4)`` worked well.\n            * On 192x256 a value of ``(0.2, 0.7)`` worked well.\n            * On 960x1280 a value of ``(0.7, 0.95)`` worked well.\n\n        Datatype behaviour:\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    flake_size_uniformity : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Controls the size uniformity of the snowflakes. Higher values mean\n        that the snowflakes are more similarly sized.\n        Valid values are in the interval ``[0.0, 1.0]``.\n        Recommended to be around ``0.5``.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    angle : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Angle in degrees of motion blur applied to the snowflakes, where\n        ``0.0`` is motion blur that points straight upwards.\n        Recommended to be in the interval ``[-30, 30]``.\n        See also :func:`~imgaug.augmenters.blur.MotionBlur.__init__`.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    speed : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Perceived falling speed of the snowflakes. This parameter controls the\n        motion blur's kernel size. It follows roughly the form\n        ``kernel_size = image_size * speed``. Hence, values around ``1.0``\n        denote that the motion blur should \"stretch\" each snowflake over the\n        whole image.\n\n        Valid values are in the interval ``[0.0, 1.0]``.\n        Recommended values:\n\n            * On 96x128 a value of ``(0.01, 0.05)`` worked well.\n            * On 192x256 a value of ``(0.007, 0.03)`` worked well.\n            * On 960x1280 a value of ``(0.001, 0.03)`` worked well.\n\n        Datatype behaviour:\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    blur_sigma_fraction : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Standard deviation (as a fraction of the image size) of gaussian blur\n        applied to the snowflakes.\n        Valid values are in the interval ``[0.0, 1.0]``.\n        Recommended to be in the interval ``[0.0001, 0.001]``. May still\n        require tinkering based on image size.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    blur_sigma_limits : tuple of float, optional\n        Controls allowed min and max values of `blur_sigma_fraction`\n        after(!) multiplication with the image size. First value is the\n        minimum, second value is the maximum. Values outside of that range\n        will be clipped to be within that range. This prevents extreme\n        values for very small or large images.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    \"\"\"\n\n    def __init__(self, density, density_uniformity, flake_size,\n                 flake_size_uniformity, angle, speed, blur_sigma_fraction,\n                 blur_sigma_limits=(0.5, 3.75),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(SnowflakesLayer, self).__init__(\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n        self.density = density\n        self.density_uniformity = iap.handle_continuous_param(\n            density_uniformity, \"density_uniformity\", value_range=(0.0, 1.0))\n        self.flake_size = iap.handle_continuous_param(\n            flake_size, \"flake_size\", value_range=(0.0+1e-4, 1.0))\n        self.flake_size_uniformity = iap.handle_continuous_param(\n            flake_size_uniformity, \"flake_size_uniformity\",\n            value_range=(0.0, 1.0))\n        self.angle = iap.handle_continuous_param(angle, \"angle\")\n        self.speed = iap.handle_continuous_param(\n            speed, \"speed\", value_range=(0.0, 1.0))\n        self.blur_sigma_fraction = iap.handle_continuous_param(\n            blur_sigma_fraction, \"blur_sigma_fraction\", value_range=(0.0, 1.0))\n\n        # (min, max), same for all images\n        self.blur_sigma_limits = blur_sigma_limits\n\n        # (height, width), same for all images\n        self.gate_noise_size = (8, 8)\n\n    # Added in 0.4.0.\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        if batch.images is None:\n            return batch\n\n        images = batch.images\n\n        rss = random_state.duplicate(len(images))\n        for i, (image, rs) in enumerate(zip(images, rss)):\n            batch.images[i] = self.draw_on_image(image, rs)\n        return batch\n\n    def get_parameters(self):\n        \"\"\"See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`.\"\"\"\n        return [self.density,\n                self.density_uniformity,\n                self.flake_size,\n                self.flake_size_uniformity,\n                self.angle,\n                self.speed,\n                self.blur_sigma_fraction,\n                self.blur_sigma_limits,\n                self.gate_noise_size]\n\n    def draw_on_image(self, image, random_state):\n        assert image.ndim == 3, (\n            \"Expected input image to be three-dimensional, \"\n            \"got %d dimensions.\" % (image.ndim,))\n        assert image.shape[2] in [1, 3], (\n            \"Expected to get image with a channel axis of size 1 or 3, \"\n            \"got %d (shape: %s)\" % (image.shape[2], image.shape))\n\n        rss = random_state.duplicate(2)\n\n        flake_size_sample = self.flake_size.draw_sample(random_state)\n        flake_size_uniformity_sample = self.flake_size_uniformity.draw_sample(\n            random_state)\n        angle_sample = self.angle.draw_sample(random_state)\n        speed_sample = self.speed.draw_sample(random_state)\n        blur_sigma_fraction_sample = self.blur_sigma_fraction.draw_sample(\n            random_state)\n\n        height, width, nb_channels = image.shape\n        downscale_factor = np.clip(1.0 - flake_size_sample, 0.001, 1.0)\n        height_down = max(1, int(height*downscale_factor))\n        width_down = max(1, int(width*downscale_factor))\n        noise = self._generate_noise(\n            height_down,\n            width_down,\n            self.density,\n            rss[0]\n        )\n\n        # gate the sampled noise via noise in range [0.0, 1.0]\n        # this leads to less flakes in some areas of the image and more in\n        # other areas\n        gate_noise = iap.Beta(1.0, 1.0 - self.density_uniformity)\n        noise = self._gate(noise, gate_noise, self.gate_noise_size, rss[1])\n        noise = ia.imresize_single_image(noise, (height, width),\n                                         interpolation=\"cubic\")\n\n        # apply a bit of gaussian blur and then motion blur according to\n        # angle and speed\n        sigma = max(height, width) * blur_sigma_fraction_sample\n        sigma = np.clip(sigma,\n                        self.blur_sigma_limits[0], self.blur_sigma_limits[1])\n        noise_small_blur = self._blur(noise, sigma)\n        noise_small_blur = self._motion_blur(noise_small_blur,\n                                             angle=angle_sample,\n                                             speed=speed_sample,\n                                             random_state=random_state)\n\n        noise_small_blur_rgb = self._postprocess_noise(\n            noise_small_blur, flake_size_uniformity_sample, nb_channels)\n\n        return self._blend(image, speed_sample, noise_small_blur_rgb)\n\n    @classmethod\n    def _generate_noise(cls, height, width, density, random_state):\n        noise = arithmetic.Salt(p=density, random_state=random_state)\n        return noise.augment_image(np.zeros((height, width), dtype=np.uint8))\n\n    @classmethod\n    def _gate(cls, noise, gate_noise, gate_size, random_state):\n        # the beta distribution here has most of its weight around 1.0 and\n        # will only rarely sample values around 0.0 the average of the\n        # sampled values seems to be at around 0.6-0.75\n        gate_noise = gate_noise.draw_samples(gate_size, random_state)\n        gate_noise_up = ia.imresize_single_image(gate_noise, noise.shape[0:2],\n                                                 interpolation=\"cubic\")\n        gate_noise_up = np.clip(gate_noise_up, 0.0, 1.0)\n        return np.clip(\n            noise.astype(np.float32) * gate_noise_up, 0, 255\n        ).astype(np.uint8)\n\n    @classmethod\n    def _blur(cls, noise, sigma):\n        return blur.blur_gaussian_(noise, sigma=sigma)\n\n    @classmethod\n    def _motion_blur(cls, noise, angle, speed, random_state):\n        size = max(noise.shape[0:2])\n        k = int(speed * size)\n        if k <= 1:\n            return noise\n\n        # we use max(k, 3) here because MotionBlur errors for anything less\n        # than 3\n        blurer = blur.MotionBlur(\n            k=max(k, 3), angle=angle, direction=1.0, random_state=random_state)\n        return blurer.augment_image(noise)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _postprocess_noise(cls, noise_small_blur,\n                           flake_size_uniformity_sample, nb_channels):\n        # use contrast adjustment of noise to make the flake size a bit less\n        # uniform then readjust the noise values to make them more visible\n        # again\n        gain = 1.0 + 2*(1 - flake_size_uniformity_sample)\n        gain_adj = 1.0 + 5*(1 - flake_size_uniformity_sample)\n        noise_small_blur = contrast.GammaContrast(gain).augment_image(\n            noise_small_blur)\n        noise_small_blur = noise_small_blur.astype(np.float32) * gain_adj\n        noise_small_blur_rgb = np.tile(\n            noise_small_blur[..., np.newaxis], (1, 1, nb_channels))\n        return noise_small_blur_rgb\n\n    # Added in 0.4.0.\n    @classmethod\n    def _blend(cls, image, speed_sample, noise_small_blur_rgb):\n        # blend:\n        # sum for a bit of glowy, hardly visible flakes\n        # max for the main flakes\n        image_f32 = image.astype(np.float32)\n        image_f32 = cls._blend_by_sum(\n            image_f32, (0.1 + 20*speed_sample) * noise_small_blur_rgb)\n        image_f32 = cls._blend_by_max(\n            image_f32, (1.0 + 20*speed_sample) * noise_small_blur_rgb)\n        return image_f32\n\n    # TODO replace this by a function from module blend.py\n    @classmethod\n    def _blend_by_sum(cls, image_f32, noise_small_blur_rgb):\n        image_f32 = image_f32 + noise_small_blur_rgb\n        return np.clip(image_f32, 0, 255).astype(np.uint8)\n\n    # TODO replace this by a function from module blend.py\n    @classmethod\n    def _blend_by_max(cls, image_f32, noise_small_blur_rgb):\n        image_f32 = np.maximum(image_f32, noise_small_blur_rgb)\n        return np.clip(image_f32, 0, 255).astype(np.uint8)\n\n\nclass Snowflakes(meta.SomeOf):\n    \"\"\"Add falling snowflakes to images.\n\n    This is a wrapper around\n    :class:`~imgaug.augmenters.weather.SnowflakesLayer`. It executes 1 to 3\n    layers per image.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; tested\n        * ``uint16``: no (1)\n        * ``uint32``: no (1)\n        * ``uint64``: no (1)\n        * ``int8``: no (1)\n        * ``int16``: no (1)\n        * ``int32``: no (1)\n        * ``int64``: no (1)\n        * ``float16``: no (1)\n        * ``float32``: no (1)\n        * ``float64``: no (1)\n        * ``float128``: no (1)\n        * ``bool``: no (1)\n\n        - (1) Parameters of this augmenter are optimized for the value range\n              of ``uint8``. While other dtypes may be accepted, they will lead\n              to images augmented in ways inappropriate for the respective\n              dtype.\n\n    Parameters\n    ----------\n    density : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Density of the snowflake layer, as a probability of each pixel in\n        low resolution space to be a snowflake.\n        Valid values are in the interval ``[0.0, 1.0]``.\n        Recommended to be in the interval ``[0.01, 0.075]``.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    density_uniformity : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Size uniformity of the snowflakes. Higher values denote more\n        similarly sized snowflakes.\n        Valid values are in the interval ``[0.0, 1.0]``.\n        Recommended to be around ``0.5``.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    flake_size : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Size of the snowflakes. This parameter controls the resolution at\n        which snowflakes are sampled. Higher values mean that the resolution\n        is closer to the input image's resolution and hence each sampled\n        snowflake will be smaller (because of the smaller pixel size).\n\n        Valid values are in the interval ``(0.0, 1.0]``.\n        Recommended values:\n\n            * On ``96x128`` a value of ``(0.1, 0.4)`` worked well.\n            * On ``192x256`` a value of ``(0.2, 0.7)`` worked well.\n            * On ``960x1280`` a value of ``(0.7, 0.95)`` worked well.\n\n        Datatype behaviour:\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    flake_size_uniformity : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Controls the size uniformity of the snowflakes. Higher values mean\n        that the snowflakes are more similarly sized.\n        Valid values are in the interval ``[0.0, 1.0]``.\n        Recommended to be around ``0.5``.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    angle : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Angle in degrees of motion blur applied to the snowflakes, where\n        ``0.0`` is motion blur that points straight upwards.\n        Recommended to be in the interval ``[-30, 30]``.\n        See also :func:`~imgaug.augmenters.blur.MotionBlur.__init__`.\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    speed : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Perceived falling speed of the snowflakes. This parameter controls the\n        motion blur's kernel size. It follows roughly the form\n        ``kernel_size = image_size * speed``. Hence, values around ``1.0``\n        denote that the motion blur should \"stretch\" each snowflake over\n        the whole image.\n\n        Valid values are in the interval ``[0.0, 1.0]``.\n        Recommended values:\n\n            * On ``96x128`` a value of ``(0.01, 0.05)`` worked well.\n            * On ``192x256`` a value of ``(0.007, 0.03)`` worked well.\n            * On ``960x1280`` a value of ``(0.001, 0.03)`` worked well.\n\n        Datatype behaviour:\n\n            * If a ``number``, then that value will always be used.\n            * If a ``tuple`` ``(a, b)``, then a value will be uniformly sampled\n              per image from the interval ``[a, b]``.\n            * If a ``list``, then a random value will be sampled from that\n              ``list`` per image.\n            * If a ``StochasticParameter``, then a value will be sampled\n              per image from that parameter.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Snowflakes(flake_size=(0.1, 0.4), speed=(0.01, 0.05))\n\n    Add snowflakes to small images (around ``96x128``).\n\n    >>> aug = iaa.Snowflakes(flake_size=(0.2, 0.7), speed=(0.007, 0.03))\n\n    Add snowflakes to medium-sized images (around ``192x256``).\n\n    >>> aug = iaa.Snowflakes(flake_size=(0.7, 0.95), speed=(0.001, 0.03))\n\n    Add snowflakes to large images (around ``960x1280``).\n\n    \"\"\"\n\n    def __init__(self, density=(0.005, 0.075), density_uniformity=(0.3, 0.9),\n                 flake_size=(0.2, 0.7), flake_size_uniformity=(0.4, 0.8),\n                 angle=(-30, 30), speed=(0.007, 0.03),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        layer = SnowflakesLayer(\n            density=density,\n            density_uniformity=density_uniformity,\n            flake_size=flake_size,\n            flake_size_uniformity=flake_size_uniformity,\n            angle=angle,\n            speed=speed,\n            blur_sigma_fraction=(0.0001, 0.001),\n            seed=seed,\n            random_state=random_state,\n            deterministic=deterministic\n        )\n\n        super(Snowflakes, self).__init__(\n            (1, 3),\n            children=[layer.deepcopy() for _ in range(3)],\n            random_order=False,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n\nclass RainLayer(SnowflakesLayer):\n    \"\"\"Add a single layer of falling raindrops to images.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; indirectly tested (1)\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n        - (1) indirectly tested via tests for :class:`Rain`\n\n    Parameters\n    ----------\n    density : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Same as in :class:`~imgaug.augmenters.weather.SnowflakesLayer`.\n\n    density_uniformity : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Same as in :class:`~imgaug.augmenters.weather.SnowflakesLayer`.\n\n    drop_size : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Same as `flake_size` in\n        :class:`~imgaug.augmenters.weather.SnowflakesLayer`.\n\n    drop_size_uniformity : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Same as `flake_size_uniformity` in\n        :class:`~imgaug.augmenters.weather.SnowflakesLayer`.\n\n    angle : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Same as in :class:`~imgaug.augmenters.weather.SnowflakesLayer`.\n\n    speed : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Same as in :class:`~imgaug.augmenters.weather.SnowflakesLayer`.\n\n    blur_sigma_fraction : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Same as in :class:`~imgaug.augmenters.weather.SnowflakesLayer`.\n\n    blur_sigma_limits : tuple of float, optional\n        Same as in :class:`~imgaug.augmenters.weather.SnowflakesLayer`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, density, density_uniformity, drop_size,\n                 drop_size_uniformity, angle, speed, blur_sigma_fraction,\n                 blur_sigma_limits=(0.5, 3.75),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        super(RainLayer, self).__init__(\n            density, density_uniformity, drop_size,\n            drop_size_uniformity, angle, speed, blur_sigma_fraction,\n            blur_sigma_limits=blur_sigma_limits,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n\n    # Added in 0.4.0.\n    @classmethod\n    def _blur(cls, noise, sigma):\n        return noise\n\n    # Added in 0.4.0.\n    @classmethod\n    def _postprocess_noise(cls, noise_small_blur,\n                           flake_size_uniformity_sample, nb_channels):\n        noise_small_blur_rgb = np.tile(\n            noise_small_blur[..., np.newaxis], (1, 1, nb_channels))\n        return noise_small_blur_rgb\n\n    # Added in 0.4.0.\n    @classmethod\n    def _blend(cls, image, speed_sample, noise_small_blur_rgb):\n        # We set the mean color based on the noise here. That's a pseudo-random\n        # approach that saves us from adding the random state as a parameter.\n        # Note that the sum of noise_small_blur_rgb can be 0 when at least one\n        # image axis size is 0.\n        noise_sum = np.sum(noise_small_blur_rgb.flat[0:1000])\n        noise_sum = noise_sum if noise_sum > 0 else 1\n        drop_mean_color = 110 + (240 - 110) % noise_sum\n        noise_small_blur_rgb = noise_small_blur_rgb / 255.0\n        # The 1.3 multiplier increases the visibility of drops a bit.\n        noise_small_blur_rgb = np.clip(1.3 * noise_small_blur_rgb, 0, 1.0)\n        image_f32 = image.astype(np.float32)\n        image_f32 = (\n            (1 - noise_small_blur_rgb) * image_f32\n            + noise_small_blur_rgb * drop_mean_color\n        )\n        return np.clip(image_f32, 0, 255).astype(np.uint8)\n\n\nclass Rain(meta.SomeOf):\n    \"\"\"Add falling snowflakes to images.\n\n    This is a wrapper around\n    :class:`~imgaug.augmenters.weather.RainLayer`. It executes 1 to 3\n    layers per image.\n\n    .. note::\n\n        This augmenter currently seems to work best for medium-sized images\n        around ``192x256``. For smaller images, you may want to increase the\n        `speed` value to e.g. ``(0.1, 0.3)``, otherwise the drops tend to\n        look like snowflakes. For larger images, you may want to increase\n        the `drop_size` to e.g. ``(0.10, 0.20)``.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; tested\n        * ``uint16``: no (1)\n        * ``uint32``: no (1)\n        * ``uint64``: no (1)\n        * ``int8``: no (1)\n        * ``int16``: no (1)\n        * ``int32``: no (1)\n        * ``int64``: no (1)\n        * ``float16``: no (1)\n        * ``float32``: no (1)\n        * ``float64``: no (1)\n        * ``float128``: no (1)\n        * ``bool``: no (1)\n\n        - (1) Parameters of this augmenter are optimized for the value range\n              of ``uint8``. While other dtypes may be accepted, they will lead\n              to images augmented in ways inappropriate for the respective\n              dtype.\n\n    Parameters\n    ----------\n    drop_size : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        See :class:`~imgaug.augmenters.weather.RainLayer`.\n\n    speed : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        See :class:`~imgaug.augmenters.weather.RainLayer`.\n\n    seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    name : None or str, optional\n        See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.\n\n    random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n        Old name for parameter `seed`.\n        Its usage will not yet cause a deprecation warning,\n        but it is still recommended to use `seed` now.\n        Outdated since 0.4.0.\n\n    deterministic : bool, optional\n        Deprecated since 0.4.0.\n        See method ``to_deterministic()`` for an alternative and for\n        details about what the \"deterministic mode\" actually does.\n\n    Examples\n    --------\n    >>> import imgaug.augmenters as iaa\n    >>> aug = iaa.Rain(speed=(0.1, 0.3))\n\n    Add rain to small images (around ``96x128``).\n\n    >>> aug = iaa.Rain()\n\n    Add rain to medium sized images (around ``192x256``).\n\n    >>> aug = iaa.Rain(drop_size=(0.10, 0.20))\n\n    Add rain to large images (around ``960x1280``).\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, nb_iterations=(1, 3),\n                 drop_size=(0.01, 0.02),\n                 speed=(0.04, 0.20),\n                 seed=None, name=None,\n                 random_state=\"deprecated\", deterministic=\"deprecated\"):\n        layer = RainLayer(\n            density=(0.03, 0.14),\n            density_uniformity=(0.8, 1.0),\n            drop_size=drop_size,\n            drop_size_uniformity=(0.2, 0.5),\n            angle=(-15, 15),\n            speed=speed,\n            blur_sigma_fraction=(0.001, 0.001),\n            seed=seed,\n            random_state=random_state,\n            deterministic=deterministic\n        )\n\n        super(Rain, self).__init__(\n            nb_iterations,\n            children=[layer.deepcopy() for _ in range(3)],\n            random_order=False,\n            seed=seed, name=name,\n            random_state=random_state, deterministic=deterministic)\n"
  },
  {
    "path": "imgaug/data.py",
    "content": "\"\"\"Functions to generate example data, e.g. example images or segmaps.\n\nAdded in 0.5.0.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport os\nimport json\n\nimport imageio\nimport numpy as np\n\n# filepath to the quokka image, its annotations and depth map\n# Added in 0.5.0.\n_FILE_DIR = os.path.dirname(os.path.abspath(__file__))\n# Added in 0.5.0.\n_QUOKKA_FP = os.path.join(_FILE_DIR, \"quokka.jpg\")\n# Added in 0.5.0.\n_QUOKKA_ANNOTATIONS_FP = os.path.join(_FILE_DIR, \"quokka_annotations.json\")\n# Added in 0.5.0.\n_QUOKKA_DEPTH_MAP_HALFRES_FP = os.path.join(\n    _FILE_DIR, \"quokka_depth_map_halfres.png\")\n\n\ndef _quokka_normalize_extract(extract):\n    \"\"\"Generate a normalized rectangle for the standard quokka image.\n\n    Added in 0.5.0. (Moved from ``imgaug.imgaug``.)\n\n    Parameters\n    ----------\n    extract : 'square' or tuple of number or imgaug.augmentables.bbs.BoundingBox or imgaug.augmentables.bbs.BoundingBoxesOnImage\n        Unnormalized representation of the image subarea to be extracted.\n\n            * If ``str`` ``square``, then a squared area\n              ``(x: 0 to max 643, y: 0 to max 643)`` will be extracted from\n              the image.\n            * If a ``tuple``, then expected to contain four ``number`` s\n              denoting ``(x1, y1, x2, y2)``.\n            * If a :class:`~imgaug.augmentables.bbs.BoundingBox`, then that\n              bounding box's area will be extracted from the image.\n            * If a :class:`~imgaug.augmentables.bbs.BoundingBoxesOnImage`,\n              then expected to contain exactly one bounding box and a shape\n              matching the full image dimensions (i.e. ``(643, 960, *)``).\n              Then the one bounding box will be used similar to\n              ``BoundingBox`` above.\n\n    Returns\n    -------\n    imgaug.augmentables.bbs.BoundingBox\n        Normalized representation of the area to extract from the standard\n        quokka image.\n\n    \"\"\"\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage\n\n    if extract == \"square\":\n        bb = BoundingBox(x1=0, y1=0, x2=643, y2=643)\n    elif isinstance(extract, tuple) and len(extract) == 4:\n        bb = BoundingBox(x1=extract[0], y1=extract[1],\n                         x2=extract[2], y2=extract[3])\n    elif isinstance(extract, BoundingBox):\n        bb = extract\n    elif isinstance(extract, BoundingBoxesOnImage):\n        assert len(extract.bounding_boxes) == 1, (\n            \"Provided BoundingBoxesOnImage instance may currently only \"\n            \"contain a single bounding box.\")\n        assert extract.shape[0:2] == (643, 960), (\n            \"Expected BoundingBoxesOnImage instance on an image of shape \"\n            \"(643, 960, ?). Got shape %s.\" % (extract.shape,))\n        bb = extract.bounding_boxes[0]\n    else:\n        raise Exception(\n            \"Expected 'square' or tuple of four entries or BoundingBox or \"\n            \"BoundingBoxesOnImage for parameter 'extract', \"\n            \"got %s.\" % (type(extract),)\n        )\n    return bb\n\n\n# TODO is this the same as the project functions in augmentables?\ndef _compute_resized_shape(from_shape, to_shape):\n    \"\"\"Compute the intended new shape of an image-like array after resizing.\n\n    Added in 0.5.0. (Moved from ``imgaug.imgaug``.)\n\n    Parameters\n    ----------\n    from_shape : tuple or ndarray\n        Old shape of the array. Usually expected to be a ``tuple`` of form\n        ``(H, W)`` or ``(H, W, C)`` or alternatively an array with two or\n        three dimensions.\n\n    to_shape : None or tuple of ints or tuple of floats or int or float or ndarray\n        New shape of the array.\n\n            * If ``None``, then `from_shape` will be used as the new shape.\n            * If an ``int`` ``V``, then the new shape will be ``(V, V, [C])``,\n              where ``C`` will be added if it is part of `from_shape`.\n            * If a ``float`` ``V``, then the new shape will be\n              ``(H*V, W*V, [C])``, where ``H`` and ``W`` are the old\n              height/width.\n            * If a ``tuple`` ``(H', W', [C'])`` of ints, then ``H'`` and ``W'``\n              will be used as the new height and width.\n            * If a ``tuple`` ``(H', W', [C'])`` of floats (except ``C``), then\n              ``H'`` and ``W'`` will be used as the new height and width.\n            * If a numpy array, then the array's shape will be used.\n\n    Returns\n    -------\n    tuple of int\n        New shape.\n\n    \"\"\"\n    from . import imgaug as ia\n\n    if ia.is_np_array(from_shape):\n        from_shape = from_shape.shape\n    if ia.is_np_array(to_shape):\n        to_shape = to_shape.shape\n\n    to_shape_computed = list(from_shape)\n\n    if to_shape is None:\n        pass\n    elif isinstance(to_shape, tuple):\n        assert len(from_shape) in [2, 3]\n        assert len(to_shape) in [2, 3]\n\n        if len(from_shape) == 3 and len(to_shape) == 3:\n            assert from_shape[2] == to_shape[2]\n        elif len(to_shape) == 3:\n            to_shape_computed.append(to_shape[2])\n\n        is_to_s_valid_values = all(\n            [v is None or ia.is_single_number(v) for v in to_shape[0:2]])\n        assert is_to_s_valid_values, (\n            \"Expected the first two entries in to_shape to be None or \"\n            \"numbers, got types %s.\" % (\n                str([type(v) for v in to_shape[0:2]]),))\n\n        for i, from_shape_i in enumerate(from_shape[0:2]):\n            if to_shape[i] is None:\n                to_shape_computed[i] = from_shape_i\n            elif ia.is_single_integer(to_shape[i]):\n                to_shape_computed[i] = to_shape[i]\n            else:  # float\n                to_shape_computed[i] = int(np.round(from_shape_i * to_shape[i]))\n    elif ia.is_single_integer(to_shape) or ia.is_single_float(to_shape):\n        to_shape_computed = _compute_resized_shape(\n            from_shape, (to_shape, to_shape))\n    else:\n        raise Exception(\n            \"Expected to_shape to be None or ndarray or tuple of floats or \"\n            \"tuple of ints or single int or single float, \"\n            \"got %s.\" % (type(to_shape),))\n\n    return tuple(to_shape_computed)\n\n\ndef quokka(size=None, extract=None):\n    \"\"\"Return an image of a quokka as a numpy array.\n\n    Added in 0.5.0. (Moved from ``imgaug.imgaug``.)\n\n    Parameters\n    ----------\n    size : None or float or tuple of int, optional\n        Size of the output image. Input into\n        :func:`~imgaug.imgaug.imresize_single_image`. Usually expected to be a\n        ``tuple`` ``(H, W)``, where ``H`` is the desired height and ``W`` is\n        the width. If ``None``, then the image will not be resized.\n\n    extract : None or 'square' or tuple of number or imgaug.augmentables.bbs.BoundingBox or imgaug.augmentables.bbs.BoundingBoxesOnImage\n        Subarea of the quokka image to extract:\n\n            * If ``None``, then the whole image will be used.\n            * If ``str`` ``square``, then a squared area\n              ``(x: 0 to max 643, y: 0 to max 643)`` will be extracted from\n              the image.\n            * If a ``tuple``, then expected to contain four ``number`` s\n              denoting ``(x1, y1, x2, y2)``.\n            * If a :class:`~imgaug.augmentables.bbs.BoundingBox`, then that\n              bounding box's area will be extracted from the image.\n            * If a :class:`~imgaug.augmentables.bbs.BoundingBoxesOnImage`,\n              then expected to contain exactly one bounding box and a shape\n              matching the full image dimensions (i.e. ``(643, 960, *)``).\n              Then the one bounding box will be used similar to\n              ``BoundingBox`` above.\n\n    Returns\n    -------\n    (H,W,3) ndarray\n        The image array of dtype ``uint8``.\n\n    \"\"\"\n    from . import imgaug as ia\n\n    img = imageio.imread(_QUOKKA_FP, pilmode=\"RGB\")\n    if extract is not None:\n        bb = _quokka_normalize_extract(extract)\n        img = bb.extract_from_image(img)\n    if size is not None:\n        shape_resized = _compute_resized_shape(img.shape, size)\n        img = ia.imresize_single_image(img, shape_resized[0:2])\n    return img\n\n\ndef quokka_square(size=None):\n    \"\"\"Return an (square) image of a quokka as a numpy array.\n\n    Added in 0.5.0. (Moved from ``imgaug.imgaug``.)\n\n    Parameters\n    ----------\n    size : None or float or tuple of int, optional\n        Size of the output image. Input into\n        :func:`~imgaug.imgaug.imresize_single_image`. Usually expected to be a\n        ``tuple`` ``(H, W)``, where ``H`` is the desired height and ``W`` is\n        the width. If ``None``, then the image will not be resized.\n\n    Returns\n    -------\n    (H,W,3) ndarray\n        The image array of dtype ``uint8``.\n\n    \"\"\"\n    return quokka(size=size, extract=\"square\")\n\n\ndef quokka_heatmap(size=None, extract=None):\n    \"\"\"Return a heatmap (here: depth map) for the standard example quokka image.\n\n    Added in 0.5.0. (Moved from ``imgaug.imgaug``.)\n\n    Parameters\n    ----------\n    size : None or float or tuple of int, optional\n        See :func:`~imgaug.imgaug.quokka`.\n\n    extract : None or 'square' or tuple of number or imgaug.augmentables.bbs.BoundingBox or imgaug.augmentables.bbs.BoundingBoxesOnImage\n        See :func:`~imgaug.imgaug.quokka`.\n\n    Returns\n    -------\n    imgaug.augmentables.heatmaps.HeatmapsOnImage\n        Depth map as an heatmap object. Values close to ``0.0`` denote objects\n        that are close to the camera. Values close to ``1.0`` denote objects\n        that are furthest away (among all shown objects).\n\n    \"\"\"\n    # TODO get rid of this deferred import\n    from . import imgaug as ia\n    from imgaug.augmentables.heatmaps import HeatmapsOnImage\n\n    img = imageio.imread(_QUOKKA_DEPTH_MAP_HALFRES_FP, pilmode=\"RGB\")\n    img = ia.imresize_single_image(img, (643, 960), interpolation=\"cubic\")\n\n    if extract is not None:\n        bb = _quokka_normalize_extract(extract)\n        img = bb.extract_from_image(img)\n    if size is None:\n        size = img.shape[0:2]\n\n    shape_resized = _compute_resized_shape(img.shape, size)\n    img = ia.imresize_single_image(img, shape_resized[0:2])\n    img_0to1 = img[..., 0]  # depth map was saved as 3-channel RGB\n    img_0to1 = img_0to1.astype(np.float32) / 255.0\n    img_0to1 = 1 - img_0to1  # depth map was saved as 0 being furthest away\n\n    return HeatmapsOnImage(img_0to1, shape=img_0to1.shape[0:2] + (3,))\n\n\ndef quokka_segmentation_map(size=None, extract=None):\n    \"\"\"Return a segmentation map for the standard example quokka image.\n\n    Added in 0.5.0. (Moved from ``imgaug.imgaug``.)\n\n    Parameters\n    ----------\n    size : None or float or tuple of int, optional\n        See :func:`~imgaug.imgaug.quokka`.\n\n    extract : None or 'square' or tuple of number or imgaug.augmentables.bbs.BoundingBox or imgaug.augmentables.bbs.BoundingBoxesOnImage\n        See :func:`~imgaug.imgaug.quokka`.\n\n    Returns\n    -------\n    imgaug.augmentables.segmaps.SegmentationMapsOnImage\n        Segmentation map object.\n\n    \"\"\"\n    # pylint: disable=invalid-name\n    import skimage.draw\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.segmaps import SegmentationMapsOnImage\n\n    with open(_QUOKKA_ANNOTATIONS_FP, \"r\") as f:\n        json_dict = json.load(f)\n\n    xx = []\n    yy = []\n    for kp_dict in json_dict[\"polygons\"][0][\"keypoints\"]:\n        x = kp_dict[\"x\"]\n        y = kp_dict[\"y\"]\n        xx.append(x)\n        yy.append(y)\n\n    img_seg = np.zeros((643, 960, 1), dtype=np.int32)\n    rr, cc = skimage.draw.polygon(\n        np.array(yy), np.array(xx), shape=img_seg.shape)\n    img_seg[rr, cc, 0] = 1\n\n    if extract is not None:\n        bb = _quokka_normalize_extract(extract)\n        img_seg = bb.extract_from_image(img_seg)\n\n    segmap = SegmentationMapsOnImage(img_seg, shape=img_seg.shape[0:2] + (3,))\n\n    if size is not None:\n        shape_resized = _compute_resized_shape(img_seg.shape, size)\n        segmap = segmap.resize(shape_resized[0:2])\n        segmap.shape = tuple(shape_resized[0:2]) + (3,)\n\n    return segmap\n\n\ndef quokka_keypoints(size=None, extract=None):\n    \"\"\"Return example keypoints on the standard example quokke image.\n\n    The keypoints cover the eyes, ears, nose and paws.\n\n    Added in 0.5.0. (Moved from ``imgaug.imgaug``.)\n\n    Parameters\n    ----------\n    size : None or float or tuple of int or tuple of float, optional\n        Size of the output image on which the keypoints are placed. If\n        ``None``, then the keypoints are not projected to any new size\n        (positions on the original image are used). ``float`` s lead to\n        relative size changes, ``int`` s to absolute sizes in pixels.\n\n    extract : None or 'square' or tuple of number or imgaug.augmentables.bbs.BoundingBox or imgaug.augmentables.bbs.BoundingBoxesOnImage\n        Subarea to extract from the image. See :func:`~imgaug.imgaug.quokka`.\n\n    Returns\n    -------\n    imgaug.augmentables.kps.KeypointsOnImage\n        Example keypoints on the quokka image.\n\n    \"\"\"\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.kps import Keypoint, KeypointsOnImage\n\n    left, top = 0, 0\n    if extract is not None:\n        bb_extract = _quokka_normalize_extract(extract)\n        left = bb_extract.x1\n        top = bb_extract.y1\n    with open(_QUOKKA_ANNOTATIONS_FP, \"r\") as f:\n        json_dict = json.load(f)\n    keypoints = []\n    for kp_dict in json_dict[\"keypoints\"]:\n        keypoints.append(Keypoint(x=kp_dict[\"x\"] - left, y=kp_dict[\"y\"] - top))\n    if extract is not None:\n        shape = (bb_extract.height, bb_extract.width, 3)\n    else:\n        shape = (643, 960, 3)\n    kpsoi = KeypointsOnImage(keypoints, shape=shape)\n    if size is not None:\n        shape_resized = _compute_resized_shape(shape, size)\n        kpsoi = kpsoi.on(shape_resized)\n    return kpsoi\n\n\ndef quokka_bounding_boxes(size=None, extract=None):\n    \"\"\"Return example bounding boxes on the standard example quokke image.\n\n    Currently only a single bounding box is returned that covers the quokka.\n\n    Added in 0.5.0. (Moved from ``imgaug.imgaug``.)\n\n    Parameters\n    ----------\n    size : None or float or tuple of int or tuple of float, optional\n        Size of the output image on which the BBs are placed. If ``None``, then\n        the BBs are not projected to any new size (positions on the original\n        image are used). ``float`` s lead to relative size changes, ``int`` s\n        to absolute sizes in pixels.\n\n    extract : None or 'square' or tuple of number or imgaug.augmentables.bbs.BoundingBox or imgaug.augmentables.bbs.BoundingBoxesOnImage\n        Subarea to extract from the image. See :func:`~imgaug.imgaug.quokka`.\n\n    Returns\n    -------\n    imgaug.augmentables.bbs.BoundingBoxesOnImage\n        Example BBs on the quokka image.\n\n    \"\"\"\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage\n\n    left, top = 0, 0\n    if extract is not None:\n        bb_extract = _quokka_normalize_extract(extract)\n        left = bb_extract.x1\n        top = bb_extract.y1\n    with open(_QUOKKA_ANNOTATIONS_FP, \"r\") as f:\n        json_dict = json.load(f)\n    bbs = []\n    for bb_dict in json_dict[\"bounding_boxes\"]:\n        bbs.append(\n            BoundingBox(\n                x1=bb_dict[\"x1\"] - left,\n                y1=bb_dict[\"y1\"] - top,\n                x2=bb_dict[\"x2\"] - left,\n                y2=bb_dict[\"y2\"] - top\n            )\n        )\n    if extract is not None:\n        shape = (bb_extract.height, bb_extract.width, 3)\n    else:\n        shape = (643, 960, 3)\n    bbsoi = BoundingBoxesOnImage(bbs, shape=shape)\n    if size is not None:\n        shape_resized = _compute_resized_shape(shape, size)\n        bbsoi = bbsoi.on(shape_resized)\n    return bbsoi\n\n\ndef quokka_polygons(size=None, extract=None):\n    \"\"\"\n    Returns example polygons on the standard example quokke image.\n\n    The result contains one polygon, covering the quokka's outline.\n\n    Added in 0.5.0. (Moved from ``imgaug.imgaug``.)\n\n    Parameters\n    ----------\n    size : None or float or tuple of int or tuple of float, optional\n        Size of the output image on which the polygons are placed. If ``None``,\n        then the polygons are not projected to any new size (positions on the\n        original image are used). ``float`` s lead to relative size changes,\n        ``int`` s to absolute sizes in pixels.\n\n    extract : None or 'square' or tuple of number or imgaug.augmentables.bbs.BoundingBox or imgaug.augmentables.bbs.BoundingBoxesOnImage\n        Subarea to extract from the image. See :func:`~imgaug.imgaug.quokka`.\n\n    Returns\n    -------\n    imgaug.augmentables.polys.PolygonsOnImage\n        Example polygons on the quokka image.\n\n    \"\"\"\n    # TODO get rid of this deferred import\n    from imgaug.augmentables.polys import Polygon, PolygonsOnImage\n\n    left, top = 0, 0\n    if extract is not None:\n        bb_extract = _quokka_normalize_extract(extract)\n        left = bb_extract.x1\n        top = bb_extract.y1\n    with open(_QUOKKA_ANNOTATIONS_FP, \"r\") as f:\n        json_dict = json.load(f)\n    polygons = []\n    for poly_json in json_dict[\"polygons\"]:\n        polygons.append(\n            Polygon([(point[\"x\"] - left, point[\"y\"] - top)\n                     for point in poly_json[\"keypoints\"]])\n        )\n    if extract is not None:\n        shape = (bb_extract.height, bb_extract.width, 3)\n    else:\n        shape = (643, 960, 3)\n    psoi = PolygonsOnImage(polygons, shape=shape)\n    if size is not None:\n        shape_resized = _compute_resized_shape(shape, size)\n        psoi = psoi.on(shape_resized)\n    return psoi\n"
  },
  {
    "path": "imgaug/dtypes.py",
    "content": "\"\"\"Functions to interact/analyze with numpy dtypes.\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport numpy as np\nimport six.moves as sm\n\nimport imgaug as ia\n\nKIND_TO_DTYPES = {\n    \"i\": [\"int8\", \"int16\", \"int32\", \"int64\"],\n    \"u\": [\"uint8\", \"uint16\", \"uint32\", \"uint64\"],\n    \"b\": [\"bool\"],\n    \"f\": [\"float16\", \"float32\", \"float64\", \"float128\"]\n}\n\n# Added in 0.5.0.\n_DTYPE_STR_TO_DTYPES_CACHE = dict()\n\n_UINT8_DTYPE = np.dtype(\"uint8\")  # Added in 0.5.0.\n_UINT16_DTYPE = np.dtype(\"uint16\")  # Added in 0.5.0.\n_UINT32_DTYPE = np.dtype(\"uint32\")  # Added in 0.5.0.\n_UINT64_DTYPE = np.dtype(\"uint64\")  # Added in 0.5.0.\n_INT8_DTYPE = np.dtype(\"int8\")  # Added in 0.5.0.\n_INT16_DTYPE = np.dtype(\"int16\")  # Added in 0.5.0.\n_INT32_DTYPE = np.dtype(\"int32\")  # Added in 0.5.0.\n_INT64_DTYPE = np.dtype(\"int64\")  # Added in 0.5.0.\n_FLOAT16_DTYPE = np.dtype(\"float16\")  # Added in 0.5.0.\n_FLOAT32_DTYPE = np.dtype(\"float32\")  # Added in 0.5.0.\n_FLOAT64_DTYPE = np.dtype(\"float64\")  # Added in 0.5.0.\n_BOOL_DTYPE = np.dtype(\"bool\")  # Added in 0.5.0.\n\n# Added in 0.5.0.\ntry:\n    _FLOAT128_DTYPE = np.dtype(\"float128\")\nexcept TypeError:\n    _FLOAT128_DTYPE = None\n\n# Added in 0.5.0.\n_DTYPE_NAME_TO_DTYPE = {\n    \"uint8\": _UINT8_DTYPE,\n    \"uint16\": _UINT16_DTYPE,\n    \"uint32\": _UINT32_DTYPE,\n    \"uint64\": _UINT64_DTYPE,\n    \"int8\": _INT8_DTYPE,\n    \"int16\": _INT16_DTYPE,\n    \"int32\": _INT32_DTYPE,\n    \"int64\": _INT64_DTYPE,\n    \"float16\": _FLOAT16_DTYPE,\n    \"float32\": _FLOAT32_DTYPE,\n    \"float64\": _FLOAT64_DTYPE,\n    \"float128\": _FLOAT128_DTYPE,\n    \"bool\": _BOOL_DTYPE\n}\n\n\ndef normalize_dtypes(dtypes):\n    if not isinstance(dtypes, list):\n        return [normalize_dtype(dtypes)]\n    return [normalize_dtype(dtype) for dtype in dtypes]\n\n\ndef normalize_dtype(dtype):\n    assert not isinstance(dtype, list), (\n        \"Expected a single dtype-like, got a list instead.\")\n    return (\n        dtype.dtype\n        if ia.is_np_array(dtype) or ia.is_np_scalar(dtype)\n        else np.dtype(dtype)\n    )\n\n\ndef change_dtype_(arr, dtype, clip=True, round=True):\n    # pylint: disable=redefined-builtin\n    assert ia.is_np_array(arr), (\n        \"Expected array as input, got type %s.\" % (type(arr),))\n    dtype = normalize_dtype(dtype)\n\n    if arr.dtype.name == dtype.name:\n        return arr\n\n    if round and arr.dtype.kind == \"f\" and dtype.kind in [\"u\", \"i\", \"b\"]:\n        arr = np.round(arr)\n\n    if clip:\n        min_value, _, max_value = get_value_range_of_dtype(dtype)\n        arr = clip_(arr, min_value, max_value)\n\n    return arr.astype(dtype, copy=False)\n\n\ndef change_dtypes_(images, dtypes, clip=True, round=True):\n    # pylint: disable=redefined-builtin\n    if ia.is_np_array(images):\n        if ia.is_iterable(dtypes):\n            dtypes = normalize_dtypes(dtypes)\n            n_distinct_dtypes = len({dt.name for dt in dtypes})\n            assert len(dtypes) == len(images), (\n                \"If an iterable of dtypes is provided to \"\n                \"change_dtypes_(), it must contain as many dtypes as \"\n                \"there are images. Got %d dtypes and %d images.\" % (\n                    len(dtypes), len(images))\n            )\n\n            assert n_distinct_dtypes == 1, (\n                \"If an image array is provided to change_dtypes_(), the \"\n                \"provided 'dtypes' argument must either be a single dtype \"\n                \"or an iterable of N times the *same* dtype for N images. \"\n                \"Got %d distinct dtypes.\" % (n_distinct_dtypes,)\n            )\n\n            dtype = dtypes[0]\n        else:\n            dtype = normalize_dtype(dtypes)\n\n        result = change_dtype_(images, dtype, clip=clip, round=round)\n    elif ia.is_iterable(images):\n        dtypes = (\n            [normalize_dtype(dtypes)] * len(images)\n            if not isinstance(dtypes, list)\n            else normalize_dtypes(dtypes)\n        )\n        assert len(images) == len(dtypes), (\n            \"Expected the provided images and dtypes to match, but got \"\n            \"iterables of size %d (images) %d (dtypes).\" % (\n                len(images), len(dtypes)))\n\n        result = images\n        for i, (image, dtype) in enumerate(zip(images, dtypes)):\n            assert ia.is_np_array(image), (\n                \"Expected each image to be an ndarray, got type %s \"\n                \"instead.\" % (type(image),))\n            result[i] = change_dtype_(image, dtype, clip=clip, round=round)\n    else:\n        raise Exception(\"Expected numpy array or iterable of numpy arrays, \"\n                        \"got type '%s'.\" % (type(images),))\n    return result\n\n\n# TODO replace this everywhere in the library with change_dtypes_\n# TODO mark as deprecated\ndef restore_dtypes_(images, dtypes, clip=True, round=True):\n    # pylint: disable=redefined-builtin\n    return change_dtypes_(images, dtypes, clip=clip, round=round)\n\n\ndef copy_dtypes_for_restore(images, force_list=False):\n    if ia.is_np_array(images):\n        if force_list:\n            return [images.dtype for _ in sm.xrange(len(images))]\n        return images.dtype\n    return [image.dtype for image in images]\n\n\ndef increase_itemsize_of_dtype(dtype, factor):\n    dtype = normalize_dtype(dtype)\n\n    assert ia.is_single_integer(factor), (\n        \"Expected 'factor' to be an integer, got type %s instead.\" % (\n            type(factor),))\n    # int8 -> int64 = factor 8\n    # uint8 -> uint64 = factor 8\n    # float16 -> float128 = factor 8\n    assert factor in [1, 2, 4, 8], (\n        \"The itemsize may only be increased any of the following factors: \"\n        \"1, 2, 4 or 8. Got factor %d.\" % (factor,))\n    assert dtype.kind != \"b\", \"Cannot increase the itemsize of boolean.\"\n\n    dt_high_name = \"%s%d\" % (dtype.kind, dtype.itemsize * factor)\n\n    try:\n        dt_high = np.dtype(dt_high_name)\n        return dt_high\n    except TypeError:\n        raise TypeError(\n            \"Unable to create a numpy dtype matching the name '%s'. \"\n            \"This error was caused when trying to find a dtype \"\n            \"that increases the itemsize of dtype '%s' by a factor of %d.\"\n            \"This error can be avoided by choosing arrays with lower \"\n            \"resolution dtypes as inputs, e.g. by reducing \"\n            \"float32 to float16.\" % (\n                dt_high_name,\n                dtype.name,\n                factor\n            )\n        )\n\n\ndef get_minimal_dtype(arrays, increase_itemsize_factor=1):\n    assert isinstance(arrays, list), (\n        \"Expected a list of arrays or dtypes, got type %s.\" % (type(arrays),))\n    assert len(arrays) > 0, (\n        \"Cannot estimate minimal dtype of an empty iterable.\")\n\n    input_dts = normalize_dtypes(arrays)\n\n    # This loop construct handles (1) list of a single dtype, (2) list of two\n    # dtypes and (3) list of 3+ dtypes. Note that promote_dtypes() always\n    # expects exactly two dtypes.\n    promoted_dt = input_dts[0]\n    input_dts = input_dts[1:]\n    while len(input_dts) >= 1:\n        promoted_dt = np.promote_types(promoted_dt, input_dts[0])\n        input_dts = input_dts[1:]\n\n    if increase_itemsize_factor > 1:\n        assert isinstance(promoted_dt, np.dtype), (\n            \"Expected numpy.dtype output from numpy.promote_dtypes, got type \"\n            \"%s.\" % (type(promoted_dt),))\n        return increase_itemsize_of_dtype(promoted_dt,\n                                          increase_itemsize_factor)\n    return promoted_dt\n\n\n# TODO rename to: promote_arrays_to_minimal_dtype_\ndef promote_array_dtypes_(arrays, dtypes=None, increase_itemsize_factor=1):\n    if dtypes is None:\n        dtypes = normalize_dtypes(arrays)\n    elif not isinstance(dtypes, list):\n        dtypes = [dtypes]\n    dtype = get_minimal_dtype(dtypes,\n                              increase_itemsize_factor=increase_itemsize_factor)\n    return change_dtypes_(arrays, dtype, clip=False, round=False)\n\n\ndef increase_array_resolutions_(arrays, factor):\n    dts = normalize_dtypes(arrays)\n    dts = [increase_itemsize_of_dtype(dt, factor) for dt in dts]\n    return change_dtypes_(arrays, dts, round=False, clip=False)\n\n\ndef get_value_range_of_dtype(dtype):\n    dtype = normalize_dtype(dtype)\n\n    if dtype.kind == \"f\":\n        finfo = np.finfo(dtype)\n        return finfo.min, 0.0, finfo.max\n    if dtype.kind == \"u\":\n        iinfo = np.iinfo(dtype)\n        return iinfo.min, iinfo.min + 0.5 * iinfo.max, iinfo.max\n    if dtype.kind == \"i\":\n        iinfo = np.iinfo(dtype)\n        return iinfo.min, -0.5, iinfo.max\n    if dtype.kind == \"b\":\n        return 0, None, 1\n\n    raise Exception(\"Cannot estimate value range of dtype '%s' \"\n                    \"(type: %s)\" % (str(dtype), type(dtype)))\n\n\n# TODO call this function wherever data is clipped\ndef clip_(array, min_value, max_value):\n    # uint64 is disallowed, because numpy's clip seems to convert it to float64\n    # int64 is disallowed, because numpy's clip converts it to float64 since\n    # 1.17\n    # TODO find the cause for that\n    gate_dtypes_strs(\n        {array.dtype},\n        allowed=\"bool uint8 uint16 uint32 int8 int16 int32 \"\n                \"float16 float32 float64 float128\",\n        disallowed=\"uint64 int64\"\n    )\n\n    # If the min of the input value range is above the allowed min, we do not\n    # have to clip to the allowed min as we cannot exceed it anyways.\n    # Analogous for max. In fact, we must not clip then to min/max as that can\n    # lead to errors in numpy's clip. E.g.\n    #     >>> arr = np.zeros((1,), dtype=np.int32)\n    #     >>> np.clip(arr, 0, np.iinfo(np.dtype(\"uint32\")).max)\n    # will return\n    #     array([-1], dtype=int32)\n    # (observed on numpy version 1.15.2).\n    min_value_arrdt, _, max_value_arrdt = get_value_range_of_dtype(array.dtype)\n    if min_value is not None and min_value < min_value_arrdt:\n        min_value = None\n    if max_value is not None and max_value_arrdt < max_value:\n        max_value = None\n\n    if min_value is not None or max_value is not None:\n        # for scalar arrays, i.e. with shape = (), \"out\" is not a valid\n        # argument\n        if len(array.shape) == 0:\n            array = np.clip(array, min_value, max_value)\n        elif array.dtype.name == \"int32\":\n            # Since 1.17 (before maybe too?), numpy.clip() turns int32\n            # to float64. float64 should cover the whole value range of int32,\n            # so the dtype is not rejected here.\n            # TODO Verify this. Is rounding needed before conversion?\n            array = np.clip(array, min_value, max_value).astype(array.dtype)\n        else:\n            array = np.clip(array, min_value, max_value, out=array)\n    return array\n\n\ndef clip_to_dtype_value_range_(array, dtype, validate=True,\n                               validate_values=None):\n    dtype = normalize_dtype(dtype)\n    min_value, _, max_value = get_value_range_of_dtype(dtype)\n    if validate:\n        array_val = array\n        if ia.is_single_integer(validate):\n            assert validate >= 1, (\n                \"If 'validate' is an integer, it must have a value >=1, \"\n                \"got %d instead.\" % (validate,))\n            assert validate_values is None, (\n                \"If 'validate' is an integer, 'validate_values' must be \"\n                \"None. Got type %s instead.\" % (type(validate_values),))\n            array_val = array.flat[0:validate]\n        if validate_values is not None:\n            min_value_found, max_value_found = validate_values\n        else:\n            min_value_found = np.min(array_val)\n            max_value_found = np.max(array_val)\n        assert min_value <= min_value_found <= max_value, (\n            \"Minimum value of array is outside of allowed value range (%.4f \"\n            \"vs %.4f to %.4f).\" % (min_value_found, min_value, max_value))\n        assert min_value <= max_value_found <= max_value, (\n            \"Maximum value of array is outside of allowed value range (%.4f \"\n            \"vs %.4f to %.4f).\" % (max_value_found, min_value, max_value))\n\n    return clip_(array, min_value, max_value)\n\n\ndef gate_dtypes_strs(dtypes, allowed, disallowed, augmenter=None):\n    \"\"\"Verify that input dtypes match allowed/disallowed dtype strings.\n\n    Added in 0.5.0.\n\n    Parameters\n    ----------\n    dtypes : numpy.ndarray or iterable of numpy.ndarray or iterable of numpy.dtype\n        One or more input dtypes to verify.\n\n    allowed : str\n        Names of one or more allowed dtypes, separated by single spaces.\n\n    disallowed : str\n        Names of disallowed dtypes, separated by single spaces.\n        Must not intersect with allowed dtypes.\n\n    augmenter : None or imgaug.augmenters.meta.Augmenter, optional\n        If the gating happens for an augmenter, it should be provided\n        here. This information will be used to improve output error\n        messages and warnings.\n\n    \"\"\"\n    allowed, disallowed = _convert_gate_dtype_strs_to_types(\n        allowed, disallowed\n    )\n    return _gate_dtypes(dtypes, allowed, disallowed, augmenter=augmenter)\n\n\n# Added in 0.5.0.\ndef _convert_gate_dtype_strs_to_types(allowed, disallowed):\n    allowed_types = _convert_dtype_strs_to_types(allowed)\n    disallowed_types = _convert_dtype_strs_to_types(disallowed)\n\n    intersection = allowed_types.intersection(disallowed_types)\n    nb_overlapping = len(intersection)\n    assert nb_overlapping == 0, (\n        \"Expected 'allowed' and 'disallowed' dtypes to not contain the same \"\n        \"dtypes, but %d appeared in both arguments. Got allowed: %s, \"\n        \"disallowed: %s, intersection: %s\" % (\n            nb_overlapping,\n            allowed,\n            disallowed,\n            intersection\n        )\n    )\n\n    return allowed_types, disallowed_types\n\n\n# Added in 0.5.0.\ndef _convert_dtype_strs_to_types_cached(dtypes):\n    dtypes_parsed = _DTYPE_STR_TO_DTYPES_CACHE.get(dtypes, None)\n    if dtypes_parsed is None:\n        dtypes_parsed = _convert_dtype_strs_to_types_cached(dtypes)\n        _DTYPE_STR_TO_DTYPES_CACHE[dtypes] = dtypes_parsed\n    return dtypes_parsed\n\n\n# Added in 0.5.0.\ndef _convert_dtype_strs_to_types(dtypes):\n    result = set()\n    for name in dtypes.split(\" \"):\n        name = name.strip()\n        if name:\n            dtype = _DTYPE_NAME_TO_DTYPE[name]\n\n            # this if ignores float128 if it is not available on the user\n            # system\n            if dtype is not None:\n                result.add(dtype)\n    return result\n\n\n# Deprecated since 0.5.0.\n@ia.deprecated(\"imgaug.dtypes.gate_dtypes_strs\")\ndef gate_dtypes(dtypes, allowed, disallowed, augmenter=None):\n    def _cvt(dts):\n        normalized = set()\n        if not isinstance(dts, list):\n            dts = [dts]\n\n        for dtype in dts:\n            try:\n                dtype = normalize_dtype(dtype)\n                normalized.add(dtype)\n            except TypeError:\n                pass\n        return normalized\n\n    dtypes_norm = _cvt(dtypes)\n    allowed_norm = _cvt(allowed)\n    disallowed_norm = _cvt(disallowed)\n\n    return _gate_dtypes(\n        dtypes_norm, allowed_norm, disallowed_norm, augmenter=augmenter\n    )\n\n\ndef _gate_dtypes(dtypes, allowed, disallowed, augmenter=None):\n    \"\"\"Verify that input dtypes are among allowed and not disallowed dtypes.\n\n    Added in 0.5.0.\n\n    Parameters\n    ----------\n    dtypes : numpy.ndarray or iterable of numpy.ndarray or iterable of numpy.dtype\n        One or more input dtypes to verify.\n        Must not be a dtype function (like ``np.int64``), only a proper\n        dtype (like ``np.dtype(\"int64\")``). For performance reasons this is\n        not validated.\n\n    allowed : set of numpy.dtype\n        One or more allowed dtypes.\n\n    disallowed : None or set of numpy.dtype\n        Any number of disallowed dtypes. Should not intersect with allowed\n        dtypes.\n\n    augmenter : None or imgaug.augmenters.meta.Augmenter, optional\n        If the gating happens for an augmenter, it should be provided\n        here. This information will be used to improve output error\n        messages and warnings.\n\n    \"\"\"\n    if isinstance(dtypes, np.ndarray) or ia.is_np_scalar(dtypes):\n        dtypes = set([dtypes.dtype])\n    elif isinstance(dtypes, list):\n        dtypes = {arr.dtype for arr in dtypes}\n\n    dts_not_explicitly_allowed = dtypes - allowed\n    all_allowed = (not dts_not_explicitly_allowed)\n\n    if all_allowed:\n        return\n\n    if disallowed is None:\n        disallowed = set()\n\n    dts_explicitly_disallowed = dts_not_explicitly_allowed.intersection(\n        disallowed\n    )\n    dts_undefined = dts_not_explicitly_allowed - disallowed\n\n    if dts_explicitly_disallowed:\n        for dtype in dts_explicitly_disallowed:\n            if augmenter is None:\n                raise ValueError(\n                    \"Got dtype '%s', which is a forbidden dtype (%s).\" % (\n                        np.dtype(dtype).name,\n                        _dtype_names_to_string(disallowed)\n                    ))\n\n            raise ValueError(\n                \"Got dtype '%s' in augmenter '%s' (class '%s'), which \"\n                \"is a forbidden dtype (%s).\" % (\n                    np.dtype(dtype).name,\n                    augmenter.name,\n                    augmenter.__class__.__name__,\n                    _dtype_names_to_string(disallowed),\n                ))\n\n    if dts_undefined:\n        for dtype in dts_undefined:\n            if augmenter is None:\n                ia.warn(\n                    \"Got dtype '%s', which was neither explicitly allowed \"\n                    \"(%s), nor explicitly disallowed (%s). Generated \"\n                    \"outputs may contain errors.\" % (\n                        dtype.name,\n                        _dtype_names_to_string(allowed),\n                        _dtype_names_to_string(disallowed),\n                    )\n                )\n            else:\n                ia.warn(\n                    \"Got dtype '%s' in augmenter '%s' (class '%s'), which was \"\n                    \"neither explicitly allowed (%s), nor explicitly \"\n                    \"disallowed (%s). Generated outputs may contain \"\n                    \"errors.\" % (\n                        dtype.name,\n                        augmenter.name,\n                        augmenter.__class__.__name__,\n                        _dtype_names_to_string(allowed),\n                        _dtype_names_to_string(disallowed),\n                    )\n                )\n\n\n# Added in 0.5.0.\ndef _dtype_names_to_string(dtypes):\n    if isinstance(dtypes, set):\n        dtypes = list(sorted(dtypes))\n    return \", \".join([np.dtype(dt).name for dt in dtypes])\n\n\ndef allow_only_uint8(dtypes, augmenter=None):\n    \"\"\"Verify that input dtypes are uint8.\n\n    Added in 0.5.0.\n\n    Parameters\n    ----------\n    dtypes : numpy.ndarray or iterable of numpy.ndarray or iterable of numpy.dtype\n        One or more input dtypes to verify.\n\n    augmenter : None or imgaug.augmenters.meta.Augmenter, optional\n        If the gating happens for an augmenter, it should be provided\n        here. This information will be used to improve output error\n        messages and warnings.\n\n    \"\"\"\n    return gate_dtypes_strs(\n        dtypes,\n        allowed=\"uint8\",\n        disallowed=\"uint16 uint32 uint64 \"\n                   \"int8 int16 int32 int64 \"\n                   \"float16 float32 float64 float128 \"\n                   \"bool\",\n        augmenter=augmenter\n    )\n"
  },
  {
    "path": "imgaug/external/README.md",
    "content": "This directory is intended for libraries that are required, but not added to the dependencies.\nThat is e.g. the case to avoid bloating up the dependencies for small things\nor because the libraries had to be somehow modified.\n\n* `opensimplex.py`: https://github.com/lmas/opensimplex\n* `poly_point_isect.py`: https://github.com/ideasman42/isect_segments-bentley_ottmann\n* `poly_point_isect_py2py3.py`: Same as `poly_point_isect.py`, but modified to also be compatible with python 2.7. "
  },
  {
    "path": "imgaug/external/__init__.py",
    "content": ""
  },
  {
    "path": "imgaug/external/opensimplex.py",
    "content": "\"\"\"\nThis is a copy of the OpenSimplex library,\nbased on commit d861cb290531ad15825f21dc4cc35c5d4f407259 from 20.07.2017.\n\"\"\"\n\n# Based on: https://gist.github.com/KdotJPG/b1270127455a94ac5d19\n\nimport sys\nfrom ctypes import c_long\nfrom math import floor as _floor\n\n\nif sys.version_info[0] < 3:\n    def floor(num):\n        return int(_floor(num))\nelse:\n    floor = _floor\n\n\nSTRETCH_CONSTANT_2D = -0.211324865405187    # (1/Math.sqrt(2+1)-1)/2\nSQUISH_CONSTANT_2D = 0.366025403784439      # (Math.sqrt(2+1)-1)/2\nSTRETCH_CONSTANT_3D = -1.0 / 6              # (1/Math.sqrt(3+1)-1)/3\nSQUISH_CONSTANT_3D = 1.0 / 3                # (Math.sqrt(3+1)-1)/3\nSTRETCH_CONSTANT_4D = -0.138196601125011    # (1/Math.sqrt(4+1)-1)/4\nSQUISH_CONSTANT_4D = 0.309016994374947      # (Math.sqrt(4+1)-1)/4\n\nNORM_CONSTANT_2D = 47\nNORM_CONSTANT_3D = 103\nNORM_CONSTANT_4D = 30\n\nDEFAULT_SEED = 0\n\n\n# Gradients for 2D. They approximate the directions to the\n# vertices of an octagon from the center.\nGRADIENTS_2D = (\n     5,  2,    2,  5,\n    -5,  2,   -2,  5,\n     5, -2,    2, -5,\n    -5, -2,   -2, -5,\n)\n\n# Gradients for 3D. They approximate the directions to the\n# vertices of a rhombicuboctahedron from the center, skewed so\n# that the triangular and square facets can be inscribed inside\n# circles of the same radius.\nGRADIENTS_3D = (\n    -11,  4,  4,     -4,  11,  4,    -4,  4,  11,\n     11,  4,  4,      4,  11,  4,     4,  4,  11,\n    -11, -4,  4,     -4, -11,  4,    -4, -4,  11,\n     11, -4,  4,      4, -11,  4,     4, -4,  11,\n    -11,  4, -4,     -4,  11, -4,    -4,  4, -11,\n     11,  4, -4,      4,  11, -4,     4,  4, -11,\n    -11, -4, -4,     -4, -11, -4,    -4, -4, -11,\n     11, -4, -4,      4, -11, -4,     4, -4, -11,\n)\n\n# Gradients for 4D. They approximate the directions to the\n# vertices of a disprismatotesseractihexadecachoron from the center,\n# skewed so that the tetrahedral and cubic facets can be inscribed inside\n# spheres of the same radius.\nGRADIENTS_4D = (\n     3,  1,  1,  1,      1,  3,  1,  1,      1,  1,  3,  1,      1,  1,  1,  3,\n    -3,  1,  1,  1,     -1,  3,  1,  1,     -1,  1,  3,  1,     -1,  1,  1,  3,\n     3, -1,  1,  1,      1, -3,  1,  1,      1, -1,  3,  1,      1, -1,  1,  3,\n    -3, -1,  1,  1,     -1, -3,  1,  1,     -1, -1,  3,  1,     -1, -1,  1,  3,\n     3,  1, -1,  1,      1,  3, -1,  1,      1,  1, -3,  1,      1,  1, -1,  3,\n    -3,  1, -1,  1,     -1,  3, -1,  1,     -1,  1, -3,  1,     -1,  1, -1,  3,\n     3, -1, -1,  1,      1, -3, -1,  1,      1, -1, -3,  1,      1, -1, -1,  3,\n    -3, -1, -1,  1,     -1, -3, -1,  1,     -1, -1, -3,  1,     -1, -1, -1,  3,\n     3,  1,  1, -1,      1,  3,  1, -1,      1,  1,  3, -1,      1,  1,  1, -3,\n    -3,  1,  1, -1,     -1,  3,  1, -1,     -1,  1,  3, -1,     -1,  1,  1, -3,\n     3, -1,  1, -1,      1, -3,  1, -1,      1, -1,  3, -1,      1, -1,  1, -3,\n    -3, -1,  1, -1,     -1, -3,  1, -1,     -1, -1,  3, -1,     -1, -1,  1, -3,\n     3,  1, -1, -1,      1,  3, -1, -1,      1,  1, -3, -1,      1,  1, -1, -3,\n    -3,  1, -1, -1,     -1,  3, -1, -1,     -1,  1, -3, -1,     -1,  1, -1, -3,\n     3, -1, -1, -1,      1, -3, -1, -1,      1, -1, -3, -1,      1, -1, -1, -3,\n    -3, -1, -1, -1,     -1, -3, -1, -1,     -1, -1, -3, -1,     -1, -1, -1, -3,\n)\n\n\ndef overflow(x):\n    # Since normal python ints and longs can be quite humongous we have to use\n    # this hack to make them be able to overflow\n    return c_long(x).value\n\n\nclass OpenSimplex(object):\n    \"\"\"\n    OpenSimplex n-dimensional gradient noise functions.\n    \"\"\"\n\n    def __init__(self, seed=DEFAULT_SEED):\n        \"\"\"\n        Initiate the class and generate permutation arrays from a seed number.\n        \"\"\"\n        # Initializes the class using a permutation array generated from a 64-bit seed.\n        # Generates a proper permutation (i.e. doesn't merely perform N\n        # successive pair swaps on a base array)\n        perm = self._perm = [0] * 256 # Have to zero fill so we can properly loop over it later\n        perm_grad_index_3D = self._perm_grad_index_3D = [0] * 256\n        source = [i for i in range(0, 256)]\n        seed = overflow(seed * 6364136223846793005 + 1442695040888963407)\n        seed = overflow(seed * 6364136223846793005 + 1442695040888963407)\n        seed = overflow(seed * 6364136223846793005 + 1442695040888963407)\n        for i in range(255, -1, -1):\n            seed = overflow(seed * 6364136223846793005 + 1442695040888963407)\n            r = int((seed + 31) % (i + 1))\n            if r < 0:\n                r += i + 1\n            perm[i] = source[r]\n            perm_grad_index_3D[i] = int((perm[i] % (len(GRADIENTS_3D) / 3)) * 3)\n            source[r] = source[i]\n\n    def _extrapolate2d(self, xsb, ysb, dx, dy):\n        perm = self._perm\n        index = perm[(perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E\n\n        g1, g2 = GRADIENTS_2D[index:index + 2]\n        return g1 * dx + g2 * dy\n\n    def _extrapolate3d(self, xsb, ysb, zsb, dx, dy, dz):\n        perm = self._perm\n        index = self._perm_grad_index_3D[\n            (perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF\n            ]\n\n        g1, g2, g3 = GRADIENTS_3D[index:index + 3]\n        return g1 * dx + g2 * dy + g3 * dz\n\n    def _extrapolate4d(self, xsb, ysb, zsb, wsb, dx, dy, dz, dw):\n        perm = self._perm\n        index = perm[(\n                         perm[(\n                                  perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb\n                ) & 0xFF] + wsb\n        ) & 0xFF] & 0xFC\n\n        g1, g2, g3, g4 = GRADIENTS_4D[index:index + 4]\n        return g1 * dx + g2 * dy + g3 * dz + g4 * dw\n\n\n    def noise2d(self, x, y):\n        \"\"\"\n        Generate 2D OpenSimplex noise from X,Y coordinates.\n        \"\"\"\n        # Place input coordinates onto grid.\n        stretch_offset = (x + y) * STRETCH_CONSTANT_2D\n        xs = x + stretch_offset\n        ys = y + stretch_offset\n\n        # Floor to get grid coordinates of rhombus (stretched square) super-cell origin.\n        xsb = floor(xs)\n        ysb = floor(ys)\n\n        # Skew out to get actual coordinates of rhombus origin. We'll need these later.\n        squish_offset = (xsb + ysb) * SQUISH_CONSTANT_2D\n        xb = xsb + squish_offset\n        yb = ysb + squish_offset\n\n        # Compute grid coordinates relative to rhombus origin.\n        xins = xs - xsb\n        yins = ys - ysb\n\n        # Sum those together to get a value that determines which region we're in.\n        in_sum = xins + yins\n\n        # Positions relative to origin point.\n        dx0 = x - xb\n        dy0 = y - yb\n\n        value = 0\n\n        # Contribution (1,0)\n        dx1 = dx0 - 1 - SQUISH_CONSTANT_2D\n        dy1 = dy0 - 0 - SQUISH_CONSTANT_2D\n        attn1 = 2 - dx1 * dx1 - dy1 * dy1\n        extrapolate = self._extrapolate2d\n        if attn1 > 0:\n            attn1 *= attn1\n            value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, dx1, dy1)\n\n        # Contribution (0,1)\n        dx2 = dx0 - 0 - SQUISH_CONSTANT_2D\n        dy2 = dy0 - 1 - SQUISH_CONSTANT_2D\n        attn2 = 2 - dx2 * dx2 - dy2 * dy2\n        if attn2 > 0:\n            attn2 *= attn2\n            value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, dx2, dy2)\n\n        if in_sum <= 1: # We're inside the triangle (2-Simplex) at (0,0)\n            zins = 1 - in_sum\n            if zins > xins or zins > yins: # (0,0) is one of the closest two triangular vertices\n                if xins > yins:\n                    xsv_ext = xsb + 1\n                    ysv_ext = ysb - 1\n                    dx_ext = dx0 - 1\n                    dy_ext = dy0 + 1\n                else:\n                    xsv_ext = xsb - 1\n                    ysv_ext = ysb + 1\n                    dx_ext = dx0 + 1\n                    dy_ext = dy0 - 1\n            else: # (1,0) and (0,1) are the closest two vertices.\n                xsv_ext = xsb + 1\n                ysv_ext = ysb + 1\n                dx_ext = dx0 - 1 - 2 * SQUISH_CONSTANT_2D\n                dy_ext = dy0 - 1 - 2 * SQUISH_CONSTANT_2D\n        else: # We're inside the triangle (2-Simplex) at (1,1)\n            zins = 2 - in_sum\n            if zins < xins or zins < yins: # (0,0) is one of the closest two triangular vertices\n                if xins > yins:\n                    xsv_ext = xsb + 2\n                    ysv_ext = ysb + 0\n                    dx_ext = dx0 - 2 - 2 * SQUISH_CONSTANT_2D\n                    dy_ext = dy0 + 0 - 2 * SQUISH_CONSTANT_2D\n                else:\n                    xsv_ext = xsb + 0\n                    ysv_ext = ysb + 2\n                    dx_ext = dx0 + 0 - 2 * SQUISH_CONSTANT_2D\n                    dy_ext = dy0 - 2 - 2 * SQUISH_CONSTANT_2D\n            else: # (1,0) and (0,1) are the closest two vertices.\n                dx_ext = dx0\n                dy_ext = dy0\n                xsv_ext = xsb\n                ysv_ext = ysb\n            xsb += 1\n            ysb += 1\n            dx0 = dx0 - 1 - 2 * SQUISH_CONSTANT_2D\n            dy0 = dy0 - 1 - 2 * SQUISH_CONSTANT_2D\n\n        # Contribution (0,0) or (1,1)\n        attn0 = 2 - dx0 * dx0 - dy0 * dy0\n        if attn0 > 0:\n            attn0 *= attn0\n            value += attn0 * attn0 * extrapolate(xsb, ysb, dx0, dy0)\n\n        # Extra Vertex\n        attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext\n        if attn_ext > 0:\n            attn_ext *= attn_ext\n            value += attn_ext * attn_ext * extrapolate(xsv_ext, ysv_ext, dx_ext, dy_ext)\n\n        return value / NORM_CONSTANT_2D\n\n\n    def noise3d(self, x, y, z):\n        \"\"\"\n        Generate 3D OpenSimplex noise from X,Y,Z coordinates.\n        \"\"\"\n        # Place input coordinates on simplectic honeycomb.\n        stretch_offset = (x + y + z) * STRETCH_CONSTANT_3D\n        xs = x + stretch_offset\n        ys = y + stretch_offset\n        zs = z + stretch_offset\n\n        # Floor to get simplectic honeycomb coordinates of rhombohedron (stretched cube) super-cell origin.\n        xsb = floor(xs)\n        ysb = floor(ys)\n        zsb = floor(zs)\n\n        # Skew out to get actual coordinates of rhombohedron origin. We'll need these later.\n        squish_offset = (xsb + ysb + zsb) * SQUISH_CONSTANT_3D\n        xb = xsb + squish_offset\n        yb = ysb + squish_offset\n        zb = zsb + squish_offset\n\n        # Compute simplectic honeycomb coordinates relative to rhombohedral origin.\n        xins = xs - xsb\n        yins = ys - ysb\n        zins = zs - zsb\n\n        # Sum those together to get a value that determines which region we're in.\n        in_sum = xins + yins + zins\n\n        # Positions relative to origin point.\n        dx0 = x - xb\n        dy0 = y - yb\n        dz0 = z - zb\n\n        value = 0\n        extrapolate = self._extrapolate3d\n        if in_sum <= 1: # We're inside the tetrahedron (3-Simplex) at (0,0,0)\n\n            # Determine which two of (0,0,1), (0,1,0), (1,0,0) are closest.\n            a_point = 0x01\n            a_score = xins\n            b_point = 0x02\n            b_score = yins\n            if a_score >= b_score and zins > b_score:\n                b_score = zins\n                b_point = 0x04\n            elif a_score < b_score and zins > a_score:\n                a_score = zins\n                a_point = 0x04\n\n            # Now we determine the two lattice points not part of the tetrahedron that may contribute.\n            # This depends on the closest two tetrahedral vertices, including (0,0,0)\n            wins = 1 - in_sum\n            if wins > a_score or wins > b_score: # (0,0,0) is one of the closest two tetrahedral vertices.\n                c = b_point if (b_score > a_score) else a_point # Our other closest vertex is the closest out of a and b.\n\n                if (c & 0x01) == 0:\n                    xsv_ext0 = xsb - 1\n                    xsv_ext1 = xsb\n                    dx_ext0 = dx0 + 1\n                    dx_ext1 = dx0\n                else:\n                    xsv_ext0 = xsv_ext1 = xsb + 1\n                    dx_ext0 = dx_ext1 = dx0 - 1\n\n                if (c & 0x02) == 0:\n                    ysv_ext0 = ysv_ext1 = ysb\n                    dy_ext0 = dy_ext1 = dy0\n                    if (c & 0x01) == 0:\n                        ysv_ext1 -= 1\n                        dy_ext1 += 1\n                    else:\n                        ysv_ext0 -= 1\n                        dy_ext0 += 1\n                else:\n                    ysv_ext0 = ysv_ext1 = ysb + 1\n                    dy_ext0 = dy_ext1 = dy0 - 1\n\n                if (c & 0x04) == 0:\n                    zsv_ext0 = zsb\n                    zsv_ext1 = zsb - 1\n                    dz_ext0 = dz0\n                    dz_ext1 = dz0 + 1\n                else:\n                    zsv_ext0 = zsv_ext1 = zsb + 1\n                    dz_ext0 = dz_ext1 = dz0 - 1\n            else: # (0,0,0) is not one of the closest two tetrahedral vertices.\n                c = (a_point | b_point) # Our two extra vertices are determined by the closest two.\n\n                if (c & 0x01) == 0:\n                    xsv_ext0 = xsb\n                    xsv_ext1 = xsb - 1\n                    dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_3D\n                    dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D\n                else:\n                    xsv_ext0 = xsv_ext1 = xsb + 1\n                    dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D\n                    dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D\n\n                if (c & 0x02) == 0:\n                    ysv_ext0 = ysb\n                    ysv_ext1 = ysb - 1\n                    dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_3D\n                    dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D\n                else:\n                    ysv_ext0 = ysv_ext1 = ysb + 1\n                    dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D\n                    dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D\n\n                if (c & 0x04) == 0:\n                    zsv_ext0 = zsb\n                    zsv_ext1 = zsb - 1\n                    dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_3D\n                    dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D\n                else:\n                    zsv_ext0 = zsv_ext1 = zsb + 1\n                    dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D\n                    dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D\n\n            # Contribution (0,0,0)\n            attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0\n            if attn0 > 0:\n                attn0 *= attn0\n                value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, dx0, dy0, dz0)\n\n            # Contribution (1,0,0)\n            dx1 = dx0 - 1 - SQUISH_CONSTANT_3D\n            dy1 = dy0 - 0 - SQUISH_CONSTANT_3D\n            dz1 = dz0 - 0 - SQUISH_CONSTANT_3D\n            attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1\n            if attn1 > 0:\n                attn1 *= attn1\n                value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1)\n\n            # Contribution (0,1,0)\n            dx2 = dx0 - 0 - SQUISH_CONSTANT_3D\n            dy2 = dy0 - 1 - SQUISH_CONSTANT_3D\n            dz2 = dz1\n            attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2\n            if attn2 > 0:\n                attn2 *= attn2\n                value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2)\n\n            # Contribution (0,0,1)\n            dx3 = dx2\n            dy3 = dy1\n            dz3 = dz0 - 1 - SQUISH_CONSTANT_3D\n            attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3\n            if attn3 > 0:\n                attn3 *= attn3\n                value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3)\n        elif in_sum >= 2: # We're inside the tetrahedron (3-Simplex) at (1,1,1)\n\n            # Determine which two tetrahedral vertices are the closest, out of (1,1,0), (1,0,1), (0,1,1) but not (1,1,1).\n            a_point = 0x06\n            a_score = xins\n            b_point = 0x05\n            b_score = yins\n            if a_score <= b_score and zins < b_score:\n                b_score = zins\n                b_point = 0x03\n            elif a_score > b_score and zins < a_score:\n                a_score = zins\n                a_point = 0x03\n\n            # Now we determine the two lattice points not part of the tetrahedron that may contribute.\n            # This depends on the closest two tetrahedral vertices, including (1,1,1)\n            wins = 3 - in_sum\n            if wins < a_score or wins < b_score: # (1,1,1) is one of the closest two tetrahedral vertices.\n                c = b_point if (b_score < a_score) else a_point # Our other closest vertex is the closest out of a and b.\n\n                if (c & 0x01) != 0:\n                    xsv_ext0 = xsb + 2\n                    xsv_ext1 = xsb + 1\n                    dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_3D\n                    dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D\n                else:\n                    xsv_ext0 = xsv_ext1 = xsb\n                    dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_3D\n\n                if (c & 0x02) != 0:\n                    ysv_ext0 = ysv_ext1 = ysb + 1\n                    dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D\n                    if (c & 0x01) != 0:\n                        ysv_ext1 += 1\n                        dy_ext1 -= 1\n                    else:\n                        ysv_ext0 += 1\n                        dy_ext0 -= 1\n                else:\n                    ysv_ext0 = ysv_ext1 = ysb\n                    dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_3D\n\n                if (c & 0x04) != 0:\n                    zsv_ext0 = zsb + 1\n                    zsv_ext1 = zsb + 2\n                    dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D\n                    dz_ext1 = dz0 - 2 - 3 * SQUISH_CONSTANT_3D\n                else:\n                    zsv_ext0 = zsv_ext1 = zsb\n                    dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_3D\n            else: # (1,1,1) is not one of the closest two tetrahedral vertices.\n                c = (a_point & b_point) # Our two extra vertices are determined by the closest two.\n\n                if (c & 0x01) != 0:\n                    xsv_ext0 = xsb + 1\n                    xsv_ext1 = xsb + 2\n                    dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D\n                    dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D\n                else:\n                    xsv_ext0 = xsv_ext1 = xsb\n                    dx_ext0 = dx0 - SQUISH_CONSTANT_3D\n                    dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D\n\n                if (c & 0x02) != 0:\n                    ysv_ext0 = ysb + 1\n                    ysv_ext1 = ysb + 2\n                    dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D\n                    dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D\n                else:\n                    ysv_ext0 = ysv_ext1 = ysb\n                    dy_ext0 = dy0 - SQUISH_CONSTANT_3D\n                    dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D\n\n                if (c & 0x04) != 0:\n                    zsv_ext0 = zsb + 1\n                    zsv_ext1 = zsb + 2\n                    dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D\n                    dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D\n                else:\n                    zsv_ext0 = zsv_ext1 = zsb\n                    dz_ext0 = dz0 - SQUISH_CONSTANT_3D\n                    dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D\n\n            # Contribution (1,1,0)\n            dx3 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D\n            dy3 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D\n            dz3 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D\n            attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3\n            if attn3 > 0:\n                attn3 *= attn3\n                value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx3, dy3, dz3)\n\n            # Contribution (1,0,1)\n            dx2 = dx3\n            dy2 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D\n            dz2 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D\n            attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2\n            if attn2 > 0:\n                attn2 *= attn2\n                value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx2, dy2, dz2)\n\n            # Contribution (0,1,1)\n            dx1 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D\n            dy1 = dy3\n            dz1 = dz2\n            attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1\n            if attn1 > 0:\n                attn1 *= attn1\n                value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx1, dy1, dz1)\n\n            # Contribution (1,1,1)\n            dx0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D\n            dy0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D\n            dz0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D\n            attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0\n            if attn0 > 0:\n                attn0 *= attn0\n                value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, dx0, dy0, dz0)\n        else: # We're inside the octahedron (Rectified 3-Simplex) in between.\n            # Decide between point (0,0,1) and (1,1,0) as closest\n            p1 = xins + yins\n            if p1 > 1:\n                a_score = p1 - 1\n                a_point = 0x03\n                a_is_further_side = True\n            else:\n                a_score = 1 - p1\n                a_point = 0x04\n                a_is_further_side = False\n\n            # Decide between point (0,1,0) and (1,0,1) as closest\n            p2 = xins + zins\n            if p2 > 1:\n                b_score = p2 - 1\n                b_point = 0x05\n                b_is_further_side = True\n            else:\n                b_score = 1 - p2\n                b_point = 0x02\n                b_is_further_side = False\n\n            # The closest out of the two (1,0,0) and (0,1,1) will replace the furthest out of the two decided above, if closer.\n            p3 = yins + zins\n            if p3 > 1:\n                score = p3 - 1\n                if a_score <= b_score and a_score < score:\n                    a_point = 0x06\n                    a_is_further_side = True\n                elif a_score > b_score and b_score < score:\n                    b_point = 0x06\n                    b_is_further_side = True\n            else:\n                score = 1 - p3\n                if a_score <= b_score and a_score < score:\n                    a_point = 0x01\n                    a_is_further_side = False\n                elif a_score > b_score and b_score < score:\n                    b_point = 0x01\n                    b_is_further_side = False\n\n            # Where each of the two closest points are determines how the extra two vertices are calculated.\n            if a_is_further_side == b_is_further_side:\n                if a_is_further_side: # Both closest points on (1,1,1) side\n\n                    # One of the two extra points is (1,1,1)\n                    dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D\n                    dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D\n                    dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D\n                    xsv_ext0 = xsb + 1\n                    ysv_ext0 = ysb + 1\n                    zsv_ext0 = zsb + 1\n\n                    # Other extra point is based on the shared axis.\n                    c = (a_point & b_point)\n                    if (c & 0x01) != 0:\n                        dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D\n                        dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D\n                        dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D\n                        xsv_ext1 = xsb + 2\n                        ysv_ext1 = ysb\n                        zsv_ext1 = zsb\n                    elif (c & 0x02) != 0:\n                        dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D\n                        dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D\n                        dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D\n                        xsv_ext1 = xsb\n                        ysv_ext1 = ysb + 2\n                        zsv_ext1 = zsb\n                    else:\n                        dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D\n                        dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D\n                        dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D\n                        xsv_ext1 = xsb\n                        ysv_ext1 = ysb\n                        zsv_ext1 = zsb + 2\n                else:# Both closest points on (0,0,0) side\n\n                    # One of the two extra points is (0,0,0)\n                    dx_ext0 = dx0\n                    dy_ext0 = dy0\n                    dz_ext0 = dz0\n                    xsv_ext0 = xsb\n                    ysv_ext0 = ysb\n                    zsv_ext0 = zsb\n\n                    # Other extra point is based on the omitted axis.\n                    c = (a_point | b_point)\n                    if (c & 0x01) == 0:\n                        dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D\n                        dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D\n                        dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D\n                        xsv_ext1 = xsb - 1\n                        ysv_ext1 = ysb + 1\n                        zsv_ext1 = zsb + 1\n                    elif (c & 0x02) == 0:\n                        dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D\n                        dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D\n                        dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D\n                        xsv_ext1 = xsb + 1\n                        ysv_ext1 = ysb - 1\n                        zsv_ext1 = zsb + 1\n                    else:\n                        dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D\n                        dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D\n                        dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D\n                        xsv_ext1 = xsb + 1\n                        ysv_ext1 = ysb + 1\n                        zsv_ext1 = zsb - 1\n            else: # One point on (0,0,0) side, one point on (1,1,1) side\n                if a_is_further_side:\n                    c1 = a_point\n                    c2 = b_point\n                else:\n                    c1 = b_point\n                    c2 = a_point\n\n                # One contribution is a _permutation of (1,1,-1)\n                if (c1 & 0x01) == 0:\n                    dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_3D\n                    dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D\n                    dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D\n                    xsv_ext0 = xsb - 1\n                    ysv_ext0 = ysb + 1\n                    zsv_ext0 = zsb + 1\n                elif (c1 & 0x02) == 0:\n                    dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D\n                    dy_ext0 = dy0 + 1 - SQUISH_CONSTANT_3D\n                    dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D\n                    xsv_ext0 = xsb + 1\n                    ysv_ext0 = ysb - 1\n                    zsv_ext0 = zsb + 1\n                else:\n                    dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D\n                    dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D\n                    dz_ext0 = dz0 + 1 - SQUISH_CONSTANT_3D\n                    xsv_ext0 = xsb + 1\n                    ysv_ext0 = ysb + 1\n                    zsv_ext0 = zsb - 1\n\n                # One contribution is a _permutation of (0,0,2)\n                dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D\n                dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D\n                dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D\n                xsv_ext1 = xsb\n                ysv_ext1 = ysb\n                zsv_ext1 = zsb\n                if (c2 & 0x01) != 0:\n                    dx_ext1 -= 2\n                    xsv_ext1 += 2\n                elif (c2 & 0x02) != 0:\n                    dy_ext1 -= 2\n                    ysv_ext1 += 2\n                else:\n                    dz_ext1 -= 2\n                    zsv_ext1 += 2\n\n            # Contribution (1,0,0)\n            dx1 = dx0 - 1 - SQUISH_CONSTANT_3D\n            dy1 = dy0 - 0 - SQUISH_CONSTANT_3D\n            dz1 = dz0 - 0 - SQUISH_CONSTANT_3D\n            attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1\n            if attn1 > 0:\n                attn1 *= attn1\n                value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1)\n\n            # Contribution (0,1,0)\n            dx2 = dx0 - 0 - SQUISH_CONSTANT_3D\n            dy2 = dy0 - 1 - SQUISH_CONSTANT_3D\n            dz2 = dz1\n            attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2\n            if attn2 > 0:\n                attn2 *= attn2\n                value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2)\n\n            # Contribution (0,0,1)\n            dx3 = dx2\n            dy3 = dy1\n            dz3 = dz0 - 1 - SQUISH_CONSTANT_3D\n            attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3\n            if attn3 > 0:\n                attn3 *= attn3\n                value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3)\n\n            # Contribution (1,1,0)\n            dx4 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D\n            dy4 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D\n            dz4 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D\n            attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4\n            if attn4 > 0:\n                attn4 *= attn4\n                value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx4, dy4, dz4)\n\n            # Contribution (1,0,1)\n            dx5 = dx4\n            dy5 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D\n            dz5 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D\n            attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5\n            if attn5 > 0:\n                attn5 *= attn5\n                value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx5, dy5, dz5)\n\n            # Contribution (0,1,1)\n            dx6 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D\n            dy6 = dy4\n            dz6 = dz5\n            attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6\n            if attn6 > 0:\n                attn6 *= attn6\n                value += attn6 * attn6 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx6, dy6, dz6)\n\n        # First extra vertex\n        attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0\n        if attn_ext0 > 0:\n            attn_ext0 *= attn_ext0\n            value += attn_ext0 * attn_ext0 * extrapolate(xsv_ext0, ysv_ext0, zsv_ext0, dx_ext0, dy_ext0, dz_ext0)\n\n        # Second extra vertex\n        attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1\n        if attn_ext1 > 0:\n            attn_ext1 *= attn_ext1\n            value += attn_ext1 * attn_ext1 * extrapolate(xsv_ext1, ysv_ext1, zsv_ext1, dx_ext1, dy_ext1, dz_ext1)\n\n        return value / NORM_CONSTANT_3D\n\n\n    def noise4d(self, x, y, z, w):\n        \"\"\"\n        Generate 4D OpenSimplex noise from X,Y,Z,W coordinates.\n        \"\"\"\n        # Place input coordinates on simplectic honeycomb.\n        stretch_offset = (x + y + z + w) * STRETCH_CONSTANT_4D\n        xs = x + stretch_offset\n        ys = y + stretch_offset\n        zs = z + stretch_offset\n        ws = w + stretch_offset\n\n        # Floor to get simplectic honeycomb coordinates of rhombo-hypercube super-cell origin.\n        xsb = floor(xs)\n        ysb = floor(ys)\n        zsb = floor(zs)\n        wsb = floor(ws)\n\n        # Skew out to get actual coordinates of stretched rhombo-hypercube origin. We'll need these later.\n        squish_offset = (xsb + ysb + zsb + wsb) * SQUISH_CONSTANT_4D\n        xb = xsb + squish_offset\n        yb = ysb + squish_offset\n        zb = zsb + squish_offset\n        wb = wsb + squish_offset\n\n        # Compute simplectic honeycomb coordinates relative to rhombo-hypercube origin.\n        xins = xs - xsb\n        yins = ys - ysb\n        zins = zs - zsb\n        wins = ws - wsb\n\n        # Sum those together to get a value that determines which region we're in.\n        in_sum = xins + yins + zins + wins\n\n        # Positions relative to origin po.\n        dx0 = x - xb\n        dy0 = y - yb\n        dz0 = z - zb\n        dw0 = w - wb\n\n        value = 0\n        extrapolate = self._extrapolate4d\n        if in_sum <= 1: # We're inside the pentachoron (4-Simplex) at (0,0,0,0)\n\n            # Determine which two of (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0) are closest.\n            a_po = 0x01\n            a_score = xins\n            b_po = 0x02\n            b_score = yins\n            if a_score >= b_score and zins > b_score:\n                b_score = zins\n                b_po = 0x04\n            elif a_score < b_score and zins > a_score:\n                a_score = zins\n                a_po = 0x04\n\n            if a_score >= b_score and wins > b_score:\n                b_score = wins\n                b_po = 0x08\n            elif a_score < b_score and wins > a_score:\n                a_score = wins\n                a_po = 0x08\n\n            # Now we determine the three lattice pos not part of the pentachoron that may contribute.\n            # This depends on the closest two pentachoron vertices, including (0,0,0,0)\n            uins = 1 - in_sum\n            if uins > a_score or uins > b_score: # (0,0,0,0) is one of the closest two pentachoron vertices.\n                c = b_po if (b_score > a_score) else a_po # Our other closest vertex is the closest out of a and b.\n                if (c & 0x01) == 0:\n                    xsv_ext0 = xsb - 1\n                    xsv_ext1 = xsv_ext2 = xsb\n                    dx_ext0 = dx0 + 1\n                    dx_ext1 = dx_ext2 = dx0\n                else:\n                    xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1\n                    dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 1\n\n                if (c & 0x02) == 0:\n                    ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb\n                    dy_ext0 = dy_ext1 = dy_ext2 = dy0\n                    if (c & 0x01) == 0x01:\n                        ysv_ext0 -= 1\n                        dy_ext0 += 1\n                    else:\n                        ysv_ext1 -= 1\n                        dy_ext1 += 1\n\n                else:\n                    ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1\n                    dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1\n\n                if (c & 0x04) == 0:\n                    zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb\n                    dz_ext0 = dz_ext1 = dz_ext2 = dz0\n                    if (c & 0x03) != 0:\n                        if (c & 0x03) == 0x03:\n                            zsv_ext0 -= 1\n                            dz_ext0 += 1\n                        else:\n                            zsv_ext1 -= 1\n                            dz_ext1 += 1\n\n                    else:\n                        zsv_ext2 -= 1\n                        dz_ext2 += 1\n\n                else:\n                    zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1\n                    dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1\n\n\n                if (c & 0x08) == 0:\n                    wsv_ext0 = wsv_ext1 = wsb\n                    wsv_ext2 = wsb - 1\n                    dw_ext0 = dw_ext1 = dw0\n                    dw_ext2 = dw0 + 1\n                else:\n                    wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1\n                    dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 1\n\n            else: # (0,0,0,0) is not one of the closest two pentachoron vertices.\n                c = (a_po | b_po) # Our three extra vertices are determined by the closest two.\n\n                if (c & 0x01) == 0:\n                    xsv_ext0 = xsv_ext2 = xsb\n                    xsv_ext1 = xsb - 1\n                    dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D\n                    dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_4D\n                    dx_ext2 = dx0 - SQUISH_CONSTANT_4D\n                else:\n                    xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1\n                    dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    dx_ext1 = dx_ext2 = dx0 - 1 - SQUISH_CONSTANT_4D\n\n                if (c & 0x02) == 0:\n                    ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb\n                    dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D\n                    dy_ext1 = dy_ext2 = dy0 - SQUISH_CONSTANT_4D\n                    if (c & 0x01) == 0x01:\n                        ysv_ext1 -= 1\n                        dy_ext1 += 1\n                    else:\n                        ysv_ext2 -= 1\n                        dy_ext2 += 1\n\n                else:\n                    ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1\n                    dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    dy_ext1 = dy_ext2 = dy0 - 1 - SQUISH_CONSTANT_4D\n\n                if (c & 0x04) == 0:\n                    zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb\n                    dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D\n                    dz_ext1 = dz_ext2 = dz0 - SQUISH_CONSTANT_4D\n                    if (c & 0x03) == 0x03:\n                        zsv_ext1 -= 1\n                        dz_ext1 += 1\n                    else:\n                        zsv_ext2 -= 1\n                        dz_ext2 += 1\n\n                else:\n                    zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1\n                    dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    dz_ext1 = dz_ext2 = dz0 - 1 - SQUISH_CONSTANT_4D\n\n\n                if (c & 0x08) == 0:\n                    wsv_ext0 = wsv_ext1 = wsb\n                    wsv_ext2 = wsb - 1\n                    dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D\n                    dw_ext1 = dw0 - SQUISH_CONSTANT_4D\n                    dw_ext2 = dw0 + 1 - SQUISH_CONSTANT_4D\n                else:\n                    wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1\n                    dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    dw_ext1 = dw_ext2 = dw0 - 1 - SQUISH_CONSTANT_4D\n\n            # Contribution (0,0,0,0)\n            attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0\n            if attn0 > 0:\n                attn0 *= attn0\n                value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 0, dx0, dy0, dz0, dw0)\n\n            # Contribution (1,0,0,0)\n            dx1 = dx0 - 1 - SQUISH_CONSTANT_4D\n            dy1 = dy0 - 0 - SQUISH_CONSTANT_4D\n            dz1 = dz0 - 0 - SQUISH_CONSTANT_4D\n            dw1 = dw0 - 0 - SQUISH_CONSTANT_4D\n            attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1\n            if attn1 > 0:\n                attn1 *= attn1\n                value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1)\n\n            # Contribution (0,1,0,0)\n            dx2 = dx0 - 0 - SQUISH_CONSTANT_4D\n            dy2 = dy0 - 1 - SQUISH_CONSTANT_4D\n            dz2 = dz1\n            dw2 = dw1\n            attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2\n            if attn2 > 0:\n                attn2 *= attn2\n                value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2)\n\n            # Contribution (0,0,1,0)\n            dx3 = dx2\n            dy3 = dy1\n            dz3 = dz0 - 1 - SQUISH_CONSTANT_4D\n            dw3 = dw1\n            attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3\n            if attn3 > 0:\n                attn3 *= attn3\n                value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3)\n\n            # Contribution (0,0,0,1)\n            dx4 = dx2\n            dy4 = dy1\n            dz4 = dz1\n            dw4 = dw0 - 1 - SQUISH_CONSTANT_4D\n            attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4\n            if attn4 > 0:\n                attn4 *= attn4\n                value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4)\n\n        elif in_sum >= 3: # We're inside the pentachoron (4-Simplex) at (1,1,1,1)\n            # Determine which two of (1,1,1,0), (1,1,0,1), (1,0,1,1), (0,1,1,1) are closest.\n            a_po = 0x0E\n            a_score = xins\n            b_po = 0x0D\n            b_score = yins\n            if a_score <= b_score and zins < b_score:\n                b_score = zins\n                b_po = 0x0B\n            elif a_score > b_score and zins < a_score:\n                a_score = zins\n                a_po = 0x0B\n\n            if a_score <= b_score and wins < b_score:\n                b_score = wins\n                b_po = 0x07\n            elif a_score > b_score and wins < a_score:\n                a_score = wins\n                a_po = 0x07\n\n            # Now we determine the three lattice pos not part of the pentachoron that may contribute.\n            # This depends on the closest two pentachoron vertices, including (0,0,0,0)\n            uins = 4 - in_sum\n            if uins < a_score or uins < b_score: # (1,1,1,1) is one of the closest two pentachoron vertices.\n                c = b_po if (b_score < a_score) else a_po # Our other closest vertex is the closest out of a and b.\n\n                if (c & 0x01) != 0:\n                    xsv_ext0 = xsb + 2\n                    xsv_ext1 = xsv_ext2 = xsb + 1\n                    dx_ext0 = dx0 - 2 - 4 * SQUISH_CONSTANT_4D\n                    dx_ext1 = dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D\n                else:\n                    xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb\n                    dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 4 * SQUISH_CONSTANT_4D\n\n                if (c & 0x02) != 0:\n                    ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1\n                    dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D\n                    if (c & 0x01) != 0:\n                        ysv_ext1 += 1\n                        dy_ext1 -= 1\n                    else:\n                        ysv_ext0 += 1\n                        dy_ext0 -= 1\n\n                else:\n                    ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb\n                    dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 4 * SQUISH_CONSTANT_4D\n\n                if (c & 0x04) != 0:\n                    zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1\n                    dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D\n                    if (c & 0x03) != 0x03:\n                        if (c & 0x03) == 0:\n                            zsv_ext0 += 1\n                            dz_ext0 -= 1\n                        else:\n                            zsv_ext1 += 1\n                            dz_ext1 -= 1\n\n                    else:\n                        zsv_ext2 += 1\n                        dz_ext2 -= 1\n\n                else:\n                    zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb\n                    dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 4 * SQUISH_CONSTANT_4D\n\n                if (c & 0x08) != 0:\n                    wsv_ext0 = wsv_ext1 = wsb + 1\n                    wsv_ext2 = wsb + 2\n                    dw_ext0 = dw_ext1 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D\n                    dw_ext2 = dw0 - 2 - 4 * SQUISH_CONSTANT_4D\n                else:\n                    wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb\n                    dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 4 * SQUISH_CONSTANT_4D\n\n            else: # (1,1,1,1) is not one of the closest two pentachoron vertices.\n                c = (a_po & b_po) # Our three extra vertices are determined by the closest two.\n\n                if (c & 0x01) != 0:\n                    xsv_ext0 = xsv_ext2 = xsb + 1\n                    xsv_ext1 = xsb + 2\n                    dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    dx_ext1 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D\n                    dx_ext2 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D\n                else:\n                    xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb\n                    dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D\n                    dx_ext1 = dx_ext2 = dx0 - 3 * SQUISH_CONSTANT_4D\n\n                if (c & 0x02) != 0:\n                    ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1\n                    dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    dy_ext1 = dy_ext2 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D\n                    if (c & 0x01) != 0:\n                        ysv_ext2 += 1\n                        dy_ext2 -= 1\n                    else:\n                        ysv_ext1 += 1\n                        dy_ext1 -= 1\n\n                else:\n                    ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb\n                    dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D\n                    dy_ext1 = dy_ext2 = dy0 - 3 * SQUISH_CONSTANT_4D\n\n                if (c & 0x04) != 0:\n                    zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1\n                    dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    dz_ext1 = dz_ext2 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D\n                    if (c & 0x03) != 0:\n                        zsv_ext2 += 1\n                        dz_ext2 -= 1\n                    else:\n                        zsv_ext1 += 1\n                        dz_ext1 -= 1\n\n                else:\n                    zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb\n                    dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D\n                    dz_ext1 = dz_ext2 = dz0 - 3 * SQUISH_CONSTANT_4D\n\n                if (c & 0x08) != 0:\n                    wsv_ext0 = wsv_ext1 = wsb + 1\n                    wsv_ext2 = wsb + 2\n                    dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    dw_ext1 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D\n                    dw_ext2 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D\n                else:\n                    wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb\n                    dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D\n                    dw_ext1 = dw_ext2 = dw0 - 3 * SQUISH_CONSTANT_4D\n\n            # Contribution (1,1,1,0)\n            dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D\n            dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D\n            dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D\n            dw4 = dw0 - 3 * SQUISH_CONSTANT_4D\n            attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4\n            if attn4 > 0:\n                attn4 *= attn4\n                value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4)\n\n            # Contribution (1,1,0,1)\n            dx3 = dx4\n            dy3 = dy4\n            dz3 = dz0 - 3 * SQUISH_CONSTANT_4D\n            dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D\n            attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3\n            if attn3 > 0:\n                attn3 *= attn3\n                value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3)\n\n            # Contribution (1,0,1,1)\n            dx2 = dx4\n            dy2 = dy0 - 3 * SQUISH_CONSTANT_4D\n            dz2 = dz4\n            dw2 = dw3\n            attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2\n            if attn2 > 0:\n                attn2 *= attn2\n                value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2)\n\n            # Contribution (0,1,1,1)\n            dx1 = dx0 - 3 * SQUISH_CONSTANT_4D\n            dz1 = dz4\n            dy1 = dy4\n            dw1 = dw3\n            attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1\n            if attn1 > 0:\n                attn1 *= attn1\n                value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1)\n\n            # Contribution (1,1,1,1)\n            dx0 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D\n            dy0 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D\n            dz0 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D\n            dw0 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D\n            attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0\n            if attn0 > 0:\n                attn0 *= attn0\n                value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 1, dx0, dy0, dz0, dw0)\n\n        elif in_sum <= 2: # We're inside the first dispentachoron (Rectified 4-Simplex)\n            a_is_bigger_side = True\n            b_is_bigger_side = True\n\n            # Decide between (1,1,0,0) and (0,0,1,1)\n            if xins + yins > zins + wins:\n                a_score = xins + yins\n                a_po = 0x03\n            else:\n                a_score = zins + wins\n                a_po = 0x0C\n\n            # Decide between (1,0,1,0) and (0,1,0,1)\n            if xins + zins > yins + wins:\n                b_score = xins + zins\n                b_po = 0x05\n            else:\n                b_score = yins + wins\n                b_po = 0x0A\n\n            # Closer between (1,0,0,1) and (0,1,1,0) will replace the further of a and b, if closer.\n            if xins + wins > yins + zins:\n                score = xins + wins\n                if a_score >= b_score and score > b_score:\n                    b_score = score\n                    b_po = 0x09\n                elif a_score < b_score and score > a_score:\n                    a_score = score\n                    a_po = 0x09\n\n            else:\n                score = yins + zins\n                if a_score >= b_score and score > b_score:\n                    b_score = score\n                    b_po = 0x06\n                elif a_score < b_score and score > a_score:\n                    a_score = score\n                    a_po = 0x06\n\n            # Decide if (1,0,0,0) is closer.\n            p1 = 2 - in_sum + xins\n            if a_score >= b_score and p1 > b_score:\n                b_score = p1\n                b_po = 0x01\n                b_is_bigger_side = False\n            elif a_score < b_score and p1 > a_score:\n                a_score = p1\n                a_po = 0x01\n                a_is_bigger_side = False\n\n            # Decide if (0,1,0,0) is closer.\n            p2 = 2 - in_sum + yins\n            if a_score >= b_score and p2 > b_score:\n                b_score = p2\n                b_po = 0x02\n                b_is_bigger_side = False\n            elif a_score < b_score and p2 > a_score:\n                a_score = p2\n                a_po = 0x02\n                a_is_bigger_side = False\n\n            # Decide if (0,0,1,0) is closer.\n            p3 = 2 - in_sum + zins\n            if a_score >= b_score and p3 > b_score:\n                b_score = p3\n                b_po = 0x04\n                b_is_bigger_side = False\n            elif a_score < b_score and p3 > a_score:\n                a_score = p3\n                a_po = 0x04\n                a_is_bigger_side = False\n\n            # Decide if (0,0,0,1) is closer.\n            p4 = 2 - in_sum + wins\n            if a_score >= b_score and p4 > b_score:\n                b_po = 0x08\n                b_is_bigger_side = False\n            elif a_score < b_score and p4 > a_score:\n                a_po = 0x08\n                a_is_bigger_side = False\n\n            # Where each of the two closest pos are determines how the extra three vertices are calculated.\n            if a_is_bigger_side == b_is_bigger_side:\n                if a_is_bigger_side: # Both closest pos on the bigger side\n                    c1 = (a_po | b_po)\n                    c2 = (a_po & b_po)\n                    if (c1 & 0x01) == 0:\n                        xsv_ext0 = xsb\n                        xsv_ext1 = xsb - 1\n                        dx_ext0 = dx0 - 3 * SQUISH_CONSTANT_4D\n                        dx_ext1 = dx0 + 1 - 2 * SQUISH_CONSTANT_4D\n                    else:\n                        xsv_ext0 = xsv_ext1 = xsb + 1\n                        dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D\n                        dx_ext1 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D\n\n                    if (c1 & 0x02) == 0:\n                        ysv_ext0 = ysb\n                        ysv_ext1 = ysb - 1\n                        dy_ext0 = dy0 - 3 * SQUISH_CONSTANT_4D\n                        dy_ext1 = dy0 + 1 - 2 * SQUISH_CONSTANT_4D\n                    else:\n                        ysv_ext0 = ysv_ext1 = ysb + 1\n                        dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D\n                        dy_ext1 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D\n\n                    if (c1 & 0x04) == 0:\n                        zsv_ext0 = zsb\n                        zsv_ext1 = zsb - 1\n                        dz_ext0 = dz0 - 3 * SQUISH_CONSTANT_4D\n                        dz_ext1 = dz0 + 1 - 2 * SQUISH_CONSTANT_4D\n                    else:\n                        zsv_ext0 = zsv_ext1 = zsb + 1\n                        dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D\n                        dz_ext1 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D\n\n                    if (c1 & 0x08) == 0:\n                        wsv_ext0 = wsb\n                        wsv_ext1 = wsb - 1\n                        dw_ext0 = dw0 - 3 * SQUISH_CONSTANT_4D\n                        dw_ext1 = dw0 + 1 - 2 * SQUISH_CONSTANT_4D\n                    else:\n                        wsv_ext0 = wsv_ext1 = wsb + 1\n                        dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D\n                        dw_ext1 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D\n\n                    # One combination is a _permutation of (0,0,0,2) based on c2\n                    xsv_ext2 = xsb\n                    ysv_ext2 = ysb\n                    zsv_ext2 = zsb\n                    wsv_ext2 = wsb\n                    dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D\n                    dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D\n                    dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D\n                    dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D\n                    if (c2 & 0x01) != 0:\n                        xsv_ext2 += 2\n                        dx_ext2 -= 2\n                    elif (c2 & 0x02) != 0:\n                        ysv_ext2 += 2\n                        dy_ext2 -= 2\n                    elif (c2 & 0x04) != 0:\n                        zsv_ext2 += 2\n                        dz_ext2 -= 2\n                    else:\n                        wsv_ext2 += 2\n                        dw_ext2 -= 2\n\n                else: # Both closest pos on the smaller side\n                    # One of the two extra pos is (0,0,0,0)\n                    xsv_ext2 = xsb\n                    ysv_ext2 = ysb\n                    zsv_ext2 = zsb\n                    wsv_ext2 = wsb\n                    dx_ext2 = dx0\n                    dy_ext2 = dy0\n                    dz_ext2 = dz0\n                    dw_ext2 = dw0\n\n                    # Other two pos are based on the omitted axes.\n                    c = (a_po | b_po)\n\n                    if (c & 0x01) == 0:\n                        xsv_ext0 = xsb - 1\n                        xsv_ext1 = xsb\n                        dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D\n                        dx_ext1 = dx0 - SQUISH_CONSTANT_4D\n                    else:\n                        xsv_ext0 = xsv_ext1 = xsb + 1\n                        dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D\n\n                    if (c & 0x02) == 0:\n                        ysv_ext0 = ysv_ext1 = ysb\n                        dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D\n                        if (c & 0x01) == 0x01:\n                            ysv_ext0 -= 1\n                            dy_ext0 += 1\n                        else:\n                            ysv_ext1 -= 1\n                            dy_ext1 += 1\n\n                    else:\n                        ysv_ext0 = ysv_ext1 = ysb + 1\n                        dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D\n\n                    if (c & 0x04) == 0:\n                        zsv_ext0 = zsv_ext1 = zsb\n                        dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D\n                        if (c & 0x03) == 0x03:\n                            zsv_ext0 -= 1\n                            dz_ext0 += 1\n                        else:\n                            zsv_ext1 -= 1\n                            dz_ext1 += 1\n\n                    else:\n                        zsv_ext0 = zsv_ext1 = zsb + 1\n                        dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D\n\n\n                    if (c & 0x08) == 0:\n                        wsv_ext0 = wsb\n                        wsv_ext1 = wsb - 1\n                        dw_ext0 = dw0 - SQUISH_CONSTANT_4D\n                        dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D\n                    else:\n                        wsv_ext0 = wsv_ext1 = wsb + 1\n                        dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D\n\n            else: # One po on each \"side\"\n                if a_is_bigger_side:\n                    c1 = a_po\n                    c2 = b_po\n                else:\n                    c1 = b_po\n                    c2 = a_po\n\n                # Two contributions are the bigger-sided po with each 0 replaced with -1.\n                if (c1 & 0x01) == 0:\n                    xsv_ext0 = xsb - 1\n                    xsv_ext1 = xsb\n                    dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D\n                    dx_ext1 = dx0 - SQUISH_CONSTANT_4D\n                else:\n                    xsv_ext0 = xsv_ext1 = xsb + 1\n                    dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D\n\n                if (c1 & 0x02) == 0:\n                    ysv_ext0 = ysv_ext1 = ysb\n                    dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D\n                    if (c1 & 0x01) == 0x01:\n                        ysv_ext0 -= 1\n                        dy_ext0 += 1\n                    else:\n                        ysv_ext1 -= 1\n                        dy_ext1 += 1\n\n                else:\n                    ysv_ext0 = ysv_ext1 = ysb + 1\n                    dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D\n\n                if (c1 & 0x04) == 0:\n                    zsv_ext0 = zsv_ext1 = zsb\n                    dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D\n                    if (c1 & 0x03) == 0x03:\n                        zsv_ext0 -= 1\n                        dz_ext0 += 1\n                    else:\n                        zsv_ext1 -= 1\n                        dz_ext1 += 1\n\n                else:\n                    zsv_ext0 = zsv_ext1 = zsb + 1\n                    dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D\n\n                if (c1 & 0x08) == 0:\n                    wsv_ext0 = wsb\n                    wsv_ext1 = wsb - 1\n                    dw_ext0 = dw0 - SQUISH_CONSTANT_4D\n                    dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D\n                else:\n                    wsv_ext0 = wsv_ext1 = wsb + 1\n                    dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D\n\n                # One contribution is a _permutation of (0,0,0,2) based on the smaller-sided po\n                xsv_ext2 = xsb\n                ysv_ext2 = ysb\n                zsv_ext2 = zsb\n                wsv_ext2 = wsb\n                dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D\n                dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D\n                dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D\n                dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D\n                if (c2 & 0x01) != 0:\n                    xsv_ext2 += 2\n                    dx_ext2 -= 2\n                elif (c2 & 0x02) != 0:\n                    ysv_ext2 += 2\n                    dy_ext2 -= 2\n                elif (c2 & 0x04) != 0:\n                    zsv_ext2 += 2\n                    dz_ext2 -= 2\n                else:\n                    wsv_ext2 += 2\n                    dw_ext2 -= 2\n\n            # Contribution (1,0,0,0)\n            dx1 = dx0 - 1 - SQUISH_CONSTANT_4D\n            dy1 = dy0 - 0 - SQUISH_CONSTANT_4D\n            dz1 = dz0 - 0 - SQUISH_CONSTANT_4D\n            dw1 = dw0 - 0 - SQUISH_CONSTANT_4D\n            attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1\n            if attn1 > 0:\n                attn1 *= attn1\n                value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1)\n\n            # Contribution (0,1,0,0)\n            dx2 = dx0 - 0 - SQUISH_CONSTANT_4D\n            dy2 = dy0 - 1 - SQUISH_CONSTANT_4D\n            dz2 = dz1\n            dw2 = dw1\n            attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2\n            if attn2 > 0:\n                attn2 *= attn2\n                value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2)\n\n            # Contribution (0,0,1,0)\n            dx3 = dx2\n            dy3 = dy1\n            dz3 = dz0 - 1 - SQUISH_CONSTANT_4D\n            dw3 = dw1\n            attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3\n            if attn3 > 0:\n                attn3 *= attn3\n                value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3)\n\n            # Contribution (0,0,0,1)\n            dx4 = dx2\n            dy4 = dy1\n            dz4 = dz1\n            dw4 = dw0 - 1 - SQUISH_CONSTANT_4D\n            attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4\n            if attn4 > 0:\n                attn4 *= attn4\n                value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4)\n\n            # Contribution (1,1,0,0)\n            dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D\n            attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5\n            if attn5 > 0:\n                attn5 *= attn5\n                value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5)\n\n            # Contribution (1,0,1,0)\n            dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D\n            attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6\n            if attn6 > 0:\n                attn6 *= attn6\n                value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6)\n\n            # Contribution (1,0,0,1)\n            dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D\n            attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7\n            if attn7 > 0:\n                attn7 *= attn7\n                value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7)\n\n            # Contribution (0,1,1,0)\n            dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D\n            attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8\n            if attn8 > 0:\n                attn8 *= attn8\n                value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8)\n\n            # Contribution (0,1,0,1)\n            dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D\n            attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9\n            if attn9 > 0:\n                attn9 *= attn9\n                value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9)\n\n            # Contribution (0,0,1,1)\n            dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D\n            attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10\n            if attn10 > 0:\n                attn10 *= attn10\n                value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10)\n\n        else: # We're inside the second dispentachoron (Rectified 4-Simplex)\n            a_is_bigger_side = True\n            b_is_bigger_side = True\n\n            # Decide between (0,0,1,1) and (1,1,0,0)\n            if xins + yins < zins + wins:\n                a_score = xins + yins\n                a_po = 0x0C\n            else:\n                a_score = zins + wins\n                a_po = 0x03\n\n            # Decide between (0,1,0,1) and (1,0,1,0)\n            if xins + zins < yins + wins:\n                b_score = xins + zins\n                b_po = 0x0A\n            else:\n                b_score = yins + wins\n                b_po = 0x05\n\n            # Closer between (0,1,1,0) and (1,0,0,1) will replace the further of a and b, if closer.\n            if xins + wins < yins + zins:\n                score = xins + wins\n                if a_score <= b_score and score < b_score:\n                    b_score = score\n                    b_po = 0x06\n                elif a_score > b_score and score < a_score:\n                    a_score = score\n                    a_po = 0x06\n\n            else:\n                score = yins + zins\n                if a_score <= b_score and score < b_score:\n                    b_score = score\n                    b_po = 0x09\n                elif a_score > b_score and score < a_score:\n                    a_score = score\n                    a_po = 0x09\n\n            # Decide if (0,1,1,1) is closer.\n            p1 = 3 - in_sum + xins\n            if a_score <= b_score and p1 < b_score:\n                b_score = p1\n                b_po = 0x0E\n                b_is_bigger_side = False\n            elif a_score > b_score and p1 < a_score:\n                a_score = p1\n                a_po = 0x0E\n                a_is_bigger_side = False\n\n            # Decide if (1,0,1,1) is closer.\n            p2 = 3 - in_sum + yins\n            if a_score <= b_score and p2 < b_score:\n                b_score = p2\n                b_po = 0x0D\n                b_is_bigger_side = False\n            elif a_score > b_score and p2 < a_score:\n                a_score = p2\n                a_po = 0x0D\n                a_is_bigger_side = False\n\n            # Decide if (1,1,0,1) is closer.\n            p3 = 3 - in_sum + zins\n            if a_score <= b_score and p3 < b_score:\n                b_score = p3\n                b_po = 0x0B\n                b_is_bigger_side = False\n            elif a_score > b_score and p3 < a_score:\n                a_score = p3\n                a_po = 0x0B\n                a_is_bigger_side = False\n\n            # Decide if (1,1,1,0) is closer.\n            p4 = 3 - in_sum + wins\n            if a_score <= b_score and p4 < b_score:\n                b_po = 0x07\n                b_is_bigger_side = False\n            elif a_score > b_score and p4 < a_score:\n                a_po = 0x07\n                a_is_bigger_side = False\n\n            # Where each of the two closest pos are determines how the extra three vertices are calculated.\n            if a_is_bigger_side == b_is_bigger_side:\n                if a_is_bigger_side: # Both closest pos on the bigger side\n                    c1 = (a_po & b_po)\n                    c2 = (a_po | b_po)\n\n                    # Two contributions are _permutations of (0,0,0,1) and (0,0,0,2) based on c1\n                    xsv_ext0 = xsv_ext1 = xsb\n                    ysv_ext0 = ysv_ext1 = ysb\n                    zsv_ext0 = zsv_ext1 = zsb\n                    wsv_ext0 = wsv_ext1 = wsb\n                    dx_ext0 = dx0 - SQUISH_CONSTANT_4D\n                    dy_ext0 = dy0 - SQUISH_CONSTANT_4D\n                    dz_ext0 = dz0 - SQUISH_CONSTANT_4D\n                    dw_ext0 = dw0 - SQUISH_CONSTANT_4D\n                    dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_4D\n                    dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_4D\n                    dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_4D\n                    dw_ext1 = dw0 - 2 * SQUISH_CONSTANT_4D\n                    if (c1 & 0x01) != 0:\n                        xsv_ext0 += 1\n                        dx_ext0 -= 1\n                        xsv_ext1 += 2\n                        dx_ext1 -= 2\n                    elif (c1 & 0x02) != 0:\n                        ysv_ext0 += 1\n                        dy_ext0 -= 1\n                        ysv_ext1 += 2\n                        dy_ext1 -= 2\n                    elif (c1 & 0x04) != 0:\n                        zsv_ext0 += 1\n                        dz_ext0 -= 1\n                        zsv_ext1 += 2\n                        dz_ext1 -= 2\n                    else:\n                        wsv_ext0 += 1\n                        dw_ext0 -= 1\n                        wsv_ext1 += 2\n                        dw_ext1 -= 2\n\n                    # One contribution is a _permutation of (1,1,1,-1) based on c2\n                    xsv_ext2 = xsb + 1\n                    ysv_ext2 = ysb + 1\n                    zsv_ext2 = zsb + 1\n                    wsv_ext2 = wsb + 1\n                    dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D\n                    if (c2 & 0x01) == 0:\n                        xsv_ext2 -= 2\n                        dx_ext2 += 2\n                    elif (c2 & 0x02) == 0:\n                        ysv_ext2 -= 2\n                        dy_ext2 += 2\n                    elif (c2 & 0x04) == 0:\n                        zsv_ext2 -= 2\n                        dz_ext2 += 2\n                    else:\n                        wsv_ext2 -= 2\n                        dw_ext2 += 2\n\n                else: # Both closest pos on the smaller side\n                    # One of the two extra pos is (1,1,1,1)\n                    xsv_ext2 = xsb + 1\n                    ysv_ext2 = ysb + 1\n                    zsv_ext2 = zsb + 1\n                    wsv_ext2 = wsb + 1\n                    dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D\n                    dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D\n                    dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D\n                    dw_ext2 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D\n\n                    # Other two pos are based on the shared axes.\n                    c = (a_po & b_po)\n                    if (c & 0x01) != 0:\n                        xsv_ext0 = xsb + 2\n                        xsv_ext1 = xsb + 1\n                        dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D\n                        dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D\n                    else:\n                        xsv_ext0 = xsv_ext1 = xsb\n                        dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D\n\n                    if (c & 0x02) != 0:\n                        ysv_ext0 = ysv_ext1 = ysb + 1\n                        dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D\n                        if (c & 0x01) == 0:\n                            ysv_ext0 += 1\n                            dy_ext0 -= 1\n                        else:\n                            ysv_ext1 += 1\n                            dy_ext1 -= 1\n\n                    else:\n                        ysv_ext0 = ysv_ext1 = ysb\n                        dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D\n\n                    if (c & 0x04) != 0:\n                        zsv_ext0 = zsv_ext1 = zsb + 1\n                        dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D\n                        if (c & 0x03) == 0:\n                            zsv_ext0 += 1\n                            dz_ext0 -= 1\n                        else:\n                            zsv_ext1 += 1\n                            dz_ext1 -= 1\n\n                    else:\n                        zsv_ext0 = zsv_ext1 = zsb\n                        dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D\n\n\n                    if (c & 0x08) != 0:\n                        wsv_ext0 = wsb + 1\n                        wsv_ext1 = wsb + 2\n                        dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D\n                        dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D\n                    else:\n                        wsv_ext0 = wsv_ext1 = wsb\n                        dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D\n\n            else: # One po on each \"side\"\n                if a_is_bigger_side:\n                    c1 = a_po\n                    c2 = b_po\n                else:\n                    c1 = b_po\n                    c2 = a_po\n\n                # Two contributions are the bigger-sided po with each 1 replaced with 2.\n                if (c1 & 0x01) != 0:\n                    xsv_ext0 = xsb + 2\n                    xsv_ext1 = xsb + 1\n                    dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D\n                    dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D\n                else:\n                    xsv_ext0 = xsv_ext1 = xsb\n                    dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D\n\n                if (c1 & 0x02) != 0:\n                    ysv_ext0 = ysv_ext1 = ysb + 1\n                    dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D\n                    if (c1 & 0x01) == 0:\n                        ysv_ext0 += 1\n                        dy_ext0 -= 1\n                    else:\n                        ysv_ext1 += 1\n                        dy_ext1 -= 1\n\n                else:\n                    ysv_ext0 = ysv_ext1 = ysb\n                    dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D\n\n                if (c1 & 0x04) != 0:\n                    zsv_ext0 = zsv_ext1 = zsb + 1\n                    dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D\n                    if (c1 & 0x03) == 0:\n                        zsv_ext0 += 1\n                        dz_ext0 -= 1\n                    else:\n                        zsv_ext1 += 1\n                        dz_ext1 -= 1\n\n                else:\n                    zsv_ext0 = zsv_ext1 = zsb\n                    dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D\n\n                if (c1 & 0x08) != 0:\n                    wsv_ext0 = wsb + 1\n                    wsv_ext1 = wsb + 2\n                    dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D\n                    dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D\n                else:\n                    wsv_ext0 = wsv_ext1 = wsb\n                    dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D\n\n                # One contribution is a _permutation of (1,1,1,-1) based on the smaller-sided po\n                xsv_ext2 = xsb + 1\n                ysv_ext2 = ysb + 1\n                zsv_ext2 = zsb + 1\n                wsv_ext2 = wsb + 1\n                dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D\n                dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D\n                dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D\n                dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D\n                if (c2 & 0x01) == 0:\n                    xsv_ext2 -= 2\n                    dx_ext2 += 2\n                elif (c2 & 0x02) == 0:\n                    ysv_ext2 -= 2\n                    dy_ext2 += 2\n                elif (c2 & 0x04) == 0:\n                    zsv_ext2 -= 2\n                    dz_ext2 += 2\n                else:\n                    wsv_ext2 -= 2\n                    dw_ext2 += 2\n\n            # Contribution (1,1,1,0)\n            dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D\n            dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D\n            dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D\n            dw4 = dw0 - 3 * SQUISH_CONSTANT_4D\n            attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4\n            if attn4 > 0:\n                attn4 *= attn4\n                value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4)\n\n            # Contribution (1,1,0,1)\n            dx3 = dx4\n            dy3 = dy4\n            dz3 = dz0 - 3 * SQUISH_CONSTANT_4D\n            dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D\n            attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3\n            if attn3 > 0:\n                attn3 *= attn3\n                value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3)\n\n            # Contribution (1,0,1,1)\n            dx2 = dx4\n            dy2 = dy0 - 3 * SQUISH_CONSTANT_4D\n            dz2 = dz4\n            dw2 = dw3\n            attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2\n            if attn2 > 0:\n                attn2 *= attn2\n                value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2)\n\n            # Contribution (0,1,1,1)\n            dx1 = dx0 - 3 * SQUISH_CONSTANT_4D\n            dz1 = dz4\n            dy1 = dy4\n            dw1 = dw3\n            attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1\n            if attn1 > 0:\n                attn1 *= attn1\n                value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1)\n\n            # Contribution (1,1,0,0)\n            dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D\n            attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5\n            if attn5 > 0:\n                attn5 *= attn5\n                value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5)\n\n            # Contribution (1,0,1,0)\n            dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D\n            attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6\n            if attn6 > 0:\n                attn6 *= attn6\n                value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6)\n\n            # Contribution (1,0,0,1)\n            dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D\n            attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7\n            if attn7 > 0:\n                attn7 *= attn7\n                value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7)\n\n            # Contribution (0,1,1,0)\n            dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D\n            attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8\n            if attn8 > 0:\n                attn8 *= attn8\n                value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8)\n\n            # Contribution (0,1,0,1)\n            dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D\n            attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9\n            if attn9 > 0:\n                attn9 *= attn9\n                value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9)\n\n            # Contribution (0,0,1,1)\n            dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D\n            dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D\n            dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D\n            attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10\n            if attn10 > 0:\n                attn10 *= attn10\n                value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10)\n\n        # First extra vertex\n        attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0 - dw_ext0 * dw_ext0\n        if attn_ext0 > 0:\n            attn_ext0 *= attn_ext0\n            value += attn_ext0 * attn_ext0 * extrapolate(xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0, dx_ext0, dy_ext0, dz_ext0, dw_ext0)\n\n        # Second extra vertex\n        attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1 - dw_ext1 * dw_ext1\n        if attn_ext1 > 0:\n            attn_ext1 *= attn_ext1\n            value += attn_ext1 * attn_ext1 * extrapolate(xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1, dx_ext1, dy_ext1, dz_ext1, dw_ext1)\n\n        # Third extra vertex\n        attn_ext2 = 2 - dx_ext2 * dx_ext2 - dy_ext2 * dy_ext2 - dz_ext2 * dz_ext2 - dw_ext2 * dw_ext2\n        if attn_ext2 > 0:\n            attn_ext2 *= attn_ext2\n            value += attn_ext2 * attn_ext2 * extrapolate(xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2, dx_ext2, dy_ext2, dz_ext2, dw_ext2)\n\n        return value / NORM_CONSTANT_4D\n"
  },
  {
    "path": "imgaug/external/poly_point_isect_py2py3.py",
    "content": "# BentleyOttmann sweep-line implementation\n# (for finding all intersections in a set of line segments)\n\n__all__ = (\n    \"isect_segments\",\n    \"isect_polygon\",\n\n    # same as above but includes segments with each intersections\n    \"isect_segments_include_segments\",\n    \"isect_polygon_include_segments\",\n\n    # for testing only (correct but slow)\n    \"isect_segments__naive\",\n    \"isect_polygon__naive\",\n    )\n\n# ----------------------------------------------------------------------------\n# Main Poly Intersection\n\n# Defines to change behavior.\n#\n# Whether to ignore intersections of line segments when both\n# their end points form the intersection point.\nUSE_IGNORE_SEGMENT_ENDINGS = True\n\nUSE_DEBUG = True\n\nUSE_VERBOSE = False\n\n# checks we should NOT need,\n# but do them in case we find a test-case that fails.\nUSE_PARANOID = False\n\n# Support vertical segments,\n# (the bentley-ottmann method doesn't support this).\n# We use the term 'START_VERTICAL' for a vertical segment,\n# to differentiate it from START/END/INTERSECTION\nUSE_VERTICAL = True\n# end defines!\n# ------------\n\n# ---------\n# Constants\nX, Y = 0, 1\n\n# -----------------------------------------------------------------------------\n# Switchable Number Implementation\n\nNUMBER_TYPE = 'native'\n\nif NUMBER_TYPE == 'native':\n    Real = float\n    ########################################################################\n    # decreased 1e-10 to 1e-4 here, otherwise large float values of 10k+\n    # caused not found errors in remove()\n    NUM_EPS = Real(\"1e-4\")\n    ########################################################################\n    NUM_INF = Real(float(\"inf\"))\nelif NUMBER_TYPE == 'decimal':\n    # Not passing tests!\n    import decimal\n    Real = decimal.Decimal\n    decimal.getcontext().prec = 80\n    NUM_EPS = Real(\"1e-10\")\n    NUM_INF = Real(float(\"inf\"))\nelif NUMBER_TYPE == 'numpy':\n    import numpy\n    Real = numpy.float64\n    del numpy\n    NUM_EPS = Real(\"1e-10\")\n    NUM_INF = Real(float(\"inf\"))\nelif NUMBER_TYPE == 'gmpy2':\n    # Not passing tests!\n    import gmpy2\n    gmpy2.set_context(gmpy2.ieee(128))\n    Real = gmpy2.mpz\n    NUM_EPS = Real(float(\"1e-10\"))\n    NUM_INF = gmpy2.get_emax_max()\n    del gmpy2\nelse:\n    raise Exception(\"Type not found\")\n\nNUM_EPS_SQ = NUM_EPS * NUM_EPS\nNUM_ZERO = Real(0.0)\nNUM_ONE = Real(1.0)\n\n\nclass Event:\n    __slots__ = (\n        \"type\",\n        \"point\",\n        \"segment\",\n\n        # this is just cache,\n        # we may remove or calculate slope on the fly\n        \"slope\",\n        \"span\",\n        ) + (() if not USE_DEBUG else (\n         # debugging only\n        \"other\",\n        \"in_sweep\",\n        ))\n\n    class Type:\n        END = 0\n        INTERSECTION = 1\n        START = 2\n        if USE_VERTICAL:\n            START_VERTICAL = 3\n\n    def __init__(self, type, point, segment, slope):\n        assert(isinstance(point, tuple))\n        self.type = type\n        self.point = point\n        self.segment = segment\n\n        # will be None for INTERSECTION\n        self.slope = slope\n        if segment is not None:\n            self.span = segment[1][X] - segment[0][X]\n\n        if USE_DEBUG:\n            self.other = None\n            self.in_sweep = False\n\n    # note that this isn't essential,\n    # it just avoids non-deterministic ordering, see #9.\n    def __hash__(self):\n        return hash(self.point)\n\n    def is_vertical(self):\n        # return self.segment[0][X] == self.segment[1][X]\n        return self.span == NUM_ZERO\n\n    def y_intercept_x(self, x):\n        # vertical events only for comparison (above_all check)\n        # never added into the binary-tree its self\n        if USE_VERTICAL:\n            if self.is_vertical():\n                return None\n\n        if x <= self.segment[0][X]:\n            return self.segment[0][Y]\n        elif x >= self.segment[1][X]:\n            return self.segment[1][Y]\n\n        # use the largest to avoid float precision error with nearly vertical lines.\n        delta_x0 = x - self.segment[0][X]\n        delta_x1 = self.segment[1][X] - x\n        if delta_x0 > delta_x1:\n            ifac = delta_x0 / self.span\n            fac = NUM_ONE - ifac\n        else:\n            fac = delta_x1 / self.span\n            ifac = NUM_ONE - fac\n        assert(fac <= NUM_ONE)\n        return (self.segment[0][Y] * fac) + (self.segment[1][Y] * ifac)\n\n    @staticmethod\n    def Compare(sweep_line, this, that):\n        if this is that:\n            return 0\n        if USE_DEBUG:\n            if this.other is that:\n                return 0\n        current_point_x = sweep_line._current_event_point_x\n        this_y = this.y_intercept_x(current_point_x)\n        that_y = that.y_intercept_x(current_point_x)\n        # print(this_y, that_y)\n        if USE_VERTICAL:\n            if this_y is None:\n                this_y = this.point[Y]\n            if that_y is None:\n                that_y = that.point[Y]\n\n        delta_y = this_y - that_y\n\n        assert((delta_y < NUM_ZERO) == (this_y < that_y))\n        # NOTE, VERY IMPORTANT TO USE EPSILON HERE!\n        # otherwise w/ float precision errors we get incorrect comparisons\n        # can get very strange & hard to debug output without this.\n        if abs(delta_y) > NUM_EPS:\n            return -1 if (delta_y < NUM_ZERO) else 1\n        else:\n            this_slope = this.slope\n            that_slope = that.slope\n            if this_slope != that_slope:\n                if sweep_line._before:\n                    return -1 if (this_slope > that_slope) else 1\n                else:\n                    return 1 if (this_slope > that_slope) else -1\n\n        delta_x_p1 = this.segment[0][X] - that.segment[0][X]\n        if delta_x_p1 != NUM_ZERO:\n            return -1 if (delta_x_p1 < NUM_ZERO) else 1\n\n        delta_x_p2 = this.segment[1][X] - that.segment[1][X]\n        if delta_x_p2 != NUM_ZERO:\n            return -1 if (delta_x_p2 < NUM_ZERO) else 1\n\n        return 0\n\n    def __repr__(self):\n        if self.segment is not None:\n            return (\"Event(0x%x, s0=%r, s1=%r, p=%r, type=%d, slope=%r)\" % (\n                id(self),\n                self.segment[0], self.segment[1],\n                self.point,\n                self.type,\n                self.slope,\n                ))\n        else:\n            return (\"Event(0x%x, s0=%r, s1=%r, p=%r, type=%d, slope=%r)\" % (\n                id(self),\n                \"None\", \"None\",\n                self.point,\n                self.type,\n                self.slope,\n                ))\n\n\nclass SweepLine:\n    __slots__ = (\n        # A map holding all intersection points mapped to the Events\n        # that form these intersections.\n        # {Point: set(Event, ...), ...}\n        \"intersections\",\n        \"queue\",\n\n        # Events (sorted set of ordered events, no values)\n        #\n        # note: START & END events are considered the same so checking if an event is in the tree\n        # will return true if its opposite side is found.\n        # This is essential for the algorithm to work, and why we don't explicitly remove START events.\n        # Instead, the END events are never added to the current sweep, and removing them also removes the start.\n        \"_events_current_sweep\",\n        # The point of the current Event.\n        \"_current_event_point_x\",\n        # A flag to indicate if we're slightly before or after the line.\n        \"_before\",\n        )\n\n    def __init__(self):\n        self.intersections = {}\n\n        self._current_event_point_x = None\n        self._events_current_sweep = RBTree(cmp=Event.Compare, cmp_data=self)\n        self._before = True\n\n    def get_intersections(self):\n        \"\"\"\n        Return a list of unordered intersection points.\n        \"\"\"\n        if Real is float:\n            return list(self.intersections.keys())\n        else:\n            return [(float(p[0]), float(p[1])) for p in self.intersections.keys()]\n\n    # Not essential for implementing this algorithm, but useful.\n    def get_intersections_with_segments(self):\n        \"\"\"\n        Return a list of unordered intersection '(point, segment)' pairs,\n        where segments may contain 2 or more values.\n        \"\"\"\n        if Real is float:\n            return [\n                (p, [event.segment for event in event_set])\n                for p, event_set in self.intersections.items()\n            ]\n        else:\n            return [\n                (\n                    (float(p[0]), float(p[1])),\n                    [((float(event.segment[0][0]), float(event.segment[0][1])),\n                      (float(event.segment[1][0]), float(event.segment[1][1])))\n                     for event in event_set],\n                )\n                for p, event_set in self.intersections.items()\n            ]\n\n    # Checks if an intersection exists between two Events 'a' and 'b'.\n    def _check_intersection(self, a, b):\n        # Return immediately in case either of the events is null, or\n        # if one of them is an INTERSECTION event.\n        if ((a is None or b is None) or\n                (a.type == Event.Type.INTERSECTION) or\n                (b.type == Event.Type.INTERSECTION)):\n\n            return\n\n        if a is b:\n            return\n\n        # Get the intersection point between 'a' and 'b'.\n        p = isect_seg_seg_v2_point(\n                a.segment[0], a.segment[1],\n                b.segment[0], b.segment[1])\n\n        # No intersection exists.\n        if p is None:\n            return\n\n        # If the intersection is formed by both the segment endings, AND\n        # USE_IGNORE_SEGMENT_ENDINGS is true,\n        # return from this method.\n        if USE_IGNORE_SEGMENT_ENDINGS:\n            if ((len_squared_v2v2(p, a.segment[0]) < NUM_EPS_SQ or\n                 len_squared_v2v2(p, a.segment[1]) < NUM_EPS_SQ) and\n                (len_squared_v2v2(p, b.segment[0]) < NUM_EPS_SQ or\n                 len_squared_v2v2(p, b.segment[1]) < NUM_EPS_SQ)):\n\n                return\n\n        # Add the intersection.\n        events_for_point = self.intersections.pop(p, set())\n        is_new = len(events_for_point) == 0\n        events_for_point.add(a)\n        events_for_point.add(b)\n        self.intersections[p] = events_for_point\n\n        # If the intersection occurs to the right of the sweep line, OR\n        # if the intersection is on the sweep line and it's above the\n        # current event-point, add it as a new Event to the queue.\n        if is_new and p[X] >= self._current_event_point_x:\n            event_isect = Event(Event.Type.INTERSECTION, p, None, None)\n            self.queue.offer(p, event_isect)\n\n    def _sweep_to(self, p):\n        if p[X] == self._current_event_point_x:\n            # happens in rare cases,\n            # we can safely ignore\n            return\n\n        self._current_event_point_x = p[X]\n\n    def insert(self, event):\n        assert(event not in self._events_current_sweep)\n        assert(not USE_VERTICAL or event.type != Event.Type.START_VERTICAL)\n        if USE_DEBUG:\n            assert(event.in_sweep == False)\n            assert(event.other.in_sweep == False)\n\n        self._events_current_sweep.insert(event, None)\n\n        if USE_DEBUG:\n            event.in_sweep = True\n            event.other.in_sweep = True\n\n    def remove(self, event):\n        try:\n            self._events_current_sweep.remove(event)\n            if USE_DEBUG:\n                assert(event.in_sweep == True)\n                assert(event.other.in_sweep == True)\n                event.in_sweep = False\n                event.other.in_sweep = False\n            return True\n        except KeyError:\n            if USE_DEBUG:\n                assert(event.in_sweep == False)\n                assert(event.other.in_sweep == False)\n            return False\n\n    def above(self, event):\n        return self._events_current_sweep.succ_key(event, None)\n\n    def below(self, event):\n        return self._events_current_sweep.prev_key(event, None)\n\n    '''\n    def above_all(self, event):\n        while True:\n            event = self.above(event)\n            if event is None:\n                break\n            yield event\n    '''\n\n    def above_all(self, event):\n        # assert(event not in self._events_current_sweep)\n        return self._events_current_sweep.key_slice(event, None, reverse=False)\n\n    def handle(self, p, events_current):\n        if len(events_current) == 0:\n            return\n        # done already\n        # self._sweep_to(events_current[0])\n        assert(p[0] == self._current_event_point_x)\n\n        if not USE_IGNORE_SEGMENT_ENDINGS:\n            if len(events_current) > 1:\n                for i in range(0, len(events_current) - 1):\n                    for j in range(i + 1, len(events_current)):\n                        self._check_intersection(\n                                events_current[i], events_current[j])\n\n        for e in events_current:\n            self.handle_event(e)\n\n    def handle_event(self, event):\n        t = event.type\n        if t == Event.Type.START:\n            # print(\"  START\")\n            self._before = False\n            self.insert(event)\n\n            e_above = self.above(event)\n            e_below = self.below(event)\n\n            self._check_intersection(event, e_above)\n            self._check_intersection(event, e_below)\n            if USE_PARANOID:\n                self._check_intersection(e_above, e_below)\n\n        elif t == Event.Type.END:\n            # print(\"  END\")\n            self._before = True\n\n            e_above = self.above(event)\n            e_below = self.below(event)\n\n            self.remove(event)\n\n            self._check_intersection(e_above, e_below)\n            if USE_PARANOID:\n                self._check_intersection(event, e_above)\n                self._check_intersection(event, e_below)\n\n        elif t == Event.Type.INTERSECTION:\n            # print(\"  INTERSECTION\")\n            self._before = True\n            event_set = self.intersections[event.point]\n            # note: events_current aren't sorted.\n            reinsert_stack = []  # Stack\n            for e in event_set:\n                # Since we know the Event wasn't already removed,\n                # we want to insert it later on.\n                if self.remove(e):\n                    reinsert_stack.append(e)\n            self._before = False\n\n            # Insert all Events that we were able to remove.\n            while reinsert_stack:\n                e = reinsert_stack.pop()\n\n                self.insert(e)\n\n                e_above = self.above(e)\n                e_below = self.below(e)\n\n                self._check_intersection(e, e_above)\n                self._check_intersection(e, e_below)\n                if USE_PARANOID:\n                    self._check_intersection(e_above, e_below)\n        elif (USE_VERTICAL and\n                (t == Event.Type.START_VERTICAL)):\n\n            # just check sanity\n            assert(event.segment[0][X] == event.segment[1][X])\n            assert(event.segment[0][Y] <= event.segment[1][Y])\n\n            # In this case we only need to find all segments in this span.\n            y_above_max = event.segment[1][Y]\n\n            # self.insert(event)\n            for e_above in self.above_all(event):\n                if e_above.type == Event.Type.START_VERTICAL:\n                    continue\n                y_above = e_above.y_intercept_x(\n                        self._current_event_point_x)\n                if USE_IGNORE_SEGMENT_ENDINGS:\n                    if y_above >= y_above_max - NUM_EPS:\n                        break\n                else:\n                    if y_above > y_above_max:\n                        break\n\n                # We know this intersects,\n                # so we could use a faster function now:\n                # ix = (self._current_event_point_x, y_above)\n                # ...however best use existing functions\n                # since it does all sanity checks on endpoints... etc.\n                self._check_intersection(event, e_above)\n\n            # self.remove(event)\n\n\nclass EventQueue:\n    __slots__ = (\n        # note: we only ever pop_min, this could use a 'heap' structure.\n        # The sorted map holding the points -> event list\n        # [Point: Event] (tree)\n        \"events_scan\",\n        )\n\n    def __init__(self, segments, line):\n        self.events_scan = RBTree()\n        # segments = [s for s in segments if s[0][0] != s[1][0] and s[0][1] != s[1][1]]\n\n        for s in segments:\n            assert(s[0][X] <= s[1][X])\n\n            slope = slope_v2v2(*s)\n\n            if s[0] == s[1]:\n                pass\n            elif USE_VERTICAL and (s[0][X] == s[1][X]):\n                e_start = Event(Event.Type.START_VERTICAL, s[0], s, slope)\n\n                if USE_DEBUG:\n                    e_start.other = e_start  # FAKE, avoid error checking\n\n                self.offer(s[0], e_start)\n            else:\n                e_start = Event(Event.Type.START, s[0], s, slope)\n                e_end   = Event(Event.Type.END,   s[1], s, slope)\n\n                if USE_DEBUG:\n                    e_start.other = e_end\n                    e_end.other = e_start\n\n                self.offer(s[0], e_start)\n                self.offer(s[1], e_end)\n\n        line.queue = self\n\n    def offer(self, p, e):\n        \"\"\"\n        Offer a new event ``s`` at point ``p`` in this queue.\n        \"\"\"\n        existing = self.events_scan.setdefault(\n                p, ([], [], [], []) if USE_VERTICAL else\n                   ([], [], []))\n        # Can use double linked-list for easy insertion at beginning/end\n        '''\n        if e.type == Event.Type.END:\n            existing.insert(0, e)\n        else:\n            existing.append(e)\n        '''\n\n        existing[e.type].append(e)\n\n    # return a set of events\n    def poll(self):\n        \"\"\"\n        Get, and remove, the first (lowest) item from this queue.\n\n        :return: the first (lowest) item from this queue.\n        :rtype: Point, Event pair.\n        \"\"\"\n        assert(len(self.events_scan) != 0)\n        p, events_current = self.events_scan.pop_min()\n        return p, events_current\n\n\ndef isect_segments_impl(segments, include_segments=False):\n    # order points left -> right\n    if Real is float:\n        segments = [\n            # in nearly all cases, comparing X is enough,\n            # but compare Y too for vertical lines\n            (s[0], s[1]) if (s[0] <= s[1]) else\n            (s[1], s[0])\n            for s in segments]\n    else:\n        segments = [\n            # in nearly all cases, comparing X is enough,\n            # but compare Y too for vertical lines\n            (\n                (Real(s[0][0]), Real(s[0][1])),\n                (Real(s[1][0]), Real(s[1][1])),\n            ) if (s[0] <= s[1]) else\n            (\n                (Real(s[1][0]), Real(s[1][1])),\n                (Real(s[0][0]), Real(s[0][1])),\n            )\n            for s in segments]\n\n    sweep_line = SweepLine()\n    queue = EventQueue(segments, sweep_line)\n\n    while len(queue.events_scan) > 0:\n        if USE_VERBOSE:\n            print(len(queue.events_scan), sweep_line._current_event_point_x)\n        p, e_ls = queue.poll()\n        for events_current in e_ls:\n            if events_current:\n                sweep_line._sweep_to(p)\n                sweep_line.handle(p, events_current)\n\n    if include_segments is False:\n        return sweep_line.get_intersections()\n    else:\n        return sweep_line.get_intersections_with_segments()\n\n\ndef isect_polygon_impl(points, include_segments=False):\n    n = len(points)\n    segments = [\n        (tuple(points[i]), tuple(points[(i + 1) % n]))\n        for i in range(n)]\n    return isect_segments_impl(segments, include_segments=include_segments)\n\n\ndef isect_segments(segments):\n    return isect_segments_impl(segments, include_segments=False)\n\n\ndef isect_polygon(segments):\n    return isect_polygon_impl(segments, include_segments=False)\n\n\ndef isect_segments_include_segments(segments):\n    return isect_segments_impl(segments, include_segments=True)\n\n\ndef isect_polygon_include_segments(segments):\n    return isect_polygon_impl(segments, include_segments=True)\n\n\n# ----------------------------------------------------------------------------\n# 2D math utilities\n\n\ndef slope_v2v2(p1, p2):\n    if p1[X] == p2[X]:\n        if p1[Y] < p2[Y]:\n            return NUM_INF\n        else:\n            return -NUM_INF\n    else:\n        return (p2[Y] - p1[Y]) / (p2[X] - p1[X])\n\n\ndef sub_v2v2(a, b):\n    return (\n        a[0] - b[0],\n        a[1] - b[1])\n\n\ndef dot_v2v2(a, b):\n    return (\n        (a[0] * b[0]) +\n        (a[1] * b[1]))\n\n\ndef len_squared_v2v2(a, b):\n    c = sub_v2v2(a, b)\n    return dot_v2v2(c, c)\n\n\ndef line_point_factor_v2(p, l1, l2, default=NUM_ZERO):\n    u = sub_v2v2(l2, l1)\n    h = sub_v2v2(p, l1)\n    dot = dot_v2v2(u, u)\n    return (dot_v2v2(u, h) / dot) if dot != NUM_ZERO else default\n\n\ndef isect_seg_seg_v2_point(v1, v2, v3, v4, bias=NUM_ZERO):\n    # Only for predictability and hashable point when same input is given\n    if v1 > v2:\n        v1, v2 = v2, v1\n    if v3 > v4:\n        v3, v4 = v4, v3\n\n    if (v1, v2) > (v3, v4):\n        v1, v2, v3, v4 = v3, v4, v1, v2\n\n    div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0])\n    if div == NUM_ZERO:\n        return None\n\n    vi = (((v3[0] - v4[0]) *\n           (v1[0] * v2[1] - v1[1] * v2[0]) - (v1[0] - v2[0]) *\n           (v3[0] * v4[1] - v3[1] * v4[0])) / div,\n          ((v3[1] - v4[1]) *\n           (v1[0] * v2[1] - v1[1] * v2[0]) - (v1[1] - v2[1]) *\n           (v3[0] * v4[1] - v3[1] * v4[0])) / div,\n          )\n\n    fac = line_point_factor_v2(vi, v1, v2, default=-NUM_ONE)\n    if fac < NUM_ZERO - bias or fac > NUM_ONE + bias:\n        return None\n\n    fac = line_point_factor_v2(vi, v3, v4, default=-NUM_ONE)\n    if fac < NUM_ZERO - bias or fac > NUM_ONE + bias:\n        return None\n\n    # vi = round(vi[X], 8), round(vi[Y], 8)\n    return vi\n\n\n# ----------------------------------------------------------------------------\n# Simple naive line intersect, (for testing only)\n\n\ndef isect_segments__naive(segments):\n    \"\"\"\n    Brute force O(n2) version of ``isect_segments`` for test validation.\n    \"\"\"\n    isect = []\n\n    # order points left -> right\n    if Real is float:\n        segments = [\n            (s[0], s[1]) if s[0][X] <= s[1][X] else\n            (s[1], s[0])\n            for s in segments]\n    else:\n        segments = [\n            (\n                (Real(s[0][0]), Real(s[0][1])),\n                (Real(s[1][0]), Real(s[1][1])),\n            ) if (s[0] <= s[1]) else\n            (\n                (Real(s[1][0]), Real(s[1][1])),\n                (Real(s[0][0]), Real(s[0][1])),\n            )\n            for s in segments]\n\n    n = len(segments)\n\n    for i in range(n):\n        a0, a1 = segments[i]\n        for j in range(i + 1, n):\n            b0, b1 = segments[j]\n            if a0 not in (b0, b1) and a1 not in (b0, b1):\n                ix = isect_seg_seg_v2_point(a0, a1, b0, b1)\n                if ix is not None:\n                    # USE_IGNORE_SEGMENT_ENDINGS handled already\n                    isect.append(ix)\n\n    return isect\n\n\ndef isect_polygon__naive(points):\n    \"\"\"\n    Brute force O(n2) version of ``isect_polygon`` for test validation.\n    \"\"\"\n    isect = []\n\n    n = len(points)\n\n    if Real is float:\n        pass\n    else:\n        points = [(Real(p[0]), Real(p[1])) for p in points]\n\n\n    for i in range(n):\n        a0, a1 = points[i], points[(i + 1) % n]\n        for j in range(i + 1, n):\n            b0, b1 = points[j], points[(j + 1) % n]\n            if a0 not in (b0, b1) and a1 not in (b0, b1):\n                ix = isect_seg_seg_v2_point(a0, a1, b0, b1)\n                if ix is not None:\n\n                    if USE_IGNORE_SEGMENT_ENDINGS:\n                        if ((len_squared_v2v2(ix, a0) < NUM_EPS_SQ or\n                             len_squared_v2v2(ix, a1) < NUM_EPS_SQ) and\n                            (len_squared_v2v2(ix, b0) < NUM_EPS_SQ or\n                             len_squared_v2v2(ix, b1) < NUM_EPS_SQ)):\n                            continue\n\n                    isect.append(ix)\n\n    return isect\n\n\n# ----------------------------------------------------------------------------\n# Inline Libs\n#\n# bintrees: 2.0.2, extracted from:\n# http://pypi.python.org/pypi/bintrees\n#\n# - Removed unused functions, such as slicing and range iteration.\n# - Added 'cmp' and and 'cmp_data' arguments,\n#   so we can define our own comparison that takes an arg.\n#   Needed for sweep-line.\n# - Added support for 'default' arguments for prev_item/succ_item,\n#   so we can avoid exception handling.\n\n# -------\n# ABCTree\n\nfrom operator import attrgetter\n_sentinel = object()\n\n\nclass _ABCTree(object):\n    def __init__(self, items=None, cmp=None, cmp_data=None):\n        \"\"\"T.__init__(...) initializes T; see T.__class__.__doc__ for signature\"\"\"\n        self._root = None\n        self._count = 0\n        if cmp is None:\n            def cmp(cmp_data, a, b):\n                if a < b:\n                    return -1\n                elif a > b:\n                    return 1\n                else:\n                    return 0\n        self._cmp = cmp\n        self._cmp_data = cmp_data\n        if items is not None:\n            self.update(items)\n\n    def clear(self):\n        \"\"\"T.clear() -> None.  Remove all items from T.\"\"\"\n        def _clear(node):\n            if node is not None:\n                _clear(node.left)\n                _clear(node.right)\n                node.free()\n        _clear(self._root)\n        self._count = 0\n        self._root = None\n\n    @property\n    def count(self):\n        \"\"\"Get items count.\"\"\"\n        return self._count\n\n    def get_value(self, key):\n        node = self._root\n        while node is not None:\n            cmp = self._cmp(self._cmp_data, key, node.key)\n            if cmp == 0:\n                return node.value\n            elif cmp < 0:\n                node = node.left\n            else:\n                node = node.right\n        raise KeyError(str(key))\n\n    def pop_item(self):\n        \"\"\"T.pop_item() -> (k, v), remove and return some (key, value) pair as a\n        2-tuple; but raise KeyError if T is empty.\n        \"\"\"\n        if self.is_empty():\n            raise KeyError(\"pop_item(): tree is empty\")\n        node = self._root\n        while True:\n            if node.left is not None:\n                node = node.left\n            elif node.right is not None:\n                node = node.right\n            else:\n                break\n        key = node.key\n        value = node.value\n        self.remove(key)\n        return key, value\n    popitem = pop_item  # for compatibility  to dict()\n\n    def min_item(self):\n        \"\"\"Get item with min key of tree, raises ValueError if tree is empty.\"\"\"\n        if self.is_empty():\n            raise ValueError(\"Tree is empty\")\n        node = self._root\n        while node.left is not None:\n            node = node.left\n        return node.key, node.value\n\n    def max_item(self):\n        \"\"\"Get item with max key of tree, raises ValueError if tree is empty.\"\"\"\n        if self.is_empty():\n            raise ValueError(\"Tree is empty\")\n        node = self._root\n        while node.right is not None:\n            node = node.right\n        return node.key, node.value\n\n    def succ_item(self, key, default=_sentinel):\n        \"\"\"Get successor (k,v) pair of key, raises KeyError if key is max key\n        or key does not exist. optimized for pypy.\n        \"\"\"\n        # removed graingets version, because it was little slower on CPython and much slower on pypy\n        # this version runs about 4x faster with pypy than the Cython version\n        # Note: Code sharing of succ_item() and ceiling_item() is possible, but has always a speed penalty.\n        node = self._root\n        succ_node = None\n        while node is not None:\n            cmp = self._cmp(self._cmp_data, key, node.key)\n            if cmp == 0:\n                break\n            elif cmp < 0:\n                if (succ_node is None) or self._cmp(self._cmp_data, node.key, succ_node.key) < 0:\n                    succ_node = node\n                node = node.left\n            else:\n                node = node.right\n\n        if node is None:  # stay at dead end\n            if default is _sentinel:\n                raise KeyError(str(key))\n            return default\n        # found node of key\n        if node.right is not None:\n            # find smallest node of right subtree\n            node = node.right\n            while node.left is not None:\n                node = node.left\n            if succ_node is None:\n                succ_node = node\n            elif self._cmp(self._cmp_data, node.key, succ_node.key) < 0:\n                succ_node = node\n        elif succ_node is None:  # given key is biggest in tree\n            if default is _sentinel:\n                raise KeyError(str(key))\n            return default\n        return succ_node.key, succ_node.value\n\n    def prev_item(self, key, default=_sentinel):\n        \"\"\"Get predecessor (k,v) pair of key, raises KeyError if key is min key\n        or key does not exist. optimized for pypy.\n        \"\"\"\n        # removed graingets version, because it was little slower on CPython and much slower on pypy\n        # this version runs about 4x faster with pypy than the Cython version\n        # Note: Code sharing of prev_item() and floor_item() is possible, but has always a speed penalty.\n        node = self._root\n        prev_node = None\n\n        while node is not None:\n            cmp = self._cmp(self._cmp_data, key, node.key)\n            if cmp == 0:\n                break\n            elif cmp < 0:\n                node = node.left\n            else:\n                if (prev_node is None) or self._cmp(self._cmp_data, prev_node.key, node.key) < 0:\n                    prev_node = node\n                node = node.right\n\n        if node is None:  # stay at dead end (None)\n            if default is _sentinel:\n                raise KeyError(str(key))\n            return default\n        # found node of key\n        if node.left is not None:\n            # find biggest node of left subtree\n            node = node.left\n            while node.right is not None:\n                node = node.right\n            if prev_node is None:\n                prev_node = node\n            elif self._cmp(self._cmp_data, prev_node.key, node.key) < 0:\n                prev_node = node\n        elif prev_node is None:  # given key is smallest in tree\n            if default is _sentinel:\n                raise KeyError(str(key))\n            return default\n        return prev_node.key, prev_node.value\n\n    def __repr__(self):\n        \"\"\"T.__repr__(...) <==> repr(x)\"\"\"\n        tpl = \"%s({%s})\" % (self.__class__.__name__, '%s')\n        return tpl % \", \".join((\"%r: %r\" % item for item in self.items()))\n\n    def __contains__(self, key):\n        \"\"\"k in T -> True if T has a key k, else False\"\"\"\n        try:\n            self.get_value(key)\n            return True\n        except KeyError:\n            return False\n\n    def __len__(self):\n        \"\"\"T.__len__() <==> len(x)\"\"\"\n        return self.count\n\n    def is_empty(self):\n        \"\"\"T.is_empty() -> False if T contains any items else True\"\"\"\n        return self.count == 0\n\n    def set_default(self, key, default=None):\n        \"\"\"T.set_default(k[,d]) -> T.get(k,d), also set T[k]=d if k not in T\"\"\"\n        try:\n            return self.get_value(key)\n        except KeyError:\n            self.insert(key, default)\n            return default\n    setdefault = set_default  # for compatibility to dict()\n\n    def get(self, key, default=None):\n        \"\"\"T.get(k[,d]) -> T[k] if k in T, else d.  d defaults to None.\"\"\"\n        try:\n            return self.get_value(key)\n        except KeyError:\n            return default\n\n    def pop(self, key, *args):\n        \"\"\"T.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n        If key is not found, d is returned if given, otherwise KeyError is raised\n        \"\"\"\n        if len(args) > 1:\n            raise TypeError(\"pop expected at most 2 arguments, got %d\" % (1 + len(args)))\n        try:\n            value = self.get_value(key)\n            self.remove(key)\n            return value\n        except KeyError:\n            if len(args) == 0:\n                raise\n            else:\n                return args[0]\n\n    def prev_key(self, key, default=_sentinel):\n        \"\"\"Get predecessor to key, raises KeyError if key is min key\n        or key does not exist.\n        \"\"\"\n        item = self.prev_item(key, default)\n        return default if item is default else item[0]\n\n    def succ_key(self, key, default=_sentinel):\n        \"\"\"Get successor to key, raises KeyError if key is max key\n        or key does not exist.\n        \"\"\"\n        item = self.succ_item(key, default)\n        return default if item is default else item[0]\n\n    def pop_min(self):\n        \"\"\"T.pop_min() -> (k, v), remove item with minimum key, raise ValueError\n        if T is empty.\n        \"\"\"\n        item = self.min_item()\n        self.remove(item[0])\n        return item\n\n    def pop_max(self):\n        \"\"\"T.pop_max() -> (k, v), remove item with maximum key, raise ValueError\n        if T is empty.\n        \"\"\"\n        item = self.max_item()\n        self.remove(item[0])\n        return item\n\n    def min_key(self):\n        \"\"\"Get min key of tree, raises ValueError if tree is empty. \"\"\"\n        return self.min_item()[0]\n\n    def max_key(self):\n        \"\"\"Get max key of tree, raises ValueError if tree is empty. \"\"\"\n        return self.max_item()[0]\n\n    def key_slice(self, start_key, end_key, reverse=False):\n        \"\"\"T.key_slice(start_key, end_key) -> key iterator:\n        start_key <= key < end_key.\n\n        Yields keys in ascending order if reverse is False else in descending order.\n        \"\"\"\n        return (k for k, v in self.iter_items(start_key, end_key, reverse=reverse))\n\n    def iter_items(self,  start_key=None, end_key=None, reverse=False):\n        \"\"\"Iterates over the (key, value) items of the associated tree,\n        in ascending order if reverse is True, iterate in descending order,\n        reverse defaults to False\"\"\"\n        # optimized iterator (reduced method calls) - faster on CPython but slower on pypy\n\n        if self.is_empty():\n            return []\n        if reverse:\n            return self._iter_items_backward(start_key, end_key)\n        else:\n            return self._iter_items_forward(start_key, end_key)\n\n    def _iter_items_forward(self, start_key=None, end_key=None):\n        for item in self._iter_items(left=attrgetter(\"left\"), right=attrgetter(\"right\"),\n                                     start_key=start_key, end_key=end_key):\n            yield item\n\n    def _iter_items_backward(self, start_key=None, end_key=None):\n        for item in self._iter_items(left=attrgetter(\"right\"), right=attrgetter(\"left\"),\n                                     start_key=start_key, end_key=end_key):\n            yield item\n\n    def _iter_items(self, left=attrgetter(\"left\"), right=attrgetter(\"right\"), start_key=None, end_key=None):\n        node = self._root\n        stack = []\n        go_left = True\n        in_range = self._get_in_range_func(start_key, end_key)\n\n        while True:\n            if left(node) is not None and go_left:\n                stack.append(node)\n                node = left(node)\n            else:\n                if in_range(node.key):\n                    yield node.key, node.value\n                if right(node) is not None:\n                    node = right(node)\n                    go_left = True\n                else:\n                    if not len(stack):\n                        return  # all done\n                    node = stack.pop()\n                    go_left = False\n\n    def _get_in_range_func(self, start_key, end_key):\n        if start_key is None and end_key is None:\n            return lambda x: True\n        else:\n            if start_key is None:\n                start_key = self.min_key()\n            if end_key is None:\n                return (lambda x: self._cmp(self._cmp_data, start_key, x) <= 0)\n            else:\n                return (lambda x: self._cmp(self._cmp_data, start_key, x) <= 0 and\n                        self._cmp(self._cmp_data, x, end_key) < 0)\n\n\n# ------\n# RBTree\n\nclass Node(object):\n    \"\"\"Internal object, represents a tree node.\"\"\"\n    __slots__ = ['key', 'value', 'red', 'left', 'right']\n\n    def __init__(self, key=None, value=None):\n        self.key = key\n        self.value = value\n        self.red = True\n        self.left = None\n        self.right = None\n\n    def free(self):\n        self.left = None\n        self.right = None\n        self.key = None\n        self.value = None\n\n    def __getitem__(self, key):\n        \"\"\"N.__getitem__(key) <==> x[key], where key is 0 (left) or 1 (right).\"\"\"\n        return self.left if key == 0 else self.right\n\n    def __setitem__(self, key, value):\n        \"\"\"N.__setitem__(key, value) <==> x[key]=value, where key is 0 (left) or 1 (right).\"\"\"\n        if key == 0:\n            self.left = value\n        else:\n            self.right = value\n\n\nclass RBTree(_ABCTree):\n    \"\"\"\n    RBTree implements a balanced binary tree with a dict-like interface.\n\n    see: http://en.wikipedia.org/wiki/Red_black_tree\n    \"\"\"\n    @staticmethod\n    def is_red(node):\n        if (node is not None) and node.red:\n            return True\n        else:\n            return False\n\n    @staticmethod\n    def jsw_single(root, direction):\n        other_side = 1 - direction\n        save = root[other_side]\n        root[other_side] = save[direction]\n        save[direction] = root\n        root.red = True\n        save.red = False\n        return save\n\n    @staticmethod\n    def jsw_double(root, direction):\n        other_side = 1 - direction\n        root[other_side] = RBTree.jsw_single(root[other_side], other_side)\n        return RBTree.jsw_single(root, direction)\n\n    def _new_node(self, key, value):\n        \"\"\"Create a new tree node.\"\"\"\n        self._count += 1\n        return Node(key, value)\n\n    def insert(self, key, value):\n        \"\"\"T.insert(key, value) <==> T[key] = value, insert key, value into tree.\"\"\"\n        if self._root is None:  # Empty tree case\n            self._root = self._new_node(key, value)\n            self._root.red = False  # make root black\n            return\n\n        head = Node()  # False tree root\n        grand_parent = None\n        grand_grand_parent = head\n        parent = None  # parent\n        direction = 0\n        last = 0\n\n        # Set up helpers\n        grand_grand_parent.right = self._root\n        node = grand_grand_parent.right\n        # Search down the tree\n        while True:\n            if node is None:  # Insert new node at the bottom\n                node = self._new_node(key, value)\n                parent[direction] = node\n            elif RBTree.is_red(node.left) and RBTree.is_red(node.right):  # Color flip\n                node.red = True\n                node.left.red = False\n                node.right.red = False\n\n            # Fix red violation\n            if RBTree.is_red(node) and RBTree.is_red(parent):\n                direction2 = 1 if grand_grand_parent.right is grand_parent else 0\n                if node is parent[last]:\n                    grand_grand_parent[direction2] = RBTree.jsw_single(grand_parent, 1 - last)\n                else:\n                    grand_grand_parent[direction2] = RBTree.jsw_double(grand_parent, 1 - last)\n\n            # Stop if found\n            if self._cmp(self._cmp_data, key, node.key) == 0:\n                node.value = value  # set new value for key\n                break\n\n            last = direction\n            direction = 0 if (self._cmp(self._cmp_data, key, node.key) < 0) else 1\n            # Update helpers\n            if grand_parent is not None:\n                grand_grand_parent = grand_parent\n            grand_parent = parent\n            parent = node\n            node = node[direction]\n\n        self._root = head.right  # Update root\n        self._root.red = False  # make root black\n\n    def remove(self, key):\n        \"\"\"T.remove(key) <==> del T[key], remove item <key> from tree.\"\"\"\n        if self._root is None:\n            raise KeyError(str(key))\n        head = Node()  # False tree root\n        node = head\n        node.right = self._root\n        parent = None\n        grand_parent = None\n        found = None  # Found item\n        direction = 1\n\n        # Search and push a red down\n        while node[direction] is not None:\n            last = direction\n\n            # Update helpers\n            grand_parent = parent\n            parent = node\n            node = node[direction]\n\n            direction = 1 if (self._cmp(self._cmp_data, node.key, key) < 0) else 0\n\n            # Save found node\n            if self._cmp(self._cmp_data, key, node.key) == 0:\n                found = node\n\n            # Push the red node down\n            if not RBTree.is_red(node) and not RBTree.is_red(node[direction]):\n                if RBTree.is_red(node[1 - direction]):\n                    parent[last] = RBTree.jsw_single(node, direction)\n                    parent = parent[last]\n                elif not RBTree.is_red(node[1 - direction]):\n                    sibling = parent[1 - last]\n                    if sibling is not None:\n                        if (not RBTree.is_red(sibling[1 - last])) and (not RBTree.is_red(sibling[last])):\n                            # Color flip\n                            parent.red = False\n                            sibling.red = True\n                            node.red = True\n                        else:\n                            direction2 = 1 if grand_parent.right is parent else 0\n                            if RBTree.is_red(sibling[last]):\n                                grand_parent[direction2] = RBTree.jsw_double(parent, last)\n                            elif RBTree.is_red(sibling[1-last]):\n                                grand_parent[direction2] = RBTree.jsw_single(parent, last)\n                            # Ensure correct coloring\n                            grand_parent[direction2].red = True\n                            node.red = True\n                            grand_parent[direction2].left.red = False\n                            grand_parent[direction2].right.red = False\n\n        # Replace and remove if found\n        if found is not None:\n            found.key = node.key\n            found.value = node.value\n            parent[int(parent.right is node)] = node[int(node.left is None)]\n            node.free()\n            self._count -= 1\n\n        # Update root and make it black\n        self._root = head.right\n        if self._root is not None:\n            self._root.red = False\n        if not found:\n            raise KeyError(str(key))\n"
  },
  {
    "path": "imgaug/imgaug.py",
    "content": "\"\"\"Collection of basic functions used throughout imgaug.\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport math\nimport numbers\nimport sys\nimport os\nimport types\nimport functools\n# collections.abc exists since 3.3 and is expected to be used for 3.8+\ntry:\n    from collections.abc import Iterable\nexcept ImportError:\n    from collections import Iterable\n\nimport numpy as np\nimport cv2\nimport six\nimport six.moves as sm\nimport skimage.draw\nimport skimage.measure\ntry:\n    import numba\nexcept ImportError:\n    numba = None\n\n\nALL = \"ALL\"\n\nDEFAULT_FONT_FP = os.path.join(\n    os.path.dirname(os.path.abspath(__file__)),\n    \"DejaVuSans.ttf\"\n)\n\n\n# to check if a dtype instance is among these dtypes, use e.g.\n# `dtype.type in  NP_FLOAT_TYPES` do not just use `dtype in NP_FLOAT_TYPES` as\n# that would fail\nNP_FLOAT_TYPES = set(np.sctypes[\"float\"])\nNP_INT_TYPES = set(np.sctypes[\"int\"])\nNP_UINT_TYPES = set(np.sctypes[\"uint\"])\n\nIMSHOW_BACKEND_DEFAULT = \"matplotlib\"\n\nIMRESIZE_VALID_INTERPOLATIONS = [\n    \"nearest\", \"linear\", \"area\", \"cubic\",\n    cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_AREA, cv2.INTER_CUBIC]\n\n# Cache dict to save kernels used for pooling.\n# Added in 0.5.0.\n_POOLING_KERNELS_CACHE = {}\n\n# Added in 0.5.0.\n_NUMBA_INSTALLED = numba is not None\n\n# Added in 0.5.0.\n_UINT8_DTYPE = np.dtype(\"uint8\")\n\n\n###############################################################################\n# Helpers for deprecation\n###############################################################################\n\nclass DeprecationWarning(Warning):  # pylint: disable=redefined-builtin\n    \"\"\"Warning for deprecated calls.\n\n    Since python 2.7 DeprecatedWarning is silent by default. So we define\n    our own DeprecatedWarning here so that it is not silent by default.\n\n    \"\"\"\n\n\ndef warn(msg, category=UserWarning, stacklevel=2):\n    \"\"\"Generate a a warning with stacktrace.\n\n    Parameters\n    ----------\n    msg : str\n        The message of the warning.\n\n    category : class\n        The class of the warning to produce.\n\n    stacklevel : int, optional\n        How many steps above this function to \"jump\" in the stacktrace when\n        displaying file and line number of the error message.\n        Usually ``2``.\n\n    \"\"\"\n    import warnings\n    warnings.warn(msg, category=category, stacklevel=stacklevel)\n\n\ndef warn_deprecated(msg, stacklevel=2):\n    \"\"\"Generate a non-silent deprecation warning with stacktrace.\n\n    The used warning is ``imgaug.imgaug.DeprecationWarning``.\n\n    Parameters\n    ----------\n    msg : str\n        The message of the warning.\n\n    stacklevel : int, optional\n        How many steps above this function to \"jump\" in the stacktrace when\n        displaying file and line number of the error message.\n        Usually ``2``\n\n    \"\"\"\n    warn(msg, category=DeprecationWarning, stacklevel=stacklevel)\n\n\nclass deprecated(object):  # pylint: disable=invalid-name\n    \"\"\"Decorator to mark deprecated functions with warning.\n\n    Adapted from\n    <https://github.com/scikit-image/scikit-image/blob/master/skimage/_shared/utils.py>.\n\n    Parameters\n    ----------\n    alt_func : None or str, optional\n        If given, tell user what function to use instead.\n\n    behavior : {'warn', 'raise'}, optional\n        Behavior during call to deprecated function: ``warn`` means that the\n        user is warned that the function is deprecated; ``raise`` means that\n        an error is raised.\n\n    removed_version : None or str, optional\n        The package version in which the deprecated function will be removed.\n\n    comment : None or str, optional\n        An optional comment that will be appended to the warning message.\n\n    \"\"\"\n\n    def __init__(self, alt_func=None, behavior=\"warn\", removed_version=None,\n                 comment=None):\n        self.alt_func = alt_func\n        self.behavior = behavior\n        self.removed_version = removed_version\n        self.comment = comment\n\n    def __call__(self, func):\n        alt_msg = None\n        if self.alt_func is not None:\n            alt_msg = \"Use ``%s`` instead.\" % (self.alt_func,)\n\n        rmv_msg = None\n        if self.removed_version is not None:\n            rmv_msg = \"It will be removed in version %s.\" % (\n                self.removed_version,)\n\n        comment_msg = None\n        if self.comment is not None and len(self.comment) > 0:\n            comment_msg = \"%s.\" % (self.comment.rstrip(\". \"),)\n\n        addendum = \" \".join([submsg\n                             for submsg\n                             in [alt_msg, rmv_msg, comment_msg]\n                             if submsg is not None])\n\n        @functools.wraps(func)\n        def wrapped(*args, **kwargs):\n            # getargpec() is deprecated\n            # pylint: disable=deprecated-method\n\n            # TODO add class name if class method\n            import inspect\n            # arg_names = func.__code__.co_varnames\n\n            # getargspec() was deprecated in py3, but doesn't exist in py2\n            if hasattr(inspect, \"getfullargspec\"):\n                arg_names = inspect.getfullargspec(func)[0]\n            else:\n                arg_names = inspect.getargspec(func)[0]\n\n            if \"self\" in arg_names or \"cls\" in arg_names:\n                main_msg = \"Method ``%s.%s()`` is deprecated.\" % (\n                    args[0].__class__.__name__, func.__name__)\n            else:\n                main_msg = \"Function ``%s()`` is deprecated.\" % (\n                    func.__name__,)\n\n            msg = (main_msg + \" \" + addendum).rstrip(\" \").replace(\"``\", \"`\")\n\n            if self.behavior == \"warn\":\n                warn_deprecated(msg, stacklevel=3)\n            elif self.behavior == \"raise\":\n                raise DeprecationWarning(msg)\n            return func(*args, **kwargs)\n\n        # modify doc string to display deprecation warning\n        doc = \"**Deprecated**. \" + addendum\n        if wrapped.__doc__ is None:\n            wrapped.__doc__ = doc\n        else:\n            wrapped.__doc__ = doc + \"\\n\\n    \" + wrapped.__doc__\n\n        return wrapped\n\n###############################################################################\n\n\ndef is_np_array(val):\n    \"\"\"Check whether a variable is a numpy array.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` if the variable is a numpy array. Otherwise ``False``.\n\n    \"\"\"\n    # using np.generic here via isinstance(val, (np.ndarray, np.generic))\n    # seems to also fire for scalar numpy values even though those are not\n    # arrays\n    return isinstance(val, np.ndarray)\n\n\ndef is_np_scalar(val):\n    \"\"\"Check whether a variable is a numpy scalar.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` if the variable is a numpy scalar. Otherwise ``False``.\n\n    \"\"\"\n    # Note that isscalar() alone also fires for thinks like python strings\n    # or booleans.\n    # The isscalar() was added to make this function not fire for non-scalar\n    # numpy types. Not sure if it is necessary.\n    return isinstance(val, np.generic) and np.isscalar(val)\n\n\ndef is_single_integer(val):\n    \"\"\"Check whether a variable is an ``int``.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` if the variable is an ``int``. Otherwise ``False``.\n\n    \"\"\"\n    return isinstance(val, numbers.Integral) and not isinstance(val, bool)\n\n\ndef is_single_float(val):\n    \"\"\"Check whether a variable is a ``float``.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` if the variable is a ``float``. Otherwise ``False``.\n\n    \"\"\"\n    return (\n        isinstance(val, numbers.Real)\n        and not is_single_integer(val)\n        and not isinstance(val, bool)\n    )\n\n\ndef is_single_number(val):\n    \"\"\"Check whether a variable is a ``number``, i.e. an ``int`` or ``float``.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` if the variable is a ``number``. Otherwise ``False``.\n\n    \"\"\"\n    return is_single_integer(val) or is_single_float(val)\n\n\ndef is_iterable(val):\n    \"\"\"\n    Checks whether a variable is iterable.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` if the variable is an iterable. Otherwise ``False``.\n\n    \"\"\"\n    return isinstance(val, Iterable)\n\n\n# TODO convert to is_single_string() or rename is_single_integer/float/number()\ndef is_string(val):\n    \"\"\"Check whether a variable is a string.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` if the variable is a string. Otherwise ``False``.\n\n    \"\"\"\n    return isinstance(val, six.string_types)\n\n\ndef is_single_bool(val):\n    \"\"\"Check whether a variable is a ``bool``.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` if the variable is a ``bool``. Otherwise ``False``.\n\n    \"\"\"\n    # pylint: disable=unidiomatic-typecheck\n    return type(val) == type(True)\n\n\ndef is_integer_array(val):\n    \"\"\"Check whether a variable is a numpy integer array.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` if the variable is a numpy integer array. Otherwise ``False``.\n\n    \"\"\"\n    return is_np_array(val) and issubclass(val.dtype.type, np.integer)\n\n\ndef is_float_array(val):\n    \"\"\"Check whether a variable is a numpy float array.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` if the variable is a numpy float array. Otherwise ``False``.\n\n    \"\"\"\n    return is_np_array(val) and issubclass(val.dtype.type, np.floating)\n\n\ndef is_callable(val):\n    \"\"\"Check whether a variable is a callable, e.g. a function.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` if the variable is a callable. Otherwise ``False``.\n\n    \"\"\"\n    # python 3.x with x <= 2 does not support callable(), apparently\n    if sys.version_info[0] == 3 and sys.version_info[1] <= 2:\n        return hasattr(val, '__call__')\n    return callable(val)\n\n\ndef is_generator(val):\n    \"\"\"Check whether a variable is a generator.\n\n    Parameters\n    ----------\n    val\n        The variable to check.\n\n    Returns\n    -------\n    bool\n        ``True`` is the variable is a generator. Otherwise ``False``.\n\n    \"\"\"\n    return isinstance(val, types.GeneratorType)\n\n\ndef flatten(nested_iterable):\n    \"\"\"Flatten arbitrarily nested lists/tuples.\n\n    Code partially taken from https://stackoverflow.com/a/10824420.\n\n    Parameters\n    ----------\n    nested_iterable\n        A ``list`` or ``tuple`` of arbitrarily nested values.\n\n    Yields\n    ------\n    any\n        All values in `nested_iterable`, flattened.\n\n    \"\"\"\n    # don't just check if something is iterable here, because then strings\n    # and arrays will be split into their characters and components\n    if not isinstance(nested_iterable, (list, tuple)):\n        yield nested_iterable\n    else:\n        for i in nested_iterable:\n            if isinstance(i, (list, tuple)):\n                for j in flatten(i):\n                    yield j\n            else:\n                yield i\n\n\n# TODO no longer used anywhere. deprecate?\ndef caller_name():\n    \"\"\"Return the name of the caller, e.g. a function.\n\n    Returns\n    -------\n    str\n        The name of the caller as a string\n\n    \"\"\"\n    # pylint: disable=protected-access\n    return sys._getframe(1).f_code.co_name\n\n\ndef seed(entropy=None, seedval=None):\n    \"\"\"Set the seed of imgaug's global RNG.\n\n    The global RNG controls most of the \"randomness\" in imgaug.\n\n    The global RNG is the default one used by all augmenters. Under special\n    circumstances (e.g. when an augmenter is switched to deterministic mode),\n    the global RNG is replaced with a local one. The state of that replacement\n    may be dependent on the global RNG's state at the time of creating the\n    child RNG.\n\n    .. note::\n\n        This function is not yet marked as deprecated, but might be in the\n        future. The preferred way to seed `imgaug` is via\n        :func:`~imgaug.random.seed`.\n\n    Parameters\n    ----------\n    entropy : int\n        The seed value to use.\n\n    seedval : None or int, optional\n        Deprecated since 0.4.0.\n\n    \"\"\"\n    assert entropy is not None or seedval is not None, (\n        \"Expected argument 'entropy' or 'seedval' to be not-None, but both\"\n        \"were None.\")\n\n    if seedval is not None:\n        assert entropy is None, (\n            \"Argument 'seedval' is the outdated name for 'entropy'. Hence, \"\n            \"if it is provided, 'entropy' must be None. Got 'entropy' value \"\n            \"of type %s.\" % (type(entropy),))\n\n        warn_deprecated(\"Parameter 'seedval' is deprecated. Use \"\n                        \"'entropy' instead.\")\n        entropy = seedval\n\n    import imgaug.random\n    imgaug.random.seed(entropy)\n\n\n@deprecated(\"imgaug.random.normalize_generator\")\ndef normalize_random_state(random_state):\n    \"\"\"Normalize various inputs to a numpy random generator.\n\n    Parameters\n    ----------\n    random_state : None or int or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.bit_generator.SeedSequence or numpy.random.RandomState\n        See :func:`~imgaug.random.normalize_generator`.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator`` (even if\n        the input was a ``RandomState``).\n\n    \"\"\"\n    import imgaug.random\n    return imgaug.random.normalize_generator_(random_state)\n\n\n@deprecated(\"imgaug.random.get_global_rng\")\ndef current_random_state():\n    \"\"\"Get or create the current global RNG of imgaug.\n\n    Note that the first call to this function will create a global RNG.\n\n    Returns\n    -------\n    imgaug.random.RNG\n        The global RNG to use.\n\n    \"\"\"\n    import imgaug.random\n    return imgaug.random.get_global_rng()\n\n\n@deprecated(\"imgaug.random.convert_seed_to_rng\")\ndef new_random_state(seed=None, fully_random=False):\n    \"\"\"Create a new numpy random number generator.\n\n    Parameters\n    ----------\n    seed : None or int, optional\n        The seed value to use. If ``None`` and `fully_random` is ``False``,\n        the seed will be derived from the global RNG. If `fully_random` is\n        ``True``, the seed will be provided by the OS.\n\n    fully_random : bool, optional\n        Whether the seed will be provided by the OS.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator``.\n        Both are initialized with the provided seed.\n\n    \"\"\"\n    # pylint: disable=redefined-outer-name\n    import imgaug.random\n    if seed is None:\n        if fully_random:\n            return imgaug.random.RNG.create_fully_random()\n        return imgaug.random.RNG.create_pseudo_random_()\n    return imgaug.random.RNG(seed)\n\n\n# TODO seems to not be used anywhere anymore\n@deprecated(\"imgaug.random.convert_seed_to_rng\")\ndef dummy_random_state():\n    \"\"\"Create a dummy random state using a seed of ``1``.\n\n    Returns\n    -------\n    imgaug.random.RNG\n        The new random state.\n\n    \"\"\"\n    import imgaug.random\n    return imgaug.random.RNG(1)\n\n\n@deprecated(\"imgaug.random.copy_generator_unless_global_rng\")\ndef copy_random_state(random_state, force_copy=False):\n    \"\"\"Copy an existing numpy (random number) generator.\n\n    Parameters\n    ----------\n    random_state : numpy.random.Generator or numpy.random.RandomState\n        The generator to copy.\n\n    force_copy : bool, optional\n        If ``True``, this function will always create a copy of every random\n        state. If ``False``, it will not copy numpy's default random state,\n        but all other random states.\n\n    Returns\n    -------\n    rs_copy : numpy.random.RandomState\n        The copied random state.\n\n    \"\"\"\n    import imgaug.random\n    if force_copy:\n        return imgaug.random.copy_generator(random_state)\n    return imgaug.random.copy_generator_unless_global_generator(random_state)\n\n\n@deprecated(\"imgaug.random.derive_generator_\")\ndef derive_random_state(random_state):\n    \"\"\"Derive a child numpy random generator from another one.\n\n    Parameters\n    ----------\n    random_state : numpy.random.Generator or numpy.random.RandomState\n        The generator from which to derive a new child generator.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator``.\n        In both cases a derived child generator.\n\n    \"\"\"\n    import imgaug.random\n    return imgaug.random.derive_generator_(random_state)\n\n\n@deprecated(\"imgaug.random.derive_generators_\")\ndef derive_random_states(random_state, n=1):\n    \"\"\"Derive child numpy random generators from another one.\n\n    Parameters\n    ----------\n    random_state : numpy.random.Generator or numpy.random.RandomState\n        The generator from which to derive new child generators.\n\n    n : int, optional\n        Number of child generators to derive.\n\n    Returns\n    -------\n    list of numpy.random.Generator or list of numpy.random.RandomState\n        In numpy <=1.16 a ``list`` of  ``RandomState`` s,\n        in 1.17+ a ``list`` of ``Generator`` s.\n        In both cases lists of derived child generators.\n\n    \"\"\"\n    import imgaug.random\n    return imgaug.random.derive_generators_(random_state, n=n)\n\n\n@deprecated(\"imgaug.random.advance_generator_\")\ndef forward_random_state(random_state):\n    \"\"\"Advance a numpy random generator's internal state.\n\n    Parameters\n    ----------\n    random_state : numpy.random.Generator or numpy.random.RandomState\n        Generator of which to advance the internal state.\n\n    \"\"\"\n    import imgaug.random\n    imgaug.random.advance_generator_(random_state)\n\n\n# TODO change this to some atan2 stuff?\ndef angle_between_vectors(v1, v2):\n    \"\"\"Calculcate the angle in radians between vectors `v1` and `v2`.\n\n    From\n    http://stackoverflow.com/questions/2827393/angles-between-two-n-dimensional-vectors-in-python\n\n    Parameters\n    ----------\n    v1 : (N,) ndarray\n        First vector.\n\n    v2 : (N,) ndarray\n        Second vector.\n\n    Returns\n    -------\n    float\n        Angle in radians.\n\n    Examples\n    --------\n    >>> angle_between_vectors(np.float32([1, 0, 0]), np.float32([0, 1, 0]))\n    1.570796...\n\n    >>> angle_between_vectors(np.float32([1, 0, 0]), np.float32([1, 0, 0]))\n    0.0\n\n    >>> angle_between_vectors(np.float32([1, 0, 0]), np.float32([-1, 0, 0]))\n    3.141592...\n\n    \"\"\"\n    # pylint: disable=invalid-name\n    length1 = np.linalg.norm(v1)\n    length2 = np.linalg.norm(v2)\n    v1_unit = (v1 / length1) if length1 > 0 else np.float32(v1) * 0\n    v2_unit = (v2 / length2) if length2 > 0 else np.float32(v2) * 0\n    return np.arccos(np.clip(np.dot(v1_unit, v2_unit), -1.0, 1.0))\n\n\n# TODO is this used anywhere?\n# TODO this might also be covered by augmentables.utils or\n#      augmentables.polys/lines\ndef compute_line_intersection_point(x1, y1, x2, y2, x3, y3, x4, y4):\n    \"\"\"Compute the intersection point of two lines.\n\n    Taken from https://stackoverflow.com/a/20679579 .\n\n    Parameters\n    ----------\n    x1 : number\n        x coordinate of the first point on line 1.\n        (The lines extends beyond this point.)\n\n    y1 : number\n        y coordinate of the first point on line 1.\n        (The lines extends beyond this point.)\n\n    x2 : number\n        x coordinate of the second point on line 1.\n        (The lines extends beyond this point.)\n\n    y2 : number\n        y coordinate of the second point on line 1.\n        (The lines extends beyond this point.)\n\n    x3 : number\n        x coordinate of the first point on line 2.\n        (The lines extends beyond this point.)\n\n    y3 : number\n        y coordinate of the first point on line 2.\n        (The lines extends beyond this point.)\n\n    x4 : number\n        x coordinate of the second point on line 2.\n        (The lines extends beyond this point.)\n\n    y4 : number\n        y coordinate of the second point on line 2.\n        (The lines extends beyond this point.)\n\n    Returns\n    -------\n    tuple of number or bool\n        The coordinate of the intersection point as a ``tuple`` ``(x, y)``.\n        If the lines are parallel (no intersection point or an infinite number\n        of them), the result is ``False``.\n\n    \"\"\"\n    # pylint: disable=invalid-name\n    def _make_line(point1, point2):\n        line_y = (point1[1] - point2[1])\n        line_x = (point2[0] - point1[0])\n        slope = (point1[0] * point2[1] - point2[0] * point1[1])\n        return line_y, line_x, -slope\n\n    line1 = _make_line((x1, y1), (x2, y2))\n    line2 = _make_line((x3, y3), (x4, y4))\n\n    D = line1[0] * line2[1] - line1[1] * line2[0]\n    Dx = line1[2] * line2[1] - line1[1] * line2[2]\n    Dy = line1[0] * line2[2] - line1[2] * line2[0]\n    if D != 0:\n        x = Dx / D\n        y = Dy / D\n        return x, y\n    return False\n\n\n# TODO replace by cv2.putText()?\ndef draw_text(img, y, x, text, color=(0, 255, 0), size=25):\n    \"\"\"Draw text on an image.\n\n    This uses by default DejaVuSans as its font, which is included in this\n    library.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: yes; not tested\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n        TODO check if other dtypes could be enabled\n\n    Parameters\n    ----------\n    img : (H,W,3) ndarray\n        The image array to draw text on.\n        Expected to be of dtype ``uint8`` or ``float32`` (expected value\n        range is ``[0.0, 255.0]``).\n\n    y : int\n        x-coordinate of the top left corner of the text.\n\n    x : int\n        y- coordinate of the top left corner of the text.\n\n    text : str\n        The text to draw.\n\n    color : iterable of int, optional\n        Color of the text to draw. For RGB-images this is expected to be an\n        RGB color.\n\n    size : int, optional\n        Font size of the text to draw.\n\n    Returns\n    -------\n    (H,W,3) ndarray\n        Input image with text drawn on it.\n\n    \"\"\"\n    from PIL import (\n        Image as PIL_Image,\n        ImageDraw as PIL_ImageDraw,\n        ImageFont as PIL_ImageFont\n    )\n\n    assert img.dtype.name in [\"uint8\", \"float32\"], (\n        \"Can currently draw text only on images of dtype 'uint8' or \"\n        \"'float32'. Got dtype %s.\" % (img.dtype.name,))\n\n    input_dtype = img.dtype\n    if img.dtype == np.float32:\n        img = img.astype(np.uint8)\n\n    img = PIL_Image.fromarray(img)\n    font = PIL_ImageFont.truetype(DEFAULT_FONT_FP, size)\n    context = PIL_ImageDraw.Draw(img)\n    context.text((x, y), text, fill=tuple(color), font=font)\n    img_np = np.asarray(img)\n\n    # PIL/asarray returns read only array\n    if not img_np.flags[\"WRITEABLE\"]:\n        try:\n            # this seems to no longer work with np 1.16 (or was pillow\n            # updated?)\n            img_np.setflags(write=True)\n        except ValueError as ex:\n            if \"cannot set WRITEABLE flag to True of this array\" in str(ex):\n                img_np = np.copy(img_np)\n\n    if img_np.dtype != input_dtype:\n        img_np = img_np.astype(input_dtype)\n\n    return img_np\n\n\n# TODO rename sizes to size?\ndef imresize_many_images(images, sizes=None, interpolation=None):\n    \"\"\"Resize each image in a list or array to a specified size.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: no (1)\n        * ``uint64``: no (2)\n        * ``int8``: yes; tested (3)\n        * ``int16``: yes; tested\n        * ``int32``: limited; tested (4)\n        * ``int64``: no (2)\n        * ``float16``: yes; tested (5)\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: no (1)\n        * ``bool``: yes; tested (6)\n\n        - (1) rejected by ``cv2.imresize``\n        - (2) results too inaccurate\n        - (3) mapped internally to ``int16`` when interpolation!=\"nearest\"\n        - (4) only supported for interpolation=\"nearest\", other interpolations\n              lead to cv2 error\n        - (5) mapped internally to ``float32``\n        - (6) mapped internally to ``uint8``\n\n    Parameters\n    ----------\n    images : (N,H,W,[C]) ndarray or list of (H,W,[C]) ndarray\n        Array of the images to resize.\n        Usually recommended to be of dtype ``uint8``.\n\n    sizes : float or iterable of int or iterable of float\n        The new size of the images, given either as a fraction (a single\n        float) or as a ``(height, width)`` ``tuple`` of two integers or as a\n        ``(height fraction, width fraction)`` ``tuple`` of two floats.\n\n    interpolation : None or str or int, optional\n        The interpolation to use during resize.\n        If ``int``, then expected to be one of:\n\n            * ``cv2.INTER_NEAREST`` (nearest neighbour interpolation)\n            * ``cv2.INTER_LINEAR`` (linear interpolation)\n            * ``cv2.INTER_AREA`` (area interpolation)\n            * ``cv2.INTER_CUBIC`` (cubic interpolation)\n\n        If ``str``, then expected to be one of:\n\n            * ``nearest`` (identical to ``cv2.INTER_NEAREST``)\n            * ``linear`` (identical to ``cv2.INTER_LINEAR``)\n            * ``area`` (identical to ``cv2.INTER_AREA``)\n            * ``cubic`` (identical to ``cv2.INTER_CUBIC``)\n\n        If ``None``, the interpolation will be chosen automatically. For size\n        increases, ``area`` interpolation will be picked and for size\n        decreases, ``linear`` interpolation will be picked.\n\n    Returns\n    -------\n    (N,H',W',[C]) ndarray\n        Array of the resized images.\n\n    Examples\n    --------\n    >>> import imgaug as ia\n    >>> images = np.zeros((2, 8, 16, 3), dtype=np.uint8)\n    >>> images_resized = ia.imresize_many_images(images, 2.0)\n    >>> images_resized.shape\n    (2, 16, 32, 3)\n\n    Convert two RGB images of height ``8`` and width ``16`` to images of\n    height ``2*8=16`` and width ``2*16=32``.\n\n    >>> images_resized = ia.imresize_many_images(images, (2.0, 4.0))\n    >>> images_resized.shape\n    (2, 16, 64, 3)\n\n    Convert two RGB images of height ``8`` and width ``16`` to images of\n    height ``2*8=16`` and width ``4*16=64``.\n\n    >>> images_resized = ia.imresize_many_images(images, (16, 32))\n    >>> images_resized.shape\n    (2, 16, 32, 3)\n\n    Converts two RGB images of height ``8`` and width ``16`` to images of\n    height ``16`` and width ``32``.\n\n    \"\"\"\n    # pylint: disable=too-many-statements\n\n    # we just do nothing if the input contains zero images\n    # one could also argue that an exception would be appropriate here\n    if len(images) == 0:\n        return images\n\n    # verify that sizes contains only values >0\n    if is_single_number(sizes) and sizes <= 0:\n        raise ValueError(\n            \"If 'sizes' is given as a single number, it is expected to \"\n            \"be >= 0, got %.8f.\" % (sizes,))\n\n    # change after the validation to make the above error messages match the\n    # original input\n    if is_single_number(sizes):\n        sizes = (sizes, sizes)\n    else:\n        assert len(sizes) == 2, (\n            \"If 'sizes' is given as a tuple, it is expected be a tuple of two \"\n            \"entries, got %d entries.\" % (len(sizes),))\n        assert all([is_single_number(val) and val >= 0 for val in sizes]), (\n            \"If 'sizes' is given as a tuple, it is expected be a tuple of two \"\n            \"ints or two floats, each >= 0, got types %s with values %s.\" % (\n                str([type(val) for val in sizes]), str(sizes)))\n\n    # if input is a list, call this function N times for N images\n    # but check beforehand if all images have the same shape, then just\n    # convert to a single array and de-convert afterwards\n    if isinstance(images, list):\n        nb_shapes = len({image.shape for image in images})\n        if nb_shapes == 1:\n            return list(imresize_many_images(\n                np.array(images), sizes=sizes, interpolation=interpolation))\n\n        return [\n            imresize_many_images(\n                image[np.newaxis, ...],\n                sizes=sizes,\n                interpolation=interpolation)[0, ...]\n            for image in images]\n\n    shape = images.shape\n    assert images.ndim in [3, 4], \"Expected array of shape (N, H, W, [C]), \" \\\n                                  \"got shape %s\" % (str(shape),)\n    nb_images = shape[0]\n    height_image, width_image = shape[1], shape[2]\n    nb_channels = shape[3] if images.ndim > 3 else None\n\n    height_target, width_target = sizes[0], sizes[1]\n    height_target = (int(np.round(height_image * height_target))\n                     if is_single_float(height_target)\n                     else height_target)\n    width_target = (int(np.round(width_image * width_target))\n                    if is_single_float(width_target)\n                    else width_target)\n\n    if height_target == height_image and width_target == width_image:\n        return np.copy(images)\n\n    # return empty array if input array contains zero-sized axes\n    # note that None==0 is not True (for case nb_channels=None)\n    if 0 in [height_target, width_target, nb_channels]:\n        shape_out = tuple([shape[0], height_target, width_target]\n                          + list(shape[3:]))\n        return np.zeros(shape_out, dtype=images.dtype)\n\n    # place this after the (h==h' and w==w') check so that images with\n    # zero-sized don't result in errors if the aren't actually resized\n    # verify that all input images have height/width > 0\n    has_zero_size_axes = any([axis == 0 for axis in images.shape[1:]])\n    assert not has_zero_size_axes, (\n        \"Cannot resize images, because at least one image has a height and/or \"\n        \"width and/or number of channels of zero. \"\n        \"Observed shapes were: %s.\" % (\n            str([image.shape for image in images]),))\n\n    inter = interpolation\n    assert inter is None or inter in IMRESIZE_VALID_INTERPOLATIONS, (\n        \"Expected 'interpolation' to be None or one of %s. Got %s.\" % (\n            \", \".join(\n                [str(valid_ip) for valid_ip in IMRESIZE_VALID_INTERPOLATIONS]\n            ),\n            str(inter)\n        )\n    )\n    if inter is None:\n        if height_target > height_image or width_target > width_image:\n            inter = cv2.INTER_AREA\n        else:\n            inter = cv2.INTER_LINEAR\n    elif inter in [\"nearest\", cv2.INTER_NEAREST]:\n        inter = cv2.INTER_NEAREST\n    elif inter in [\"linear\", cv2.INTER_LINEAR]:\n        inter = cv2.INTER_LINEAR\n    elif inter in [\"area\", cv2.INTER_AREA]:\n        inter = cv2.INTER_AREA\n    else:  # if ip in [\"cubic\", cv2.INTER_CUBIC]:\n        inter = cv2.INTER_CUBIC\n\n    # TODO find more beautiful way to avoid circular imports\n    from . import dtypes as iadt\n    if inter == cv2.INTER_NEAREST:\n        iadt.gate_dtypes_strs(\n            images,\n            allowed=\"bool uint8 uint16 int8 int16 int32 \"\n                    \"float16 float32 float64\",\n            disallowed=\"uint32 uint64 int64 float128\",\n            augmenter=None\n        )\n    else:\n        iadt.gate_dtypes_strs(\n            images,\n            allowed=\"bool uint8 uint16 int8 int16 float16 float32 float64\",\n            disallowed=\"uint32 uint64 int32 int64 float128\",\n            augmenter=None\n        )\n\n    result_shape = (nb_images, height_target, width_target)\n    if nb_channels is not None:\n        result_shape = result_shape + (nb_channels,)\n    result = np.zeros(result_shape, dtype=images.dtype)\n    for i, image in enumerate(images):\n        input_dtype = image.dtype\n        input_dtype_name = input_dtype.name\n\n        if input_dtype_name == \"bool\":\n            image = image.astype(np.uint8) * 255\n        elif input_dtype_name == \"int8\" and inter != cv2.INTER_NEAREST:\n            image = image.astype(np.int16)\n        elif input_dtype_name == \"float16\":\n            image = image.astype(np.float32)\n\n        if nb_channels is not None and nb_channels > 512:\n            channels = [\n                cv2.resize(image[..., c], (width_target, height_target),\n                           interpolation=inter) for c in sm.xrange(nb_channels)]\n            result_img = np.stack(channels, axis=-1)\n        else:\n            result_img = cv2.resize(\n                image, (width_target, height_target), interpolation=inter)\n\n        assert result_img.dtype.name == image.dtype.name, (\n            \"Expected cv2.resize() to keep the input dtype '%s', but got \"\n            \"'%s'. This is an internal error. Please report.\" % (\n                image.dtype.name, result_img.dtype.name\n            )\n        )\n\n        # cv2 removes the channel axis if input was (H, W, 1)\n        # we re-add it (but only if input was not (H, W))\n        if (len(result_img.shape) == 2 and nb_channels is not None\n                and nb_channels == 1):\n            result_img = result_img[:, :, np.newaxis]\n\n        if input_dtype_name == \"bool\":\n            result_img = result_img > 127\n        elif input_dtype_name == \"int8\" and inter != cv2.INTER_NEAREST:\n            # TODO somehow better avoid circular imports here\n            from . import dtypes as iadt\n            result_img = iadt.restore_dtypes_(result_img, np.int8)\n        elif input_dtype_name == \"float16\":\n            # TODO see above\n            from . import dtypes as iadt\n            result_img = iadt.restore_dtypes_(result_img, np.float16)\n        result[i] = result_img\n    return result\n\n\ndef _assert_two_or_three_dims(shape):\n    if hasattr(shape, \"shape\"):\n        shape = shape.shape\n    assert len(shape) in [2, 3], (\n        \"Expected image with two or three dimensions, but got %d dimensions \"\n        \"and shape %s.\" % (len(shape), shape))\n\n\ndef imresize_single_image(image, sizes, interpolation=None):\n    \"\"\"Resize a single image.\n\n    **Supported dtypes**:\n\n        See :func:`~imgaug.imgaug.imresize_many_images`.\n\n    Parameters\n    ----------\n    image : (H,W,C) ndarray or (H,W) ndarray\n        Array of the image to resize.\n        Usually recommended to be of dtype ``uint8``.\n\n    sizes : float or iterable of int or iterable of float\n        See :func:`~imgaug.imgaug.imresize_many_images`.\n\n    interpolation : None or str or int, optional\n        See :func:`~imgaug.imgaug.imresize_many_images`.\n\n    Returns\n    -------\n    (H',W',C) ndarray or (H',W') ndarray\n        The resized image.\n\n    \"\"\"\n    _assert_two_or_three_dims(image)\n\n    grayscale = False\n    if image.ndim == 2:\n        grayscale = True\n        image = image[:, :, np.newaxis]\n\n    rs = imresize_many_images(\n        image[np.newaxis, :, :, :], sizes, interpolation=interpolation)\n    if grayscale:\n        return rs[0, :, :, 0]\n    return rs[0, ...]\n\n\ndef pool(arr, block_size, func, pad_mode=\"constant\", pad_cval=0,\n         preserve_dtype=True, cval=None):\n    \"\"\"Resize an array by pooling values within blocks.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; tested\n        * ``uint32``: yes; tested (2)\n        * ``uint64``: no (1)\n        * ``int8``: yes; tested\n        * ``int16``: yes; tested\n        * ``int32``: yes; tested (2)\n        * ``int64``: no (1)\n        * ``float16``: yes; tested\n        * ``float32``: yes; tested\n        * ``float64``: yes; tested\n        * ``float128``: yes; tested (2)\n        * ``bool``: yes; tested\n\n        - (1) results too inaccurate (at least when using np.average as func)\n        - (2) Note that scikit-image documentation says that the wrapped\n              pooling function converts inputs to ``float64``. Actual tests\n              showed no indication of that happening (at least when using\n              preserve_dtype=True).\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Image-like array to pool. Ideally of datatype ``float64``.\n\n    block_size : int or tuple of int\n        Spatial size of each group of values to pool, aka kernel size.\n\n          * If a single ``int``, then a symmetric block of that size along\n            height and width will be used.\n          * If a ``tuple`` of two values, it is assumed to be the block size\n            along height and width of the image-like, with pooling happening\n            per channel.\n          * If a ``tuple`` of three values, it is assumed to be the block size\n            along height, width and channels.\n\n    func : callable\n        Function to apply to a given block in order to convert it to a single\n        number, e.g. :func:`numpy.average`, :func:`numpy.min`,\n        :func:`numpy.max`.\n\n    pad_mode : str, optional\n        Padding mode to use if the array cannot be divided by `block_size`\n        without remainder. See :func:`~imgaug.imgaug.pad` for details.\n\n    pad_cval : number, optional\n        Value to use for padding if `mode` is ``constant``.\n        See :func:`numpy.pad` for details.\n\n    preserve_dtype : bool, optional\n        Whether to convert the array back to the input datatype if it is\n        changed away from that in the pooling process.\n\n    cval : None or number, optional\n        Deprecated. Old name for `pad_cval`.\n\n    Returns\n    -------\n    (H',W') ndarray or (H',W',C') ndarray\n        Array after pooling.\n\n    \"\"\"\n    # TODO find better way to avoid circular import\n    from . import dtypes as iadt\n    from .augmenters import size as iasize\n\n    if arr.size == 0:\n        return np.copy(arr)\n\n    iadt.gate_dtypes_strs(\n        {arr.dtype},\n        allowed=\"bool uint8 uint16 uint32 int8 int16 int32 \"\n                \"float16 float32 float64 float128\",\n        disallowed=\"uint64 int64\"\n    )\n\n    if cval is not None:\n        warn_deprecated(\"`cval` is a deprecated argument in pool(). \"\n                        \"Use `pad_cval` instead.\")\n        pad_cval = cval\n\n    _assert_two_or_three_dims(arr)\n\n    is_valid_int = is_single_integer(block_size) and block_size >= 1\n    is_valid_tuple = is_iterable(block_size) and len(block_size) in [2, 3] \\\n        and [is_single_integer(val) and val >= 1 for val in block_size]\n    assert is_valid_int or is_valid_tuple, (\n        \"Expected argument 'block_size' to be a single integer >0 or \"\n        \"a tuple of 2 or 3 values with each one being >0. Got %s.\" % (\n            str(block_size)))\n\n    if is_single_integer(block_size):\n        block_size = [block_size, block_size]\n    if len(block_size) < arr.ndim:\n        block_size = list(block_size) + [1]\n\n    # We use custom padding here instead of the one from block_reduce(),\n    # because (1) it is expected to be faster and (2) it allows us more\n    # flexibility wrt to padding modes.\n    arr = iasize.pad_to_multiples_of(\n        arr,\n        height_multiple=block_size[0],\n        width_multiple=block_size[1],\n        mode=pad_mode,\n        cval=pad_cval\n    )\n\n    input_dtype = arr.dtype\n\n    arr_reduced = skimage.measure.block_reduce(arr, tuple(block_size), func,\n                                               cval=cval)\n    if preserve_dtype and arr_reduced.dtype.name != input_dtype.name:\n        arr_reduced = arr_reduced.astype(input_dtype)\n    return arr_reduced\n\n\n# This automatically calls a special uint8 method if it fulfills standard\n# cv2 criteria. Otherwise it falls back to pool().\n# Added in 0.5.0.\ndef _pool_dispatcher_(arr, block_size, func_uint8, blockfunc, pad_mode=\"edge\",\n                      pad_cval=255, preserve_dtype=True, cval=None,\n                      copy=False):\n    if not isinstance(block_size, (tuple, list)):\n        block_size = (block_size, block_size)\n\n    if 0 in block_size:\n        return arr if not copy else np.copy(arr)\n\n    shape = arr.shape\n    nb_channels = 0 if len(shape) <= 2 else shape[-1]\n\n    valid_for_cv2 = (\n        arr.dtype.name == \"uint8\"\n        and len(block_size) == 2\n        and nb_channels <= 512\n        and 0 not in shape\n    )\n    if valid_for_cv2:\n        return func_uint8(arr, block_size, pad_mode=pad_mode,\n                          pad_cval=pad_cval if cval is None else cval)\n    return pool(arr, block_size, blockfunc, pad_mode=pad_mode,\n                pad_cval=pad_cval, preserve_dtype=preserve_dtype, cval=cval)\n\n\ndef avg_pool(arr, block_size, pad_mode=\"reflect\", pad_cval=128,\n             preserve_dtype=True, cval=None):\n    \"\"\"Resize an array using average pooling.\n\n    Defaults to ``pad_mode=\"reflect\"`` to ensure that padded values do not\n    affect the average.\n\n    .. note::\n\n        This function currently rounds ``0.5`` up for (most) ``uint8``\n        images, but rounds it down for other dtypes.\n\n    **Supported dtypes**:\n\n        See :func:`~imgaug.imgaug.pool`.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Image-like array to pool.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    block_size : int or tuple of int\n        Size of each block of values to pool.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    pad_mode : str, optional\n        Padding mode to use if the array cannot be divided by `block_size`\n        without remainder.\n        See :func:`~imgaug.imgaug.pad` for details.\n\n    pad_cval : number, optional\n        Padding value.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    preserve_dtype : bool, optional\n        Whether to preserve the input array dtype.\n        See  :func:`~imgaug.imgaug.pool` for details.\n\n    cval : None or number, optional\n        Deprecated. Old name for `pad_cval`.\n\n    Returns\n    -------\n    (H',W') ndarray or (H',W',C') ndarray\n        Array after average pooling.\n\n    \"\"\"\n    return _pool_dispatcher_(\n        arr,\n        block_size,\n        _avg_pool_uint8,\n        np.average,\n        pad_mode=pad_mode,\n        pad_cval=pad_cval,\n        preserve_dtype=preserve_dtype,\n        cval=cval,\n        copy=True\n    )\n\n\n# Added in 0.5.0.\ndef _avg_pool_uint8(arr, block_size, pad_mode=\"reflect\", pad_cval=128):\n    from imgaug.augmenters.size import pad_to_multiples_of\n\n    ndim_in = arr.ndim\n\n    shape = arr.shape\n    if shape[0] % block_size[0] != 0 or shape[1] % block_size[1] != 0:\n        arr = pad_to_multiples_of(\n            arr,\n            height_multiple=block_size[0],\n            width_multiple=block_size[1],\n            mode=pad_mode,\n            cval=pad_cval\n        )\n\n    height = arr.shape[0] // block_size[0]\n    width = arr.shape[1] // block_size[1]\n\n    arr = cv2.resize(arr, (width, height), interpolation=cv2.INTER_AREA)\n\n    if arr.ndim < ndim_in:\n        arr = arr[:, :, np.newaxis]\n\n    return arr\n\n\ndef max_pool(arr, block_size, pad_mode=\"edge\", pad_cval=0,\n             preserve_dtype=True, cval=None):\n    \"\"\"Resize an array using max-pooling.\n\n    Defaults to ``pad_mode=\"edge\"`` to ensure that padded values do not affect\n    the maximum, even if the dtype was something else than ``uint8``.\n\n    **Supported dtypes**:\n\n        See :func:`~imgaug.imgaug.pool`.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Image-like array to pool.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    block_size : int or tuple of int\n        Size of each block of values to pool.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    pad_mode : str, optional\n        Padding mode to use if the array cannot be divided by `block_size`\n        without remainder.\n        See :func:`~imgaug.imgaug.pad` for details.\n\n    pad_cval : number, optional\n        Padding value.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    preserve_dtype : bool, optional\n        Whether to preserve the input array dtype.\n        See  :func:`~imgaug.imgaug.pool` for details.\n\n    cval : None or number, optional\n        Deprecated. Old name for `pad_cval`.\n\n    Returns\n    -------\n    (H',W') ndarray or (H',W',C') ndarray\n        Array after max-pooling.\n\n    \"\"\"\n    return max_pool_(np.copy(arr), block_size, pad_mode=pad_mode,\n                     pad_cval=pad_cval, preserve_dtype=preserve_dtype,\n                     cval=cval)\n\n\ndef max_pool_(arr, block_size, pad_mode=\"edge\", pad_cval=0,\n              preserve_dtype=True, cval=None):\n    \"\"\"Resize an array in-place using max-pooling.\n\n    Defaults to ``pad_mode=\"edge\"`` to ensure that padded values do not affect\n    the maximum, even if the dtype was something else than ``uint8``.\n\n    Added in 0.5.0.\n\n    **Supported dtypes**:\n\n        See :func:`~imgaug.imgaug.pool`.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Image-like array to pool.\n        May be altered in-place.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    block_size : int or tuple of int\n        Size of each block of values to pool.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    pad_mode : str, optional\n        Padding mode to use if the array cannot be divided by `block_size`\n        without remainder.\n        See :func:`~imgaug.imgaug.pad` for details.\n\n    pad_cval : number, optional\n        Padding value.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    preserve_dtype : bool, optional\n        Whether to preserve the input array dtype.\n        See  :func:`~imgaug.imgaug.pool` for details.\n\n    cval : None or number, optional\n        Deprecated. Old name for `pad_cval`.\n\n    Returns\n    -------\n    (H',W') ndarray or (H',W',C') ndarray\n        Array after max-pooling.\n        Might be a view of `arr`.\n\n    \"\"\"\n    return _pool_dispatcher_(\n        arr,\n        block_size,\n        _max_pool_uint8_,\n        np.max,\n        pad_mode=pad_mode,\n        pad_cval=pad_cval,\n        preserve_dtype=preserve_dtype,\n        cval=cval\n    )\n\n\ndef min_pool(arr, block_size, pad_mode=\"edge\", pad_cval=255,\n             preserve_dtype=True):\n    \"\"\"Resize an array using min-pooling.\n\n    Defaults to ``pad_mode=\"edge\"`` to ensure that padded values do not affect\n    the minimum, even if the dtype was something else than ``uint8``.\n\n    **Supported dtypes**:\n\n        See :func:`~imgaug.imgaug.pool`.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Image-like array to pool.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    block_size : int or tuple of int\n        Size of each block of values to pool.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    pad_mode : str, optional\n        Padding mode to use if the array cannot be divided by `block_size`\n        without remainder.\n        See :func:`~imgaug.imgaug.pad` for details.\n\n    pad_cval : number, optional\n        Padding value.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    preserve_dtype : bool, optional\n        Whether to preserve the input array dtype.\n        See  :func:`~imgaug.imgaug.pool` for details.\n\n    Returns\n    -------\n    (H',W') ndarray or (H',W',C') ndarray\n        Array after min-pooling.\n\n    \"\"\"\n    return min_pool_(np.copy(arr), block_size, pad_mode=pad_mode,\n                     pad_cval=pad_cval, preserve_dtype=preserve_dtype)\n\n\ndef min_pool_(arr, block_size, pad_mode=\"edge\", pad_cval=255,\n              preserve_dtype=True):\n    \"\"\"Resize an array in-place using min-pooling.\n\n    Defaults to ``pad_mode=\"edge\"`` to ensure that padded values do not affect\n    the minimum, even if the dtype was something else than ``uint8``.\n\n    Added in 0.5.0.\n\n    **Supported dtypes**:\n\n        See :func:`~imgaug.imgaug.pool`.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Image-like array to pool.\n        May be altered in-place.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    block_size : int or tuple of int\n        Size of each block of values to pool.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    pad_mode : str, optional\n        Padding mode to use if the array cannot be divided by `block_size`\n        without remainder.\n        See :func:`~imgaug.imgaug.pad` for details.\n\n    pad_cval : number, optional\n        Padding value.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    preserve_dtype : bool, optional\n        Whether to preserve the input array dtype.\n        See  :func:`~imgaug.imgaug.pool` for details.\n\n    Returns\n    -------\n    (H',W') ndarray or (H',W',C') ndarray\n        Array after min-pooling.\n        Might be a view of `arr`.\n\n    \"\"\"\n    return _pool_dispatcher_(\n        arr,\n        block_size,\n        _min_pool_uint8_,\n        np.min,\n        pad_mode=pad_mode,\n        pad_cval=pad_cval,\n        preserve_dtype=preserve_dtype\n    )\n\n\n# Added in 0.5.0.\ndef _min_pool_uint8_(arr, block_size, pad_mode=\"edge\", pad_cval=255):\n    return _minmax_pool_uint8_(arr, block_size, cv2.erode,\n                               pad_mode=pad_mode, pad_cval=pad_cval)\n\n\n# Added in 0.5.0.\ndef _max_pool_uint8_(arr, block_size, pad_mode=\"edge\", pad_cval=0):\n    return _minmax_pool_uint8_(arr, block_size, cv2.dilate,\n                               pad_mode=pad_mode, pad_cval=pad_cval)\n\n\n# Added in 0.5.0.\ndef _minmax_pool_uint8_(arr, block_size, func, pad_mode, pad_cval):\n    from imgaug.augmenters.size import pad_to_multiples_of\n\n    ndim_in = arr.ndim\n\n    shape = arr.shape\n    if shape[0] % block_size[0] != 0 or shape[1] % block_size[1] != 0:\n        arr = pad_to_multiples_of(\n            arr,\n            height_multiple=block_size[0],\n            width_multiple=block_size[1],\n            mode=pad_mode,\n            cval=pad_cval\n        )\n\n    kernel = globals()[\"_POOLING_KERNELS_CACHE\"].get(block_size, None)\n    if kernel is None:\n        kernel = np.ones(block_size, dtype=np.uint8)\n        if block_size[0] <= 30 and block_size[1] <= 30:\n            globals()[\"_POOLING_KERNELS_CACHE\"][block_size] = kernel\n\n    # TODO why was this done with image flips instead of kernel flips?\n    arr = cv2.flip(arr, -1)\n    arr = func(arr, kernel, iterations=1)\n    arr = cv2.flip(arr, -1)\n\n    if arr.ndim < ndim_in:\n        arr = arr[:, :, np.newaxis]\n\n    start_height = (block_size[0] - 1) // 2\n    start_width = (block_size[1] - 1) // 2\n    return arr[start_height::block_size[0], start_width::block_size[1]]\n\n\ndef median_pool(arr, block_size, pad_mode=\"reflect\", pad_cval=128,\n                preserve_dtype=True):\n    \"\"\"Resize an array using median-pooling.\n\n    Defaults to ``pad_mode=\"reflect\"`` to ensure that padded values do not\n    affect the average.\n\n    **Supported dtypes**:\n\n        See :func:`~imgaug.imgaug.pool`.\n\n    Parameters\n    ----------\n    arr : (H,W) ndarray or (H,W,C) ndarray\n        Image-like array to pool.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    block_size : int or tuple of int\n        Size of each block of values to pool.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    pad_mode : str, optional\n        Padding mode to use if the array cannot be divided by `block_size`\n        without remainder.\n        See :func:`~imgaug.imgaug.pad` for details.\n\n    pad_cval : number, optional\n        Padding value.\n        See :func:`~imgaug.imgaug.pool` for details.\n\n    preserve_dtype : bool, optional\n        Whether to preserve the input array dtype.\n        See  :func:`~imgaug.imgaug.pool` for details.\n\n    Returns\n    -------\n    (H',W') ndarray or (H',W',C') ndarray\n        Array after min-pooling.\n\n    \"\"\"\n    # This uses a custom dispatcher (compared to avg/min/max pool), because\n    # cv2 medianBlur only works with odd kernel sizes > 1, does not support\n    # height/width-wise ksizes and uses a different method for ksizes > 5\n    # leading to different performance characteristics for ksizes <= 5 and\n    # ksizes > 5.\n\n    if not isinstance(block_size, (tuple, list)):\n        block_size = (block_size, block_size)\n\n    if 0 in block_size:\n        return np.copy(arr)\n\n    shape = arr.shape\n    nb_channels = 0 if len(shape) <= 2 else shape[-1]\n\n    valid_for_cv2 = (\n        arr.dtype.name == \"uint8\"\n        and len(block_size) == 2\n        and block_size[0] == block_size[1]\n        and (\n            block_size[0] in [3, 5]\n            or (\n                block_size[0] in [7, 9, 11, 13]\n                and (shape[0] * shape[1]) <= (32 * 32)\n            )\n        )\n        and nb_channels <= 512\n        and 0 not in shape\n    )\n    if valid_for_cv2:\n        return _median_pool_cv2(arr, block_size[0], pad_mode=pad_mode,\n                                pad_cval=pad_cval)\n    return pool(arr, block_size, np.median, pad_mode=pad_mode,\n                pad_cval=pad_cval, preserve_dtype=preserve_dtype)\n\n\n# block_size must be a single integer here, in contrast to the other cv2\n# pool methods that support (int, int).\n# Added in 0.5.0.\ndef _median_pool_cv2(arr, block_size, pad_mode, pad_cval):\n    from imgaug.augmenters.size import pad_to_multiples_of\n\n    ndim_in = arr.ndim\n\n    shape = arr.shape\n    if shape[0] % block_size != 0 or shape[1] % block_size != 0:\n        arr = pad_to_multiples_of(\n            arr,\n            height_multiple=block_size,\n            width_multiple=block_size,\n            mode=pad_mode,\n            cval=pad_cval\n        )\n\n    arr = cv2.medianBlur(arr, block_size)\n\n    if arr.ndim < ndim_in:\n        arr = arr[:, :, np.newaxis]\n\n    start_height = (block_size - 1) // 2\n    start_width = (block_size - 1) // 2\n    return arr[start_height::block_size, start_width::block_size]\n\n\ndef draw_grid(images, rows=None, cols=None):\n    \"\"\"Combine multiple images into a single grid-like image.\n\n    Calling this function with four images of the same shape and ``rows=2``,\n    ``cols=2`` will combine the four images to a single image array of shape\n    ``(2*H, 2*W, C)``, where ``H`` is the height of any of the images\n    (analogous ``W``) and ``C`` is the number of channels of any image.\n\n    Calling this function with four images of the same shape and ``rows=4``,\n    ``cols=1`` is analogous to calling :func:`numpy.vstack` on the images.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: yes; fully tested\n        * ``uint32``: yes; fully tested\n        * ``uint64``: yes; fully tested\n        * ``int8``: yes; fully tested\n        * ``int16``: yes; fully tested\n        * ``int32``: yes; fully tested\n        * ``int64``: yes; fully tested\n        * ``float16``: yes; fully tested\n        * ``float32``: yes; fully tested\n        * ``float64``: yes; fully tested\n        * ``float128``: yes; fully tested\n        * ``bool``: yes; fully tested\n\n    Parameters\n    ----------\n    images : (N,H,W,3) ndarray or iterable of (H,W,3) array\n        The input images to convert to a grid.\n\n    rows : None or int, optional\n        The number of rows to show in the grid.\n        If ``None``, it will be automatically derived.\n\n    cols : None or int, optional\n        The number of cols to show in the grid.\n        If ``None``, it will be automatically derived.\n\n    Returns\n    -------\n    (H',W',3) ndarray\n        Image of the generated grid.\n\n    \"\"\"\n    nb_images = len(images)\n    assert nb_images > 0, \"Expected to get at least one image, got none.\"\n\n    if is_np_array(images):\n        assert images.ndim == 4, (\n            \"Expected to get an array of four dimensions denoting \"\n            \"(N, H, W, C), got %d dimensions and shape %s.\" % (\n                images.ndim, images.shape))\n    else:\n        assert is_iterable(images), (\n            \"Expected to get an iterable of ndarrays, \"\n            \"got %s.\" % (type(images),))\n        assert all([is_np_array(image) for image in images]), (\n            \"Expected to get an iterable of ndarrays, \"\n            \"got types %s.\" % (\n                \", \".join([str(type(image)) for image in images],)))\n        assert all([image.ndim == 3 for image in images]), (\n            \"Expected to get images with three dimensions. Got shapes %s.\" % (\n                \", \".join([str(image.shape) for image in images])))\n        assert len({image.dtype.name for image in images}) == 1, (\n            \"Expected to get images with the same dtypes, got dtypes %s.\" % (\n                \", \".join([image.dtype.name for image in images])))\n        assert len({image.shape[-1] for image in images}) == 1, (\n            \"Expected to get images with the same number of channels, \"\n            \"got shapes %s.\" % (\n                \", \".join([str(image.shape) for image in images])))\n\n    cell_height = max([image.shape[0] for image in images])\n    cell_width = max([image.shape[1] for image in images])\n    nb_channels = images[0].shape[2]\n\n    if rows is None and cols is None:\n        rows = cols = int(math.ceil(math.sqrt(nb_images)))\n    elif rows is not None:\n        cols = int(math.ceil(nb_images / rows))\n    elif cols is not None:\n        rows = int(math.ceil(nb_images / cols))\n    assert rows * cols >= nb_images, (\n        \"Expected rows*cols to lead to at least as many cells as there were \"\n        \"images provided, but got %d rows, %d cols (=%d cells) for %d \"\n        \"images. \" % (rows, cols, rows*cols, nb_images))\n\n    width = cell_width * cols\n    height = cell_height * rows\n    dtype = images.dtype if is_np_array(images) else images[0].dtype\n    grid = np.zeros((height, width, nb_channels), dtype=dtype)\n    cell_idx = 0\n    for row_idx in sm.xrange(rows):\n        for col_idx in sm.xrange(cols):\n            if cell_idx < nb_images:\n                image = images[cell_idx]\n                cell_y1 = cell_height * row_idx\n                cell_y2 = cell_y1 + image.shape[0]\n                cell_x1 = cell_width * col_idx\n                cell_x2 = cell_x1 + image.shape[1]\n                grid[cell_y1:cell_y2, cell_x1:cell_x2, :] = image\n            cell_idx += 1\n\n    return grid\n\n\ndef show_grid(images, rows=None, cols=None):\n    \"\"\"Combine multiple images into a single image and plot the result.\n\n    This will show a window of the results of :func:`~imgaug.imgaug.draw_grid`.\n\n    **Supported dtypes**:\n\n        minimum of (\n            :func:`~imgaug.imgaug.draw_grid`,\n            :func:`~imgaug.imgaug.imshow`\n        )\n\n    Parameters\n    ----------\n    images : (N,H,W,3) ndarray or iterable of (H,W,3) array\n        See :func:`~imgaug.imgaug.draw_grid`.\n\n    rows : None or int, optional\n        See :func:`~imgaug.imgaug.draw_grid`.\n\n    cols : None or int, optional\n        See :func:`~imgaug.imgaug.draw_grid`.\n\n    \"\"\"\n    grid = draw_grid(images, rows=rows, cols=cols)\n    imshow(grid)\n\n\ndef imshow(image, backend=IMSHOW_BACKEND_DEFAULT):\n    \"\"\"Show an image in a window.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; not tested\n        * ``uint16``: ?\n        * ``uint32``: ?\n        * ``uint64``: ?\n        * ``int8``: ?\n        * ``int16``: ?\n        * ``int32``: ?\n        * ``int64``: ?\n        * ``float16``: ?\n        * ``float32``: ?\n        * ``float64``: ?\n        * ``float128``: ?\n        * ``bool``: ?\n\n    Parameters\n    ----------\n    image : (H,W,3) ndarray\n        Image to show.\n\n    backend : {'matplotlib', 'cv2'}, optional\n        Library to use to show the image. May be either matplotlib or\n        OpenCV ('cv2'). OpenCV tends to be faster, but apparently causes more\n        technical issues.\n\n    \"\"\"\n    assert backend in [\"matplotlib\", \"cv2\"], (\n        \"Expected backend 'matplotlib' or 'cv2', got %s.\" % (backend,))\n\n    if backend == \"cv2\":\n        image_bgr = image\n        if image.ndim == 3 and image.shape[2] in [3, 4]:\n            image_bgr = image[..., 0:3][..., ::-1]\n\n        win_name = \"imgaug-default-window\"\n        cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)\n        cv2.imshow(win_name, image_bgr)\n        cv2.waitKey(0)\n        cv2.destroyWindow(win_name)\n    else:\n        # import only when necessary (faster startup; optional dependency;\n        # less fragile -- see issue #225)\n        import matplotlib.pyplot as plt\n\n        dpi = 96\n        h, w = image.shape[0] / dpi, image.shape[1] / dpi\n        # if the figure is too narrow, the footer may appear and make the fig\n        # suddenly wider (ugly)\n        w = max(w, 6)\n\n        fig, ax = plt.subplots(figsize=(w, h), dpi=dpi)\n        fig.canvas.set_window_title(\"imgaug.imshow(%s)\" % (image.shape,))\n        # cmap=gray is automatically only activate for grayscale images\n        ax.imshow(image, cmap=\"gray\")\n        plt.show()\n\n\ndef do_assert(condition, message=\"Assertion failed.\"):\n    \"\"\"Assert that a ``condition`` holds or raise an ``Exception`` otherwise.\n\n    This was added because `assert` statements are removed in optimized code.\n    It replaced `assert` statements throughout the library, but that was\n    reverted again for readability and performance reasons.\n\n    Parameters\n    ----------\n    condition : bool\n        If ``False``, an exception is raised.\n\n    message : str, optional\n        Error message.\n\n    \"\"\"\n    if not condition:\n        raise AssertionError(str(message))\n\n\n# Added in 0.4.0.\ndef _normalize_cv2_input_arr_(arr):\n    flags = arr.flags\n    if not flags[\"OWNDATA\"]:\n        arr = np.copy(arr)\n        flags = arr.flags\n    if not flags[\"C_CONTIGUOUS\"]:\n        arr = np.ascontiguousarray(arr)\n    return arr\n\n\ndef apply_lut(image, table):\n    \"\"\"Map an input image to a new one using a lookup table.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        See :func:`~imgaug.imgaug.apply_lut_`.\n\n    Parameters\n    ----------\n    image : ndarray\n        See :func:`~imgaug.imgaug.apply_lut_`.\n\n    table : ndarray or list of ndarray\n        See :func:`~imgaug.imgaug.apply_lut_`.\n\n    Returns\n    -------\n    ndarray\n        Image after mapping via lookup table.\n\n    \"\"\"\n    return apply_lut_(np.copy(image), table)\n\n\n# TODO make this function compatible with short max sized images, probably\n#      isn't right now\ndef apply_lut_(image, table):\n    \"\"\"Map an input image in-place to a new one using a lookup table.\n\n    Added in 0.4.0.\n\n    **Supported dtypes**:\n\n        * ``uint8``: yes; fully tested\n        * ``uint16``: no\n        * ``uint32``: no\n        * ``uint64``: no\n        * ``int8``: no\n        * ``int16``: no\n        * ``int32``: no\n        * ``int64``: no\n        * ``float16``: no\n        * ``float32``: no\n        * ``float64``: no\n        * ``float128``: no\n        * ``bool``: no\n\n    Parameters\n    ----------\n    image : ndarray\n        Image of dtype ``uint8`` and shape ``(H,W)`` or ``(H,W,C)``.\n\n    table : ndarray or list of ndarray\n        Table of dtype ``uint8`` containing the mapping from old to new\n        values. Either a ``list`` of ``C`` ``(256,)`` arrays or a single\n        array of shape ``(256,)`` or ``(256, C)`` or ``(1, 256, C)``.\n        In case of ``(256,)`` the same table is used for all channels,\n        otherwise a channelwise table is used and ``C`` is expected to match\n        the number of channels.\n\n    Returns\n    -------\n    ndarray\n        Image after mapping via lookup table.\n        This *might* be the same array instance as provided via `image`.\n\n    \"\"\"\n\n    image_shape_orig = image.shape\n    nb_channels = 1 if len(image_shape_orig) == 2 else image_shape_orig[-1]\n\n    if 0 in image_shape_orig:\n        return image\n\n    image = _normalize_cv2_input_arr_(image)\n\n    # [(256,), (256,), ...] => (256, C)\n    if isinstance(table, list):\n        assert len(table) == nb_channels, (\n            \"Expected to get %d tables (one per channel), got %d instead.\" % (\n                nb_channels, len(table)))\n        table = np.stack(table, axis=-1)\n\n    # (256, C) => (1, 256, C)\n    if table.shape == (256, nb_channels):\n        table = table[np.newaxis, :, :]\n\n    assert table.shape == (256,) or table.shape == (1, 256, nb_channels), (\n        \"Expected 'table' to be any of the following: \"\n        \"A list of C (256,) arrays, an array of shape (256,), an array of \"\n        \"shape (256, C), an array of shape (1, 256, C). Transformed 'table' \"\n        \"up to shape %s for image with shape %s (C=%d).\" % (\n            table.shape, image_shape_orig, nb_channels))\n\n    if nb_channels > 512:\n        if table.shape == (256,):\n            table = np.tile(table[np.newaxis, :, np.newaxis],\n                            (1, 1, nb_channels))\n\n        subluts = []\n        for group_idx in np.arange(int(np.ceil(nb_channels / 512))):\n            c_start = group_idx * 512\n            c_end = c_start + 512\n            subluts.append(apply_lut_(image[:, :, c_start:c_end],\n                                      table[:, :, c_start:c_end]))\n\n        return np.concatenate(subluts, axis=2)\n\n    assert image.dtype == _UINT8_DTYPE, (\n        \"Expected uint8 image, got dtype %s.\" % (image.dtype.name,))\n\n    image = cv2.LUT(image, table, dst=image)\n    return image\n\n\n# Added in 0.5.0.\ndef _identity_decorator(*_dec_args, **_dec_kwargs):\n    def _decorator(func):\n        @functools.wraps(func)\n        def _wrapper(*args, **kwargs):\n            return func(*args, **kwargs)\n        return _wrapper\n    return _decorator\n\n\n# Added in 0.5.0.\nif numba is not None:\n    _numbajit = numba.jit\nelse:\n    _numbajit = _identity_decorator\n\n\nclass HooksImages(object):\n    \"\"\"Class to intervene with image augmentation runs.\n\n    This is e.g. useful to dynamically deactivate some augmenters.\n\n    Parameters\n    ----------\n    activator : None or callable, optional\n        A function that gives permission to execute an augmenter.\n        The expected interface is::\n\n            ``f(images, augmenter, parents, default)``\n\n        where ``images`` are the input images to augment, ``augmenter`` is the\n        instance of the augmenter to execute, ``parents`` are previously\n        executed augmenters and ``default`` is an expected default value to be\n        returned if the activator function does not plan to make a decision\n        for the given inputs.\n\n    propagator : None or callable, optional\n        A function that gives permission to propagate the augmentation further\n        to the children of an augmenter. This happens after the activator.\n        In theory, an augmenter may augment images itself (if allowed by the\n        activator) and then execute child augmenters afterwards (if allowed by\n        the propagator). If the activator returned ``False``, the propagation\n        step will never be executed.\n        The expected interface is::\n\n            ``f(images, augmenter, parents, default)``\n\n        with all arguments having identical meaning to the activator.\n\n    preprocessor : None or callable, optional\n        A function to call before an augmenter performed any augmentations.\n        The interface is:\n\n            ``f(images, augmenter, parents)``\n\n        with all arguments having identical meaning to the activator.\n        It is expected to return the input images, optionally modified.\n\n    postprocessor : None or callable, optional\n        A function to call after an augmenter performed augmentations.\n        The interface is the same as for the `preprocessor`.\n\n    Examples\n    --------\n    >>> import numpy as np\n    >>> import imgaug as ia\n    >>> import imgaug.augmenters as iaa\n    >>> seq = iaa.Sequential([\n    >>>     iaa.GaussianBlur(3.0, name=\"blur\"),\n    >>>     iaa.Dropout(0.05, name=\"dropout\"),\n    >>>     iaa.Affine(translate_px=-5, name=\"affine\")\n    >>> ])\n    >>> images = [np.zeros((10, 10), dtype=np.uint8)]\n    >>>\n    >>> def activator(images, augmenter, parents, default):\n    >>>     return False if augmenter.name in [\"blur\", \"dropout\"] else default\n    >>>\n    >>> seq_det = seq.to_deterministic()\n    >>> images_aug = seq_det.augment_images(images)\n    >>> heatmaps = [np.random.rand(*(3, 10, 10))]\n    >>> heatmaps_aug = seq_det.augment_images(\n    >>>     heatmaps,\n    >>>     hooks=ia.HooksImages(activator=activator)\n    >>> )\n\n    This augments images and their respective heatmaps in the same way.\n    The heatmaps however are only modified by ``Affine``, not by\n    ``GaussianBlur`` or ``Dropout``.\n\n    \"\"\"\n\n    def __init__(self, activator=None, propagator=None, preprocessor=None,\n                 postprocessor=None):\n        self.activator = activator\n        self.propagator = propagator\n        self.preprocessor = preprocessor\n        self.postprocessor = postprocessor\n\n    def is_activated(self, images, augmenter, parents, default):\n        \"\"\"Estimate whether an augmenter may be executed.\n\n        This also affects propagation of data to child augmenters.\n\n        Returns\n        -------\n        bool\n            If ``True``, the augmenter may be executed.\n            Otherwise ``False``.\n\n        \"\"\"\n        if self.activator is None:\n            return default\n        return self.activator(images, augmenter, parents, default)\n\n    def is_propagating(self, images, augmenter, parents, default):\n        \"\"\"Estimate whether an augmenter may call its children.\n\n        This function decides whether an augmenter with children is allowed\n        to call these in order to further augment the inputs.\n        Note that if the augmenter itself performs augmentations (before/after\n        calling its children), these may still be executed, even if this\n        method returns ``False``.\n\n        Returns\n        -------\n        bool\n            If ``True``, the augmenter may propagate data to its children.\n            Otherwise ``False``.\n\n        \"\"\"\n        if self.propagator is None:\n            return default\n        return self.propagator(images, augmenter, parents, default)\n\n    def preprocess(self, images, augmenter, parents):\n        \"\"\"Preprocess input data per augmenter before augmentation.\n\n        Returns\n        -------\n        (N,H,W,C) ndarray or (N,H,W) ndarray or list of (H,W,C) ndarray or list of (H,W) ndarray\n            The input images, optionally modified.\n\n        \"\"\"\n        if self.preprocessor is None:\n            return images\n        return self.preprocessor(images, augmenter, parents)\n\n    def postprocess(self, images, augmenter, parents):\n        \"\"\"Postprocess input data per augmenter after augmentation.\n\n        Returns\n        -------\n        (N,H,W,C) ndarray or (N,H,W) ndarray or list of (H,W,C) ndarray or list of (H,W) ndarray\n            The input images, optionally modified.\n\n        \"\"\"\n        if self.postprocessor is None:\n            return images\n        return self.postprocessor(images, augmenter, parents)\n\n\nclass HooksHeatmaps(HooksImages):\n    \"\"\"Class to intervene with heatmap augmentation runs.\n\n    This is e.g. useful to dynamically deactivate some augmenters.\n\n    This class is currently the same as the one for images. This may or may\n    not change in the future.\n\n    \"\"\"\n\n\nclass HooksKeypoints(HooksImages):\n    \"\"\"Class to intervene with keypoint augmentation runs.\n\n    This is e.g. useful to dynamically deactivate some augmenters.\n\n    This class is currently the same as the one for images. This may or may\n    not change in the future.\n\n    \"\"\"\n\n\n#####################################################################\n# Create classes/functions that were moved to other files and create\n# DeprecatedWarnings when they are called.\n#####################################################################\n\ndef _mark_moved_class_or_function(class_name_old, module_name_new,\n                                  class_name_new):\n    # pylint: disable=redefined-outer-name\n    class_name_new = (class_name_new\n                      if class_name_new is not None\n                      else class_name_old)\n\n    def _func(*args, **kwargs):\n        import importlib\n        warn_deprecated(\n            \"Using imgaug.imgaug.%s is deprecated. Use %s.%s instead.\" % (\n                class_name_old, module_name_new, class_name_new\n            ))\n        module = importlib.import_module(module_name_new)\n        return getattr(module, class_name_new)(*args, **kwargs)\n\n    return _func\n\n\nMOVED = [\n    (\"Keypoint\", \"imgaug.augmentables.kps\", None),\n    (\"KeypointsOnImage\", \"imgaug.augmentables.kps\", None),\n    (\"BoundingBox\", \"imgaug.augmentables.bbs\", None),\n    (\"BoundingBoxesOnImage\", \"imgaug.augmentables.bbs\", None),\n    (\"Polygon\", \"imgaug.augmentables.polys\", None),\n    (\"PolygonsOnImage\", \"imgaug.augmentables.polys\", None),\n    (\"MultiPolygon\", \"imgaug.augmentables.polys\", None),\n    (\"_ConcavePolygonRecoverer\", \"imgaug.augmentables.polys\", None),\n    (\"HeatmapsOnImage\", \"imgaug.augmentables.heatmaps\", None),\n    (\"SegmentationMapsOnImage\", \"imgaug.augmentables.segmaps\", None),\n    (\"Batch\", \"imgaug.augmentables.batches\", None),\n    (\"BatchLoader\", \"imgaug.multicore\", None),\n    (\"BackgroundAugmenter\", \"imgaug.multicore\", None),\n    (\"compute_geometric_median\", \"imgaug.augmentables.kps\", None),\n    (\"_convert_points_to_shapely_line_string\", \"imgaug.augmentables.polys\",\n     None),\n    (\"_interpolate_point_pair\", \"imgaug.augmentables.polys\", None),\n    (\"_interpolate_points\", \"imgaug.augmentables.polys\", None),\n    (\"_interpolate_points_by_max_distance\", \"imgaug.augmentables.polys\", None),\n    (\"pad\", \"imgaug.augmenters.size\", None),\n    (\"pad_to_aspect_ratio\", \"imgaug.augmenters.size\", None),\n    (\"pad_to_multiples_of\", \"imgaug.augmenters.size\", None),\n    (\"compute_paddings_for_aspect_ratio\", \"imgaug.augmenters.size\",\n     \"compute_paddings_to_reach_aspect_ratio\"),\n    (\"compute_paddings_to_reach_multiples_of\", \"imgaug.augmenters.size\", None),\n    (\"compute_paddings_to_reach_exponents_of\", \"imgaug.augmenters.size\", None),\n    (\"quokka\", \"imgaug.data\", None),\n    (\"quokka_square\", \"imgaug.data\", None),\n    (\"quokka_heatmap\", \"imgaug.data\", None),\n    (\"quokka_segmentation_map\", \"imgaug.data\", None),\n    (\"quokka_keypoints\", \"imgaug.data\", None),\n    (\"quokka_bounding_boxes\", \"imgaug.data\", None),\n    (\"quokka_polygons\", \"imgaug.data\", None),\n]\n\nfor class_name_old, module_name_new, class_name_new in MOVED:\n    locals()[class_name_old] = _mark_moved_class_or_function(\n        class_name_old, module_name_new, class_name_new)\n"
  },
  {
    "path": "imgaug/multicore.py",
    "content": "\"\"\"Classes and functions dealing with augmentation on multiple CPU cores.\"\"\"\nfrom __future__ import print_function, division, absolute_import\nimport sys\nimport multiprocessing\nimport threading\nimport traceback\nimport time\nimport random\nimport platform\n\nimport numpy as np\nimport cv2\n\nimport imgaug.imgaug as ia\nimport imgaug.random as iarandom\nfrom imgaug.augmentables.batches import Batch, UnnormalizedBatch\n\nif sys.version_info[0] == 2:\n    # pylint: disable=redefined-builtin, import-error\n    import cPickle as pickle\n    from Queue import Empty as QueueEmpty, Full as QueueFull\n    import socket\n    BrokenPipeError = socket.error\nelif sys.version_info[0] == 3:\n    import pickle\n    from queue import Empty as QueueEmpty, Full as QueueFull\n\n\n_CONTEXT = None\n\n\n# Added in 0.4.0.\ndef _get_context_method():\n    vinfo = sys.version_info\n\n    # get_context() is only supported in 3.5 and later (same for\n    # set_start_method)\n    get_context_unsupported = (\n        vinfo[0] == 2\n        or (vinfo[0] == 3 and vinfo[1] <= 3))\n\n    method = None\n    # Fix random hanging code in NixOS by switching to spawn method,\n    # see issue #414\n    # TODO This is only a workaround and doesn't really fix the underlying\n    #      issue. The cause of the underlying issue is currently unknown.\n    #      Its possible that #535 fixes the issue, though earlier tests\n    #      indicated that the cause was something else.\n    # TODO this might break the semaphore used to prevent out of memory\n    #      errors\n    if \"NixOS\" in platform.version():\n        method = \"spawn\"\n        if get_context_unsupported:\n            ia.warn(\"Detected usage of imgaug.multicore in python <=3.4 \"\n                    \"and NixOS. This is known to sometimes cause endlessly \"\n                    \"hanging programs when also making use of multicore \"\n                    \"augmentation (aka background augmentation). Use \"\n                    \"python 3.5 or later to prevent this.\")\n    elif platform.system() == \"Darwin\" and vinfo[0:2] == (3, 7):\n        # On Mac with python 3.7 there seems to be a problem with matplotlib,\n        # resulting in the error \"libc++abi.dylib: terminating with uncaught\n        # exception of type std::runtime_error: Couldn't close file\".\n        # The error seems to be due to opened files that get closed in\n        # child processes and can be prevented by switching to spawn mode.\n        # See https://github.com/matplotlib/matplotlib/issues/15410\n        # and https://bugs.python.org/issue33725.\n        # It is possible that this problem also affects other python versions,\n        # but here it only appeared (consistently) in the 3.7 tests and the\n        # reports also seem to be focused around 3.7, suggesting explicitly\n        # to update to 3.8.2.\n        method = \"spawn\"\n\n    if get_context_unsupported:\n        return False\n    return method\n\n\n# Added in 0.4.0.\ndef _set_context(method):\n    # method=False indicates that multiprocessing module (i.e. no context)\n    # should be used, e.g. because get_context() is not supported\n    globals()[\"_CONTEXT\"] = (\n        multiprocessing if method is False\n        else multiprocessing.get_context(method))\n\n\n# Added in 0.4.0.\ndef _reset_context():\n    globals()[\"_CONTEXT\"] = None\n\n\n# Added in 0.4.0.\ndef _autoset_context():\n    _set_context(_get_context_method())\n\n\n# Added in 0.4.0.\ndef _get_context():\n    if _CONTEXT is None:\n        _autoset_context()\n    return _CONTEXT\n\n\nclass Pool(object):\n    \"\"\"\n    Wrapper around ``multiprocessing.Pool`` for multicore augmentation.\n\n    Parameters\n    ----------\n    augseq : imgaug.augmenters.meta.Augmenter\n        The augmentation sequence to apply to batches.\n\n    processes : None or int, optional\n        The number of background workers, similar to the same parameter in\n        multiprocessing.Pool. If ``None``, the number of the machine's CPU\n        cores will be used (this counts hyperthreads as CPU cores). If this is\n        set to a negative value ``p``, then ``P - abs(p)`` will be used,\n        where ``P`` is the number of CPU cores. E.g. ``-1`` would use all\n        cores except one (this is useful to e.g. reserve one core to feed\n        batches to the GPU).\n\n    maxtasksperchild : None or int, optional\n        The number of tasks done per worker process before the process is\n        killed and restarted, similar to the same parameter in\n        multiprocessing.Pool. If ``None``, worker processes will not be\n        automatically restarted.\n\n    seed : None or int, optional\n        The seed to use for child processes. If ``None``, a random seed will\n        be used.\n\n    \"\"\"\n    # This attribute saves the augmentation sequence for background workers so\n    # that it does not have to be resend with every batch. The attribute is set\n    # once per worker in the worker's initializer. As each worker has its own\n    # process, it is a different variable per worker (though usually should be\n    # of equal content).\n    _WORKER_AUGSEQ = None\n\n    # This attribute saves the initial seed for background workers so that for\n    # any future batch the batch's specific seed can be derived, roughly via\n    # SEED_START+SEED_BATCH. As each worker has its own process, this seed can\n    # be unique per worker even though all seemingly use the same constant\n    # attribute.\n    _WORKER_SEED_START = None\n\n    def __init__(self, augseq, processes=None, maxtasksperchild=None,\n                 seed=None):\n        # make sure that don't call pool again in a child process\n        assert Pool._WORKER_AUGSEQ is None, (\n            \"_WORKER_AUGSEQ was already set when calling Pool.__init__(). \"\n            \"Did you try to instantiate a Pool within a Pool?\")\n        assert processes is None or processes != 0, (\n            \"Expected `processes` to be `None` (\\\"use as many cores as \"\n            \"available\\\") or a negative integer (\\\"use as many as available \"\n            \"MINUS this number\\\") or an integer>1 (\\\"use exactly that many \"\n            \"processes\\\"). Got type %s, value %s instead.\" % (\n                type(processes), str(processes))\n        )\n\n        self.augseq = augseq\n        self.processes = processes\n        self.maxtasksperchild = maxtasksperchild\n\n        if seed is not None:\n            assert iarandom.SEED_MIN_VALUE <= seed <= iarandom.SEED_MAX_VALUE, (\n                \"Expected `seed` to be either `None` or a value between \"\n                \"%d and %d. Got type %s, value %s instead.\" % (\n                    iarandom.SEED_MIN_VALUE,\n                    iarandom.SEED_MAX_VALUE,\n                    type(seed),\n                    str(seed)\n                )\n            )\n        self.seed = seed\n\n        # multiprocessing.Pool instance\n        self._pool = None\n\n        # Running counter of the number of augmented batches. This will be\n        # used to send indexes for each batch to the workers so that they can\n        # augment using SEED_BASE+SEED_BATCH and ensure consistency of applied\n        # augmentation order between script runs.\n        self._batch_idx = 0\n\n    @property\n    def pool(self):\n        \"\"\"Return or create the ``multiprocessing.Pool`` instance.\n\n        This creates a new instance upon the first call and afterwards\n        returns that instance (until the property ``_pool`` is set to\n        ``None`` again).\n\n        Returns\n        -------\n        multiprocessing.Pool\n            The ``multiprocessing.Pool`` used internally by this\n            ``imgaug.multicore.Pool``.\n\n        \"\"\"\n        if self._pool is None:\n            processes = self.processes\n            if processes is not None and processes < 0:\n                # cpu count returns the number of logical cpu cores, i.e.\n                # including hyperthreads could also use\n                # os.sched_getaffinity(0) here, which seems to not exist on\n                # BSD though.\n                # In python 3.4+, there is also os.cpu_count(), which\n                # multiprocessing.cpu_count() then redirects to.\n                # At least one guy on stackoverflow.com/questions/1006289\n                # reported that only os.* existed, not the multiprocessing\n                # method.\n                # TODO make this also check if os.cpu_count exists as a\n                #      fallback\n                try:\n                    processes = _get_context().cpu_count() - abs(processes)\n                    processes = max(processes, 1)\n                except (ImportError, NotImplementedError):\n                    ia.warn(\n                        \"Could not find method multiprocessing.cpu_count(). \"\n                        \"This will likely lead to more CPU cores being used \"\n                        \"for the background augmentation than originally \"\n                        \"intended.\")\n                    processes = None\n\n            self._pool = _get_context().Pool(\n                processes,\n                initializer=_Pool_initialize_worker,\n                initargs=(self.augseq, self.seed),\n                maxtasksperchild=self.maxtasksperchild)\n        return self._pool\n\n    def map_batches(self, batches, chunksize=None):\n        \"\"\"\n        Augment a list of batches.\n\n        Parameters\n        ----------\n        batches : list of imgaug.augmentables.batches.Batch\n            The batches to augment.\n\n        chunksize : None or int, optional\n            Rough indicator of how many tasks should be sent to each worker.\n            Increasing this number can improve performance.\n\n        Returns\n        -------\n        list of imgaug.augmentables.batches.Batch\n            Augmented batches.\n\n        \"\"\"\n        self._assert_batches_is_list(batches)\n        return self.pool.map(\n            _Pool_starworker,\n            self._handle_batch_ids(batches),\n            chunksize=chunksize)\n\n    def map_batches_async(self, batches, chunksize=None, callback=None,\n                          error_callback=None):\n        \"\"\"\n        Augment batches asynchonously.\n\n        Parameters\n        ----------\n        batches : list of imgaug.augmentables.batches.Batch\n            The batches to augment.\n\n        chunksize : None or int, optional\n            Rough indicator of how many tasks should be sent to each worker.\n            Increasing this number can improve performance.\n\n        callback : None or callable, optional\n            Function to call upon finish. See ``multiprocessing.Pool``.\n\n        error_callback : None or callable, optional\n            Function to call upon errors. See ``multiprocessing.Pool``.\n\n        Returns\n        -------\n        multiprocessing.MapResult\n            Asynchonous result. See ``multiprocessing.Pool``.\n\n        \"\"\"\n        self._assert_batches_is_list(batches)\n        return self.pool.map_async(\n            _Pool_starworker,\n            self._handle_batch_ids(batches),\n            chunksize=chunksize,\n            callback=callback,\n            error_callback=error_callback)\n\n    @classmethod\n    def _assert_batches_is_list(cls, batches):\n        assert isinstance(batches, list), (\n            \"Expected `batches` to be a list, got type %s. Call \"\n            \"imap_batches() if you use generators.\") % (type(batches),)\n\n    def imap_batches(self, batches, chunksize=1, output_buffer_size=None):\n        \"\"\"\n        Augment batches from a generator.\n\n        Pattern for output buffer constraint is from\n        https://stackoverflow.com/a/47058399.\n\n        Parameters\n        ----------\n        batches : generator of imgaug.augmentables.batches.Batch\n            The batches to augment, provided as a generator. Each call to the\n            generator should yield exactly one batch.\n\n        chunksize : None or int, optional\n            Rough indicator of how many tasks should be sent to each worker.\n            Increasing this number can improve performance.\n\n        output_buffer_size : None or int, optional\n            Max number of batches to handle *at the same time* in the *whole*\n            pipeline (including already augmented batches that are waiting to\n            be requested). If the buffer size is reached, no new batches will\n            be loaded from `batches` until a produced (i.e. augmented) batch is\n            consumed (i.e. requested from this method).\n            The buffer is unlimited if this is set to ``None``. For large\n            datasets, this should be set to an integer value to avoid filling\n            the whole RAM if loading+augmentation happens faster than training.\n\n            *New in version 0.3.0.*\n\n        Yields\n        ------\n        imgaug.augmentables.batches.Batch\n            Augmented batch.\n\n        \"\"\"\n        self._assert_batches_is_generator(batches)\n\n        # buffer is either None or a Semaphore\n        output_buffer_left = _create_output_buffer_left(output_buffer_size)\n\n        # TODO change this to 'yield from' once switched to 3.3+\n        gen = self.pool.imap(\n            _Pool_starworker,\n            self._ibuffer_batch_loading(\n                self._handle_batch_ids_gen(batches),\n                output_buffer_left\n            ),\n            chunksize=chunksize)\n\n        for batch in gen:\n            yield batch\n            if output_buffer_left is not None:\n                output_buffer_left.release()\n\n    def imap_batches_unordered(self, batches, chunksize=1,\n                               output_buffer_size=None):\n        \"\"\"Augment batches from a generator (without preservation of order).\n\n        Pattern for output buffer constraint is from\n        https://stackoverflow.com/a/47058399.\n\n        Parameters\n        ----------\n        batches : generator of imgaug.augmentables.batches.Batch\n            The batches to augment, provided as a generator. Each call to the\n            generator should yield exactly one batch.\n\n        chunksize : None or int, optional\n            Rough indicator of how many tasks should be sent to each worker.\n            Increasing this number can improve performance.\n\n        output_buffer_size : None or int, optional\n            Max number of batches to handle *at the same time* in the *whole*\n            pipeline (including already augmented batches that are waiting to\n            be requested). If the buffer size is reached, no new batches will\n            be loaded from `batches` until a produced (i.e. augmented) batch is\n            consumed (i.e. requested from this method).\n            The buffer is unlimited if this is set to ``None``. For large\n            datasets, this should be set to an integer value to avoid filling\n            the whole RAM if loading+augmentation happens faster than training.\n\n            *New in version 0.3.0.*\n\n        Yields\n        ------\n        imgaug.augmentables.batches.Batch\n            Augmented batch.\n\n        \"\"\"\n        self._assert_batches_is_generator(batches)\n\n        # buffer is either None or a Semaphore\n        output_buffer_left = _create_output_buffer_left(output_buffer_size)\n\n        gen = self.pool.imap_unordered(\n            _Pool_starworker,\n            self._ibuffer_batch_loading(\n                self._handle_batch_ids_gen(batches),\n                output_buffer_left\n            ),\n            chunksize=chunksize\n        )\n\n        for batch in gen:\n            yield batch\n            if output_buffer_left is not None:\n                output_buffer_left.release()\n\n    @classmethod\n    def _assert_batches_is_generator(cls, batches):\n        assert ia.is_generator(batches), (\n            \"Expected `batches` to be generator, got type %s. Call \"\n            \"map_batches() if you use lists.\") % (type(batches),)\n\n    def __enter__(self):\n        assert self._pool is None, (\n            \"Tried to __enter__ a pool that has already been initialized.\")\n        _ = self.pool  # initialize internal multiprocessing pool instance\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        self.close()\n\n    def close(self):\n        \"\"\"Close the pool gracefully.\"\"\"\n        if self._pool is not None:\n            self._pool.close()\n            self._pool.join()\n            self._pool = None\n\n    def terminate(self):\n        \"\"\"Terminate the pool immediately.\"\"\"\n        if self._pool is not None:\n            self._pool.terminate()\n            self._pool.join()\n            self._pool = None\n\n    # TODO why does this function exist if it may only be called after\n    #      close/terminate and both of these two already call join() themselves\n    def join(self):\n        \"\"\"\n        Wait for the workers to exit.\n\n        This may only be called after first calling\n        :func:`~imgaug.multicore.Pool.close` or\n        :func:`~imgaug.multicore.Pool.terminate`.\n\n        \"\"\"\n        if self._pool is not None:\n            self._pool.join()\n\n    def _handle_batch_ids(self, batches):\n        ids = np.arange(self._batch_idx, self._batch_idx + len(batches))\n        inputs = list(zip(ids, batches))\n        self._batch_idx += len(batches)\n        return inputs\n\n    def _handle_batch_ids_gen(self, batches):\n        for batch in batches:\n            batch_idx = self._batch_idx\n            yield batch_idx, batch\n            self._batch_idx += 1\n\n    @classmethod\n    def _ibuffer_batch_loading(cls, batches, output_buffer_left):\n        for batch in batches:\n            if output_buffer_left is not None:\n                output_buffer_left.acquire()\n            yield batch\n\n\ndef _create_output_buffer_left(output_buffer_size):\n    output_buffer_left = None\n    if output_buffer_size:\n        assert output_buffer_size > 0, (\n            \"Expected buffer size to be greater than zero, but got size %d \"\n            \"instead.\" % (output_buffer_size,))\n        output_buffer_left = _get_context().Semaphore(output_buffer_size)\n    return output_buffer_left\n\n\n# This could be a classmethod or staticmethod of Pool in 3.x, but in 2.7 that\n# leads to pickle errors.\ndef _Pool_initialize_worker(augseq, seed_start):\n    # pylint: disable=invalid-name, protected-access\n\n    # Not using this seems to have caused infinite hanging in the case\n    # of gaussian blur on at least MacOSX.\n    # It is also in most cases probably not sensible to use multiple\n    # threads while already running augmentation in multiple processes.\n    cv2.setNumThreads(0)\n\n    if seed_start is None:\n        # pylint falsely thinks in older versions that\n        # multiprocessing.current_process() was not callable, see\n        # https://github.com/PyCQA/pylint/issues/1699\n        # pylint: disable=not-callable\n        process_name = _get_context().current_process().name\n        # pylint: enable=not-callable\n\n        # time_ns() exists only in 3.7+\n        if sys.version_info[0] == 3 and sys.version_info[1] >= 7:\n            seed_offset = time.time_ns()\n        else:\n            seed_offset = int(time.time() * 10**6) % 10**6\n        seed = hash(process_name) + seed_offset\n        _reseed_global_local(seed, augseq)\n    Pool._WORKER_SEED_START = seed_start\n    Pool._WORKER_AUGSEQ = augseq\n    # not sure if really necessary, but shouldn't hurt either\n    Pool._WORKER_AUGSEQ.localize_random_state_()\n\n\n# This could be a classmethod or staticmethod of Pool in 3.x, but in 2.7 that\n# leads to pickle errors.\ndef _Pool_worker(batch_idx, batch):\n    # pylint: disable=invalid-name, protected-access\n    assert ia.is_single_integer(batch_idx), (\n        \"Expected `batch_idx` to be an integer. Got type %s instead.\" % (\n            type(batch_idx)\n        ))\n    assert isinstance(batch, (UnnormalizedBatch, Batch)), (\n        \"Expected `batch` to be either an instance of \"\n        \"`imgaug.augmentables.batches.UnnormalizedBatch` or \"\n        \"`imgaug.augmentables.batches.Batch`. Got type %s instead.\" % (\n            type(batch)\n        ))\n    assert Pool._WORKER_AUGSEQ is not None, (\n        \"Expected `Pool._WORKER_AUGSEQ` to NOT be `None`. Did you manually \"\n        \"call _Pool_worker()?\")\n\n    augseq = Pool._WORKER_AUGSEQ\n    # TODO why is this if here? _WORKER_SEED_START should always be set?\n    if Pool._WORKER_SEED_START is not None:\n        seed = Pool._WORKER_SEED_START + batch_idx\n        _reseed_global_local(seed, augseq)\n    result = augseq.augment_batch_(batch)\n    return result\n\n\n# could be a classmethod or staticmethod of Pool in 3.x, but in 2.7 that leads\n# to pickle errors starworker is here necessary, because starmap does not exist\n# in 2.7\ndef _Pool_starworker(inputs):\n    # pylint: disable=invalid-name\n    return _Pool_worker(*inputs)\n\n\ndef _reseed_global_local(base_seed, augseq):\n    seed_global = _derive_seed(base_seed, -10**9)\n    seed_local = _derive_seed(base_seed)\n    iarandom.seed(seed_global)\n    augseq.seed_(seed_local)\n\n\ndef _derive_seed(base_seed, offset=0):\n    return (\n        iarandom.SEED_MIN_VALUE\n        + (base_seed + offset)\n        % (iarandom.SEED_MAX_VALUE - iarandom.SEED_MIN_VALUE)\n    )\n\n\nclass BatchLoader(object):\n    \"\"\"**Deprecated**. Load batches in the background.\n\n    Deprecated. Use ``imgaug.multicore.Pool`` instead.\n\n    Loaded batches can be accesses using :attr:`imgaug.BatchLoader.queue`.\n\n    Parameters\n    ----------\n    load_batch_func : callable or generator\n        Generator or generator function (i.e. function that yields Batch\n        objects) or a function that returns a list of Batch objects.\n        Background loading automatically stops when the last batch was yielded\n        or the last batch in the list was reached.\n\n    queue_size : int, optional\n        Maximum number of batches to store in the queue. May be set higher\n        for small images and/or small batches.\n\n    nb_workers : int, optional\n        Number of workers to run in the background.\n\n    threaded : bool, optional\n        Whether to run the background processes using threads (True) or full\n        processes (False).\n\n    \"\"\"\n\n    @ia.deprecated(alt_func=\"imgaug.multicore.Pool\")\n    def __init__(self, load_batch_func, queue_size=50, nb_workers=1,\n                 threaded=True):\n        assert queue_size >= 2, (\n            \"Queue size for BatchLoader must be at least 2, \"\n            \"got %d.\" % (queue_size,))\n        assert nb_workers >= 1, (\n            \"Number of workers for BatchLoader must be at least 1, \"\n            \"got %d\" % (nb_workers,))\n        self._queue_internal = multiprocessing.Queue(queue_size//2)\n        self.queue = multiprocessing.Queue(queue_size//2)\n        self.join_signal = multiprocessing.Event()\n        self.workers = []\n        self.threaded = threaded\n        seeds = iarandom.get_global_rng().generate_seeds_(nb_workers)\n        for i in range(nb_workers):\n            if threaded:\n                worker = threading.Thread(\n                    target=self._load_batches,\n                    args=(load_batch_func, self._queue_internal,\n                          self.join_signal, None)\n                )\n            else:\n                worker = multiprocessing.Process(\n                    target=self._load_batches,\n                    args=(load_batch_func, self._queue_internal,\n                          self.join_signal, seeds[i])\n                )\n            worker.daemon = True\n            worker.start()\n            self.workers.append(worker)\n\n        self.main_worker_thread = threading.Thread(\n            target=self._main_worker,\n            args=()\n        )\n        self.main_worker_thread.daemon = True\n        self.main_worker_thread.start()\n\n    def count_workers_alive(self):\n        return sum([int(worker.is_alive()) for worker in self.workers])\n\n    def all_finished(self):\n        \"\"\"\n        Determine whether the workers have finished the loading process.\n\n        Returns\n        -------\n        out : bool\n            True if all workers have finished. Else False.\n\n        \"\"\"\n        return self.count_workers_alive() == 0\n\n    def _main_worker(self):\n        workers_running = self.count_workers_alive()\n\n        while workers_running > 0 and not self.join_signal.is_set():\n            # wait for a new batch in the source queue and load it\n            try:\n                batch_str = self._queue_internal.get(timeout=0.1)\n                if batch_str == \"\":\n                    workers_running -= 1\n                else:\n                    self.queue.put(batch_str)\n            except QueueEmpty:\n                time.sleep(0.01)\n            except (EOFError, BrokenPipeError):\n                break\n\n            workers_running = self.count_workers_alive()\n\n        # All workers have finished, move the remaining entries from internal\n        # to external queue\n        while True:\n            try:\n                batch_str = self._queue_internal.get(timeout=0.005)\n                if batch_str != \"\":\n                    self.queue.put(batch_str)\n            except QueueEmpty:\n                break\n            except (EOFError, BrokenPipeError):\n                break\n\n        self.queue.put(pickle.dumps(None, protocol=-1))\n        time.sleep(0.01)\n\n    @classmethod\n    def _load_batches(cls, load_batch_func, queue_internal, join_signal,\n                      seedval):\n        # pylint: disable=broad-except\n        if seedval is not None:\n            random.seed(seedval)\n            np.random.seed(seedval)\n            iarandom.seed(seedval)\n\n        try:\n            gen = (\n                load_batch_func()\n                if not ia.is_generator(load_batch_func)\n                else load_batch_func\n            )\n            for batch in gen:\n                assert isinstance(batch, Batch), (\n                    \"Expected batch returned by load_batch_func to \"\n                    \"be of class imgaug.Batch, got %s.\" % (\n                        type(batch),))\n                batch_pickled = pickle.dumps(batch, protocol=-1)\n                while not join_signal.is_set():\n                    try:\n                        queue_internal.put(batch_pickled, timeout=0.005)\n                        break\n                    except QueueFull:\n                        pass\n                if join_signal.is_set():\n                    break\n        except Exception:\n            traceback.print_exc()\n        finally:\n            queue_internal.put(\"\")\n        time.sleep(0.01)\n\n    def terminate(self):\n        \"\"\"Stop all workers.\"\"\"\n        # pylint: disable=protected-access\n        if not self.join_signal.is_set():\n            self.join_signal.set()\n        # give minimal time to put generated batches in queue and gracefully\n        # shut down\n        time.sleep(0.01)\n\n        if self.main_worker_thread.is_alive():\n            self.main_worker_thread.join()\n\n        if self.threaded:\n            for worker in self.workers:\n                if worker.is_alive():\n                    worker.join()\n        else:\n            for worker in self.workers:\n                if worker.is_alive():\n                    worker.terminate()\n                    worker.join()\n\n            # wait until all workers are fully terminated\n            while not self.all_finished():\n                time.sleep(0.001)\n\n        # empty queue until at least one element can be added and place None\n        # as signal that BL finished\n        if self.queue.full():\n            self.queue.get()\n        self.queue.put(pickle.dumps(None, protocol=-1))\n        time.sleep(0.01)\n\n        # clean the queue, this reportedly prevents hanging threads\n        while True:\n            try:\n                self._queue_internal.get(timeout=0.005)\n            except QueueEmpty:\n                break\n\n        if not self._queue_internal._closed:\n            self._queue_internal.close()\n        if not self.queue._closed:\n            self.queue.close()\n        self._queue_internal.join_thread()\n        self.queue.join_thread()\n        time.sleep(0.025)\n\n    def __del__(self):\n        if not self.join_signal.is_set():\n            self.join_signal.set()\n\n\nclass BackgroundAugmenter(object):\n    \"\"\"\n    **Deprecated**. Augment batches in the background processes.\n\n    Deprecated. Use ``imgaug.multicore.Pool`` instead.\n\n    This is a wrapper around the multiprocessing module.\n\n    Parameters\n    ----------\n    batch_loader : BatchLoader or multiprocessing.Queue\n        BatchLoader object that loads the data fed into the\n        BackgroundAugmenter, or alternatively a Queue. If a Queue, then it\n        must be made sure that a final ``None`` in the Queue signals that the\n        loading is finished and no more batches will follow. Otherwise the\n        BackgroundAugmenter will wait forever for the next batch.\n\n    augseq : Augmenter\n        An augmenter to apply to all loaded images.\n        This may be e.g. a Sequential to apply multiple augmenters.\n\n    queue_size : int\n        Size of the queue that is used to temporarily save the augmentation\n        results. Larger values offer the background processes more room\n        to save results when the main process doesn't load much, i.e. they\n        can lead to smoother and faster training. For large images, high\n        values can block a lot of RAM though.\n\n    nb_workers : 'auto' or int\n        Number of background workers to spawn.\n        If ``auto``, it will be set to ``C-1``, where ``C`` is the number of\n        CPU cores.\n\n    \"\"\"\n\n    @ia.deprecated(alt_func=\"imgaug.multicore.Pool\")\n    def __init__(self, batch_loader, augseq, queue_size=50, nb_workers=\"auto\"):\n        assert queue_size > 0, (\n            \"Expected 'queue_size' to be at least 1, got %d.\" % (queue_size,))\n        self.augseq = augseq\n        self.queue_source = (\n            batch_loader\n            if isinstance(batch_loader, multiprocessing.queues.Queue)\n            else batch_loader.queue\n        )\n        self.queue_result = multiprocessing.Queue(queue_size)\n\n        if nb_workers == \"auto\":\n            try:\n                nb_workers = multiprocessing.cpu_count()\n            except (ImportError, NotImplementedError):\n                nb_workers = 1\n            # try to reserve at least one core for the main process\n            nb_workers = max(1, nb_workers - 1)\n        else:\n            assert nb_workers >= 1, (\n                \"Expected 'nb_workers' to be \\\"auto\\\" or at least 1, \"\n                \"got %d instead.\" % (nb_workers,))\n\n        self.nb_workers = nb_workers\n        self.workers = []\n        self.nb_workers_finished = 0\n\n        seeds = iarandom.get_global_rng().generate_seeds_(nb_workers)\n        for i in range(nb_workers):\n            worker = multiprocessing.Process(\n                target=self._augment_images_worker,\n                args=(augseq, self.queue_source, self.queue_result, seeds[i])\n            )\n            worker.daemon = True\n            worker.start()\n            self.workers.append(worker)\n\n    def all_finished(self):\n        return self.nb_workers_finished == self.nb_workers\n\n    def get_batch(self):\n        \"\"\"\n        Returns a batch from the queue of augmented batches.\n\n        If workers are still running and there are no batches in the queue,\n        it will automatically wait for the next batch.\n\n        Returns\n        -------\n        out : None or imgaug.Batch\n            One batch or None if all workers have finished.\n\n        \"\"\"\n        if self.all_finished():\n            return None\n\n        batch_str = self.queue_result.get()\n        batch = pickle.loads(batch_str)\n        if batch is not None:\n            return batch\n\n        self.nb_workers_finished += 1\n        if self.nb_workers_finished >= self.nb_workers:\n            try:\n                # remove `None` from the source queue\n                self.queue_source.get(timeout=0.001)\n            except QueueEmpty:\n                pass\n            return None\n        return self.get_batch()\n\n    @classmethod\n    def _augment_images_worker(cls, augseq, queue_source, queue_result,\n                               seedval):\n        \"\"\"\n        Augment endlessly images in the source queue.\n\n        This is a worker function for that endlessly queries the source queue\n        (input batches), augments batches in it and sends the result to the\n        output queue.\n\n        \"\"\"\n        np.random.seed(seedval)\n        random.seed(seedval)\n        augseq.seed_(seedval)\n        iarandom.seed(seedval)\n\n        loader_finished = False\n\n        while not loader_finished:\n            # wait for a new batch in the source queue and load it\n            try:\n                batch_str = queue_source.get(timeout=0.1)\n                batch = pickle.loads(batch_str)\n                if batch is None:\n                    loader_finished = True\n                    # put it back in so that other workers know that the\n                    # loading queue is finished\n                    queue_source.put(pickle.dumps(None, protocol=-1))\n                else:\n                    batch_aug = augseq.augment_batch_(batch)\n\n                    # send augmented batch to output queue\n                    batch_str = pickle.dumps(batch_aug, protocol=-1)\n                    queue_result.put(batch_str)\n            except QueueEmpty:\n                time.sleep(0.01)\n\n        queue_result.put(pickle.dumps(None, protocol=-1))\n        time.sleep(0.01)\n\n    def terminate(self):\n        \"\"\"\n        Terminates all background processes immediately.\n\n        This will also free their RAM.\n\n        \"\"\"\n        # pylint: disable=protected-access\n        for worker in self.workers:\n            if worker.is_alive():\n                worker.terminate()\n        self.nb_workers_finished = len(self.workers)\n\n        if not self.queue_result._closed:\n            self.queue_result.close()\n        time.sleep(0.01)\n\n    def __del__(self):\n        time.sleep(0.1)\n        self.terminate()\n"
  },
  {
    "path": "imgaug/parameters.py",
    "content": "\"\"\"Classes and methods to use for parameters of augmenters.\n\nThis module contains e.g. classes representing probability\ndistributions (guassian, poisson etc.), classes representing noise sources\nand methods to normalize parameter-related user inputs.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\nimport copy as copy_module\nfrom collections import defaultdict\nfrom abc import ABCMeta, abstractmethod\nimport tempfile\nfrom functools import reduce, wraps\nfrom operator import mul as mul_op\n\nimport numpy as np\nimport six\nimport six.moves as sm\nimport scipy\nimport scipy.stats\nimport imageio\nimport cv2\n\nfrom . import imgaug as ia\nfrom . import dtypes as iadt\nfrom . import random as iarandom\nfrom .external.opensimplex import OpenSimplex\n\n\n# Added in 0.5.0.\n_PREFETCHING_ENABLED = True\n# Added in 0.5.0.\n_NB_PREFETCH = 10000\n# Added in 0.5.0.\n_NB_PREFETCH_STRINGS = 1000\n\n\n# Added in 0.5.0.\ndef _prefetchable(func):\n    @wraps(func)\n    def _inner(*args, **kwargs):\n        param = func(*args, **kwargs)\n        return _wrap_leafs_of_param_in_prefetchers(param, _NB_PREFETCH)\n    return _inner\n\n\n# Added in 0.5.0.\ndef _prefetchable_str(func):\n    @wraps(func)\n    def _inner(*args, **kwargs):\n        param = func(*args, **kwargs)\n        return _wrap_leafs_of_param_in_prefetchers(param, _NB_PREFETCH_STRINGS)\n    return _inner\n\n\n# Added in 0.5.0.\ndef _wrap_param_in_prefetchers(param, nb_prefetch):\n    for key, value in param.__dict__.items():\n        if isinstance(value, StochasticParameter):\n            param.__dict__[key] = _wrap_param_in_prefetchers(value, nb_prefetch)\n\n    if param.prefetchable:\n        return AutoPrefetcher(param, nb_prefetch)\n    return param\n\n\n# Added in 0.5.0.\ndef _wrap_leafs_of_param_in_prefetchers(param, nb_prefetch):\n    param_wrapped, _did_wrap_any_child = \\\n        _wrap_leafs_of_param_in_prefetchers_recursive(\n            param, nb_prefetch\n        )\n    return param_wrapped\n\n\n# Added in 0.5.0.\ndef _wrap_leafs_of_param_in_prefetchers_recursive(param, nb_prefetch):\n    # Do not descent into AutoPrefetcher, otherwise we risk turning an\n    # AutoPrefetcher(X) into AutoPrefetcher(AutoPrefetcher(X)) if X is\n    # prefetchable\n    if isinstance(param, AutoPrefetcher):\n        # report did_wrap_any_child=True here, so that parent parameters\n        # are not wrapped in prefetchers, which could lead to ugly scenarios\n        # like AutoPrefetcher(Normal(AutoPrefetcher(Uniform(-1.0, 1.0))),\n        return param, True\n\n    if isinstance(param, (list, tuple)):\n        result = []\n        did_wrap_any_child = False\n        for param_i in param:\n            param_i_wrapped, did_wrap_any_child_i = \\\n                _wrap_leafs_of_param_in_prefetchers_recursive(\n                    param_i, nb_prefetch\n                )\n            result.append(param_i_wrapped)\n            did_wrap_any_child = did_wrap_any_child or did_wrap_any_child_i\n\n        if not did_wrap_any_child:\n            return param, False\n        if isinstance(param, tuple):\n            return tuple(result), did_wrap_any_child\n        return result, did_wrap_any_child\n\n    if not isinstance(param, StochasticParameter):\n        return param, False\n\n    did_wrap_any_child = False\n    for key, value in param.__dict__.items():\n        param_wrapped, did_wrap_i = \\\n            _wrap_leafs_of_param_in_prefetchers_recursive(\n                value, nb_prefetch\n            )\n\n        param.__dict__[key] = param_wrapped\n        did_wrap_any_child = did_wrap_any_child or did_wrap_i\n\n    if param.prefetchable and not did_wrap_any_child and _PREFETCHING_ENABLED:\n        return AutoPrefetcher(param, nb_prefetch), True\n    return param, did_wrap_any_child\n\n\ndef toggle_prefetching(enabled):\n    \"\"\"Toggle prefetching on or off.\n\n    Added in 0.5.0.\n\n    Parameters\n    ----------\n    enabled : bool\n        Whether enabled is activated (``True``) or off (``False``).\n\n    \"\"\"\n    # pylint: disable=global-statement\n    global _PREFETCHING_ENABLED\n    _PREFETCHING_ENABLED = enabled\n\n\nclass toggled_prefetching(object):  # pylint: disable=invalid-name\n    \"\"\"Context that toggles prefetching on or off depending on a flag.\n\n    Added in 0.5.0.\n\n    Parameters\n    ----------\n    enabled : bool\n        Whether enabled is activated (``True``) or off (``False``).\n\n    \"\"\"\n\n    # Added in 0.5.0.\n    def __init__(self, enabled):\n        self.enabled = enabled\n        self._old_state = None\n\n    # Added in 0.5.0.\n    def __enter__(self):\n        # pylint: disable=global-statement\n        global _PREFETCHING_ENABLED\n        self._old_state = _PREFETCHING_ENABLED\n        _PREFETCHING_ENABLED = self.enabled\n\n    # Added in 0.5.0.\n    def __exit__(self, exception_type, exception_value, exception_traceback):\n        # pylint: disable=global-statement\n        global _PREFETCHING_ENABLED\n        _PREFETCHING_ENABLED = self._old_state\n\n\nclass no_prefetching(toggled_prefetching):  # pylint: disable=invalid-name\n    \"\"\"Context that deactviates prefetching.\n\n    Added in 0.5.0.\n\n    \"\"\"\n\n    # Added in 0.5.0.\n    def __init__(self):\n        super(no_prefetching, self).__init__(False)\n\n\ndef _check_value_range(value, name, value_range):\n    if value_range is None:\n        return True\n\n    if isinstance(value_range, tuple):\n        assert len(value_range) == 2, (\n            \"If 'value_range' is a tuple, it must contain exactly 2 entries, \"\n            \"got %d.\" % (len(value_range),))\n\n        if value_range[0] is None and value_range[1] is None:\n            return True\n\n        if value_range[0] is None:\n            assert value <= value_range[1], (\n                \"Parameter '%s' is outside of the expected value \"\n                \"range (x <= %.4f)\" % (name, value_range[1]))\n            return True\n\n        if value_range[1] is None:\n            assert value_range[0] <= value, (\n                \"Parameter '%s' is outside of the expected value \"\n                \"range (%.4f <= x)\" % (name, value_range[0]))\n            return True\n\n        assert value_range[0] <= value <= value_range[1], (\n            \"Parameter '%s' is outside of the expected value \"\n            \"range (%.4f <= x <= %.4f)\" % (\n                name, value_range[0], value_range[1]))\n\n        return True\n\n    if ia.is_callable(value_range):\n        value_range(value)\n        return True\n\n    raise Exception(\"Unexpected input for value_range, got %s.\" % (\n        str(value_range),))\n\n\n# FIXME this uses _check_value_range, which checks for a<=x<=b, but a produced\n#       Uniform parameter has value range a<=x<b.\ndef handle_continuous_param(param, name, value_range=None,\n                            tuple_to_uniform=True, list_to_choice=True,\n                            prefetch=True):\n    result = None\n\n    if ia.is_single_number(param):\n        _check_value_range(param, name, value_range)\n        result = Deterministic(param)\n    elif tuple_to_uniform and isinstance(param, tuple):\n        assert len(param) == 2, (\n            \"Expected parameter '%s' with type tuple to have exactly two \"\n            \"entries, but got %d.\" % (name, len(param)))\n        assert all([ia.is_single_number(v) for v in param]), (\n            \"Expected parameter '%s' with type tuple to only contain \"\n            \"numbers, got %s.\" % (name, [type(v) for v in param],))\n        _check_value_range(param[0], name, value_range)\n        _check_value_range(param[1], name, value_range)\n        result = Uniform(param[0], param[1])\n    elif (list_to_choice and ia.is_iterable(param)\n          and not isinstance(param, tuple)):\n        assert all([ia.is_single_number(v) for v in param]), (\n            \"Expected iterable parameter '%s' to only contain numbers, \"\n            \"got %s.\" % (name, [type(v) for v in param],))\n        for param_i in param:\n            _check_value_range(param_i, name, value_range)\n        result = Choice(param)\n    elif isinstance(param, StochasticParameter):\n        result = param\n\n    if result is not None:\n        if prefetch:\n            return _wrap_leafs_of_param_in_prefetchers(result, _NB_PREFETCH)\n        return result\n\n    allowed_type = \"number\"\n    list_str = \", list of %s\" % (allowed_type,) if list_to_choice else \"\"\n    raise Exception(\n        \"Expected %s, tuple of two %s%s or StochasticParameter for %s, \"\n        \"got %s.\" % (\n            allowed_type, allowed_type, list_str, name, type(param),))\n\n\ndef handle_discrete_param(param, name, value_range=None, tuple_to_uniform=True,\n                          list_to_choice=True, allow_floats=True,\n                          prefetch=True):\n    result = None\n\n    if (ia.is_single_integer(param)\n            or (allow_floats and ia.is_single_float(param))):\n        _check_value_range(param, name, value_range)\n        result = Deterministic(int(param))\n    elif tuple_to_uniform and isinstance(param, tuple):\n        assert len(param) == 2, (\n            \"Expected parameter '%s' with type tuple to have exactly two \"\n            \"entries, but got %d.\" % (name, len(param)))\n        is_valid_types = all([\n            ia.is_single_number(v)\n            if allow_floats else ia.is_single_integer(v)\n            for v in param])\n        assert is_valid_types, (\n            \"Expected parameter '%s' of type tuple to only contain %s, \"\n            \"got %s.\" % (\n                name,\n                \"number\" if allow_floats else \"integer\",\n                [type(v) for v in param],))\n\n        _check_value_range(param[0], name, value_range)\n        _check_value_range(param[1], name, value_range)\n        result = DiscreteUniform(int(param[0]), int(param[1]))\n    elif (list_to_choice and ia.is_iterable(param)\n          and not isinstance(param, tuple)):\n        is_valid_types = all([\n            ia.is_single_number(v)\n            if allow_floats else ia.is_single_integer(v)\n            for v in param])\n        assert is_valid_types, (\n            \"Expected iterable parameter '%s' to only contain %s, \"\n            \"got %s.\" % (\n                name,\n                \"number\" if allow_floats else \"integer\",\n                [type(v) for v in param],))\n\n        for param_i in param:\n            _check_value_range(param_i, name, value_range)\n        result = Choice([int(param_i) for param_i in param])\n    elif isinstance(param, StochasticParameter):\n        result = param\n\n    if result is not None:\n        if prefetch:\n            return _wrap_leafs_of_param_in_prefetchers(result, _NB_PREFETCH)\n        return result\n\n    allowed_type = \"number\" if allow_floats else \"int\"\n    list_str = \", list of %s\" % (allowed_type,) if list_to_choice else \"\"\n    raise Exception(\n        \"Expected %s, tuple of two %s%s or StochasticParameter for %s, \"\n        \"got %s.\" % (\n            allowed_type, allowed_type, list_str, name, type(param),))\n\n\n# Added in 0.4.0.\ndef handle_categorical_string_param(param, name, valid_values=None,\n                                    prefetch=True):\n    result = None\n\n    if param == ia.ALL and valid_values is not None:\n        result = Choice(list(valid_values))\n    elif ia.is_string(param):\n        if valid_values is not None:\n            assert param in valid_values, (\n                \"Expected parameter '%s' to be one of: %s. Got: %s.\" % (\n                    name, \", \".join(list(valid_values)), param))\n        result = Deterministic(param)\n    elif isinstance(param, list):\n        assert all([ia.is_string(val) for val in param]), (\n            \"Expected list provided for parameter '%s' to only contain \"\n            \"strings, got types: %s.\" % (\n                name, \", \".join([type(v).__name__ for v in param])))\n        if valid_values is not None:\n            assert all([val in valid_values for val in param]), (\n                \"Expected list provided for parameter '%s' to only contain \"\n                \"the following allowed strings: %s. Got strings: %s.\" % (\n                    name, \", \".join(valid_values), \", \".join(param)\n                ))\n        result = Choice(param)\n    elif isinstance(param, StochasticParameter):\n        result = param\n\n    # we currently prefetch only 1k values here instead of 10k, because\n    # strings might be rather long\n    if result is not None:\n        if prefetch:\n            return _wrap_leafs_of_param_in_prefetchers(result, _NB_PREFETCH_STRINGS)\n        return result\n\n    raise Exception(\n        \"Expected parameter '%s' to be%s a string, a list of \"\n        \"strings or StochasticParameter, got %s.\" % (\n            name,\n            \" imgaug.ALL,\" if valid_values is not None else \"\",\n            type(param).__name__,))\n\n\ndef handle_discrete_kernel_size_param(param, name, value_range=(1, None),\n                                      allow_floats=True, prefetch=True):\n    # pylint: disable=invalid-name\n\n    result = None, None\n    if (ia.is_single_integer(param)\n            or (allow_floats and ia.is_single_float(param))):\n        _check_value_range(param, name, value_range)\n        result = Deterministic(int(param)), None\n    elif isinstance(param, tuple):\n        assert len(param) == 2, (\n            \"Expected parameter '%s' with type tuple to have exactly two \"\n            \"entries, but got %d.\" % (name, len(param)))\n        if (all([ia.is_single_integer(param_i) for param_i in param])\n                or (allow_floats and all([ia.is_single_float(param_i)\n                                          for param_i in param]))):\n            _check_value_range(param[0], name, value_range)\n            _check_value_range(param[1], name, value_range)\n            result = DiscreteUniform(int(param[0]), int(param[1])), None\n        elif all([isinstance(param_i, StochasticParameter)\n                  for param_i in param]):\n            result = param[0], param[1]\n        else:\n            handled = (\n                handle_discrete_param(\n                    param[0], \"%s[0]\" % (name,), value_range,\n                    allow_floats=allow_floats),\n                handle_discrete_param(\n                    param[1], \"%s[1]\" % (name,), value_range,\n                    allow_floats=allow_floats)\n            )\n\n            result = handled\n    elif ia.is_iterable(param) and not isinstance(param, tuple):\n        is_valid_types = all([\n            ia.is_single_number(v)\n            if allow_floats else ia.is_single_integer(v)\n            for v in param])\n        assert is_valid_types, (\n            \"Expected iterable parameter '%s' to only contain %s, \"\n            \"got %s.\" % (\n                name,\n                \"number\" if allow_floats else \"integer\",\n                [type(v) for v in param],))\n\n        for param_i in param:\n            _check_value_range(param_i, name, value_range)\n        result = Choice([int(param_i) for param_i in param]), None\n    elif isinstance(param, StochasticParameter):\n        result = param, None\n\n    result_pf = []\n    for v in result:\n        if v is not None and prefetch:\n            v = _wrap_leafs_of_param_in_prefetchers(v, _NB_PREFETCH)\n        result_pf.append(v)\n\n    if result_pf != [None, None]:\n        return tuple(result_pf)\n\n    raise Exception(\n        \"Expected int, tuple/list with 2 entries or StochasticParameter. \"\n        \"Got %s.\" % (type(param),))\n\n\ndef handle_probability_param(param, name, tuple_to_uniform=False,\n                             list_to_choice=False, prefetch=True):\n    eps = 1e-6\n\n    result = None\n\n    if param in [True, False, 0, 1]:\n        result = Deterministic(int(param))\n    elif ia.is_single_number(param):\n        assert 0.0 <= param <= 1.0, (\n            \"Expected probability of parameter '%s' to be in the interval \"\n            \"[0.0, 1.0], got %.4f.\" % (name, param,))\n        if 0.0-eps < param < 0.0+eps or 1.0-eps < param < 1.0+eps:\n            return Deterministic(int(np.round(param)))\n        result = Binomial(param)\n    elif tuple_to_uniform and isinstance(param, tuple):\n        assert all([ia.is_single_number(v) for v in param]), (\n            \"Expected parameter '%s' of type tuple to only contain numbers, \"\n            \"got %s.\" % (name, [type(v) for v in param],))\n        assert len(param) == 2, (\n            \"Expected parameter '%s' of type tuple to contain exactly 2 \"\n            \"entries, got %d.\" % (name, len(param)))\n        assert 0 <= param[0] <= 1.0 and 0 <= param[1] <= 1.0, (\n            \"Expected parameter '%s' of type tuple to contain two \"\n            \"probabilities in the interval [0.0, 1.0]. \"\n            \"Got values %.4f and %.4f.\" % (name, param[0], param[1]))\n        result = Binomial(Uniform(param[0], param[1]))\n    elif list_to_choice and ia.is_iterable(param):\n        assert all([ia.is_single_number(v) for v in param]), (\n            \"Expected iterable parameter '%s' to only contain numbers, \"\n            \"got %s.\" % (name, [type(v) for v in param],))\n        assert all([0 <= p_i <= 1.0 for p_i in param]), (\n            \"Expected iterable parameter '%s' to only contain probabilities \"\n            \"in the interval [0.0, 1.0], got values %s.\" % (\n                name, \", \".join([\"%.4f\" % (p_i,) for p_i in param])))\n        result = Binomial(Choice(param))\n    elif isinstance(param, StochasticParameter):\n        result = param\n\n    if result is not None:\n        if prefetch:\n            return _wrap_leafs_of_param_in_prefetchers(result, _NB_PREFETCH)\n        return result\n\n    raise Exception(\n        \"Expected boolean or number or StochasticParameter for %s, \"\n        \"got %s.\" % (name, type(param),))\n\n\ndef force_np_float_dtype(val):\n    if val.dtype.kind == \"f\":\n        return val\n    return val.astype(np.float64)\n\n\ndef both_np_float_if_one_is_float(a, b):\n    # pylint: disable=invalid-name\n    a_f = a.dtype.type in ia.NP_FLOAT_TYPES\n    b_f = b.dtype.type in ia.NP_FLOAT_TYPES\n    if a_f and b_f:\n        return a, b\n    if a_f:\n        return a, b.astype(np.float64)\n    if b_f:\n        return a.astype(np.float64), b\n    return a.astype(np.float64), b.astype(np.float64)\n\n\ndef draw_distributions_grid(params, rows=None, cols=None,\n                            graph_sizes=(350, 350), sample_sizes=None,\n                            titles=None):\n    if titles is None:\n        titles = [None] * len(params)\n    elif titles is False:\n        titles = [False] * len(params)\n\n    if sample_sizes is not None:\n        images = [\n            param_i.draw_distribution_graph(size=size_i, title=title_i)\n            for param_i, size_i, title_i in zip(params, sample_sizes, titles)]\n    else:\n        images = [\n            param_i.draw_distribution_graph(title=title_i)\n            for param_i, title_i in zip(params, titles)]\n\n    images_rs = ia.imresize_many_images(images, sizes=graph_sizes)\n    grid = ia.draw_grid(images_rs, rows=rows, cols=cols)\n    return grid\n\n\ndef show_distributions_grid(params, rows=None, cols=None,\n                            graph_sizes=(350, 350), sample_sizes=None,\n                            titles=None):\n    ia.imshow(\n        draw_distributions_grid(\n            params,\n            graph_sizes=graph_sizes,\n            sample_sizes=sample_sizes,\n            rows=rows,\n            cols=cols,\n            titles=titles\n        )\n    )\n\n\n@six.add_metaclass(ABCMeta)\nclass StochasticParameter(object):\n    \"\"\"Abstract parent class for all stochastic parameters.\n\n    Stochastic parameters are here all parameters from which values are\n    supposed to be sampled. Usually the sampled values are to a degree random.\n    E.g. a stochastic parameter may be the uniform distribution over the\n    interval ``[-10, 10]``. Samples from that distribution (and therefore the\n    stochastic parameter) could be ``5.2``, ``-3.7``, ``-9.7``, ``6.4``, etc.\n\n    \"\"\"\n\n    def __init__(self):\n        pass\n\n    @property\n    def prefetchable(self):\n        \"\"\"Determines whether this parameter may be prefetched.\n\n        Added in 0.5.0.\n\n        Returns\n        -------\n        bool\n            Whether to allow prefetching of this parameter's samples.\n            This should usually only be ``True`` for parameters that actually\n            perform random sampling, i.e. depend on an RNG.\n\n        \"\"\"\n        return False\n\n    def draw_sample(self, random_state=None):\n        \"\"\"\n        Draws a single sample value from this parameter.\n\n        Parameters\n        ----------\n        random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n            A seed or random number generator to use during the sampling\n            process. If ``None``, the global RNG will be used.\n            See also :func:`~imgaug.augmenters.meta.Augmenter.__init__`\n            for a similar parameter with more details.\n\n        Returns\n        -------\n        any\n            A single sample value.\n\n        \"\"\"\n        return self.draw_samples(1, random_state=random_state)[0]\n\n    def draw_samples(self, size, random_state=None):\n        \"\"\"Draw one or more samples from the parameter.\n\n        Parameters\n        ----------\n        size : tuple of int or int\n            Number of samples by dimension.\n\n        random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional\n            A seed or random number generator to use during the sampling\n            process. If ``None``, the global RNG will be used.\n            See also :func:`~imgaug.augmenters.meta.Augmenter.__init__`\n            for a similar parameter with more details.\n\n        Returns\n        -------\n        ndarray\n            Sampled values. Usually a numpy ndarray of basically any dtype,\n            though not strictly limited to numpy arrays. Its shape is expected\n            to match `size`.\n\n        \"\"\"\n        if not isinstance(random_state, iarandom.RNG):\n            random_state = iarandom.RNG(random_state)\n        samples = self._draw_samples(\n            size if not ia.is_single_integer(size) else tuple([size]),\n            random_state)\n        random_state.advance_()\n        return samples\n\n    @abstractmethod\n    def _draw_samples(self, size, random_state):\n        raise NotImplementedError()\n\n    def __add__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Add(self, other)\n        raise Exception(\n            \"Invalid datatypes in: StochasticParameter + %s. \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __sub__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Subtract(self, other)\n        raise Exception(\n            \"Invalid datatypes in: StochasticParameter - %s. \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __mul__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Multiply(self, other)\n        raise Exception(\n            \"Invalid datatypes in: StochasticParameter * %s. \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __pow__(self, other, z=None):\n        if z is not None:\n            raise NotImplementedError(\n                \"Modulo power is currently not supported by \"\n                \"StochasticParameter.\")\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Power(self, other)\n        raise Exception(\n            \"Invalid datatypes in: StochasticParameter ** %s. \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __div__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Divide(self, other)\n        raise Exception(\n            \"Invalid datatypes in: StochasticParameter / %s. \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __truediv__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Divide(self, other)\n        raise Exception(\n            \"Invalid datatypes in: StochasticParameter / %s (truediv). \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __floordiv__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Discretize(Divide(self, other))\n        raise Exception(\n            \"Invalid datatypes in: StochasticParameter // %s (floordiv). \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __radd__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Add(other, self)\n        raise Exception(\n            \"Invalid datatypes in: %s + StochasticParameter. \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __rsub__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Subtract(other, self)\n        raise Exception(\n            \"Invalid datatypes in: %s - StochasticParameter. \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __rmul__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Multiply(other, self)\n        raise Exception(\n            \"Invalid datatypes in: %s * StochasticParameter. \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __rpow__(self, other, z=None):\n        if z is not None:\n            raise NotImplementedError(\n                \"Modulo power is currently not supported by \"\n                \"StochasticParameter.\")\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Power(other, self)\n        raise Exception(\n            \"Invalid datatypes in: %s ** StochasticParameter. \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __rdiv__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Divide(other, self)\n        raise Exception(\n            \"Invalid datatypes in: %s / StochasticParameter. \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __rtruediv__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Divide(other, self)\n        raise Exception(\n            \"Invalid datatypes in: %s / StochasticParameter (rtruediv). \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def __rfloordiv__(self, other):\n        if ia.is_single_number(other) or isinstance(other, StochasticParameter):\n            return Discretize(Divide(other, self))\n        raise Exception(\n            \"Invalid datatypes in: StochasticParameter // %s (rfloordiv). \"\n            \"Expected second argument to be number or \"\n            \"StochasticParameter.\" % (type(other),))\n\n    def copy(self):\n        \"\"\"Create a shallow copy of this parameter.\n\n        Returns\n        -------\n        imgaug.parameters.StochasticParameter\n            Shallow copy.\n\n        \"\"\"\n        return copy_module.copy(self)\n\n    def deepcopy(self):\n        \"\"\"Create a deep copy of this parameter.\n\n        Returns\n        -------\n        imgaug.parameters.StochasticParameter\n            Deep copy.\n\n        \"\"\"\n        return copy_module.deepcopy(self)\n\n    def draw_distribution_graph(self, title=None, size=(1000, 1000), bins=100):\n        \"\"\"Generate an image visualizing the parameter's sample distribution.\n\n        Parameters\n        ----------\n        title : None or False or str, optional\n            Title of the plot. ``None`` is automatically replaced by a title\n            derived from ``str(param)``. If set to ``False``, no title will be\n            shown.\n\n        size : tuple of int\n            Number of points to sample. This is always expected to have at\n            least two values. The first defines the number of sampling runs,\n            the second (and further) dimensions define the size assigned\n            to each :func:`~imgaug.parameters.StochasticParameter.draw_samples`\n            call. E.g. ``(10, 20, 15)`` will lead to ``10`` calls of\n            ``draw_samples(size=(20, 15))``. The results will be merged to a\n            single 1d array.\n\n        bins : int\n            Number of bins in the plot histograms.\n\n        Returns\n        -------\n        data : (H,W,3) ndarray\n            Image of the plot.\n\n        \"\"\"\n        # import only when necessary (faster startup; optional dependency;\n        # less fragile -- see issue #225)\n        import matplotlib.pyplot as plt\n\n        points = []\n        for _ in sm.xrange(size[0]):\n            points.append(self.draw_samples(size[1:]).flatten())\n        points = np.concatenate(points)\n\n        fig = plt.figure()\n        fig.add_subplot(111)\n        ax = fig.gca()\n        heights, bins = np.histogram(points, bins=bins)\n        heights = heights / sum(heights)\n        ax.bar(bins[:-1], heights,\n               width=(max(bins) - min(bins))/len(bins),\n               color=\"blue\",\n               alpha=0.75)\n\n        if title is None:\n            title = str(self)\n        if title is not False:\n            # split long titles - otherwise matplotlib generates errors\n            title_fragments = [title[i:i+50]\n                               for i in sm.xrange(0, len(title), 50)]\n            ax.set_title(\"\\n\".join(title_fragments))\n        fig.tight_layout(pad=0)\n\n        with tempfile.NamedTemporaryFile(mode=\"wb+\", suffix=\".png\") as f:\n            # We don't add bbox_inches='tight' here so that\n            # draw_distributions_grid has an easier time combining many plots.\n            # Note that we could also use 'f.name' here instead of 'f', but\n            # that fails on Windows.\n            fig.savefig(f, format=\"png\")\n\n            # Use f.seek() here, because otherwise we get an error that\n            # the file was not a png image.\n            f.seek(0)\n            data = imageio.imread(\n                f, pilmode=\"RGB\", format=\"png\"\n            )[..., 0:3]\n\n        plt.close()\n\n        return data\n\n\nclass AutoPrefetcher(StochasticParameter):\n    \"\"\"Parameter that prefetches random samples from a child parameter.\n\n    This parameter will fetch ``N`` random samples in one big swoop and then\n    return ``M`` of these samples upon each call, with ``M << N``.\n    This improves the sampling efficiency by performing as few sampling\n    calls as possible.\n\n    This parameter will only start to prefetch after the first call.\n    In some cases this prevents inefficiencies when augmenters are only used\n    once. (Though this only works if the respective augmenter performs\n    a single sampling call per batch and not one call per image.)\n\n    This parameter will throw away its prefetched samples if a new RNG\n    is provided (compared to the previous call). It will however ignore the\n    state of the RNG.\n\n    This parameter should only wrap leaf nodes. In something like\n    ``Add(1, Normal(Uniform(0, 1), Uniform(0, 2)))`` it should only be applied\n    to the two ``Uniform`` instaces. Otherwise, only a single sample of\n    ``Uniform(0, 1)`` might be taken and influence thousands of samples of\n    ``Normal``.\n\n    Note that the samples returned by this parameter are part of a larger\n    array. In-place changes to these samples should hence be performed with\n    some caution.\n\n    Added in 0.5.0.\n\n    \"\"\"\n\n    # Added in 0.5.0.\n    def __init__(self, other_param, nb_prefetch):\n        super(AutoPrefetcher, self).__init__()\n        self.other_param = other_param\n        self.nb_prefetch = nb_prefetch\n\n        self.samples = None\n        self.index = 0\n        self.last_rng_idx = None\n\n    # Added in 0.5.0.\n    def _draw_samples(self, size, random_state):\n        # pylint: disable=protected-access\n        if not _PREFETCHING_ENABLED:\n            return self.other_param.draw_samples(size, random_state)\n\n        if self.last_rng_idx is None or random_state._idx != self.last_rng_idx:\n            self.last_rng_idx = random_state._idx\n            self.samples = None\n            return self.other_param.draw_samples(size, random_state)\n\n        self.last_rng_idx = random_state._idx\n\n        nb_components = reduce(mul_op, size)\n\n        if nb_components >= self.nb_prefetch:\n            return self.other_param.draw_samples(size, random_state)\n\n        if self.samples is None:\n            self._prefetch(random_state)\n\n        leftover = len(self.samples) - self.index - nb_components\n        if leftover <= 0:\n            self._prefetch(random_state)\n\n        samples = self.samples[self.index:self.index+nb_components]\n        self.index += nb_components\n\n        return samples.reshape(size)\n\n    # Added in 0.5.0.\n    def _prefetch(self, random_state):\n        samples = self.other_param.draw_samples((self.nb_prefetch,),\n                                                random_state)\n        if self.samples is None:\n            self.samples = samples\n        else:\n            self.samples = np.concatenate([\n                self.samples[self.index:], samples\n            ], axis=0)\n        self.index = 0\n\n    # Added in 0.5.0.\n    def __getattr__(self, attr):\n        other_param = super(\n            AutoPrefetcher, self\n        ).__getattribute__(\"other_param\")\n        return getattr(other_param, attr)\n\n    # Added in 0.5.0.\n    def __repr__(self):\n        return self.__str__()\n\n    # Added in 0.5.0.\n    def __str__(self):\n        has_samples = (self.samples is not None)\n        return (\n            \"AutoPrefetcher(\"\n            \"nb_prefetch=%d, \"\n            \"samples=%s (dtype %s), \"\n            \"index=%d, \"\n            \"last_rng_idx=%s, \"\n            \"other_param=%s\"\n            \")\" % (\n                self.nb_prefetch,\n                self.samples.shape if has_samples else \"None\",\n                self.samples.dtype.name if has_samples else \"None\",\n                self.index,\n                self.last_rng_idx,\n                str(self.other_param)\n            )\n        )\n\n\nclass Deterministic(StochasticParameter):\n    \"\"\"Parameter that is a constant value.\n\n    If ``N`` values are sampled from this parameter, it will return ``N`` times\n    ``V``, where ``V`` is the constant value.\n\n    Parameters\n    ----------\n    value : number or str or imgaug.parameters.StochasticParameter\n        A constant value to use.\n        A string may be provided to generate arrays of strings.\n        If this is a StochasticParameter, a single value will be sampled\n        from it exactly once and then used as the constant value.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Deterministic(10)\n    >>> param.draw_sample()\n    10\n\n    Will always sample the value 10.\n\n    \"\"\"\n    def __init__(self, value):\n        super(Deterministic, self).__init__()\n\n        if isinstance(value, StochasticParameter):\n            self.value = value.draw_sample()\n        elif ia.is_single_number(value) or ia.is_string(value):\n            self.value = value\n        else:\n            raise Exception(\"Expected StochasticParameter object or number or \"\n                            \"string, got %s.\" % (type(value),))\n\n    def _draw_samples(self, size, random_state):\n        kwargs = {}\n        if ia.is_single_integer(self.value):\n            kwargs = {\"dtype\": np.int32}\n        elif ia.is_single_float(self.value):\n            kwargs = {\"dtype\": np.float32}\n        return np.full(size, self.value, **kwargs)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        if ia.is_single_integer(self.value):\n            return \"Deterministic(int %d)\" % (self.value,)\n        if ia.is_single_float(self.value):\n            return \"Deterministic(float %.8f)\" % (self.value,)\n        return \"Deterministic(%s)\" % (str(self.value),)\n\n\n# TODO replace two-value parameters used in tests with this\nclass DeterministicList(StochasticParameter):\n    \"\"\"Parameter that repeats elements from a list in the given order.\n\n    E.g. of samples of shape ``(A, B, C)`` are requested, this parameter will\n    return the first ``A*B*C`` elements, reshaped to ``(A, B, C)`` from the\n    provided list. If the list contains less than ``A*B*C`` elements, it\n    will (by default) be tiled until it is long enough (i.e. the sampling\n    will start again at the first element, if necessary multiple times).\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    values : ndarray or iterable of number\n        An iterable of values to sample from in the order within the iterable.\n\n    \"\"\"\n\n    # Added in 0.4.0.\n    def __init__(self, values):\n        super(DeterministicList, self).__init__()\n\n        assert ia.is_iterable(values), (\n            \"Expected to get an iterable as input, got type %s.\" % (\n                type(values).__name__,))\n        assert len(values) > 0, (\"Expected to get at least one value, got \"\n                                 \"zero.\")\n\n        if ia.is_np_array(values):\n            # this would not be able to handle e.g. [[1, 2], [3]] and output\n            # dtype object due to the non-regular shape, hence we have the\n            # else block\n            self.values = values.flatten()\n        else:\n            self.values = np.array(list(ia.flatten(values)))\n            kind = self.values.dtype.kind\n\n            # limit to 32bit instead of 64bit for efficiency\n            if kind == \"i\":\n                self.values = self.values.astype(np.int32)\n            elif kind == \"f\":\n                self.values = self.values.astype(np.float32)\n\n    # Added in 0.4.0.\n    def _draw_samples(self, size, random_state):\n        nb_requested = int(np.prod(size))\n        values = self.values\n        if nb_requested > self.values.size:\n            # we don't use itertools.cycle() here, as that would require\n            # running through a loop potentially many times (as `size` can\n            # be very large), which would be slow\n            multiplier = int(np.ceil(nb_requested / values.size))\n            values = np.tile(values, (multiplier,))\n        return values[:nb_requested].reshape(size)\n\n    # Added in 0.4.0.\n    def __repr__(self):\n        return self.__str__()\n\n    # Added in 0.4.0.\n    def __str__(self):\n        if self.values.dtype.kind == \"f\":\n            values = [\"%.4f\" % (value,) for value in self.values]\n            return \"DeterministicList([%s])\" % (\", \".join(values),)\n        return \"DeterministicList(%s)\" % (str(self.values.tolist()),)\n\n\nclass Choice(StochasticParameter):\n    \"\"\"Parameter that samples value from a list of allowed values.\n\n    Parameters\n    ----------\n    a : iterable\n        List of allowed values.\n        Usually expected to be ``int`` s, ``float`` s or ``str`` s.\n        May also contain ``StochasticParameter`` s. Each\n        ``StochasticParameter`` that is randomly picked will automatically be\n        replaced by a sample of itself (or by ``N`` samples if the parameter\n        was picked ``N`` times).\n\n    replace : bool, optional\n        Whether to perform sampling with or without replacing.\n\n    p : None or iterable of number, optional\n        Probabilities of each element in `a`.\n        Must have the same length as `a` (if provided).\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Choice([5, 17, 25], p=[0.25, 0.5, 0.25])\n    >>> sample = param.draw_sample()\n    >>> assert sample in [5, 17, 25]\n\n    Create and sample from a parameter, which will produce with ``50%``\n    probability the sample ``17`` and in the other ``50%`` of all cases the\n    sample ``5`` or ``25``..\n\n    \"\"\"\n    def __init__(self, a, replace=True, p=None):\n        # pylint: disable=invalid-name\n        super(Choice, self).__init__()\n\n        assert ia.is_iterable(a), (\n            \"Expected a to be an iterable (e.g. list), got %s.\" % (type(a),))\n        self.a = a\n        self.replace = replace\n        if p is not None:\n            assert ia.is_iterable(p), (\n                \"Expected p to be None or an iterable, got %s.\" % (type(p),))\n            assert len(p) == len(a), (\n                \"Expected lengths of a and p to be identical, \"\n                \"got %d and %d.\" % (len(a), len(p)))\n        self.p = p\n\n    # Added in 0.5.0.\n    @property\n    def prefetchable(self):\n        \"\"\"See :func:`StochasticParameter.prefetchable`.\"\"\"\n        return self.replace\n\n    def _draw_samples(self, size, random_state):\n        if any([isinstance(a_i, StochasticParameter) for a_i in self.a]):\n            rngs = random_state.duplicate(1+len(self.a))\n            samples = rngs[0].choice(\n                self.a, np.prod(size), replace=self.replace, p=self.p)\n\n            # collect the sampled parameters and how many samples must be taken\n            # from each of them\n            params_counter = defaultdict(lambda: 0)\n            for sample in samples:\n                if isinstance(sample, StochasticParameter):\n                    key = str(sample)\n                    params_counter[key] += 1\n\n            # collect per parameter once the required number of samples\n            # iterate here over self.a to always use the same seed for\n            # the same parameter\n            # TODO this might fail if the same parameter is added multiple\n            #      times to self.a?\n            # TODO this will fail if a parameter cant handle size=(N,)\n            param_to_samples = dict()\n            for i, param in enumerate(self.a):\n                key = str(param)\n                if key in params_counter:\n                    param_to_samples[key] = param.draw_samples(\n                        size=(params_counter[key],),\n                        random_state=rngs[1+i]\n                    )\n\n            # assign the values sampled from the parameters to the `samples`\n            # array by replacing the respective parameter\n            param_to_readcount = defaultdict(lambda: 0)\n            for i, sample in enumerate(samples):\n                if isinstance(sample, StochasticParameter):\n                    key = str(sample)\n                    readcount = param_to_readcount[key]\n                    samples[i] = param_to_samples[key][readcount]\n                    param_to_readcount[key] += 1\n\n            samples = samples.reshape(size)\n        else:\n            samples = random_state.choice(self.a, size, replace=self.replace,\n                                          p=self.p)\n\n        dtype = samples.dtype\n        if dtype.itemsize*8 > 32:\n            # strings have kind \"U\"\n            kind = dtype.kind\n            if kind == \"i\":\n                samples = samples.astype(np.int32)\n            elif kind == \"u\":\n                samples = samples.astype(np.uint32)\n            elif kind == \"f\":\n                samples = samples.astype(np.float32)\n\n        return samples\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Choice(a=%s, replace=%s, p=%s)\" % (\n            str(self.a), str(self.replace), str(self.p),)\n\n\nclass Binomial(StochasticParameter):\n    \"\"\"Binomial distribution.\n\n    Parameters\n    ----------\n    p : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Probability of the binomial distribution. Expected to be in the\n        interval ``[0.0, 1.0]``.\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be\n              sampled from the continuous interval ``[a, b)`` once per call.\n            * If a ``list`` of ``number``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`Binomial.draw_sample` or\n        :func:`Binomial.draw_samples`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Binomial(Uniform(0.01, 0.2))\n\n    Create a binomial distribution that uses a varying probability between\n    ``0.01`` and ``0.2``, randomly and uniformly estimated once per sampling\n    call.\n\n    \"\"\"\n\n    def __init__(self, p):\n        super(Binomial, self).__init__()\n        self.p = handle_continuous_param(p, \"p\")\n\n    # Added in 0.5.0.\n    @property\n    def prefetchable(self):\n        \"\"\"See :func:`StochasticParameter.prefetchable`.\"\"\"\n        return True\n\n    def _draw_samples(self, size, random_state):\n        p = self.p.draw_sample(random_state=random_state)\n        assert 0 <= p <= 1.0, (\n            \"Expected probability p to be in the interval [0.0, 1.0], \"\n            \"got %.4f.\" % (p,))\n        return random_state.binomial(1, p, size).astype(np.int32)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Binomial(%s)\" % (self.p,)\n\n\nclass DiscreteUniform(StochasticParameter):\n    \"\"\"Uniform distribution over the discrete interval ``[a..b]``.\n\n    Parameters\n    ----------\n    a : int or tuple of int or list of int or imgaug.parameters.StochasticParameter\n        Lower bound of the interval.\n        If ``a>b``, `a` and `b` will automatically be flipped.\n        If ``a==b``, all generated values will be identical to `a`.\n\n            * If a single ``int``, this ``int`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be\n              sampled from the discrete interval ``[a..b]`` once per call.\n            * If a ``list`` of ``int``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`DiscreteUniform.draw_sample` or\n        :func:`DiscreteUniform.draw_samples`.\n\n    b : int or imgaug.parameters.StochasticParameter\n        Upper bound of the interval. Analogous to `a`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.DiscreteUniform(10, Choice([20, 30, 40]))\n    >>> sample = param.draw_sample()\n    >>> assert 10 <= sample <= 40\n\n    Create a discrete uniform distribution which's interval differs between\n    calls and can be ``[10..20]``, ``[10..30]`` or ``[10..40]``.\n\n    \"\"\"\n\n    def __init__(self, a, b):\n        # pylint: disable=invalid-name\n        super(DiscreteUniform, self).__init__()\n\n        self.a = handle_discrete_param(a, \"a\")\n        self.b = handle_discrete_param(b, \"b\")\n\n    # Added in 0.5.0.\n    @property\n    def prefetchable(self):\n        \"\"\"See :func:`StochasticParameter.prefetchable`.\"\"\"\n        return True\n\n    def _draw_samples(self, size, random_state):\n        # pylint: disable=invalid-name\n        a = self.a.draw_sample(random_state=random_state)\n        b = self.b.draw_sample(random_state=random_state)\n        if a > b:\n            a, b = b, a\n        elif a == b:\n            return np.full(size, a, dtype=np.int32)\n        return random_state.integers(a, b + 1, size, dtype=np.int32)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"DiscreteUniform(%s, %s)\" % (self.a, self.b)\n\n\nclass Poisson(StochasticParameter):\n    \"\"\"Parameter that resembles a poisson distribution.\n\n    A poisson distribution with ``lambda=0`` has its highest probability at\n    point ``0`` and decreases quickly from there.\n    Poisson distributions are discrete and never negative.\n\n    Parameters\n    ----------\n    lam : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Lambda parameter of the poisson distribution.\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be\n              sampled from the continuous interval ``[a, b)`` once per call.\n            * If a ``list`` of ``number``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`Poisson.draw_sample` or\n        :func:`Poisson.draw_samples`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Poisson(1)\n    >>> sample = param.draw_sample()\n    >>> assert sample >= 0\n\n    Create a poisson distribution with ``lambda=1`` and sample a value from\n    it.\n\n    \"\"\"\n\n    def __init__(self, lam):\n        super(Poisson, self).__init__()\n\n        self.lam = handle_continuous_param(lam, \"lam\")\n\n    # Added in 0.5.0.\n    @property\n    def prefetchable(self):\n        \"\"\"See :func:`StochasticParameter.prefetchable`.\"\"\"\n        return True\n\n    def _draw_samples(self, size, random_state):\n        lam = self.lam.draw_sample(random_state=random_state)\n        lam = max(lam, 0)\n\n        return random_state.poisson(lam=lam, size=size).astype(np.int32)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Poisson(%s)\" % (self.lam,)\n\n\nclass Normal(StochasticParameter):\n    \"\"\"Parameter that resembles a normal/gaussian distribution.\n\n    Parameters\n    ----------\n    loc : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        The mean of the normal distribution.\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be\n              sampled from the continuous interval ``[a, b)`` once per call.\n            * If a ``list`` of ``number``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`Laplace.draw_sample` or\n        :func:`Laplace.draw_samples`.\n\n    scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        The standard deviation of the normal distribution.\n        If this parameter reaches ``0``, the output array will be filled with\n        `loc`.\n        Datatype behaviour is the analogous to `loc`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Normal(Choice([-1.0, 1.0]), 1.0)\n\n    Create a gaussian distribution with a mean that differs by call.\n    Samples values may sometimes follow ``N(-1.0, 1.0)`` and sometimes\n    ``N(1.0, 1.0)``.\n\n    \"\"\"\n    def __init__(self, loc, scale):\n        super(Normal, self).__init__()\n\n        self.loc = handle_continuous_param(loc, \"loc\")\n        self.scale = handle_continuous_param(scale, \"scale\",\n                                             value_range=(0, None))\n\n    # Added in 0.5.0.\n    @property\n    def prefetchable(self):\n        \"\"\"See :func:`StochasticParameter.prefetchable`.\"\"\"\n        return True\n\n    def _draw_samples(self, size, random_state):\n        loc = self.loc.draw_sample(random_state=random_state)\n        scale = self.scale.draw_sample(random_state=random_state)\n        assert scale >= 0, \"Expected scale to be >=0, got %.4f.\" % (scale,)\n        if scale == 0:\n            return np.full(size, loc, dtype=np.float32)\n        return random_state.normal(loc, scale, size=size).astype(np.float32)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Normal(loc=%s, scale=%s)\" % (self.loc, self.scale)\n\n\n# TODO docstring for parameters is outdated\nclass TruncatedNormal(StochasticParameter):\n    \"\"\"Parameter that resembles a truncated normal distribution.\n\n    A truncated normal distribution is similar to a normal distribution,\n    except the domain is smoothly bounded to a min and max value.\n\n    This is a wrapper around :func:`scipy.stats.truncnorm`.\n\n    Parameters\n    ----------\n    loc : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        The mean of the normal distribution.\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be\n              sampled from the continuous interval ``[a, b)`` once per call.\n            * If a ``list`` of ``number``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`TruncatedNormal.draw_sample` or\n        :func:`TruncatedNormal.draw_samples`.\n\n    scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        The standard deviation of the normal distribution.\n        If this parameter reaches ``0``, the output array will be filled with\n        `loc`.\n        Datatype behaviour is the same as for `loc`.\n\n    low : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        The minimum value of the truncated normal distribution.\n        Datatype behaviour is the same as for `loc`.\n\n    high : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        The maximum value of the truncated normal distribution.\n        Datatype behaviour is the same as for `loc`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.TruncatedNormal(0, 5.0, low=-10, high=10)\n    >>> samples = param.draw_samples(100, random_state=0)\n    >>> assert np.all(samples >= -10)\n    >>> assert np.all(samples <= 10)\n\n    Create a truncated normal distribution with its minimum at ``-10.0``\n    and its maximum at ``10.0``.\n\n    \"\"\"\n\n    def __init__(self, loc, scale, low=-np.inf, high=np.inf):\n        super(TruncatedNormal, self).__init__()\n\n        self.loc = handle_continuous_param(loc, \"loc\")\n        self.scale = handle_continuous_param(scale, \"scale\",\n                                             value_range=(0, None))\n        self.low = handle_continuous_param(low, \"low\")\n        self.high = handle_continuous_param(high, \"high\")\n\n    # Added in 0.5.0.\n    @property\n    def prefetchable(self):\n        \"\"\"See :func:`StochasticParameter.prefetchable`.\"\"\"\n        return True\n\n    def _draw_samples(self, size, random_state):\n        # pylint: disable=invalid-name\n        loc = self.loc.draw_sample(random_state=random_state)\n        scale = self.scale.draw_sample(random_state=random_state)\n        low = self.low.draw_sample(random_state=random_state)\n        high = self.high.draw_sample(random_state=random_state)\n        seed = random_state.generate_seed_()\n        if low > high:\n            low, high = high, low\n        assert scale >= 0, \"Expected scale to be >=0, got %.4f.\" % (scale,)\n        if scale == 0:\n            return np.full(size, fill_value=loc, dtype=np.float32)\n        a = (low - loc) / scale\n        b = (high - loc) / scale\n        tnorm = scipy.stats.truncnorm(a=a, b=b, loc=loc, scale=scale)\n\n        # Using a seed here works with both np.random interfaces.\n        # Last time tried, scipy crashed when providing just\n        # random_state.generator on the new np.random interface.\n        return tnorm.rvs(size=size, random_state=seed).astype(np.float32)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"TruncatedNormal(loc=%s, scale=%s, low=%s, high=%s)\" % (\n            self.loc, self.scale, self.low, self.high)\n\n\nclass Laplace(StochasticParameter):\n    \"\"\"Parameter that resembles a (continuous) laplace distribution.\n\n    This is a wrapper around numpy's :func:`numpy.random.laplace`.\n\n    Parameters\n    ----------\n    loc : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        The position of the distribution peak, similar to the mean in normal\n        distributions.\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be\n              sampled from the continuous interval ``[a, b)`` once per call.\n            * If a ``list`` of ``number``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`Laplace.draw_sample` or\n        :func:`Laplace.draw_samples`.\n\n    scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        The exponential decay factor, similar to the standard deviation in\n        gaussian distributions.\n        If this parameter reaches ``0``, the output array will be filled with\n        `loc`.\n        Datatype behaviour is the analogous to `loc`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Laplace(0, 1.0)\n\n    Create a laplace distribution, which's peak is at ``0`` and decay is\n    ``1.0``.\n\n    \"\"\"\n    def __init__(self, loc, scale):\n        super(Laplace, self).__init__()\n\n        self.loc = handle_continuous_param(loc, \"loc\")\n        self.scale = handle_continuous_param(scale, \"scale\",\n                                             value_range=(0, None))\n\n    # Added in 0.5.0.\n    @property\n    def prefetchable(self):\n        \"\"\"See :func:`StochasticParameter.prefetchable`.\"\"\"\n        return True\n\n    def _draw_samples(self, size, random_state):\n        loc = self.loc.draw_sample(random_state=random_state)\n        scale = self.scale.draw_sample(random_state=random_state)\n        assert scale >= 0, \"Expected scale to be >=0, got %s.\" % (scale,)\n        if scale == 0:\n            return np.full(size, loc, dtype=np.float32)\n        return random_state.laplace(loc, scale, size=size).astype(np.float32)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Laplace(loc=%s, scale=%s)\" % (self.loc, self.scale)\n\n\nclass ChiSquare(StochasticParameter):\n    \"\"\"Parameter that resembles a (continuous) chi-square distribution.\n\n    This is a wrapper around numpy's :func:`numpy.random.chisquare`.\n\n    Parameters\n    ----------\n    df : int or tuple of two int or list of int or imgaug.parameters.StochasticParameter\n        Degrees of freedom. Expected value range is ``[1, inf)``.\n\n            * If a single ``int``, this ``int`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be\n              sampled from the discrete interval ``[a..b]`` once per call.\n            * If a ``list`` of ``int``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`ChiSquare.draw_sample` or\n        :func:`ChiSquare.draw_samples`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.ChiSquare(df=2)\n\n    Create a chi-square distribution with two degrees of freedom.\n\n    \"\"\"\n    def __init__(self, df):\n        # pylint: disable=invalid-name\n        super(ChiSquare, self).__init__()\n\n        self.df = handle_discrete_param(df, \"df\", value_range=(1, None))\n\n    # Added in 0.5.0.\n    @property\n    def prefetchable(self):\n        \"\"\"See :func:`StochasticParameter.prefetchable`.\"\"\"\n        return True\n\n    def _draw_samples(self, size, random_state):\n        # pylint: disable=invalid-name\n        df = self.df.draw_sample(random_state=random_state)\n        assert df >= 1, \"Expected df to be >=1, got %d.\" % (df,)\n        return random_state.chisquare(df, size=size).astype(np.float32)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"ChiSquare(df=%s)\" % (self.df,)\n\n\nclass Weibull(StochasticParameter):\n    \"\"\"\n    Parameter that resembles a (continuous) weibull distribution.\n\n    This is a wrapper around numpy's :func:`numpy.random.weibull`.\n\n    Parameters\n    ----------\n    a : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Shape parameter of the distribution.\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be\n              sampled from the continuous interval ``[a, b)`` once per call.\n            * If a ``list`` of ``number``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`Weibull.draw_sample` or\n        :func:`Weibull.draw_samples`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Weibull(a=0.5)\n\n    Create a weibull distribution with shape 0.5.\n\n    \"\"\"\n    def __init__(self, a):\n        # pylint: disable=invalid-name\n        super(Weibull, self).__init__()\n\n        self.a = handle_continuous_param(a, \"a\", value_range=(0.0001, None))\n\n    # Added in 0.5.0.\n    @property\n    def prefetchable(self):\n        \"\"\"See :func:`StochasticParameter.prefetchable`.\"\"\"\n        return True\n\n    def _draw_samples(self, size, random_state):\n        # pylint: disable=invalid-name\n        a = self.a.draw_sample(random_state=random_state)\n        assert a > 0, \"Expected a to be >0, got %.4f.\" % (a,)\n        return random_state.weibull(a, size=size).astype(np.float32)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Weibull(a=%s)\" % (self.a,)\n\n\n# TODO rename (a, b) to (low, high) as in numpy?\nclass Uniform(StochasticParameter):\n    \"\"\"Parameter that resembles a uniform distribution over ``[a, b)``.\n\n    Parameters\n    ----------\n    a : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Lower bound of the interval.\n        If ``a>b``, `a` and `b` will automatically be flipped.\n        If ``a==b``, all generated values will be identical to `a`.\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be\n              sampled from the continuous interval ``[a, b)`` once per call.\n            * If a ``list`` of ``number``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`Uniform.draw_sample` or\n        :func:`Uniform.draw_samples`.\n\n    b : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Upper bound of the interval. Analogous to `a`.\n\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Uniform(0, 10.0)\n    >>> sample = param.draw_sample()\n    >>> assert 0 <= sample < 10.0\n\n    Create and sample from a uniform distribution over ``[0, 10.0)``.\n\n    \"\"\"\n    def __init__(self, a, b):\n        # pylint: disable=invalid-name\n        super(Uniform, self).__init__()\n\n        self.a = handle_continuous_param(a, \"a\")\n        self.b = handle_continuous_param(b, \"b\")\n\n    # Added in 0.5.0.\n    @property\n    def prefetchable(self):\n        \"\"\"See :func:`StochasticParameter.prefetchable`.\"\"\"\n        return True\n\n    def _draw_samples(self, size, random_state):\n        # pylint: disable=invalid-name\n        a = self.a.draw_sample(random_state=random_state)\n        b = self.b.draw_sample(random_state=random_state)\n        if a > b:\n            a, b = b, a\n        elif a == b:\n            return np.full(size, a, dtype=np.float32)\n        return random_state.uniform(a, b, size).astype(np.float32)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Uniform(%s, %s)\" % (self.a, self.b)\n\n\nclass Beta(StochasticParameter):\n    \"\"\"Parameter that resembles a (continuous) beta distribution.\n\n    Parameters\n    ----------\n    alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        alpha parameter of the beta distribution.\n        Expected value range is ``(0, inf)``. Values below ``0`` are\n        automatically clipped to ``0+epsilon``.\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be\n              sampled from the continuous interval ``[a, b)`` once per call.\n            * If a ``list`` of ``number``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`Beta.draw_sample` or\n        :func:`Beta.draw_samples`.\n\n    beta : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Beta parameter of the beta distribution. Analogous to `alpha`.\n\n    epsilon : number\n        Clipping parameter. If `alpha` or `beta` end up ``<=0``, they are clipped to ``0+epsilon``.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Beta(0.4, 0.6)\n\n    Create a beta distribution with ``alpha=0.4`` and ``beta=0.6``.\n\n    \"\"\"\n    def __init__(self, alpha, beta, epsilon=0.0001):\n        super(Beta, self).__init__()\n\n        self.alpha = handle_continuous_param(alpha, \"alpha\")\n        self.beta = handle_continuous_param(beta, \"beta\")\n\n        assert ia.is_single_number(epsilon), (\n            \"Expected epsilon to a number, got type %s.\" % (type(epsilon),))\n        self.epsilon = epsilon\n\n    # Added in 0.5.0.\n    @property\n    def prefetchable(self):\n        \"\"\"See :func:`StochasticParameter.prefetchable`.\"\"\"\n        return True\n\n    def _draw_samples(self, size, random_state):\n        alpha = self.alpha.draw_sample(random_state=random_state)\n        beta = self.beta.draw_sample(random_state=random_state)\n        alpha = max(alpha, self.epsilon)\n        beta = max(beta, self.epsilon)\n        return random_state.beta(alpha, beta, size=size).astype(np.float32)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Beta(%s, %s)\" % (self.alpha, self.beta)\n\n\nclass FromLowerResolution(StochasticParameter):\n    \"\"\"Parameter to sample from other parameters at lower image resolutions.\n\n    This parameter is intended to be used with parameters that would usually\n    sample one value per pixel (or one value per pixel and channel). Instead\n    of sampling from the other parameter at full resolution, it samples at\n    lower resolution, e.g. ``0.5*H x 0.5*W`` with ``H`` being the height and\n    ``W`` being the width. After the low-resolution sampling this parameter\n    then upscales the result to ``HxW``.\n\n    This parameter is intended to produce coarse samples. E.g. combining\n    this with :class:`Binomial` can lead to large rectangular areas of\n    ``1`` s and ``0`` s.\n\n    Parameters\n    ----------\n    other_param : imgaug.parameters.StochasticParameter\n        The other parameter which is to be sampled on a coarser image.\n\n    size_percent : None or number or iterable of number or imgaug.parameters.StochasticParameter, optional\n        Size of the 2d sampling plane in percent of the requested size.\n        I.e. this is relative to the size provided in the call to\n        ``draw_samples(size)``. Lower values will result in smaller sampling\n        planes, which are then upsampled to `size`. This means that lower\n        values will result in larger rectangles. The size may be provided as\n        a constant value or a tuple ``(a, b)``, which will automatically be\n        converted to the continuous uniform range ``[a, b)`` or a\n        :class:`StochasticParameter`, which will be queried per call to\n        :func:`FromLowerResolution.draw_sample` and\n        :func:`FromLowerResolution.draw_samples`.\n\n    size_px : None or number or iterable of numbers or imgaug.parameters.StochasticParameter, optional\n        Size of the 2d sampling plane in pixels.\n        Lower values will result in smaller sampling planes, which are then\n        upsampled to the input `size` of ``draw_samples(size)``.\n        This means that lower values will result in larger rectangles.\n        The size may be provided as a constant value or a tuple ``(a, b)``,\n        which will automatically be converted to the discrete uniform\n        range ``[a..b]`` or a :class:`StochasticParameter`, which will be\n        queried once per call to :func:`FromLowerResolution.draw_sample` and\n        :func:`FromLowerResolution.draw_samples`.\n\n    method : str or int or imgaug.parameters.StochasticParameter, optional\n        Upsampling/interpolation method to use. This is used after the sampling\n        is finished and the low resolution plane has to be upsampled to the\n        requested `size` in ``draw_samples(size, ...)``. The method may be\n        the same as in :func:`~imgaug.imgaug.imresize_many_images`. Usually\n        ``nearest`` or ``linear`` are good choices. ``nearest`` will result\n        in rectangles with sharp edges and ``linear`` in rectangles with\n        blurry and round edges. The method may be provided as a\n        :class:`StochasticParameter`, which will be queried once per call to\n        :func:`FromLowerResolution.draw_sample` and\n        :func:`FromLowerResolution.draw_samples`.\n\n    min_size : int, optional\n        Minimum size in pixels of the low resolution sampling plane.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.FromLowerResolution(\n    >>>     Binomial(0.05),\n    >>>     size_px=(2, 16),\n    >>>     method=Choice([\"nearest\", \"linear\"]))\n\n    Samples from a binomial distribution with ``p=0.05``. The sampling plane\n    will always have a size HxWxC with H and W being independently sampled\n    from ``[2..16]`` (i.e. it may range from ``2x2xC`` up to ``16x16xC`` max,\n    but may also be e.g. ``4x8xC``). The upsampling method will be ``nearest``\n    in ``50%`` of all cases and ``linear`` in the other 50 percent. The result\n    will sometimes be rectangular patches of sharp ``1`` s surrounded by\n    ``0`` s and sometimes blurry blobs of ``1``s, surrounded by values\n    ``<1.0``.\n\n    \"\"\"\n    def __init__(self, other_param, size_percent=None, size_px=None,\n                 method=\"nearest\", min_size=1):\n        super(FromLowerResolution, self).__init__()\n\n        assert size_percent is not None or size_px is not None, (\n            \"Expected either 'size_percent' or 'size_px' to be provided, \"\n            \"got neither of them.\")\n\n        if size_percent is not None:\n            self.size_method = \"percent\"\n            self.size_px = None\n            if ia.is_single_number(size_percent):\n                self.size_percent = Deterministic(size_percent)\n            elif ia.is_iterable(size_percent):\n                assert len(size_percent) == 2, (\n                    \"Expected iterable 'size_percent' to contain exactly 2 \"\n                    \"values, got %d.\" % (len(size_percent),))\n                self.size_percent = Uniform(size_percent[0], size_percent[1])\n            elif isinstance(size_percent, StochasticParameter):\n                self.size_percent = size_percent\n            else:\n                raise Exception(\n                    \"Expected int, float, tuple of two ints/floats or \"\n                    \"StochasticParameter for size_percent, \"\n                    \"got %s.\" % (type(size_percent),))\n        else:  # = elif size_px is not None:\n            self.size_method = \"px\"\n            self.size_percent = None\n            if ia.is_single_integer(size_px):\n                self.size_px = Deterministic(size_px)\n            elif ia.is_iterable(size_px):\n                assert len(size_px) == 2, (\n                    \"Expected iterable 'size_px' to contain exactly 2 \"\n                    \"values, got %d.\" % (len(size_px),))\n                self.size_px = DiscreteUniform(size_px[0], size_px[1])\n            elif isinstance(size_px, StochasticParameter):\n                self.size_px = size_px\n            else:\n                raise Exception(\n                    \"Expected int, float, tuple of two ints/floats or \"\n                    \"StochasticParameter for size_px, \"\n                    \"got %s.\" % (type(size_px),))\n\n        self.other_param = other_param\n\n        if ia.is_string(method) or ia.is_single_integer(method):\n            self.method = Deterministic(method)\n        elif isinstance(method, StochasticParameter):\n            self.method = method\n        else:\n            raise Exception(\"Expected string or StochasticParameter, \"\n                            \"got %s.\" % (type(method),))\n\n        self.min_size = min_size\n\n    def _draw_samples(self, size, random_state):\n        if len(size) == 3:\n            n = 1\n            h, w, c = size\n        elif len(size) == 4:\n            n, h, w, c = size\n        else:\n            raise Exception(\"FromLowerResolution can only generate samples \"\n                            \"of shape (H, W, C) or (N, H, W, C), \"\n                            \"requested was %s.\" % (str(size),))\n\n        if self.size_method == \"percent\":\n            hw_percents = self.size_percent.draw_samples(\n                (n, 2), random_state=random_state)\n            hw_pxs = (hw_percents * np.array([h, w])).astype(np.int32)\n        else:\n            hw_pxs = self.size_px.draw_samples(\n                (n, 2), random_state=random_state)\n\n        methods = self.method.draw_samples((n,), random_state=random_state)\n        result = None\n        for i, (hw_px, method) in enumerate(zip(hw_pxs, methods)):\n            h_small = max(hw_px[0], self.min_size)\n            w_small = max(hw_px[1], self.min_size)\n            samples = self.other_param.draw_samples(\n                (1, h_small, w_small, c), random_state=random_state)\n\n            # This (1) makes sure that samples are of dtypes supported by\n            # imresize_many_images, and (2) forces samples to be float-kind\n            # if the requested interpolation is something else than nearest\n            # neighbour interpolation. (2) is a bit hacky and makes sure that\n            # continuous values are produced for e.g. cubic interpolation.\n            # This is particularly important for e.g. binomial distributios\n            # used in FromLowerResolution and thereby in e.g. CoarseDropout,\n            # where integer-kinds would lead to sharp edges despite using\n            # cubic interpolation.\n            if samples.dtype.kind == \"f\":\n                samples = iadt.restore_dtypes_(samples, np.float32)\n            elif samples.dtype.kind == \"i\":\n                if method == \"nearest\":\n                    samples = iadt.restore_dtypes_(samples, np.int32)\n                else:\n                    samples = iadt.restore_dtypes_(samples, np.float32)\n            else:\n                assert samples.dtype.kind == \"u\", (\n                    \"FromLowerResolution can only process outputs of kind \"\n                    \"f (float), i (int) or u (uint), got %s.\" % (\n                        samples.dtype.kind))\n                if method == \"nearest\":\n                    samples = iadt.restore_dtypes_(samples, np.uint16)\n                else:\n                    samples = iadt.restore_dtypes_(samples, np.float32)\n\n            samples_upscaled = ia.imresize_many_images(\n                samples, (h, w), interpolation=method)\n\n            if result is None:\n                result = np.zeros((n, h, w, c), dtype=samples_upscaled.dtype)\n            result[i] = samples_upscaled\n\n        if len(size) == 3:\n            return result[0]\n        return result\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        if self.size_method == \"percent\":\n            pattern = (\n                \"FromLowerResolution(\"\n                \"size_percent=%s, method=%s, other_param=%s\"\n                \")\")\n            return pattern % (self.size_percent, self.method, self.other_param)\n\n        pattern = (\n            \"FromLowerResolution(\"\n            \"size_px=%s, method=%s, other_param=%s\"\n            \")\")\n        return pattern % (self.size_px, self.method, self.other_param)\n\n\nclass Clip(StochasticParameter):\n    \"\"\"Clip another parameter to a defined value range.\n\n    Parameters\n    ----------\n    other_param : imgaug.parameters.StochasticParameter\n        The other parameter, which's values are to be clipped.\n\n    minval : None or number, optional\n        The minimum value to use.\n        If ``None``, no minimum will be used.\n\n    maxval : None or number, optional\n        The maximum value to use.\n        If ``None``, no maximum will be used.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Clip(Normal(0, 1.0), minval=-2.0, maxval=2.0)\n\n    Create a standard gaussian distribution, which's values never go below\n    ``-2.0`` or above ``2.0``. Note that this will lead to small \"bumps\" of\n    higher probability at ``-2.0`` and ``2.0``, as values below/above these\n    will be clipped to them. For smoother limitations on gaussian\n    distributions, see :class:`TruncatedNormal`.\n\n    \"\"\"\n\n    def __init__(self, other_param, minval=None, maxval=None):\n        super(Clip, self).__init__()\n\n        _assert_arg_is_stoch_param(\"other_param\", other_param)\n        assert minval is None or ia.is_single_number(minval), (\n            \"Expected 'minval' to be None or a number, got type %s.\" % (\n                type(minval),))\n        assert maxval is None or ia.is_single_number(maxval), (\n            \"Expected 'maxval' to be None or a number, got type %s.\" % (\n                type(maxval),))\n\n        self.other_param = other_param\n        self.minval = minval\n        self.maxval = maxval\n\n    def _draw_samples(self, size, random_state):\n        samples = self.other_param.draw_samples(size, random_state=random_state)\n        if self.minval is not None or self.maxval is not None:\n            # Note that this would produce a warning if 'samples' is int64\n            # or uint64\n            samples = np.clip(samples, self.minval, self.maxval, out=samples)\n        return samples\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        opstr = str(self.other_param)\n        if self.minval is not None and self.maxval is not None:\n            return \"Clip(%s, %.6f, %.6f)\" % (\n                opstr, float(self.minval), float(self.maxval))\n        if self.minval is not None:\n            return \"Clip(%s, %.6f, None)\" % (opstr, float(self.minval))\n        if self.maxval is not None:\n            return \"Clip(%s, None, %.6f)\" % (opstr, float(self.maxval))\n        return \"Clip(%s, None, None)\" % (opstr,)\n\n\nclass Discretize(StochasticParameter):\n    \"\"\"Convert a continuous distribution to a discrete one.\n\n    This will round the values and then cast them to integers.\n    Values sampled from already discrete distributions are not changed.\n\n    Parameters\n    ----------\n    other_param : imgaug.parameters.StochasticParameter\n        The other parameter, which's values are to be discretized.\n\n    round : bool, optional\n        Whether to round before converting to integer dtype.\n\n        Added in 0.4.0.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Discretize(iap.Normal(0, 1.0))\n\n    Create a discrete standard gaussian distribution.\n\n    \"\"\"\n    def __init__(self, other_param, round=True):\n        # pylint: disable=redefined-builtin\n        super(Discretize, self).__init__()\n        _assert_arg_is_stoch_param(\"other_param\", other_param)\n        self.other_param = other_param\n        self.round = round\n\n    def _draw_samples(self, size, random_state):\n        samples = self.other_param.draw_samples(size, random_state=random_state)\n        assert samples.dtype.kind in [\"u\", \"i\", \"b\", \"f\"], (\n            \"Expected to get uint, int, bool or float dtype as samples in \"\n            \"Discretize(), but got dtype '%s' (kind '%s') instead.\" % (\n                samples.dtype.name, samples.dtype.kind))\n\n        if samples.dtype.kind in [\"u\", \"i\", \"b\"]:\n            return samples\n\n        # floats seem to reliably cover ints that have half the number of\n        # bits -- probably not the case for float128 though as that is\n        # really float96\n        bitsize = 8 * samples.dtype.itemsize // 2\n        # in case some weird system knows something like float8 we set a\n        # lower bound here -- shouldn't happen though\n        bitsize = max(bitsize, 8)\n        dtype = np.dtype(\"int%d\" % (bitsize,))\n        if self.round:\n            samples = np.round(samples)\n        return samples.astype(dtype)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        opstr = str(self.other_param)\n        return \"Discretize(%s, round=%s)\" % (opstr, str(self.round))\n\n\nclass Multiply(StochasticParameter):\n    \"\"\"Multiply the samples of another stochastic parameter.\n\n    Parameters\n    ----------\n    other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Other parameter which's sampled values are to be multiplied with `val`.\n        Let ``S`` be the requested shape of samples, then the datatype\n        behaviour is as follows:\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value to fill an array of shape ``S``.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of\n              shape ``S`` will be filled with uniformly sampled values from\n              the continuous interval ``[a, b)``.\n            * If a ``list`` of ``number``, an array of shape ``S`` will be\n              filled with randomly picked values from the ``list``.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call to generate an array of shape ``S``.\n\n        \"per call\" denotes a call of :func:`Multiply.draw_sample` or\n        :func:`Multiply.draw_samples`.\n\n    val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Multiplier to use.\n        Datatype behaviour is analogous to `other_param`, though if\n        ``elementwise=False`` (the default), only a single sample will be\n        generated per call instead of ``S``.\n\n    elementwise : bool, optional\n        Controls the sampling behaviour of `val`.\n        If set to ``False``, a single samples will be requested from `val` and\n        used as the constant multiplier.\n        If set to ``True``, samples of shape ``S`` will be requested from\n        `val` and multiplied elementwise with the samples of `other_param`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Multiply(iap.Uniform(0.0, 1.0), -1)\n\n    Convert a uniform distribution from ``[0.0, 1.0)`` to ``(-1.0, 0.0]``.\n\n    \"\"\"\n    def __init__(self, other_param, val, elementwise=False):\n        super(Multiply, self).__init__()\n\n        self.other_param = handle_continuous_param(other_param, \"other_param\",\n                                                   prefetch=False)\n        self.val = handle_continuous_param(val, \"val\", prefetch=False)\n        self.elementwise = elementwise\n\n    def _draw_samples(self, size, random_state):\n        rngs = random_state.duplicate(2)\n        samples = self.other_param.draw_samples(size, random_state=rngs[0])\n\n        elementwise = (\n            self.elementwise\n            and not isinstance(self.val, Deterministic))\n\n        if elementwise:\n            val_samples = self.val.draw_samples(size, random_state=rngs[1])\n        else:\n            val_samples = self.val.draw_sample(random_state=rngs[1])\n\n        if elementwise:\n            return np.multiply(samples, val_samples)\n        return samples * val_samples\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Multiply(%s, %s, %s)\" % (\n            str(self.other_param), str(self.val), self.elementwise)\n\n\nclass Divide(StochasticParameter):\n    \"\"\"Divide the samples of another stochastic parameter.\n\n    This parameter will automatically prevent division by zero (uses 1.0)\n    as the denominator in these cases.\n\n    Parameters\n    ----------\n    other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Other parameter which's sampled values are to be divided by `val`.\n        Let ``S`` be the requested shape of samples, then the datatype\n        behaviour is as follows:\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value to fill an array of shape ``S``.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of\n              shape ``S`` will be filled with uniformly sampled values from\n              the continuous interval ``[a, b)``.\n            * If a ``list`` of ``number``, an array of shape ``S`` will be\n              filled with randomly picked values from the ``list``.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call to generate an array of shape ``S``.\n\n        \"per call\" denotes a call of :func:`Divide.draw_sample` or\n        :func:`Divide.draw_samples`.\n\n    val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Denominator to use.\n        Datatype behaviour is analogous to `other_param`, though if\n        ``elementwise=False`` (the default), only a single sample will be\n        generated per call instead of ``S``.\n\n    elementwise : bool, optional\n        Controls the sampling behaviour of `val`.\n        If set to ``False``, a single samples will be requested from `val` and\n        used as the constant denominator.\n        If set to ``True``, samples of shape ``S`` will be requested from\n        `val` and used to divide the samples of `other_param` elementwise.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Divide(iap.Uniform(0.0, 1.0), 2)\n\n    Convert a uniform distribution ``[0.0, 1.0)`` to ``[0, 0.5)``.\n\n    \"\"\"\n\n    def __init__(self, other_param, val, elementwise=False):\n        super(Divide, self).__init__()\n\n        self.other_param = handle_continuous_param(other_param, \"other_param\",\n                                                   prefetch=False)\n        self.val = handle_continuous_param(val, \"val\", prefetch=False)\n        self.elementwise = elementwise\n\n    def _draw_samples(self, size, random_state):\n        # pylint: disable=no-else-return\n        rngs = random_state.duplicate(2)\n        samples = self.other_param.draw_samples(size, random_state=rngs[0])\n\n        elementwise = (\n            self.elementwise\n            and not isinstance(self.val, Deterministic))\n\n        if elementwise:\n            val_samples = self.val.draw_samples(size, random_state=rngs[1])\n\n            # prevent division by zero\n            val_samples[val_samples == 0] = 1\n\n            return np.divide(\n                force_np_float_dtype(samples),\n                force_np_float_dtype(val_samples)\n            )\n        else:\n            val_sample = self.val.draw_sample(random_state=rngs[1])\n\n            # prevent division by zero\n            if val_sample == 0:\n                val_sample = 1\n\n            return force_np_float_dtype(samples) / float(val_sample)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Divide(%s, %s, %s)\" % (\n            str(self.other_param), str(self.val), self.elementwise)\n\n\n# TODO sampling (N,) from something like 10+Uniform(0, 1) will return\n#      N times the same value as (N,) values will be sampled from 10, but only\n#      one from Uniform() unless elementwise=True is explicitly set. That\n#      seems unintuitive. How can this be prevented?\nclass Add(StochasticParameter):\n    \"\"\"Add to the samples of another stochastic parameter.\n\n    Parameters\n    ----------\n    other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Samples of `val` will be added to samples of this parameter.\n        Let ``S`` be the requested shape of samples, then the datatype\n        behaviour is as follows:\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value to fill an array of shape ``S``.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of\n              shape ``S`` will be filled with uniformly sampled values from\n              the continuous interval ``[a, b)``.\n            * If a ``list`` of ``number``, an array of shape ``S`` will be\n              filled with randomly picked values from the ``list``.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call to generate an array of shape ``S``.\n\n        \"per call\" denotes a call of :func:`Add.draw_sample` or\n        :func:`Add.draw_samples`.\n\n    val : number or tuple of two number or list of number or imgaug.parameters.StochasticParameter\n        Value to add to the samples of `other_param`.\n        Datatype behaviour is analogous to `other_param`, though if\n        ``elementwise=False`` (the default), only a single sample will be\n        generated per call instead of ``S``.\n\n    elementwise : bool, optional\n        Controls the sampling behaviour of `val`.\n        If set to ``False``, a single samples will be requested from `val` and\n        used as the constant multiplier.\n        If set to ``True``, samples of shape ``S`` will be requested from\n        `val` and added elementwise with the samples of `other_param`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Add(Uniform(0.0, 1.0), 1.0)\n\n    Convert a uniform distribution from ``[0.0, 1.0)`` to ``[1.0, 2.0)``.\n\n    \"\"\"\n\n    def __init__(self, other_param, val, elementwise=False):\n        super(Add, self).__init__()\n\n        self.other_param = handle_continuous_param(other_param, \"other_param\",\n                                                   prefetch=False)\n        self.val = handle_continuous_param(val, \"val\", prefetch=False)\n        self.elementwise = elementwise\n\n    def _draw_samples(self, size, random_state):\n        rngs = random_state.duplicate(2)\n        samples = self.other_param.draw_samples(size, random_state=rngs[0])\n\n        elementwise = (\n            self.elementwise and not isinstance(self.val, Deterministic))\n\n        if elementwise:\n            val_samples = self.val.draw_samples(size, random_state=rngs[1])\n        else:\n            val_samples = self.val.draw_sample(random_state=rngs[1])\n\n        if elementwise:\n            return np.add(samples, val_samples)\n        return samples + val_samples\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Add(%s, %s, %s)\" % (\n            str(self.other_param), str(self.val), self.elementwise)\n\n\nclass Subtract(StochasticParameter):\n    \"\"\"Subtract from the samples of another stochastic parameter.\n\n    Parameters\n    ----------\n    other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Samples of `val` will be subtracted from samples of this parameter.\n        Let ``S`` be the requested shape of samples, then the datatype\n        behaviour is as follows:\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value to fill an array of shape ``S``.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of\n              shape ``S`` will be filled with uniformly sampled values from\n              the continuous interval ``[a, b)``.\n            * If a ``list`` of ``number``, an array of shape ``S`` will be\n              filled with randomly picked values from the ``list``.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call to generate an array of shape ``S``.\n\n        \"per call\" denotes a call of :func:`Subtract.draw_sample` or\n        :func:`Subtract.draw_samples`.\n\n    val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Value to subtract from the other parameter.\n        Datatype behaviour is analogous to `other_param`, though if\n        ``elementwise=False`` (the default), only a single sample will be\n        generated per call instead of ``S``.\n\n    elementwise : bool, optional\n        Controls the sampling behaviour of `val`.\n        If set to ``False``, a single samples will be requested from `val` and\n        used as the constant multiplier.\n        If set to ``True``, samples of shape ``S`` will be requested from\n        `val` and subtracted elementwise from the samples of `other_param`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Subtract(iap.Uniform(0.0, 1.0), 1.0)\n\n    Convert a uniform distribution from ``[0.0, 1.0)`` to ``[-1.0, 0.0)``.\n\n    \"\"\"\n    def __init__(self, other_param, val, elementwise=False):\n        super(Subtract, self).__init__()\n\n        self.other_param = handle_continuous_param(other_param, \"other_param\",\n                                                   prefetch=False)\n        self.val = handle_continuous_param(val, \"val\", prefetch=False)\n        self.elementwise = elementwise\n\n    def _draw_samples(self, size, random_state):\n        rngs = random_state.duplicate(2)\n        samples = self.other_param.draw_samples(size, random_state=rngs[0])\n\n        elementwise = (self.elementwise\n                       and not isinstance(self.val, Deterministic))\n\n        if elementwise:\n            val_samples = self.val.draw_samples(size, random_state=rngs[1])\n        else:\n            val_samples = self.val.draw_sample(random_state=rngs[1])\n\n        if elementwise:\n            return np.subtract(samples, val_samples)\n        return samples - val_samples\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Subtract(%s, %s, %s)\" % (\n            str(self.other_param), str(self.val), self.elementwise)\n\n\nclass Power(StochasticParameter):\n    \"\"\"Exponentiate the samples of another stochastic parameter.\n\n    Parameters\n    ----------\n    other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Other parameter which's sampled values are to be exponentiated by `val`.\n        Let ``S`` be the requested shape of samples, then the datatype\n        behaviour is as follows:\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value to fill an array of shape ``S``.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of\n              shape ``S`` will be filled with uniformly sampled values from\n              the continuous interval ``[a, b)``.\n            * If a ``list`` of ``number``, an array of shape ``S`` will be\n              filled with randomly picked values from the ``list``.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call to generate an array of shape ``S``.\n\n        \"per call\" denotes a call of :func:`Power.draw_sample` or\n        :func:`Power.draw_samples`.\n\n    val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter\n        Value to use exponentiate the samples of `other_param`.\n        Datatype behaviour is analogous to `other_param`, though if\n        ``elementwise=False`` (the default), only a single sample will be\n        generated per call instead of ``S``.\n\n    elementwise : bool, optional\n        Controls the sampling behaviour of `val`.\n        If set to ``False``, a single samples will be requested from `val` and\n        used as the constant multiplier.\n        If set to ``True``, samples of shape ``S`` will be requested from\n        `val` and used to exponentiate elementwise the samples of `other_param`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Power(iap.Uniform(0.0, 1.0), 2)\n\n    Converts a uniform range ``[0.0, 1.0)`` to a distribution that is peaked\n    towards 1.0.\n\n    \"\"\"\n    def __init__(self, other_param, val, elementwise=False):\n        super(Power, self).__init__()\n\n        self.other_param = handle_continuous_param(other_param, \"other_param\",\n                                                   prefetch=False)\n        self.val = handle_continuous_param(val, \"val\", prefetch=False)\n        self.elementwise = elementwise\n\n    def _draw_samples(self, size, random_state):\n        rngs = random_state.duplicate(2)\n        samples = self.other_param.draw_samples(size, random_state=rngs[0])\n\n        elementwise = (\n            self.elementwise\n            and not isinstance(self.val, Deterministic))\n\n        if elementwise:\n            exponents = self.val.draw_samples(size, random_state=rngs[1])\n        else:\n            exponents = self.val.draw_sample(random_state=rngs[1])\n\n        # without this we get int results in the case of\n        # Power(<int>, <stochastic float param>)\n        samples, exponents = both_np_float_if_one_is_float(samples, exponents)\n        samples_dtype = samples.dtype\n\n        # TODO switch to this as numpy>=1.15 is now a requirement\n        #      float_power requires numpy>=1.12\n        # result = np.float_power(samples, exponents)\n        # TODO why was float32 type here replaced with complex number\n        #      formulation?\n        result = np.power(samples.astype(np.complex), exponents).real\n        if result.dtype != samples_dtype:\n            result = result.astype(samples_dtype)\n\n        return result\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"Power(%s, %s, %s)\" % (\n            str(self.other_param), str(self.val), self.elementwise)\n\n\nclass Absolute(StochasticParameter):\n    \"\"\"Convert the samples of another parameter to their absolute values.\n\n    Parameters\n    ----------\n    other_param : imgaug.parameters.StochasticParameter\n        Other parameter which's sampled values are to be modified.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Absolute(iap.Uniform(-1.0, 1.0))\n\n    Convert a uniform distribution from ``[-1.0, 1.0)`` to ``[0.0, 1.0]``.\n\n    \"\"\"\n    def __init__(self, other_param):\n        super(Absolute, self).__init__()\n\n        _assert_arg_is_stoch_param(\"other_param\", other_param)\n\n        self.other_param = other_param\n\n    def _draw_samples(self, size, random_state):\n        samples = self.other_param.draw_samples(size, random_state=random_state)\n        return np.absolute(samples)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        opstr = str(self.other_param)\n        return \"Absolute(%s)\" % (opstr,)\n\n\nclass RandomSign(StochasticParameter):\n    \"\"\"Convert a parameter's samples randomly to positive or negative values.\n\n    Parameters\n    ----------\n    other_param : imgaug.parameters.StochasticParameter\n        Other parameter which's sampled values are to be modified.\n\n    p_positive : number\n        Fraction of values that are supposed to be turned to positive values.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.RandomSign(iap.Poisson(1))\n\n    Create a poisson distribution with ``alpha=1`` that is mirrored/copied (not\n    flipped) at the y-axis.\n\n    \"\"\"\n\n    def __init__(self, other_param, p_positive=0.5):\n        super(RandomSign, self).__init__()\n\n        _assert_arg_is_stoch_param(\"other_param\", other_param)\n        assert ia.is_single_number(p_positive), (\n            \"Expected 'p_positive' to be a number, got %s.\" % (\n                type(p_positive)))\n        assert 0.0 <= p_positive <= 1.0, (\n            \"Expected 'p_positive' to be in the interval [0.0, 1.0], \"\n            \"got %.4f.\" % (p_positive,))\n\n        self.other_param = other_param\n        self.p_positive = p_positive\n\n    def _draw_samples(self, size, random_state):\n        rss = random_state.duplicate(2)\n        samples = self.other_param.draw_samples(size, random_state=rss[0])\n        # TODO add method to change from uint to int here instead of assert\n        assert samples.dtype.kind in [\"f\", \"i\"], (\n            \"Expected to get samples of kind float or int, but got dtype %s \"\n            \"of kind %s.\" % (samples.dtype.name, samples.dtype.kind))\n        # TODO convert to same kind as samples\n        coinflips = rss[1].binomial(\n            1, self.p_positive, size=size).astype(np.int8)\n        signs = coinflips * 2 - 1\n        # Add absolute here to guarantee that we get p_positive percent of\n        # positive values. Otherwise we would merely flip p_positive percent\n        # of all signs.\n        # TODO test if\n        #          result[coinflips_mask] *= (-1)\n        #      is faster  (with protection against mask being empty?)\n        result = np.absolute(samples) * signs\n        return result\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        opstr = str(self.other_param)\n        return \"RandomSign(%s, %.2f)\" % (opstr, self.p_positive)\n\n\nclass ForceSign(StochasticParameter):\n    \"\"\"Convert a parameter's samples to either positive or negative values.\n\n    Parameters\n    ----------\n    other_param : imgaug.parameters.StochasticParameter\n        Other parameter which's sampled values are to be modified.\n\n    positive : bool\n        Whether to force all signs to be positive (``True``) or\n        negative (``False``).\n\n    mode : {'invert', 'reroll'}, optional\n        Method to change the signs. Valid values are ``invert`` and ``reroll``.\n        ``invert`` means that wrong signs are simply flipped.\n        ``reroll`` means that all samples with wrong signs are sampled again,\n        optionally many times, until they randomly end up having the correct\n        sign.\n\n    reroll_count_max : int, optional\n        If `mode` is set to ``reroll``, this determines how often values may\n        be rerolled before giving up and simply flipping the sign (as in\n        ``mode=\"invert\"``). This shouldn't be set too high, as rerolling is\n        expensive.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.ForceSign(iap.Poisson(1), positive=False)\n\n    Create a poisson distribution with ``alpha=1`` that is flipped towards\n    negative values.\n\n    \"\"\"\n\n    def __init__(self, other_param, positive, mode=\"invert\",\n                 reroll_count_max=2):\n        super(ForceSign, self).__init__()\n\n        _assert_arg_is_stoch_param(\"other_param\", other_param)\n        self.other_param = other_param\n\n        assert positive in [True, False], (\n            \"Expected 'positive' to be True or False, got type %s.\" % (\n                type(positive),))\n        self.positive = positive\n\n        assert mode in [\"invert\", \"reroll\"], (\n            \"Expected 'mode' to be \\\"invert\\\" or \\\"reroll\\\", got %s.\" % (mode,))\n        self.mode = mode\n\n        assert ia.is_single_integer(reroll_count_max), (\n            \"Expected 'reroll_count_max' to be an integer, got type %s.\" % (\n                type(reroll_count_max)))\n        self.reroll_count_max = reroll_count_max\n\n    def _draw_samples(self, size, random_state):\n        rngs = random_state.duplicate(1+self.reroll_count_max)\n        samples = self.other_param.draw_samples(size, random_state=rngs[0])\n\n        if self.mode == \"invert\":\n            if self.positive:\n                samples[samples < 0] *= (-1)\n            else:\n                samples[samples > 0] *= (-1)\n        else:\n            if self.positive:\n                bad_samples = np.where(samples < 0)[0]\n            else:\n                bad_samples = np.where(samples > 0)[0]\n\n            reroll_count = 0\n            while len(bad_samples) > 0 and reroll_count < self.reroll_count_max:\n                # This rerolls the full input size, even when only a tiny\n                # fraction of the values were wrong. That is done, because not\n                # all parameters necessarily support any number of dimensions\n                # for `size`, so we cant just resample size=N for N values\n                # with wrong signs.\n                # There is still quite some room for improvement here.\n                samples_reroll = self.other_param.draw_samples(\n                    size,\n                    random_state=rngs[1+reroll_count]\n                )\n                samples[bad_samples] = samples_reroll[bad_samples]\n\n                reroll_count += 1\n                if self.positive:\n                    bad_samples = np.where(samples < 0)[0]\n                else:\n                    bad_samples = np.where(samples > 0)[0]\n\n            if len(bad_samples) > 0:\n                samples[bad_samples] *= (-1)\n\n        return samples\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        opstr = str(self.other_param)\n        return \"ForceSign(%s, %s, %s, %d)\" % (\n            opstr, str(self.positive), self.mode, self.reroll_count_max)\n\n\ndef Positive(other_param, mode=\"invert\", reroll_count_max=2):\n    \"\"\"Convert another parameter's results to positive values.\n\n    Parameters\n    ----------\n    other_param : imgaug.parameters.StochasticParameter\n        Other parameter which's sampled values are to be\n        modified.\n\n    mode : {'invert', 'reroll'}, optional\n        How to change the signs. Valid values are ``invert`` and ``reroll``.\n        ``invert`` means that wrong signs are simply flipped.\n        ``reroll`` means that all samples with wrong signs are sampled again,\n        optionally many times, until they randomly end up having the correct\n        sign.\n\n    reroll_count_max : int, optional\n        If `mode` is set to ``reroll``, this determines how often values may\n        be rerolled before giving up and simply flipping the sign (as in\n        ``mode=\"invert\"``). This shouldn't be set too high, as rerolling is\n        expensive.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Positive(iap.Normal(0, 1), mode=\"reroll\")\n\n    Create a gaussian distribution that has only positive values.\n    If any negative value is sampled in the process, that sample is resampled\n    up to two times to get a positive one. If it isn't positive after the\n    second resampling step, the sign is simply flipped.\n\n    \"\"\"\n    # pylint: disable=invalid-name\n    return ForceSign(\n        other_param=other_param,\n        positive=True,\n        mode=mode,\n        reroll_count_max=reroll_count_max\n    )\n\n\ndef Negative(other_param, mode=\"invert\", reroll_count_max=2):\n    \"\"\"Convert another parameter's results to negative values.\n\n    Parameters\n    ----------\n    other_param : imgaug.parameters.StochasticParameter\n        Other parameter which's sampled values are to be\n        modified.\n\n    mode : {'invert', 'reroll'}, optional\n        How to change the signs. Valid values are ``invert`` and ``reroll``.\n        ``invert`` means that wrong signs are simply flipped.\n        ``reroll`` means that all samples with wrong signs are sampled again,\n        optionally many times, until they randomly end up having the correct\n        sign.\n\n    reroll_count_max : int, optional\n        If `mode` is set to ``reroll``, this determines how often values may\n        be rerolled before giving up and simply flipping the sign (as in\n        ``mode=\"invert\"``). This shouldn't be set too high, as rerolling is\n        expensive.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Negative(iap.Normal(0, 1), mode=\"reroll\")\n\n    Create a gaussian distribution that has only negative values.\n    If any positive value is sampled in the process, that sample is resampled\n    up to two times to get a negative one. If it isn't negative after the\n    second resampling step, the sign is simply flipped.\n\n    \"\"\"\n    # pylint: disable=invalid-name\n    return ForceSign(\n        other_param=other_param,\n        positive=False,\n        mode=mode,\n        reroll_count_max=reroll_count_max\n    )\n\n\n# TODO this always aggregates the result in high resolution space, instead of\n#      aggregating them in low resolution and then only upscaling the final\n#      image (for N iterations that would save up to N-1 upscales)\nclass IterativeNoiseAggregator(StochasticParameter):\n    \"\"\"Aggregate multiple iterations of samples from another parameter.\n\n    This is supposed to be used in conjunction with :class:`SimplexNoise` or\n    :class:`FrequencyNoise`. If a shape ``S`` is requested, it will request\n    ``I`` times ``S`` samples from the underlying parameter, where ``I`` is\n    the number of iterations. The ``I`` arrays will be combined to a single\n    array of shape ``S`` using an aggregation method, e.g. simple averaging.\n\n    Parameters\n    ----------\n    other_param : StochasticParameter\n        The other parameter from which to sample one or more times.\n\n    iterations : int or iterable of int or list of int or imgaug.parameters.StochasticParameter, optional\n        The number of iterations.\n\n            * If a single ``int``, this ``int`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be\n              sampled from the discrete interval ``[a..b]`` once per call.\n            * If a ``list`` of ``int``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of\n        :func:`IterativeNoiseAggregator.draw_sample` or\n        :func:`IterativeNoiseAggregator.draw_samples`.\n\n    aggregation_method : imgaug.ALL or {'min', 'avg', 'max'} or list of str or imgaug.parameters.StochasticParameter, optional\n        The method to use to aggregate the samples of multiple iterations\n        to a single output array. All methods combine several arrays of\n        shape ``S`` each to a single array of shape ``S`` and hence work\n        elementwise. Known methods are ``min`` (take the minimum over all\n        iterations), ``max`` (take the maximum) and ``avg`` (take the average).\n\n            * If an ``str``, it must be one of the described methods and\n              will be used for all calls..\n            * If a ``list`` of ``str``, it must contain one or more of the\n              described methods and a random one will be samples once per call.\n            * If ``imgaug.ALL``, then equivalent to the ``list``\n              ``[\"min\", \"max\", \"avg\"]``.\n            * If :class:`StochasticParameter`, a value will be sampled from\n              that parameter once per call and must be one of the described\n              methods..\n\n        \"per call\" denotes a call of\n        :func:`IterativeNoiseAggregator.draw_sample` or\n        :func:`IterativeNoiseAggregator.draw_samples`.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> noise = iap.IterativeNoiseAggregator(\n    >>>     iap.SimplexNoise(),\n    >>>     iterations=(2, 5),\n    >>>     aggregation_method=\"max\")\n\n    Create a parameter that -- upon each call -- generates ``2`` to ``5``\n    arrays of simplex noise with the same shape. Then it combines these\n    noise maps to a single map using elementwise maximum.\n\n    \"\"\"\n\n    def __init__(self, other_param, iterations=(1, 3),\n                 aggregation_method=[\"max\", \"avg\"]):\n        # pylint: disable=dangerous-default-value\n        super(IterativeNoiseAggregator, self).__init__()\n        _assert_arg_is_stoch_param(\"other_param\", other_param)\n        self.other_param = other_param\n\n        def _assert_within_bounds(_iterations):\n            assert all([1 <= val <= 10000 for val in _iterations]), (\n                \"Expected 'iterations' to only contain values within \"\n                \"the interval [1, 1000], got values %s.\" % (\n                    \", \".join([str(val) for val in _iterations]),))\n\n        if ia.is_single_integer(iterations):\n            _assert_within_bounds([iterations])\n            self.iterations = Deterministic(iterations)\n        elif isinstance(iterations, list):\n            assert len(iterations) > 0, (\n                \"Expected 'iterations' of type list to contain at least one \"\n                \"entry, got %d.\" % (len(iterations),))\n            _assert_within_bounds(iterations)\n            self.iterations = Choice(iterations)\n        elif ia.is_iterable(iterations):\n            assert len(iterations) == 2, (\n                \"Expected iterable non-list 'iteratons' to contain exactly \"\n                \"two entries, got %d.\" % (len(iterations),))\n            assert all([ia.is_single_integer(val) for val in iterations]), (\n                \"Expected iterable non-list 'iterations' to only contain \"\n                \"integers, got types %s.\" % (\n                    \", \".join([str(type(val)) for val in iterations]),))\n            _assert_within_bounds(iterations)\n            self.iterations = DiscreteUniform(iterations[0], iterations[1])\n        elif isinstance(iterations, StochasticParameter):\n            self.iterations = iterations\n        else:\n            raise Exception(\n                \"Expected iterations to be int or tuple of two ints or \"\n                \"StochasticParameter, got %s.\" % (type(iterations),))\n\n        if aggregation_method == ia.ALL:\n            self.aggregation_method = Choice([\"min\", \"max\", \"avg\"])\n        elif ia.is_string(aggregation_method):\n            self.aggregation_method = Deterministic(aggregation_method)\n        elif isinstance(aggregation_method, list):\n            assert len(aggregation_method) >= 1, (\n                \"Expected at least one aggregation method got %d.\" % (\n                    len(aggregation_method),))\n            assert all([ia.is_string(val) for val in aggregation_method]), (\n                \"Expected aggregation methods provided as strings, \"\n                \"got types %s.\" % (\n                    \", \".join([str(type(v)) for v in aggregation_method])))\n            self.aggregation_method = Choice(aggregation_method)\n        elif isinstance(aggregation_method, StochasticParameter):\n            self.aggregation_method = aggregation_method\n        else:\n            raise Exception(\n                \"Expected aggregation_method to be string or list of strings \"\n                \"or StochasticParameter, got %s.\" % (\n                    type(aggregation_method),))\n\n    def _draw_samples(self, size, random_state):\n        rngs = random_state.duplicate(2)\n        aggregation_method = self.aggregation_method.draw_sample(\n            random_state=rngs[0])\n        iterations = self.iterations.draw_sample(random_state=rngs[1])\n        assert iterations > 0, (\n            \"Expected to sample at least one iteration of aggregation. \"\n            \"Got %d.\" % (iterations,))\n\n        rngs_iterations = rngs[1].duplicate(iterations)\n\n        result = np.zeros(size, dtype=np.float32)\n        for i in sm.xrange(iterations):\n            noise_iter = self.other_param.draw_samples(\n                size, random_state=rngs_iterations[i])\n\n            if aggregation_method == \"avg\":\n                result += noise_iter\n            elif aggregation_method == \"min\":\n                if i == 0:\n                    result = noise_iter\n                else:\n                    result = np.minimum(result, noise_iter)\n            else:  # self.aggregation_method == \"max\"\n                if i == 0:\n                    result = noise_iter\n                else:\n                    result = np.maximum(result, noise_iter)\n\n        if aggregation_method == \"avg\":\n            result = result / iterations\n\n        return result\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        opstr = str(self.other_param)\n        return \"IterativeNoiseAggregator(%s, %s, %s)\" % (\n            opstr, str(self.iterations), str(self.aggregation_method))\n\n\nclass Sigmoid(StochasticParameter):\n    \"\"\"Apply a sigmoid function to the outputs of another parameter.\n\n    This is intended to be used in combination with :class:`SimplexNoise` or\n    :class:`FrequencyNoise`. It pushes the noise values away from ``~0.5`` and\n    towards ``0.0`` or ``1.0``, making the noise maps more binary.\n\n    Parameters\n    ----------\n    other_param : imgaug.parameters.StochasticParameter\n        The other parameter to which the sigmoid will be applied.\n\n    threshold : number or tuple of number or iterable of number or imgaug.parameters.StochasticParameter, optional\n        Sets the value of the sigmoid's saddle point, i.e. where values\n        start to quickly shift from ``0.0`` to ``1.0``.\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be\n              sampled from the continuous interval ``[a, b)`` once per call.\n            * If a ``list`` of ``number``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`Sigmoid.draw_sample` or\n        :func:`Sigmoid.draw_samples`.\n\n    activated : bool or number, optional\n        Defines whether the sigmoid is activated. If this is ``False``, the\n        results of `other_param` will not be altered. This may be set to a\n        ``float`` ``p`` in value range``[0.0, 1.0]``, which will result in\n        `activated` being ``True`` in ``p`` percent of all calls.\n\n    mul : number, optional\n        The results of `other_param` will be multiplied with this value before\n        applying the sigmoid. For noise values (range ``[0.0, 1.0]``) this\n        should be set to about ``20``.\n\n    add : number, optional\n        This value will be added to the results of `other_param` before\n        applying the sigmoid. For noise values (range ``[0.0, 1.0]``) this\n        should be set to about ``-10.0``, provided `mul` was set to ``20``.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.Sigmoid(\n    >>>     iap.SimplexNoise(),\n    >>>     activated=0.5,\n    >>>     mul=20,\n    >>>     add=-10)\n\n    Applies a sigmoid to simplex noise in ``50%`` of all calls. The noise\n    results are modified to match the sigmoid's expected value range. The\n    sigmoid's outputs are in the range ``[0.0, 1.0]``.\n\n    \"\"\"\n\n    def __init__(self, other_param, threshold=(-10, 10), activated=True,\n                 mul=1, add=0):\n        super(Sigmoid, self).__init__()\n        _assert_arg_is_stoch_param(\"other_param\", other_param)\n        self.other_param = other_param\n\n        self.threshold = handle_continuous_param(threshold, \"threshold\",\n                                                 prefetch=False)\n        self.activated = handle_probability_param(activated, \"activated\",\n                                                  prefetch=False)\n\n        assert ia.is_single_number(mul), (\n            \"Expected 'mul' to be a number, got type %s.\" % (type(mul),))\n        assert mul > 0, (\n            \"Expected 'mul' to be greater than zero, got %.4f.\" % (mul,))\n        self.mul = mul\n\n        assert ia.is_single_number(add), (\n            \"Expected 'add' to be a number, got type %s.\" % (type(add),))\n        self.add = add\n\n    @staticmethod\n    def create_for_noise(other_param, threshold=(-10, 10), activated=True):\n        \"\"\"Create a Sigmoid adjusted for noise parameters.\n\n        \"noise\" here denotes :class:`SimplexNoise` and :class:`FrequencyNoise`.\n\n        Parameters\n        ----------\n        other_param : imgaug.parameters.StochasticParameter\n            See :func:`~imgaug.parameters.Sigmoid.__init__`.\n\n        threshold : number or tuple of number or iterable of number or imgaug.parameters.StochasticParameter, optional\n            See :func:`~imgaug.parameters.Sigmoid.__init__`.\n\n        activated : bool or number, optional\n            See :func:`~imgaug.parameters.Sigmoid.__init__`.\n\n        Returns\n        -------\n        Sigmoid\n            A sigmoid adjusted to be used with noise.\n\n        \"\"\"\n        return Sigmoid(other_param, threshold, activated, mul=20, add=-10)\n\n    def _draw_samples(self, size, random_state):\n        rngs = random_state.duplicate(3)\n        result = self.other_param.draw_samples(size, random_state=rngs[0])\n        if result.dtype.kind != \"f\":\n            result = result.astype(np.float32)\n        activated = self.activated.draw_sample(random_state=rngs[1])\n        threshold = self.threshold.draw_sample(random_state=rngs[2])\n        if activated > 0.5:\n            # threshold must be subtracted here, not added\n            # higher threshold = move threshold of sigmoid towards the right\n            #                  = make it harder to pass the threshold\n            #                  = more 0.0s / less 1.0s\n            # by subtracting a high value, it moves each x towards the left,\n            # leading to more values being left of the threshold, leading\n            # to more 0.0s\n            return 1 / (1 + np.exp(-(result * self.mul + self.add - threshold)))\n        return result\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        opstr = str(self.other_param)\n        return \"Sigmoid(%s, %s, %s, %s, %s)\" % (\n            opstr, str(self.threshold), str(self.activated), str(self.mul),\n            str(self.add))\n\n\nclass SimplexNoise(StochasticParameter):\n    \"\"\"Parameter that generates simplex noise of varying resolutions.\n\n    This parameter expects to sample noise for 2d planes, i.e. for\n    sizes ``(H, W, [C])`` and will return a value in the range ``[0.0, 1.0]``\n    per spatial location in that plane.\n\n    The noise is sampled from low resolution planes and\n    upscaled to the requested height and width. The size of the low\n    resolution plane may be defined (large values can be slow) and the\n    interpolation method for upscaling can be set.\n\n    Parameters\n    ----------\n    size_px_max : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Maximum height and width in pixels of the low resolution plane.\n        Upon any sampling call, the requested shape will be downscaled until\n        the height or width (whichever is larger) does not exceed this maximum\n        value anymore. Then the noise will be sampled at that shape and later\n        upscaled back to the requested shape.\n\n            * If a single ``int``, this ``int`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be\n              sampled from the discrete interval ``[a..b]`` once per call.\n            * If a ``list`` of ``int``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`SimplexNoise.draw_sample` or\n        :func:`SimplexNoise.draw_samples`.\n\n    upscale_method : str or int or list of str or list of int or imgaug.parameters.StochasticParameter, optional\n        After generating the noise maps in low resolution environments, they\n        have to be upscaled to the originally requested shape (i.e. usually\n        the image size). This parameter controls the interpolation method to\n        use. See also :func:`~imgaug.imgaug.imresize_many_images` for a\n        description of possible values.\n\n            * If ``imgaug.ALL``, then either ``nearest`` or ``linear`` or\n              ``area`` or ``cubic`` is picked per iteration (all same\n              probability).\n            * If ``str``, then that value will always be used as the method\n              (must be ``nearest`` or ``linear`` or ``area`` or ``cubic``).\n            * If ``list`` of ``str``, then a random value will be picked from\n              that list per call.\n            * If :class:`StochasticParameter`, then a random value will be\n              sampled from that parameter per call.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.SimplexNoise(upscale_method=\"linear\")\n\n    Create a parameter that produces smooth simplex noise of varying sizes.\n\n    >>> param = iap.SimplexNoise(\n    >>>     size_px_max=(8, 16),\n    >>>     upscale_method=\"nearest\")\n\n    Create a parameter that produces rectangular simplex noise of rather\n    high detail.\n\n    \"\"\"\n\n    def __init__(self, size_px_max=(2, 16),\n                 upscale_method=[\"linear\", \"nearest\"]):\n        # pylint: disable=dangerous-default-value\n        super(SimplexNoise, self).__init__()\n        self.size_px_max = handle_discrete_param(\n            size_px_max, \"size_px_max\", value_range=(1, 10000))\n\n        if upscale_method == ia.ALL:\n            self.upscale_method = Choice([\"nearest\", \"linear\", \"area\",\n                                          \"cubic\"])\n        elif ia.is_string(upscale_method):\n            self.upscale_method = Deterministic(upscale_method)\n        elif isinstance(upscale_method, list):\n            assert len(upscale_method) >= 1, (\n                \"Expected at least one upscale method, \"\n                \"got %d.\" % (len(upscale_method),))\n            assert all([ia.is_string(val) for val in upscale_method]), (\n                \"Expected all upscale methods to be strings, got types %s.\" % (\n                    \", \".join([str(type(v)) for v in upscale_method])))\n            self.upscale_method = Choice(upscale_method)\n        elif isinstance(upscale_method, StochasticParameter):\n            self.upscale_method = upscale_method\n        else:\n            raise Exception(\n                \"Expected upscale_method to be string or list of strings or \"\n                \"StochasticParameter, got %s.\" % (type(upscale_method),))\n\n    def _draw_samples(self, size, random_state):\n        assert len(size) in [2, 3], (\n            \"Expected requested noise to have shape (H, W) or (H, W, C), \"\n            \"got shape %s.\" % (size,))\n        height, width = size[0:2]\n        nb_channels = 1 if len(size) == 2 else size[2]\n\n        channels = [self._draw_samples_hw(height, width, random_state)\n                    for _ in np.arange(nb_channels)]\n\n        if len(size) == 2:\n            return channels[0]\n        return np.stack(channels, axis=-1)\n\n    def _draw_samples_hw(self, height, width, random_state):\n        iterations = 1\n        rngs = random_state.duplicate(1+iterations)\n        aggregation_method = \"max\"\n        upscale_methods = self.upscale_method.draw_samples(\n            (iterations,), random_state=rngs[0])\n        result = np.zeros((height, width), dtype=np.float32)\n        for i in sm.xrange(iterations):\n            noise_iter = self._draw_samples_iteration(\n                height, width, rngs[1+i], upscale_methods[i])\n            if aggregation_method == \"avg\":\n                result += noise_iter\n            elif aggregation_method == \"min\":\n                if i == 0:\n                    result = noise_iter\n                else:\n                    result = np.minimum(result, noise_iter)\n            else:  # self.aggregation_method == \"max\"\n                if i == 0:\n                    result = noise_iter\n                else:\n                    result = np.maximum(result, noise_iter)\n\n        if aggregation_method == \"avg\":\n            result = result / iterations\n\n        return result\n\n    def _draw_samples_iteration(self, height, width, rng, upscale_method):\n        opensimplex_seed = rng.generate_seed_()\n\n        # we have to use int(.) here, otherwise we can get warnings about\n        # value overflows in OpenSimplex L103\n        generator = OpenSimplex(seed=int(opensimplex_seed))\n\n        maxlen = max(height, width)\n        size_px_max = self.size_px_max.draw_sample(random_state=rng)\n        if maxlen > size_px_max:\n            downscale_factor = size_px_max / maxlen\n            h_small = int(height * downscale_factor)\n            w_small = int(width * downscale_factor)\n        else:\n            h_small = height\n            w_small = width\n\n        # don't go below Hx1 or 1xW\n        h_small = max(h_small, 1)\n        w_small = max(w_small, 1)\n\n        noise = np.zeros((h_small, w_small), dtype=np.float32)\n        for y in sm.xrange(h_small):\n            for x in sm.xrange(w_small):\n                noise[y, x] = generator.noise2d(y=y, x=x)\n\n        # TODO this was previously (noise+0.5)/2, which was wrong as the noise\n        #      here is in range [-1.0, 1.0], but this new normalization might\n        #      lead to bad masks due to too many values being significantly\n        #      above 0.0 instead of being clipped to 0?\n        noise_0to1 = (noise + 1.0) / 2\n        noise_0to1 = np.clip(noise_0to1, 0.0, 1.0)\n\n        if noise_0to1.shape != (height, width):\n            noise_0to1_uint8 = (noise_0to1 * 255).astype(np.uint8)\n            noise_0to1_3d = np.tile(\n                noise_0to1_uint8[..., np.newaxis], (1, 1, 3))\n            noise_0to1 = ia.imresize_single_image(\n                noise_0to1_3d, (height, width), interpolation=upscale_method)\n            noise_0to1 = (noise_0to1[..., 0] / 255.0).astype(np.float32)\n\n        return noise_0to1\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"SimplexNoise(%s, %s)\" % (\n            str(self.size_px_max),\n            str(self.upscale_method)\n        )\n\n\nclass FrequencyNoise(StochasticParameter):\n    \"\"\"Parameter to generate noise of varying frequencies.\n\n    This parameter expects to sample noise for 2d planes, i.e. for\n    sizes ``(H, W, [C])`` and will return a value in the range ``[0.0, 1.0]``\n    per spatial location in that plane.\n\n    The exponent controls the frequencies and therefore noise patterns.\n    Small values (around ``-4.0``) will result in large blobs. Large values\n    (around ``4.0``) will result in small, repetitive patterns.\n\n    The noise is sampled from low resolution planes and\n    upscaled to the requested height and width. The size of the low\n    resolution plane may be defined (high values can be slow) and the\n    interpolation method for upscaling can be set.\n\n    Parameters\n    ----------\n    exponent : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional\n        Exponent to use when scaling in the frequency domain.\n        Sane values are in the range ``-4`` (large blobs) to ``4`` (small\n        patterns). To generate cloud-like structures, use roughly ``-2``.\n\n            * If a single ``number``, this ``number`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be\n              sampled from the continuous interval ``[a, b)`` once per call.\n            * If a ``list`` of ``number``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n    size_px_max : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional\n        Maximum height and width in pixels of the low resolution plane.\n        Upon any sampling call, the requested shape will be downscaled until\n        the height or width (whichever is larger) does not exceed this maximum\n        value anymore. Then the noise will be sampled at that shape and later\n        upscaled back to the requested shape.\n\n            * If a single ``int``, this ``int`` will be used as a\n              constant value.\n            * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be\n              sampled from the discrete interval ``[a..b]`` once per call.\n            * If a ``list`` of ``int``, a random value will be picked from\n              the ``list`` once per call.\n            * If a :class:`StochasticParameter`, that parameter will be\n              queried once per call.\n\n        \"per call\" denotes a call of :func:`FrequencyNoise.draw_sample` or\n        :func:`FrequencyNoise.draw_samples`.\n\n    upscale_method : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional\n        After generating the noise maps in low resolution environments, they\n        have to be upscaled to the originally requested shape (i.e. usually\n        the image size). This parameter controls the interpolation method to\n        use. See also :func:`~imgaug.imgaug.imresize_many_images` for a\n        description of possible values.\n\n            * If ``imgaug.ALL``, then either ``nearest`` or ``linear`` or\n              ``area`` or ``cubic`` is picked per iteration (all same\n              probability).\n            * If ``str``, then that value will always be used as the method\n              (must be ``nearest`` or ``linear`` or ``area`` or ``cubic``).\n            * If ``list`` of ``str``, then a random value will be picked from\n              that list per call.\n            * If :class:`StochasticParameter`, then a random value will be\n              sampled from that parameter per call.\n\n    Examples\n    --------\n    >>> import imgaug.parameters as iap\n    >>> param = iap.FrequencyNoise(\n    >>>     exponent=-2,\n    >>>     size_px_max=(16, 32),\n    >>>     upscale_method=\"linear\")\n\n    Create a parameter that produces noise with cloud-like patterns.\n\n    \"\"\"\n\n    def __init__(self, exponent=(-4, 4), size_px_max=(4, 32),\n                 upscale_method=[\"linear\", \"nearest\"]):\n        # pylint: disable=dangerous-default-value\n        super(FrequencyNoise, self).__init__()\n        self.exponent = handle_continuous_param(exponent, \"exponent\")\n        self.size_px_max = handle_discrete_param(\n            size_px_max, \"size_px_max\", value_range=(1, 10000))\n\n        if upscale_method == ia.ALL:\n            self.upscale_method = Choice([\"nearest\", \"linear\", \"area\",\n                                          \"cubic\"])\n        elif ia.is_string(upscale_method):\n            self.upscale_method = Deterministic(upscale_method)\n        elif isinstance(upscale_method, list):\n            assert len(upscale_method) >= 1, (\n                \"Expected at least one upscale method, \"\n                \"got %d.\" % (len(upscale_method),))\n            assert all([ia.is_string(val) for val in upscale_method]), (\n                \"Expected all upscale methods to be strings, got types %s.\" % (\n                    \", \".join([str(type(v)) for v in upscale_method])))\n            self.upscale_method = Choice(upscale_method)\n        elif isinstance(upscale_method, StochasticParameter):\n            self.upscale_method = upscale_method\n        else:\n            raise Exception(\n                \"Expected upscale_method to be string or list of strings or \"\n                \"StochasticParameter, got %s.\" % (type(upscale_method),))\n\n        self._distance_matrix_cache = np.zeros((0, 0), dtype=np.float32)\n\n    # TODO this is the same as in SimplexNoise, make DRY\n    def _draw_samples(self, size, random_state):\n        # code here is similar to:\n        #   http://www.redblobgames.com/articles/noise/2d/\n        #   http://www.redblobgames.com/articles/noise/2d/2d-noise.js\n\n        assert len(size) in [2, 3], (\n            \"Expected requested noise to have shape (H, W) or (H, W, C), \"\n            \"got shape %s.\" % (size,))\n        height, width = size[0:2]\n        nb_channels = 1 if len(size) == 2 else size[2]\n\n        channels = [self._draw_samples_hw(height, width, random_state)\n                    for _ in np.arange(nb_channels)]\n\n        if len(size) == 2:\n            return channels[0]\n        return np.stack(channels, axis=-1)\n\n    def _draw_samples_hw(self, height, width, random_state):\n        maxlen = max(height, width)\n        size_px_max = self.size_px_max.draw_sample(random_state=random_state)\n        h_small, w_small = height, width\n        if maxlen > size_px_max:\n            downscale_factor = size_px_max / maxlen\n            h_small = int(height * downscale_factor)\n            w_small = int(width * downscale_factor)\n\n        # don't go below Hx4 or 4xW\n        h_small = max(h_small, 4)\n        w_small = max(w_small, 4)\n\n        # exponents to pronounce some frequencies\n        exponent = self.exponent.draw_sample(random_state=random_state)\n\n        # base function to invert, derived from a distance matrix (euclidean\n        # distance to image center)\n        f = self._get_distance_matrix_cached((h_small, w_small))\n\n        # prevent divide by zero warnings at the image corners in\n        # f**exponent\n        f[0, 0] = 1\n        f[-1, 0] = 1\n        f[0, -1] = 1\n        f[-1, -1] = 1\n\n        scale = f ** exponent\n\n        # invert setting corners to 1\n        scale[0, 0] = 0\n        scale[-1, 0] = 0\n        scale[0, -1] = 0\n        scale[-1, -1] = 0\n\n        # generate random base matrix\n        # first channel: wn_r, second channel: wn_a\n        wn = random_state.random(size=(2, h_small, w_small))\n        wn[0, ...] *= (max(h_small, w_small) ** 2)\n        wn[1, ...] *= 2 * np.pi\n        wn[0, ...] *= np.cos(wn[1, ...])\n        wn[1, ...] *= np.sin(wn[1, ...])\n        wn *= scale[np.newaxis, :, :]\n        wn = wn.transpose((1, 2, 0))\n        if wn.dtype != np.float32:\n            wn = wn.astype(np.float32)\n\n        # equivalent but slightly faster then:\n        #   wn_freqs_mul = np.zeros(treal.shape, dtype=np.complex)\n        #   wn_freqs_mul.real = wn[0]\n        #   wn_freqs_mul.imag = wn[1]\n        #   wn_inv = np.fft.ifft2(wn_freqs_mul).real\n        wn_inv = cv2.idft(wn)[:, :, 0]\n\n        # normalize to 0 to 1\n        # equivalent to but slightly faster than:\n        #   wn_inv_min = np.min(wn_inv)\n        #   wn_inv_max = np.max(wn_inv)\n        #   noise_0to1 = (wn_inv - wn_inv_min) / (wn_inv_max - wn_inv_min)\n        # does not accept wn_inv as dst directly\n        noise_0to1 = cv2.normalize(\n            wn_inv,\n            dst=np.zeros_like(wn_inv),\n            alpha=0.01,\n            beta=1.0,\n            norm_type=cv2.NORM_MINMAX\n        )\n\n        # upscale from low resolution to image size\n        if noise_0to1.shape != (height, width):\n            upscale_method = self.upscale_method.draw_sample(\n                random_state=random_state\n            )\n            noise_0to1 = ia.imresize_single_image(\n                noise_0to1.astype(np.float32),\n                (height, width),\n                interpolation=upscale_method)\n            if upscale_method == \"cubic\":\n                noise_0to1 = np.clip(noise_0to1, 0.0, 1.0)\n\n        return noise_0to1\n\n    def _get_distance_matrix_cached(self, size):\n        cache = self._distance_matrix_cache\n        height, width = cache.shape\n        if height < size[0] or width < size[1]:\n            self._distance_matrix_cache = self._create_distance_matrix(\n                (max(height, size[0]), max(width, size[1]))\n            )\n\n        return self._extract_distance_matrix(self._distance_matrix_cache, size)\n\n    @classmethod\n    def _extract_distance_matrix(cls, matrix, size):\n        height, width = matrix.shape[0:2]\n        leftover_y = (height - size[0]) / 2\n        leftover_x = (width - size[1]) / 2\n        y1 = int(np.floor(leftover_y))\n        y2 = height - int(np.ceil(leftover_y))\n        x1 = int(np.floor(leftover_x))\n        x2 = width - int(np.ceil(leftover_x))\n        return matrix[y1:y2, x1:x2]\n\n    @classmethod\n    def _create_distance_matrix(cls, size):\n        def _create_line(line_size):\n            start = np.arange(line_size // 2)\n            middle = [line_size//2] if line_size % 2 == 1 else []\n            end = start[::-1]\n            return np.concatenate([start, middle, end])\n\n        height, width = size\n        ydist = _create_line(height) ** 2\n        xdist = _create_line(width) ** 2\n        ydist_2d = np.broadcast_to(ydist[:, np.newaxis], size)\n        xdist_2d = np.broadcast_to(xdist[np.newaxis, :], size)\n        dist = np.sqrt(ydist_2d + xdist_2d)\n        return dist\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __str__(self):\n        return \"FrequencyNoise(%s, %s, %s)\" % (\n            str(self.exponent),\n            str(self.size_px_max),\n            str(self.upscale_method))\n\n\ndef _assert_arg_is_stoch_param(arg_name, arg_value):\n    assert isinstance(arg_value, StochasticParameter), (\n        \"Expected '%s' to be a StochasticParameter, \"\n        \"got type %s.\" % (arg_name, arg_value,))\n"
  },
  {
    "path": "imgaug/quokka_annotations.json",
    "content": "{\n  \"bounding_boxes\": [\n    {\n      \"x1\": 148.0,\n      \"y1\": 50.0,\n      \"x2\": 550.0,\n      \"y2\": 642.0,\n      \"label\": \"animal\"\n    }\n  ],\n  \"keypoints\": [\n    {\n      \"x\": 163.0,\n      \"y\": 78.0,\n      \"label\": \"ear_tip_left\"\n    },\n    {\n      \"x\": 422.0,\n      \"y\": 56.0,\n      \"label\": \"ear_tip_right\"\n    },\n    {\n      \"x\": 248.0,\n      \"y\": 205.0,\n      \"label\": \"eye_left\"\n    },\n    {\n      \"x\": 366.0,\n      \"y\": 194.0,\n      \"label\": \"eye_right\"\n    },\n    {\n      \"x\": 313.0,\n      \"y\": 270.0,\n      \"label\": \"nose\"\n    },\n    {\n      \"x\": 244.0,\n      \"y\": 535.0,\n      \"label\": \"paw_left\"\n    },\n    {\n      \"x\": 290,\n      \"y\": 536,\n      \"label\": \"paw_right\"\n    }\n  ],\n  \"polygons\": [\n    {\n      \"keypoints\": [\n        {\"x\": 422.0, \"y\": 56.0},\n        {\"x\": 435.0, \"y\": 66.0},\n        {\"x\": 435.0, \"y\": 111.0},\n        {\"x\": 435.0, \"y\": 111.0},\n        {\"x\": 422.0, \"y\": 158.0},\n        {\"x\": 451.0, \"y\": 233.0},\n        {\"x\": 468.0, \"y\": 298.0},\n        {\"x\": 474.0, \"y\": 355.0},\n        {\"x\": 474.0, \"y\": 374.0},\n        {\"x\": 515.0, \"y\": 395.0},\n        {\"x\": 541.0, \"y\": 467.0},\n        {\"x\": 548.0, \"y\": 499.0},\n        {\"x\": 547.0, \"y\": 559.0},\n        {\"x\": 532.0, \"y\": 641.0},\n        {\"x\": 235.0, \"y\": 641.0},\n        {\"x\": 208.0, \"y\": 608.0},\n        {\"x\": 188.0, \"y\": 574.0},\n        {\"x\": 177.0, \"y\": 535.0},\n        {\"x\": 159.0, \"y\": 493.0},\n        {\"x\": 151.0, \"y\": 466.0},\n        {\"x\": 154.0, \"y\": 432.0},\n        {\"x\": 155.0, \"y\": 389.0},\n        {\"x\": 147.0, \"y\": 363.0},\n        {\"x\": 143.0, \"y\": 329.0},\n        {\"x\": 145.0, \"y\": 289.0},\n        {\"x\": 170.0, \"y\": 237.0},\n        {\"x\": 181.0, \"y\": 186.0},\n        {\"x\": 182.0, \"y\": 168.0},\n        {\"x\": 163.0, \"y\": 144.0},\n        {\"x\": 154.0, \"y\": 115.0},\n        {\"x\": 155.0, \"y\": 84.0},\n        {\"x\": 172.0, \"y\": 69.0},\n        {\"x\": 205.0, \"y\": 60.0},\n        {\"x\": 244.0, \"y\": 62.0},\n        {\"x\": 279.0, \"y\": 66.0},\n        {\"x\": 307.0, \"y\": 71.0},\n        {\"x\": 345.0, \"y\": 61.0},\n        {\"x\": 375.0, \"y\": 60.0},\n        {\"x\": 403.0, \"y\": 50.0}\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "imgaug/random.py",
    "content": "\"\"\"Classes and functions related to pseudo-random number generation.\n\nThis module deals with the generation of pseudo-random numbers.\nIt provides the :class:`~imgaug.random.RNG` class, which is the primary\nrandom number generator in ``imgaug``. It also provides various utility\nfunctions related random number generation, such as copying random number\ngenerators or setting their state.\n\nThe main benefit of this module is to hide the actually used random number\ngeneration classes and methods behin imgaug-specific classes and methods.\nThis allows to deal with numpy using two different interfaces (one old\ninterface in numpy <=1.16 and a new one in numpy 1.17+). It also allows\nto potentially switch to a different framework/library in the future.\n\nDefinitions\n-----------\n\n- *numpy generator* or *numpy random number generator*: Usually an instance\n  of :class:`numpy.random.Generator`. Can often also denote an instance\n  of :class:`numpy.random.RandomState` as both have almost the same interface.\n- *RandomState*: An instance of `numpy.random.RandomState`.\n  Note that outside of this module, the term \"random state\" often roughly\n  translates to \"any random number generator with numpy-like interface\n  in a given state\", i.e. it can then include instances of\n  :class:`numpy.random.Generator` or :class:`~imgaug.random.RNG`.\n- *RNG*: An instance of :class:`~imgaug.random.RNG`.\n\nExamples\n--------\n\n>>> import imgaug.random as iarandom\n>>> rng = iarandom.RNG(1234)\n>>> rng.integers(0, 1000)\n\nInitialize a random number generator with seed ``1234``, then sample\na single integer from the discrete interval ``[0, 1000)``.\nThis will use a :class:`numpy.random.Generator` in numpy 1.17+ and\nautomatically fall back to :class:`numpy.random.RandomState` in numpy <=1.16.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport copy as copylib\n\nimport numpy as np\nimport six.moves as sm\n\n\n# Check if numpy is version 1.17 or later. In that version, the new random\n# number interface was added.\n# Note that a valid version number can also be \"1.18.0.dev0+285ab1d\",\n# in which the last component cannot easily be converted to an int. Hence we\n# only pick the first two components.\nSUPPORTS_NEW_NP_RNG_STYLE = False\nBIT_GENERATOR = None\n_NP_VERSION = list(map(int, np.__version__.split(\".\")[0:2]))\nif _NP_VERSION[0] > 1 or _NP_VERSION[1] >= 17:\n    SUPPORTS_NEW_NP_RNG_STYLE = True\n    BIT_GENERATOR = np.random.SFC64  # pylint: disable=invalid-name\n\n    # interface of BitGenerator\n    # in 1.17 this was at numpy.random.bit_generator.BitGenerator\n    # in 1.18 this was moved to numpy.random.BitGenerator\n    # pylint: disable=invalid-name, no-member\n    if _NP_VERSION[1] == 17:\n        # Added in 0.4.0.\n        _BIT_GENERATOR_INTERFACE = np.random.bit_generator.BitGenerator\n    else:\n        # Added in 0.4.0.\n        _BIT_GENERATOR_INTERFACE = np.random.BitGenerator\n    # pylint: enable=invalid-name, no-member\n\n# We instantiate a current/global random state here once.\nGLOBAL_RNG = None\n\n# use 2**31 instead of 2**32 as the maximum here, because 2**31 errored on\n# some systems\nSEED_MIN_VALUE = 0\nSEED_MAX_VALUE = 2**31-1\n\n# Added in 0.5.0.\n_RNG_IDX = 1\n\n# TODO decrease pool_size in SeedSequence to 2 or 1?\n# TODO add 'with resetted_rng(...)'\n# TODO change random_state to rng or seed\n\n\nclass RNG(object):\n    \"\"\"\n    Random number generator for imgaug.\n\n    This class is a wrapper around ``numpy.random.Generator`` and\n    automatically falls back to ``numpy.random.RandomState`` in case of\n    numpy version 1.16 or lower. It allows to use numpy 1.17's sampling\n    functions in 1.16 too and supports a variety of useful functions on\n    the wrapped sampler, e.g. gettings its state or copying it.\n\n    Not supported sampling functions of numpy <=1.16:\n\n    * :func:`numpy.random.RandomState.rand`\n    * :func:`numpy.random.RandomState.randn`\n    * :func:`numpy.random.RandomState.randint`\n    * :func:`numpy.random.RandomState.random_integers`\n    * :func:`numpy.random.RandomState.random_sample`\n    * :func:`numpy.random.RandomState.ranf`\n    * :func:`numpy.random.RandomState.sample`\n    * :func:`numpy.random.RandomState.seed`\n    * :func:`numpy.random.RandomState.get_state`\n    * :func:`numpy.random.RandomState.set_state`\n\n    In :func:`~imgaug.random.RNG.choice`, the `axis` argument is not yet\n    supported.\n\n    Parameters\n    ----------\n    generator : None or int or RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState\n        The numpy random number generator to use. In case of numpy\n        version 1.17 or later, this shouldn't be a ``RandomState`` as that\n        class is outdated.\n        Behaviour for different datatypes:\n\n          * If ``None``: The global RNG is wrapped by this RNG (they are then\n            effectively identical, any sampling on this RNG will affect the\n            global RNG).\n          * If ``int``: In numpy 1.17+, the value is used as a seed for a\n            ``Generator`` wrapped by this RNG. I.e. it will be provided as the\n            entropy to a ``SeedSequence``, which will then be used for an\n            ``SFC64`` bit generator and wrapped by a ``Generator``.\n            In numpy <=1.16, the value is used as a seed for a ``RandomState``,\n            which is then wrapped by this RNG.\n          * If :class:`RNG`: That RNG's ``generator`` attribute will be used\n            as the generator for this RNG, i.e. the same as\n            ``RNG(other_rng.generator)``.\n          * If :class:`numpy.random.Generator`: That generator will be wrapped.\n          * If :class:`numpy.random.BitGenerator`: A numpy\n            generator will be created (and wrapped by this RNG) that contains\n            the bit generator.\n          * If :class:`numpy.random.SeedSequence`: A numpy\n            generator will be created (and wrapped by this RNG) that contains\n            an ``SFC64`` bit generator initialized with the given\n            ``SeedSequence``.\n          * If :class:`numpy.random.RandomState`: In numpy <=1.16, this\n            ``RandomState`` will be wrapped and used to sample random values.\n            In numpy 1.17+, a seed will be derived from this ``RandomState``\n            and a new ``numpy.generator.Generator`` based on an ``SFC64``\n            bit generator will be created and wrapped by this RNG.\n\n    \"\"\"\n\n    # TODO add maybe a __new__ here that feeds-through an RNG input without\n    #      wrapping it in RNG(rng_input)?\n    def __init__(self, generator):\n        # pylint: disable=protected-access, global-statement\n        global _RNG_IDX\n\n        if isinstance(generator, RNG):\n            self.generator = generator.generator\n        else:\n            self.generator = normalize_generator_(generator)\n        self._is_new_rng_style = (\n            not isinstance(self.generator, np.random.RandomState))\n\n        # _idx is used to have a unique id for each RNG.\n        # This is currently necessary for AutoPrefetcher. It could be done\n        # similarly via the generator state, though at a much higher\n        # computational cost. id(rng) cannot be used for this as multiple\n        # RNG instances with different states may have the same id() value.\n        self._idx = _RNG_IDX\n        _RNG_IDX += 1\n\n    @property\n    def state(self):\n        \"\"\"Get the state of this RNG.\n\n        Returns\n        -------\n        tuple or dict\n            The state of the RNG.\n            In numpy 1.17+, the bit generator's state will be returned.\n            In numpy <=1.16, the ``RandomState`` 's state is returned.\n            In both cases the state is a copy. In-place changes will not affect\n            the RNG.\n\n        \"\"\"\n        return get_generator_state(self.generator)\n\n    @state.setter\n    def state(self, value):\n        \"\"\"Set the state if the RNG in-place.\n\n        Parameters\n        ----------\n        value : tuple or dict\n            The new state of the RNG.\n            Should correspond to the output of the ``state`` property.\n\n        \"\"\"\n        self.set_state_(value)\n\n    def set_state_(self, value):\n        \"\"\"Set the state if the RNG in-place.\n\n        Parameters\n        ----------\n        value : tuple or dict\n            The new state of the RNG.\n            Should correspond to the output of the ``state`` property.\n\n        Returns\n        -------\n        RNG\n            The RNG itself.\n\n        \"\"\"\n        set_generator_state_(self.generator, value)\n        return self\n\n    def use_state_of_(self, other):\n        \"\"\"Copy and use (in-place) the state of another RNG.\n\n        .. note::\n\n            It is often sensible to first verify that neither this RNG nor\n            `other` are identical to the global RNG.\n\n        Parameters\n        ----------\n        other : RNG\n            The other RNG, which's state will be copied.\n\n        Returns\n        -------\n        RNG\n            The RNG itself.\n\n        \"\"\"\n        return self.set_state_(other.state)\n\n    def is_global_rng(self):\n        \"\"\"Estimate whether this RNG is identical to the global RNG.\n\n        Returns\n        -------\n        bool\n            ``True`` is this RNG's underlying generator is identical to the\n            global RNG's underlying generator. The RNGs themselves may\n            be different, only the wrapped generator matters.\n            ``False`` otherwise.\n\n        \"\"\"\n        # We use .generator here, because otherwise RNG(global_rng) would be\n        # viewed as not-identical to the global RNG, even though its generator\n        # and bit generator are identical.\n        return get_global_rng().generator is self.generator\n\n    def equals_global_rng(self):\n        \"\"\"Estimate whether this RNG has the same state as the global RNG.\n\n        Returns\n        -------\n        bool\n            ``True`` is this RNG has the same state as the global RNG, i.e.\n            it will lead to the same sampled values given the same sampling\n            method calls. The RNGs *don't* have to be identical object\n            instances, which protects against e.g. copy effects.\n            ``False`` otherwise.\n\n        \"\"\"\n        return get_global_rng().equals(self)\n\n    def generate_seed_(self):\n        \"\"\"Sample a random seed.\n\n        This advances the underlying generator's state.\n\n        See ``SEED_MIN_VALUE`` and ``SEED_MAX_VALUE`` for the seed's value\n        range.\n\n        Returns\n        -------\n        int\n            The sampled seed.\n\n        \"\"\"\n        return generate_seed_(self.generator)\n\n    def generate_seeds_(self, n):\n        \"\"\"Generate `n` random seed values.\n\n        This advances the underlying generator's state.\n\n        See ``SEED_MIN_VALUE`` and ``SEED_MAX_VALUE`` for the seed's value\n        range.\n\n        Parameters\n        ----------\n        n : int\n            Number of seeds to sample.\n\n        Returns\n        -------\n        ndarray\n            1D-array of ``int32`` seeds.\n\n        \"\"\"\n        return generate_seeds_(self.generator, n)\n\n    def reset_cache_(self):\n        \"\"\"Reset all cache of this RNG.\n\n        Returns\n        -------\n        RNG\n            The RNG itself.\n\n        \"\"\"\n        reset_generator_cache_(self.generator)\n        return self\n\n    def derive_rng_(self):\n        \"\"\"Create a child RNG.\n\n        This advances the underlying generator's state.\n\n        Returns\n        -------\n        RNG\n            A child RNG.\n\n        \"\"\"\n        return self.derive_rngs_(1)[0]\n\n    def derive_rngs_(self, n):\n        \"\"\"Create `n` child RNGs.\n\n        This advances the underlying generator's state.\n\n        Parameters\n        ----------\n        n : int\n            Number of child RNGs to derive.\n\n        Returns\n        -------\n        list of RNG\n            Child RNGs.\n\n        \"\"\"\n        return [RNG(gen) for gen in derive_generators_(self.generator, n)]\n\n    def equals(self, other):\n        \"\"\"Estimate whether this RNG and `other` have the same state.\n\n        Returns\n        -------\n        bool\n            ``True`` if this RNG's generator and the generator of `other`\n            have equal internal states. ``False`` otherwise.\n\n        \"\"\"\n        assert isinstance(other, RNG), (\n            \"Expected 'other' to be an RNG, got type %s. \"\n            \"Use imgaug.random.is_generator_equal_to() to compare \"\n            \"numpy generators or RandomStates.\" % (type(other),))\n        return is_generator_equal_to(self.generator, other.generator)\n\n    def advance_(self):\n        \"\"\"Advance the RNG's internal state in-place by one step.\n\n        This advances the underlying generator's state.\n\n        .. note::\n\n            This simply samples one or more random values. This means that\n            a call of this method will not completely change the outputs of\n            the next called sampling method. To achieve more drastic output\n            changes, call :func:`~imgaug.random.RNG.derive_rng_`.\n\n        Returns\n        -------\n        RNG\n            The RNG itself.\n\n        \"\"\"\n        advance_generator_(self.generator)\n        return self\n\n    def copy(self):\n        \"\"\"Create a copy of this RNG.\n\n        Returns\n        -------\n        RNG\n            Copy of this RNG. The copy will produce the same random samples.\n\n        \"\"\"\n        return RNG(copy_generator(self.generator))\n\n    def copy_unless_global_rng(self):\n        \"\"\"Create a copy of this RNG unless it is the global RNG.\n\n        Returns\n        -------\n        RNG\n            Copy of this RNG unless it is the global RNG. In the latter case\n            the RNG instance itself will be returned without any changes.\n\n        \"\"\"\n        if self.is_global_rng():\n            return self\n        return self.copy()\n\n    def duplicate(self, n):\n        \"\"\"Create a list containing `n` times this RNG.\n\n        This method was mainly introduced as a replacement for previous\n        calls of :func:`~imgaug.random.RNG.derive_rngs_`. These calls\n        turned out to be very slow in numpy 1.17+ and were hence replaced\n        by simple duplication (except for the cases where child RNGs\n        absolutely *had* to be created).\n        This RNG duplication method doesn't help very much against code\n        repetition, but it does *mark* the points where it would be desirable\n        to create child RNGs for various reasons. Once deriving child RNGs\n        is somehow sped up in the future, these calls can again be\n        easily found and replaced.\n\n        Parameters\n        ----------\n        n : int\n            Length of the output list.\n\n        Returns\n        -------\n        list of RNG\n            List containing `n` times this RNG (same instances, no copies).\n\n        \"\"\"\n        return [self for _ in sm.xrange(n)]\n\n    @classmethod\n    def create_fully_random(cls):\n        \"\"\"Create a new RNG, based on entropy provided from the OS.\n\n        Returns\n        -------\n        RNG\n            A new RNG. It is not derived from any other previously created\n            RNG, nor does it depend on the seeding of imgaug or numpy.\n\n        \"\"\"\n        return RNG(create_fully_random_generator())\n\n    @classmethod\n    def create_pseudo_random_(cls):\n        \"\"\"Create a new RNG in pseudo-random fashion.\n\n        A seed will be sampled from the current global RNG and used to\n        initialize the new RNG.\n\n        This advandes the global RNG's state.\n\n        Returns\n        -------\n        RNG\n            A new RNG, derived from the current global RNG.\n\n        \"\"\"\n        return get_global_rng().derive_rng_()\n\n    @classmethod\n    def create_if_not_rng_(cls, generator):\n        \"\"\"Create a new RNG from any input, but feed-through RNGs unchanged.\n\n        Added in 0.5.0.\n\n        Parameters\n        ----------\n        generator : None or int or RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState\n            The numpy random number generator to use.\n            If this is an :class:`RNG`, it will be returned without change.\n            See ``__init__`` for details.\n\n        Returns\n        -------\n        RNG\n            If an :class:`RNG` was provided, then the input :class:`RNG`\n            instance without any change. Otherwise equivalent to\n            ``RNG(inputs)``.\n\n        \"\"\"\n        if isinstance(generator, RNG):\n            return generator\n        return RNG(generator)\n\n    ###########################################################################\n    # Below:\n    #   Aliases for methods of numpy.random.Generator functions\n    #\n    # The methods below could also be handled with less code using some magic\n    # methods. Explicitly writing things down here has the advantage that\n    # the methods actually appear in the autogenerated API.\n    ###########################################################################\n\n    def integers(self, low, high=None, size=None, dtype=\"int32\",\n                 endpoint=False):\n        \"\"\"Call numpy's ``integers()`` or ``randint()``.\n\n        .. note::\n\n            Changed `dtype` argument default value from numpy's ``int64`` to\n            ``int32``.\n\n        \"\"\"\n        return polyfill_integers(\n            self.generator, low=low, high=high, size=size, dtype=dtype,\n            endpoint=endpoint)\n\n    def random(self, size, dtype=\"float32\", out=None):\n        \"\"\"Call numpy's ``random()`` or ``random_sample()``.\n\n        .. note::\n\n            Changed `dtype` argument default value from numpy's ``d`` to\n            ``float32``.\n\n        \"\"\"\n        return polyfill_random(\n            self.generator, size=size, dtype=dtype, out=out)\n\n    # TODO add support for Generator's 'axis' argument\n    def choice(self, a, size=None, replace=True, p=None):\n        \"\"\"Call :func:`numpy.random.Generator.choice`.\"\"\"\n        # pylint: disable=invalid-name\n        return self.generator.choice(a=a, size=size, replace=replace, p=p)\n\n    def bytes(self, length):\n        \"\"\"Call :func:`numpy.random.Generator.bytes`.\"\"\"\n        return self.generator.bytes(length=length)\n\n    # TODO mark in-place\n    def shuffle(self, x):\n        \"\"\"Call :func:`numpy.random.Generator.shuffle`.\"\"\"\n        # note that shuffle() does not allow keyword arguments\n        # note that shuffle() works in-place\n        self.generator.shuffle(x)\n\n    def permutation(self, x):\n        \"\"\"Call :func:`numpy.random.Generator.permutation`.\"\"\"\n        # note that permutation() does not allow keyword arguments\n        return self.generator.permutation(x)\n\n    def beta(self, a, b, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.beta`.\"\"\"\n        # pylint: disable=invalid-name\n        return self.generator.beta(a=a, b=b, size=size)\n\n    def binomial(self, n, p, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.binomial`.\"\"\"\n        return self.generator.binomial(n=n, p=p, size=size)\n\n    def chisquare(self, df, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.chisquare`.\"\"\"\n        # pylint: disable=invalid-name\n        return self.generator.chisquare(df=df, size=size)\n\n    def dirichlet(self, alpha, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.dirichlet`.\"\"\"\n        return self.generator.dirichlet(alpha=alpha, size=size)\n\n    def exponential(self, scale=1.0, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.exponential`.\"\"\"\n        return self.generator.exponential(scale=scale, size=size)\n\n    def f(self, dfnum, dfden, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.f`.\"\"\"\n        return self.generator.f(dfnum=dfnum, dfden=dfden, size=size)\n\n    def gamma(self, shape, scale=1.0, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.gamma`.\"\"\"\n        return self.generator.gamma(shape=shape, scale=scale, size=size)\n\n    def geometric(self, p, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.geometric`.\"\"\"\n        return self.generator.geometric(p=p, size=size)\n\n    def gumbel(self, loc=0.0, scale=1.0, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.gumbel`.\"\"\"\n        return self.generator.gumbel(loc=loc, scale=scale, size=size)\n\n    def hypergeometric(self, ngood, nbad, nsample, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.hypergeometric`.\"\"\"\n        return self.generator.hypergeometric(\n            ngood=ngood, nbad=nbad, nsample=nsample, size=size)\n\n    def laplace(self, loc=0.0, scale=1.0, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.laplace`.\"\"\"\n        return self.generator.laplace(loc=loc, scale=scale, size=size)\n\n    def logistic(self, loc=0.0, scale=1.0, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.logistic`.\"\"\"\n        return self.generator.logistic(loc=loc, scale=scale, size=size)\n\n    def lognormal(self, mean=0.0, sigma=1.0, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.lognormal`.\"\"\"\n        return self.generator.lognormal(mean=mean, sigma=sigma, size=size)\n\n    def logseries(self, p, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.logseries`.\"\"\"\n        return self.generator.logseries(p=p, size=size)\n\n    def multinomial(self, n, pvals, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.multinomial`.\"\"\"\n        return self.generator.multinomial(n=n, pvals=pvals, size=size)\n\n    def multivariate_normal(self, mean, cov, size=None, check_valid=\"warn\",\n                            tol=1e-8):\n        \"\"\"Call :func:`numpy.random.Generator.multivariate_normal`.\"\"\"\n        return self.generator.multivariate_normal(\n            mean=mean, cov=cov, size=size, check_valid=check_valid, tol=tol)\n\n    def negative_binomial(self, n, p, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.negative_binomial`.\"\"\"\n        return self.generator.negative_binomial(n=n, p=p, size=size)\n\n    def noncentral_chisquare(self, df, nonc, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.noncentral_chisquare`.\"\"\"\n        # pylint: disable=invalid-name\n        return self.generator.noncentral_chisquare(df=df, nonc=nonc, size=size)\n\n    def noncentral_f(self, dfnum, dfden, nonc, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.noncentral_f`.\"\"\"\n        return self.generator.noncentral_f(\n            dfnum=dfnum, dfden=dfden, nonc=nonc, size=size)\n\n    def normal(self, loc=0.0, scale=1.0, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.normal`.\"\"\"\n        return self.generator.normal(loc=loc, scale=scale, size=size)\n\n    def pareto(self, a, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.pareto`.\"\"\"\n        # pylint: disable=invalid-name\n        return self.generator.pareto(a=a, size=size)\n\n    def poisson(self, lam=1.0, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.poisson`.\"\"\"\n        return self.generator.poisson(lam=lam, size=size)\n\n    def power(self, a, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.power`.\"\"\"\n        # pylint: disable=invalid-name\n        return self.generator.power(a=a, size=size)\n\n    def rayleigh(self, scale=1.0, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.rayleigh`.\"\"\"\n        return self.generator.rayleigh(scale=scale, size=size)\n\n    def standard_cauchy(self, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.standard_cauchy`.\"\"\"\n        return self.generator.standard_cauchy(size=size)\n\n    def standard_exponential(self, size=None, dtype=\"float32\", method=\"zig\",\n                             out=None):\n        \"\"\"Call :func:`numpy.random.Generator.standard_exponential`.\n\n        .. note::\n\n            Changed `dtype` argument default value from numpy's ``d`` to\n            ``float32``.\n\n        \"\"\"\n        if self._is_new_rng_style:\n            return self.generator.standard_exponential(\n                size=size, dtype=dtype, method=method, out=out)\n        result = self.generator.standard_exponential(size=size).astype(dtype)\n        if out is not None:\n            assert out.dtype.name == result.dtype.name, (\n                \"Expected out array to have the same dtype as \"\n                \"standard_exponential()'s result array. Got %s (out) and \"\n                \"%s (result) instead.\" % (out.dtype.name, result.dtype.name))\n            out[...] = result\n        return result\n\n    def standard_gamma(self, shape, size=None, dtype=\"float32\", out=None):\n        \"\"\"Call :func:`numpy.random.Generator.standard_gamma`.\n\n        .. note::\n\n            Changed `dtype` argument default value from numpy's ``d`` to\n            ``float32``.\n\n        \"\"\"\n        if self._is_new_rng_style:\n            return self.generator.standard_gamma(\n                shape=shape, size=size, dtype=dtype, out=out)\n        result = self.generator.standard_gamma(\n            shape=shape, size=size).astype(dtype)\n        if out is not None:\n            assert out.dtype.name == result.dtype.name, (\n                \"Expected out array to have the same dtype as \"\n                \"standard_gamma()'s result array. Got %s (out) and \"\n                \"%s (result) instead.\" % (out.dtype.name, result.dtype.name))\n            out[...] = result\n        return result\n\n    def standard_normal(self, size=None, dtype=\"float32\", out=None):\n        \"\"\"Call :func:`numpy.random.Generator.standard_normal`.\n\n        .. note::\n\n            Changed `dtype` argument default value from numpy's ``d`` to\n            ``float32``.\n\n        \"\"\"\n        if self._is_new_rng_style:\n            return self.generator.standard_normal(\n                size=size, dtype=dtype, out=out)\n        result = self.generator.standard_normal(size=size).astype(dtype)\n        if out is not None:\n            assert out.dtype.name == result.dtype.name, (\n                \"Expected out array to have the same dtype as \"\n                \"standard_normal()'s result array. Got %s (out) and \"\n                \"%s (result) instead.\" % (out.dtype.name, result.dtype.name))\n            out[...] = result\n        return result\n\n    def standard_t(self, df, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.standard_t`.\"\"\"\n        # pylint: disable=invalid-name\n        return self.generator.standard_t(df=df, size=size)\n\n    def triangular(self, left, mode, right, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.triangular`.\"\"\"\n        return self.generator.triangular(\n            left=left, mode=mode, right=right, size=size)\n\n    def uniform(self, low=0.0, high=1.0, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.uniform`.\"\"\"\n        return self.generator.uniform(low=low, high=high, size=size)\n\n    def vonmises(self, mu, kappa, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.vonmises`.\"\"\"\n        # pylint: disable=invalid-name\n        return self.generator.vonmises(mu=mu, kappa=kappa, size=size)\n\n    def wald(self, mean, scale, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.wald`.\"\"\"\n        return self.generator.wald(mean=mean, scale=scale, size=size)\n\n    def weibull(self, a, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.weibull`.\"\"\"\n        # pylint: disable=invalid-name\n        return self.generator.weibull(a=a, size=size)\n\n    def zipf(self, a, size=None):\n        \"\"\"Call :func:`numpy.random.Generator.zipf`.\"\"\"\n        # pylint: disable=invalid-name\n        return self.generator.zipf(a=a, size=size)\n\n    ##################################################################\n    # Outdated methods from RandomState\n    # These are added here for backwards compatibility in case of old\n    # custom augmenters and Lambda calls that rely on the RandomState\n    # API.\n    ##################################################################\n\n    def rand(self, *args):\n        \"\"\"Call :func:`numpy.random.RandomState.rand`.\n\n        .. warning::\n\n            This method is outdated in numpy. Use :func:`RNG.random` instead.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        return self.random(size=args)\n\n    def randint(self, low, high=None, size=None, dtype=\"int32\"):\n        \"\"\"Call :func:`numpy.random.RandomState.randint`.\n\n        .. note::\n\n            Changed `dtype` argument default value from numpy's ``I`` to\n            ``int32``.\n\n        .. warning::\n\n            This method is outdated in numpy. Use :func:`RNG.integers`\n            instead.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        return self.integers(low=low, high=high, size=size, dtype=dtype,\n                             endpoint=False)\n\n    def randn(self, *args):\n        \"\"\"Call :func:`numpy.random.RandomState.randn`.\n\n        .. warning::\n\n            This method is outdated in numpy. Use :func:`RNG.standard_normal`\n            instead.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        return self.standard_normal(size=args)\n\n    def random_integers(self, low, high=None, size=None):\n        \"\"\"Call :func:`numpy.random.RandomState.random_integers`.\n\n        .. warning::\n\n            This method is outdated in numpy. Use :func:`RNG.integers`\n            instead.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        if high is None:\n            return self.integers(low=1, high=low, size=size, endpoint=True)\n        return self.integers(low=low, high=high, size=size, endpoint=True)\n\n    def random_sample(self, size):\n        \"\"\"Call :func:`numpy.random.RandomState.random_sample`.\n\n        .. warning::\n\n            This method is outdated in numpy. Use :func:`RNG.uniform`\n            instead.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        return self.uniform(0.0, 1.0, size=size)\n\n    def tomaxint(self, size=None):\n        \"\"\"Call :func:`numpy.random.RandomState.tomaxint`.\n\n        .. warning::\n\n            This method is outdated in numpy. Use :func:`RNG.integers`\n            instead.\n\n        Added in 0.4.0.\n\n        \"\"\"\n        import sys\n        maxint = sys.maxsize\n        int32max = np.iinfo(np.int32).max\n        return self.integers(0, min(maxint, int32max), size=size,\n                             endpoint=True)\n\n\ndef supports_new_numpy_rng_style():\n    \"\"\"\n    Determine whether numpy supports the new ``random`` interface (v1.17+).\n\n    Returns\n    -------\n    bool\n        ``True`` if the new ``random`` interface is supported by numpy, i.e.\n        if numpy has version 1.17 or later. Otherwise ``False``, i.e.\n        numpy has version 1.16 or older and ``numpy.random.RandomState``\n        should be used instead.\n\n    \"\"\"\n    return SUPPORTS_NEW_NP_RNG_STYLE\n\n\ndef get_global_rng():\n    \"\"\"\n    Get or create the current global RNG of imgaug.\n\n    Note that the first call to this function will create a global RNG.\n\n    Returns\n    -------\n    RNG\n        The global RNG to use.\n\n    \"\"\"\n    # TODO change global_rng to singleton\n    # pylint: disable=global-statement, redefined-outer-name\n    global GLOBAL_RNG\n    if GLOBAL_RNG is None:\n        # This uses numpy's random state to sample a seed.\n        # Alternatively, `secrets.randbits(n_bits)` (3.6+) and\n        # `os.urandom(n_bytes)` could be used.\n        # See https://stackoverflow.com/a/27286733/3760780\n        # for an explanation how random.seed() picks a random seed value.\n        seed = generate_seed_(np.random)\n\n        GLOBAL_RNG = RNG(convert_seed_to_generator(seed))\n    return GLOBAL_RNG\n\n\n# This is an in-place operation, but does not use a trailing slash to indicate\n# that in order to match the interface of `random` and `numpy.random`.\ndef seed(entropy):\n    \"\"\"Set the seed of imgaug's global RNG (in-place).\n\n    The global RNG controls most of the \"randomness\" in imgaug.\n\n    The global RNG is the default one used by all augmenters. Under special\n    circumstances (e.g. when an augmenter is switched to deterministic mode),\n    the global RNG is replaced with a local one. The state of that replacement\n    may be dependent on the global RNG's state at the time of creating the\n    child RNG.\n\n    Parameters\n    ----------\n    entropy : int\n        The seed value to use.\n\n    \"\"\"\n    if SUPPORTS_NEW_NP_RNG_STYLE:\n        _seed_np117_(entropy)\n    else:\n        _seed_np116_(entropy)\n\n\ndef _seed_np117_(entropy):\n    # We can't easily seed a BitGenerator in-place, nor can we easily modify\n    # a Generator's bit_generator in-place. So instead we create a new\n    # bit generator and set the current global RNG's internal bit generator\n    # state to a copy of the new bit generator's state.\n    get_global_rng().state = BIT_GENERATOR(entropy).state\n\n\ndef _seed_np116_(entropy):\n    get_global_rng().generator.seed(entropy)\n\n\ndef normalize_generator(generator):\n    \"\"\"Normalize various inputs to a numpy (random number) generator.\n\n    This function will first copy the provided argument, i.e. it never returns\n    a provided instance itself.\n\n    Parameters\n    ----------\n    generator : None or int or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState\n        The numpy random number generator to normalize. In case of numpy\n        version 1.17 or later, this shouldn't be a ``RandomState`` as that\n        class is outdated.\n        Behaviour for different datatypes:\n\n          * If ``None``: The global RNG's generator is returned.\n          * If ``int``: In numpy 1.17+, the value is used as a seed for a\n            ``Generator``, i.e. it will be provided as the entropy to a\n            ``SeedSequence``, which will then be used for an ``SFC64`` bit\n            generator and wrapped by a ``Generator``, which is then returned.\n            In numpy <=1.16, the value is used as a seed for a ``RandomState``,\n            which will then be returned.\n          * If :class:`numpy.random.Generator`: That generator will be\n            returned.\n          * If :class:`numpy.random.BitGenerator`: A numpy\n            generator will be created and returned that contains the bit\n            generator.\n          * If :class:`numpy.random.SeedSequence`: A numpy\n            generator will be created and returned that contains an ``SFC64``\n            bit generator initialized with the given ``SeedSequence``.\n          * If :class:`numpy.random.RandomState`: In numpy <=1.16, this\n            ``RandomState`` will be returned. In numpy 1.17+, a seed will be\n            derived from this ``RandomState`` and a new\n            ``numpy.generator.Generator`` based on an ``SFC64`` bit generator\n            will be created and returned.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator`` (even if\n        the input was a ``RandomState``).\n\n    \"\"\"\n    return normalize_generator_(copylib.deepcopy(generator))\n\n\ndef normalize_generator_(generator):\n    \"\"\"Normalize in-place various inputs to a numpy (random number) generator.\n\n    This function will try to return the provided instance itself.\n\n    Parameters\n    ----------\n    generator : None or int or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState\n        See :func:`~imgaug.random.normalize_generator`.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator`` (even if\n        the input was a ``RandomState``).\n\n    \"\"\"\n    if not SUPPORTS_NEW_NP_RNG_STYLE:\n        return _normalize_generator_np116_(generator)\n    return _normalize_generator_np117_(generator)\n\n\ndef _normalize_generator_np117_(generator):\n    if generator is None:\n        return get_global_rng().generator\n\n    if isinstance(generator, np.random.SeedSequence):\n        return np.random.Generator(\n            BIT_GENERATOR(generator)\n        )\n\n    if isinstance(generator, _BIT_GENERATOR_INTERFACE):\n        generator = np.random.Generator(generator)\n        # TODO is it necessary/sensible here to reset the cache?\n        reset_generator_cache_(generator)\n        return generator\n\n    if isinstance(generator, np.random.Generator):\n        # TODO is it necessary/sensible here to reset the cache?\n        reset_generator_cache_(generator)\n        return generator\n\n    if isinstance(generator, np.random.RandomState):\n        # TODO warn\n        # TODO reset the cache here too?\n        return convert_seed_to_generator(generate_seed_(generator))\n\n    # seed given\n    seed_ = generator\n    return convert_seed_to_generator(seed_)\n\n\ndef _normalize_generator_np116_(random_state):\n    if random_state is None:\n        return get_global_rng().generator\n    if isinstance(random_state, np.random.RandomState):\n        # TODO reset the cache here, like in np117?\n        return random_state\n    # seed given\n    seed_ = random_state\n    return convert_seed_to_generator(seed_)\n\n\ndef convert_seed_to_generator(entropy):\n    \"\"\"Convert a seed value to a numpy (random number) generator.\n\n    Parameters\n    ----------\n    entropy : int\n        The seed value to use.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator``.\n        Both are initialized with the provided seed.\n\n    \"\"\"\n    if not SUPPORTS_NEW_NP_RNG_STYLE:\n        return _convert_seed_to_generator_np116(entropy)\n    return _convert_seed_to_generator_np117(entropy)\n\n\ndef _convert_seed_to_generator_np117(entropy):\n    seed_sequence = np.random.SeedSequence(entropy)\n    return convert_seed_sequence_to_generator(seed_sequence)\n\n\ndef _convert_seed_to_generator_np116(entropy):\n    return np.random.RandomState(entropy)\n\n\ndef convert_seed_sequence_to_generator(seed_sequence):\n    \"\"\"Convert a seed sequence to a numpy (random number) generator.\n\n    Parameters\n    ----------\n    seed_sequence : numpy.random.SeedSequence\n        The seed value to use.\n\n    Returns\n    -------\n    numpy.random.Generator\n        Generator initialized with the provided seed sequence.\n\n    \"\"\"\n    return np.random.Generator(BIT_GENERATOR(seed_sequence))\n\n\ndef create_pseudo_random_generator_():\n    \"\"\"Create a new numpy (random) generator, derived from the global RNG.\n\n    This function advances the global RNG's state.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator``.\n        Both are initialized with a seed sampled from the global RNG.\n\n    \"\"\"\n    # could also use derive_rng(get_global_rng()) here\n    random_seed = generate_seed_(get_global_rng().generator)\n    return convert_seed_to_generator(random_seed)\n\n\ndef create_fully_random_generator():\n    \"\"\"Create a new numpy (random) generator, derived from OS's entropy.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator``.\n        Both are initialized with entropy requested from the OS. They are\n        hence independent of entered seeds or the library's global RNG.\n\n    \"\"\"\n    if not SUPPORTS_NEW_NP_RNG_STYLE:\n        return _create_fully_random_generator_np116()\n    return _create_fully_random_generator_np117()\n\n\ndef _create_fully_random_generator_np117():\n    # TODO need entropy here?\n    return np.random.Generator(np.random.SFC64())\n\n\ndef _create_fully_random_generator_np116():\n    return np.random.RandomState()\n\n\ndef generate_seed_(generator):\n    \"\"\"Sample a seed from the provided generator.\n\n    This function advances the generator's state.\n\n    See ``SEED_MIN_VALUE`` and ``SEED_MAX_VALUE`` for the seed's value\n    range.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        The generator from which to sample the seed.\n\n    Returns\n    -------\n    int\n        The sampled seed.\n\n    \"\"\"\n    return generate_seeds_(generator, 1)[0]\n\n\ndef generate_seeds_(generator, n):\n    \"\"\"Sample `n` seeds from the provided generator.\n\n    This function advances the generator's state.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        The generator from which to sample the seed.\n\n    n : int\n        Number of seeds to sample.\n\n    Returns\n    -------\n    ndarray\n        1D-array of ``int32`` seeds.\n\n    \"\"\"\n    return polyfill_integers(generator, SEED_MIN_VALUE, SEED_MAX_VALUE,\n                             size=(n,))\n\n\ndef copy_generator(generator):\n    \"\"\"Copy an existing numpy (random number) generator.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        The generator to copy.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator``.\n        Both are copies of the input argument.\n\n    \"\"\"\n    if isinstance(generator, np.random.RandomState):\n        return _copy_generator_np116(generator)\n    return _copy_generator_np117(generator)\n\n\ndef _copy_generator_np117(generator):\n    # TODO not sure if it is enough to only copy the state\n    # TODO initializing a bit gen and then copying the state might be slower\n    #      then just deepcopying the whole thing\n    old_bit_gen = generator.bit_generator\n    new_bit_gen = old_bit_gen.__class__(1)\n    new_bit_gen.state = copylib.deepcopy(old_bit_gen.state)\n    return np.random.Generator(new_bit_gen)\n\n\ndef _copy_generator_np116(random_state):\n    rs_copy = np.random.RandomState(1)\n    state = random_state.get_state()\n    rs_copy.set_state(state)\n    return rs_copy\n\n\ndef copy_generator_unless_global_generator(generator):\n    \"\"\"Copy a numpy generator unless it is the current global generator.\n\n    \"global generator\" here denotes the generator contained in the\n    global RNG's ``.generator`` attribute.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        The generator to copy.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator``.\n        Both are copies of the input argument, unless that input is\n        identical to the global generator. If it is identical, the\n        instance itself will be returned without copying it.\n\n    \"\"\"\n    if generator is get_global_rng().generator:\n        return generator\n    return copy_generator(generator)\n\n\ndef reset_generator_cache_(generator):\n    \"\"\"Reset a numpy (random number) generator's internal cache.\n\n    This function modifies the generator's state in-place.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        The generator of which to reset the cache.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator``.\n        In both cases the input argument itself.\n\n    \"\"\"\n    if isinstance(generator, np.random.RandomState):\n        return _reset_generator_cache_np116_(generator)\n    return _reset_generator_cache_np117_(generator)\n\n\ndef _reset_generator_cache_np117_(generator):\n    # This deactivates usage of the cache. We could also remove the cached\n    # value itself in \"uinteger\", but setting the RNG to ignore the cached\n    # value should be enough.\n    state = _get_generator_state_np117(generator)\n    state[\"has_uint32\"] = 0\n    _set_generator_state_np117_(generator, state)\n    return generator\n\n\ndef _reset_generator_cache_np116_(random_state):\n    # State tuple content:\n    #   'MT19937', array of ints, unknown int, cache flag, cached value\n    # The cache flag only affects the standard_normal() method.\n    state = list(random_state.get_state())\n    state[-2] = 0\n    random_state.set_state(tuple(state))\n    return random_state\n\n\ndef derive_generator_(generator):\n    \"\"\"Create a child numpy (random number) generator from an existing one.\n\n    This advances the generator's state.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        The generator from which to derive a new child generator.\n\n    Returns\n    -------\n    numpy.random.Generator or numpy.random.RandomState\n        In numpy <=1.16 a ``RandomState``, in 1.17+ a ``Generator``.\n        In both cases a derived child generator.\n\n    \"\"\"\n    return derive_generators_(generator, n=1)[0]\n\n\n# TODO does this advance the RNG in 1.17? It should advance it for security\n#      reasons\ndef derive_generators_(generator, n):\n    \"\"\"Create child numpy (random number) generators from an existing one.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        The generator from which to derive new child generators.\n\n    n : int\n        Number of child generators to derive.\n\n    Returns\n    -------\n    list of numpy.random.Generator or list of numpy.random.RandomState\n        In numpy <=1.16 a list of  ``RandomState`` s,\n        in 1.17+ a list of ``Generator`` s.\n        In both cases lists of derived child generators.\n\n    \"\"\"\n    if isinstance(generator, np.random.RandomState):\n        return _derive_generators_np116_(generator, n=n)\n    return _derive_generators_np117_(generator, n=n)\n\n\ndef _derive_generators_np117_(generator, n):\n    # TODO possible to get the SeedSequence from 'rng'?\n    \"\"\"\n    advance_rng_(rng)\n    rng = copylib.deepcopy(rng)\n    reset_rng_cache_(rng)\n    state = rng.bit_generator.state\n    rngs = []\n    for i in sm.xrange(n):\n        state[\"state\"][\"state\"] += (i * 100003 + 17)\n        rng.bit_generator.state = state\n        rngs.append(rng)\n        rng = copylib.deepcopy(rng)\n    return rngs\n    \"\"\"\n\n    # We generate here two integers instead of one, because the internal state\n    # of the RNG might have one 32bit integer still cached up, which would\n    # then be returned first when calling integers(). This should usually be\n    # fine, but there is some risk involved that this will lead to sampling\n    # many times the same seed in loop constructions (if the internal state\n    # is not properly advanced and the cache is then also not reset). Adding\n    # 'size=(2,)' decreases that risk. (It is then enough to e.g. call once\n    # random() to advance the internal state. No resetting of caches is\n    # needed.)\n    seed_ = generator.integers(SEED_MIN_VALUE, SEED_MAX_VALUE, dtype=\"int32\",\n                               size=(2,))[-1]\n\n    seed_seq = np.random.SeedSequence(seed_)\n    seed_seqs = seed_seq.spawn(n)\n    return [convert_seed_sequence_to_generator(seed_seq)\n            for seed_seq in seed_seqs]\n\n\ndef _derive_generators_np116_(random_state, n):\n    seed_ = random_state.randint(SEED_MIN_VALUE, SEED_MAX_VALUE)\n    return [_convert_seed_to_generator_np116(seed_ + i) for i in sm.xrange(n)]\n\n\ndef get_generator_state(generator):\n    \"\"\"Get the state of this provided generator.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        The generator, which's state is supposed to be extracted.\n\n    Returns\n    -------\n    tuple or dict\n        The state of the generator.\n        In numpy 1.17+, the bit generator's state will be returned.\n        In numpy <=1.16, the ``RandomState`` 's state is returned.\n        In both cases the state is a copy. In-place changes will not affect\n        the RNG.\n\n    \"\"\"\n    if isinstance(generator, np.random.RandomState):\n        return _get_generator_state_np116(generator)\n    return _get_generator_state_np117(generator)\n\n\ndef _get_generator_state_np117(generator):\n    return generator.bit_generator.state\n\n\ndef _get_generator_state_np116(random_state):\n    return random_state.get_state()\n\n\ndef set_generator_state_(generator, state):\n    \"\"\"Set the state of a numpy (random number) generator in-place.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        The generator, which's state is supposed to be modified.\n\n    state : tuple or dict\n        The new state of the generator.\n        Should correspond to the output of\n        :func:`~imgaug.random.get_generator_state`.\n\n    \"\"\"\n    if isinstance(generator, np.random.RandomState):\n        _set_generator_state_np116_(generator, state)\n    else:\n        _set_generator_state_np117_(generator, state)\n\n\ndef _set_generator_state_np117_(generator, state):\n    generator.bit_generator.state = state\n\n\ndef _set_generator_state_np116_(random_state, state):\n    random_state.set_state(state)\n\n\ndef is_generator_equal_to(generator, other_generator):\n    \"\"\"Estimate whether two generator have the same class and state.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        First generator used in the comparison.\n\n    other_generator : numpy.random.Generator or numpy.random.RandomState\n        Second generator used in the comparison.\n\n    Returns\n    -------\n    bool\n        ``True`` if `generator` 's class and state are the same as the\n        class and state of `other_generator`. ``False`` otherwise.\n\n    \"\"\"\n    if isinstance(generator, np.random.RandomState):\n        return _is_generator_equal_to_np116(generator, other_generator)\n    return _is_generator_equal_to_np117(generator, other_generator)\n\n\ndef _is_generator_equal_to_np117(generator, other_generator):\n    assert generator.__class__ is other_generator.__class__, (\n        \"Expected both rngs to have the same class, \"\n        \"got types '%s' and '%s'.\" % (type(generator), type(other_generator)))\n\n    state1 = get_generator_state(generator)\n    state2 = get_generator_state(other_generator)\n    assert state1[\"bit_generator\"] == \"SFC64\", (\n        \"Can currently only compare the states of numpy.random.SFC64 bit \"\n        \"generators, got %s.\" % (state1[\"bit_generator\"],))\n    assert state2[\"bit_generator\"] == \"SFC64\", (\n        \"Can currently only compare the states of numpy.random.SFC64 bit \"\n        \"generators, got %s.\" % (state2[\"bit_generator\"],))\n\n    if state1[\"has_uint32\"] != state2[\"has_uint32\"]:\n        return False\n\n    if state1[\"has_uint32\"] == state2[\"has_uint32\"] == 1:\n        if state1[\"uinteger\"] != state2[\"uinteger\"]:\n            return False\n\n    return np.array_equal(state1[\"state\"][\"state\"], state2[\"state\"][\"state\"])\n\n\ndef _is_generator_equal_to_np116(random_state, other_random_state):\n    state1 = _get_generator_state_np116(random_state)\n    state2 = _get_generator_state_np116(other_random_state)\n    # Note that state1 and state2 are tuples with the value at index 1 being\n    # a numpy array and the values at 2-4 being ints/floats, so we can't just\n    # apply array_equal to state1[1:4+1] and state2[1:4+1]. We need a loop\n    # here.\n    for i in sm.xrange(1, 4+1):\n        if not np.array_equal(state1[i], state2[i]):\n            return False\n    return True\n\n\ndef advance_generator_(generator):\n    \"\"\"Advance a numpy random generator's internal state in-place by one step.\n\n    This advances the generator's state.\n\n    .. note::\n\n        This simply samples one or more random values. This means that\n        a call of this method will not completely change the outputs of\n        the next called sampling method. To achieve more drastic output\n        changes, call :func:`~imgaug.random.derive_generator_`.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        Generator of which to advance the internal state.\n\n    \"\"\"\n    if isinstance(generator, np.random.RandomState):\n        _advance_generator_np116_(generator)\n    else:\n        _advance_generator_np117_(generator)\n\n\ndef _advance_generator_np117_(generator):\n    _reset_generator_cache_np117_(generator)\n    generator.random()\n\n\ndef _advance_generator_np116_(generator):\n    _reset_generator_cache_np116_(generator)\n    generator.uniform()\n\n\ndef polyfill_integers(generator, low, high=None, size=None, dtype=\"int32\",\n                      endpoint=False):\n    \"\"\"Sample integers from a generator in different numpy versions.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        The generator to sample from. If it is a ``RandomState``,\n        :func:`numpy.random.RandomState.randint` will be called,\n        otherwise :func:`numpy.random.Generator.integers`.\n\n    low : int or array-like of ints\n        See :func:`numpy.random.Generator.integers`.\n\n    high : int or array-like of ints, optional\n        See :func:`numpy.random.Generator.integers`.\n\n    size : int or tuple of ints, optional\n        See :func:`numpy.random.Generator.integers`.\n\n    dtype : {str, dtype}, optional\n        See :func:`numpy.random.Generator.integers`.\n\n    endpoint : bool, optional\n        See :func:`numpy.random.Generator.integers`.\n\n    Returns\n    -------\n    int or ndarray of ints\n        See :func:`numpy.random.Generator.integers`.\n\n    \"\"\"\n    if hasattr(generator, \"randint\"):\n        if endpoint:\n            if high is None:\n                high = low + 1\n                low = 0\n            else:\n                high = high + 1\n        return generator.randint(low=low, high=high, size=size, dtype=dtype)\n    return generator.integers(low=low, high=high, size=size, dtype=dtype,\n                              endpoint=endpoint)\n\n\ndef polyfill_random(generator, size, dtype=\"float32\", out=None):\n    \"\"\"Sample random floats from a generator in different numpy versions.\n\n    Parameters\n    ----------\n    generator : numpy.random.Generator or numpy.random.RandomState\n        The generator to sample from. Both ``RandomState`` and ``Generator``\n        support ``random()``, but with different interfaces.\n\n    size : int or tuple of ints, optional\n        See :func:`numpy.random.Generator.random`.\n\n    dtype : {str, dtype}, optional\n        See :func:`numpy.random.Generator.random`.\n\n    out : ndarray, optional\n        See :func:`numpy.random.Generator.random`.\n\n\n    Returns\n    -------\n    float or ndarray of floats\n        See :func:`numpy.random.Generator.random`.\n\n    \"\"\"\n    if hasattr(generator, \"random_sample\"):\n        # note that numpy.random in <=1.16 supports random(), but\n        # numpy.random.RandomState does not\n        result = generator.random_sample(size=size).astype(dtype)\n        if out is not None:\n            assert out.dtype.name == result.dtype.name, (\n                \"Expected out array to have the same dtype as \"\n                \"random_sample()'s result array. Got %s (out) and %s (result) \"\n                \"instead.\" % (out.dtype.name, result.dtype.name))\n            out[...] = result\n        return result\n    return generator.random(size=size, dtype=dtype, out=out)\n\n\n# TODO add tests\nclass temporary_numpy_seed(object):\n    \"\"\"Context to temporarily alter the random state of ``numpy.random``.\n\n    The random state's internal state will be set back to the original one\n    once the context finishes.\n\n    Added in 0.4.0.\n\n    Parameters\n    ----------\n    entropy : None or int\n        The seed value to use.\n        If `None` then the seed will not be altered and the internal state\n        of ``numpy.random`` will not be reset back upon context exit (i.e.\n        this context will do nothing).\n\n    \"\"\"\n    # pylint complains about class name\n    # pylint: disable=invalid-name\n\n    def __init__(self, entropy=None):\n        self.old_state = None\n        self.entropy = entropy\n\n    def __enter__(self):\n        if self.entropy is not None:\n            self.old_state = np.random.get_state()\n            np.random.seed(self.entropy)\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        if self.entropy is not None:\n            np.random.set_state(self.old_state)\n"
  },
  {
    "path": "imgaug/testutils.py",
    "content": "\"\"\"\nSome utility functions that are only used for unittests.\nPlacing them in test/ directory seems to be against convention, so they are part of the library.\n\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport random\nimport copy\nimport warnings\nimport tempfile\nimport shutil\nimport re\nimport sys\nimport importlib\nimport functools\n\nimport numpy as np\nimport six.moves as sm\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\ntry:\n    import cPickle as pickle\nexcept ImportError:\n    import pickle\n\nimport imgaug as ia\nimport imgaug.random as iarandom\nimport imgaug.parameters as iap\nfrom imgaug.augmentables.kps import KeypointsOnImage\n\n\nclass ArgCopyingMagicMock(mock.MagicMock):\n    \"\"\"A MagicMock that copies its call args/kwargs before storing the call.\n\n    This is useful for imgaug as many augmentation methods change data\n    in-place.\n\n    Taken from https://stackoverflow.com/a/23264042/3760780\n\n    \"\"\"\n\n    def _mock_call(self, *args, **kwargs):\n        args_copy = copy.deepcopy(args)\n        kwargs_copy = copy.deepcopy(kwargs)\n        return super(ArgCopyingMagicMock, self)._mock_call(\n            *args_copy, **kwargs_copy)\n\n\n# Added in 0.4.0.\ndef assert_cbaois_equal(observed, expected, max_distance=1e-4):\n    # pylint: disable=unidiomatic-typecheck\n    if isinstance(observed, list) or isinstance(expected, list):\n        assert isinstance(observed, list)\n        assert isinstance(expected, list)\n        assert len(observed) == len(expected)\n        for observed_i, expected_i in zip(observed, expected):\n            assert_cbaois_equal(observed_i, expected_i,\n                                max_distance=max_distance)\n    else:\n        assert type(observed) == type(expected)\n        assert len(observed.items) == len(expected.items)\n        assert observed.shape == expected.shape\n        for item_a, item_b in zip(observed.items, expected.items):\n            assert item_a.coords_almost_equals(item_b,\n                                               max_distance=max_distance)\n        if isinstance(expected, ia.PolygonsOnImage):\n            for item_obs, item_exp in zip(observed.items, expected.items):\n                if item_exp.is_valid:\n                    assert item_obs.is_valid\n\n\ndef create_random_images(size):\n    return np.random.uniform(0, 255, size).astype(np.uint8)\n\n\ndef create_random_keypoints(size_images, nb_keypoints_per_img):\n    result = []\n    for _ in sm.xrange(size_images[0]):\n        kps = []\n        height, width = size_images[1], size_images[2]\n        for _ in sm.xrange(nb_keypoints_per_img):\n            x = np.random.randint(0, width-1)\n            y = np.random.randint(0, height-1)\n            kps.append(ia.Keypoint(x=x, y=y))\n        result.append(ia.KeypointsOnImage(kps, shape=size_images[1:]))\n    return result\n\n\ndef array_equal_lists(list1, list2):\n    assert isinstance(list1, list), (\n        \"Expected list1 to be a list, got type %s.\" % (type(list1),))\n    assert isinstance(list2, list), (\n        \"Expected list2 to be a list, got type %s.\" % (type(list2),))\n\n    if len(list1) != len(list2):\n        return False\n\n    for arr1, arr2 in zip(list1, list2):\n        if not np.array_equal(arr1, arr2):\n            return False\n\n    return True\n\n\ndef keypoints_equal(kpsois1, kpsois2, eps=0.001):\n    if isinstance(kpsois1, KeypointsOnImage):\n        assert isinstance(kpsois2, KeypointsOnImage)\n        kpsois1 = [kpsois1]\n        kpsois2 = [kpsois2]\n\n    if len(kpsois1) != len(kpsois2):\n        return False\n\n    for kpsoi1, kpsoi2 in zip(kpsois1, kpsois2):\n        kps1 = kpsoi1.keypoints\n        kps2 = kpsoi2.keypoints\n        if len(kps1) != len(kps2):\n            return False\n\n        for kp1, kp2 in zip(kps1, kps2):\n            x_equal = (float(kp2.x) - eps\n                       <= float(kp1.x)\n                       <= float(kp2.x) + eps)\n            y_equal = (float(kp2.y) - eps\n                       <= float(kp1.y)\n                       <= float(kp2.y) + eps)\n            if not x_equal or not y_equal:\n                return False\n\n    return True\n\n\ndef reseed(seed=0):\n    iarandom.seed(seed)\n    np.random.seed(seed)\n    random.seed(seed)\n\n\n# Added in 0.4.0.\ndef runtest_pickleable_uint8_img(augmenter, shape=(15, 15, 3), iterations=3):\n    image = np.mod(np.arange(int(np.prod(shape))), 256).astype(np.uint8)\n    image = image.reshape(shape)\n    augmenter_pkl = pickle.loads(pickle.dumps(augmenter, protocol=-1))\n\n    for _ in np.arange(iterations):\n        image_aug = augmenter(image=image)\n        image_aug_pkl = augmenter_pkl(image=image)\n        assert np.array_equal(image_aug, image_aug_pkl)\n\n\ndef wrap_shift_deprecation(func, *args, **kwargs):\n    \"\"\"Helper for tests of CBA shift() functions.\n\n    Added in 0.4.0.\n\n    \"\"\"\n    # No deprecated arguments? Just call the functions directly.\n    deprecated_kwargs = [\"top\", \"right\", \"bottom\", \"left\"]\n    if not any([kwname in kwargs for kwname in deprecated_kwargs]):\n        return func()\n\n    # Deprecated arguments? Log warnings and assume that there was a\n    # deprecation warning with expected message.\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        result = func()\n\n        assert (\n            \"These are deprecated. Use `x` and `y` instead.\"\n            in str(caught_warnings[-1].message)\n        )\n\n        return result\n\n\nclass TemporaryDirectory(object):\n    \"\"\"Create a context for a temporary directory.\n\n    The directory is automatically removed at the end of the context.\n    This context is available in ``tmpfile.TemporaryDirectory``, but only\n    from 3.2+.\n\n    Added in 0.4.0.\n\n    \"\"\"\n\n    def __init__(self, suffix=\"\", prefix=\"tmp\", dir=None):\n        # pylint: disable=redefined-builtin\n        self.name = tempfile.mkdtemp(suffix, prefix, dir)\n\n    def __enter__(self):\n        return self.name\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        shutil.rmtree(self.name)\n\n\n# Copied from\n# https://github.com/python/cpython/blob/master/Lib/unittest/case.py\n# at commit 293dd23 (Nov 19, 2019).\n# Required at least to enable assertWarns() in python <3.2.\n# Added in 0.4.0.\ndef _is_subtype(expected, basetype):\n    if isinstance(expected, tuple):\n        return all(_is_subtype(e, basetype) for e in expected)\n    return isinstance(expected, type) and issubclass(expected, basetype)\n\n\n# Copied from\n# https://github.com/python/cpython/blob/master/Lib/unittest/case.py\n# at commit 293dd23 (Nov 19, 2019).\n# Required at least to enable assertWarns() in python <3.2.\n# Added in 0.4.0.\nclass _BaseTestCaseContext:\n    # Added in 0.4.0.\n    def __init__(self, test_case):\n        self.test_case = test_case\n\n    # Added in 0.4.0.\n    def _raiseFailure(self, standardMsg):\n        # pylint: disable=invalid-name, protected-access, no-member\n        msg = self.test_case._formatMessage(self.msg, standardMsg)\n        raise self.test_case.failureException(msg)\n\n\n# Copied from\n# https://github.com/python/cpython/blob/master/Lib/unittest/case.py\n# at commit 293dd23 (Nov 19, 2019).\n# Required at least to enable assertWarns() in python <3.2.\n# Added in 0.4.0.\nclass _AssertRaisesBaseContext(_BaseTestCaseContext):\n    # Added in 0.4.0.\n    def __init__(self, expected, test_case, expected_regex=None):\n        _BaseTestCaseContext.__init__(self, test_case)\n        self.expected = expected\n        self.test_case = test_case\n        if expected_regex is not None:\n            expected_regex = re.compile(expected_regex)\n        self.expected_regex = expected_regex\n        self.obj_name = None\n        self.msg = None\n\n    # Added in 0.4.0.\n    # pylint: disable=inconsistent-return-statements\n    def handle(self, name, args, kwargs):\n        \"\"\"\n        If args is empty, assertRaises/Warns is being used as a\n        context manager, so check for a 'msg' kwarg and return self.\n        If args is not empty, call a callable passing positional and keyword\n        arguments.\n        \"\"\"\n        # pylint: disable=no-member, self-cls-assignment, not-context-manager\n        try:\n            if not _is_subtype(self.expected, self._base_type):\n                raise TypeError('%s() arg 1 must be %s' %\n                                (name, self._base_type_str))\n            if not args:\n                self.msg = kwargs.pop('msg', None)\n                if kwargs:\n                    raise TypeError('%r is an invalid keyword argument for '\n                                    'this function' % (next(iter(kwargs)),))\n                return self\n\n            callable_obj = args[0]\n            args = args[1:]\n\n            try:\n                self.obj_name = callable_obj.__name__\n            except AttributeError:\n                self.obj_name = str(callable_obj)\n            with self:\n                callable_obj(*args, **kwargs)\n        finally:\n            # bpo-23890: manually break a reference cycle\n            self = None\n    # pylint: enable=inconsistent-return-statements\n\n\n# Copied from\n# https://github.com/python/cpython/blob/master/Lib/unittest/case.py\n# at commit 293dd23 (Nov 19, 2019).\n# Required at least to enable assertWarns() in python <3.2.\n# Added in 0.4.0.\nclass _AssertWarnsContext(_AssertRaisesBaseContext):\n    \"\"\"A context manager used to implement TestCase.assertWarns* methods.\"\"\"\n\n    _base_type = Warning\n    _base_type_str = 'a warning type or tuple of warning types'\n\n    # Added in 0.4.0.\n    def __enter__(self):\n        # The __warningregistry__'s need to be in a pristine state for tests\n        # to work properly.\n        # pylint: disable=invalid-name, attribute-defined-outside-init\n        for v in sys.modules.values():\n            if getattr(v, '__warningregistry__', None):\n                v.__warningregistry__ = {}\n        self.warnings_manager = warnings.catch_warnings(record=True)\n        self.warnings = self.warnings_manager.__enter__()\n        warnings.simplefilter(\"always\", self.expected)\n        return self\n\n    # Added in 0.4.0.\n    def __exit__(self, exc_type, exc_value, tb):\n        # pylint: disable=invalid-name, attribute-defined-outside-init\n        self.warnings_manager.__exit__(exc_type, exc_value, tb)\n        if exc_type is not None:\n            # let unexpected exceptions pass through\n            return\n        try:\n            exc_name = self.expected.__name__\n        except AttributeError:\n            exc_name = str(self.expected)\n        first_matching = None\n        for m in self.warnings:\n            w = m.message\n            if not isinstance(w, self.expected):\n                continue\n            if first_matching is None:\n                first_matching = w\n            if (self.expected_regex is not None and\n                    not self.expected_regex.search(str(w))):\n                continue\n            # store warning for later retrieval\n            self.warning = w\n            self.filename = m.filename\n            self.lineno = m.lineno\n            return\n        # Now we simply try to choose a helpful failure message\n        if first_matching is not None:\n            self._raiseFailure('\"{}\" does not match \"{}\"'.format(\n                self.expected_regex.pattern, str(first_matching)))\n        if self.obj_name:\n            self._raiseFailure(\"{} not triggered by {}\".format(exc_name,\n                                                               self.obj_name))\n        else:\n            self._raiseFailure(\"{} not triggered\".format(exc_name))\n\n\n# Partially copied from\n# https://github.com/python/cpython/blob/master/Lib/unittest/case.py\n# at commit 293dd23 (Nov 19, 2019).\n# Required at least to enable assertWarns() in python <3.2.\ndef assertWarns(testcase, expected_warning, *args, **kwargs):\n    \"\"\"Context with same functionality as ``assertWarns`` in ``unittest``.\n\n    Note that unittest's ``assertWarns`` is only available in python 3.2+.\n\n    Added in 0.4.0.\n\n    Example\n    -------\n    >>> def test_foo(self):\n    >>>     with assertWarns(self, UserWarning):\n    >>>         pass\n\n    \"\"\"\n    # pylint: disable=invalid-name\n    context = _AssertWarnsContext(expected_warning, testcase)\n    return context.handle(\"assertWarns\", args, kwargs)\n\n\nclass temporary_constants(object):\n    \"\"\"Context to temporarily change the value of one or more constants.\n\n    Added in 0.5.0.\n\n    \"\"\"\n\n    # pylint: disable=invalid-name\n\n    UNCHANGED = object()\n\n    def __init__(self, paths, values):\n        if ia.is_string(paths):\n            paths = [paths]\n            values = [values]\n\n        self.paths = [\".\".join(path_i.split(\".\")[:-1]) for path_i in paths]\n        self.cnames = [path_i.split(\".\")[-1] for path_i in paths]\n        self.values = values\n        self.old_values = None\n\n    def __enter__(self):\n        old_values = []\n        for path, cname, value in zip(self.paths, self.cnames, self.values):\n            module = importlib.import_module(path)\n            old_values.append(getattr(module, cname))\n            if value is not temporary_constants.UNCHANGED:\n                setattr(module, cname, value)\n        self.old_values = old_values\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        gen = zip(self.paths, self.cnames, self.old_values)\n        for path, cname, old_value in gen:\n            module = importlib.import_module(path)\n            setattr(module, cname, old_value)\n\n\ndef is_parameter_instance(param, param_type):\n    \"\"\"Perform an isinstance check on a parameter while ignoring prefetching.\n\n    This is identical to ``isinstance(param, param_type)``, unless `param`\n    is an instance of :class:`imgaug.parameters.AutoPrefetcher`, then it\n    is equivalent to ``isinstance(param.other_param, param_type)`` (potentially\n    recursively evaluated until `param` is no longer prefetched).\n\n    Added in 0.5.0.\n\n    Parameters\n    ----------\n    param : imgaug.parameters.StochasticParameter\n        The parameter to check.\n\n    param_type : type\n        The desired type. Similar as in ``isinstance``.\n        E.g. ``imgaug.parameters.Deterministic``.\n\n    Returns\n    -------\n    bool\n        Whether the parameter is of the given type.\n\n    \"\"\"\n    return isinstance(remove_prefetching(param), param_type)\n\n\ndef remove_prefetching(param):\n    \"\"\"Convert a possibly-prefetched parameter into a not-prefetched one.\n\n    Added in 0.5.0.\n\n    Parameters\n    ----------\n    param : imgaug.parameters.StochasticParameter\n        Parameter to remove prefetching from.\n\n    Returns\n    -------\n    imgaug.parameters.StochasticParameter\n        The input parameter without prefetching. (Not copied.)\n        If the input parameter was not prefetched, it will be returned without\n        change.\n\n    \"\"\"\n    if isinstance(param, iap.AutoPrefetcher):\n        return remove_prefetching(param.other_param)\n    return param\n\n\ndef ensure_deprecation_warning(expected_text):\n    \"\"\"Ensure that a decorated function raises a deprecation warning.\n\n    Added in 0.5.0.\n\n    Parameters\n    ----------\n    expected_text : str\n        Expected text fragment to be found in warning's text.\n\n    Returns\n    -------\n    callable\n        Decorated function.\n\n    \"\"\"\n    def _wrapper_with_args(func):\n        @functools.wraps(func)\n        def _wrapper(*args, **kwargs):\n            with warnings.catch_warnings(record=True) as caught_warnings:\n                func(*args, **kwargs)\n\n            assert len(caught_warnings) == 1, (\n                \"Expected 1 warning, got %d.\" % (len(caught_warnings),)\n            )\n            assert (\n                expected_text\n                in str(caught_warnings[-1].message)\n            )\n\n        return _wrapper\n    return _wrapper_with_args\n"
  },
  {
    "path": "imgaug/validation.py",
    "content": "\"\"\"Helper functions to validate input data and produce error messages.\"\"\"\nfrom __future__ import print_function, division, absolute_import\n\nimport imgaug as ia\n\n\ndef convert_iterable_to_string_of_types(iterable_var):\n    \"\"\"Convert an iterable of values to a string of their types.\n\n    Parameters\n    ----------\n    iterable_var : iterable\n        An iterable of variables, e.g. a list of integers.\n\n    Returns\n    -------\n    str\n        String representation of the types in `iterable_var`. One per item\n        in `iterable_var`. Separated by commas.\n\n    \"\"\"\n    types = [str(type(var_i)) for var_i in iterable_var]\n    return \", \".join(types)\n\n\ndef is_iterable_of(iterable_var, classes):\n    \"\"\"Check whether `iterable_var` contains only instances of given classes.\n\n    Parameters\n    ----------\n    iterable_var : iterable\n        An iterable of items that will be matched against `classes`.\n\n    classes : type or iterable of type\n        One or more classes that each item in `var` must be an instanceof.\n        If this is an iterable, a single match per item is enough.\n\n    Returns\n    -------\n    bool\n        Whether `var` only contains instances of `classes`.\n        If `var` was empty, ``True`` will be returned.\n\n    \"\"\"\n    if not ia.is_iterable(iterable_var):\n        return False\n\n    for var_i in iterable_var:\n        if not isinstance(var_i, classes):\n            return False\n\n    return True\n\n\ndef assert_is_iterable_of(iterable_var, classes):\n    \"\"\"Assert that `iterable_var` only contains instances of given classes.\n\n    Parameters\n    ----------\n    iterable_var : iterable\n        See :func:`~imgaug.validation.is_iterable_of`.\n\n    classes : type or iterable of type\n        See :func:`~imgaug.validation.is_iterable_of`.\n\n    \"\"\"\n    valid = is_iterable_of(iterable_var, classes)\n    if not valid:\n        expected_types_str = (\n            \", \".join([class_.__name__ for class_ in classes])\n            if not isinstance(classes, type)\n            else classes.__name__)\n        if not ia.is_iterable(iterable_var):\n            raise AssertionError(\n                \"Expected an iterable of the following types: %s. \"\n                \"Got instead a single instance of: %s.\" % (\n                    expected_types_str,\n                    type(iterable_var).__name__)\n            )\n\n        raise AssertionError(\n            \"Expected an iterable of the following types: %s. \"\n            \"Got an iterable of types: %s.\" % (\n                expected_types_str,\n                convert_iterable_to_string_of_types(iterable_var))\n        )\n"
  },
  {
    "path": "pytest.ini",
    "content": "[pytest]\naddopts = -p pytester -p no:doctest --xdoctest --xdoctest-global-exec=\"import imgaug as ia\\nfrom imgaug import augmenters as iaa\"\nnorecursedirs = .git ignore build __pycache__ docs doc *.egg-info _*\n"
  },
  {
    "path": "readthedocs.yml",
    "content": "version: 2\n\nsubmodules:\n  include: all\n  recursive: true"
  },
  {
    "path": "requirements.txt",
    "content": "six\n# The test for imgaug.augmenters.blend.blend_alpha() fails for numpy<1.15.\n# All other tests seemed to work as of 2019/01/04.\nnumpy>=1.15\nscipy\nPillow\nmatplotlib\nscikit-image>=0.14.2\nopencv-python-headless\n# imageio versions past 2.6.1 do not support <3.5 anymore\nimageio<=2.6.1; python_version<'3.5'\nimageio; python_version>='3.5'\nShapely\n# numba is not available in <=3.5\nnumba; python_version>='3.6'\n"
  },
  {
    "path": "setup.cfg",
    "content": "[metadata]\ndescription-file = README.md\n"
  },
  {
    "path": "setup.py",
    "content": "# pylint: disable=missing-module-docstring\nimport re\n\nfrom pkg_resources import get_distribution, DistributionNotFound\nfrom setuptools import setup, find_packages\n\nlong_description = \"\"\"A library for image augmentation in machine learning experiments, particularly convolutional\nneural networks. Supports the augmentation of images, keypoints/landmarks, bounding boxes, heatmaps and segmentation\nmaps in a variety of different ways.\"\"\"\n\nINSTALL_REQUIRES = [\n    \"six\",\n    \"numpy>=1.15\",\n    \"scipy\",\n    \"Pillow\",\n    \"matplotlib\",\n    \"scikit-image>=0.14.2\",\n    \"opencv-python-headless\",\n    \"imageio<=2.6.1; python_version<'3.5'\",\n    \"imageio; python_version>='3.5'\",\n    \"Shapely\"\n]\n\nALT_INSTALL_REQUIRES = {\n    \"opencv-python-headless\": [\"opencv-python\", \"opencv-contrib-python\", \"opencv-contrib-python-headless\"],\n}\n\n\ndef check_alternative_installation(install_require, alternative_install_requires):\n    \"\"\"If some version version of alternative requirement installed, return alternative,\n    else return main.\n    \"\"\"\n    for alternative_install_require in alternative_install_requires:\n        try:\n            alternative_pkg_name = re.split(r\"[!<>=]\", alternative_install_require)[0]\n            get_distribution(alternative_pkg_name)\n            return str(alternative_install_require)\n        except DistributionNotFound:\n            continue\n\n    return str(install_require)\n\n\ndef get_install_requirements(main_requires, alternative_requires):\n    \"\"\"Iterates over all install requires\n    If an install require has an alternative option, check if this option is installed\n    If that is the case, replace the install require by the alternative to not install dual package\"\"\"\n    install_requires = []\n    for main_require in main_requires:\n        if main_require in alternative_requires:\n            main_require = check_alternative_installation(main_require, alternative_requires.get(main_require))\n        install_requires.append(main_require)\n\n    return install_requires\n\n\nINSTALL_REQUIRES = get_install_requirements(INSTALL_REQUIRES, ALT_INSTALL_REQUIRES)\n\nsetup(\n    name=\"imgaug\",\n    version=\"0.4.0\",\n    author=\"Alexander Jung\",\n    author_email=\"kontakt@ajung.name\",\n    url=\"https://github.com/aleju/imgaug\",\n    download_url=\"https://github.com/aleju/imgaug/archive/0.4.0.tar.gz\",\n    install_requires=INSTALL_REQUIRES,\n    packages=find_packages(),\n    include_package_data=True,\n    package_data={\n        \"\": [\"LICENSE\", \"README.md\", \"requirements.txt\"],\n        \"imgaug\": [\"DejaVuSans.ttf\", \"quokka.jpg\", \"quokka_annotations.json\", \"quokka_depth_map_halfres.png\"],\n        \"imgaug.checks\": [\"README.md\"]\n    },\n    license=\"MIT\",\n    description=\"Image augmentation library for deep neural networks\",\n    long_description=long_description,\n    keywords=[\"augmentation\", \"image\", \"deep learning\", \"neural network\", \"CNN\", \"machine learning\",\n              \"computer vision\", \"overfitting\"],\n    classifiers=[\n        \"Development Status :: 4 - Beta\",\n        \"Intended Audience :: Science/Research\",\n        \"Intended Audience :: Developers\",\n        \"Intended Audience :: Information Technology\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Natural Language :: English\",\n        \"Operating System :: OS Independent\",\n        \"Programming Language :: Python :: 2.7\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.4\",\n        \"Programming Language :: Python :: 3.5\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Topic :: Scientific/Engineering :: Artificial Intelligence\",\n        \"Topic :: Scientific/Engineering :: Image Recognition\",\n        \"Topic :: Software Development :: Libraries :: Python Modules\"\n    ]\n)\n"
  },
  {
    "path": "test/augmentables/test_batches.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport warnings\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\n\nimport imgaug as ia\nimport imgaug.augmenters as iaa\nfrom imgaug.testutils import reseed\nfrom imgaug.augmentables.batches import _BatchInAugmentation\n\n\nATTR_NAMES = [\"images\", \"heatmaps\", \"segmentation_maps\", \"keypoints\",\n              \"bounding_boxes\", \"polygons\", \"line_strings\"]\n\n\n# TODO test __init__()\nclass TestUnnormalizedBatch(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_get_column_names__only_images(self):\n        batch = ia.UnnormalizedBatch(\n            images=np.zeros((1, 2, 2, 3), dtype=np.uint8)\n        )\n\n        names = batch.get_column_names()\n\n        assert names == [\"images\"]\n\n    def test_get_column_names__all_columns(self):\n        batch = ia.UnnormalizedBatch(\n            images=np.zeros((1, 2, 2, 3), dtype=np.uint8),\n            heatmaps=[np.zeros((2, 2, 1), dtype=np.float32)],\n            segmentation_maps=[np.zeros((2, 2, 1), dtype=np.int32)],\n            keypoints=[[(0, 0)]],\n            bounding_boxes=[[ia.BoundingBox(0, 0, 1, 1)]],\n            polygons=[[ia.Polygon([(0, 0), (1, 0), (1, 1)])]],\n            line_strings=[[ia.LineString([(0, 0), (1, 0)])]]\n        )\n\n        names = batch.get_column_names()\n\n        assert names == [\"images\", \"heatmaps\", \"segmentation_maps\",\n                         \"keypoints\", \"bounding_boxes\", \"polygons\",\n                         \"line_strings\"]\n\n    def test_to_normalized_batch__only_images(self):\n        batch = ia.UnnormalizedBatch(\n            images=np.zeros((1, 2, 2, 3), dtype=np.uint8)\n        )\n\n        batch_norm = batch.to_normalized_batch()\n\n        assert isinstance(batch_norm, ia.Batch)\n        assert ia.is_np_array(batch_norm.images_unaug)\n        assert batch_norm.images_unaug.shape == (1, 2, 2, 3)\n        assert batch_norm.get_column_names() == [\"images\"]\n\n    def test_to_normalized_batch__all_columns(self):\n        batch = ia.UnnormalizedBatch(\n            images=np.zeros((1, 2, 2, 3), dtype=np.uint8),\n            heatmaps=[np.zeros((2, 2, 1), dtype=np.float32)],\n            segmentation_maps=[np.zeros((2, 2, 1), dtype=np.int32)],\n            keypoints=[[(0, 0)]],\n            bounding_boxes=[[ia.BoundingBox(0, 0, 1, 1)]],\n            polygons=[[ia.Polygon([(0, 0), (1, 0), (1, 1)])]],\n            line_strings=[[ia.LineString([(0, 0), (1, 0)])]]\n        )\n\n        batch_norm = batch.to_normalized_batch()\n\n        assert isinstance(batch_norm, ia.Batch)\n        assert ia.is_np_array(batch_norm.images_unaug)\n        assert batch_norm.images_unaug.shape == (1, 2, 2, 3)\n        assert isinstance(batch_norm.heatmaps_unaug[0], ia.HeatmapsOnImage)\n        assert isinstance(batch_norm.segmentation_maps_unaug[0],\n                          ia.SegmentationMapsOnImage)\n        assert isinstance(batch_norm.keypoints_unaug[0], ia.KeypointsOnImage)\n        assert isinstance(batch_norm.bounding_boxes_unaug[0],\n                          ia.BoundingBoxesOnImage)\n        assert isinstance(batch_norm.polygons_unaug[0], ia.PolygonsOnImage)\n        assert isinstance(batch_norm.line_strings_unaug[0],\n                          ia.LineStringsOnImage)\n        assert batch_norm.get_column_names() == [\n            \"images\", \"heatmaps\", \"segmentation_maps\", \"keypoints\",\n            \"bounding_boxes\", \"polygons\", \"line_strings\"]\n\n    def test_fill_from_augmented_normalized_batch(self):\n        batch = ia.UnnormalizedBatch(\n            images=np.zeros((1, 2, 2, 3), dtype=np.uint8),\n            heatmaps=[np.zeros((2, 2, 1), dtype=np.float32)],\n            segmentation_maps=[np.zeros((2, 2, 1), dtype=np.int32)],\n            keypoints=[[(0, 0)]],\n            bounding_boxes=[[ia.BoundingBox(0, 0, 1, 1)]],\n            polygons=[[ia.Polygon([(0, 0), (1, 0), (1, 1)])]],\n            line_strings=[[ia.LineString([(0, 0), (1, 0)])]]\n        )\n        batch_norm = ia.Batch(\n            images=np.zeros((1, 2, 2, 3), dtype=np.uint8),\n            heatmaps=[\n                ia.HeatmapsOnImage(\n                    np.zeros((2, 2, 1), dtype=np.float32),\n                    shape=(2, 2, 3)\n                )\n            ],\n            segmentation_maps=[\n                ia.SegmentationMapsOnImage(\n                    np.zeros((2, 2, 1), dtype=np.int32),\n                    shape=(2, 2, 3)\n                )\n            ],\n            keypoints=[\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(0, 0)],\n                    shape=(2, 2, 3)\n                )\n            ],\n            bounding_boxes=[\n                ia.BoundingBoxesOnImage(\n                    [ia.BoundingBox(0, 0, 1, 1)],\n                    shape=(2, 2, 3)\n                )\n            ],\n            polygons=[\n                ia.PolygonsOnImage(\n                    [ia.Polygon([(0, 0), (1, 0), (1, 1)])],\n                    shape=(2, 2, 3)\n                )\n            ],\n            line_strings=[\n                ia.LineStringsOnImage(\n                    [ia.LineString([(0, 0), (1, 0)])],\n                    shape=(2, 2, 3)\n                )\n            ]\n        )\n        batch_norm.images_aug = batch_norm.images_unaug\n        batch_norm.heatmaps_aug = batch_norm.heatmaps_unaug\n        batch_norm.segmentation_maps_aug = batch_norm.segmentation_maps_unaug\n        batch_norm.keypoints_aug = batch_norm.keypoints_unaug\n        batch_norm.bounding_boxes_aug = batch_norm.bounding_boxes_unaug\n        batch_norm.polygons_aug = batch_norm.polygons_unaug\n        batch_norm.line_strings_aug = batch_norm.line_strings_unaug\n\n        batch = batch.fill_from_augmented_normalized_batch(batch_norm)\n\n        assert batch.images_aug.shape == (1, 2, 2, 3)\n        assert ia.is_np_array(batch.heatmaps_aug[0])\n        assert ia.is_np_array(batch.segmentation_maps_aug[0])\n        assert batch.keypoints_aug[0][0] == (0, 0)\n        assert batch.bounding_boxes_aug[0][0].x1 == 0\n        assert batch.polygons_aug[0][0].exterior[0][0] == 0\n        assert batch.line_strings_aug[0][0].coords[0][0] == 0\n\n\nclass TestBatch(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___no_arguments(self):\n        batch = ia.Batch()\n        for attr_name in ATTR_NAMES:\n            assert getattr(batch, \"%s_unaug\" % (attr_name,)) is None\n            assert getattr(batch, \"%s_aug\" % (attr_name,)) is None\n        assert batch.data is None\n\n    def test___init___all_arguments_provided(self):\n        # we exploit here that Batch() init does not verify its inputs\n        batch = ia.Batch(\n            images=0,\n            heatmaps=1,\n            segmentation_maps=2,\n            keypoints=3,\n            bounding_boxes=4,\n            polygons=5,\n            line_strings=6,\n            data=7\n        )\n        for i, attr_name in enumerate(ATTR_NAMES):\n            assert getattr(batch, \"%s_unaug\" % (attr_name,)) == i\n            assert getattr(batch, \"%s_aug\" % (attr_name,)) is None\n        assert batch.data == 7\n\n    def test_warnings_for_deprecated_properties(self):\n        batch = ia.Batch()\n        # self.assertWarns does not exist in py2.7\n        deprecated_attr_names = [\"images\", \"heatmaps\", \"segmentation_maps\",\n                                 \"keypoints\", \"bounding_boxes\"]\n        for attr_name in deprecated_attr_names:\n            with self.subTest(attr_name=attr_name),\\\n                    warnings.catch_warnings(record=True) as caught_warnings:\n                warnings.simplefilter(\"always\")\n\n                _ = getattr(batch, attr_name)\n                assert len(caught_warnings) == 1\n                assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n    def test_get_column_names__only_images(self):\n        batch = ia.Batch(\n            images=np.zeros((1, 2, 2, 3), dtype=np.uint8)\n        )\n\n        names = batch.get_column_names()\n\n        assert names == [\"images\"]\n\n    def test_get_column_names__all_columns(self):\n        batch = ia.Batch(\n            images=np.zeros((1, 2, 2, 3), dtype=np.uint8),\n            heatmaps=[np.zeros((2, 2, 1), dtype=np.float32)],\n            segmentation_maps=[np.zeros((2, 2, 1), dtype=np.int32)],\n            keypoints=[\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(x=0, y=0)],\n                    shape=(2, 2, 3)\n                )\n            ],\n            bounding_boxes=[\n                ia.BoundingBoxesOnImage(\n                    [ia.BoundingBox(0, 0, 1, 1)],\n                    shape=(2, 2, 3)\n                )\n            ],\n            polygons=[\n                ia.PolygonsOnImage(\n                    [ia.Polygon([(0, 0), (1, 0), (1, 1)])],\n                    shape=(2, 2, 3)\n                )\n            ],\n            line_strings=[\n                ia.LineStringsOnImage(\n                    [ia.LineString([(0, 0), (1, 0)])],\n                    shape=(2, 2, 3)\n                )\n            ]\n        )\n\n        names = batch.get_column_names()\n\n        assert names == [\"images\", \"heatmaps\", \"segmentation_maps\",\n                         \"keypoints\", \"bounding_boxes\", \"polygons\",\n                         \"line_strings\"]\n\n    def test_to_normalized_batch(self):\n        batch = ia.Batch(\n            images=np.zeros((1, 2, 2, 3), dtype=np.uint8)\n        )\n\n        batch_norm = batch.to_normalized_batch()\n\n        assert batch_norm is batch\n\n    def test_to_batch_in_augmentation__only_images(self):\n        batch = ia.Batch(\n            images=np.zeros((1, 2, 2, 3), dtype=np.uint8)\n        )\n\n        batch_inaug = batch.to_batch_in_augmentation()\n\n        assert isinstance(batch_inaug, _BatchInAugmentation)\n        assert ia.is_np_array(batch_inaug.images)\n        assert batch_inaug.images.shape == (1, 2, 2, 3)\n        assert batch_inaug.get_column_names() == [\"images\"]\n\n    def test_to_batch_in_augmentation__all_columns(self):\n        batch = ia.Batch(\n            images=np.zeros((1, 2, 2, 3), dtype=np.uint8),\n            heatmaps=[\n                ia.HeatmapsOnImage(\n                    np.zeros((2, 2, 1), dtype=np.float32),\n                    shape=(2, 2, 3)\n                )\n            ],\n            segmentation_maps=[\n                ia.SegmentationMapsOnImage(\n                    np.zeros((2, 2, 1), dtype=np.int32),\n                    shape=(2, 2, 3)\n                )\n            ],\n            keypoints=[\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(x=0, y=0)],\n                    shape=(2, 2, 3)\n                )\n            ],\n            bounding_boxes=[\n                ia.BoundingBoxesOnImage(\n                    [ia.BoundingBox(0, 0, 1, 1)],\n                    shape=(2, 2, 3)\n                )\n            ],\n            polygons=[\n                ia.PolygonsOnImage(\n                    [ia.Polygon([(0, 0), (1, 0), (1, 1)])],\n                    shape=(2, 2, 3)\n                )\n            ],\n            line_strings=[\n                ia.LineStringsOnImage(\n                    [ia.LineString([(0, 0), (1, 0)])],\n                    shape=(2, 2, 3)\n                )\n            ]\n        )\n\n        batch_inaug = batch.to_batch_in_augmentation()\n\n        assert isinstance(batch_inaug, _BatchInAugmentation)\n        assert ia.is_np_array(batch_inaug.images)\n        assert batch_inaug.images.shape == (1, 2, 2, 3)\n        assert isinstance(batch_inaug.heatmaps[0], ia.HeatmapsOnImage)\n        assert isinstance(batch_inaug.segmentation_maps[0],\n                          ia.SegmentationMapsOnImage)\n        assert isinstance(batch_inaug.keypoints[0], ia.KeypointsOnImage)\n        assert isinstance(batch_inaug.bounding_boxes[0],\n                          ia.BoundingBoxesOnImage)\n        assert isinstance(batch_inaug.polygons[0], ia.PolygonsOnImage)\n        assert isinstance(batch_inaug.line_strings[0], ia.LineStringsOnImage)\n        assert batch_inaug.get_column_names() == [\n            \"images\", \"heatmaps\", \"segmentation_maps\", \"keypoints\",\n            \"bounding_boxes\", \"polygons\", \"line_strings\"]\n\n    def test_fill_from_batch_in_augmentation(self):\n        batch = ia.Batch(images=1)\n        batch_inaug = _BatchInAugmentation(\n            images=2,\n            heatmaps=3,\n            segmentation_maps=4,\n            keypoints=5,\n            bounding_boxes=6,\n            polygons=7,\n            line_strings=8\n        )\n\n        batch = batch.fill_from_batch_in_augmentation_(batch_inaug)\n\n        assert batch.images_aug == 2\n        assert batch.heatmaps_aug == 3\n        assert batch.segmentation_maps_aug == 4\n        assert batch.keypoints_aug == 5\n        assert batch.bounding_boxes_aug == 6\n        assert batch.polygons_aug == 7\n        assert batch.line_strings_aug == 8\n\n    def test_deepcopy_no_arguments(self):\n        batch = ia.Batch()\n        observed = batch.deepcopy()\n        keys = list(observed.__dict__.keys())\n        assert len(keys) >= 14\n        for attr_name in keys:\n            assert getattr(observed, attr_name) is None\n\n    def test_deepcopy_only_images_provided(self):\n        images = np.zeros((1, 1, 3), dtype=np.uint8)\n        batch = ia.Batch(images=images)\n        observed = batch.deepcopy()\n        for attr_name in observed.__dict__.keys():\n            if attr_name != \"images_unaug\":\n                assert getattr(observed, attr_name) is None\n        assert ia.is_np_array(observed.images_unaug)\n\n    def test_deepcopy_every_argument_provided(self):\n        images = np.zeros((1, 1, 1, 3), dtype=np.uint8)\n        heatmaps = [ia.HeatmapsOnImage(np.zeros((1, 1, 1), dtype=np.float32),\n                                       shape=(4, 4, 3))]\n        segmentation_maps = [\n            ia.SegmentationMapsOnImage(np.zeros((1, 1), dtype=np.int32),\n                                       shape=(5, 5, 3))]\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)],\n                                         shape=(6, 6, 3))]\n        bounding_boxes = [\n            ia.BoundingBoxesOnImage([\n                ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)\n            ], shape=(7, 7, 3))]\n        polygons = [\n            ia.PolygonsOnImage([\n                ia.Polygon([(0, 0), (10, 0), (10, 10)])\n            ], shape=(100, 100, 3))]\n        line_strings = [\n            ia.LineStringsOnImage([\n                ia.LineString([(1, 1), (11, 1), (11, 11)])\n            ], shape=(101, 101, 3))]\n        data = {\"test\": 123, \"foo\": \"bar\", \"test2\": [1, 2, 3]}\n\n        batch = ia.Batch(\n            images=images,\n            heatmaps=heatmaps,\n            segmentation_maps=segmentation_maps,\n            keypoints=keypoints,\n            bounding_boxes=bounding_boxes,\n            polygons=polygons,\n            line_strings=line_strings,\n            data=data\n        )\n        observed = batch.deepcopy()\n\n        for attr_name in observed.__dict__.keys():\n            if \"_unaug\" not in attr_name and attr_name != \"data\":\n                assert getattr(observed, attr_name) is None\n\n        # must not be identical\n        assert observed.images_unaug is not images\n        assert observed.heatmaps_unaug is not heatmaps\n        assert observed.segmentation_maps_unaug is not segmentation_maps\n        assert observed.keypoints_unaug is not keypoints\n        assert observed.bounding_boxes_unaug is not bounding_boxes\n        assert observed.polygons_unaug is not polygons\n        assert observed.line_strings_unaug is not line_strings\n        assert observed.data is not data\n\n        # verify that lists were not shallow-copied\n        assert observed.heatmaps_unaug[0] is not heatmaps[0]\n        assert observed.segmentation_maps_unaug[0] is not segmentation_maps[0]\n        assert observed.keypoints_unaug[0] is not keypoints[0]\n        assert observed.bounding_boxes_unaug[0] is not bounding_boxes[0]\n        assert observed.polygons_unaug[0] is not polygons[0]\n        assert observed.line_strings_unaug[0] is not line_strings[0]\n        assert observed.data[\"test2\"] is not data[\"test2\"]\n\n        # but must be equal\n        assert ia.is_np_array(observed.images_unaug)\n        assert observed.images_unaug.shape == (1, 1, 1, 3)\n        assert isinstance(observed.heatmaps_unaug[0], ia.HeatmapsOnImage)\n        assert isinstance(observed.segmentation_maps_unaug[0],\n                          ia.SegmentationMapsOnImage)\n        assert isinstance(observed.keypoints_unaug[0], ia.KeypointsOnImage)\n        assert isinstance(observed.bounding_boxes_unaug[0],\n                          ia.BoundingBoxesOnImage)\n        assert isinstance(observed.polygons_unaug[0], ia.PolygonsOnImage)\n        assert isinstance(observed.line_strings_unaug[0], ia.LineStringsOnImage)\n        assert isinstance(observed.data, dict)\n\n        assert observed.heatmaps_unaug[0].shape == (4, 4, 3)\n        assert observed.segmentation_maps_unaug[0].shape == (5, 5, 3)\n        assert observed.keypoints_unaug[0].shape == (6, 6, 3)\n        assert observed.bounding_boxes_unaug[0].shape == (7, 7, 3)\n        assert observed.polygons_unaug[0].shape == (100, 100, 3)\n        assert observed.line_strings_unaug[0].shape == (101, 101, 3)\n\n        assert observed.heatmaps_unaug[0].arr_0to1.shape == (1, 1, 1)\n        assert observed.segmentation_maps_unaug[0].arr.shape == (1, 1, 1)\n        assert observed.keypoints_unaug[0].keypoints[0].x == 1\n        assert observed.keypoints_unaug[0].keypoints[0].y == 2\n        assert observed.bounding_boxes_unaug[0].bounding_boxes[0].x1 == 1\n        assert observed.bounding_boxes_unaug[0].bounding_boxes[0].y1 == 2\n        assert observed.bounding_boxes_unaug[0].bounding_boxes[0].x2 == 3\n        assert observed.bounding_boxes_unaug[0].bounding_boxes[0].y2 == 4\n        assert observed.polygons_unaug[0].polygons[0].exterior[0, 0] == 0\n        assert observed.polygons_unaug[0].polygons[0].exterior[0, 1] == 0\n        assert observed.polygons_unaug[0].polygons[0].exterior[1, 0] == 10\n        assert observed.polygons_unaug[0].polygons[0].exterior[1, 1] == 0\n        assert observed.polygons_unaug[0].polygons[0].exterior[2, 0] == 10\n        assert observed.polygons_unaug[0].polygons[0].exterior[2, 1] == 10\n        assert observed.line_strings_unaug[0].line_strings[0].coords[0, 0] == 1\n        assert observed.line_strings_unaug[0].line_strings[0].coords[0, 1] == 1\n        assert observed.line_strings_unaug[0].line_strings[0].coords[1, 0] == 11\n        assert observed.line_strings_unaug[0].line_strings[0].coords[1, 1] == 1\n        assert observed.line_strings_unaug[0].line_strings[0].coords[2, 0] == 11\n        assert observed.line_strings_unaug[0].line_strings[0].coords[2, 1] == 11\n\n        assert observed.data[\"test\"] == 123\n        assert observed.data[\"foo\"] == \"bar\"\n        assert observed.data[\"test2\"] == [1, 2, 3]\n\n\n# TODO test __init__\n#      test apply_propagation_hooks_\n#      test invert_apply_propagation_hooks_\nclass Test_BatchInAugmentation(unittest.TestCase):\n    def test_empty__all_columns_none(self):\n        batch = _BatchInAugmentation()\n        assert batch.empty\n\n    def test_empty__with_columns_set(self):\n        kwargs = [\n            {\"images\": [2]},\n            {\"heatmaps\": [3]},\n            {\"segmentation_maps\": [4]},\n            {\"keypoints\": [5]},\n            {\"bounding_boxes\": [6]},\n            {\"polygons\": [7]},\n            {\"line_strings\": [8]}\n        ]\n        for kwargs_i in kwargs:\n            batch = _BatchInAugmentation(**kwargs_i)\n            assert not batch.empty\n\n    def test_nb_rows__when_empty(self):\n        batch = _BatchInAugmentation()\n        assert batch.nb_rows == 0\n\n    def test_nb_rows__with_empty_column(self):\n        batch = _BatchInAugmentation(images=[])\n        assert batch.nb_rows == 0\n\n    def test_nb_rows__with_columns_set(self):\n        kwargs = [\n            {\"images\": [0]},\n            {\"heatmaps\": [0]},\n            {\"segmentation_maps\": [0]},\n            {\"keypoints\": [0]},\n            {\"bounding_boxes\": [0]},\n            {\"polygons\": [0]},\n            {\"line_strings\": [0]}\n        ]\n        for kwargs_i in kwargs:\n            batch = _BatchInAugmentation(**kwargs_i)\n            assert batch.nb_rows == 1\n\n    def test_nb_rows__with_two_columns(self):\n        batch = _BatchInAugmentation(images=[0, 0], keypoints=[0, 0])\n        assert batch.nb_rows == 2\n\n    def test_columns__when_empty(self):\n        batch = _BatchInAugmentation()\n        assert len(batch.columns) == 0\n\n    def test_columns__with_empty_column(self):\n        batch = _BatchInAugmentation(images=[])\n\n        columns = batch.columns\n\n        assert len(columns) == 0\n\n    def test_columns__with_columns_set(self):\n        kwargs = [\n            {\"images\": [0]},\n            {\"heatmaps\": [0]},\n            {\"segmentation_maps\": [0]},\n            {\"keypoints\": [0]},\n            {\"bounding_boxes\": [0]},\n            {\"polygons\": [0]},\n            {\"line_strings\": [0]}\n        ]\n        for kwargs_i in kwargs:\n            batch = _BatchInAugmentation(**kwargs_i)\n            columns = batch.columns\n            assert len(columns) == 1\n            assert columns[0].name == list(kwargs_i.keys())[0]\n\n    def test_columns__with_two_columns(self):\n        batch = _BatchInAugmentation(images=[0, 0], keypoints=[1, 1])\n\n        columns = batch.columns\n\n        assert len(columns) == 2\n        assert columns[0].name == \"images\"\n        assert columns[1].name == \"keypoints\"\n        assert columns[0].value == [0, 0]\n        assert columns[1].value == [1, 1]\n\n    def test_get_column_names__with_two_columns(self):\n        batch = _BatchInAugmentation(images=[0, 0], keypoints=[1, 1])\n        assert batch.get_column_names() == [\"images\", \"keypoints\"]\n\n    def test_get_rowwise_shapes__images_is_single_array(self):\n        batch = _BatchInAugmentation(images=np.zeros((2, 3, 4, 1)))\n        shapes = batch.get_rowwise_shapes()\n        assert shapes == [(3, 4, 1), (3, 4, 1)]\n\n    def test_get_rowwise_shapes__images_is_multiple_arrays(self):\n        batch = _BatchInAugmentation(\n            images=[np.zeros((3, 4, 1)), np.zeros((4, 5, 1))]\n        )\n        shapes = batch.get_rowwise_shapes()\n        assert shapes == [(3, 4, 1), (4, 5, 1)]\n\n    def test_get_rowwise_shapes__nonimages(self):\n        heatmaps = [\n            ia.HeatmapsOnImage(\n                np.zeros((1, 2, 1), dtype=np.float32),\n                shape=(1, 2, 3))\n        ]\n        segmaps = [\n            ia.SegmentationMapsOnImage(\n                np.zeros((1, 2, 1), dtype=np.int32),\n                shape=(1, 2, 3))\n        ]\n        keypoints = [\n            ia.KeypointsOnImage(\n                [ia.Keypoint(0, 0)],\n                shape=(1, 2, 3))\n        ]\n        bounding_boxes = [\n            ia.BoundingBoxesOnImage(\n                [ia.BoundingBox(0, 1, 2, 3)],\n                shape=(1, 2, 3)\n            )\n        ]\n        polygons = [\n            ia.PolygonsOnImage(\n                [ia.Polygon([(0, 0), (1, 0), (1, 1)])],\n                shape=(1, 2, 3)\n            )\n        ]\n        line_strings = [\n            ia.LineStringsOnImage(\n                [ia.LineString([(0, 0), (1, 0)])],\n                shape=(1, 2, 3)\n            )\n        ]\n\n        kwargs = [\n            {\"heatmaps\": heatmaps},\n            {\"segmentation_maps\": segmaps},\n            {\"keypoints\": keypoints},\n            {\"bounding_boxes\": bounding_boxes},\n            {\"polygons\": polygons},\n            {\"line_strings\": line_strings}\n        ]\n        for kwargs_i in kwargs:\n            batch = _BatchInAugmentation(**kwargs_i)\n            shapes = batch.get_rowwise_shapes()\n            assert shapes == [(1, 2, 3)]\n\n    def test_subselect_rows_by_indices__none_selected(self):\n        batch = _BatchInAugmentation(\n            images=np.zeros((3, 3, 4, 1)),\n            keypoints=[\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(0, 0)],\n                    shape=(3, 4, 1)\n                ),\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(1, 1)],\n                    shape=(3, 4, 1)\n                ),\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(2, 2)],\n                    shape=(3, 4, 1)\n                )\n            ]\n        )\n\n        batch_sub = batch.subselect_rows_by_indices([])\n\n        assert batch_sub.images is None\n        assert batch_sub.keypoints is None\n\n    def test_subselect_rows_by_indices__two_of_three_selected(self):\n        batch = _BatchInAugmentation(\n            images=np.zeros((3, 3, 4, 1)),\n            keypoints=[\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(0, 0)],\n                    shape=(3, 4, 1)\n                ),\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(1, 1)],\n                    shape=(3, 4, 1)\n                ),\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(2, 2)],\n                    shape=(3, 4, 1)\n                )\n            ]\n        )\n\n        batch_sub = batch.subselect_rows_by_indices([0, 2])\n\n        assert batch_sub.images.shape == (2, 3, 4, 1)\n        assert batch_sub.keypoints[0].keypoints[0].x == 0\n        assert batch_sub.keypoints[0].keypoints[0].y == 0\n        assert batch_sub.keypoints[1].keypoints[0].x == 2\n        assert batch_sub.keypoints[1].keypoints[0].y == 2\n\n    def test_invert_subselect_rows_by_indices__none_selected(self):\n        images = np.zeros((3, 3, 4, 1), dtype=np.uint8)\n        images[0, ...] = 0\n        images[1, ...] = 1\n        images[2, ...] = 2\n        batch = _BatchInAugmentation(\n            images=images,\n            keypoints=[\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(0, 0)],\n                    shape=(3, 4, 1)\n                ),\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(1, 1)],\n                    shape=(3, 4, 1)\n                ),\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(2, 2)],\n                    shape=(3, 4, 1)\n                )\n            ]\n        )\n\n        batch_sub = batch.subselect_rows_by_indices([])\n        batch_inv = batch.invert_subselect_rows_by_indices_([], batch_sub)\n\n        assert batch_inv.images.shape == (3, 3, 4, 1)\n        assert np.max(batch_inv.images[0]) == 0\n        assert np.max(batch_inv.images[1]) == 1\n        assert np.max(batch_inv.images[2]) == 2\n        assert batch_inv.keypoints[0].keypoints[0].x == 0\n        assert batch_inv.keypoints[0].keypoints[0].y == 0\n        assert batch_inv.keypoints[1].keypoints[0].x == 1\n        assert batch_inv.keypoints[1].keypoints[0].y == 1\n        assert batch_inv.keypoints[2].keypoints[0].x == 2\n        assert batch_inv.keypoints[2].keypoints[0].y == 2\n\n    def test_invert_subselect_rows_by_indices__two_of_three_selected(self):\n        images = np.zeros((3, 3, 4, 1), dtype=np.uint8)\n        images[0, ...] = 0\n        images[1, ...] = 1\n        images[2, ...] = 2\n        batch = _BatchInAugmentation(\n            images=images,\n            keypoints=[\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(0, 0)],\n                    shape=(3, 4, 1)\n                ),\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(1, 1)],\n                    shape=(3, 4, 1)\n                ),\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(2, 2)],\n                    shape=(3, 4, 1)\n                )\n            ]\n        )\n\n        batch_sub = batch.subselect_rows_by_indices([0, 2])\n        batch_sub.images[0, ...] = 10\n        batch_sub.images[1, ...] = 20\n        batch_sub.keypoints[0].keypoints[0].x = 10\n        batch_inv = batch.invert_subselect_rows_by_indices_([0, 2], batch_sub)\n\n        assert batch_inv.images.shape == (3, 3, 4, 1)\n        assert np.max(batch_inv.images[0]) == 10\n        assert np.max(batch_inv.images[1]) == 1\n        assert np.max(batch_inv.images[2]) == 20\n        assert batch_inv.keypoints[0].keypoints[0].x == 10\n        assert batch_inv.keypoints[0].keypoints[0].y == 0\n        assert batch_inv.keypoints[1].keypoints[0].x == 1\n        assert batch_inv.keypoints[1].keypoints[0].y == 1\n        assert batch_inv.keypoints[2].keypoints[0].x == 2\n        assert batch_inv.keypoints[2].keypoints[0].y == 2\n\n    def test_propagation_hooks_ctx(self):\n        def propagator(images, augmenter, parents, default):\n            if ia.is_np_array(images):\n                return False\n            else:\n                return True\n\n        hooks = ia.HooksImages(propagator=propagator)\n\n        batch = _BatchInAugmentation(\n            images=np.zeros((3, 3, 4, 1), dtype=np.uint8),\n            keypoints=[\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(0, 0)],\n                    shape=(3, 4, 1)\n                ),\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(1, 1)],\n                    shape=(3, 4, 1)\n                ),\n                ia.KeypointsOnImage(\n                    [ia.Keypoint(2, 2)],\n                    shape=(3, 4, 1)\n                )\n            ]\n        )\n\n        with batch.propagation_hooks_ctx(iaa.Identity(), hooks, []) \\\n                as batch_prop:\n            assert batch_prop.images is None\n            assert batch_prop.keypoints is not None\n            assert len(batch_prop.keypoints) == 3\n\n            batch_prop.keypoints[0].keypoints[0].x = 10\n\n        assert batch.images is not None\n        assert batch.keypoints is not None\n        assert batch.keypoints[0].keypoints[0].x == 10\n\n    def test_to_batch_in_augmentation(self):\n        batch = _BatchInAugmentation(images=1)\n        batch_inaug = batch.to_batch_in_augmentation()\n        assert batch_inaug is batch\n\n    def test_fill_from_batch_in_augmentation(self):\n        batch = _BatchInAugmentation(images=1)\n        batch_inaug = _BatchInAugmentation(\n            images=2,\n            heatmaps=3,\n            segmentation_maps=4,\n            keypoints=5,\n            bounding_boxes=6,\n            polygons=7,\n            line_strings=8\n        )\n\n        batch = batch.fill_from_batch_in_augmentation_(batch_inaug)\n\n        assert batch.images == 2\n        assert batch.heatmaps == 3\n        assert batch.segmentation_maps == 4\n        assert batch.keypoints == 5\n        assert batch.bounding_boxes == 6\n        assert batch.polygons == 7\n        assert batch.line_strings == 8\n\n    def test_to_batch(self):\n        batch_before_aug = ia.Batch()\n        batch_before_aug.images_unaug = 0\n        batch_before_aug.heatmaps_unaug = 1\n        batch_before_aug.segmentation_maps_unaug = 2\n        batch_before_aug.keypoints_unaug = 3\n        batch_before_aug.bounding_boxes_unaug = 4\n        batch_before_aug.polygons_unaug = 5\n        batch_before_aug.line_strings_unaug = 6\n\n        batch_inaug = _BatchInAugmentation(\n            images=10,\n            heatmaps=20,\n            segmentation_maps=30,\n            keypoints=40,\n            bounding_boxes=50,\n            polygons=60,\n            line_strings=70\n        )\n\n        batch = batch_inaug.to_batch(batch_before_aug)\n\n        assert batch.images_unaug == 0\n        assert batch.heatmaps_unaug == 1\n        assert batch.segmentation_maps_unaug == 2\n        assert batch.keypoints_unaug == 3\n        assert batch.bounding_boxes_unaug == 4\n        assert batch.polygons_unaug == 5\n        assert batch.line_strings_unaug == 6\n\n        assert batch.images_aug == 10\n        assert batch.heatmaps_aug == 20\n        assert batch.segmentation_maps_aug == 30\n        assert batch.keypoints_aug == 40\n        assert batch.bounding_boxes_aug == 50\n        assert batch.polygons_aug == 60\n        assert batch.line_strings_aug == 70\n\n    def test_deepcopy(self):\n        batch = _BatchInAugmentation(\n            images=np.full((1,), 0, dtype=np.uint8),\n            heatmaps=np.full((1,), 1, dtype=np.uint8),\n            segmentation_maps=np.full((1,), 2, dtype=np.uint8),\n            keypoints=np.full((1,), 3, dtype=np.uint8),\n            bounding_boxes=np.full((1,), 4, dtype=np.uint8),\n            polygons=np.full((1,), 5, dtype=np.uint8),\n            line_strings=np.full((1,), 6, dtype=np.uint8)\n        )\n\n        batch_copy = batch.deepcopy()\n\n        assert np.max(batch_copy.images) == 0\n        assert np.max(batch_copy.heatmaps) == 1\n        assert np.max(batch_copy.segmentation_maps) == 2\n        assert np.max(batch_copy.keypoints) == 3\n        assert np.max(batch_copy.bounding_boxes) == 4\n        assert np.max(batch_copy.polygons) == 5\n        assert np.max(batch_copy.line_strings) == 6\n"
  },
  {
    "path": "test/augmentables/test_bbs.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport warnings\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\n\nimport imgaug as ia\nimport imgaug.random as iarandom\nfrom imgaug.augmentables.bbs import _LabelOnImageDrawer\nfrom imgaug.testutils import wrap_shift_deprecation, assertWarns\n\n\nclass TestBoundingBox_project_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, cba, *args, **kwargs):\n        return cba.project_(*args, **kwargs)\n\n    def test_project_same_shape(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, (10, 10), (10, 10))\n\n        assert np.isclose(bb2.y1, 10)\n        assert np.isclose(bb2.x1, 20)\n        assert np.isclose(bb2.y2, 30)\n        assert np.isclose(bb2.x2, 40)\n\n    def test_project_upscale_by_2(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, (10, 10), (20, 20))\n\n        assert np.isclose(bb2.y1, 10*2)\n        assert np.isclose(bb2.x1, 20*2)\n        assert np.isclose(bb2.y2, 30*2)\n        assert np.isclose(bb2.x2, 40*2)\n\n    def test_project_downscale_by_2(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, (10, 10), (5, 5))\n\n        assert np.isclose(bb2.y1, 10*0.5)\n        assert np.isclose(bb2.x1, 20*0.5)\n        assert np.isclose(bb2.y2, 30*0.5)\n        assert np.isclose(bb2.x2, 40*0.5)\n\n    def test_project_onto_wider_image(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, (10, 10), (10, 20))\n\n        assert np.isclose(bb2.y1, 10*1)\n        assert np.isclose(bb2.x1, 20*2)\n        assert np.isclose(bb2.y2, 30*1)\n        assert np.isclose(bb2.x2, 40*2)\n\n    def test_project_onto_higher_image(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, (10, 10), (20, 10))\n\n        assert np.isclose(bb2.y1, 10*2)\n        assert np.isclose(bb2.x1, 20*1)\n        assert np.isclose(bb2.y2, 30*2)\n        assert np.isclose(bb2.x2, 40*1)\n\n    def test_inplaceness(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, (10, 10), (10, 10))\n\n        if self._is_inplace:\n            assert bb2 is bb\n        else:\n            assert bb2 is not bb\n\n\nclass TestBoundingBox_project(TestBoundingBox_project_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, cba, *args, **kwargs):\n        return cba.project(*args, **kwargs)\n\n\nclass TestBoundingBox_extend_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, cba, *args, **kwargs):\n        return cba.extend_(*args, **kwargs)\n\n    def test_extend_all_sides_by_1(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, all_sides=1)\n\n        assert bb2.y1 == 10-1\n        assert bb2.y2 == 30+1\n        assert bb2.x1 == 20-1\n        assert bb2.x2 == 40+1\n\n    def test_extend_all_sides_by_minus_1(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, all_sides=-1)\n\n        assert bb2.y1 == 10-(-1)\n        assert bb2.y2 == 30+(-1)\n        assert bb2.x1 == 20-(-1)\n        assert bb2.x2 == 40+(-1)\n\n    def test_extend_top_by_1(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, top=1)\n\n        assert bb2.y1 == 10-1\n        assert bb2.y2 == 30+0\n        assert bb2.x1 == 20-0\n        assert bb2.x2 == 40+0\n\n    def test_extend_right_by_1(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, right=1)\n\n        assert bb2.y1 == 10-0\n        assert bb2.y2 == 30+0\n        assert bb2.x1 == 20-0\n        assert bb2.x2 == 40+1\n\n    def test_extend_bottom_by_1(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, bottom=1)\n\n        assert bb2.y1 == 10-0\n        assert bb2.y2 == 30+1\n        assert bb2.x1 == 20-0\n        assert bb2.x2 == 40+0\n\n    def test_extend_left_by_1(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, left=1)\n\n        assert bb2.y1 == 10-0\n        assert bb2.y2 == 30+0\n        assert bb2.x1 == 20-1\n        assert bb2.x2 == 40+0\n\n    def test_inplaceness(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, all_sides=1)\n\n        if self._is_inplace:\n            assert bb2 is bb\n        else:\n            assert bb2 is not bb\n\n\nclass TestBoundingBox_extend(TestBoundingBox_extend_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, cba, *args, **kwargs):\n        return cba.extend(*args, **kwargs)\n\n\nclass TestBoundingBox_clip_out_of_image_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, cba, *args, **kwargs):\n        return cba.clip_out_of_image_(*args, **kwargs)\n\n    def test_clip_out_of_image_with_bb_fully_inside_image(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb_cut = self._func(bb, (100, 100, 3))\n\n        assert bb_cut.y1 == 10\n        assert bb_cut.x1 == 20\n        assert bb_cut.y2 == 30\n        assert bb_cut.x2 == 40\n\n    def test_clip_out_of_image_with_array_as_shape(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        image = np.zeros((100, 100, 3), dtype=np.uint8)\n\n        bb_cut = bb.clip_out_of_image(image)\n\n        assert bb_cut.y1 == 10\n        assert bb_cut.x1 == 20\n        assert bb_cut.y2 == 30\n        assert bb_cut.x2 == 40\n\n    def test_clip_out_of_image_with_bb_too_high(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb_cut = self._func(bb, (20, 100, 3))\n\n        assert bb_cut.y1 == 10\n        assert bb_cut.x1 == 20\n        assert np.isclose(bb_cut.y2, 20)\n        assert bb_cut.x2 == 40\n\n    def test_clip_out_of_image_with_bb_too_wide(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb_cut = self._func(bb, (100, 30, 3))\n\n        assert bb_cut.y1 == 10\n        assert bb_cut.x1 == 20\n        assert bb_cut.y2 == 30\n        assert np.isclose(bb_cut.x2, 30)\n\n    def test_inplaceness(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        bb2 = self._func(bb, (100, 100, 3))\n\n        if self._is_inplace:\n            assert bb2 is bb\n        else:\n            assert bb2 is not bb\n\n\nclass TestBoundingBox_clip_out_of_image(TestBoundingBox_clip_out_of_image_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, cba, *args, **kwargs):\n        return cba.clip_out_of_image(*args, **kwargs)\n\n\nclass TestBoundingBox_shift_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, cba, *args, **kwargs):\n        def _func_impl():\n            return cba.shift_(*args, **kwargs)\n\n        return wrap_shift_deprecation(_func_impl, *args, **kwargs)\n\n    def test_shift_by_x(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_top = self._func(bb, x=1)\n        assert bb_top.y1 == 10\n        assert bb_top.x1 == 20 + 1\n        assert bb_top.y2 == 30\n        assert bb_top.x2 == 40 + 1\n\n    def test_shift_by_y(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_top = self._func(bb, y=1)\n        assert bb_top.y1 == 10 + 1\n        assert bb_top.x1 == 20\n        assert bb_top.y2 == 30 + 1\n        assert bb_top.x2 == 40\n\n    def test_inplaceness(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = self._func(bb, y=0)\n\n        if self._is_inplace:\n            assert bb2 is bb\n        else:\n            assert bb2 is not bb\n\n\nclass TestBoundingBox_shift(TestBoundingBox_shift_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, cba, *args, **kwargs):\n        def _func_impl():\n            return cba.shift(*args, **kwargs)\n\n        return wrap_shift_deprecation(_func_impl, *args, **kwargs)\n\n    def test_shift_top_by_zero(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_top = self._func(bb, top=0)\n        assert bb_top.y1 == 10\n        assert bb_top.x1 == 20\n        assert bb_top.y2 == 30\n        assert bb_top.x2 == 40\n\n    def test_shift_right_by_zero(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_right = self._func(bb, right=0)\n        assert bb_right.y1 == 10\n        assert bb_right.x1 == 20\n        assert bb_right.y2 == 30\n        assert bb_right.x2 == 40\n\n    def test_shift_bottom_by_zero(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_bottom = self._func(bb, bottom=0)\n        assert bb_bottom.y1 == 10\n        assert bb_bottom.x1 == 20\n        assert bb_bottom.y2 == 30\n        assert bb_bottom.x2 == 40\n\n    def test_shift_left_by_zero(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_left = self._func(bb, left=0)\n        assert bb_left.y1 == 10\n        assert bb_left.x1 == 20\n        assert bb_left.y2 == 30\n        assert bb_left.x2 == 40\n\n    def test_shift_top_by_one(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_top = self._func(bb, top=1)\n        assert bb_top.y1 == 10+1\n        assert bb_top.x1 == 20\n        assert bb_top.y2 == 30+1\n        assert bb_top.x2 == 40\n\n    def test_shift_right_by_one(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_right = self._func(bb, right=1)\n        assert bb_right.y1 == 10\n        assert bb_right.x1 == 20-1\n        assert bb_right.y2 == 30\n        assert bb_right.x2 == 40-1\n\n    def test_shift_bottom_by_one(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_bottom = self._func(bb, bottom=1)\n        assert bb_bottom.y1 == 10-1\n        assert bb_bottom.x1 == 20\n        assert bb_bottom.y2 == 30-1\n        assert bb_bottom.x2 == 40\n\n    def test_shift_left_by_one(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_left = self._func(bb, left=1)\n        assert bb_left.y1 == 10\n        assert bb_left.x1 == 20+1\n        assert bb_left.y2 == 30\n        assert bb_left.x2 == 40+1\n\n    def test_shift_top_by_minus_one(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_top = self._func(bb, top=-1)\n        assert bb_top.y1 == 10-1\n        assert bb_top.x1 == 20\n        assert bb_top.y2 == 30-1\n        assert bb_top.x2 == 40\n\n    def test_shift_right_by_minus_one(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_right = self._func(bb, right=-1)\n        assert bb_right.y1 == 10\n        assert bb_right.x1 == 20+1\n        assert bb_right.y2 == 30\n        assert bb_right.x2 == 40+1\n\n    def test_shift_bottom_by_minus_one(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_bottom = self._func(bb, bottom=-1)\n        assert bb_bottom.y1 == 10+1\n        assert bb_bottom.x1 == 20\n        assert bb_bottom.y2 == 30+1\n        assert bb_bottom.x2 == 40\n\n    def test_shift_left_by_minus_one(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_left = self._func(bb, left=-1)\n        assert bb_left.y1 == 10\n        assert bb_left.x1 == 20-1\n        assert bb_left.y2 == 30\n        assert bb_left.x2 == 40-1\n\n    def test_shift_all_sides_by_individual_amounts(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb_mix = self._func(bb, top=1, bottom=2, left=3, right=4)\n        assert bb_mix.y1 == 10+1-2\n        assert bb_mix.x1 == 20+3-4\n        assert bb_mix.y2 == 30+3-4\n        assert bb_mix.x2 == 40+1-2\n\n\nclass TestBoundingBox(unittest.TestCase):\n    def test___init__(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        assert bb.y1 == 10\n        assert bb.x1 == 20\n        assert bb.y2 == 30\n        assert bb.x2 == 40\n        assert bb.label is None\n\n    def test___init___floats(self):\n        bb = ia.BoundingBox(y1=10.1, x1=20.2, y2=30.3, x2=40.4)\n        assert np.isclose(bb.y1, 10.1)\n        assert np.isclose(bb.x1, 20.2)\n        assert np.isclose(bb.y2, 30.3)\n        assert np.isclose(bb.x2, 40.4)\n        assert bb.label is None\n\n    def test___init___label(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40, label=\"foo\")\n        assert bb.y1 == 10\n        assert bb.x1 == 20\n        assert bb.y2 == 30\n        assert bb.x2 == 40\n        assert bb.label == \"foo\"\n\n    def test___init___wrong_x1_x2_order(self):\n        bb = ia.BoundingBox(y1=10, x1=40, y2=30, x2=20)\n        assert bb.y1 == 10\n        assert bb.x1 == 20\n        assert bb.y2 == 30\n        assert bb.x2 == 40\n\n    def test___init___wrong_y1_y2_order(self):\n        bb = ia.BoundingBox(y1=30, x1=20, y2=10, x2=40)\n        assert bb.y1 == 10\n        assert bb.x1 == 20\n        assert bb.y2 == 30\n        assert bb.x2 == 40\n\n    def test_coords_property_ints(self):\n        bb = ia.BoundingBox(x1=10, y1=20, x2=30, y2=40)\n        coords = bb.coords\n        assert np.allclose(coords, [[10, 20], [30, 40]],\n                           atol=1e-4, rtol=0)\n\n    def test_coords_property_floats(self):\n        bb = ia.BoundingBox(x1=10.1, y1=20.2, x2=30.3, y2=40.4)\n        coords = bb.coords\n        assert np.allclose(coords, [[10.1, 20.2], [30.3, 40.4]],\n                           atol=1e-4, rtol=0)\n\n    def test_xy_int_properties(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        assert bb.y1_int == 10\n        assert bb.x1_int == 20\n        assert bb.y2_int == 30\n        assert bb.x2_int == 40\n\n    def test_xy_int_properties_floats(self):\n        bb = ia.BoundingBox(y1=10.1, x1=20.2, y2=30.6, x2=40.7)\n        assert bb.y1_int == 10\n        assert bb.x1_int == 20\n        assert bb.y2_int == 31\n        assert bb.x2_int == 41\n\n    def test_width(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        assert bb.width == 40 - 20\n\n    def test_width_floats(self):\n        bb = ia.BoundingBox(y1=10.1, x1=20.2, y2=30.3, x2=40.4)\n        assert np.isclose(bb.width, 40.4 - 20.2)\n\n    def test_height(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        assert bb.height == 30 - 10\n\n    def test_height_floats(self):\n        bb = ia.BoundingBox(y1=10.1, x1=20.2, y2=30.3, x2=40.4)\n        assert np.isclose(bb.height, 30.3 - 10.1)\n\n    def test_center_x(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        expected = 20 + (40 - 20)/2\n        assert np.isclose(bb.center_x, expected)\n\n    def test_center_x_floats(self):\n        bb = ia.BoundingBox(y1=10.1, x1=20.2, y2=30.3, x2=40.4)\n        expected = 20.2 + (40.4 - 20.2)/2\n        assert np.isclose(bb.center_x, expected)\n\n    def test_center_y(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        expected = 10 + (30 - 10)/2\n        assert np.isclose(bb.center_y, expected)\n\n    def test_center_y_floats(self):\n        bb = ia.BoundingBox(y1=10.1, x1=20.2, y2=30.3, x2=40.4)\n        expected = 10.1 + (30.3 - 10.1)/2\n        assert np.isclose(bb.center_y, expected)\n\n    def test_area(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        assert bb.area == (30-10) * (40-20)\n\n    def test_area_floats(self):\n        bb = ia.BoundingBox(y1=10.1, x1=20.2, y2=30.3, x2=40.4)\n        assert np.isclose(bb.area, (30.3-10.1) * (40.4-20.2))\n\n    def test_contains(self):\n        bb = ia.BoundingBox(y1=1, x1=2, y2=1+4, x2=2+5, label=None)\n        assert bb.contains(ia.Keypoint(x=2.5, y=1.5)) is True\n        assert bb.contains(ia.Keypoint(x=2, y=1)) is True\n        assert bb.contains(ia.Keypoint(x=0, y=0)) is False\n\n    def test_intersection(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=10, x1=39, y2=30, x2=59)\n\n        bb_inter = bb1.intersection(bb2)\n\n        assert bb_inter.x1 == 39\n        assert bb_inter.x2 == 40\n        assert bb_inter.y1 == 10\n        assert bb_inter.y2 == 30\n\n    def test_intersection_of_non_overlapping_bbs(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=10, x1=41, y2=30, x2=61)\n\n        bb_inter = bb1.intersection(bb2, default=False)\n\n        assert bb_inter is False\n\n    def test_union(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=10, x1=39, y2=30, x2=59)\n\n        bb_union = bb1.union(bb2)\n\n        assert bb_union.x1 == 20\n        assert bb_union.x2 == 59\n        assert bb_union.y1 == 10\n        assert bb_union.y2 == 30\n\n    def test_iou_of_identical_bbs(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n\n        iou = bb1.iou(bb2)\n\n        assert np.isclose(iou, 1.0)\n\n    def test_iou_of_non_overlapping_bbs(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=10, x1=41, y2=30, x2=61)\n\n        iou = bb1.iou(bb2)\n\n        assert np.isclose(iou, 0.0)\n\n    def test_iou_of_partially_overlapping_bbs(self):\n        bb1 = ia.BoundingBox(y1=10, x1=10, y2=20, x2=20)\n        bb2 = ia.BoundingBox(y1=15, x1=15, y2=25, x2=25)\n\n        iou = bb1.iou(bb2)\n\n        area_union = 10 * 10 + 10 * 10 - 5 * 5\n        area_intersection = 5 * 5\n        iou_expected = area_intersection / area_union\n        assert np.isclose(iou, iou_expected)\n\n    def test_compute_out_of_image_area__fully_inside(self):\n        bb = ia.BoundingBox(y1=10.1, x1=20.2, y2=30.3, x2=40.4)\n        image_shape = (100, 200, 3)\n        area_ooi = bb.compute_out_of_image_area(image_shape)\n        assert np.isclose(area_ooi, 0.0)\n\n    def test_compute_out_of_image_area__partially_ooi(self):\n        bb = ia.BoundingBox(y1=10, x1=-20, y2=30, x2=40)\n        image_shape = (100, 200, 3)\n        area_ooi = bb.compute_out_of_image_area(image_shape)\n        assert np.isclose(area_ooi, (0-(-20))*(30-10))\n\n    def test_compute_out_of_image_area__fully_ooi(self):\n        bb = ia.BoundingBox(y1=10, x1=-20, y2=30, x2=-10)\n        image_shape = (100, 200, 3)\n        area_ooi = bb.compute_out_of_image_area(image_shape)\n        assert np.isclose(area_ooi, 20*10)\n\n    def test_compute_out_of_image_area__zero_sized_image(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        image_shape = (0, 0, 3)\n        area_ooi = bb.compute_out_of_image_area(image_shape)\n        assert np.isclose(area_ooi, bb.area)\n\n    def test_compute_out_of_image_area__bb_has_zero_sized_area(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=10, x2=20)\n        image_shape = (100, 200, 3)\n        area_ooi = bb.compute_out_of_image_area(image_shape)\n        assert np.isclose(area_ooi, 0.0)\n\n    def test_compute_out_of_image_fraction__inside_image(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        image_shape = (100, 200, 3)\n\n        factor = bb.compute_out_of_image_fraction(image_shape)\n\n        assert np.isclose(factor, 0.0)\n\n    def test_compute_out_of_image_fraction__partially_ooi(self):\n        bb = ia.BoundingBox(y1=10, x1=-20, y2=30, x2=40)\n        image_shape = (100, 200, 3)\n\n        factor = bb.compute_out_of_image_fraction(image_shape)\n\n        expected = (20 * 20) / (20 * 60)\n        assert np.isclose(factor, expected)\n\n    def test_compute_out_of_image_fraction__fully_ooi(self):\n        bb = ia.BoundingBox(y1=10, x1=-20, y2=30, x2=0)\n        image_shape = (100, 200, 3)\n\n        factor = bb.compute_out_of_image_fraction(image_shape)\n\n        assert np.isclose(factor, 1.0)\n\n    def test_compute_out_of_image_fraction__zero_area_inside_image(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=10, x2=20)\n        image_shape = (100, 200, 3)\n\n        factor = bb.compute_out_of_image_fraction(image_shape)\n\n        assert np.isclose(factor, 0.0)\n\n    def test_compute_out_of_image_fraction__zero_area_ooi(self):\n        bb = ia.BoundingBox(y1=-10, x1=20, y2=-10, x2=20)\n        image_shape = (100, 200, 3)\n\n        factor = bb.compute_out_of_image_fraction(image_shape)\n\n        assert np.isclose(factor, 1.0)\n\n    def test_is_fully_within_image(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40, label=None)\n        assert bb.is_fully_within_image((100, 100, 3)) is True\n        assert bb.is_fully_within_image((20, 100, 3)) is False\n        assert bb.is_fully_within_image((100, 30, 3)) is False\n        assert bb.is_fully_within_image((1, 1, 3)) is False\n\n    def test_is_partly_within_image(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40, label=None)\n        assert bb.is_partly_within_image((100, 100, 3)) is True\n        assert bb.is_partly_within_image((20, 100, 3)) is True\n        assert bb.is_partly_within_image((100, 30, 3)) is True\n        assert bb.is_partly_within_image((1, 1, 3)) is False\n\n    def test_is_out_of_image(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40, label=None)\n\n        subtests = [\n            ((100, 100, 3), True, True, False),\n            ((100, 100, 3), False, True, False),\n            ((100, 100, 3), True, False, False),\n            ((20, 100, 3), True, True, True),\n            ((20, 100, 3), False, True, False),\n            ((20, 100, 3), True, False, True),\n            ((100, 30, 3), True, True, True),\n            ((100, 30, 3), False, True, False),\n            ((100, 30, 3), True, False, True),\n            ((1, 1, 3), True, True, True),\n            ((1, 1, 3), False, True, True),\n            ((1, 1, 3), True, False, False)\n        ]\n\n        for shape, partly, fully, expected in subtests:\n            with self.subTest(shape=shape, partly=partly, fully=fully):\n                observed = bb.is_out_of_image(shape,\n                                              partly=partly, fully=fully)\n                assert observed is expected\n\n    @mock.patch(\"imgaug.augmentables.bbs._LabelOnImageDrawer\")\n    def test_draw_label_on_image_mocked(self, mock_drawer):\n        mock_drawer.return_value = mock_drawer\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        bb = ia.BoundingBox(y1=1, x1=1, y2=3, x2=3)\n\n        result = bb.draw_label_on_image(image)\n\n        kwargs = mock_drawer.call_args_list[0][1]\n        assert kwargs[\"color\"] == (0, 255, 0)\n        assert kwargs[\"color_text\"] is None\n        assert kwargs[\"color_bg\"] is None\n        assert np.isclose(kwargs[\"alpha\"], 1.0)\n        assert kwargs[\"size\"] == 1\n        assert kwargs[\"size_text\"] == 20\n        assert kwargs[\"height\"] == 30\n        assert kwargs[\"raise_if_out_of_image\"] is False\n\n        assert mock_drawer.draw_on_image.call_count == 1\n\n    @mock.patch(\"imgaug.augmentables.bbs._LabelOnImageDrawer\")\n    def test_draw_label_on_image_mocked_inplace(self, mock_drawer):\n        mock_drawer.return_value = mock_drawer\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        bb = ia.BoundingBox(y1=1, x1=1, y2=3, x2=3)\n\n        result = bb.draw_label_on_image(image, copy=False)\n\n        kwargs = mock_drawer.call_args_list[0][1]\n        assert kwargs[\"color\"] == (0, 255, 0)\n        assert kwargs[\"color_text\"] is None\n        assert kwargs[\"color_bg\"] is None\n        assert np.isclose(kwargs[\"alpha\"], 1.0)\n        assert kwargs[\"size\"] == 1\n        assert kwargs[\"size_text\"] == 20\n        assert kwargs[\"height\"] == 30\n        assert kwargs[\"raise_if_out_of_image\"] is False\n\n        assert mock_drawer.draw_on_image_.call_count == 1\n\n    def test_draw_label_on_image(self):\n        image = np.zeros((100, 70, 3), dtype=np.uint8)\n        bb = ia.BoundingBox(y1=40, x1=10, y2=50, x2=40)\n\n        result = bb.draw_label_on_image(image,\n                                        color_bg=(123, 123, 123),\n                                        color_text=(222, 222, 222))\n\n        color_bg = np.uint8([123, 123, 123]).reshape((1, 1, -1))\n        color_text = np.uint8([222, 222, 222]).reshape((1, 1, -1))\n        matches_bg = np.min(result == color_bg, axis=-1)\n        matches_text = np.min(result == color_text, axis=-1)\n        assert np.any(matches_bg > 0)\n        assert np.any(matches_text > 0)\n\n    @classmethod\n    def _get_standard_draw_box_on_image_vars(cls):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        bb = ia.BoundingBox(y1=1, x1=1, y2=3, x2=3)\n        bb_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        bb_mask[1:3+1, 1] = True\n        bb_mask[1:3+1, 3] = True\n        bb_mask[1, 1:3+1] = True\n        bb_mask[3, 1:3+1] = True\n        return image, bb, bb_mask\n\n    def test_draw_box_on_image(self):\n        image, bb, bb_mask = self._get_standard_draw_box_on_image_vars()\n\n        image_bb = bb.draw_box_on_image(\n            image, color=[255, 255, 255], alpha=1.0, size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_bb[bb_mask] == [255, 255, 255])\n        assert np.all(image_bb[~bb_mask] == [0, 0, 0])\n        assert np.all(image == 0)\n\n    def test_draw_box_on_image_red_color(self):\n        image, bb, bb_mask = self._get_standard_draw_box_on_image_vars()\n\n        image_bb = bb.draw_box_on_image(\n            image, color=[255, 0, 0], alpha=1.0, size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_bb[bb_mask] == [255, 0, 0])\n        assert np.all(image_bb[~bb_mask] == [0, 0, 0])\n\n    def test_draw_box_on_image_single_int_as_color(self):\n        image, bb, bb_mask = self._get_standard_draw_box_on_image_vars()\n\n        image_bb = bb.draw_box_on_image(\n            image, color=128, alpha=1.0, size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_bb[bb_mask] == [128, 128, 128])\n        assert np.all(image_bb[~bb_mask] == [0, 0, 0])\n\n    def test_draw_box_on_image_alpha_at_50_percent(self):\n        image, bb, bb_mask = self._get_standard_draw_box_on_image_vars()\n\n        image_bb = bb.draw_box_on_image(\n            image + 100, color=[200, 200, 200], alpha=0.5, size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_bb[bb_mask] == [150, 150, 150])\n        assert np.all(image_bb[~bb_mask] == [100, 100, 100])\n\n    def test_draw_box_on_image_alpha_at_50_percent_and_float32_image(self):\n        image, bb, bb_mask = self._get_standard_draw_box_on_image_vars()\n\n        image_bb = bb.draw_box_on_image(\n            (image+100).astype(np.float32),\n            color=[200, 200, 200], alpha=0.5, size=1,\n            copy=True, raise_if_out_of_image=False)\n\n        assert np.sum(np.abs((image_bb - [150, 150, 150])[bb_mask])) < 0.1\n        assert np.sum(np.abs((image_bb - [100, 100, 100])[~bb_mask])) < 0.1\n\n    def test_draw_box_on_image_no_copy(self):\n        image, bb, bb_mask = self._get_standard_draw_box_on_image_vars()\n\n        image_bb = bb.draw_box_on_image(\n            image, color=[255, 255, 255], alpha=1.0, size=1, copy=False,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_bb[bb_mask] == [255, 255, 255])\n        assert np.all(image_bb[~bb_mask] == [0, 0, 0])\n        assert np.all(image[bb_mask] == [255, 255, 255])\n        assert np.all(image[~bb_mask] == [0, 0, 0])\n\n    def test_draw_box_on_image_bb_outside_of_image(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        bb = ia.BoundingBox(y1=-1, x1=-1, y2=2, x2=2)\n        bb_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        bb_mask[2, 0:3] = True\n        bb_mask[0:3, 2] = True\n\n        image_bb = bb.draw_box_on_image(\n            image, color=[255, 255, 255], alpha=1.0, size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_bb[bb_mask] == [255, 255, 255])\n        assert np.all(image_bb[~bb_mask] == [0, 0, 0])\n\n    def test_draw_box_on_image_bb_outside_of_image_and_very_small(self):\n        image, bb, bb_mask = self._get_standard_draw_box_on_image_vars()\n        bb = ia.BoundingBox(y1=-1, x1=-1, y2=1, x2=1)\n        bb_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        bb_mask[0:1+1, 1] = True\n        bb_mask[1, 0:1+1] = True\n\n        image_bb = bb.draw_box_on_image(\n            image, color=[255, 255, 255], alpha=1.0, size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_bb[bb_mask] == [255, 255, 255])\n        assert np.all(image_bb[~bb_mask] == [0, 0, 0])\n\n    def test_draw_box_on_image_size_2(self):\n        image, bb, _ = self._get_standard_draw_box_on_image_vars()\n        bb_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        bb_mask[0:5, 0:5] = True\n        bb_mask[2, 2] = False\n\n        image_bb = bb.draw_box_on_image(\n            image, color=[255, 255, 255], alpha=1.0, size=2, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_bb[bb_mask] == [255, 255, 255])\n        assert np.all(image_bb[~bb_mask] == [0, 0, 0])\n\n    def test_draw_box_on_image_raise_true_but_bb_partially_inside_image(self):\n        image, bb, bb_mask = self._get_standard_draw_box_on_image_vars()\n        bb = ia.BoundingBox(y1=-1, x1=-1, y2=1, x2=1)\n\n        _ = bb.draw_box_on_image(\n            image, color=[255, 255, 255], alpha=1.0, size=1, copy=True,\n            raise_if_out_of_image=True)\n\n    def test_draw_box_on_image_raise_true_and_bb_fully_outside_image(self):\n        image, bb, bb_mask = self._get_standard_draw_box_on_image_vars()\n        bb = ia.BoundingBox(y1=-5, x1=-5, y2=-1, x2=-1)\n\n        with self.assertRaises(Exception) as context:\n            _ = bb.draw_box_on_image(\n                image, color=[255, 255, 255], alpha=1.0, size=1, copy=True,\n                raise_if_out_of_image=True)\n\n        assert \"Cannot draw bounding box\" in str(context.exception)\n\n    def test_draw_on_image_label_is_none(self):\n        # if label is None, no label box should be drawn, only the rectangle\n        # box below the label\n        image = np.zeros((100, 70, 3), dtype=np.uint8)\n        bb = ia.BoundingBox(y1=40, x1=10, y2=50, x2=40, label=None)\n\n        image_drawn = bb.draw_on_image(image)\n\n        expected = bb.draw_box_on_image(image)\n        assert np.array_equal(image_drawn, expected)\n\n    def test_draw_on_image_label_is_str(self):\n        # if label is None, no label box should be drawn, only the rectangle\n        # box below the label\n        image = np.zeros((100, 70, 3), dtype=np.uint8)\n        bb = ia.BoundingBox(y1=40, x1=10, y2=50, x2=40, label=\"Foo\")\n\n        image_drawn = bb.draw_on_image(image)\n\n        expected = bb.draw_box_on_image(image)\n        expected = bb.draw_label_on_image(expected)\n        assert np.array_equal(image_drawn, expected)\n\n    def test_extract_from_image(self):\n        image = iarandom.RNG(1234).integers(0, 255, size=(10, 10, 3))\n        bb = ia.BoundingBox(y1=1, y2=3, x1=1, x2=3)\n        image_sub = bb.extract_from_image(image)\n        assert np.array_equal(image_sub, image[1:3, 1:3, :])\n\n    def test_extract_from_image_no_channels(self):\n        image = iarandom.RNG(1234).integers(0, 255, size=(10, 10))\n        bb = ia.BoundingBox(y1=1, y2=3, x1=1, x2=3)\n        image_sub = bb.extract_from_image(image)\n        assert np.array_equal(image_sub, image[1:3, 1:3])\n\n    def test_extract_from_image_bb_partially_out_of_image(self):\n        image = iarandom.RNG(1234).integers(0, 255, size=(10, 10, 3))\n\n        bb = ia.BoundingBox(y1=8, y2=11, x1=8, x2=11)\n        image_sub = bb.extract_from_image(image)\n\n        image_pad = np.pad(\n            image,\n            ((0, 1), (0, 1), (0, 0)),\n            mode=\"constant\",\n            constant_values=0)  # pad at bottom and right each 1px (black)\n        assert np.array_equal(image_sub, image_pad[8:11, 8:11, :])\n\n    def test_extract_from_image_bb_partially_out_of_image_no_channels(self):\n        image = iarandom.RNG(1234).integers(0, 255, size=(10, 10))\n\n        bb = ia.BoundingBox(y1=8, y2=11, x1=8, x2=11)\n        image_sub = bb.extract_from_image(image)\n\n        image_pad = np.pad(\n            image,\n            ((0, 1), (0, 1)),\n            mode=\"constant\",\n            constant_values=0)  # pad at bottom and right each 1px (black)\n        assert np.array_equal(image_sub, image_pad[8:11, 8:11])\n\n    def test_extract_from_image_bb_partially_out_of_image_top_left(self):\n        image = iarandom.RNG(1234).integers(0, 255, size=(10, 10, 3))\n\n        bb = ia.BoundingBox(y1=-1, y2=3, x1=-1, x2=4)\n        image_sub = bb.extract_from_image(image)\n\n        image_pad = np.pad(\n            image,\n            ((1, 0), (1, 0), (0, 0)),\n            mode=\"constant\",\n            constant_values=0)  # pad at top and left each 1px (black)\n        assert np.array_equal(image_sub, image_pad[0:4, 0:5, :])\n\n    def test_extract_from_image_float_coords(self):\n        image = iarandom.RNG(1234).integers(0, 255, size=(10, 10, 3))\n\n        bb = ia.BoundingBox(y1=1, y2=1.99999, x1=1, x2=1.99999)\n        image_sub = bb.extract_from_image(image)\n\n        assert np.array_equal(image_sub, image[1:1+1, 1:1+1, :])\n\n    def test_extract_from_image_bb_height_is_zero(self):\n        image = iarandom.RNG(1234).integers(0, 255, size=(10, 10, 3))\n\n        bb = ia.BoundingBox(y1=1, y2=1, x1=2, x2=4)\n        image_sub = bb.extract_from_image(image)\n\n        assert np.array_equal(image_sub, image[1:1+1, 2:4, :])\n\n    def test_extract_from_image_bb_width_is_zero(self):\n        image = iarandom.RNG(1234).integers(0, 255, size=(10, 10, 3))\n\n        bb = ia.BoundingBox(y1=1, y2=1, x1=2, x2=2)\n        image_sub = bb.extract_from_image(image)\n\n        assert np.array_equal(image_sub, image[1:1+1, 2:2+1, :])\n\n    def test_to_keypoints(self):\n        bb = ia.BoundingBox(y1=1, y2=3, x1=1, x2=3)\n\n        kps = bb.to_keypoints()\n\n        assert len(kps) == 4\n        assert kps[0].y == 1\n        assert kps[0].x == 1\n        assert kps[1].y == 1\n        assert kps[1].x == 3\n        assert kps[2].y == 3\n        assert kps[2].x == 3\n        assert kps[3].y == 3\n        assert kps[3].x == 1\n\n    def test_to_polygon(self):\n        bb = ia.BoundingBox(y1=1, y2=3, x1=1, x2=3)\n\n        poly = bb.to_polygon()\n\n        assert poly.coords_almost_equals([\n            (1, 1),\n            (3, 1),\n            (3, 3,),\n            (1, 3)\n        ])\n\n    def test_coords_almost_equals(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n        other = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n\n        equal = bb.coords_almost_equals(other)\n\n        assert equal\n\n    def test_coords_almost_equals__unequal(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n        other = ia.BoundingBox(x1=1+1, y1=3+1, x2=1+1, y2=3+1)\n\n        equal = bb.coords_almost_equals(other)\n\n        assert not equal\n\n    def test_coords_almost_equals__dist_below_max_distance(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n        other = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3+1e-5)\n\n        equal = bb.coords_almost_equals(other, max_distance=1e-4)\n\n        assert equal\n\n    def test_coords_almost_equals__dist_above_max_distance(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n        other = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3+1e-3)\n\n        equal = bb.coords_almost_equals(other, max_distance=1e-4)\n\n        assert not equal\n\n    def test_coords_almost_equals__input_is_array(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n        other = np.float32([[1, 3], [1, 3]])\n\n        equal = bb.coords_almost_equals(other)\n\n        assert equal\n\n    def test_coords_almost_equals__input_is_array_not_equal(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n        other = np.float32([[1, 3], [1, 3+0.5]])\n\n        equal = bb.coords_almost_equals(other)\n\n        assert not equal\n\n    def test_coords_almost_equals__input_is_list(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n        other = [[1, 3], [1, 3]]\n\n        equal = bb.coords_almost_equals(other)\n\n        assert equal\n\n    def test_coords_almost_equals__input_is_list_not_equal(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n        other = [[1, 3], [1, 3+0.5]]\n\n        equal = bb.coords_almost_equals(other)\n\n        assert not equal\n\n    def test_coords_almost_equals__bad_datatype(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n\n        with self.assertRaises(ValueError) as cm:\n            _ = bb.coords_almost_equals(False)\n\n        assert \"Expected 'other'\" in str(cm.exception)\n\n    @mock.patch(\"imgaug.augmentables.bbs.BoundingBox.coords_almost_equals\")\n    def test_almost_equals(self, mock_cae):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n        other = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n\n        equal = bb.almost_equals(other, max_distance=1)\n\n        assert equal\n        mock_cae.assert_called_once_with(other, max_distance=1)\n\n    def test_almost_equals__labels_none_vs_string(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3, label=\"foo\")\n        other = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3)\n\n        equal = bb.almost_equals(other)\n\n        assert not equal\n\n    def test_almost_equals__labels_different_strings(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3, label=\"foo\")\n        other = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3, label=\"bar\")\n\n        equal = bb.almost_equals(other)\n\n        assert not equal\n\n    def test_almost_equals__same_string(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3, label=\"foo\")\n        other = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3, label=\"foo\")\n\n        equal = bb.almost_equals(other)\n\n        assert equal\n\n    def test_almost_equals__distance_above_threshold(self):\n        bb = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3, label=\"foo\")\n        other = ia.BoundingBox(x1=1, y1=3, x2=1, y2=3+1e-1, label=\"foo\")\n\n        equal = bb.almost_equals(other, max_distance=1e-2)\n\n        assert not equal\n\n    def test_from_point_soup__empty_list(self):\n        with self.assertRaises(AssertionError) as ctx:\n            _ = ia.BoundingBox.from_point_soup([])\n        assert \"Expected to get at least one point\" in str(ctx.exception)\n\n    def test_from_point_soup__empty_array(self):\n        with self.assertRaises(AssertionError) as ctx:\n            _ = ia.BoundingBox.from_point_soup(np.zeros((0, 2)))\n        assert \"Expected to get at least one point\" in str(ctx.exception)\n\n    def test_from_point_soup__list_with_single_point(self):\n        points = [(1, 2)]\n        bb = ia.BoundingBox.from_point_soup(points)\n        assert bb.x1 == 1\n        assert bb.y1 == 2\n        assert bb.x2 == 1\n        assert bb.y2 == 2\n\n    def test_from_point_soup__list_with_single_point__single_level(self):\n        points = [1, 2]\n        bb = ia.BoundingBox.from_point_soup(points)\n        assert bb.x1 == 1\n        assert bb.y1 == 2\n        assert bb.x2 == 1\n        assert bb.y2 == 2\n\n    def test_from_point_soup__list_with_two_points(self):\n        points = [(1, 2), (3, 4)]\n        bb = ia.BoundingBox.from_point_soup(points)\n        assert bb.x1 == 1\n        assert bb.y1 == 2\n        assert bb.x2 == 3\n        assert bb.y2 == 4\n\n    def test_from_point_soup__list_with_three_points(self):\n        points = [(1, 4), (3, 2), (15, 16)]\n        bb = ia.BoundingBox.from_point_soup(points)\n        assert bb.x1 == 1\n        assert bb.y1 == 2\n        assert bb.x2 == 15\n        assert bb.y2 == 16\n\n    def test_from_point_soup__array_with_single_point(self):\n        points = np.float32([(1, 2)])\n        bb = ia.BoundingBox.from_point_soup(points)\n        assert bb.x1 == 1\n        assert bb.y1 == 2\n        assert bb.x2 == 1\n        assert bb.y2 == 2\n\n    def test_from_point_soup__array_with_single_point__single_level(self):\n        points = np.float32([1, 2])\n        bb = ia.BoundingBox.from_point_soup(points)\n        assert bb.x1 == 1\n        assert bb.y1 == 2\n        assert bb.x2 == 1\n        assert bb.y2 == 2\n\n    def test_from_point_soup__array_with_two_points__single_level(self):\n        points = np.float32([1, 2, 3, 4])\n        bb = ia.BoundingBox.from_point_soup(points)\n        assert bb.x1 == 1\n        assert bb.y1 == 2\n        assert bb.x2 == 3\n        assert bb.y2 == 4\n\n    def test_from_point_soup__array_with_two_points(self):\n        points = np.float32([(1, 2), (3, 4)])\n        bb = ia.BoundingBox.from_point_soup(points)\n        assert bb.x1 == 1\n        assert bb.y1 == 2\n        assert bb.x2 == 3\n        assert bb.y2 == 4\n\n    def test_from_point_soup__array_with_three_points(self):\n        points = np.float32([(1, 4), (3, 2), (15, 16)])\n        bb = ia.BoundingBox.from_point_soup(points)\n        assert bb.x1 == 1\n        assert bb.y1 == 2\n        assert bb.x2 == 15\n        assert bb.y2 == 16\n\n    def test_copy(self):\n        bb = ia.BoundingBox(y1=1, y2=3, x1=1, x2=3, label=\"test\")\n\n        bb2 = bb.copy()\n\n        assert bb2.y1 == 1\n        assert bb2.y2 == 3\n        assert bb2.x1 == 1\n        assert bb2.x2 == 3\n        assert bb2.label == \"test\"\n\n    def test_copy_and_replace_attributes(self):\n        bb = ia.BoundingBox(y1=1, y2=3, x1=1, x2=3, label=\"test\")\n\n        bb2 = bb.copy(y1=10, x1=20, y2=30, x2=40, label=\"test2\")\n\n        assert bb2.y1 == 10\n        assert bb2.x1 == 20\n        assert bb2.y2 == 30\n        assert bb2.x2 == 40\n        assert bb2.label == \"test2\"\n\n    def test_deepcopy(self):\n        bb = ia.BoundingBox(y1=1, y2=3, x1=1, x2=3, label=[\"test\"])\n\n        bb2 = bb.deepcopy()\n        bb2.label[0] = \"foo\"\n\n        assert bb2.y1 == 1\n        assert bb2.y2 == 3\n        assert bb2.x1 == 1\n        assert bb2.x2 == 3\n        assert bb2.label[0] == \"foo\"\n        assert bb.label[0] == \"test\"\n\n    def test_deepcopy_and_replace_attributes(self):\n        bb = ia.BoundingBox(y1=1, y2=3, x1=1, x2=3, label=\"test\")\n\n        bb2 = bb.deepcopy(y1=10, y2=30, x1=15, x2=35, label=\"asd\")\n\n        assert bb2.y1 == 10\n        assert bb2.y2 == 30\n        assert bb2.x1 == 15\n        assert bb2.x2 == 35\n        assert bb2.label == \"asd\"\n        assert bb.label == \"test\"\n\n    def test___getitem__(self):\n        cba = ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)\n        assert np.allclose(cba[0], (1, 2))\n        assert np.allclose(cba[1], (3, 4))\n\n    def test___iter__(self):\n        cba = ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)\n        for i, xy in enumerate(cba):\n            assert i in [0, 1]\n            if i == 0:\n                assert np.allclose(xy, (1, 2))\n            elif i == 1:\n                assert np.allclose(xy, (3, 4))\n        assert i == 1\n\n    def test_string_conversion(self):\n        bb = ia.BoundingBox(y1=1, y2=3, x1=1, x2=3)\n        assert (\n            bb.__str__()\n            == bb.__repr__()\n            == \"BoundingBox(\"\n               \"x1=1.0000, y1=1.0000, x2=3.0000, y2=3.0000, \"\n               \"label=None)\"\n        )\n\n    def test_string_conversion_with_label(self):\n        bb = ia.BoundingBox(y1=1, y2=3, x1=1, x2=3, label=\"foo\")\n        assert (\n            bb.__str__()\n            == bb.__repr__()\n            == \"BoundingBox(\"\n               \"x1=1.0000, y1=1.0000, x2=3.0000, y2=3.0000, \"\n               \"label=foo)\"\n        )\n\n\nclass TestBoundingBoxesOnImage_items_setter(unittest.TestCase):\n    def test_with_list_of_bounding_boxes(self):\n        bbs = [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4),\n               ia.BoundingBox(x1=3, y1=4, x2=5, y2=6)]\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(10, 20, 3))\n        bbsoi.items = bbs\n        assert np.all([\n            (bb_i.x1 == bb_j.x1\n             and bb_i.y1 == bb_j.y1\n             and bb_i.x2 == bb_j.x2\n             and bb_i.y2 == bb_j.y2)\n            for bb_i, bb_j\n            in zip(bbsoi.bounding_boxes, bbs)\n        ])\n\n\nclass TestBoundingBoxesOnImage_on_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, cbaoi, *args, **kwargs):\n        return cbaoi.on_(*args, **kwargs)\n\n    def test_on_same_height_width(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=45)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_projected = self._func(bbsoi, (40, 50))\n\n        assert bbsoi_projected.bounding_boxes[0].y1 == 10\n        assert bbsoi_projected.bounding_boxes[0].x1 == 20\n        assert bbsoi_projected.bounding_boxes[0].y2 == 30\n        assert bbsoi_projected.bounding_boxes[0].x2 == 40\n        assert bbsoi_projected.bounding_boxes[1].y1 == 15\n        assert bbsoi_projected.bounding_boxes[1].x1 == 25\n        assert bbsoi_projected.bounding_boxes[1].y2 == 35\n        assert bbsoi_projected.bounding_boxes[1].x2 == 45\n        assert bbsoi_projected.shape == (40, 50)\n\n    def test_on_upscaled_by_2(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=45)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_projected = self._func(bbsoi, (40*2, 50*2, 3))\n\n        assert bbsoi_projected.bounding_boxes[0].y1 == 10*2\n        assert bbsoi_projected.bounding_boxes[0].x1 == 20*2\n        assert bbsoi_projected.bounding_boxes[0].y2 == 30*2\n        assert bbsoi_projected.bounding_boxes[0].x2 == 40*2\n        assert bbsoi_projected.bounding_boxes[1].y1 == 15*2\n        assert bbsoi_projected.bounding_boxes[1].x1 == 25*2\n        assert bbsoi_projected.bounding_boxes[1].y2 == 35*2\n        assert bbsoi_projected.bounding_boxes[1].x2 == 45*2\n        assert bbsoi_projected.shape == (40*2, 50*2, 3)\n\n    def test_on_upscaled_by_2_with_shape_given_as_array(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=45)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_projected = self._func(bbsoi, np.zeros((40*2, 50*2, 3), dtype=np.uint8))\n\n        assert bbsoi_projected.bounding_boxes[0].y1 == 10*2\n        assert bbsoi_projected.bounding_boxes[0].x1 == 20*2\n        assert bbsoi_projected.bounding_boxes[0].y2 == 30*2\n        assert bbsoi_projected.bounding_boxes[0].x2 == 40*2\n        assert bbsoi_projected.bounding_boxes[1].y1 == 15*2\n        assert bbsoi_projected.bounding_boxes[1].x1 == 25*2\n        assert bbsoi_projected.bounding_boxes[1].y2 == 35*2\n        assert bbsoi_projected.bounding_boxes[1].x2 == 45*2\n        assert bbsoi_projected.shape == (40*2, 50*2, 3)\n\n    def test_inplaceness(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=45)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi2 = self._func(bbsoi, (40, 50))\n\n        if self._is_inplace:\n            assert bbsoi2 is bbsoi\n        else:\n            assert bbsoi2 is not bbsoi\n\n\nclass TestBoundingBoxesOnImage_on(TestBoundingBoxesOnImage_on_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, cbaoi, *args, **kwargs):\n        return cbaoi.on(*args, **kwargs)\n\n\nclass TestBoundingBoxesOnImage_clip_out_of_image_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, cbaoi, *args, **kwargs):\n        return cbaoi.clip_out_of_image_()\n\n    def test_clip_out_of_image(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_clip = self._func(bbsoi)\n\n        assert len(bbsoi_clip.bounding_boxes) == 2\n        assert bbsoi_clip.bounding_boxes[0].y1 == 10\n        assert bbsoi_clip.bounding_boxes[0].x1 == 20\n        assert bbsoi_clip.bounding_boxes[0].y2 == 30\n        assert bbsoi_clip.bounding_boxes[0].x2 == 40\n        assert bbsoi_clip.bounding_boxes[1].y1 == 15\n        assert bbsoi_clip.bounding_boxes[1].x1 == 25\n        assert bbsoi_clip.bounding_boxes[1].y2 == 35\n        assert np.isclose(bbsoi_clip.bounding_boxes[1].x2, 50)\n\n    def test_inplaceness(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=45)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi2 = self._func(bbsoi, (40, 50))\n\n        if self._is_inplace:\n            assert bbsoi2 is bbsoi\n        else:\n            assert bbsoi2 is not bbsoi\n\n\nclass TestBoundingBoxesOnImage_clip_out_of_image(TestBoundingBoxesOnImage_clip_out_of_image_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, cbaoi, *args, **kwargs):\n        return cbaoi.clip_out_of_image()\n\n\nclass TestBoundingBoxesOnImage(unittest.TestCase):\n    def test___init__(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=45)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n        assert bbsoi.bounding_boxes == [bb1, bb2]\n        assert bbsoi.shape == (40, 50, 3)\n\n    def test___init___array_as_shape(self):\n        image = np.zeros((40, 50, 3), dtype=np.uint8)\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=45)\n        with assertWarns(self, ia.DeprecationWarning):\n            bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=image)\n        assert bbsoi.bounding_boxes == [bb1, bb2]\n        assert bbsoi.shape == (40, 50, 3)\n\n    def test_items(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=45)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        items = bbsoi.items\n\n        assert items == [bb1, bb2]\n\n    def test_items_empty(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(40, 50, 3))\n\n        items = bbsoi.items\n\n        assert items == []\n\n    def test_height(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=45)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n        assert bbsoi.height == 40\n\n    def test_width(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=45)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n        assert bbsoi.width == 50\n\n    def test_empty_when_bbs_not_empty(self):\n        bb = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bbsoi = ia.BoundingBoxesOnImage([bb], shape=(40, 50, 3))\n        assert not bbsoi.empty\n\n    def test_empty_when_bbs_actually_empty(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(40, 50, 3))\n        assert bbsoi.empty\n\n    def test_from_xyxy_array_float(self):\n        xyxy = np.float32([\n            [0.0, 0.0, 1.0, 1.0],\n            [1.0, 2.0, 3.0, 4.0]\n        ])\n\n        bbsoi = ia.BoundingBoxesOnImage.from_xyxy_array(xyxy, shape=(40, 50, 3))\n\n        assert len(bbsoi.bounding_boxes) == 2\n        assert np.allclose(bbsoi.bounding_boxes[0].x1, 0.0)\n        assert np.allclose(bbsoi.bounding_boxes[0].y1, 0.0)\n        assert np.allclose(bbsoi.bounding_boxes[0].x2, 1.0)\n        assert np.allclose(bbsoi.bounding_boxes[0].y2, 1.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].x1, 1.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].y1, 2.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].x2, 3.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].y2, 4.0)\n        assert bbsoi.shape == (40, 50, 3)\n\n    def test_from_xyxy_array_float_3d(self):\n        xyxy = np.float32([\n            [\n                [0.0, 0.0],\n                [1.0, 1.0]\n            ],\n            [\n                [1.0, 2.0],\n                [3.0, 4.0]\n            ]\n        ])\n\n        bbsoi = ia.BoundingBoxesOnImage.from_xyxy_array(xyxy, shape=(40, 50, 3))\n\n        assert len(bbsoi.bounding_boxes) == 2\n        assert np.allclose(bbsoi.bounding_boxes[0].x1, 0.0)\n        assert np.allclose(bbsoi.bounding_boxes[0].y1, 0.0)\n        assert np.allclose(bbsoi.bounding_boxes[0].x2, 1.0)\n        assert np.allclose(bbsoi.bounding_boxes[0].y2, 1.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].x1, 1.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].y1, 2.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].x2, 3.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].y2, 4.0)\n        assert bbsoi.shape == (40, 50, 3)\n\n    def test_from_xyxy_array_int32(self):\n        xyxy = np.int32([\n            [0, 0, 1, 1],\n            [1, 2, 3, 4]\n        ])\n\n        bbsoi = ia.BoundingBoxesOnImage.from_xyxy_array(xyxy, shape=(40, 50, 3))\n\n        assert len(bbsoi.bounding_boxes) == 2\n        assert np.allclose(bbsoi.bounding_boxes[0].x1, 0.0)\n        assert np.allclose(bbsoi.bounding_boxes[0].y1, 0.0)\n        assert np.allclose(bbsoi.bounding_boxes[0].x2, 1.0)\n        assert np.allclose(bbsoi.bounding_boxes[0].y2, 1.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].x1, 1.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].y1, 2.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].x2, 3.0)\n        assert np.allclose(bbsoi.bounding_boxes[1].y2, 4.0)\n        assert bbsoi.shape == (40, 50, 3)\n\n    def test_from_xyxy_array_empty_array(self):\n        xyxy = np.zeros((0, 4), dtype=np.float32)\n\n        bbsoi = ia.BoundingBoxesOnImage.from_xyxy_array(xyxy, shape=(40, 50, 3))\n\n        assert len(bbsoi.bounding_boxes) == 0\n        assert bbsoi.shape == (40, 50, 3)\n\n    def test_from_point_soups__2d_array(self):\n        xy = np.float32([\n            [7, 3,\n             11, 5,\n             1, 7,\n             12, 19]\n        ])\n\n        bbsoi = ia.BoundingBoxesOnImage.from_point_soups(\n            xy, shape=(40, 50, 3))\n\n        assert len(bbsoi.bounding_boxes) == 1\n        assert bbsoi.bounding_boxes[0].x1 == 1\n        assert bbsoi.bounding_boxes[0].y1 == 3\n        assert bbsoi.bounding_boxes[0].x2 == 12\n        assert bbsoi.bounding_boxes[0].y2 == 19\n        assert bbsoi.shape == (40, 50, 3)\n\n    def test_from_point_soups__3d_array(self):\n        xy = np.float32([\n            [\n                [7, 3],\n                [11, 5],\n                [1, 7],\n                [12, 19]\n            ]\n        ])\n\n        bbsoi = ia.BoundingBoxesOnImage.from_point_soups(\n            xy, shape=(40, 50, 3))\n\n        assert len(bbsoi.bounding_boxes) == 1\n        assert bbsoi.bounding_boxes[0].x1 == 1\n        assert bbsoi.bounding_boxes[0].y1 == 3\n        assert bbsoi.bounding_boxes[0].x2 == 12\n        assert bbsoi.bounding_boxes[0].y2 == 19\n        assert bbsoi.shape == (40, 50, 3)\n\n    def test_from_point_soups__2d_list(self):\n        xy = [\n            [7, 3,\n             11, 5,\n             1, 7,\n             12, 19]\n        ]\n\n        bbsoi = ia.BoundingBoxesOnImage.from_point_soups(\n            xy, shape=(40, 50, 3))\n\n        assert len(bbsoi.bounding_boxes) == 1\n        assert bbsoi.bounding_boxes[0].x1 == 1\n        assert bbsoi.bounding_boxes[0].y1 == 3\n        assert bbsoi.bounding_boxes[0].x2 == 12\n        assert bbsoi.bounding_boxes[0].y2 == 19\n        assert bbsoi.shape == (40, 50, 3)\n\n    def test_from_point_soups__empty_array(self):\n        xy = np.zeros((0, 4), dtype=np.float32)\n\n        bbsoi = ia.BoundingBoxesOnImage.from_point_soups(\n            xy, shape=(40, 50, 3))\n\n        assert len(bbsoi.bounding_boxes) == 0\n        assert bbsoi.shape == (40, 50, 3)\n\n    def test_from_point_soups__empty_list(self):\n        xy = []\n\n        bbsoi = ia.BoundingBoxesOnImage.from_point_soups(\n            xy, shape=(40, 50, 3))\n\n        assert len(bbsoi.bounding_boxes) == 0\n        assert bbsoi.shape == (40, 50, 3)\n\n    def test_to_xyxy_array(self):\n        xyxy = np.float32([\n            [0.0, 0.0, 1.0, 1.0],\n            [1.0, 2.0, 3.0, 4.0]\n        ])\n        bbsoi = ia.BoundingBoxesOnImage.from_xyxy_array(xyxy, shape=(40, 50, 3))\n\n        xyxy_out = bbsoi.to_xyxy_array()\n\n        assert np.allclose(xyxy, xyxy_out)\n        assert xyxy_out.dtype.name == \"float32\"\n\n    def test_to_xyxy_array_convert_to_int32(self):\n        xyxy = np.float32([\n            [0.0, 0.0, 1.0, 1.0],\n            [1.0, 2.0, 3.0, 4.0]\n        ])\n        bbsoi = ia.BoundingBoxesOnImage.from_xyxy_array(xyxy, shape=(40, 50, 3))\n\n        xyxy_out = bbsoi.to_xyxy_array(dtype=np.int32)\n\n        assert np.allclose(xyxy.astype(np.int32), xyxy_out)\n        assert xyxy_out.dtype.name == \"int32\"\n\n    def test_to_xyxy_array_no_bbs_to_convert(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(40, 50, 3))\n\n        xyxy_out = bbsoi.to_xyxy_array(dtype=np.int32)\n\n        assert xyxy_out.shape == (0, 4)\n\n    def test_to_xy_array(self):\n        xyxy = np.float32([\n            [0.0, 0.0, 1.0, 1.0],\n            [1.0, 2.0, 3.0, 4.0]\n        ])\n        bbsoi = ia.BoundingBoxesOnImage.from_xyxy_array(xyxy, shape=(40, 50, 3))\n\n        xy_out = bbsoi.to_xy_array()\n\n        expected = np.float32([\n            [0.0, 0.0],\n            [1.0, 1.0],\n            [1.0, 2.0],\n            [3.0, 4.0]\n        ])\n        assert xy_out.shape == (4, 2)\n        assert np.allclose(xy_out, expected)\n        assert xy_out.dtype.name == \"float32\"\n\n    def test_to_xy_array__empty_instance(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(1, 2, 3))\n\n        xy_out = bbsoi.to_xy_array()\n\n        assert xy_out.shape == (0, 2)\n        assert xy_out.dtype.name == \"float32\"\n\n    def test_fill_from_xyxy_array___empty_array(self):\n        xyxy = np.zeros((0, 4), dtype=np.float32)\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(2, 2, 3))\n\n        bbsoi = bbsoi.fill_from_xyxy_array_(xyxy)\n\n        assert len(bbsoi.bounding_boxes) == 0\n\n    def test_fill_from_xyxy_array___empty_list(self):\n        xyxy = []\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(2, 2, 3))\n\n        bbsoi = bbsoi.fill_from_xyxy_array_(xyxy)\n\n        assert len(bbsoi.bounding_boxes) == 0\n\n    def test_fill_from_xyxy_array___array_with_two_coords(self):\n        xyxy = np.array(\n            [(100, 101, 102, 103),\n             (200, 201, 202, 203)], dtype=np.float32)\n        bbsoi = ia.BoundingBoxesOnImage(\n            [ia.BoundingBox(1, 2, 3, 4),\n             ia.BoundingBox(10, 20, 30, 40)],\n            shape=(2, 2, 3))\n\n        bbsoi = bbsoi.fill_from_xyxy_array_(xyxy)\n\n        assert len(bbsoi.bounding_boxes) == 2\n        assert bbsoi.bounding_boxes[0].x1 == 100\n        assert bbsoi.bounding_boxes[0].y1 == 101\n        assert bbsoi.bounding_boxes[0].x2 == 102\n        assert bbsoi.bounding_boxes[0].y2 == 103\n        assert bbsoi.bounding_boxes[1].x1 == 200\n        assert bbsoi.bounding_boxes[1].y1 == 201\n        assert bbsoi.bounding_boxes[1].x2 == 202\n        assert bbsoi.bounding_boxes[1].y2 == 203\n\n    def test_fill_from_xyxy_array___list_with_two_coords(self):\n        xyxy = [(100, 101, 102, 103),\n                (200, 201, 202, 203)]\n        bbsoi = ia.BoundingBoxesOnImage(\n            [ia.BoundingBox(1, 2, 3, 4),\n             ia.BoundingBox(10, 20, 30, 40)],\n            shape=(2, 2, 3))\n\n        bbsoi = bbsoi.fill_from_xyxy_array_(xyxy)\n\n        assert len(bbsoi.bounding_boxes) == 2\n        assert bbsoi.bounding_boxes[0].x1 == 100\n        assert bbsoi.bounding_boxes[0].y1 == 101\n        assert bbsoi.bounding_boxes[0].x2 == 102\n        assert bbsoi.bounding_boxes[0].y2 == 103\n        assert bbsoi.bounding_boxes[1].x1 == 200\n        assert bbsoi.bounding_boxes[1].y1 == 201\n        assert bbsoi.bounding_boxes[1].x2 == 202\n        assert bbsoi.bounding_boxes[1].y2 == 203\n\n    def test_fill_from_xy_array___empty_array(self):\n        xy = np.zeros((0, 2), dtype=np.float32)\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(2, 2, 3))\n\n        bbsoi = bbsoi.fill_from_xy_array_(xy)\n\n        assert len(bbsoi.bounding_boxes) == 0\n\n    def test_fill_from_xy_array___empty_list(self):\n        xy = []\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(2, 2, 3))\n\n        bbsoi = bbsoi.fill_from_xy_array_(xy)\n\n        assert len(bbsoi.bounding_boxes) == 0\n\n    def test_fill_from_xy_array___array_with_two_coords(self):\n        xy = np.array(\n            [(100, 101),\n             (102, 103),\n             (200, 201),\n             (202, 203)], dtype=np.float32)\n        bbsoi = ia.BoundingBoxesOnImage(\n            [ia.BoundingBox(1, 2, 3, 4),\n             ia.BoundingBox(10, 20, 30, 40)],\n            shape=(2, 2, 3))\n\n        bbsoi = bbsoi.fill_from_xy_array_(xy)\n\n        assert len(bbsoi.bounding_boxes) == 2\n        assert bbsoi.bounding_boxes[0].x1 == 100\n        assert bbsoi.bounding_boxes[0].y1 == 101\n        assert bbsoi.bounding_boxes[0].x2 == 102\n        assert bbsoi.bounding_boxes[0].y2 == 103\n        assert bbsoi.bounding_boxes[1].x1 == 200\n        assert bbsoi.bounding_boxes[1].y1 == 201\n        assert bbsoi.bounding_boxes[1].x2 == 202\n        assert bbsoi.bounding_boxes[1].y2 == 203\n\n    def test_fill_from_xy_array___list_with_two_coords(self):\n        xy = [(100, 101),\n              (102, 103),\n              (200, 201),\n              (202, 203)]\n        bbsoi = ia.BoundingBoxesOnImage(\n            [ia.BoundingBox(1, 2, 3, 4),\n             ia.BoundingBox(10, 20, 30, 40)],\n            shape=(2, 2, 3))\n\n        bbsoi = bbsoi.fill_from_xy_array_(xy)\n\n        assert len(bbsoi.bounding_boxes) == 2\n        assert bbsoi.bounding_boxes[0].x1 == 100\n        assert bbsoi.bounding_boxes[0].y1 == 101\n        assert bbsoi.bounding_boxes[0].x2 == 102\n        assert bbsoi.bounding_boxes[0].y2 == 103\n        assert bbsoi.bounding_boxes[1].x1 == 200\n        assert bbsoi.bounding_boxes[1].y1 == 201\n        assert bbsoi.bounding_boxes[1].x2 == 202\n        assert bbsoi.bounding_boxes[1].y2 == 203\n\n    def test_draw_on_image(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=45)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n        image = np.zeros(bbsoi.shape, dtype=np.uint8)\n\n        image_drawn = bbsoi.draw_on_image(\n            image,\n            color=[0, 255, 0], alpha=1.0, size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_drawn[10-1, 20-1, :] == [0, 0, 0])\n        assert np.all(image_drawn[10-1, 20-0, :] == [0, 0, 0])\n        assert np.all(image_drawn[10-0, 20-1, :] == [0, 0, 0])\n        assert np.all(image_drawn[10-0, 20-0, :] == [0, 255, 0])\n        assert np.all(image_drawn[10+1, 20+1, :] == [0, 0, 0])\n\n        assert np.all(image_drawn[30-1, 40-1, :] == [0, 0, 0])\n        assert np.all(image_drawn[30+1, 40-0, :] == [0, 0, 0])\n        assert np.all(image_drawn[30+0, 40+1, :] == [0, 0, 0])\n        assert np.all(image_drawn[30+0, 40+0, :] == [0, 255, 0])\n        assert np.all(image_drawn[30+1, 40+1, :] == [0, 0, 0])\n\n        assert np.all(image_drawn[15-1, 25-1, :] == [0, 0, 0])\n        assert np.all(image_drawn[15-1, 25-0, :] == [0, 0, 0])\n        assert np.all(image_drawn[15-0, 25-1, :] == [0, 0, 0])\n        assert np.all(image_drawn[15-0, 25-0, :] == [0, 255, 0])\n        assert np.all(image_drawn[15+1, 25+1, :] == [0, 0, 0])\n\n        assert np.all(image_drawn[35-1, 45-1, :] == [0, 0, 0])\n        assert np.all(image_drawn[35+1, 45+0, :] == [0, 0, 0])\n        assert np.all(image_drawn[35+0, 45+1, :] == [0, 0, 0])\n        assert np.all(image_drawn[35+0, 45+0, :] == [0, 255, 0])\n        assert np.all(image_drawn[35+1, 45+1, :] == [0, 0, 0])\n\n    def test_remove_out_of_image_(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_removed = bbsoi.remove_out_of_image_(fully=True, partly=True)\n\n        assert len(bbsoi_removed.bounding_boxes) == 1\n        assert bbsoi_removed.bounding_boxes[0] == bb1\n        assert bbsoi_removed is bbsoi\n\n    def test_remove_out_of_image(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_removed = bbsoi.remove_out_of_image(fully=True, partly=True)\n\n        assert len(bbsoi_removed.bounding_boxes) == 1\n        assert bbsoi_removed.bounding_boxes[0] == bb1\n        assert bbsoi_removed is not bbsoi\n\n    def test_remove_out_of_image_fraction_(self):\n        item1 = ia.BoundingBox(y1=1, x1=5, y2=6, x2=9)\n        item2 = ia.BoundingBox(y1=1, x1=5, y2=6, x2=15)\n        item3 = ia.BoundingBox(y1=1, x1=15, y2=6, x2=25)\n        cbaoi = ia.BoundingBoxesOnImage([item1, item2, item3],\n                                        shape=(10, 10, 3))\n\n        cbaoi_reduced = cbaoi.remove_out_of_image_fraction_(0.6)\n\n        assert len(cbaoi_reduced.items) == 2\n        assert cbaoi_reduced.items == [item1, item2]\n        assert cbaoi_reduced is cbaoi\n\n    def test_remove_out_of_image_fraction(self):\n        item1 = ia.BoundingBox(y1=1, x1=5, y2=6, x2=9)\n        item2 = ia.BoundingBox(y1=1, x1=5, y2=6, x2=15)\n        item3 = ia.BoundingBox(y1=1, x1=15, y2=6, x2=25)\n        cbaoi = ia.BoundingBoxesOnImage([item1, item2, item3],\n                                        shape=(10, 10, 3))\n\n        cbaoi_reduced = cbaoi.remove_out_of_image_fraction(0.6)\n\n        assert len(cbaoi_reduced.items) == 2\n        assert cbaoi_reduced.items == [item1, item2]\n        assert cbaoi_reduced is not cbaoi\n\n    def test_shift_(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_shifted = bbsoi.shift_(y=2)\n\n        assert len(bbsoi_shifted.bounding_boxes) == 2\n        assert bbsoi_shifted.bounding_boxes[0].y1 == 10 + 2\n        assert bbsoi_shifted.bounding_boxes[0].x1 == 20\n        assert bbsoi_shifted.bounding_boxes[0].y2 == 30 + 2\n        assert bbsoi_shifted.bounding_boxes[0].x2 == 40\n        assert bbsoi_shifted.bounding_boxes[1].y1 == 15 + 2\n        assert bbsoi_shifted.bounding_boxes[1].x1 == 25\n        assert bbsoi_shifted.bounding_boxes[1].y2 == 35 + 2\n        assert bbsoi_shifted.bounding_boxes[1].x2 == 51\n        assert bbsoi_shifted is bbsoi\n\n    def test_shift(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_shifted = bbsoi.shift(y=2)\n\n        assert len(bbsoi_shifted.bounding_boxes) == 2\n        assert bbsoi_shifted.bounding_boxes[0].y1 == 10 + 2\n        assert bbsoi_shifted.bounding_boxes[0].x1 == 20\n        assert bbsoi_shifted.bounding_boxes[0].y2 == 30 + 2\n        assert bbsoi_shifted.bounding_boxes[0].x2 == 40\n        assert bbsoi_shifted.bounding_boxes[1].y1 == 15 + 2\n        assert bbsoi_shifted.bounding_boxes[1].x1 == 25\n        assert bbsoi_shifted.bounding_boxes[1].y2 == 35 + 2\n        assert bbsoi_shifted.bounding_boxes[1].x2 == 51\n        assert bbsoi_shifted is not bbsoi\n\n    def test_shift__deprecated_args(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            bbsoi_shifted = bbsoi.shift(right=1)\n\n            assert len(bbsoi_shifted.bounding_boxes) == 2\n            assert bbsoi_shifted.bounding_boxes[0].y1 == 10\n            assert bbsoi_shifted.bounding_boxes[0].x1 == 20 - 1\n            assert bbsoi_shifted.bounding_boxes[0].y2 == 30\n            assert bbsoi_shifted.bounding_boxes[0].x2 == 40 - 1\n            assert bbsoi_shifted.bounding_boxes[1].y1 == 15\n            assert bbsoi_shifted.bounding_boxes[1].x1 == 25 - 1\n            assert bbsoi_shifted.bounding_boxes[1].y2 == 35\n            assert bbsoi_shifted.bounding_boxes[1].x2 == 51 - 1\n            assert bbsoi_shifted is not bbsoi\n\n            assert (\n                \"These are deprecated. Use `x` and `y` instead.\"\n                in str(caught_warnings[-1].message)\n            )\n\n    def test_to_keypoints_on_image(self):\n        bbsoi = ia.BoundingBoxesOnImage(\n            [ia.BoundingBox(0, 1, 2, 3),\n             ia.BoundingBox(10, 20, 30, 40)],\n            shape=(1, 2, 3))\n\n        kpsoi = bbsoi.to_keypoints_on_image()\n\n        assert len(kpsoi.keypoints) == 2*4\n\n        assert kpsoi.keypoints[0].x == 0\n        assert kpsoi.keypoints[0].y == 1\n        assert kpsoi.keypoints[1].x == 2\n        assert kpsoi.keypoints[1].y == 1\n        assert kpsoi.keypoints[2].x == 2\n        assert kpsoi.keypoints[2].y == 3\n        assert kpsoi.keypoints[3].x == 0\n        assert kpsoi.keypoints[3].y == 3\n\n        assert kpsoi.keypoints[4].x == 10\n        assert kpsoi.keypoints[4].y == 20\n        assert kpsoi.keypoints[5].x == 30\n        assert kpsoi.keypoints[5].y == 20\n        assert kpsoi.keypoints[6].x == 30\n        assert kpsoi.keypoints[6].y == 40\n        assert kpsoi.keypoints[7].x == 10\n        assert kpsoi.keypoints[7].y == 40\n\n    def test_to_keypoints_on_image__empty_instance(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(1, 2, 3))\n\n        kpsoi = bbsoi.to_keypoints_on_image()\n\n        assert len(kpsoi.keypoints) == 0\n\n    def test_invert_to_keypoints_on_image_(self):\n        bbsoi = ia.BoundingBoxesOnImage(\n            [ia.BoundingBox(0, 1, 2, 3),\n             ia.BoundingBox(10, 20, 30, 40)],\n            shape=(1, 2, 3))\n        kpsoi = ia.KeypointsOnImage(\n            [ia.Keypoint(100, 101), ia.Keypoint(102, 103),\n             ia.Keypoint(104, 105), ia.Keypoint(106, 107),\n             ia.Keypoint(110, 120), ia.Keypoint(130, 140),\n             ia.Keypoint(150, 160), ia.Keypoint(170, 180)],\n            shape=(10, 20, 30))\n\n        bbsoi_inv = bbsoi.invert_to_keypoints_on_image_(kpsoi)\n\n        assert len(bbsoi_inv.bounding_boxes) == 2\n        assert bbsoi_inv.shape == (10, 20, 30)\n        assert bbsoi_inv.bounding_boxes[0].x1 == 100\n        assert bbsoi_inv.bounding_boxes[0].y1 == 101\n        assert bbsoi_inv.bounding_boxes[0].x2 == 106\n        assert bbsoi_inv.bounding_boxes[0].y2 == 107\n        assert bbsoi_inv.bounding_boxes[1].x1 == 110\n        assert bbsoi_inv.bounding_boxes[1].y1 == 120\n        assert bbsoi_inv.bounding_boxes[1].x2 == 170\n        assert bbsoi_inv.bounding_boxes[1].y2 == 180\n\n    def test_invert_to_keypoints_on_image___empty_instance(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(1, 2, 3))\n        kpsoi = ia.KeypointsOnImage([], shape=(10, 20, 30))\n\n        bbsoi_inv = bbsoi.invert_to_keypoints_on_image_(kpsoi)\n\n        assert len(bbsoi_inv.bounding_boxes) == 0\n        assert bbsoi_inv.shape == (10, 20, 30)\n\n    def test_to_polygons_on_image(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        psoi = bbsoi.to_polygons_on_image()\n\n        assert psoi.shape == (40, 50, 3)\n        assert len(psoi.items) == 2\n        assert psoi.items[0].coords_almost_equals([\n            (20, 10),\n            (40, 10),\n            (40, 30),\n            (20, 30)\n        ])\n        assert psoi.items[1].coords_almost_equals([\n            (25, 15),\n            (51, 15),\n            (51, 35),\n            (25, 35)\n        ])\n\n    def test_to_polygons_on_image__empty_instance(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(40, 50, 3))\n\n        psoi = bbsoi.to_polygons_on_image()\n\n        assert psoi.shape == (40, 50, 3)\n        assert len(psoi.items) == 0\n\n    def test_copy(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_copy = bbsoi.copy()\n\n        assert len(bbsoi.bounding_boxes) == 2\n        assert bbsoi_copy.bounding_boxes[0].y1 == 10\n        assert bbsoi_copy.bounding_boxes[0].x1 == 20\n        assert bbsoi_copy.bounding_boxes[0].y2 == 30\n        assert bbsoi_copy.bounding_boxes[0].x2 == 40\n        assert bbsoi_copy.bounding_boxes[1].y1 == 15\n        assert bbsoi_copy.bounding_boxes[1].x1 == 25\n        assert bbsoi_copy.bounding_boxes[1].y2 == 35\n        assert bbsoi_copy.bounding_boxes[1].x2 == 51\n\n        bbsoi_copy.bounding_boxes[0].y1 = 0\n        assert bbsoi.bounding_boxes[0].y1 == 0\n        assert bbsoi_copy.bounding_boxes[0].y1 == 0\n\n    def test_copy_bounding_boxes_set(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bb3 = ia.BoundingBox(y1=15+1, x1=25+1, y2=35+1, x2=51+1)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_copy = bbsoi.copy(bounding_boxes=[bb3])\n\n        assert bbsoi_copy is not bbsoi\n        assert bbsoi_copy.shape == (40, 50, 3)\n        assert bbsoi_copy.bounding_boxes == [bb3]\n\n    def test_copy_shape_set(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_copy = bbsoi.copy(shape=(40+1, 50+1, 3))\n\n        assert bbsoi_copy is not bbsoi\n        assert bbsoi_copy.shape == (40+1, 50+1, 3)\n        assert bbsoi_copy.bounding_boxes == [bb1, bb2]\n\n    def test_deepcopy(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_copy = bbsoi.deepcopy()\n\n        assert len(bbsoi.bounding_boxes) == 2\n        assert bbsoi_copy.bounding_boxes[0].y1 == 10\n        assert bbsoi_copy.bounding_boxes[0].x1 == 20\n        assert bbsoi_copy.bounding_boxes[0].y2 == 30\n        assert bbsoi_copy.bounding_boxes[0].x2 == 40\n        assert bbsoi_copy.bounding_boxes[1].y1 == 15\n        assert bbsoi_copy.bounding_boxes[1].x1 == 25\n        assert bbsoi_copy.bounding_boxes[1].y2 == 35\n        assert bbsoi_copy.bounding_boxes[1].x2 == 51\n\n        bbsoi_copy.bounding_boxes[0].y1 = 0\n        assert bbsoi.bounding_boxes[0].y1 == 10\n        assert bbsoi_copy.bounding_boxes[0].y1 == 0\n\n    def test_deepcopy_bounding_boxes_set(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bb3 = ia.BoundingBox(y1=15+1, x1=25+1, y2=35+1, x2=51+1)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_copy = bbsoi.deepcopy(bounding_boxes=[bb3])\n\n        assert bbsoi_copy is not bbsoi\n        assert bbsoi_copy.shape == (40, 50, 3)\n        assert bbsoi_copy.bounding_boxes == [bb3]\n\n    def test_deepcopy_shape_set(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bbsoi_copy = bbsoi.deepcopy(shape=(40+1, 50+1, 3))\n\n        assert bbsoi_copy is not bbsoi\n        assert bbsoi_copy.shape == (40+1, 50+1, 3)\n        assert len(bbsoi_copy.bounding_boxes) == 2\n        assert bbsoi_copy.bounding_boxes[0].coords_almost_equals(bb1)\n        assert bbsoi_copy.bounding_boxes[1].coords_almost_equals(bb2)\n\n    def test___getitem__(self):\n        cbas = [\n            ia.BoundingBox(x1=1, y1=2, x2=3, y2=4),\n            ia.BoundingBox(x1=2, y1=3, x2=4, y2=5)\n        ]\n        cbasoi = ia.BoundingBoxesOnImage(cbas, shape=(3, 4, 3))\n\n        assert cbasoi[0] is cbas[0]\n        assert cbasoi[1] is cbas[1]\n        assert cbasoi[0:2] == cbas\n\n    def test___iter__(self):\n        cbas = [ia.BoundingBox(x1=0, y1=0, x2=2, y2=2),\n                ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)]\n        cbasoi = ia.BoundingBoxesOnImage(cbas, shape=(40, 50, 3))\n\n        for i, cba in enumerate(cbasoi):\n            assert cba is cbas[i]\n\n    def test___iter___empty(self):\n        cbasoi = ia.BoundingBoxesOnImage([], shape=(40, 50, 3))\n        i = 0\n        for _cba in cbasoi:\n            i += 1\n        assert i == 0\n\n    def test___len__(self):\n        cbas = [ia.BoundingBox(x1=0, y1=0, x2=2, y2=2),\n                ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)]\n        cbasoi = ia.BoundingBoxesOnImage(cbas, shape=(40, 50, 3))\n        assert len(cbasoi) == 2\n\n    def test_string_conversion(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40)\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51)\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bb1_expected = \"BoundingBox(x1=20.0000, y1=10.0000, \" \\\n                       \"x2=40.0000, y2=30.0000, label=None)\"\n        bb2_expected = \"BoundingBox(x1=25.0000, y1=15.0000, \" \\\n                       \"x2=51.0000, y2=35.0000, label=None)\"\n        expected = \"BoundingBoxesOnImage([%s, %s], shape=(40, 50, 3))\" % (\n            bb1_expected, bb2_expected)\n        assert (\n            bbsoi.__repr__()\n            == bbsoi.__str__()\n            == expected\n        )\n\n    def test_string_conversion_labels_are_not_none(self):\n        bb1 = ia.BoundingBox(y1=10, x1=20, y2=30, x2=40, label=\"foo\")\n        bb2 = ia.BoundingBox(y1=15, x1=25, y2=35, x2=51, label=\"bar\")\n        bbsoi = ia.BoundingBoxesOnImage([bb1, bb2], shape=(40, 50, 3))\n\n        bb1_expected = \"BoundingBox(x1=20.0000, y1=10.0000, \" \\\n                       \"x2=40.0000, y2=30.0000, label=foo)\"\n        bb2_expected = \"BoundingBox(x1=25.0000, y1=15.0000, \" \\\n                       \"x2=51.0000, y2=35.0000, label=bar)\"\n        expected = \"BoundingBoxesOnImage([%s, %s], shape=(40, 50, 3))\" % (\n            bb1_expected, bb2_expected)\n        assert (\n            bbsoi.__repr__()\n            == bbsoi.__str__()\n            == expected\n        )\n\n\nclass Test_LabelOnImageDrawer(unittest.TestCase):\n    def test_draw_on_image_(self):\n        height = 30\n        image = np.full((100, 50, 3), 100, dtype=np.uint8)\n        bb = ia.BoundingBox(x1=5, x2=20, y1=50, y2=60)\n        drawer = _LabelOnImageDrawer(color_text=(255, 255, 255),\n                                     color_bg=(0, 0, 0),\n                                     height=height)\n\n        image_drawn = drawer.draw_on_image_(np.copy(image), bb)\n\n        frac_colors_as_expected = np.average(\n            np.logical_or(image_drawn[50-1-height:50-1, 5-1:20+1, :] == 0,\n                          image_drawn[50-1-height:50-1, 5-1:20+1, :] == 255)\n        )\n        assert np.all(image_drawn[:50-1-height, :, :] == 100)\n        assert np.all(image_drawn[50-1:, :, :] == 100)\n        assert np.all(image_drawn[:, :5-1, :] == 100)\n        assert np.all(image_drawn[:, 20+1:, :] == 100)\n        assert frac_colors_as_expected > 0.75\n\n    def test_draw_on_image(self):\n        image = np.full((20, 30, 3), 100, dtype=np.uint8)\n        bb = ia.BoundingBox(x1=1, x2=6, y1=2, y2=10)\n        drawer = _LabelOnImageDrawer(color_text=(255, 255, 255),\n                                     color_bg=(0, 0, 0))\n\n        image_drawn_inplace = drawer.draw_on_image_(np.copy(image), bb)\n        image_drawn = drawer.draw_on_image_(image, bb)\n\n        assert np.array_equal(image_drawn, image_drawn_inplace)\n\n    def test__do_raise_if_out_of_image__bb_is_fully_inside(self):\n        drawer = _LabelOnImageDrawer(raise_if_out_of_image=True)\n        image = np.zeros((20, 30, 3), dtype=np.uint8)\n        bb = ia.BoundingBox(x1=1, x2=6, y1=2, y2=10)\n\n        # assert no exception\n        drawer._do_raise_if_out_of_image(image, bb)\n\n    def test__do_raise_if_out_of_image__bb_is_partially_outside(self):\n        drawer = _LabelOnImageDrawer(raise_if_out_of_image=True)\n        image = np.zeros((20, 30, 3), dtype=np.uint8)\n        bb = ia.BoundingBox(x1=30-5, x2=30+1, y1=2, y2=10)\n\n        # assert no exception\n        drawer._do_raise_if_out_of_image(image, bb)\n\n    def test__do_raise_if_out_of_image__bb_is_fully_outside(self):\n        drawer = _LabelOnImageDrawer(raise_if_out_of_image=True)\n        image = np.zeros((20, 30, 3), dtype=np.uint8)\n        bb = ia.BoundingBox(x1=30+1, x2=30+6, y1=2, y2=10)\n\n        with self.assertRaises(Exception):\n            drawer._do_raise_if_out_of_image(image, bb)\n\n    def test__preprocess_colors__only_main_color_set(self):\n        drawer = _LabelOnImageDrawer(color=(0, 255, 0))\n        color_text, color_bg = drawer._preprocess_colors()\n        assert np.array_equal(color_text, [0, 0, 0])\n        assert np.array_equal(color_bg, [0, 255, 0])\n\n    def test__preprocess_colors__subcolors_set(self):\n        drawer = _LabelOnImageDrawer(color_text=(128, 129, 130),\n                                     color_bg=(131, 132, 133))\n        color_text, color_bg = drawer._preprocess_colors()\n        assert np.array_equal(color_text, [128, 129, 130])\n        assert np.array_equal(color_bg, [131, 132, 133])\n\n    def test__preprocess_colors__text_not_set_must_be_black(self):\n        drawer = _LabelOnImageDrawer(color=(255, 255, 255),\n                                     color_bg=(255, 255, 255))\n        color_text, color_bg = drawer._preprocess_colors()\n        assert np.array_equal(color_text, [0, 0, 0])\n        assert np.array_equal(color_bg, [255, 255, 255])\n\n    def test__compute_bg_corner_coords__standard_bb(self):\n        height = 30\n        for size in [1, 2]:\n            with self.subTest(size=size):\n                drawer = _LabelOnImageDrawer(size=size, height=height)\n                bb = ia.BoundingBox(x1=10, x2=30, y1=60, y2=90)\n                image = np.zeros((100, 200, 3), dtype=np.uint8)\n                x1, y1, x2, y2 = drawer._compute_bg_corner_coords(image, bb)\n                assert np.isclose(x1, max(bb.x1 - size + 1, 0))\n                assert np.isclose(y1, max(bb.y1 - 1 - height, 0))\n                assert np.isclose(x2, min(bb.x2 + size, image.shape[1]-1))\n                assert np.isclose(y2, min(bb.y1 - 1, image.shape[0]-1))\n\n    def test__compute_bg_corner_coords__zero_sized_bb(self):\n        height = 30\n        size = 1\n        drawer = _LabelOnImageDrawer(size=1, height=height)\n        bb = ia.BoundingBox(x1=10, x2=10, y1=60, y2=90)\n        image = np.zeros((100, 200, 3), dtype=np.uint8)\n        x1, y1, x2, y2 = drawer._compute_bg_corner_coords(image, bb)\n        assert np.isclose(x1, bb.x1 - size + 1)\n        assert np.isclose(y1, bb.y1 - 1 - height)\n        assert np.isclose(x2, bb.x2 + size)\n        assert np.isclose(y2, bb.y1 - 1)\n\n    def test__draw_label_arr__label_is_none(self):\n        drawer = _LabelOnImageDrawer()\n        height = 50\n        width = 100\n        nb_channels = 3\n        color_text = np.uint8([0, 255, 0])\n        color_bg = np.uint8([255, 0, 0])\n        size_text = 20\n\n        label_arr = drawer._draw_label_arr(None, height, width, nb_channels,\n                                           np.uint8,\n                                           color_text, color_bg, size_text)\n\n        frac_textcolor = np.average(\n            np.min(label_arr == color_text.reshape((1, 1, -1)), axis=-1)\n        )\n        frac_bgcolor = np.average(\n            np.min(label_arr == color_bg.reshape((1, 1, -1)), axis=-1)\n        )\n        assert label_arr.dtype.name == \"uint8\"\n        assert label_arr.shape == (height, width, nb_channels)\n        assert frac_textcolor > 0.02\n        assert frac_bgcolor > 0.8\n        # not all pixels of the text might be drawn with exactly the text\n        # color\n        assert frac_textcolor + frac_bgcolor > 0.75\n\n    def test__draw_label_arr__label_is_str(self):\n        drawer = _LabelOnImageDrawer()\n        height = 50\n        width = 100\n        nb_channels = 3\n        color_text = np.uint8([0, 255, 0])\n        color_bg = np.uint8([255, 0, 0])\n        size_text = 20\n\n        label_arr = drawer._draw_label_arr(\"Fooo\", height, width, nb_channels,\n                                           np.uint8,\n                                           color_text, color_bg, size_text)\n\n        frac_textcolor = np.average(\n            np.min(label_arr == color_text.reshape((1, 1, -1)), axis=-1)\n        )\n        frac_bgcolor = np.average(\n            np.min(label_arr == color_bg.reshape((1, 1, -1)), axis=-1)\n        )\n        assert label_arr.dtype.name == \"uint8\"\n        assert label_arr.shape == (height, width, nb_channels)\n        assert frac_textcolor > 0.02\n        assert frac_bgcolor > 0.8\n        # not all pixels of the text might be drawn with exactly the text\n        # color\n        assert frac_textcolor + frac_bgcolor > 0.75\n\n    def test__blend_label_arr__alpha_is_1(self):\n        drawer = _LabelOnImageDrawer(alpha=1)\n        image = np.full((50, 60, 3), 100, dtype=np.uint8)\n        label_arr = np.full((10, 20, 3), 200, dtype=np.uint8)\n        x1 = 15\n        x2 = 15 + 20\n        y1 = 10\n        y2 = 10 + 10\n\n        image_blend = drawer._blend_label_arr_with_image_(image, label_arr,\n                                                          x1, y1, x2, y2)\n\n        assert np.all(image_blend[:, :15, :] == 100)\n        assert np.all(image_blend[:, 15+20:, :] == 100)\n        assert np.all(image_blend[:10, :, :] == 100)\n        assert np.all(image_blend[10+10:, :, :] == 100)\n        assert np.all(image_blend[10:10+10, 15:15+20, :] == 200)\n\n    def test__blend_label_arr__alpha_is_075(self):\n        drawer = _LabelOnImageDrawer(alpha=0.75)\n        image = np.full((50, 60, 3), 100, dtype=np.uint8)\n        label_arr = np.full((10, 20, 3), 200, dtype=np.uint8)\n        x1 = 15\n        x2 = 15 + 20\n        y1 = 10\n        y2 = 10 + 10\n\n        image_blend = drawer._blend_label_arr_with_image_(image, label_arr,\n                                                          x1, y1, x2, y2)\n\n        assert np.all(image_blend[:, :15, :] == 100)\n        assert np.all(image_blend[:, 15+20:, :] == 100)\n        assert np.all(image_blend[:10, :, :] == 100)\n        assert np.all(image_blend[10+10:, :, :] == 100)\n        assert np.all(image_blend[10:10+10, 15:15+20, :] == 100+75)\n"
  },
  {
    "path": "test/augmentables/test_heatmaps.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport six.moves as sm\n\nimport imgaug as ia\n\n\n# TODO add tests for:\n#      hooks is_activated\n#      hooks is_propagating\n#      hooks preprocess\n#      hooks postprocess\n#      HeatmapsOnImage.__init__()\n#      HeatmapsOnImage.get_arr()\n#      HeatmapsOnImage.to_uint8()\n#      HeatmapsOnImage.from_0to1()\n#      HeatmapsOnImage.copy()\n#      HeatmapsOnImage.deepcopy()\n\n\nclass TestHeatmapsOnImage_draw(unittest.TestCase):\n    def test_basic_functionality(self):\n        heatmaps_arr = np.float32([\n            [0.5, 0.0, 0.0, 0.5],\n            [0.0, 1.0, 1.0, 0.0],\n            [0.0, 1.0, 1.0, 0.0],\n            [0.5, 0.0, 0.0, 0.5],\n        ])\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(4, 4, 3))\n\n        heatmaps_drawn = heatmaps.draw()[0]\n        assert heatmaps_drawn.shape == (4, 4, 3)\n        v1 = heatmaps_drawn[0, 1]\n        v2 = heatmaps_drawn[0, 0]\n        v3 = heatmaps_drawn[1, 1]\n\n        v1_coords = [(0, 1), (0, 2), (1, 0), (1, 3), (2, 0), (2, 3), (3, 1),\n                     (3, 2)]\n        v2_coords = [(0, 0), (0, 3), (3, 0), (3, 3)]\n        v3_coords = [(1, 1), (1, 2), (2, 1), (2, 2)]\n\n        for y, x in v1_coords:\n            assert np.allclose(heatmaps_drawn[y, x], v1)\n\n        for y, x in v2_coords:\n            assert np.allclose(heatmaps_drawn[y, x], v2)\n\n        for y, x in v3_coords:\n            assert np.allclose(heatmaps_drawn[y, x], v3)\n\n    def test_use_size_arg_with_different_shape_than_heatmap_arr_shape(self):\n        # size differs from heatmap array size\n        heatmaps_arr = np.float32([\n            [0.0, 1.0],\n            [0.0, 1.0]\n        ])\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(2, 2, 3))\n\n        heatmaps_drawn = heatmaps.draw(size=(4, 4))[0]\n        assert heatmaps_drawn.shape == (4, 4, 3)\n        v1 = heatmaps_drawn[0, 0]\n        v2 = heatmaps_drawn[0, -1]\n\n        for y in sm.xrange(4):\n            for x in sm.xrange(2):\n                assert np.allclose(heatmaps_drawn[y, x], v1)\n\n        for y in sm.xrange(4):\n            for x in sm.xrange(2, 4):\n                assert np.allclose(heatmaps_drawn[y, x], v2)\n\n\n# TODO test other cmaps\nclass TestHeatmapsOnImage_draw_on_image(unittest.TestCase):\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([\n            [0.0, 1.0],\n            [0.0, 1.0]\n        ])\n        return ia.HeatmapsOnImage(heatmaps_arr, shape=(2, 2, 3))\n\n    def test_cmap_is_none(self):\n        heatmaps = self.heatmaps\n\n        image = np.uint8([\n            [0, 0, 0, 255],\n            [0, 0, 0, 255],\n            [0, 0, 0, 255],\n            [0, 0, 0, 255]\n        ])\n        image = np.tile(image[..., np.newaxis], (1, 1, 3))\n\n        heatmaps_drawn = heatmaps.draw_on_image(image, alpha=0.5, cmap=None)[0]\n        assert heatmaps_drawn.shape == (4, 4, 3)\n        assert np.all(heatmaps_drawn[0:4, 0:2, :] == 0)\n        assert (\n            np.all(heatmaps_drawn[0:4, 2:3, :] == 128)\n            or np.all(heatmaps_drawn[0:4, 2:3, :] == 127))\n        assert (\n            np.all(heatmaps_drawn[0:4, 3:4, :] == 255)\n            or np.all(heatmaps_drawn[0:4, 3:4, :] == 254))\n\n    def test_cmap_is_none_and_resize_is_image(self):\n        heatmaps = self.heatmaps\n\n        image = np.uint8([\n            [0, 0, 0, 0],\n            [0, 0, 0, 0],\n            [0, 0, 0, 0],\n            [0, 0, 0, 0]\n        ])\n        image = np.tile(image[..., np.newaxis], (1, 1, 3))\n\n        heatmaps_drawn = heatmaps.draw_on_image(\n            image, alpha=0.5, resize=\"image\", cmap=None)[0]\n        assert heatmaps_drawn.shape == (2, 2, 3)\n        assert np.all(heatmaps_drawn[0:2, 0, :] == 0)\n        assert (\n            np.all(heatmaps_drawn[0:2, 1, :] == 128)\n            or np.all(heatmaps_drawn[0:2, 1, :] == 127))\n\n\nclass TestHeatmapsOnImage_invert(unittest.TestCase):\n    @property\n    def heatmaps_arr(self):\n        return np.float32([\n            [0.0, 5.0, 10.0],\n            [-1.0, -2.0, 7.5]\n        ])\n\n    @property\n    def expected_arr(self):\n        return np.float32([\n            [8.0, 3.0, -2.0],\n            [9.0, 10.0, 0.5]\n        ])\n\n    def test_with_2d_input_array(self):\n        # (H, W)\n        heatmaps_arr = self.heatmaps_arr\n        expected = self.expected_arr\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr,\n                                      shape=(2, 3),\n                                      min_value=-2.0,\n                                      max_value=10.0)\n        assert np.allclose(heatmaps.get_arr(), heatmaps_arr)\n        assert np.allclose(heatmaps.invert().get_arr(), expected)\n\n    def test_with_3d_input_array(self):\n        # (H, W, 1)\n        heatmaps_arr = self.heatmaps_arr\n        expected = self.expected_arr\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr[..., np.newaxis],\n                                      shape=(2, 3),\n                                      min_value=-2.0,\n                                      max_value=10.0)\n        assert np.allclose(heatmaps.get_arr(),\n                           heatmaps_arr[..., np.newaxis])\n        assert np.allclose(heatmaps.invert().get_arr(),\n                           expected[..., np.newaxis])\n\n\nclass TestHeatmapsOnImage_pad(unittest.TestCase):\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([\n            [0.0, 1.0],\n            [0.0, 1.0]\n        ])\n        return ia.HeatmapsOnImage(heatmaps_arr, shape=(2, 2, 3))\n\n    def test_defaults(self):\n        heatmaps = self.heatmaps\n        heatmaps_padded = heatmaps.pad(top=1, right=2, bottom=3, left=4)\n        assert heatmaps_padded.arr_0to1.shape == (2+(1+3), 2+(4+2), 1)\n        assert np.allclose(\n            heatmaps_padded.arr_0to1[:, :, 0],\n            np.float32([\n                [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],\n                [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0],\n                [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0],\n                [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],\n                [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],\n                [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]\n            ])\n        )\n\n    def test_mode_constant_with_cval_050(self):\n        heatmaps = self.heatmaps\n        heatmaps_padded = heatmaps.pad(top=1, right=2, bottom=3, left=4,\n                                       cval=0.5)\n        assert heatmaps_padded.arr_0to1.shape == (2+(1+3), 2+(4+2), 1)\n        assert np.allclose(\n            heatmaps_padded.arr_0to1[:, :, 0],\n            np.float32([\n                [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],\n                [0.5, 0.5, 0.5, 0.5, 0.0, 1.0, 0.5, 0.5],\n                [0.5, 0.5, 0.5, 0.5, 0.0, 1.0, 0.5, 0.5],\n                [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],\n                [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],\n                [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]\n            ])\n        )\n\n    def test_mode_edge(self):\n        heatmaps = self.heatmaps\n        heatmaps_padded = heatmaps.pad(top=1, right=2, bottom=3, left=4,\n                                       mode=\"edge\")\n        assert heatmaps_padded.arr_0to1.shape == (2+(1+3), 2+(4+2), 1)\n        assert np.allclose(\n            heatmaps_padded.arr_0to1[:, :, 0],\n            np.float32([\n                [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0],\n                [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0],\n                [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0],\n                [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0],\n                [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0],\n                [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0]\n            ])\n        )\n\n\nclass TestHeatmapsOnImage_pad_to_aspect_ratio(unittest.TestCase):\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([\n            [0.0, 0.0, 1.0],\n            [0.0, 0.0, 1.0]\n        ])\n        return ia.HeatmapsOnImage(heatmaps_arr, shape=(2, 2, 3))\n\n    def test_square_ratio_with_default_mode_and_cval(self):\n        heatmaps = self.heatmaps\n        heatmaps_padded = heatmaps.pad_to_aspect_ratio(1.0)\n        assert heatmaps_padded.arr_0to1.shape == (3, 3, 1)\n        assert np.allclose(\n            heatmaps_padded.arr_0to1[:, :, 0],\n            np.float32([\n                [0.0, 0.0, 1.0],\n                [0.0, 0.0, 1.0],\n                [0.0, 0.0, 0.0]\n            ])\n        )\n\n    def test_square_ratio_with_cval_050(self):\n        heatmaps = self.heatmaps\n        heatmaps_padded = heatmaps.pad_to_aspect_ratio(1.0, cval=0.5)\n        assert heatmaps_padded.arr_0to1.shape == (3, 3, 1)\n        assert np.allclose(\n            heatmaps_padded.arr_0to1[:, :, 0],\n            np.float32([\n                [0.0, 0.0, 1.0],\n                [0.0, 0.0, 1.0],\n                [0.5, 0.5, 0.5]\n            ])\n        )\n\n    def test_square_ratio_with_edge_mode(self):\n        heatmaps = self.heatmaps\n        heatmaps_padded = heatmaps.pad_to_aspect_ratio(1.0, mode=\"edge\")\n        assert heatmaps_padded.arr_0to1.shape == (3, 3, 1)\n        assert np.allclose(\n            heatmaps_padded.arr_0to1[:, :, 0],\n            np.float32([\n                [0.0, 0.0, 1.0],\n                [0.0, 0.0, 1.0],\n                [0.0, 0.0, 1.0]\n            ])\n        )\n\n    def test_wider_than_high_ratio_with_cval_010(self):\n        heatmaps = self.heatmaps\n        heatmaps_padded = heatmaps.pad_to_aspect_ratio(2.0, cval=0.1)\n        assert heatmaps_padded.arr_0to1.shape == (2, 4, 1)\n        assert np.allclose(\n            heatmaps_padded.arr_0to1[:, :, 0],\n            np.float32([\n                [0.0, 0.0, 1.0, 0.1],\n                [0.0, 0.0, 1.0, 0.1]\n            ])\n        )\n\n    def test_higher_than_wide_ratio_with_cval_010(self):\n        heatmaps = self.heatmaps\n        heatmaps_padded = heatmaps.pad_to_aspect_ratio(0.25, cval=0.1)\n        assert heatmaps_padded.arr_0to1.shape == (12, 3, 1)\n        assert np.allclose(\n            heatmaps_padded.arr_0to1[:, :, 0],\n            np.float32([\n                [0.1, 0.1, 0.1],\n                [0.1, 0.1, 0.1],\n                [0.1, 0.1, 0.1],\n                [0.1, 0.1, 0.1],\n                [0.1, 0.1, 0.1],\n                [0.0, 0.0, 1.0],\n                [0.0, 0.0, 1.0],\n                [0.1, 0.1, 0.1],\n                [0.1, 0.1, 0.1],\n                [0.1, 0.1, 0.1],\n                [0.1, 0.1, 0.1],\n                [0.1, 0.1, 0.1]\n            ])\n        )\n\n\nclass TestHeatmapsOnImage_avg_pool(unittest.TestCase):\n    def test_with_kernel_size_2(self):\n        heatmaps_arr = np.float32([\n            [0.0, 0.0, 0.5, 1.0],\n            [0.0, 0.0, 0.5, 1.0],\n            [0.0, 0.0, 0.5, 1.0],\n            [0.0, 0.0, 0.5, 1.0]\n        ])\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(4, 4, 3))\n\n        heatmaps_pooled = heatmaps.avg_pool(2)\n        assert heatmaps_pooled.arr_0to1.shape == (2, 2, 1)\n        assert np.allclose(\n            heatmaps_pooled.arr_0to1[:, :, 0],\n            np.float32([[0.0, 0.75],\n                        [0.0, 0.75]])\n        )\n\nclass TestHeatmapsOnImage_max_pool(unittest.TestCase):\n    def test_with_kernel_size_2(self):\n        heatmaps_arr = np.float32([\n            [0.0, 0.0, 0.5, 1.0],\n            [0.0, 0.0, 0.5, 1.0],\n            [0.0, 0.0, 0.5, 1.0],\n            [0.0, 0.0, 0.5, 1.0]\n        ])\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(4, 4, 3))\n\n        heatmaps_pooled = heatmaps.max_pool(2)\n        assert heatmaps_pooled.arr_0to1.shape == (2, 2, 1)\n        assert np.allclose(\n            heatmaps_pooled.arr_0to1[:, :, 0],\n            np.float32([[0.0, 1.0],\n                        [0.0, 1.0]])\n        )\n\n\nclass TestHeatmapsOnImage_resize(unittest.TestCase):\n    def test_resize_to_exact_shape(self):\n        heatmaps_arr = np.float32([\n            [0.0, 1.0]\n        ])\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(4, 4, 3))\n\n        heatmaps_scaled = heatmaps.resize((4, 4), interpolation=\"nearest\")\n        assert heatmaps_scaled.arr_0to1.shape == (4, 4, 1)\n        assert heatmaps_scaled.arr_0to1.dtype.name == \"float32\"\n        assert np.allclose(\n            heatmaps_scaled.arr_0to1[:, :, 0],\n            np.float32([\n                [0.0, 0.0, 1.0, 1.0],\n                [0.0, 0.0, 1.0, 1.0],\n                [0.0, 0.0, 1.0, 1.0],\n                [0.0, 0.0, 1.0, 1.0]\n            ])\n        )\n\n    def test_resize_to_twice_the_size(self):\n        heatmaps_arr = np.float32([\n            [0.0, 1.0]\n        ])\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(4, 4, 3))\n\n        heatmaps_scaled = heatmaps.resize(2.0, interpolation=\"nearest\")\n        assert heatmaps_scaled.arr_0to1.shape == (2, 4, 1)\n        assert heatmaps_scaled.arr_0to1.dtype.name == \"float32\"\n        assert np.allclose(\n            heatmaps_scaled.arr_0to1[:, :, 0],\n            np.float32([\n                [0.0, 0.0, 1.0, 1.0],\n                [0.0, 0.0, 1.0, 1.0]\n            ])\n        )\n\n\nclass TestHeatmapsOnImage_from_uint8(unittest.TestCase):\n    def test_3d_uint8_array(self):\n        hm = ia.HeatmapsOnImage.from_uint8(\n            np.uint8([\n                [0, 128, 255],\n                [255, 128, 0]\n            ])[..., np.newaxis],\n            (20, 30, 3)\n        )\n        assert hm.shape == (20, 30, 3)\n        assert hm.arr_0to1.shape == (2, 3, 1)\n        assert np.allclose(hm.arr_0to1[..., 0], np.float32([\n            [0, 128/255, 1.0],\n            [1.0, 128/255, 0]\n        ]))\n\n    def test_2d_uint8_array(self):\n        hm = ia.HeatmapsOnImage.from_uint8(\n            np.uint8([\n                [0, 128, 255],\n                [255, 128, 0]\n            ]),\n            (20, 30, 3)\n        )\n        assert hm.shape == (20, 30, 3)\n        assert hm.arr_0to1.shape == (2, 3, 1)\n        assert np.allclose(hm.arr_0to1[..., 0], np.float32([\n            [0, 128/255, 1.0],\n            [1.0, 128/255, 0]\n        ]))\n\n    def test_min_value_and_max_value(self):\n        # min_value, max_value\n        hm = ia.HeatmapsOnImage.from_uint8(\n            np.uint8([\n                [0, 128, 255],\n                [255, 128, 0]\n            ])[..., np.newaxis],\n            (20, 30, 3),\n            min_value=-1.0,\n            max_value=2.0\n        )\n        assert hm.shape == (20, 30, 3)\n        assert hm.arr_0to1.shape == (2, 3, 1)\n        assert np.allclose(hm.arr_0to1[..., 0], np.float32([\n            [0, 128/255, 1.0],\n            [1.0, 128/255, 0]\n        ]))\n        assert np.allclose(hm.min_value, -1.0)\n        assert np.allclose(hm.max_value, 2.0)\n\n\nclass TestHeatmapsOnImage_change_normalization(unittest.TestCase):\n    def test_increase_max_value(self):\n        # (0.0, 1.0) -> (0.0, 2.0)\n        arr = np.float32([\n            [0.0, 0.5, 1.0],\n            [1.0, 0.5, 0.0]\n        ])\n\n        observed = ia.HeatmapsOnImage.change_normalization(\n            arr, (0.0, 1.0), (0.0, 2.0))\n\n        expected = np.float32([\n            [0.0, 1.0, 2.0],\n            [2.0, 1.0, 0.0]\n        ])\n        assert np.allclose(observed, expected)\n\n    def test_decrease_min_and_max_value(self):\n        # (0.0, 1.0) -> (-1.0, 0.0)\n        arr = np.float32([\n            [0.0, 0.5, 1.0],\n            [1.0, 0.5, 0.0]\n        ])\n\n        observed = ia.HeatmapsOnImage.change_normalization(\n            arr, (0.0, 1.0), (-1.0, 0.0))\n\n        expected = np.float32([\n            [-1.0, -0.5, 0.0],\n            [0.0, -0.5, -1.0]\n        ])\n        assert np.allclose(observed, expected)\n\n    def test_increase_min_and_max_value__non_standard_source(self):\n        # (-1.0, 1.0) -> (1.0, 3.0)\n        arr = np.float32([\n            [-1.0, 0.0, 1.0],\n            [1.0, 0.0, -1.0]\n        ])\n\n        observed = ia.HeatmapsOnImage.change_normalization(\n            arr, (-1.0, 1.0), (1.0, 3.0))\n\n        expected = np.float32([\n            [1.0, 2.0, 3.0],\n            [3.0, 2.0, 1.0]\n        ])\n        assert np.allclose(observed, expected)\n\n    def test_value_ranges_given_as_heatmaps_on_image(self):\n        # (-1.0, 1.0) -> (1.0, 3.0)\n        # value ranges given as HeatmapsOnImage\n        arr = np.float32([\n            [-1.0, 0.0, 1.0],\n            [1.0, 0.0, -1.0]\n        ])\n        source = ia.HeatmapsOnImage(\n            np.float32([[0.0]]), min_value=-1.0, max_value=1.0, shape=(1, 1, 3))\n        target = ia.HeatmapsOnImage(\n            np.float32([[1.0]]), min_value=1.0, max_value=3.0, shape=(1, 1, 3))\n\n        observed = ia.HeatmapsOnImage.change_normalization(arr, source, target)\n\n        expected = np.float32([\n            [1.0, 2.0, 3.0],\n            [3.0, 2.0, 1.0]\n        ])\n        assert np.allclose(observed, expected)\n"
  },
  {
    "path": "test/augmentables/test_kps.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport imgaug as ia\nfrom imgaug.testutils import assertWarns\n\n\nclass TestKeypoint_project_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, kp, from_shape, to_shape):\n        return kp.project_(from_shape, to_shape)\n\n    def test_project_same_image_size(self):\n        kp = ia.Keypoint(y=1, x=2)\n        kp2 = self._func(kp, (10, 10), (10, 10))\n        assert kp2.y == 1\n        assert kp2.x == 2\n\n    def test_project_onto_higher_image(self):\n        kp = ia.Keypoint(y=1, x=2)\n        kp2 = self._func(kp, (10, 10), (20, 10))\n        assert kp2.y == 2\n        assert kp2.x == 2\n\n    def test_project_onto_wider_image(self):\n        kp = ia.Keypoint(y=1, x=2)\n        kp2 = self._func(kp, (10, 10), (10, 20))\n        assert kp2.y == 1\n        assert kp2.x == 4\n\n    def test_project_onto_higher_and_wider_image(self):\n        kp = ia.Keypoint(y=1, x=2)\n        kp2 = self._func(kp, (10, 10), (20, 20))\n        assert kp2.y == 2\n        assert kp2.x == 4\n\n    def test_inplaceness(self):\n        kp = ia.Keypoint(y=1, x=2)\n        kp2 = self._func(kp, (10, 10), (10, 10))\n        if self._is_inplace:\n            assert kp is kp2\n        else:\n            assert kp is not kp2\n\n\nclass TestKeypoint_project(TestKeypoint_project_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, kp, from_shape, to_shape):\n        return kp.project(from_shape, to_shape)\n\n\nclass TestKeypoint_shift_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, kp, *args, **kwargs):\n        return kp.shift_(*args, **kwargs)\n\n    def test_shift_on_y_axis(self):\n        kp = ia.Keypoint(y=1, x=2)\n        kp2 = self._func(kp, y=1)\n        assert kp2.y == 2\n        assert kp2.x == 2\n\n    def test_shift_on_y_axis_by_negative_amount(self):\n        kp = ia.Keypoint(y=1, x=2)\n        kp2 = self._func(kp, y=-1)\n        assert kp2.y == 0\n        assert kp2.x == 2\n\n    def test_shift_on_x_axis(self):\n        kp = ia.Keypoint(y=1, x=2)\n        kp2 = self._func(kp, x=1)\n        assert kp2.y == 1\n        assert kp2.x == 3\n\n    def test_shift_on_x_axis_by_negative_amount(self):\n        kp = ia.Keypoint(y=1, x=2)\n        kp2 = self._func(kp, x=-1)\n        assert kp2.y == 1\n        assert kp2.x == 1\n\n    def test_shift_on_both_axis(self):\n        kp = ia.Keypoint(y=1, x=2)\n        kp2 = self._func(kp, y=1, x=2)\n        assert kp2.y == 2\n        assert kp2.x == 4\n\n    def test_inplaceness(self):\n        kp = ia.Keypoint(y=1, x=2)\n        kp2 = self._func(kp, x=1)\n        if self._is_inplace:\n            assert kp is kp2\n        else:\n            assert kp is not kp2\n\n\nclass TestKeypoint_shift(TestKeypoint_shift_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, kp, *args, **kwargs):\n        return kp.shift(*args, **kwargs)\n\n\nclass TestKeypoint(unittest.TestCase):\n    def test___init__(self):\n        kp = ia.Keypoint(y=1, x=2)\n        assert kp.y == 1\n        assert kp.x == 2\n\n    def test___init___negative_values(self):\n        kp = ia.Keypoint(y=-1, x=-2)\n        assert kp.y == -1\n        assert kp.x == -2\n\n    def test___init___floats(self):\n        kp = ia.Keypoint(y=1.5, x=2.5)\n        assert np.isclose(kp.y, 1.5)\n        assert np.isclose(kp.x, 2.5)\n\n    def test_coords(self):\n        kp = ia.Keypoint(x=1, y=1.5)\n        coords = kp.coords\n        assert np.allclose(coords, [1, 1.5], atol=1e-8, rtol=0)\n\n    def test_x_int(self):\n        kp = ia.Keypoint(y=1, x=2)\n        assert kp.x == 2\n        assert kp.x_int == 2\n\n    def test_x_int_for_float_inputs(self):\n        kp = ia.Keypoint(y=1, x=2.7)\n        assert np.isclose(kp.x, 2.7)\n        assert kp.x_int == 3\n\n    def test_y_int(self):\n        kp = ia.Keypoint(y=1, x=2)\n        assert kp.y == 1\n        assert kp.y_int == 1\n\n    def test_y_int_for_float_inputs(self):\n        kp = ia.Keypoint(y=1.7, x=2)\n        assert np.isclose(kp.y, 1.7)\n        assert kp.y_int == 2\n\n    def test_xy(self):\n        kp = ia.Keypoint(x=2, y=1.7)\n        assert np.allclose(kp.xy, (2, 1.7))\n\n    def test_xy_int(self):\n        kp = ia.Keypoint(x=1.3, y=1.6)\n        xy = kp.xy_int\n        assert np.allclose(xy, (1, 2))\n        assert xy.dtype.name == \"int32\"\n\n    def test_is_out_of_image(self):\n        kp = ia.Keypoint(y=1, x=2)\n        image_shape = (10, 20, 3)\n        ooi = kp.is_out_of_image(image_shape)\n        assert not ooi\n\n    def test_is_out_of_image__ooi_y(self):\n        kp = ia.Keypoint(y=11, x=2)\n        image_shape = (10, 20, 3)\n        ooi = kp.is_out_of_image(image_shape)\n        assert ooi\n\n    def test_is_out_of_image__ooi_x(self):\n        kp = ia.Keypoint(y=1, x=21)\n        image_shape = (10, 20, 3)\n        ooi = kp.is_out_of_image(image_shape)\n        assert ooi\n\n    def test_compute_out_of_image_fraction(self):\n        kp = ia.Keypoint(y=1, x=2)\n        image_shape = (10, 20, 3)\n        fraction = kp.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(fraction, 0.0)\n\n    def test_compute_out_of_image_fraction_ooi_y(self):\n        kp = ia.Keypoint(y=11, x=2)\n        image_shape = (10, 20, 3)\n        fraction = kp.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(fraction, 1.0)\n\n    def test_compute_out_of_image_fraction_ooi_x(self):\n        kp = ia.Keypoint(y=1, x=21)\n        image_shape = (10, 20, 3)\n        fraction = kp.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(fraction, 1.0)\n\n    def test_draw_on_image(self):\n        kp = ia.Keypoint(x=0, y=0)\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n        image_kp = kp.draw_on_image(\n            image, color=(0, 255, 0), alpha=1, size=1, copy=True,\n            raise_if_out_of_image=False)\n        assert np.all(image_kp[0, 0, :] == [0, 255, 0])\n        assert np.all(image_kp[1:, :, :] == 10)\n        assert np.all(image_kp[:, 1:, :] == 10)\n\n    def test_draw_on_image_kp_at_top_left_corner_size_1(self):\n        kp = ia.Keypoint(x=4, y=4)\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n        image_kp = kp.draw_on_image(\n            image, color=(0, 255, 0), alpha=1, size=1, copy=True,\n            raise_if_out_of_image=False)\n        assert np.all(image_kp[4, 4, :] == [0, 255, 0])\n        assert np.all(image_kp[:4, :, :] == 10)\n        assert np.all(image_kp[:, :4, :] == 10)\n\n    def test_draw_on_image_kp_at_top_left_corner_size_5(self):\n        kp = ia.Keypoint(x=0, y=0)\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n        image_kp = kp.draw_on_image(\n            image, color=(0, 255, 0), alpha=1, size=5, copy=True,\n            raise_if_out_of_image=False)\n        assert np.all(image_kp[:3, :3, :] == [0, 255, 0])\n        assert np.all(image_kp[3:, :, :] == 10)\n        assert np.all(image_kp[:, 3:, :] == 10)\n\n    def test_draw_on_image_kp_at_top_left_corner_custom_color_and_alpha(self):\n        kp = ia.Keypoint(x=0, y=0)\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n        image_kp = kp.draw_on_image(\n            image, color=(0, 200, 0), alpha=0.5, size=1, copy=True,\n            raise_if_out_of_image=False)\n        assert np.all(image_kp[0, 0, :] == [0 + 5, 100 + 5, 0 + 5])\n        assert np.all(image_kp[1:, :, :] == 10)\n        assert np.all(image_kp[:, 1:, :] == 10)\n\n    def test_draw_on_image_kp_somewhere_inside_image_size_5(self):\n        kp = ia.Keypoint(x=4, y=4)\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n        image_kp = kp.draw_on_image(\n            image, color=(0, 255, 0), alpha=1, size=5, copy=True,\n            raise_if_out_of_image=False)\n        assert np.all(image_kp[2:, 2:, :] == [0, 255, 0])\n        assert np.all(image_kp[:2, :, :] == 10)\n        assert np.all(image_kp[:, :2, :] == 10)\n\n    def test_draw_on_image_kp_at_bottom_right_corner_size_5(self):\n        kp = ia.Keypoint(x=5, y=5)\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n        image_kp = kp.draw_on_image(\n            image, color=(0, 255, 0), alpha=1, size=5, copy=True,\n            raise_if_out_of_image=False)\n        assert np.all(image_kp[3:, 3:, :] == [0, 255, 0])\n        assert np.all(image_kp[:3, :, :] == 10)\n        assert np.all(image_kp[:, :3, :] == 10)\n\n    def test_draw_on_image_kp_outside_image(self):\n        kp = ia.Keypoint(x=-1, y=-1)\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n        image_kp = kp.draw_on_image(\n            image, color=(0, 255, 0), alpha=1, size=5, copy=True,\n            raise_if_out_of_image=False)\n        assert np.all(image_kp[:2, :2, :] == [0, 255, 0])\n        assert np.all(image_kp[2:, :, :] == 10)\n        assert np.all(image_kp[:, 2:, :] == 10)\n\n    def test_generate_similar_points_manhattan_0_steps_list(self):\n        kp = ia.Keypoint(y=4, x=5)\n        kps_manhatten = kp.generate_similar_points_manhattan(\n            0, 1.0, return_array=False)\n        assert len(kps_manhatten) == 1\n        assert kps_manhatten[0].y == 4\n        assert kps_manhatten[0].x == 5\n\n    def test_generate_similar_points_manhattan_1_step_list(self):\n        kp = ia.Keypoint(y=4, x=5)\n        kps_manhatten = kp.generate_similar_points_manhattan(\n            1, 1.0, return_array=False)\n        assert len(kps_manhatten) == 5\n        expected = [(4, 5), (3, 5), (4, 6), (5, 5), (4, 4)]\n        for y, x in expected:\n            assert any([\n                np.allclose(\n                    [y, x],\n                    [kp_manhatten.y, kp_manhatten.x]\n                )\n                for kp_manhatten\n                in kps_manhatten\n            ])\n\n    def test_generate_similar_points_manhattan_1_step_array(self):\n        kp = ia.Keypoint(y=4, x=5)\n        kps_manhatten = kp.generate_similar_points_manhattan(\n            1, 1.0, return_array=True)\n        assert kps_manhatten.shape == (5, 2)\n        expected = [(4, 5), (3, 5), (4, 6), (5, 5), (4, 4)]\n        for y, x in expected:\n            assert any([\n                np.allclose(\n                    [y, x],\n                    [kp_manhatten_y, kp_manhatten_x]\n                )\n                for kp_manhatten_x, kp_manhatten_y\n                in kps_manhatten\n            ])\n\n    def test_coords_almost_equals(self):\n        kp1 = ia.Keypoint(x=1, y=1.5)\n        kp2 = ia.Keypoint(x=1, y=1.5)\n\n        equal = kp1.coords_almost_equals(kp2)\n\n        assert equal\n\n    def test_coords_almost_equals__unequal(self):\n        kp1 = ia.Keypoint(x=1, y=1.5)\n        kp2 = ia.Keypoint(x=1, y=1.5+10.0)\n\n        equal = kp1.coords_almost_equals(kp2)\n\n        assert not equal\n\n    def test_coords_almost_equals__distance_below_threshold(self):\n        kp1 = ia.Keypoint(x=1, y=1.5)\n        kp2 = ia.Keypoint(x=1, y=1.5+1e-2)\n\n        equal = kp1.coords_almost_equals(kp2, max_distance=1e-1)\n\n        assert equal\n\n    def test_coords_almost_equals__distance_exceeds_threshold(self):\n        kp1 = ia.Keypoint(x=1, y=1.5)\n        kp2 = ia.Keypoint(x=1, y=1.5+1e-2)\n\n        equal = kp1.coords_almost_equals(kp2, max_distance=1e-3)\n\n        assert not equal\n\n    def test_coords_almost_equals__array(self):\n        kp1 = ia.Keypoint(x=1, y=1.5)\n        kp2 = np.float32([1, 1.5])\n\n        equal = kp1.coords_almost_equals(kp2)\n\n        assert equal\n\n    def test_coords_almost_equals__array_unequal(self):\n        kp1 = ia.Keypoint(x=1, y=1.5)\n        kp2 = np.float32([1, 1.5+1.0])\n\n        equal = kp1.coords_almost_equals(kp2)\n\n        assert not equal\n\n    def test_coords_almost_equals__tuple(self):\n        kp1 = ia.Keypoint(x=1, y=1.5)\n        kp2 = (1, 1.5)\n\n        equal = kp1.coords_almost_equals(kp2)\n\n        assert equal\n\n    def test_coords_almost_equals__tuple_unequal(self):\n        kp1 = ia.Keypoint(x=1, y=1.5)\n        kp2 = (1, 1.5+1.0)\n\n        equal = kp1.coords_almost_equals(kp2)\n\n        assert not equal\n\n    @mock.patch(\"imgaug.augmentables.kps.Keypoint.coords_almost_equals\")\n    def test_almost_equals(self, mock_cae):\n        mock_cae.return_value = \"foo\"\n        kp1 = ia.Keypoint(x=1, y=1.5)\n        kp2 = ia.Keypoint(x=1, y=1.5)\n\n        result = kp1.almost_equals(kp2, max_distance=2)\n\n        assert result == \"foo\"\n        mock_cae.assert_called_once_with(kp2, max_distance=2)\n\n    def test_string_conversion_ints(self):\n        kp = ia.Keypoint(y=1, x=2)\n        assert (\n            kp.__repr__()\n            == kp.__str__()\n            == \"Keypoint(x=2.00000000, y=1.00000000)\"\n        )\n\n    def test_string_conversion_floats(self):\n        kp = ia.Keypoint(y=1.2, x=2.7)\n        assert (\n            kp.__repr__()\n            == kp.__str__()\n            == \"Keypoint(x=2.70000000, y=1.20000000)\"\n        )\n\n\nclass TestKeypointsOnImage_items_setter(unittest.TestCase):\n    def test_with_list_of_keypoints(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpsoi = ia.KeypointsOnImage(keypoints=[], shape=(10, 20, 3))\n        kpsoi.items = kps\n        assert np.all([\n            kp_i.x == kp_j.x and kp_i.y == kp_j.y\n            for kp_i, kp_j\n            in zip(kpsoi.keypoints, kps)\n        ])\n\n\nclass TestKeypointsOnImage_on_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, kpsoi, *args, **kwargs):\n        return kpsoi.on_(*args, **kwargs)\n\n    def test_same_image_size(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(10, 20, 3))\n\n        kpi2 = self._func(kpi, (10, 20, 3))\n\n        assert np.all([\n            kp_i.x == kp_j.x and kp_i.y == kp_j.y\n            for kp_i, kp_j\n            in zip(kpi.keypoints, kpi2.keypoints)\n        ])\n        assert kpi2.shape == (10, 20, 3)\n\n    def test_wider_image(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(10, 20, 3))\n\n        kpi2 = self._func(kpi, (20, 40, 3))\n\n        assert kpi2.keypoints[0].x == 2\n        assert kpi2.keypoints[0].y == 4\n        assert kpi2.keypoints[1].x == 6\n        assert kpi2.keypoints[1].y == 8\n        assert kpi2.shape == (20, 40, 3)\n\n    def test_wider_image_shape_given_as_array(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(10, 20, 3))\n\n        image = np.zeros((20, 40, 3), dtype=np.uint8)\n        kpi2 = self._func(kpi, image)\n\n        assert kpi2.keypoints[0].x == 2\n        assert kpi2.keypoints[0].y == 4\n        assert kpi2.keypoints[1].x == 6\n        assert kpi2.keypoints[1].y == 8\n        assert kpi2.shape == image.shape\n\n    def test_inplaceness(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(10, 20, 3))\n\n        kpi2 = self._func(kpi, (10, 20, 3))\n\n        if self._is_inplace:\n            assert kpi is kpi2\n        else:\n            assert kpi is not kpi2\n\n\nclass TestKeypointsOnImage_on(TestKeypointsOnImage_on_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, kpsoi, *args, **kwargs):\n        return kpsoi.on(*args, **kwargs)\n\n\nclass TestKeypointsOnImage_shift_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, kpsoi, *args, **kwargs):\n        return kpsoi.shift_(*args, **kwargs)\n\n    def test_shift_by_zero_on_both_axis(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n        kpi2 = self._func(kpi, x=0, y=0)\n        assert kpi2.keypoints[0].x == 1\n        assert kpi2.keypoints[0].y == 2\n        assert kpi2.keypoints[1].x == 3\n        assert kpi2.keypoints[1].y == 4\n\n    def test_shift_by_1_on_x_axis(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n\n        kpi2 = self._func(kpi, x=1)\n\n        assert kpi2.keypoints[0].x == 1 + 1\n        assert kpi2.keypoints[0].y == 2\n        assert kpi2.keypoints[1].x == 3 + 1\n        assert kpi2.keypoints[1].y == 4\n\n    def test_shift_by_negative_1_on_x_axis(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n\n        kpi2 = self._func(kpi, x=-1)\n\n        assert kpi2.keypoints[0].x == 1 - 1\n        assert kpi2.keypoints[0].y == 2\n        assert kpi2.keypoints[1].x == 3 - 1\n        assert kpi2.keypoints[1].y == 4\n\n    def test_shift_by_1_on_y_axis(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n\n        kpi2 = self._func(kpi, y=1)\n\n        assert kpi2.keypoints[0].x == 1\n        assert kpi2.keypoints[0].y == 2 + 1\n        assert kpi2.keypoints[1].x == 3\n        assert kpi2.keypoints[1].y == 4 + 1\n\n    def test_shift_by_negative_1_on_y_axis(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n\n        kpi2 = self._func(kpi, y=-1)\n\n        assert kpi2.keypoints[0].x == 1\n        assert kpi2.keypoints[0].y == 2 - 1\n        assert kpi2.keypoints[1].x == 3\n        assert kpi2.keypoints[1].y == 4 - 1\n\n    def test_shift_on_both_axis(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n\n        kpi2 = self._func(kpi, x=1, y=2)\n\n        assert kpi2.keypoints[0].x == 1 + 1\n        assert kpi2.keypoints[0].y == 2 + 2\n        assert kpi2.keypoints[1].x == 3 + 1\n        assert kpi2.keypoints[1].y == 4 + 2\n\n    def test_inplaceness(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n        kpi2 = self._func(kpi, x=0, y=0)\n\n        if self._is_inplace:\n            assert kpi is kpi2\n        else:\n            assert kpi is not kpi2\n\n\nclass TestKeypointsOnImage_shift(TestKeypointsOnImage_shift_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, kpsoi, *args, **kwargs):\n        return kpsoi.shift(*args, **kwargs)\n\n\nclass TestKeypointsOnImage(unittest.TestCase):\n    def test_items(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(40, 50, 3))\n\n        items = kpsoi.items\n\n        assert items == kps\n\n    def test_items_empty(self):\n        kpsoi = ia.KeypointsOnImage([], shape=(40, 50, 3))\n\n        items = kpsoi.items\n\n        assert items == []\n\n    def test_height(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(10, 20, 3))\n        assert kpi.height == 10\n\n    def test_width(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(10, 20, 3))\n        assert kpi.width == 20\n\n    def test_shape_is_array(self):\n        image = np.zeros((10, 20, 3), dtype=np.uint8)\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        with assertWarns(self, ia.DeprecationWarning):\n            kpi = ia.KeypointsOnImage(\n                keypoints=kps,\n                shape=image\n            )\n        assert kpi.shape == (10, 20, 3)\n\n    def test_draw_on_image(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n\n        kps_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        kps_mask[2, 1] = 1\n        kps_mask[4, 3] = 1\n\n        image_kps = kpi.draw_on_image(\n            image, color=[0, 255, 0], size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_kps[kps_mask] == [0, 255, 0])\n        assert np.all(image_kps[~kps_mask] == [10, 10, 10])\n\n    def test_draw_on_image_alpha_is_50_percent(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n\n        kps_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        kps_mask[2, 1] = 1\n        kps_mask[4, 3] = 1\n\n        image_kps = kpi.draw_on_image(\n            image, color=[0, 255, 0], alpha=0.5, size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        bg_plus_color_at_alpha = [int(0.5*10+0),\n                                  int(0.5*10+0.5*255),\n                                  int(10*0.5+0)]\n        assert np.all(image_kps[kps_mask] == bg_plus_color_at_alpha)\n        assert np.all(image_kps[~kps_mask] == [10, 10, 10])\n\n    def test_draw_on_image_size_3(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n\n        kps_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        kps_mask[2, 1] = 1\n        kps_mask[4, 3] = 1\n\n        image_kps = kpi.draw_on_image(\n            image, color=[0, 255, 0], size=3, copy=True,\n            raise_if_out_of_image=False)\n        kps_mask_size3 = np.copy(kps_mask)\n        kps_mask_size3[2-1:2+1+1, 1-1:1+1+1] = 1\n        kps_mask_size3[4-1:4+1+1, 3-1:3+1+1] = 1\n\n        assert np.all(image_kps[kps_mask_size3] == [0, 255, 0])\n        assert np.all(image_kps[~kps_mask_size3] == [10, 10, 10])\n\n    def test_draw_on_image_blue_color(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n\n        kps_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        kps_mask[2, 1] = 1\n        kps_mask[4, 3] = 1\n\n        image_kps = kpi.draw_on_image(\n            image, color=[0, 0, 255], size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_kps[kps_mask] == [0, 0, 255])\n        assert np.all(image_kps[~kps_mask] == [10, 10, 10])\n\n    def test_draw_on_image_single_int_as_color(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n\n        kps_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        kps_mask[2, 1] = 1\n        kps_mask[4, 3] = 1\n\n        image_kps = kpi.draw_on_image(\n            image, color=255, size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_kps[kps_mask] == [255, 255, 255])\n        assert np.all(image_kps[~kps_mask] == [10, 10, 10])\n\n    def test_draw_on_image_copy_is_false(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n\n        kps_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        kps_mask[2, 1] = 1\n        kps_mask[4, 3] = 1\n\n        image2 = np.copy(image)\n        image_kps = kpi.draw_on_image(\n            image2, color=[0, 255, 0], size=1, copy=False,\n            raise_if_out_of_image=False)\n\n        assert np.all(image2 == image_kps)\n        assert np.all(image_kps[kps_mask] == [0, 255, 0])\n        assert np.all(image_kps[~kps_mask] == [10, 10, 10])\n        assert np.all(image2[kps_mask] == [0, 255, 0])\n        assert np.all(image2[~kps_mask] == [10, 10, 10])\n\n    def test_draw_on_image_keypoint_is_outside_of_image(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(\n            keypoints=kps + [ia.Keypoint(x=100, y=100)],\n            shape=(5, 5, 3)\n        )\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n\n        kps_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        kps_mask[2, 1] = 1\n        kps_mask[4, 3] = 1\n\n        image_kps = kpi.draw_on_image(\n            image, color=[0, 255, 0], size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_kps[kps_mask] == [0, 255, 0])\n        assert np.all(image_kps[~kps_mask] == [10, 10, 10])\n\n    def test_draw_on_image_keypoint_is_outside_of_image_and_raise_true(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(\n            keypoints=kps + [ia.Keypoint(x=100, y=100)],\n            shape=(5, 5, 3)\n        )\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n\n        with self.assertRaises(Exception) as context:\n            _ = kpi.draw_on_image(\n                image, color=[0, 255, 0], size=1, copy=True,\n                raise_if_out_of_image=True)\n\n        assert \"Cannot draw keypoint\" in str(context.exception)\n\n    def test_draw_on_image_one_kp_at_bottom_right_corner(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(\n            keypoints=kps + [ia.Keypoint(x=5, y=5)],\n            shape=(5, 5, 3))\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n\n        kps_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        kps_mask[2, 1] = 1\n        kps_mask[4, 3] = 1\n\n        image_kps = kpi.draw_on_image(\n            image, color=[0, 255, 0], size=1, copy=True,\n            raise_if_out_of_image=False)\n\n        assert np.all(image_kps[kps_mask] == [0, 255, 0])\n        assert np.all(image_kps[~kps_mask] == [10, 10, 10])\n\n    def test_draw_on_image_one_kp_at_bottom_right_corner_and_raise_true(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(\n            keypoints=kps + [ia.Keypoint(x=5, y=5)],\n            shape=(5, 5, 3))\n        image = np.zeros((5, 5, 3), dtype=np.uint8) + 10\n\n        kps_mask = np.zeros(image.shape[0:2], dtype=np.bool)\n        kps_mask[2, 1] = 1\n        kps_mask[4, 3] = 1\n\n        with self.assertRaises(Exception) as context:\n            _ = kpi.draw_on_image(\n                image, color=[0, 255, 0], size=1, copy=True,\n                raise_if_out_of_image=True)\n\n        assert \"Cannot draw keypoint\" in str(context.exception)\n\n    @classmethod\n    def _test_clip_remove_frac(cls, func, inplace):\n        item1 = ia.Keypoint(x=5, y=1)\n        item2 = ia.Keypoint(x=15, y=1)\n        cbaoi = ia.KeypointsOnImage([item1, item2], shape=(10, 10, 3))\n\n        cbaoi_reduced = func(cbaoi)\n\n        assert len(cbaoi_reduced.items) == 1\n        assert np.allclose(cbaoi_reduced.to_xy_array(), [item1.xy])\n        if inplace:\n            assert cbaoi_reduced is cbaoi\n        else:\n            assert cbaoi_reduced is not cbaoi\n            assert len(cbaoi.items) == 2\n\n    def test_remove_out_of_image_fraction_(self):\n        def _func(cbaoi):\n            return cbaoi.remove_out_of_image_fraction_(0.6)\n\n        self._test_clip_remove_frac(_func, True)\n\n    def test_remove_out_of_image_fraction(self):\n        def _func(cbaoi):\n            return cbaoi.remove_out_of_image_fraction(0.6)\n\n        self._test_clip_remove_frac(_func, False)\n\n    def test_clip_out_of_image_fraction_(self):\n        def _func(cbaoi):\n            return cbaoi.clip_out_of_image_()\n\n        self._test_clip_remove_frac(_func, True)\n\n    def test_clip_out_of_image_fraction(self):\n        def _func(cbaoi):\n            return cbaoi.clip_out_of_image()\n\n        self._test_clip_remove_frac(_func, False)\n\n    def test_to_xy_array(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n\n        observed = kpi.to_xy_array()\n\n        expected = np.float32([\n            [1, 2],\n            [3, 4]\n        ])\n        assert np.allclose(observed, expected)\n\n    def test_from_xy_array(self):\n        arr = np.float32([\n            [1, 2],\n            [3, 4]\n        ])\n\n        kpi = ia.KeypointsOnImage.from_xy_array(arr, shape=(5, 5, 3))\n\n        assert np.isclose(kpi.keypoints[0].x, 1)\n        assert np.isclose(kpi.keypoints[0].y, 2)\n        assert np.isclose(kpi.keypoints[1].x, 3)\n        assert np.isclose(kpi.keypoints[1].y, 4)\n\n    def test_fill_from_xy_array___empty_array(self):\n        xy = np.zeros((0, 2), dtype=np.float32)\n        kps = ia.KeypointsOnImage([], shape=(2, 2, 3))\n\n        kps = kps.fill_from_xy_array_(xy)\n\n        assert len(kps.keypoints) == 0\n\n    def test_fill_from_xy_array___empty_list(self):\n        xy = []\n        kps = ia.KeypointsOnImage([], shape=(2, 2, 3))\n\n        kps = kps.fill_from_xy_array_(xy)\n\n        assert len(kps.keypoints) == 0\n\n    def test_fill_from_xy_array___array_with_two_coords(self):\n        xy = np.array([(0, 0), (1, 2)], dtype=np.float32)\n        kps = ia.KeypointsOnImage([ia.Keypoint(10, 20), ia.Keypoint(30, 40)],\n                                  shape=(2, 2, 3))\n\n        kps = kps.fill_from_xy_array_(xy)\n\n        assert len(kps.keypoints) == 2\n        assert kps.keypoints[0].x == 0\n        assert kps.keypoints[0].y == 0\n        assert kps.keypoints[1].x == 1\n        assert kps.keypoints[1].y == 2\n\n    def test_fill_from_xy_array___list_with_two_coords(self):\n        xy = [(0, 0), (1, 2)]\n        kps = ia.KeypointsOnImage([ia.Keypoint(10, 20), ia.Keypoint(30, 40)],\n                                  shape=(2, 2, 3))\n\n        kps = kps.fill_from_xy_array_(xy)\n\n        assert len(kps.keypoints) == 2\n        assert kps.keypoints[0].x == 0\n        assert kps.keypoints[0].y == 0\n        assert kps.keypoints[1].x == 1\n        assert kps.keypoints[1].y == 2\n\n    def test_to_keypoint_image_size_1(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n\n        image = kpi.to_keypoint_image(size=1)\n\n        kps_mask = np.zeros((5, 5, 2), dtype=np.bool)\n        kps_mask[2, 1, 0] = 1\n        kps_mask[4, 3, 1] = 1\n        assert np.all(image[kps_mask] == 255)\n        assert np.all(image[~kps_mask] == 0)\n\n    def test_to_keypoint_image_size_3(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n\n        image = kpi.to_keypoint_image(size=3)\n\n        kps_mask = np.zeros((5, 5, 2), dtype=np.bool)\n        kps_mask[2-1:2+1+1, 1-1:1+1+1, 0] = 1\n        kps_mask[4-1:4+1+1, 3-1:3+1+1, 1] = 1\n        assert np.all(image[kps_mask] >= 128)\n        assert np.all(image[~kps_mask] == 0)\n\n    def test_from_keypoint_image(self):\n        kps_image = np.zeros((5, 5, 2), dtype=np.uint8)\n        kps_image[2, 1, 0] = 255\n        kps_image[4, 3, 1] = 255\n\n        kpi2 = ia.KeypointsOnImage.from_keypoint_image(\n            kps_image, nb_channels=3)\n\n        assert kpi2.shape == (5, 5, 3)\n        assert len(kpi2.keypoints) == 2\n        assert kpi2.keypoints[0].y == 2.5\n        assert kpi2.keypoints[0].x == 1.5\n        assert kpi2.keypoints[1].y == 4.5\n        assert kpi2.keypoints[1].x == 3.5\n\n    def test_from_keypoint_image_dict_as_if_not_found_thresh_20(self):\n        kps_image = np.zeros((5, 5, 2), dtype=np.uint8)\n        kps_image[2, 1, 0] = 255\n        kps_image[4, 3, 1] = 10\n\n        kpi2 = ia.KeypointsOnImage.from_keypoint_image(\n            kps_image,\n            if_not_found_coords={\"x\": -1, \"y\": -2},\n            threshold=20,\n            nb_channels=3)\n\n        assert kpi2.shape == (5, 5, 3)\n        assert len(kpi2.keypoints) == 2\n        assert kpi2.keypoints[0].y == 2.5\n        assert kpi2.keypoints[0].x == 1.5\n        assert kpi2.keypoints[1].y == -2\n        assert kpi2.keypoints[1].x == -1\n\n    def test_from_keypoint_image_tuple_as_if_not_found_thresh_20(self):\n        kps_image = np.zeros((5, 5, 2), dtype=np.uint8)\n        kps_image[2, 1, 0] = 255\n        kps_image[4, 3, 1] = 10\n\n        kpi2 = ia.KeypointsOnImage.from_keypoint_image(\n            kps_image,\n            if_not_found_coords=(-1, -2),\n            threshold=20,\n            nb_channels=3)\n\n        assert kpi2.shape == (5, 5, 3)\n        assert len(kpi2.keypoints) == 2\n        assert kpi2.keypoints[0].y == 2.5\n        assert kpi2.keypoints[0].x == 1.5\n        assert kpi2.keypoints[1].y == -2\n        assert kpi2.keypoints[1].x == -1\n\n    def test_from_keypoint_image_none_as_if_not_found_thresh_20(self):\n        kps_image = np.zeros((5, 5, 2), dtype=np.uint8)\n        kps_image[2, 1, 0] = 255\n        kps_image[4, 3, 1] = 10\n\n        kpi2 = ia.KeypointsOnImage.from_keypoint_image(\n            kps_image,\n            if_not_found_coords=None,\n            threshold=20,\n            nb_channels=3)\n\n        assert kpi2.shape == (5, 5, 3)\n        assert len(kpi2.keypoints) == 1\n        assert kpi2.keypoints[0].y == 2.5\n        assert kpi2.keypoints[0].x == 1.5\n\n    def test_from_keypoint_image_bad_datatype_as_if_not_found(self):\n        kps_image = np.zeros((5, 5, 2), dtype=np.uint8)\n        kps_image[2, 1, 0] = 255\n        kps_image[4, 3, 1] = 10\n\n        with self.assertRaises(Exception) as context:\n            _ = ia.KeypointsOnImage.from_keypoint_image(\n                kps_image,\n                if_not_found_coords=\"exception-please\",\n                threshold=20,\n                nb_channels=3)\n\n        assert \"Expected if_not_found_coords to be\" in str(context.exception)\n\n    @classmethod\n    def _get_single_keypoint_distance_map(cls):\n        # distance map for one keypoint at (x=2, y=3) on (5, 5, 3) image\n        distance_map_xx = np.float32([\n            [0, 1, 2, 3, 4],\n            [0, 1, 2, 3, 4],\n            [0, 1, 2, 3, 4],\n            [0, 1, 2, 3, 4],\n            [0, 1, 2, 3, 4]\n        ])\n        distance_map_yy = np.float32([\n            [0, 0, 0, 0, 0],\n            [1, 1, 1, 1, 1],\n            [2, 2, 2, 2, 2],\n            [3, 3, 3, 3, 3],\n            [4, 4, 4, 4, 4]\n        ])\n        distance_map = np.sqrt(\n            (distance_map_xx - 2)**2\n            + (distance_map_yy - 3)**2)\n        return distance_map[..., np.newaxis]\n\n    def test_to_distance_maps(self):\n        kpi = ia.KeypointsOnImage(\n            keypoints=[ia.Keypoint(x=2, y=3)],\n            shape=(5, 5, 3))\n\n        distance_map = kpi.to_distance_maps()\n\n        expected = self._get_single_keypoint_distance_map()\n        assert distance_map.shape == (5, 5, 1)\n        assert np.allclose(distance_map, expected)\n\n    def test_to_distance_maps_inverted(self):\n        kpi = ia.KeypointsOnImage(\n            keypoints=[ia.Keypoint(x=2, y=3)],\n            shape=(5, 5, 3))\n\n        distance_map = kpi.to_distance_maps(inverted=True)\n\n        expected = self._get_single_keypoint_distance_map()\n        expected_inv = np.divide(np.ones_like(expected), expected+1)\n        assert distance_map.shape == (5, 5, 1)\n        assert np.allclose(distance_map, expected_inv)\n\n    @classmethod\n    def _get_two_points_keypoint_distance_map(cls):\n        # distance map for two keypoints at (x=2, y=3) and (x=1, y=0) on\n        # (4, 4, 3) image\n        #\n        # Visualization of positions on (4, 4) map (X=position, 1=KP 1 is\n        # closest, 2=KP 2 is closest, B=close to both):\n        #\n        #     [1, X, 1, 1]\n        #     [1, 1, 1, B]\n        #     [B, 2, 2, 2]\n        #     [2, 2, X, 2]\n        #\n        distance_map_x = np.float32([\n            [(0-1)**2, (1-1)**2, (2-1)**2, (3-1)**2],\n            [(0-1)**2, (1-1)**2, (2-1)**2, (3-1)**2],\n            [(0-1)**2, (1-2)**2, (2-2)**2, (3-2)**2],\n            [(0-2)**2, (1-2)**2, (2-2)**2, (3-2)**2],\n        ])\n\n        distance_map_y = np.float32([\n            [(0-0)**2, (0-0)**2, (0-0)**2, (0-0)**2],\n            [(1-0)**2, (1-0)**2, (1-0)**2, (1-0)**2],\n            [(2-0)**2, (2-3)**2, (2-3)**2, (2-3)**2],\n            [(3-3)**2, (3-3)**2, (3-3)**2, (3-3)**2],\n        ])\n\n        distance_map = np.sqrt(distance_map_x + distance_map_y)\n        return distance_map\n\n    def test_to_distance_maps_two_keypoints(self):\n        # TODO this test could have been done a bit better by simply splitting\n        #      the distance maps, one per keypoint, considering the function\n        #      returns one distance map per keypoint\n        kpi = ia.KeypointsOnImage(\n            keypoints=[ia.Keypoint(x=2, y=3), ia.Keypoint(x=1, y=0)],\n            shape=(4, 4, 3))\n\n        distance_map = kpi.to_distance_maps()\n\n        expected = self._get_two_points_keypoint_distance_map()\n        assert np.allclose(np.min(distance_map, axis=2), expected)\n\n    def test_to_distance_maps_two_keypoints_inverted(self):\n        kpi = ia.KeypointsOnImage(\n            keypoints=[ia.Keypoint(x=2, y=3), ia.Keypoint(x=1, y=0)],\n            shape=(4, 4, 3))\n\n        distance_map_inv = kpi.to_distance_maps(inverted=True)\n\n        expected = self._get_two_points_keypoint_distance_map()\n        expected_inv = np.divide(np.ones_like(expected), expected+1)\n        assert np.allclose(np.max(distance_map_inv, axis=2), expected_inv)\n\n    @classmethod\n    def _get_distance_maps_for_from_dmap_tests(cls):\n        distance_map1 = np.float32([\n            [2, 2, 2, 2, 2],\n            [2, 1, 1, 1, 2],\n            [2, 1, 0, 1, 2],\n            [2, 1, 1, 1, 2]\n        ])\n        distance_map2 = np.float32([\n            [4, 3, 2, 2, 2],\n            [4, 3, 2, 1, 1],\n            [4, 3, 2, 1, 0.1],\n            [4, 3, 2, 1, 1]\n        ])\n        distance_maps = np.concatenate([\n            distance_map1[..., np.newaxis],\n            distance_map2[..., np.newaxis]\n        ], axis=2)\n        return distance_maps\n\n    def test_from_distance_maps(self):\n        distance_maps = self._get_distance_maps_for_from_dmap_tests()\n\n        kpi = ia.KeypointsOnImage.from_distance_maps(distance_maps)\n\n        assert len(kpi.keypoints) == 2\n        assert kpi.keypoints[0].x == 2\n        assert kpi.keypoints[0].y == 2\n        assert kpi.keypoints[1].x == 4\n        assert kpi.keypoints[1].y == 2\n        assert kpi.shape == (4, 5)\n\n    def test_from_distance_maps_nb_channels_4(self):\n        distance_maps = self._get_distance_maps_for_from_dmap_tests()\n\n        kpi = ia.KeypointsOnImage.from_distance_maps(distance_maps,\n                                                     nb_channels=4)\n\n        assert len(kpi.keypoints) == 2\n        assert kpi.keypoints[0].x == 2\n        assert kpi.keypoints[0].y == 2\n        assert kpi.keypoints[1].x == 4\n        assert kpi.keypoints[1].y == 2\n        assert kpi.shape == (4, 5, 4)\n\n    def test_from_distance_maps_inverted(self):\n        distance_maps = self._get_distance_maps_for_from_dmap_tests()\n        distance_maps_inv = np.divide(\n            np.ones_like(distance_maps),\n            distance_maps+1)\n\n        kpi = ia.KeypointsOnImage.from_distance_maps(distance_maps_inv,\n                                                     inverted=True)\n\n        assert len(kpi.keypoints) == 2\n        assert kpi.keypoints[0].x == 2\n        assert kpi.keypoints[0].y == 2\n        assert kpi.keypoints[1].x == 4\n        assert kpi.keypoints[1].y == 2\n        assert kpi.shape == (4, 5)\n\n    def test_from_distance_maps_if_not_found_is_tuple_thresh_009(self):\n        distance_maps = self._get_distance_maps_for_from_dmap_tests()\n\n        kpi = ia.KeypointsOnImage.from_distance_maps(\n            distance_maps, if_not_found_coords=(1, 1), threshold=0.09)\n\n        assert len(kpi.keypoints) == 2\n        assert kpi.keypoints[0].x == 2\n        assert kpi.keypoints[0].y == 2\n        assert kpi.keypoints[1].x == 1\n        assert kpi.keypoints[1].y == 1\n        assert kpi.shape == (4, 5)\n\n    def test_from_distance_maps_if_not_found_is_dict_thresh_009(self):\n        distance_maps = self._get_distance_maps_for_from_dmap_tests()\n\n        kpi = ia.KeypointsOnImage.from_distance_maps(\n            distance_maps,\n            if_not_found_coords={\"x\": 1, \"y\": 2},\n            threshold=0.09)\n\n        assert len(kpi.keypoints) == 2\n        assert kpi.keypoints[0].x == 2\n        assert kpi.keypoints[0].y == 2\n        assert kpi.keypoints[1].x == 1\n        assert kpi.keypoints[1].y == 2\n        assert kpi.shape == (4, 5)\n\n    def test_from_distance_maps_if_not_found_is_none_thresh_009(self):\n        distance_maps = self._get_distance_maps_for_from_dmap_tests()\n\n        kpi = ia.KeypointsOnImage.from_distance_maps(\n            distance_maps,\n            if_not_found_coords=None,\n            threshold=0.09)\n\n        assert len(kpi.keypoints) == 1\n        assert kpi.keypoints[0].x == 2\n        assert kpi.keypoints[0].y == 2\n        assert kpi.shape == (4, 5)\n\n    def test_from_distance_maps_bad_datatype_for_if_not_found(self):\n        distance_maps = self._get_distance_maps_for_from_dmap_tests()\n\n        with self.assertRaises(Exception) as context:\n            _ = ia.KeypointsOnImage.from_distance_maps(\n                distance_maps,\n                if_not_found_coords=False,\n                threshold=0.09)\n\n        assert \"Expected if_not_found_coords to be\" in str(context.exception)\n\n    def test_to_keypoints_on_image(self):\n        kps = ia.KeypointsOnImage([ia.Keypoint(0, 0), ia.Keypoint(1, 2)],\n                                  shape=(1, 2, 3))\n        kps.deepcopy = mock.MagicMock()\n        kps.deepcopy.return_value = \"foo\"\n\n        kps_cp = kps.to_keypoints_on_image()\n\n        assert kps.deepcopy.call_count == 1\n        assert kps_cp == \"foo\"\n\n    def test_invert_to_keypoints_on_image_(self):\n        kps1 = ia.KeypointsOnImage([ia.Keypoint(0, 0), ia.Keypoint(1, 2)],\n                                   shape=(2, 3, 4))\n        kps2 = ia.KeypointsOnImage([ia.Keypoint(10, 10), ia.Keypoint(11, 12)],\n                                   shape=(3, 4, 5))\n\n        kps3 = kps1.invert_to_keypoints_on_image_(kps2)\n\n        assert kps3 is not kps2\n        assert kps3.shape == (3, 4, 5)\n        assert kps3.keypoints[0].x == 10\n        assert kps3.keypoints[0].y == 10\n        assert kps3.keypoints[1].x == 11\n        assert kps3.keypoints[1].y == 12\n\n    def test_copy(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n\n        kpi2 = kpi.copy()\n\n        assert kpi2.keypoints[0].x == 1\n        assert kpi2.keypoints[0].y == 2\n        assert kpi2.keypoints[1].x == 3\n        assert kpi2.keypoints[1].y == 4\n\n        kps[0].x = 100\n\n        assert kpi2.keypoints[0].x == 100\n        assert kpi2.keypoints[0].y == 2\n        assert kpi2.keypoints[1].x == 3\n        assert kpi2.keypoints[1].y == 4\n\n    def test_copy_keypoints_set(self):\n        kp1 = ia.Keypoint(x=1, y=2)\n        kp2 = ia.Keypoint(x=3, y=4)\n        kp3 = ia.Keypoint(x=5, y=6)\n        kpsoi = ia.KeypointsOnImage([kp1, kp2], shape=(40, 50, 3))\n\n        kpsoi_copy = kpsoi.copy(keypoints=[kp3])\n\n        assert kpsoi_copy is not kpsoi\n        assert kpsoi_copy.shape == (40, 50, 3)\n        assert kpsoi_copy.keypoints == [kp3]\n\n    def test_copy_shape_set(self):\n        kp1 = ia.Keypoint(x=1, y=2)\n        kp2 = ia.Keypoint(x=3, y=4)\n        kpsoi = ia.KeypointsOnImage([kp1, kp2], shape=(40, 50, 3))\n\n        kpsoi_copy = kpsoi.copy(shape=(40+1, 50+1, 3))\n\n        assert kpsoi_copy is not kpsoi\n        assert kpsoi_copy.shape == (40+1, 50+1, 3)\n        assert kpsoi_copy.keypoints == [kp1, kp2]\n\n    def test_deepcopy(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n\n        kpi2 = kpi.deepcopy()\n\n        assert kpi2.keypoints[0].x == 1\n        assert kpi2.keypoints[0].y == 2\n        assert kpi2.keypoints[1].x == 3\n        assert kpi2.keypoints[1].y == 4\n\n        kps[0].x = 100\n\n        assert kpi2.keypoints[0].x == 1\n        assert kpi2.keypoints[0].y == 2\n        assert kpi2.keypoints[1].x == 3\n        assert kpi2.keypoints[1].y == 4\n\n    def test_deepcopy_keypoints_set(self):\n        kp1 = ia.Keypoint(x=1, y=2)\n        kp2 = ia.Keypoint(x=3, y=4)\n        kp3 = ia.Keypoint(x=5, y=6)\n        kpsoi = ia.KeypointsOnImage([kp1, kp2], shape=(40, 50, 3))\n\n        kpsoi_copy = kpsoi.deepcopy(keypoints=[kp3])\n\n        assert kpsoi_copy is not kpsoi\n        assert kpsoi_copy.shape == (40, 50, 3)\n        assert kpsoi_copy.keypoints == [kp3]\n\n    def test_deepcopy_shape_set(self):\n        kp1 = ia.Keypoint(x=1, y=2)\n        kp2 = ia.Keypoint(x=3, y=4)\n        kpsoi = ia.KeypointsOnImage([kp1, kp2], shape=(40, 50, 3))\n\n        kpsoi_copy = kpsoi.deepcopy(shape=(40+1, 50+1, 3))\n\n        assert kpsoi_copy is not kpsoi\n        assert kpsoi_copy.shape == (40+1, 50+1, 3)\n        assert len(kpsoi_copy.keypoints) == 2\n        assert kpsoi_copy.keypoints[0].coords_almost_equals(kp1)\n        assert kpsoi_copy.keypoints[1].coords_almost_equals(kp2)\n\n    def test___getitem__(self):\n        cbas = [\n            ia.Keypoint(x=1, y=2),\n            ia.Keypoint(x=2, y=3)\n        ]\n        cbasoi = ia.KeypointsOnImage(cbas, shape=(3, 4, 3))\n\n        assert cbasoi[0] is cbas[0]\n        assert cbasoi[1] is cbas[1]\n        assert cbasoi[0:2] == cbas\n\n    def test___iter__(self):\n        cbas = [ia.Keypoint(x=1, y=2),\n                ia.Keypoint(x=3, y=4)]\n        cbasoi = ia.KeypointsOnImage(cbas, shape=(40, 50, 3))\n\n        for i, cba in enumerate(cbasoi):\n            assert cba is cbas[i]\n\n    def test___iter___empty(self):\n        cbasoi = ia.KeypointsOnImage([], shape=(40, 50, 3))\n        i = 0\n        for _cba in cbasoi:\n            i += 1\n        assert i == 0\n\n    def test___len__(self):\n        cbas = [ia.Keypoint(x=1, y=2),\n                ia.Keypoint(x=3, y=4)]\n        cbasoi = ia.KeypointsOnImage(cbas, shape=(40, 50, 3))\n        assert len(cbasoi) == 2\n\n    def test_string_conversion(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        kpi = ia.KeypointsOnImage(keypoints=kps, shape=(5, 5, 3))\n        expected = (\n            \"KeypointsOnImage([\"\n            \"Keypoint(x=1.00000000, y=2.00000000), \"\n            \"Keypoint(x=3.00000000, y=4.00000000)\"\n            \"], shape=(5, 5, 3)\"\n            \")\"\n        )\n        assert (\n            kpi.__repr__()\n            == kpi.__str__()\n            == expected\n        )\n"
  },
  {
    "path": "test/augmentables/test_lines.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\n\nimport imgaug as ia\nfrom imgaug.testutils import reseed, wrap_shift_deprecation, assertWarns\nfrom imgaug.augmentables.lines import LineString, LineStringsOnImage\nfrom imgaug.augmentables.kps import Keypoint\nfrom imgaug.augmentables.heatmaps import HeatmapsOnImage\n\n\nclass TestLineString_project_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, ls, from_shape, to_shape):\n        return ls.project_(from_shape, to_shape)\n\n    def test_project_to_2x_image_size(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        ls_proj = self._func(ls, (10, 10), (20, 20))\n        assert np.allclose(ls_proj.coords, [(0, 0), (2, 0), (4, 2)])\n\n    def test_project_to_2x_image_width(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        ls_proj = self._func(ls, (10, 10), (10, 20))\n        assert np.allclose(ls_proj.coords, [(0, 0), (2, 0), (4, 1)])\n\n    def test_project_to_2x_image_height(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        ls_proj = self._func(ls, (10, 10), (20, 10))\n        assert np.allclose(ls_proj.coords, [(0, 0), (1, 0), (2, 2)])\n\n    def test_inplaceness(self):\n        ls = ia.LineString([(0, 0), (1, 0)])\n        ls2 = self._func(ls, (10, 10), (10, 10))\n        if self._is_inplace:\n            assert ls is ls2\n        else:\n            assert ls is not ls2\n\n\nclass TestLineString_project(TestLineString_project_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, ls, from_shape, to_shape):\n        return ls.project(from_shape, to_shape)\n\n\nclass TestLineString_shift_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, ls, *args, **kwargs):\n        def _func_impl():\n            return ls.shift_(*args, **kwargs)\n\n        return wrap_shift_deprecation(_func_impl, *args, **kwargs)\n\n    def test_shift_along_xy(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        ls_shift = self._func(ls.deepcopy(), x=1, y=2)\n        assert ls_shift.coords_almost_equals(\n            [(0+1, 0+2), (1+1, 0+2), (2+1, 1+2)])\n\n    def test_inplaceness(self):\n        ls = ia.LineString([(0, 0), (1, 0)])\n        ls2 = self._func(ls, y=0)\n        if self._is_inplace:\n            assert ls is ls2\n        else:\n            assert ls is not ls2\n\n\nclass TestLineString_shift(TestLineString_shift_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, ls, *args, **kwargs):\n        def _func_impl():\n            return ls.shift(*args, **kwargs)\n\n        return wrap_shift_deprecation(_func_impl, *args, **kwargs)\n\n    def test_shift_by_positive_args(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        assert self._func(ls.deepcopy(), top=1).coords_almost_equals(\n            [(0, 1), (1, 1), (2, 2)])\n        assert self._func(ls.deepcopy(), right=1).coords_almost_equals(\n            [(-1, 0), (0, 0), (1, 1)])\n        assert self._func(ls.deepcopy(), bottom=1).coords_almost_equals(\n            [(0, -1), (1, -1), (2, 0)])\n        assert self._func(ls.deepcopy(), left=1).coords_almost_equals(\n            [(1, 0), (2, 0), (3, 1)])\n\n    def test_shift_by_negative_values(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        assert self._func(ls.deepcopy(), top=-1).coords_almost_equals(\n            [(0, -1), (1, -1), (2, 0)])\n        assert self._func(ls.deepcopy(), right=-1).coords_almost_equals(\n            [(1, 0), (2, 0), (3, 1)])\n        assert self._func(ls.deepcopy(), bottom=-1).coords_almost_equals(\n            [(0, 1), (1, 1), (2, 2)])\n        assert self._func(ls.deepcopy(), left=-1).coords_almost_equals(\n            [(-1, 0), (0, 0), (1, 1)])\n\n    def test_shift_by_positive_values_all_arguments_provided(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        assert self._func(\n            ls.deepcopy(), top=1, right=2, bottom=3, left=4\n        ).coords_almost_equals(\n            [(0-2+4, 0+1-3), (1-2+4, 0+1-3), (2-2+4, 1+1-3)])\n\n    def test_shift_of_empty_line_string(self):\n        ls = LineString([])\n        assert self._func(\n            ls.deepcopy(), top=1, right=2, bottom=3, left=4\n        ).coords_almost_equals([])\n\n\nclass TestLineString(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___float32_array_as_coords(self):\n        ls = LineString(np.float32([[0, 0], [1, 2]]))\n        assert np.allclose(ls.coords, np.float32([[0, 0], [1, 2]]))\n        assert ls.label is None\n\n    def test___init___list_of_tuples_as_coords(self):\n        ls = LineString([(0, 0), (1, 2)])\n        assert np.allclose(ls.coords, np.float32([[0, 0], [1, 2]]))\n        assert ls.label is None\n\n    def test___init___empty_list_as_coords(self):\n        ls = LineString([])\n        assert ls.coords.shape == (0, 2)\n        assert ls.label is None\n\n    def test___init___label_set(self):\n        ls = LineString([], label=\"test\")\n        assert ls.coords.shape == (0, 2)\n        assert ls.label == \"test\"\n\n    def test_length_with_triangle(self):\n        ls = LineString(np.float32([[0, 0], [1, 0], [1, 1]]))\n        assert np.isclose(ls.length, 2.0)\n\n    def test_length_with_realworld_line_string(self):\n        ls = LineString(np.float32([[0, 0], [1, 2], [4, 5]]))\n        assert np.isclose(ls.length,\n                          np.sqrt(1**2 + 2**2) + np.sqrt(3**2 + 3**2))\n\n    def test_length_with_single_point(self):\n        ls = LineString([(0, 0)])\n        assert np.isclose(ls.length, 0.0)\n\n    def test_length_with_zero_points(self):\n        ls = LineString([])\n        assert np.isclose(ls.length, 0.0)\n\n    def test_xx_three_points(self):\n        ls = LineString(np.float32([[0, 0], [1, 0], [2, 1]]))\n        assert np.allclose(ls.xx, np.float32([0, 1, 2]))\n\n    def test_xx_no_points(self):\n        ls = LineString([])\n        assert np.allclose(ls.xx, np.zeros((0,), dtype=np.float32))\n\n    def test_yy_three_points(self):\n        ls = LineString(np.float32([[0, 0], [0, 1], [0, 2]]))\n        assert np.allclose(ls.yy, np.float32([0, 1, 2]))\n\n    def test_yy_no_points(self):\n        ls = LineString([])\n        assert np.allclose(ls.yy, np.zeros((0,), dtype=np.float32))\n\n    def test_xx_int_three_points(self):\n        ls = LineString(np.float32([[0, 0], [1.4, 0], [2.6, 1]]))\n        assert ls.xx_int.dtype.name == \"int32\"\n        assert np.array_equal(ls.xx_int, np.int32([0, 1, 3]))\n\n    def test_xx_int_no_points(self):\n        ls = LineString([])\n        assert ls.xx_int.dtype.name == \"int32\"\n        assert np.array_equal(ls.xx_int, np.zeros((0,), dtype=np.int32))\n\n    def test_yy_int_three_points(self):\n        ls = LineString(np.float32([[0, 0], [0, 1.4], [1, 2.6]]))\n        assert ls.yy_int.dtype.name == \"int32\"\n        assert np.array_equal(ls.yy_int, np.int32([0, 1, 3]))\n\n    def test_yy_int_no_points(self):\n        ls = LineString([])\n        assert ls.yy_int.dtype.name == \"int32\"\n        assert np.array_equal(ls.yy_int, np.zeros((0,), dtype=np.int32))\n\n    def test_height_three_points(self):\n        ls = LineString(np.float32([[0, 0], [0, 1.4], [1, 2.6]]))\n        assert np.isclose(ls.height, 2.6)\n\n    def test_height_no_points(self):\n        ls = LineString([])\n        assert np.isclose(ls.height, 0.0)\n\n    def test_width_three_points(self):\n        ls = LineString(np.float32([[0, 0], [1.4, 0], [2.6, 1]]))\n        assert np.isclose(ls.width, 2.6)\n\n    def test_width_no_points(self):\n        ls = LineString([])\n        assert np.isclose(ls.width, 0.0)\n\n    def test_get_pointwise_inside_image_mask_with_single_point_tuple(self):\n        ls = LineString([(0, 0), (1.4, 0), (2.6, 1)])\n        mask = ls.get_pointwise_inside_image_mask((2, 2))\n        assert np.array_equal(mask, [True, True, False])\n\n    def test_get_pointwise_inside_image_mask_with_array(self):\n        ls = LineString([(0, 0), (1.4, 0), (2.6, 1)])\n        mask = ls.get_pointwise_inside_image_mask(\n            np.zeros((2, 2), dtype=np.uint8))\n        assert np.array_equal(mask, [True, True, False])\n\n    def test_get_pointwise_inside_image_mask_with_single_point_ls(self):\n        ls = LineString([(0, 0)])\n        mask = ls.get_pointwise_inside_image_mask((2, 2))\n        assert np.array_equal(mask, [True])\n\n    def test_get_pointwise_inside_image_mask_with_zero_points_ls(self):\n        ls = LineString([])\n        mask = ls.get_pointwise_inside_image_mask((2, 2))\n        assert mask.shape == (0,)\n\n    def test_compute_neighbour_distances_three_points(self):\n        ls = LineString([(0, 0), (1.4, 0), (2.6, 1)])\n        dists = ls.compute_neighbour_distances()\n        assert np.allclose(dists, [np.sqrt(1.4**2), np.sqrt(1.2**2+1**2)])\n\n    def test_compute_neighbour_distances_two_points(self):\n        ls = LineString([(0, 0), (1.4, 0)])\n        dists = ls.compute_neighbour_distances()\n        assert np.allclose(dists, [np.sqrt(1.4**2)])\n\n    def test_compute_neighbour_distances_single_point(self):\n        ls = LineString([(0, 0)])\n        dists = ls.compute_neighbour_distances()\n        assert dists.shape == (0,)\n\n    def test_compute_neighbour_distances_zero_points(self):\n        ls = LineString([])\n        dists = ls.compute_neighbour_distances()\n        assert dists.shape == (0,)\n\n    def test_compute_pointwise_distances_to_point_at_origin(self):\n        ls = LineString([(0, 0), (5, 0), (5, 5)])\n        dists = ls.compute_pointwise_distances((0, 0))\n        assert np.allclose(dists, [0,\n                                   5,\n                                   np.sqrt(5**2 + 5**2)])\n\n    def test_compute_pointwise_distances_to_point_at_x1_y1(self):\n        ls = LineString([(0, 0), (5, 0), (5, 5)])\n        dists = ls.compute_pointwise_distances((1, 1))\n        assert np.allclose(dists, [np.sqrt(1**2 + 1**2),\n                                   np.sqrt(4**2 + 1**2),\n                                   np.sqrt(4**2 + 4**2)])\n\n    def test_compute_pointwise_distances_from_empty_line_string(self):\n        ls = LineString([])\n        dists = ls.compute_pointwise_distances((1, 1))\n        assert dists == []\n\n    def test_compute_pointwise_distances_to_keypoint_at_origin(self):\n        ls = LineString([(0, 0), (5, 0), (5, 5)])\n        dists = ls.compute_pointwise_distances(Keypoint(x=0, y=0))\n        assert np.allclose(dists, [0, 5, np.sqrt(5**2 + 5**2)])\n\n    def test_compute_pointwise_distances_to_keypoint_at_x1_y1(self):\n        ls = LineString([(0, 0), (5, 0), (5, 5)])\n        dists = ls.compute_pointwise_distances(Keypoint(x=1, y=1))\n        assert np.allclose(dists, [np.sqrt(1**2 + 1**2),\n                                   np.sqrt(4**2 + 1**2),\n                                   np.sqrt(4**2 + 4**2)])\n\n    def test_compute_pointwise_distances_to_other_line_string_at_origin(self):\n        # line string\n        ls = LineString([(0, 0), (5, 0), (5, 5)])\n        other = LineString([(0, 0)])\n        dists = ls.compute_pointwise_distances(other)\n        assert np.allclose(dists, [0,\n                                   5,\n                                   np.sqrt(5**2 + 5**2)])\n\n    def test_compute_pointwise_distances_to_other_line_string_at_x1_y1(self):\n        ls = LineString([(0, 0), (5, 0), (5, 5)])\n        other = LineString([(1, 1)])\n        dists = ls.compute_pointwise_distances(other)\n        assert np.allclose(dists, [np.sqrt(1**2 + 1**2),\n                                   np.sqrt(4**2 + 1**2),\n                                   np.sqrt(4**2 + 4**2)])\n\n    def test_compute_pointwise_distances_to_other_line_string_two_points(self):\n        ls = LineString([(0, 0), (5, 0), (5, 5)])\n        other = LineString([(0, -1), (5, -1)])\n        dists = ls.compute_pointwise_distances(other)\n        assert np.allclose(dists, [np.sqrt(0**2 + 1**2),\n                                   np.sqrt(0**2 + 1**2),\n                                   np.sqrt(0**2 + 6**2)])\n\n    def test_compute_pointwise_distances_to_other_empty_line_string(self):\n        ls = LineString([(0, 0), (5, 0), (5, 5)])\n        other = LineString([])\n        dists = ls.compute_pointwise_distances(other, default=False)\n        assert dists is False\n\n    def test_compute_distance_from_three_point_line_string(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        points = [(0, 0), (1, 0), (0, 1), (-0.5, -0.6)]\n        expecteds = [0, 0, 1, np.sqrt(0.5**2 + 0.6**2)]\n\n        for point, expected in zip(points, expecteds):\n            with self.subTest(point=point):\n                assert np.isclose(ls.compute_distance(point), expected)\n\n    def test_compute_distance_from_single_point_line_string(self):\n        ls = LineString([(0, 0)])\n        points = [(0, 0), (-0.5, -0.6)]\n        expecteds = [0, np.sqrt(0.5**2 + 0.6**2)]\n\n        for point, expected in zip(points, expecteds):\n            with self.subTest(point=point):\n                assert np.isclose(ls.compute_distance(point), expected)\n\n    def test_compute_distance_from_empty_line_string_no_default(self):\n        ls = LineString([])\n        assert ls.compute_distance((0, 0)) is None\n\n    def test_compute_distance_from_empty_line_string_with_default(self):\n        ls = LineString([])\n        assert ls.compute_distance((0, 0), default=-1) == -1\n\n    def test_compute_distance_to_keypoint(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        assert np.isclose(ls.compute_distance(ia.Keypoint(x=0, y=1)), 1)\n\n    def test_compute_distance_to_line_string(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        points = [\n            [(0, 0)],\n            [(0, 1)],\n            [(0, 0), (0, 1)],\n            [(-1, -1), (-1, 1)]\n        ]\n        expecteds = [0, 1, 0, 1]\n\n        for point, expected in zip(points, expecteds):\n            with self.subTest(point=point):\n                assert np.isclose(ls.compute_distance(LineString(point)),\n                                  expected)\n\n    def test_compute_distance_to_invalid_datatype(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        with self.assertRaises(ValueError):\n            assert ls.compute_distance(\"foo\")\n\n    def test_contains_tuple(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        points = [(100, 0), (0, 0), (1, 0), (2, 1), (0+1e-8, 0), (0-1, 0)]\n        expecteds = [False, True, True, True, True, False]\n\n        for point, expected in zip(points, expecteds):\n            with self.subTest(point=point):\n                assert ls.contains(point) is expected\n\n    def test_contains_tuple_max_distance(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        points = [(0+1e-8, 0), (0-1, 0)]\n        max_distances = [0, 2]\n        expecteds = [False, True]\n\n        for point, max_distance, expected in zip(points, max_distances,\n                                                 expecteds):\n            with self.subTest(point=point, max_distance=max_distance):\n                assert (\n                    ls.contains(point, max_distance=max_distance)\n                    is expected\n                )\n\n    def test_contains_keypoint(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        points = [(100, 0), (0, 0), (1, 0), (2, 1), (0+1e-8, 0), (0-1, 0)]\n        expecteds = [False, True, True, True, True, False]\n\n        for point, expected in zip(points, expecteds):\n            with self.subTest(point=point):\n                assert (\n                    ls.contains(Keypoint(x=point[0], y=point[1]))\n                    is expected\n                )\n\n    def test_contains_with_single_point_line_string(self):\n        ls = LineString([(0, 0)])\n        assert ls.contains((0, 0))\n        assert not ls.contains((1, 0))\n\n    def test_contains_with_empty_line_string(self):\n        ls = LineString([])\n        assert not ls.contains((0, 0))\n        assert not ls.contains((1, 0))\n\n    def test_project_empty_line_string_to_2x_image_size(self):\n        ls = LineString([])\n        ls_proj = ls.project((10, 10), (20, 20))\n        assert ls_proj.coords.shape == (0, 2)\n\n    def test_compute_out_of_image_fraction__no_points(self):\n        ls = LineString([])\n        image_shape = (100, 200, 3)\n        factor = ls.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.0)\n\n    def test_compute_out_of_image_fraction__one_point(self):\n        ls = LineString([(1.0, 2.0)])\n        image_shape = (100, 200, 3)\n        factor = ls.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.0)\n\n    def test_compute_out_of_image_fraction__one_point__ooi(self):\n        ls = LineString([(-10.0, -20.0)])\n        image_shape = (100, 200, 3)\n        factor = ls.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 1.0)\n\n    def test_compute_out_of_image_fraction__two_points(self):\n        ls = LineString([(1.0, 2.0), (10.0, 20.0)])\n        image_shape = (100, 200, 3)\n        factor = ls.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.0)\n\n    def test_compute_out_of_image_fraction__three_points_at_same_pos(self):\n        ls = LineString([(10.0, 20.0), (10.0, 20.0), (10.0, 20.0)])\n        image_shape = (100, 200, 3)\n        factor = ls.compute_out_of_image_fraction(image_shape)\n        assert len(ls.coords) == 3\n        assert np.isclose(factor, 0.0)\n\n    def test_compute_out_of_image_fraction__partially_ooi(self):\n        ls = LineString([(9.0, 1.0), (11.0, 1.0)])\n        image_shape = (10, 10, 3)\n        factor = ls.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.5, atol=1e-3)\n\n    def test_compute_out_of_image_fraction__leaves_image_multiple_times(self):\n        ls = LineString([(9.0, 1.0), (11.0, 1.0), (11.0, 3.0),\n                         (9.0, 3.0), (9.0, 5.0), (11.0, 5.0)])\n        image_shape = (10, 10, 3)\n        factor = ls.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.5, atol=1e-3)\n\n    def test_compute_out_of_image_fraction__fully_ooi(self):\n        ls = LineString([(15.0, 15.0), (20.0, 15.0)])\n        image_shape = (10, 10, 3)\n        factor = ls.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 1.0)\n\n    def test_is_fully_within_image_with_simple_line_string(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        assert ls.is_fully_within_image((10, 10))\n        assert ls.is_fully_within_image((2, 3))\n        assert not ls.is_fully_within_image((2, 2))\n        assert not ls.is_fully_within_image((1, 1))\n\n    def test_is_fully_within_image_with_negative_coords_line_string(self):\n        ls = LineString([(-1, 0), (1, 0), (2, 1)])\n        assert not ls.is_fully_within_image((10, 10))\n        assert not ls.is_fully_within_image((2, 3))\n        assert not ls.is_fully_within_image((2, 2))\n        assert not ls.is_fully_within_image((1, 1))\n\n    def test_is_fully_within_image_with_single_point_line_string(self):\n        ls = LineString([(0, 0)])\n        assert ls.is_fully_within_image((10, 10))\n        assert ls.is_fully_within_image((2, 3))\n        assert ls.is_fully_within_image((2, 2))\n        assert ls.is_fully_within_image((1, 1))\n\n    def test_is_fully_within_image_with_empty_line_string(self):\n        ls = LineString([])\n        assert not ls.is_fully_within_image((10, 10))\n        assert not ls.is_fully_within_image((2, 3))\n        assert not ls.is_fully_within_image((2, 2))\n        assert not ls.is_fully_within_image((1, 1))\n\n    def test_is_fully_within_image_with_empty_line_string_default_set(self):\n        ls = LineString([])\n        assert ls.is_fully_within_image((10, 10), default=True)\n        assert ls.is_fully_within_image((2, 3), default=True)\n        assert ls.is_fully_within_image((2, 2), default=True)\n        assert ls.is_fully_within_image((1, 1), default=True)\n        assert ls.is_fully_within_image((10, 10), default=None) is None\n\n    def test_is_partly_within_image_with_simple_line_string(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        assert ls.is_partly_within_image((10, 10))\n        assert ls.is_partly_within_image((2, 3))\n        assert ls.is_partly_within_image((2, 2))\n        assert ls.is_partly_within_image((1, 1))\n\n    def test_is_partly_within_image_with_simple_line_string2(self):\n        ls = LineString([(1, 0), (2, 0), (3, 1)])\n        assert ls.is_partly_within_image((10, 10))\n        assert ls.is_partly_within_image((2, 3))\n        assert ls.is_partly_within_image((2, 2))\n        assert not ls.is_partly_within_image((1, 1))\n\n    def test_is_partly_within_image_with_ls_cutting_through_image(self):\n        # line string that cuts through the middle of the image,\n        # with both points outside of a BB (0, 0), (10, 10)\n        ls = LineString([(-1, 5), (11, 5)])\n        assert ls.is_partly_within_image((100, 100))\n        assert ls.is_partly_within_image((10, 12))\n        assert ls.is_partly_within_image((10, 10))\n        assert ls.is_partly_within_image((10, 1))\n        assert not ls.is_partly_within_image((1, 1))\n\n    def test_is_partly_within_image_with_line_string_around_rectangle(self):\n        # line string around inner rectangle of (-1, -1), (11, 11)\n        ls = LineString([(-1, -1), (11, -1), (11, 11), (-1, 11)])\n        assert ls.is_partly_within_image((100, 100))\n        assert ls.is_partly_within_image((12, 12))\n        assert not ls.is_partly_within_image((10, 10))\n\n    def test_is_partly_within_image_with_single_point_line_string(self):\n        ls = LineString([(11, 11)])\n        assert ls.is_partly_within_image((100, 100))\n        assert ls.is_partly_within_image((12, 12))\n        assert not ls.is_partly_within_image((10, 10))\n\n    def test_is_partly_within_image_with_empty_line_string(self):\n        ls = LineString([])\n        assert not ls.is_partly_within_image((100, 100))\n        assert not ls.is_partly_within_image((10, 10))\n        assert ls.is_partly_within_image((100, 100), default=True)\n        assert ls.is_partly_within_image((10, 10), default=True)\n        assert ls.is_partly_within_image((100, 100), default=None) is None\n        assert ls.is_partly_within_image((10, 10), default=None) is None\n\n    def test_is_out_of_image_with_simple_line_string(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        assert not ls.is_out_of_image((10, 10))\n        assert ls.is_out_of_image((1, 1), fully=False, partly=True)\n        assert not ls.is_out_of_image((1, 1), fully=True, partly=False)\n        assert ls.is_out_of_image((1, 1), fully=True, partly=True)\n        assert not ls.is_out_of_image((1, 1), fully=False, partly=False)\n\n    def test_is_out_of_image_with_empty_line_string(self):\n        ls = LineString([])\n        assert ls.is_out_of_image((10, 10))\n        assert not ls.is_out_of_image((10, 10), default=False)\n        assert ls.is_out_of_image((10, 10), default=None) is None\n\n    def test_clip_out_of_image_with_simple_line_string(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], ls)\n\n        lss_clipped = ls.clip_out_of_image((2, 2))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], ls)\n\n        lss_clipped = ls.clip_out_of_image((2, 1))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], [(0, 0), (1, 0)])\n\n    def test_clip_out_of_image_with_shifted_simple_line_string(self):\n        # same as above, all coords at x+5, y+5\n        ls = LineString([(5, 5), (6, 5), (7, 6)])\n\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], ls)\n\n        lss_clipped = ls.clip_out_of_image((4, 4))\n        assert len(lss_clipped) == 0\n\n    def test_clip_out_of_image_with_ls_partially_outside_image(self):\n        # line that leaves image plane and comes back\n        ls = LineString([(0, 0), (1, 0), (3, 0),\n                         (3, 2), (1, 2), (0, 2)])\n\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], ls)\n\n        lss_clipped = ls.clip_out_of_image((10, 2))\n        assert len(lss_clipped) == 2\n        assert _coords_eq(lss_clipped[0], [(0, 0), (1, 0), (2, 0)])\n        assert _coords_eq(lss_clipped[1], [(2, 2), (1, 2), (0, 2)])\n\n        lss_clipped = ls.clip_out_of_image((10, 1))\n        assert len(lss_clipped) == 2\n        assert _coords_eq(lss_clipped[0], [(0, 0), (1, 0)])\n        assert _coords_eq(lss_clipped[1], [(1, 2), (0, 2)])\n\n    def test_clip_out_of_image_with_ls_partially_ooi_less_points(self):\n        # same as above, but removing first and last point\n        # so that only one point before and after out of image part remain\n        ls = LineString([(1, 0), (3, 0),\n                         (3, 2), (1, 2)])\n\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], ls)\n\n        lss_clipped = ls.clip_out_of_image((10, 2))\n        assert len(lss_clipped) == 2\n        assert _coords_eq(lss_clipped[0], [(1, 0), (2, 0)])\n        assert _coords_eq(lss_clipped[1], [(2, 2), (1, 2)])\n\n        lss_clipped = ls.clip_out_of_image((10, 1))\n        assert len(lss_clipped) == 0\n\n    def test_clip_out_of_image_when_only_one_point_remains(self):\n        # same as above, but only one point out of image remains\n        ls = LineString([(1, 0), (3, 0),\n                         (1, 2)])\n\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], ls)\n\n        lss_clipped = ls.clip_out_of_image((10, 2))\n        assert len(lss_clipped) == 2\n        assert _coords_eq(lss_clipped[0], [(1, 0), (2, 0)])\n        assert _coords_eq(lss_clipped[1], [(2, 1), (1, 2)])\n\n        lss_clipped = ls.clip_out_of_image((10, 1))\n        assert len(lss_clipped) == 0\n\n    def test_clip_out_of_image_with_ls_leaving_image_multiple_times(self):\n        # line string that leaves image, comes back, then leaves again, then\n        # comes back again\n        ls = LineString([(1, 0), (3, 0),  # leaves\n                         (3, 1), (1, 1),  # comes back\n                         (1, 2), (3, 2),  # leaves\n                         (3, 3), (1, 3)])  # comes back\n\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], ls)\n\n        lss_clipped = ls.clip_out_of_image((10, 2))\n        assert len(lss_clipped) == 3  # from above: 1s line, 2nd+3rd, 4th\n        assert _coords_eq(lss_clipped[0], [(1, 0), (2, 0)])\n        assert _coords_eq(lss_clipped[1], [(2, 1), (1, 1), (1, 2), (2, 2)])\n        assert _coords_eq(lss_clipped[2], [(2, 3), (1, 3)])\n\n    def test_clip_out_of_image_with_ls_that_enters_image_from_outside(self):\n        # line string that starts out of image and ends within the image plane\n        for y in [1, 0]:\n            with self.subTest(y=y):\n                # one point inside image\n                ls = LineString([(-10, y), (3, y)])\n\n                lss_clipped = ls.clip_out_of_image((10, 10))\n                assert len(lss_clipped) == 1\n                assert _coords_eq(lss_clipped[0], [(0, y), (3, y)])\n\n                lss_clipped = ls.clip_out_of_image((2, 1))\n                assert len(lss_clipped) == 1\n                assert _coords_eq(lss_clipped[0], [(0, y), (1, y)])\n\n                lss_clipped = ls.clip_out_of_image((1, 1))\n                if y == 1:\n                    assert len(lss_clipped) == 0\n                else:\n                    assert len(lss_clipped) == 1\n                    assert _coords_eq(lss_clipped[0], [(0, y), (1, y)])\n\n                # two points inside image\n                ls = LineString([(-10, y), (3, y), (5, y)])\n\n                lss_clipped = ls.clip_out_of_image((10, 10))\n                assert len(lss_clipped) == 1\n                assert _coords_eq(lss_clipped[0], [(0, y), (3, y), (5, y)])\n\n                lss_clipped = ls.clip_out_of_image((10, 4))\n                assert len(lss_clipped) == 1\n                assert _coords_eq(lss_clipped[0], [(0, y), (3, y), (4, y)])\n\n                lss_clipped = ls.clip_out_of_image((2, 1))\n                assert len(lss_clipped) == 1\n                assert _coords_eq(lss_clipped[0], [(0, y), (1, y)])\n\n                lss_clipped = ls.clip_out_of_image((1, 1))\n                if y == 1:\n                    assert len(lss_clipped) == 0\n                else:\n                    assert len(lss_clipped) == 1\n                    assert _coords_eq(lss_clipped[0], [(0, y), (1, y)])\n\n    def test_clip_out_of_image_with_ls_that_leaves_image_from_inside(self):\n        # line string that starts within the image plane and ends outside\n        for y in [1, 0]:\n            with self.subTest(y=y):\n                # one point inside image\n                ls = LineString([(2, y), (5, y)])\n\n                lss_clipped = ls.clip_out_of_image((10, 10))\n                assert len(lss_clipped) == 1\n                assert _coords_eq(lss_clipped[0], [(2, y), (5, y)])\n\n                lss_clipped = ls.clip_out_of_image((10, 4))\n                assert _coords_eq(lss_clipped[0], [(2, y), (4, y)])\n\n                # two points inside image\n                ls = LineString([(1, y), (2, y), (5, y)])\n\n                lss_clipped = ls.clip_out_of_image((10, 10))\n                assert len(lss_clipped) == 1\n                assert _coords_eq(lss_clipped[0], [(1, y), (2, y), (5, y)])\n\n                lss_clipped = ls.clip_out_of_image((10, 4))\n                assert len(lss_clipped) == 1\n                assert _coords_eq(lss_clipped[0], [(1, y), (2, y), (4, y)])\n\n                lss_clipped = ls.clip_out_of_image((2, 1))\n                assert len(lss_clipped) == 0\n\n                # two points outside image\n                ls = LineString([(2, y), (5, y), (6, y)])\n\n                lss_clipped = ls.clip_out_of_image((10, 10))\n                assert len(lss_clipped) == 1\n                assert _coords_eq(lss_clipped[0], [(2, y), (5, y), (6, y)])\n\n                lss_clipped = ls.clip_out_of_image((10, 4))\n                assert len(lss_clipped) == 1\n                assert _coords_eq(lss_clipped[0], [(2, y), (4, y)])\n\n                lss_clipped = ls.clip_out_of_image((2, 1))\n                assert len(lss_clipped) == 0\n\n    def test_clip_out_of_image_with_ls_that_cuts_through_image(self):\n        # line string that cuts through the image plane in the center\n        for y in [1, 0]:\n            ls = LineString([(-5, y), (5, y)])\n\n            lss_clipped = ls.clip_out_of_image((10, 10))\n            assert len(lss_clipped) == 1\n            assert _coords_eq(lss_clipped[0], [(0, y), (5, y)])\n\n            lss_clipped = ls.clip_out_of_image((4, 4))\n            assert len(lss_clipped) == 1\n            assert _coords_eq(lss_clipped[0], [(0, y), (4, y)])\n\n    def test_clip_out_of_image_with_ls_that_runs_through_diagonal_corners(self):\n        # line string that cuts through the image plane from the bottom left\n        # corner to the top right corner\n        ls = LineString([(-5, -5), (5, 5)])\n\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], [(0, 0), (5, 5)])\n\n        lss_clipped = ls.clip_out_of_image((4, 4))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], [(0, 0), (4, 4)])\n\n    def test_clip_out_of_image_with_ls_that_overlaps_with_image_edge(self):\n        # line string that overlaps with the bottom edge\n        ls = LineString([(1, 0), (4, 0)])\n\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], ls)\n\n        lss_clipped = ls.clip_out_of_image((3, 3))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], [(1, 0), (3, 0)])\n\n    def test_clip_out_of_image_with_ls_that_overlaps_with_image_edge2(self):\n        # same as above, multiple points on line\n        ls = LineString([(1, 0), (4, 0), (5, 0), (6, 0), (7, 0)])\n\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], ls)\n\n        lss_clipped = ls.clip_out_of_image((5, 5))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], [(1, 0), (4, 0), (5, 0)])\n\n        lss_clipped = ls.clip_out_of_image((5, 4))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], [(1, 0), (4, 0)])\n\n        lss_clipped = ls.clip_out_of_image((5, 2))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], [(1, 0), (2, 0)])\n\n    def test_clip_out_of_image_with_ls_that_overlaps_with_image_edge3(self):\n        # line string that starts outside the image, intersects with the\n        # bottom left corner and overlaps with the bottom border\n        ls = LineString([(-5, 0), (5, 0)])\n\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], [(0, 0), (5, 0)])\n\n        lss_clipped = ls.clip_out_of_image((10, 5))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], [(0, 0), (5, 0)])\n\n        lss_clipped = ls.clip_out_of_image((10, 4))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], [(0, 0), (4, 0)])\n\n    def test_clip_out_of_image_with_ls_that_contains_a_single_point(self):\n        # line string that contains a single point\n        ls = LineString([(2, 2)])\n\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 1\n        assert _coords_eq(lss_clipped[0], ls)\n\n        lss_clipped = ls.clip_out_of_image((1, 1))\n        assert len(lss_clipped) == 0\n\n    def test_clip_out_of_image_with_ls_that_is_empty(self):\n        # line string that is empty\n        ls = LineString([])\n        lss_clipped = ls.clip_out_of_image((10, 10))\n        assert len(lss_clipped) == 0\n\n    def test_clip_out_of_image_and_is_fully_within_image(self):\n        # combine clip + is_fully_within_image\n        sizes = [(200, 400), (400, 800), (800, 1600), (1600, 3200),\n                 (3200, 6400)]\n        sizes = sizes + [(w, h) for h, w in sizes]\n        for h, w in sizes:\n            ls = LineString([(0, 10), (w, 10), (w, h), (w-10, h-10)])\n            lss_clipped = ls.clip_out_of_image((h, w))\n            assert len(lss_clipped) == 2\n            assert lss_clipped[0].is_fully_within_image((h, w))\n            assert lss_clipped[1].is_fully_within_image((h, w))\n\n            ls = LineString([(0, 10), (w+10, 10), (w+10, h-10), (w-10, h-10)])\n            lss_clipped = ls.clip_out_of_image((h, w))\n            assert len(lss_clipped) == 2\n            assert lss_clipped[0].is_fully_within_image((h, w))\n            assert lss_clipped[1].is_fully_within_image((h, w))\n\n            ls = LineString([(-10, 10), (w+10, 10), (w-10, h-10)])\n            lss_clipped = ls.clip_out_of_image((h, w))\n            assert len(lss_clipped) == 2\n            assert lss_clipped[0].is_fully_within_image((h, w))\n            assert lss_clipped[1].is_fully_within_image((h, w))\n\n    def test_draw_mask(self):\n        ls = LineString([(0, 1), (5, 1), (5, 5)])\n\n        arr = ls.draw_mask(\n            (10, 10), size_lines=1, size_points=0, raise_if_out_of_image=False)\n\n        assert np.all(arr[1, 0:5])\n        assert np.all(arr[1:5, 5])\n        assert not np.any(arr[0, :])\n        assert not np.any(arr[2:, 0:5])\n\n    def test_draw_mask_of_empty_line_string(self):\n        ls = LineString([])\n\n        arr = ls.draw_mask(\n            (10, 10), size_lines=1, raise_if_out_of_image=False)\n\n        assert not np.any(arr)\n\n    def test_draw_line_heatmap_array(self):\n        ls = LineString([(0, 1), (5, 1), (5, 5)])\n\n        arr = ls.draw_lines_heatmap_array(\n            (10, 10), alpha=0.5, size=1, raise_if_out_of_image=False)\n\n        assert _drawing_allclose(arr[1, 0:5], 0.5)\n        assert _drawing_allclose(arr[1:5, 5], 0.5)\n        assert _drawing_allclose(arr[0, :], 0.0)\n        assert _drawing_allclose(arr[2:, 0:5], 0.0)\n\n    def test_draw_line_heatmap_array_with_empty_line_string(self):\n        ls = LineString([])\n\n        arr = ls.draw_lines_heatmap_array(\n            (10, 10), alpha=0.5, size=1, raise_if_out_of_image=False)\n\n        assert _drawing_allclose(arr, 0.0)\n\n    def test_draw_points_heatmap_array(self):\n        ls = LineString([(0, 1), (5, 1), (5, 5)])\n\n        arr = ls.draw_points_heatmap_array(\n            (10, 10), alpha=0.5, size=1, raise_if_out_of_image=False)\n\n        assert _drawing_allclose(arr[1, 0], 0.5)\n        assert _drawing_allclose(arr[1, 5], 0.5)\n        assert _drawing_allclose(arr[5, 5], 0.5)\n        assert _drawing_allclose(arr[0, :], 0.0)\n        assert _drawing_allclose(arr[2:, 0:5], 0.0)\n\n    def test_draw_points_heatmap_array_with_empty_line_string(self):\n        ls = LineString([])\n\n        arr = ls.draw_points_heatmap_array(\n            (10, 10), alpha=0.5, size=1, raise_if_out_of_image=False)\n\n        assert _drawing_allclose(arr, 0.0)\n\n    def test_draw_heatmap_array_calls_other_drawing_functions(self):\n        ls = LineString([(0, 1), (9, 1)])\n\n        module_name = \"imgaug.augmentables.lines.\"\n        line_fname = \"%sLineString.draw_lines_heatmap_array\" % (module_name,)\n        points_fname = \"%sLineString.draw_points_heatmap_array\" % (module_name,)\n        with mock.patch(line_fname, return_value=1) as mock_line, \\\n                mock.patch(points_fname, return_value=2) as mock_points:\n            _arr = ls.draw_heatmap_array(\n                (10, 10),\n                alpha_lines=0.9, alpha_points=0.8,\n                size_lines=3, size_points=5,\n                antialiased=True,\n                raise_if_out_of_image=True)\n\n            assert mock_line.call_count == 1\n            assert mock_points.call_count == 1\n\n            assert mock_line.call_args_list[0][0][0] == (10, 10)\n            assert np.isclose(mock_line.call_args_list[0][1][\"alpha\"], 0.9)\n            assert mock_line.call_args_list[0][1][\"size\"] == 3\n            assert mock_line.call_args_list[0][1][\"antialiased\"] is True\n            assert mock_line.call_args_list[0][1][\"raise_if_out_of_image\"] \\\n                is True\n\n            assert mock_points.call_args_list[0][0][0] == (10, 10)\n            assert np.isclose(mock_points.call_args_list[0][1][\"alpha\"], 0.8)\n            assert mock_points.call_args_list[0][1][\"size\"] == 5\n            assert mock_points.call_args_list[0][1][\"raise_if_out_of_image\"] \\\n                is True\n\n    def test_draw_heatmap_array_without_mocking(self):\n        ls = LineString([(0, 1), (5, 1), (5, 5)])\n\n        arr = ls.draw_heatmap_array((10, 10),\n                                    alpha_lines=0.9, alpha_points=0.5,\n                                    size_lines=1, size_points=3,\n                                    antialiased=False,\n                                    raise_if_out_of_image=False)\n\n        assert _drawing_allclose(arr[1, 0:5], 0.9)\n        assert _drawing_allclose(arr[1, 0:5], 0.9)\n        assert _drawing_allclose(arr[1, 0:5], 0.9)\n        assert _drawing_allclose(arr[2:5, 5], 0.9)\n        assert _drawing_allclose(arr[2:5, 5], 0.9)\n        assert _drawing_allclose(arr[2:5, 5], 0.9)\n\n        assert _drawing_allclose(arr[0, 0:2], 0.5)\n        assert _drawing_allclose(arr[2, 0:2], 0.5)\n\n        assert _drawing_allclose(arr[0, 4:6 + 1], 0.5)\n        assert _drawing_allclose(arr[2, 4], 0.5)\n        assert _drawing_allclose(arr[2, 6], 0.5)\n\n        assert _drawing_allclose(arr[4, 4], 0.5)\n        assert _drawing_allclose(arr[4, 6], 0.5)\n        assert _drawing_allclose(arr[6, 4:6 + 1], 0.5)\n\n        assert _drawing_allclose(arr[0, 3], 0.0)\n        assert _drawing_allclose(arr[7:, :], 0.0)\n\n    def test_draw_heatmap_array_with_empty_line_string(self):\n        ls = LineString([])\n\n        arr = ls.draw_heatmap_array((10, 10))\n\n        assert arr.shape == (10, 10)\n        assert np.sum(arr) == 0\n\n    def test_draw_line_on_image_with_image_of_zeros(self):\n        # image of 0s\n        ls = LineString([(0, 1), (9, 1)])\n\n        img = ls.draw_lines_on_image(\n            np.zeros((3, 10, 3), dtype=np.uint8),\n            color=(10, 200, 20),\n            alpha=1.0, size=1,\n            antialiased=False,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert np.all(img[0, :, :] == 0)\n        assert np.all(img[1, :, 0] == 10)\n        assert np.all(img[1, :, 1] == 200)\n        assert np.all(img[1, :, 2] == 20)\n        assert np.all(img[2, :, :] == 0)\n\n    def test_draw_line_on_image_with_2d_image_of_zeros(self):\n        # image of 0s, 2D input image\n        ls = LineString([(0, 1), (9, 1)])\n\n        img = ls.draw_lines_on_image(\n            np.zeros((3, 10), dtype=np.uint8),\n            color=200,\n            alpha=1.0, size=1,\n            antialiased=False,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert np.all(img[0, :] == 0)\n        assert np.all(img[1, :] == 200)\n        assert np.all(img[2, :] == 0)\n\n    def test_draw_line_on_image_of_ones(self):\n        # image of 1s\n        ls = LineString([(0, 1), (9, 1)])\n\n        img = ls.draw_lines_on_image(\n            np.ones((3, 10, 3), dtype=np.uint8),\n            color=(10, 200, 20),\n            alpha=1.0, size=1,\n            antialiased=False,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert np.all(img[0, :, :] == 1)\n        assert np.all(img[1, :, 0] == 10)\n        assert np.all(img[1, :, 1] == 200)\n        assert np.all(img[1, :, 2] == 20)\n        assert np.all(img[2, :, :] == 1)\n\n    def test_draw_line_on_image_alpha_at_50_percent(self):\n        # alpha=0.5\n        ls = LineString([(0, 1), (9, 1)])\n\n        img = ls.draw_lines_on_image(\n            np.zeros((3, 10, 3), dtype=np.uint8),\n            color=(10, 200, 20),\n            alpha=0.5, size=1,\n            antialiased=False,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert np.all(img[0, :, :] == 0)\n        assert np.all(img[1, :, 0] == 5)\n        assert np.all(img[1, :, 1] == 100)\n        assert np.all(img[1, :, 2] == 10)\n        assert np.all(img[2, :, :] == 0)\n\n    def test_draw_line_on_image_alpha_at_50_percent_with_background(self):\n        # alpha=0.5 with background\n        ls = LineString([(0, 1), (9, 1)])\n\n        img = ls.draw_lines_on_image(\n            10 + np.zeros((3, 10, 3), dtype=np.uint8),\n            color=(10, 200, 20),\n            alpha=0.5, size=1,\n            antialiased=False,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert np.all(img[0, :, :] == 10)\n        assert np.all(img[1, :, 0] == 5+5)\n        assert np.all(img[1, :, 1] == 5+100)\n        assert np.all(img[1, :, 2] == 5+10)\n        assert np.all(img[2, :, :] == 10)\n\n    def test_draw_line_on_image_with_size_3(self):\n        # size=3\n        ls = LineString([(0, 5), (9, 5)])\n\n        img = ls.draw_lines_on_image(\n            np.zeros((10, 10, 3), dtype=np.uint8),\n            color=(10, 200, 20),\n            alpha=1.0, size=3,\n            antialiased=False,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert np.all(img[5-1:5+1+1, :, 0] == 10)\n        assert np.all(img[5-1:5+1+1, :, 1] == 200)\n        assert np.all(img[5-1:5+1+1, :, 2] == 20)\n        assert np.all(img[:5-1, :, :] == 0)\n        assert np.all(img[5+1+1:, :, :] == 0)\n\n    def test_draw_line_on_image_with_2d_image_and_size_3(self):\n        # size=3, 2D input image\n        ls = LineString([(0, 5), (9, 5)])\n\n        img = ls.draw_lines_on_image(\n            np.zeros((10, 10), dtype=np.uint8),\n            color=200,\n            alpha=1.0, size=3,\n            antialiased=False,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert np.all(img[5-1:5+1+1, :] == 200)\n        assert np.all(img[:5-1, :] == 0)\n        assert np.all(img[5+1+1:, :] == 0)\n\n    def test_draw_line_on_image_with_size_3_and_antialiasing(self):\n        # size=3, antialiasing\n        ls = LineString([(0, 0), (9, 9)])\n\n        img = ls.draw_lines_on_image(\n            np.zeros((10, 10, 3), dtype=np.uint8),\n            color=(100, 100, 100),\n            alpha=1.0, size=3,\n            antialiased=False,\n            raise_if_out_of_image=False\n        )\n        img_aa = ls.draw_lines_on_image(\n            np.zeros((10, 10, 3), dtype=np.uint8),\n            color=(100, 100, 100),\n            alpha=1.0, size=3,\n            antialiased=True,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert img_aa.dtype.name == \"uint8\"\n        assert np.sum(img) > 5 * 3 * 100\n        assert np.sum(img_aa) > 5 * 3 * 100\n        assert not np.array_equal(img, img_aa)\n        assert np.all(img[:3, -3:, :] == 0)\n        assert np.all(img_aa[:3, -3:, :] == 0)\n        assert np.all(img[-3:, :3, :] == 0)\n        assert np.all(img_aa[-3:, :3, :] == 0)\n\n    def test_draw_line_on_image_with_line_partially_outside_image(self):\n        # line partially outside if image\n        ls = LineString([(-1, 1), (9, 1)])\n\n        img = ls.draw_lines_on_image(\n            np.zeros((3, 10, 3), dtype=np.uint8),\n            color=(10, 200, 20),\n            alpha=1.0, size=1,\n            antialiased=False,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert np.all(img[0, :, :] == 0)\n        assert np.all(img[1, :, 0] == 10)\n        assert np.all(img[1, :, 1] == 200)\n        assert np.all(img[1, :, 2] == 20)\n        assert np.all(img[2, :, :] == 0)\n\n    def test_draw_line_on_image_with_line_fully_outside_image(self):\n        # line fully outside if image\n        ls = LineString([(-10, 1), (-9, 1)])\n\n        img = ls.draw_lines_on_image(\n            np.zeros((3, 10, 3), dtype=np.uint8),\n            color=(10, 200, 20),\n            alpha=1.0, size=1,\n            antialiased=False,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert np.all(img == 0)\n\n    def test_draw_line_on_image_with_line_fully_ooi_and_raise_true(self):\n        # raise_if_out_of_image=True\n        ls = LineString([(0-5, 5), (-1, 5)])\n\n        with self.assertRaises(Exception) as context:\n            _img = ls.draw_lines_on_image(\n                np.zeros((10, 10, 3), dtype=np.uint8),\n                color=(100, 100, 100),\n                alpha=1.0, size=3,\n                antialiased=False,\n                raise_if_out_of_image=True\n            )\n\n        assert \"Cannot draw line string \" in str(context.exception)\n\n    def test_draw_line_on_image_with_line_part_inside_img_and_raise_true(self):\n        # raise_if_out_of_image=True BUT line is partially inside image\n        # (no point is inside image though)\n        ls = LineString([(-1, 5), (11, 5)])\n\n        _img = ls.draw_lines_on_image(\n            np.zeros((10, 10, 3), dtype=np.uint8),\n            color=(100, 100, 100),\n            alpha=1.0, size=3,\n            antialiased=False,\n            raise_if_out_of_image=True\n        )\n\n    def test_draw_points_on_image_with_image_of_zeros(self):\n        # iamge of 0s\n        ls = LineString([(0, 1), (9, 1)])\n\n        img = ls.draw_points_on_image(\n            np.zeros((3, 10, 3), dtype=np.uint8),\n            color=(10, 255, 20),\n            alpha=1.0, size=3,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert np.all(img[0:2, 0:2, 0] == 10)\n        assert np.all(img[0:2, 0:2, 1] == 255)\n        assert np.all(img[0:2, 0:2, 2] == 20)\n        assert np.all(img[0:2, -2:, 0] == 10)\n        assert np.all(img[0:2, -2:, 1] == 255)\n        assert np.all(img[0:2, -2:, 2] == 20)\n        assert np.all(img[:, 2:-2, :] == 0)\n\n    def test_draw_points_on_image_with_image_of_ones(self):\n        # image of 1s\n        ls = LineString([(0, 1), (9, 1)])\n\n        img = ls.draw_points_on_image(\n            np.ones((3, 10, 3), dtype=np.uint8),\n            color=(10, 200, 20),\n            alpha=1.0, size=3,\n            raise_if_out_of_image=False\n        )\n\n        assert img.dtype.name == \"uint8\"\n        assert np.all(img[0:2, 0:2, 0] == 10)\n        assert np.all(img[0:2, 0:2, 1] == 200)\n        assert np.all(img[0:2, 0:2, 2] == 20)\n        assert np.all(img[0:2, -2:, 0] == 10)\n        assert np.all(img[0:2, -2:, 1] == 200)\n        assert np.all(img[0:2, -2:, 2] == 20)\n        assert np.all(img[:, 2:-2, :] == 1)\n\n    def test_draw_points_on_image_with_alpha_50_percent(self):\n        # alpha=0.5\n        ls = LineString([(0, 1), (9, 1)])\n\n        img = ls.draw_points_on_image(\n            np.zeros((3, 10, 3), dtype=np.uint8),\n            color=(10, 200, 20),\n            alpha=0.5, size=3,\n            raise_if_out_of_image=False\n        )\n\n        assert np.all(img[0:2, 0:2, 0] == 5)\n        assert np.all(img[0:2, 0:2, 1] == 100)\n        assert np.all(img[0:2, 0:2, 2] == 10)\n        assert np.all(img[0:2, -2:, 0] == 5)\n        assert np.all(img[0:2, -2:, 1] == 100)\n        assert np.all(img[0:2, -2:, 2] == 10)\n        assert np.all(img[:, 2:-2, :] == 0)\n\n    def test_draw_points_on_image_with_size_one(self):\n        # size=1\n        ls = LineString([(0, 1), (9, 1)])\n\n        img = ls.draw_points_on_image(\n            np.zeros((3, 10, 3), dtype=np.uint8),\n            color=(10, 200, 20),\n            alpha=1.0, size=1,\n            raise_if_out_of_image=False\n        )\n\n        assert np.all(img[0, :, :] == 0)\n        assert np.all(img[2, :, :] == 0)\n\n        assert np.all(img[1, 0, 0] == 10)\n        assert np.all(img[1, 0, 1] == 200)\n        assert np.all(img[1, 0, 2] == 20)\n\n        assert np.all(img[1, -1, 0] == 10)\n        assert np.all(img[1, -1, 1] == 200)\n        assert np.all(img[1, -1, 2] == 20)\n\n    def test_draw_points_on_image_with_ls_ooi_and_raise_true(self):\n        with self.assertRaises(Exception) as context:\n            ls = LineString([(0-5, 1), (9+5, 1)])\n            _img = ls.draw_points_on_image(\n                np.zeros((3, 10, 3), dtype=np.uint8),\n                color=(10, 200, 20),\n                alpha=0.5, size=1,\n                raise_if_out_of_image=True\n            )\n\n        assert \"Cannot draw keypoint \" in str(context.exception)\n\n    def test_draw_on_image_with_mocking(self):\n        ls = LineString([(0, 1), (9, 1)])\n\n        module_name = \"imgaug.augmentables.lines.\"\n        line_fname = \"%sLineString.draw_lines_on_image\" % (module_name,)\n        points_fname = \"%sLineString.draw_points_on_image\" % (module_name,)\n        with mock.patch(line_fname, return_value=1) as mock_line, \\\n                mock.patch(points_fname, return_value=2) as mock_points:\n            _image = ls.draw_on_image(\n                np.zeros((10, 10, 3), dtype=np.uint8),\n                color=(1, 2, 3), color_lines=(4, 5, 6), color_points=(7, 8, 9),\n                alpha=1.0, alpha_lines=0.9, alpha_points=0.8,\n                size=1, size_lines=3, size_points=5,\n                antialiased=False,\n                raise_if_out_of_image=True)\n\n        assert mock_line.call_count == 1\n        assert mock_points.call_count == 1\n\n        assert mock_line.call_args_list[0][0][0].shape == (10, 10, 3)\n        assert mock_line.call_args_list[0][1][\"color\"][0] == 4\n        assert mock_line.call_args_list[0][1][\"color\"][1] == 5\n        assert mock_line.call_args_list[0][1][\"color\"][2] == 6\n        assert np.isclose(mock_line.call_args_list[0][1][\"alpha\"], 0.9)\n        assert mock_line.call_args_list[0][1][\"size\"] == 3\n        assert mock_line.call_args_list[0][1][\"antialiased\"] is False\n        assert mock_line.call_args_list[0][1][\"raise_if_out_of_image\"] \\\n            is True\n\n        assert mock_points.call_args_list[0][0][0] == 1  # mock_line result\n        assert mock_points.call_args_list[0][1][\"color\"][0] == 7\n        assert mock_points.call_args_list[0][1][\"color\"][1] == 8\n        assert mock_points.call_args_list[0][1][\"color\"][2] == 9\n        assert np.isclose(mock_points.call_args_list[0][1][\"alpha\"], 0.8)\n        assert mock_points.call_args_list[0][1][\"size\"] == 5\n        assert mock_points.call_args_list[0][1][\"raise_if_out_of_image\"] \\\n            is True\n\n    def test_draw_on_image_without_mocking(self):\n        ls = LineString([(0, 1), (5, 1), (5, 5)])\n\n        img = ls.draw_on_image(np.zeros((10, 10, 3), dtype=np.uint8),\n                               color=(200, 120, 40), alpha=0.5, size=1)\n\n        assert np.all(img[1, 0:5, 0] == 100)\n        assert np.all(img[1, 0:5, 1] == 60)\n        assert np.all(img[1, 0:5, 2] == 20)\n        assert np.all(img[1:5, 5, 0] == 100)\n        assert np.all(img[1:5, 5, 1] == 60)\n        assert np.all(img[1:5, 5, 2] == 20)\n        assert np.all(img[0:2+1, 0:2, 0] >= 50)  # color_points is 0.5*color\n        assert np.all(img[0:2+1, 0:2, 1] >= 30)\n        assert np.all(img[0:2+1, 0:2, 2] >= 10)\n        assert np.all(img[0:2+1, 4:6+1, 0] >= 50)\n        assert np.all(img[0:2+1, 4:6+1, 1] >= 30)\n        assert np.all(img[0:2+1, 4:6+1, 2] >= 10)\n        assert np.all(img[4:6+1, 4:6+1, 0] >= 50)\n        assert np.all(img[4:6+1, 4:6+1, 1] >= 30)\n        assert np.all(img[4:6+1, 4:6+1, 2] >= 10)\n        assert np.all(img[0, 3, :] == 0)\n        assert np.all(img[7:, :, :] == 0)\n\n    def test_draw_on_image_with_empty_line_string(self):\n        ls = LineString([])\n\n        img = ls.draw_on_image(np.zeros((10, 10, 3), dtype=np.uint8))\n\n        assert img.shape == (10, 10, 3)\n        assert np.sum(img) == 0\n\n    def test_extract_from_image_size_1_single_channel(self):\n        ls = LineString([(0, 5), (9, 5)])\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n\n        extract = ls.extract_from_image(img, antialiased=False)\n\n        assert extract.shape == (1, 10, 1)\n        assert np.array_equal(extract, img[5:6, 0:10, :])\n\n    def test_extract_from_image_size_3_single_channel(self):\n        ls = LineString([(1, 5), (8, 5)])\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n\n        extract = ls.extract_from_image(img, size=3, antialiased=False)\n\n        assert extract.shape == (3, 10, 1)\n        assert np.array_equal(extract, img[4:6+1, 0:10, :])\n\n    def test_extract_from_image_size_3_rgb(self):\n        # size=3, RGB image\n        ls = LineString([(1, 5), (8, 5)])\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n        img_rgb = np.tile(img, (1, 1, 3))\n        img_rgb[..., 1] += 10\n        img_rgb[..., 2] += 20\n\n        extract = ls.extract_from_image(img_rgb, size=3, antialiased=False)\n\n        assert extract.shape == (3, 10, 3)\n        assert np.array_equal(extract, img_rgb[4:6+1, 0:10, :])\n\n    def test_extract_from_image_antialiased_true(self):\n        # weak antialiased=True test\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n        ls = LineString([(1, 1), (9, 9)])\n\n        extract_aa = ls.extract_from_image(img, size=3, antialiased=True)\n        extract = ls.extract_from_image(img, size=3, antialiased=False)\n\n        assert extract_aa.shape == extract.shape\n        assert np.sum(extract_aa) > np.sum(extract)\n\n    def test_extract_from_image_pad_false(self):\n        # pad=False\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n        ls = LineString([(-5, 5), (-3, 5)])\n\n        extract = ls.extract_from_image(img, size=1, antialiased=False,\n                                        pad=False, prevent_zero_size=True)\n\n        assert extract.shape == (1, 1, 1)\n        assert np.sum(extract) == 0\n\n    def test_extract_from_image_pad_false_and_prevent_zero_size_false(self):\n        # pad=False, prevent_zero_size=False\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n        ls = LineString([(-5, 5), (-3, 5)])\n\n        extract = ls.extract_from_image(img, size=1, antialiased=False,\n                                        pad=False, prevent_zero_size=False)\n\n        assert extract.shape == (0, 0, 1)\n\n    def test_extract_from_image_pad_max(self):\n        # pad_max=1\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n        ls = LineString([(-5, 5), (9, 5)])\n\n        extract = ls.extract_from_image(img, antialiased=False, pad=True,\n                                        pad_max=1)\n\n        assert extract.shape == (1, 11, 1)\n        assert np.array_equal(extract[:, 1:], img[5:6, 0:10, :])\n        assert np.all(extract[0, 0, :] == 0)\n\n    def test_extract_from_image_with_single_point_line_string(self):\n        # 1 coord\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n        ls = LineString([(1, 1)])\n\n        extract = ls.extract_from_image(img)\n\n        assert extract.shape == (1, 1, 1)\n        assert np.sum(extract) == img[1:2, 1:2, :]\n\n    def test_extract_from_image_with_single_point_ls_negative_coords(self):\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n        ls = LineString([(-10, -10)])\n\n        extract = ls.extract_from_image(img)\n\n        assert extract.shape == (1, 1, 1)\n        assert np.sum(extract) == 0\n\n    def test_extract_from_image_with_1_point_neg_coords_prevent_zero_size(self):\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n        ls = LineString([(-10, -10)])\n\n        extract = ls.extract_from_image(img, prevent_zero_size=True)\n\n        assert extract.shape == (1, 1, 1)\n        assert np.sum(extract) == 0\n\n    def test_extract_from_image_with_empty_line_string(self):\n        # 0 coords\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n        ls = LineString([])\n\n        extract = ls.extract_from_image(img)\n\n        assert extract.shape == (1, 1, 1)\n        assert np.sum(extract) == 0\n\n    def test_extract_from_image_with_empty_line_string_prevent_zero_size(self):\n        img = np.arange(10*10).reshape((10, 10, 1)).astype(np.uint8)\n        ls = LineString([])\n\n        extract = ls.extract_from_image(img, prevent_zero_size=False)\n\n        assert extract.shape == (0, 0, 1)\n        assert np.sum(extract) == 0\n\n    def test_concatenate_line_string_with_itself(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        ls_concat = ls.concatenate(ls)\n        assert ls_concat.coords_almost_equals([\n            (0, 0), (1, 0), (2, 1), (0, 0), (1, 0), (2, 1)\n        ])\n\n    def test_concatenate_empty_line_string_with_itself(self):\n        ls = LineString([])\n        ls_concat = ls.concatenate(ls)\n        assert ls_concat.coords_almost_equals([])\n\n    def test_concatenate_empty_line_string_with_single_point_line_string(self):\n        ls = LineString([])\n        ls_concat = ls.concatenate(LineString([(0, 0)]))\n        assert ls_concat.coords_almost_equals([(0, 0)])\n\n    def test_concatenate_single_point_line_string_with_empty_line_string(self):\n        ls = LineString([(0, 0)])\n        ls_concat = ls.concatenate(LineString([]))\n        assert ls_concat.coords_almost_equals([(0, 0)])\n\n    def test_concatenate_empty_line_string_with_list_of_tuples(self):\n        ls = LineString([])\n        ls_concat = ls.concatenate([(0, 0)])\n        assert ls_concat.coords_almost_equals([(0, 0)])\n\n    def test_to_keypoints(self):\n        ls = LineString([(0, 0), (1, 0), (2, 1)])\n        observed = ls.to_keypoints()\n        assert all([isinstance(kp, ia.Keypoint) for kp in observed])\n        assert np.isclose(observed[0].x, 0)\n        assert np.isclose(observed[0].y, 0)\n        assert np.isclose(observed[1].x, 1)\n        assert np.isclose(observed[1].y, 0)\n        assert np.isclose(observed[2].x, 2)\n        assert np.isclose(observed[2].y, 1)\n\n    def test_to_keypoints_with_empty_line_string(self):\n        ls = LineString([])\n        assert len(ls.to_keypoints()) == 0\n\n    def test_to_bounding_box(self):\n        ls = LineString([(0, 0), (1, 0), (1, 1)])\n        observed = ls.to_bounding_box()\n        assert isinstance(observed, ia.BoundingBox)\n        assert np.isclose(observed.x1, 0)\n        assert np.isclose(observed.y1, 0)\n        assert np.isclose(observed.x2, 1)\n        assert np.isclose(observed.y2, 1)\n\n    def test_to_bounding_box_with_single_point_line_string(self):\n        ls = LineString([(0, 0)])\n        observed = ls.to_bounding_box()\n        assert isinstance(observed, ia.BoundingBox)\n        assert np.isclose(observed.x1, 0)\n        assert np.isclose(observed.y1, 0)\n        assert np.isclose(observed.x2, 0)\n        assert np.isclose(observed.y2, 0)\n\n    def test_to_bounding_box_with_empty_line_string(self):\n        ls = LineString([])\n        observed = ls.to_bounding_box()\n        assert observed is None\n\n    def test_to_polygon(self):\n        ls = LineString([(0, 0), (1, 0), (1, 1)])\n        observed = ls.to_polygon()\n        assert isinstance(observed, ia.Polygon)\n        assert np.allclose(observed.exterior, [(0, 0), (1, 0), (1, 1)])\n\n    def test_to_polygon_with_single_point_line_string(self):\n        ls = LineString([(0, 0)])\n        observed = ls.to_polygon()\n        assert isinstance(observed, ia.Polygon)\n        assert np.allclose(observed.exterior, [(0, 0)])\n\n    def test_to_polygon_with_empty_line_string(self):\n        ls = LineString([])\n        observed = ls.to_polygon()\n        assert isinstance(observed, ia.Polygon)\n        assert len(observed.exterior) == 0\n\n    # TODO add antialiased=True test\n    def test_to_heatmap(self):\n        ls = LineString([(0, 5), (5, 5)])\n        observed = ls.to_heatmap((10, 10), antialiased=False)\n        assert isinstance(observed, HeatmapsOnImage)\n        assert observed.shape == (10, 10)\n        assert observed.arr_0to1.shape == (10, 10, 1)\n        assert np.allclose(observed.arr_0to1[0:5, :, :], 0.0)\n        assert np.allclose(observed.arr_0to1[5, 0:5, :], 1.0)\n        assert np.allclose(observed.arr_0to1[6:, :, :], 0.0)\n\n    def test_to_heatmap_with_empty_line_string(self):\n        ls = LineString([])\n        observed = ls.to_heatmap((5, 5), antialiased=False)\n        assert observed.shape == (5, 5)\n        assert observed.arr_0to1.shape == (5, 5, 1)\n        assert np.allclose(observed.arr_0to1, 0.0)\n\n    # TODO change this after the segmap PR was merged\n\n    def test_segmentation_map(self):\n        from imgaug.augmentables.segmaps import SegmentationMapsOnImage\n        ls = LineString([(0, 5), (5, 5)])\n        observed = ls.to_segmentation_map((10, 10))\n        assert isinstance(observed, SegmentationMapsOnImage)\n        assert observed.shape == (10, 10)\n        assert observed.arr.shape == (10, 10, 1)\n        assert np.all(observed.arr[0:5, :, :] == 0)\n        assert np.all(observed.arr[5, 0:5, :] == 1)\n        assert np.all(observed.arr[6:, :, :] == 0)\n\n        ls = LineString([])\n        observed = ls.to_segmentation_map((5, 5))\n        assert observed.shape == (5, 5)\n        assert observed.arr.shape == (5, 5, 1)\n        assert np.all(observed.arr == 0)\n\n    def test_coords_almost_equals_with_3_point_ls_90deg_angle(self):\n        ls = LineString([(0, 0), (1, 0), (1, 1)])\n        assert ls.coords_almost_equals(ls)\n        assert ls.coords_almost_equals([(0, 0), (1, 0), (1, 1)])\n        assert not ls.shift(y=1).coords_almost_equals(ls)\n        assert ls.shift(y=1).coords_almost_equals(ls, max_distance=1.01)\n        assert ls.coords_almost_equals([(0, 0), (0.5, 0), (1, 0), (1, 1)])\n\n    def test_coords_almost_equals_with_4_point_ls_90deg_angle(self):\n        ls = LineString([(0, 0), (0.5, 0), (1, 0), (1, 1)])\n        assert ls.coords_almost_equals([(0, 0), (1, 0), (1, 1)])\n\n    def test_coords_almost_equals_with_1_point_ls(self):\n        ls = LineString([(0, 0)])\n        assert ls.coords_almost_equals([(0, 0)])\n        assert not ls.coords_almost_equals([(0+1, 0)])\n        assert ls.coords_almost_equals([(0+1, 0)], max_distance=1.01)\n        assert not ls.coords_almost_equals([])\n\n    def test_coords_almost_equals_with_empty_line_string(self):\n        ls = LineString([])\n        assert ls.coords_almost_equals([])\n        assert not ls.coords_almost_equals([(0, 0)])\n\n    def test_coords_almost_equals_with_two_rectangular_line_strings(self):\n        # two rectangles around height=10, width=10 image,\n        # both LS closed, second one has more points around the left edge\n        ls_a = LineString([(0, 0), (10, 0), (10, 10), (0, 10), (0, 0)])\n        ls_b = LineString([(0, 0), (10, 0), (10, 10), (0, 10),\n                           (0, 5.01), (0, 5.0), (0, 4.99), (0, 0)])\n        assert ls_a.coords_almost_equals(ls_b)\n\n    def test_coords_almost_equals_different_ls_but_overlapping_points(self):\n        # almost the same as in above test\n        # two rectangles around height=10, width=10 image,\n        # both LS closed, second one has more points around the left edge\n        # AND around left edge the second line string suddenly has a line\n        # up to the right edge, which immediately returns back to the left\n        # edge\n        # All points overlap between the lines, i.e. this test fails if naively\n        # checking for overlapping points.\n        ls_a = LineString([(0, 0), (10, 0), (10, 10), (0, 10), (0, 0)])\n        ls_b = LineString([(0, 0), (10, 0), (10, 10), (0, 10),\n                           (0, 5.01), (10, 5.0), (0, 4.99), (0, 0)])\n        assert not ls_a.coords_almost_equals(ls_b)\n\n    def test_almost_equals_three_point_ls_without_label(self):\n        ls = LineString([(0, 0), (1, 0), (1, 1)])\n        ls_shifted = ls.shift(y=1)\n        assert ls.almost_equals(ls)\n        assert not ls.almost_equals(ls_shifted)\n        assert ls.almost_equals(LineString([(0, 0), (1, 0), (1, 1)],\n                                           label=None))\n        assert not ls.almost_equals(LineString([(0, 0), (1, 0), (1, 1)],\n                                               label=\"foo\"))\n\n    def test_almost_equals_three_point_ls_with_label(self):\n        ls = LineString([(0, 0), (1, 0), (1, 1)], label=\"foo\")\n        assert not ls.almost_equals(LineString([(0, 0), (1, 0), (1, 1)],\n                                               label=None))\n        assert ls.almost_equals(LineString([(0, 0), (1, 0), (1, 1)],\n                                           label=\"foo\"))\n\n    def test_almost_equals_empty_line_string(self):\n        ls = LineString([])\n        assert ls.almost_equals(LineString([]))\n        assert not ls.almost_equals(LineString([], label=\"foo\"))\n\n    def test_almost_equals_empty_line_string_with_label(self):\n        ls = LineString([], label=\"foo\")\n        assert not ls.almost_equals(LineString([]))\n        assert ls.almost_equals(LineString([], label=\"foo\"))\n\n    def test_copy_with_various_line_strings(self):\n        coords = [\n            [(0, 0), (1, 0), (1, 1)],\n            [(0, 0), (1.5, 0), (1.6, 1)],\n            [(0, 0)],\n            [],\n            [(0, 0), (1, 0), (1, 1)]\n        ]\n        labels = [None, None, None, None, \"foo\"]\n\n        for coords_i, label in zip(coords, labels):\n            with self.subTest(coords=coords_i, label=label):\n                ls = LineString(coords_i, label=label)\n                observed = ls.copy()\n                assert observed is not ls\n                assert observed.coords is ls.coords\n                assert observed.label is ls.label\n\n    def test_copy_with_coords_arg_set(self):\n        ls = LineString([(0, 0), (1, 0), (1, 1)])\n        observed = ls.copy(coords=[(0, 0)])\n        assert observed.coords_almost_equals([(0, 0)])\n        assert observed.label is None\n\n    def test_copy_with_label_arg_set(self):\n        ls = LineString([(0, 0), (1, 0), (1, 1)])\n        observed = ls.copy(label=\"bar\")\n        assert observed.coords is ls.coords\n        assert observed.label == \"bar\"\n\n    def test_deepcopy_with_various_line_strings(self):\n        coords = [\n            [(0, 0), (1, 0), (1, 1)],\n            [(0, 0), (1.5, 0), (1.6, 1)],\n            [(0, 0)],\n            [],\n            [(0, 0), (1, 0), (1, 1)]\n        ]\n        labels = [None, None, None, None, \"foo\"]\n\n        for coords_i, label in zip(coords, labels):\n            with self.subTest(coords=coords_i, label=label):\n                ls = LineString(coords_i, label=label)\n                observed = ls.deepcopy()\n                assert observed is not ls\n                assert observed.coords is not ls.coords\n                assert observed.label == ls.label\n\n    def test_deepcopy_with_coords_arg_set(self):\n        ls = LineString([(0, 0), (1, 0), (1, 1)])\n        observed = ls.deepcopy(coords=[(0, 0)])\n        assert observed.coords_almost_equals([(0, 0)])\n        assert observed.label is None\n\n    def test_deepcopy_with_label_arg_set(self):\n        ls = LineString([(0, 0), (1, 0), (1, 1)])\n        observed = ls.deepcopy(label=\"bar\")\n        assert observed.coords is not ls.coords\n        assert observed.label == \"bar\"\n\n    def test_string_conversion(self):\n        coords = [\n            [(0, 0), (1, 0), (1, 1)],\n            [(0, 0), (1.5, 0), (1.6, 1)],\n            [(0, 0)],\n            [],\n            [(0, 0), (1, 0), (1, 1)]\n        ]\n        labels = [None, None, None, None, \"foo\"]\n\n        for coords_i, label in zip(coords, labels):\n            with self.subTest(coords=coords_i, label=label):\n                ls = LineString(coords_i, label=label)\n                # __str__() is tested more thoroughly in other tests\n                assert ls.__repr__() == ls.__str__()\n\n    def test___getitem__(self):\n        cba = ia.LineString([(0, 1), (2, 3)])\n        assert np.allclose(cba[0], (0, 1))\n        assert np.allclose(cba[1], (2, 3))\n\n    def test___getitem___slice(self):\n        cba = ia.LineString([(0, 1), (2, 3), (4, 5)])\n        assert np.allclose(cba[1:], [(2, 3), (4, 5)])\n\n    def test___iter___two_points(self):\n        cba = LineString([(1, 2), (3, 4)])\n        for i, xy in enumerate(cba):\n            assert i in [0, 1]\n            if i == 0:\n                assert np.allclose(xy, (1, 2))\n            elif i == 1:\n                assert np.allclose(xy, (3, 4))\n        assert i == 1\n\n    def test___iter___zero_points(self):\n        cba = LineString([])\n        i = 0\n        for xy in cba:\n            i += 1\n        assert i == 0\n\n    def test___str__(self):\n        coords = [\n            [(0, 0), (1, 0), (1, 1)],\n            [(0, 0), (1.5, 0), (1.6, 1)],\n            [(0, 0)],\n            [],\n            [(0, 0), (1, 0), (1, 1)]\n        ]\n        labels = [None, None, None, None, \"foo\"]\n        expecteds = [\n            \"LineString([(0.00, 0.00), (1.00, 0.00), (1.00, 1.00)], \"\n            \"label=None)\",\n            \"LineString([(0.00, 0.00), (1.50, 0.00), (1.60, 1.00)], \"\n            \"label=None)\",\n            \"LineString([(0.00, 0.00)], label=None)\",\n            \"LineString([], label=None)\",\n            \"LineString([(0.00, 0.00), (1.00, 0.00), (1.00, 1.00)], \"\n            \"label=foo)\"\n        ]\n\n        for coords_i, label, expected in zip(coords, labels, expecteds):\n            with self.subTest(coords=coords_i, label=label):\n                ls = LineString(coords_i, label=label)\n                observed = ls.__str__()\n                assert observed == expected\n\n\nclass TestLineStringsOnImage_items_setter(unittest.TestCase):\n    def test_with_list_of_line_strings(self):\n        ls = [ia.LineString([(0, 0), (1, 0)]),\n              ia.LineString([(1, 1), (10, 0)])]\n        lsoi = ia.LineStringsOnImage([], shape=(10, 20, 3))\n        lsoi.items = ls\n        assert np.all([\n            (np.allclose(ls_i.coords, ls_j.coords))\n            for ls_i, ls_j\n            in zip(lsoi.line_strings, ls)\n        ])\n\n\nclass TestLineStringsOnImage_on_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, lsoi, to_shape):\n        return lsoi.on_(to_shape)\n\n    def test_on_image_with_same_shape(self):\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        lsoi_proj = self._func(lsoi, (100, 100, 3))\n\n        assert np.all([ls_a.coords_almost_equals(ls_b)\n                      for ls_a, ls_b\n                      in zip(lsoi.line_strings, lsoi_proj.line_strings)])\n        assert lsoi_proj.shape == (100, 100, 3)\n\n    def test_on_image_with_2x_size(self):\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        lsoi_proj = self._func(lsoi, (200, 200, 3))\n\n        assert lsoi_proj.line_strings[0].coords_almost_equals(\n            [(0, 0), (1*2, 0), (2*2, 1*2)]\n        )\n        assert lsoi_proj.line_strings[1].coords_almost_equals(\n            [(10*2, 10*2)]\n        )\n        assert lsoi_proj.shape == (200, 200, 3)\n\n    def test_on_image_with_2x_size_and_empty_list_of_line_strings(self):\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        lsoi_proj = self._func(lsoi, (200, 200, 3))\n\n        assert len(lsoi_proj.line_strings) == 0\n        assert lsoi_proj.shape == (200, 200, 3)\n\n    def test_inplaceness(self):\n        ls = ia.LineString([(0, 0), (1, 0)])\n        lsoi = LineStringsOnImage([ls], shape=(10, 10, 3))\n        lsoi2 = self._func(lsoi, (10, 10))\n        if self._is_inplace:\n            assert lsoi is lsoi2\n        else:\n            assert lsoi is not lsoi2\n\n\nclass TestLineStringsOnImage_on(TestLineStringsOnImage_on_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, lsoi, to_shape):\n        return lsoi.on(to_shape)\n\n\nclass TestLineStringsOnImage_remove_out_of_image_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, lsoi, fully=True, partly=False):\n        return lsoi.remove_out_of_image_(fully=fully, partly=partly)\n\n    def test_remove_out_of_image_with_two_line_strings(self):\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        observed = self._func(lsoi)\n\n        assert len(observed.line_strings) == 2\n        assert observed.line_strings[0] is ls1\n        assert observed.line_strings[1] is ls2\n\n    def test_remove_out_of_image_with_empty_list_of_line_strings(self):\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = self._func(lsoi)\n\n        assert len(observed.line_strings) == 0\n        assert observed.shape == (100, 100, 3)\n\n    def test_remove_out_of_image_with_one_empty_line_string(self):\n        ls1 = LineString([])\n        lsoi = LineStringsOnImage([ls1], shape=(100, 100, 3))\n\n        observed = self._func(lsoi)\n\n        assert len(observed.line_strings) == 0\n        assert observed.shape == (100, 100, 3)\n\n    def test_remove_out_of_image_with_one_line_string(self):\n        ls1 = LineString([(-10, -10), (5, 5)])\n        lsoi = LineStringsOnImage([ls1], shape=(100, 100, 3))\n\n        observed = self._func(lsoi)\n\n        assert len(observed.line_strings) == 1\n        assert observed.line_strings[0] is ls1\n        assert observed.shape == (100, 100, 3)\n\n    def test_remove_out_of_image_remove_even_partial_oois(self):\n        ls1 = LineString([(-10, -10), (5, 5)])\n        lsoi = LineStringsOnImage([ls1], shape=(100, 100, 3))\n\n        observed = self._func(lsoi, partly=True, fully=True)\n\n        assert len(observed.line_strings) == 0\n        assert observed.shape == (100, 100, 3)\n\n    def test_remove_out_of_image_with_one_single_point_line_strings(self):\n        ls1 = LineString([(-10, -10)])\n        lsoi = LineStringsOnImage([ls1], shape=(100, 100, 3))\n\n        observed = self._func(lsoi)\n\n        assert len(observed.line_strings) == 0\n        assert observed.shape == (100, 100, 3)\n\n    def test_remove_out_of_image_partly_false_and_fully_false(self):\n        ls1 = LineString([(-10, -10)])\n        lsoi = LineStringsOnImage([ls1], shape=(100, 100, 3))\n\n        observed = self._func(lsoi, partly=False, fully=False)\n\n        assert len(observed.line_strings) == 1\n        assert observed.line_strings[0] is ls1\n        assert observed.shape == (100, 100, 3)\n\n    def test_inplaceness(self):\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        lsoi2 = self._func(lsoi)\n\n        if self._is_inplace:\n            assert lsoi is lsoi2\n        else:\n            assert lsoi is not lsoi2\n\n\nclass TestLineStringsOnImage_remove_out_of_image(\n        TestLineStringsOnImage_remove_out_of_image_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, lsoi, fully=True, partly=False):\n        return lsoi.remove_out_of_image(fully=fully, partly=partly)\n\n\nclass TestLineStringsOnImage_clip_out_of_image_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, lsoi):\n        return lsoi.clip_out_of_image_()\n\n    def test_clip_out_of_image_with_two_simple_line_strings(self):\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        observed = self._func(lsoi)\n\n        expected = []\n        expected.extend(ls1.clip_out_of_image((100, 100, 3)))\n        expected.extend(ls2.clip_out_of_image((100, 100, 3)))\n        assert len(lsoi.line_strings) == len(expected)\n        for ls_obs, ls_exp in zip(observed.line_strings, expected):\n            assert ls_obs.coords_almost_equals(ls_exp)\n        assert observed.shape == (100, 100, 3)\n\n    def test_clip_out_of_image_with_empty_list_of_line_strings(self):\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = self._func(lsoi)\n\n        assert len(observed.line_strings) == 0\n        assert observed.shape == (100, 100, 3)\n\n    def test_clip_out_of_image_with_one_empty_line_string(self):\n        ls1 = LineString([])\n        lsoi = LineStringsOnImage([ls1], shape=(100, 100, 3))\n\n        observed = self._func(lsoi)\n\n        assert len(observed.line_strings) == 0\n        assert observed.shape == (100, 100, 3)\n\n    def test_clip_out_of_image_with_single_point_ls_and_negative_coords(self):\n        ls1 = LineString([(-10, -10)])\n        lsoi = LineStringsOnImage([ls1], shape=(100, 100, 3))\n\n        observed = self._func(lsoi)\n\n        assert len(observed.line_strings) == 0\n        assert observed.shape == (100, 100, 3)\n\n    def test_inplaceness(self):\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        lsoi2 = self._func(lsoi)\n\n        if self._is_inplace:\n            assert lsoi is lsoi2\n        else:\n            assert lsoi is not lsoi2\n\n\nclass TestLineStringsOnImage_clip_out_of_image(\n        TestLineStringsOnImage_clip_out_of_image_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, lsoi):\n        return lsoi.clip_out_of_image()\n\n\nclass TestLineStringsOnImage_shift_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, lsoi, *args, **kwargs):\n        def _func_impl():\n            return lsoi.shift_(*args, **kwargs)\n\n        if len(lsoi.line_strings) == 0:\n            return _func_impl()\n        return wrap_shift_deprecation(_func_impl, *args, **kwargs)\n\n    def test_shift_two_simple_line_strings_along_xy(self):\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        observed = self._func(lsoi.deepcopy(), x=1, y=2)\n\n        assert observed.line_strings[0].coords_almost_equals(\n            ls1.shift(x=1, y=2)\n        )\n        assert observed.line_strings[1].coords_almost_equals(\n            ls2.shift(x=1, y=2)\n        )\n        assert observed.shape == (100, 100, 3)\n\n    def test_inplaceness(self):\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        lsoi2 = self._func(lsoi, y=1)\n\n        if self._is_inplace:\n            assert lsoi is lsoi2\n        else:\n            assert lsoi is not lsoi2\n\n\nclass TestLineStringsOnImage_shift(TestLineStringsOnImage_shift_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, lsoi, *args, **kwargs):\n        def _func_impl():\n            return lsoi.shift(*args, **kwargs)\n\n        return wrap_shift_deprecation(_func_impl, *args, **kwargs)\n\n    def test_shift_with_two_simple_line_strings(self):\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        observed = self._func(lsoi.deepcopy(), top=1, right=2, bottom=3, left=4)\n\n        assert observed.line_strings[0].coords_almost_equals([\n            (0 - 2 + 4, 0 + 1 - 3),\n            (1 - 2 + 4, 0 + 1 - 3),\n            (2 - 2 + 4, 1 + 1 - 3)\n        ])\n        assert observed.line_strings[1].coords_almost_equals([\n            (10 - 2 + 4, 10 + 1 - 3)\n        ])\n        assert observed.shape == (100, 100, 3)\n\n    def test_shift_with_empty_list_of_line_strings(self):\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = self._func(lsoi, top=1, right=2, bottom=3, left=4)\n\n        assert len(observed.line_strings) == 0\n        assert observed.shape == (100, 100, 3)\n\n\n# TODO test to_keypoints_on_image()\n#      test invert_to_keypoints_on_image()\n#      test to_xy_array()\n#      test fill_from_xy_array_()\nclass TestLineStringsOnImage(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        lsoi = LineStringsOnImage([\n            LineString([]),\n            LineString([(0, 0), (5, 0)])\n        ], shape=(10, 10, 3))\n        assert len(lsoi.line_strings) == 2\n        assert lsoi.shape == (10, 10, 3)\n\n    def test___init___shape_is_array(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        with assertWarns(self, ia.DeprecationWarning):\n            lsoi = LineStringsOnImage([\n                LineString([]),\n                LineString([(0, 0), (5, 0)])\n            ], shape=image)\n        assert len(lsoi.line_strings) == 2\n        assert lsoi.shape == (10, 10, 3)\n\n    def test___init___with_empty_list(self):\n        lsoi = LineStringsOnImage([], shape=(10, 10))\n        assert lsoi.line_strings == []\n        assert lsoi.shape == (10, 10)\n\n    def test_items(self):\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        items = lsoi.items\n\n        assert items == [ls1, ls2]\n\n    def test_items_empty(self):\n        kpsoi = LineStringsOnImage([], shape=(40, 50, 3))\n\n        items = kpsoi.items\n\n        assert items == []\n\n    def test_empty_with_empty_list(self):\n        lsoi = LineStringsOnImage([], shape=(10, 10, 3))\n        assert lsoi.empty\n\n    def test_empty_with_list_of_empty_line_string(self):\n        lsoi = LineStringsOnImage([LineString([])], shape=(10, 10, 3))\n        assert not lsoi.empty\n\n    def test_empty_with_list_of_single_point_line_string(self):\n        lsoi = LineStringsOnImage([LineString([(0, 0)])], shape=(10, 10, 3))\n        assert not lsoi.empty\n\n    def test_from_xy_arrays_single_input_array_with_two_line_strings(self):\n        arrs = np.float32([\n            [(0, 0), (10, 10), (5, 10)],\n            [(5, 5), (15, 15), (10, 15)]\n        ])\n\n        lsoi = LineStringsOnImage.from_xy_arrays(arrs, shape=(100, 100, 3))\n\n        assert len(lsoi.line_strings) == 2\n        assert lsoi.line_strings[0].coords_almost_equals(arrs[0])\n        assert lsoi.line_strings[1].coords_almost_equals(arrs[1])\n\n    def test_from_xy_arrays_with_list_of_two_line_string_arrays(self):\n        arrs = [\n            np.float32([(0, 0), (10, 10), (5, 10)]),\n            np.float32([(5, 5), (15, 15), (10, 15), (25, 25)])\n        ]\n\n        lsoi = LineStringsOnImage.from_xy_arrays(arrs, shape=(100, 100, 3))\n\n        assert len(lsoi.line_strings) == 2\n        assert lsoi.line_strings[0].coords_almost_equals(arrs[0])\n        assert lsoi.line_strings[1].coords_almost_equals(arrs[1])\n\n    def test_from_xy_arrays_with_empty_3d_array_and_0_points_per_ls(self):\n        arrs = np.zeros((0, 0, 2), dtype=np.float32)\n\n        lsoi = LineStringsOnImage.from_xy_arrays(arrs, shape=(100, 100, 3))\n\n        assert len(lsoi.line_strings) == 0\n\n    def test_from_xy_arrays_with_empty_3d_array_and_5_points_per_ls(self):\n        arrs = np.zeros((0, 5, 2), dtype=np.float32)\n\n        lsoi = LineStringsOnImage.from_xy_arrays(arrs, shape=(100, 100, 3))\n\n        assert len(lsoi.line_strings) == 0\n\n    def test_to_xy_arrays_with_two_line_strings_of_same_length(self):\n        ls1 = LineString([(0, 0), (10, 10), (5, 10)])\n        ls2 = LineString([(5, 5), (15, 15), (10, 15)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        arrs = lsoi.to_xy_arrays()\n\n        assert isinstance(arrs, list)\n        assert len(arrs) == 2\n        assert arrs[0].dtype.name == \"float32\"\n        assert arrs[1].dtype.name == \"float32\"\n        assert np.allclose(arrs, [\n            [(0, 0), (10, 10), (5, 10)],\n            [(5, 5), (15, 15), (10, 15)]\n        ])\n\n    def test_to_xy_arrays_with_two_line_strings_of_different_lengths(self):\n        ls1 = LineString([(0, 0), (10, 10), (5, 10)])\n        ls2 = LineString([(5, 5), (15, 15), (10, 15), (25, 25)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        arrs = lsoi.to_xy_arrays()\n\n        assert isinstance(arrs, list)\n        assert len(arrs) == 2\n        assert arrs[0].dtype.name == \"float32\"\n        assert arrs[1].dtype.name == \"float32\"\n        assert np.allclose(arrs[0], [(0, 0), (10, 10), (5, 10)])\n        assert np.allclose(arrs[1], [(5, 5), (15, 15), (10, 15), (25, 25)])\n\n    def test_to_xy_arrays_with_two_line_strings_one_of_them_empty(self):\n        ls1 = LineString([])\n        ls2 = LineString([(5, 5), (15, 15), (10, 15), (25, 25)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        arrs = lsoi.to_xy_arrays()\n\n        assert isinstance(arrs, list)\n        assert len(arrs) == 2\n        assert arrs[0].dtype.name == \"float32\"\n        assert arrs[1].dtype.name == \"float32\"\n        assert arrs[0].shape == (0, 2)\n        assert np.allclose(arrs[1], [(5, 5), (15, 15), (10, 15), (25, 25)])\n\n    def test_to_xy_arrays_with_two_empty_list_of_line_strings(self):\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        arrs = lsoi.to_xy_arrays()\n\n        assert isinstance(arrs, list)\n        assert len(arrs) == 0\n\n    def test_draw_on_image_with_default_settings(self):\n        ls1 = LineString([(0, 0), (10, 10), (5, 10)])\n        ls2 = LineString([(5, 5), (15, 15), (10, 15), (25, 25)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n        img = np.zeros((100, 100, 3), dtype=np.uint8) + 1\n\n        observed = lsoi.draw_on_image(img)\n\n        expected = np.copy(img)\n        for ls in [ls1, ls2]:\n            expected = ls.draw_on_image(expected)\n        assert np.array_equal(observed, expected)\n\n    def test_draw_on_image_with_custom_settings(self):\n        ls1 = LineString([(0, 0), (10, 10), (5, 10)])\n        ls2 = LineString([(5, 5), (15, 15), (10, 15), (25, 25)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n        img = np.zeros((100, 100, 3), dtype=np.uint8) + 1\n\n        observed = lsoi.draw_on_image(img,\n                                      color_lines=(0, 0, 255),\n                                      color_points=(255, 0, 0),\n                                      alpha_lines=0.5,\n                                      alpha_points=0.6,\n                                      antialiased=False)\n\n        expected = np.copy(img)\n        for ls in [ls1, ls2]:\n            expected = ls.draw_on_image(expected,\n                                        color_lines=(0, 0, 255),\n                                        color_points=(255, 0, 0),\n                                        alpha_lines=0.5,\n                                        alpha_points=0.6,\n                                        antialiased=False)\n        assert np.array_equal(observed, expected)\n\n    def test_draw_on_image_with_default_settings_one_ls_empty(self):\n        ls1 = LineString([])\n        ls2 = LineString([(5, 5), (15, 15), (10, 15), (25, 25)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n        img = np.zeros((100, 100, 3), dtype=np.uint8) + 1\n\n        observed = lsoi.draw_on_image(img)\n\n        expected = np.copy(img)\n        for ls in [ls1, ls2]:\n            expected = ls.draw_on_image(expected)\n        assert np.array_equal(observed, expected)\n\n    def test_draw_on_image_with_default_settings_and_empty_list_of_ls(self):\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n        img = np.zeros((100, 100, 3), dtype=np.uint8) + 1\n\n        observed = lsoi.draw_on_image(img)\n\n        expected = np.copy(img)\n        assert np.array_equal(observed, expected)\n\n    def test_remove_out_of_image_fraction_(self):\n        item1 = ia.LineString([(5, 1), (9, 1)])\n        item2 = ia.LineString([(5, 1), (15, 1)])\n        item3 = ia.LineString([(15, 1), (25, 1)])\n        cbaoi = ia.LineStringsOnImage([item1, item2, item3],\n                                      shape=(10, 10, 3))\n\n        cbaoi_reduced = cbaoi.remove_out_of_image_fraction_(0.6)\n\n        assert len(cbaoi_reduced.items) == 2\n        assert cbaoi_reduced.items == [item1, item2]\n        assert cbaoi_reduced is cbaoi\n\n    def test_remove_out_of_image_fraction(self):\n        item1 = ia.LineString([(5, 1), (9, 1)])\n        item2 = ia.LineString([(5, 1), (15, 1)])\n        item3 = ia.LineString([(15, 1), (25, 1)])\n        cbaoi = ia.LineStringsOnImage([item1, item2, item3],\n                                      shape=(10, 10, 3))\n\n        cbaoi_reduced = cbaoi.remove_out_of_image_fraction(0.6)\n\n        assert len(cbaoi_reduced.items) == 2\n        assert cbaoi_reduced.items == [item1, item2]\n        assert cbaoi_reduced is not cbaoi\n\n    def test_to_xy_array(self):\n        lsoi = ia.LineStringsOnImage(\n            [ia.LineString([(0, 0), (1, 2)]),\n             ia.LineString([(10, 20), (30, 40)])],\n            shape=(1, 2, 3))\n\n        xy_out = lsoi.to_xy_array()\n\n        expected = np.float32([\n            [0.0, 0.0],\n            [1.0, 2.0],\n            [10.0, 20.0],\n            [30.0, 40.0]\n        ])\n        assert xy_out.shape == (4, 2)\n        assert np.allclose(xy_out, expected)\n        assert xy_out.dtype.name == \"float32\"\n\n    def test_to_xy_array__empty_object(self):\n        lsoi = ia.LineStringsOnImage(\n            [],\n            shape=(1, 2, 3))\n\n        xy_out = lsoi.to_xy_array()\n\n        assert xy_out.shape == (0, 2)\n        assert xy_out.dtype.name == \"float32\"\n\n    def test_fill_from_xy_array___empty_array(self):\n        xy = np.zeros((0, 2), dtype=np.float32)\n        lsoi = ia.LineStringsOnImage([], shape=(2, 2, 3))\n\n        lsoi = lsoi.fill_from_xy_array_(xy)\n\n        assert len(lsoi.line_strings) == 0\n\n    def test_fill_from_xy_array___empty_list(self):\n        xy = []\n        lsoi = ia.LineStringsOnImage([], shape=(2, 2, 3))\n\n        lsoi = lsoi.fill_from_xy_array_(xy)\n\n        assert len(lsoi.line_strings) == 0\n\n    def test_fill_from_xy_array___array_with_two_coords(self):\n        xy = np.array(\n            [(100, 101),\n             (102, 103),\n             (200, 201),\n             (202, 203)], dtype=np.float32)\n        lsoi = ia.LineStringsOnImage(\n            [ia.LineString([(0, 0), (1, 2)]),\n             ia.LineString([(10, 20), (30, 40)])],\n            shape=(2, 2, 3))\n\n        lsoi = lsoi.fill_from_xy_array_(xy)\n\n        assert len(lsoi.line_strings) == 2\n        assert np.allclose(\n            lsoi.line_strings[0].coords,\n            [(100, 101), (102, 103)])\n        assert np.allclose(\n            lsoi.line_strings[1].coords,\n            [(200, 201), (202, 203)])\n\n    def test_fill_from_xy_array___list_with_two_coords(self):\n        xy = [(100, 101),\n              (102, 103),\n              (200, 201),\n              (202, 203)]\n        lsoi = ia.LineStringsOnImage(\n            [ia.LineString([(0, 0), (1, 2)]),\n             ia.LineString([(10, 20), (30, 40)])],\n            shape=(2, 2, 3))\n\n        lsoi = lsoi.fill_from_xy_array_(xy)\n\n        assert len(lsoi.line_strings) == 2\n        assert np.allclose(\n            lsoi.line_strings[0].coords,\n            [(100, 101), (102, 103)])\n        assert np.allclose(\n            lsoi.line_strings[1].coords,\n            [(200, 201), (202, 203)])\n\n    def test_to_keypoints_on_image(self):\n        lsoi = ia.LineStringsOnImage(\n            [ia.LineString([(0, 0), (1, 2)]),\n             ia.LineString([(10, 20), (30, 40)])],\n            shape=(1, 2, 3))\n\n        kpsoi = lsoi.to_keypoints_on_image()\n\n        assert len(kpsoi.keypoints) == 2*2\n        assert kpsoi.keypoints[0].x == 0\n        assert kpsoi.keypoints[0].y == 0\n        assert kpsoi.keypoints[1].x == 1\n        assert kpsoi.keypoints[1].y == 2\n        assert kpsoi.keypoints[2].x == 10\n        assert kpsoi.keypoints[2].y == 20\n        assert kpsoi.keypoints[3].x == 30\n        assert kpsoi.keypoints[3].y == 40\n\n    def test_to_keypoints_on_image__empty_instance(self):\n        lsoi = ia.LineStringsOnImage([], shape=(1, 2, 3))\n\n        kpsoi = lsoi.to_keypoints_on_image()\n\n        assert len(kpsoi.keypoints) == 0\n\n    def test_invert_to_keypoints_on_image_(self):\n        lsoi = ia.LineStringsOnImage(\n            [ia.LineString([(0, 0), (1, 2)]),\n             ia.LineString([(10, 20), (30, 40)])],\n            shape=(1, 2, 3))\n        kpsoi = ia.KeypointsOnImage(\n            [ia.Keypoint(100, 101), ia.Keypoint(102, 103),\n             ia.Keypoint(110, 120), ia.Keypoint(130, 140)],\n            shape=(10, 20, 30))\n\n        lsoi_inv = lsoi.invert_to_keypoints_on_image_(kpsoi)\n\n        assert len(lsoi_inv.line_strings) == 2\n        assert lsoi_inv.shape == (10, 20, 30)\n        assert np.allclose(\n            lsoi.line_strings[0].coords,\n            [(100, 101), (102, 103)])\n        assert np.allclose(\n            lsoi.line_strings[1].coords,\n            [(110, 120), (130, 140)])\n\n    def test_invert_to_keypoints_on_image___empty_instance(self):\n        lsoi = ia.LineStringsOnImage([], shape=(1, 2, 3))\n        kpsoi = ia.KeypointsOnImage([], shape=(10, 20, 30))\n\n        lsoi_inv = lsoi.invert_to_keypoints_on_image_(kpsoi)\n\n        assert len(lsoi_inv.line_strings) == 0\n        assert lsoi_inv.shape == (10, 20, 30)\n\n    def test_copy_with_two_line_strings(self):\n        # basic test, without labels\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        observed = lsoi.copy()\n\n        assert observed.line_strings[0] is ls1\n        assert observed.line_strings[1] is ls2\n        assert observed.shape == (100, 100, 3)\n\n    def test_copy_with_two_line_strings_and_labels(self):\n        # basic test, with labels\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)], label=\"foo\")\n        ls2 = LineString([(10, 10)], label=\"bar\")\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        observed = lsoi.copy()\n\n        assert observed.line_strings[0] is ls1\n        assert observed.line_strings[1] is ls2\n        assert observed.line_strings[0].label == \"foo\"\n        assert observed.line_strings[1].label == \"bar\"\n        assert observed.shape == (100, 100, 3)\n\n    def test_copy_with_empty_list_of_line_strings(self):\n        # LSOI is empty\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = lsoi.copy()\n\n        assert observed.line_strings == []\n        assert observed.shape == (100, 100, 3)\n\n    def test_copy_and_replace_line_strings_attribute(self):\n        # provide line_strings\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = lsoi.copy(line_strings=[ls1, ls2], shape=(200, 201, 3))\n\n        assert observed.line_strings[0] is ls1\n        assert observed.line_strings[1] is ls2\n        assert observed.shape == (200, 201, 3)\n\n    def test_copy_and_replace_line_strings_attribute_with_labeled_ls(self):\n        # provide line_strings, with labels\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)], label=\"foo\")\n        ls2 = LineString([(10, 10)], label=\"bar\")\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = lsoi.copy(line_strings=[ls1, ls2], shape=(200, 201, 3))\n\n        assert observed.line_strings[0] is ls1\n        assert observed.line_strings[1] is ls2\n        assert observed.line_strings[0].label == \"foo\"\n        assert observed.line_strings[1].label == \"bar\"\n        assert observed.shape == (200, 201, 3)\n\n    def test_copy_and_replace_line_strings_attribute_with_empty_list(self):\n        # provide empty list of line_strings\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = lsoi.copy(line_strings=[], shape=(200, 201, 3))\n\n        assert observed.line_strings == []\n        assert observed.shape == (200, 201, 3)\n\n    def test_deepcopy_with_two_line_strings(self):\n        # basic test, without labels\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        observed = lsoi.deepcopy()\n\n        assert observed.line_strings[0] is not ls1\n        assert observed.line_strings[1] is not ls2\n        assert observed.line_strings[0].coords_almost_equals(ls1)\n        assert observed.line_strings[1].coords_almost_equals(ls2)\n        assert observed.shape == (100, 100, 3)\n\n    def test_deepcopy_with_two_line_strings_and_labels(self):\n        # basic test, with labels\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)], label=\"foo\")\n        ls2 = LineString([(10, 10)], label=\"bar\")\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        observed = lsoi.deepcopy()\n\n        assert observed.line_strings[0] is not ls1\n        assert observed.line_strings[1] is not ls2\n        assert observed.line_strings[0].coords_almost_equals(ls1)\n        assert observed.line_strings[1].coords_almost_equals(ls2)\n        assert observed.line_strings[0].label == \"foo\"\n        assert observed.line_strings[1].label == \"bar\"\n        assert observed.shape == (100, 100, 3)\n\n    def test_deepcopy_with_empty_list_of_line_strings(self):\n        # LSOI is empty\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = lsoi.deepcopy()\n\n        assert observed.line_strings == []\n        assert observed.shape == (100, 100, 3)\n\n    def test_deepcopy_and_replace_line_strings_attribute(self):\n        # provide line_strings\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = lsoi.deepcopy(line_strings=[ls1, ls2], shape=(200, 201, 3))\n\n        # line strings provided via line_strings are also deepcopied\n        assert observed.line_strings[0].coords_almost_equals(ls1)\n        assert observed.line_strings[1].coords_almost_equals(ls2)\n        assert observed.shape == (200, 201, 3)\n\n    def test_deepcopy_and_replace_line_strings_attribute_with_labeled_ls(self):\n        # provide line_strings, with labels\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)], label=\"foo\")\n        ls2 = LineString([(10, 10)], label=\"bar\")\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = lsoi.deepcopy(line_strings=[ls1, ls2], shape=(200, 201, 3))\n\n        # line strings provided via line_strings are also deepcopied\n        assert observed.line_strings[0].coords_almost_equals(ls1)\n        assert observed.line_strings[1].coords_almost_equals(ls2)\n        assert observed.line_strings[0].label == \"foo\"\n        assert observed.line_strings[1].label == \"bar\"\n        assert observed.shape == (200, 201, 3)\n\n    def test_deepcopy_and_replace_line_strings_attribute_with_empty_list(self):\n        # provide empty list of line_strings\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = lsoi.deepcopy(line_strings=[], shape=(200, 201, 3))\n\n        assert observed.line_strings == []\n        assert observed.shape == (200, 201, 3)\n\n    def test___getitem__(self):\n        cbas = [\n            ia.LineString([(0, 0), (1, 0), (1, 1)]),\n            ia.LineString([(1, 1), (2, 1), (2, 2)])\n        ]\n        cbasoi = ia.LineStringsOnImage(cbas, shape=(3, 4, 3))\n\n        assert cbasoi[0] is cbas[0]\n        assert cbasoi[1] is cbas[1]\n        assert cbasoi[0:2] == cbas\n\n    def test___iter__(self):\n        cbas = [ia.LineString([(0, 0), (1, 1)]),\n                ia.LineString([(1, 2), (3, 4)])]\n        cbasoi = ia.LineStringsOnImage(cbas, shape=(40, 50, 3))\n\n        for i, cba in enumerate(cbasoi):\n            assert cba is cbas[i]\n\n    def test___iter___empty(self):\n        cbasoi = ia.LineStringsOnImage([], shape=(40, 50, 3))\n        i = 0\n        for _cba in cbasoi:\n            i += 1\n        assert i == 0\n\n    def test___len__(self):\n        cbas = [ia.LineString([(0, 0), (1, 1)]),\n                ia.LineString([(1, 2), (3, 4)])]\n        cbasoi = ia.LineStringsOnImage(cbas, shape=(40, 50, 3))\n        assert len(cbasoi) == 2\n\n    def test___repr__(self):\n        def _func(obj):\n            return obj.__repr__()\n\n        self._test_str_repr(_func)\n\n    def test___str__(self):\n        def _func(obj):\n            return obj.__str__()\n\n        self._test_str_repr(_func)\n\n    def test___repr___empty_list_of_line_strings(self):\n        def _func(obj):\n            return obj.__repr__()\n\n        self._test_str_repr_empty_list_of_line_strings(_func)\n\n    def test___str___empty_list_of_line_strings(self):\n        def _func(obj):\n            return obj.__str__()\n\n        self._test_str_repr_empty_list_of_line_strings(_func)\n\n    @classmethod\n    def _test_str_repr(cls, func):\n        ls1 = LineString([(0, 0), (1, 0), (2, 1)])\n        ls2 = LineString([(10, 10)])\n        lsoi = LineStringsOnImage([ls1, ls2], shape=(100, 100, 3))\n\n        observed = func(lsoi)\n\n        expected = \"LineStringsOnImage([%s, %s], shape=(100, 100, 3))\" % (\n            func(ls1), func(ls2)\n        )\n        assert observed == expected\n\n    @classmethod\n    def _test_str_repr_empty_list_of_line_strings(cls, func):\n        lsoi = LineStringsOnImage([], shape=(100, 100, 3))\n\n        observed = func(lsoi)\n\n        expected = \"LineStringsOnImage([], shape=(100, 100, 3))\"\n        assert observed == expected\n\n\ndef _coords_eq(ls, other):\n    return ls.coords_almost_equals(other, max_distance=1e-2)\n\n\ndef _drawing_allclose(arr, v):\n    # draw_points_heatmaps_array() and draw_line_heatmap_array() are\n    # currently limited to 1/255 accuracy due to drawing in uint8\n    return np.allclose(arr, v, atol=(1.01/255), rtol=0)\n"
  },
  {
    "path": "test/augmentables/test_normalization.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport six.moves as sm\n\nimport imgaug as ia\nimport imgaug.augmentables.normalization as normalization\nfrom imgaug.testutils import reseed\n\n\n# TODO split up tests here\n\nclass TestNormalization(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_invert_normalize_images(self):\n        assert normalization.invert_normalize_images(None, None) is None\n\n        arr = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n        arr_old = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n        observed = normalization.invert_normalize_images(arr, arr_old)\n        assert ia.is_np_array(observed)\n        assert observed.shape == (1, 4, 4, 3)\n        assert observed.dtype.name == \"uint8\"\n\n        arr = np.zeros((1, 4, 4, 1), dtype=np.uint8)\n        arr_old = np.zeros((4, 4), dtype=np.uint8)\n        observed = normalization.invert_normalize_images(arr, arr_old)\n        assert ia.is_np_array(observed)\n        assert observed.shape == (4, 4)\n        assert observed.dtype.name == \"uint8\"\n\n        arr = np.zeros((1, 4, 4, 1), dtype=np.uint8)\n        arr_old = np.zeros((1, 4, 4), dtype=np.uint8)\n        observed = normalization.invert_normalize_images(arr, arr_old)\n        assert ia.is_np_array(observed)\n        assert observed.shape == (1, 4, 4)\n        assert observed.dtype.name == \"uint8\"\n\n        images = []\n        images_old = []\n        observed = normalization.invert_normalize_images(images, images_old)\n        assert isinstance(observed, list)\n        assert len(observed) == 0\n\n        arr1 = np.zeros((4, 4, 1), dtype=np.uint8)\n        arr2 = np.zeros((5, 5, 3), dtype=np.uint8)\n        arr1_old = np.zeros((4, 4), dtype=np.uint8)\n        arr2_old = np.zeros((5, 5, 3), dtype=np.uint8)\n        observed = normalization.invert_normalize_images([arr1, arr2],\n                                                         [arr1_old, arr2_old])\n        assert isinstance(observed, list)\n        assert len(observed) == 2\n        assert ia.is_np_array(observed[0])\n        assert ia.is_np_array(observed[1])\n        assert observed[0].shape == (4, 4)\n        assert observed[1].shape == (5, 5, 3)\n        assert observed[0].dtype.name == \"uint8\"\n        assert observed[1].dtype.name == \"uint8\"\n\n        # ---------\n        # images turned to list during augmentation\n        # ---------\n        # different shapes, each 3D\n        images = [np.zeros((3, 4, 1), dtype=np.uint8),\n                  np.zeros((4, 3, 1), dtype=np.uint8)]\n        images_old = np.zeros((2, 4, 4, 1), dtype=np.uint8)\n        observed = normalization.invert_normalize_images(images, images_old)\n        assert isinstance(observed, list)\n        assert len(observed) == 2\n        assert observed[0] is images[0]\n        assert observed[1] is images[1]\n\n        # different shapes, each 2D\n        images = [np.zeros((3, 4, 1), dtype=np.uint8),\n                  np.zeros((4, 3, 1), dtype=np.uint8)]\n        images_old = np.zeros((2, 4, 4), dtype=np.uint8)\n        observed = normalization.invert_normalize_images(images, images_old)\n        assert isinstance(observed, list)\n        assert len(observed) == 2\n        assert observed[0].shape == (3, 4)\n        assert observed[1].shape == (4, 3)\n\n        # same shapes, each 3D\n        images = [np.zeros((3, 4, 1), dtype=np.uint8),\n                  np.zeros((3, 4, 1), dtype=np.uint8)]\n        images_old = np.zeros((2, 4, 4, 1), dtype=np.uint8)\n        observed = normalization.invert_normalize_images(images, images_old)\n        # assert ia.is_np_array(observed)\n        # assert observed.shape == (2, 3, 4, 1)\n        assert isinstance(observed, list)\n        assert len(observed) == 2\n        assert observed[0] is images[0]\n        assert observed[1] is images[1]\n\n        # same shapes, each 2D\n        images = [np.zeros((3, 4, 1), dtype=np.uint8),\n                  np.zeros((3, 4, 1), dtype=np.uint8)]\n        images_old = np.zeros((2, 4, 4), dtype=np.uint8)\n        observed = normalization.invert_normalize_images(images, images_old)\n        # assert ia.is_np_array(observed)\n        # assert observed.shape == (2, 3, 4)\n        assert isinstance(observed, list)\n        assert len(observed) == 2\n        assert observed[0].shape == (3, 4)\n        assert observed[1].shape == (3, 4)\n\n        # single item in list\n        images = [np.zeros((3, 4, 1), dtype=np.uint8)]\n        images_old = np.zeros((1, 4, 4), dtype=np.uint8)\n        observed = normalization.invert_normalize_images(images, images_old)\n        # assert ia.is_np_array(observed)\n        # assert observed.shape == (1, 3, 4)\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert observed[0].shape == (3, 4)\n\n        # single item in list, original was 2D\n        images = [np.zeros((3, 4, 1), dtype=np.uint8)]\n        images_old = np.zeros((4, 4), dtype=np.uint8)\n        observed = normalization.invert_normalize_images(images, images_old)\n        # assert ia.is_np_array(observed)\n        # assert observed.shape == (3, 4)\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert observed[0].shape == (3, 4)\n\n        with self.assertRaises(ValueError):\n            normalization.invert_normalize_images(False, False)\n\n    def test_invert_normalize_heatmaps(self):\n        def _norm_and_invert(heatmaps, images):\n            return normalization.invert_normalize_heatmaps(\n                normalization.normalize_heatmaps(heatmaps, shapes=images),\n                heatmaps\n            )\n\n        # ----\n        # None\n        # ----\n        observed = normalization.invert_normalize_heatmaps(None, None)\n        assert observed is None\n\n        # ----\n        # array\n        # ----\n        for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                       np.zeros((1, 1, 1, 3), dtype=np.uint8)]:\n            before = np.zeros((1, 1, 1, 1), dtype=np.float32) + 0.1\n            after = _norm_and_invert(before, images=images)\n            assert ia.is_np_array(after)\n            assert after.shape == (1, 1, 1, 1)\n            assert after.dtype.name == \"float32\"\n            assert np.allclose(after, before)\n\n        # ----\n        # single HeatmapsOnImage\n        # ----\n        before = ia.HeatmapsOnImage(\n                    np.zeros((1, 1, 1), dtype=np.float32) + 0.1,\n                    shape=(1, 1, 3))\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, ia.HeatmapsOnImage)\n        assert after.shape == before.shape\n        assert np.allclose(after.arr_0to1, before.arr_0to1)\n\n        # ----\n        # empty iterable\n        # ----\n        before = []\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, list)\n        assert len(after) == 0\n\n        # ----\n        # iterable of arrays\n        # ----\n        for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                       np.zeros((1, 1, 1, 3), dtype=np.uint8)]:\n            before = [np.zeros((1, 1, 1), dtype=np.float32) + 0.1]\n            after = _norm_and_invert(before, images=images)\n            assert isinstance(after, list)\n            assert len(after) == 1\n            assert after[0].shape == (1, 1, 1)\n            assert after[0].dtype.name == \"float32\"\n            assert np.allclose(after[0], before[0])\n\n        # ----\n        # iterable of HeatmapsOnImage\n        # ----\n        before = [ia.HeatmapsOnImage(\n                    np.zeros((1, 1, 1), dtype=np.float32) + 0.1,\n                    shape=(1, 1, 3))]\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, list)\n        assert isinstance(after[0], ia.HeatmapsOnImage)\n        assert after[0].shape == before[0].shape\n        assert np.allclose(after[0].arr_0to1, before[0].arr_0to1)\n\n    def test_invert_normalize_segmentation_maps(self):\n        def _norm_and_invert(segmaps, images):\n            return normalization.invert_normalize_segmentation_maps(\n                normalization.normalize_segmentation_maps(\n                    segmaps, shapes=images),\n                segmaps\n            )\n\n        # ----\n        # None\n        # ----\n        observed = normalization.invert_normalize_segmentation_maps(None, None)\n        assert observed is None\n\n        # ----\n        # array\n        # ----\n        for dt in [np.dtype(\"int32\"), np.dtype(\"uint16\"), np.dtype(bool)]:\n            for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                           np.zeros((1, 1, 3), dtype=np.uint8)]:\n                before = np.ones((1, 1, 1, 1), dtype=dt)\n                after = _norm_and_invert(before, images=images)\n                assert ia.is_np_array(after)\n                assert after.shape == (1, 1, 1, 1)\n                assert after.dtype.name == dt.name\n                assert np.array_equal(after, before)\n\n        # ----\n        # single SegmentationMapsOnImage\n        # ----\n        before = ia.SegmentationMapsOnImage(\n                     np.zeros((1, 1, 1), dtype=np.int32) + 1,\n                     shape=(1, 1, 3))\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, ia.SegmentationMapsOnImage)\n        assert after.shape == before.shape\n        assert np.array_equal(after.arr, before.arr)\n\n        # ----\n        # empty iterable\n        # ----\n        before = []\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, list)\n        assert len(after) == 0\n\n        # ----\n        # iterable of arrays\n        # ----\n        for dt in [np.dtype(\"int32\"), np.dtype(\"uint16\"), np.dtype(bool)]:\n            for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                           np.zeros((1, 1, 1, 3), dtype=np.uint8)]:\n                before = [np.ones((1, 1, 1), dtype=dt)]\n                after = _norm_and_invert(before, images=images)\n                assert isinstance(after, list)\n                assert len(after) == 1\n                assert after[0].shape == (1, 1, 1)\n                assert after[0].dtype.name == dt.name\n                assert np.array_equal(after[0], before[0])\n\n        # ----\n        # iterable of SegmentationMapsOnImage\n        # ----\n        before = [ia.SegmentationMapsOnImage(\n                    np.zeros((1, 1, 1), dtype=np.int32) + 1,\n                    shape=(1, 1, 3))]\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, list)\n        assert isinstance(after[0], ia.SegmentationMapsOnImage)\n        assert after[0].shape == before[0].shape\n        assert np.allclose(after[0].arr, before[0].arr)\n\n    def test_invert_normalize_keypoints(self):\n        def _norm_and_invert(kps, images):\n            return normalization.invert_normalize_keypoints(\n                normalization.normalize_keypoints(\n                    kps, shapes=images),\n                kps\n            )\n\n        # ----\n        # None\n        # ----\n        observed = normalization.invert_normalize_keypoints(None, None)\n        assert observed is None\n\n        # ----\n        # array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                           np.zeros((1, 1, 1, 3), dtype=np.uint8)]:\n                before = np.zeros((1, 1, 2), dtype=dt) + 1\n                after = _norm_and_invert(before, images=images)\n                assert ia.is_np_array(after)\n                assert after.shape == (1, 1, 2)\n                assert after.dtype.name == dt.name\n                assert np.allclose(after, 1)\n\n        # ----\n        # (x,y)\n        # ----\n        before = (1, 2)\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, tuple)\n        assert after == (1, 2)\n\n        # ----\n        # single Keypoint instance\n        # ----\n        before = ia.Keypoint(x=1, y=2)\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, ia.Keypoint)\n        assert after.x == 1\n        assert after.y == 2\n\n        # ----\n        # single KeypointsOnImage instance\n        # ----\n        before = ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)], shape=(1, 1, 3))\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, ia.KeypointsOnImage)\n        assert len(after.keypoints) == 1\n        assert after.keypoints[0].x == 1\n        assert after.keypoints[0].y == 2\n        assert after.shape == (1, 1, 3)\n\n        # ----\n        # empty iterable\n        # ----\n        before = []\n        after = _norm_and_invert(before, images=None)\n        assert after == []\n\n        # ----\n        # iterable of array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                           np.zeros((1, 1, 1, 3), dtype=np.uint8)]:\n                before = np.zeros((1, 1, 2), dtype=dt) + 1\n                after = _norm_and_invert(before, images=images)\n                assert ia.is_np_array(after)\n                assert after.shape == (1, 1, 2)\n                assert after.dtype.name == dt.name\n                assert np.allclose(after, 1)\n\n        # ----\n        # iterable of (x,y)\n        # ----\n        before = [(1, 2), (3, 4)]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert after == [(1, 2), (3, 4)]\n\n        # ----\n        # iterable of Keypoint\n        # ----\n        before = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert isinstance(after[0], ia.Keypoint)\n        assert isinstance(after[1], ia.Keypoint)\n        assert after[0].x == 1\n        assert after[0].y == 2\n        assert after[1].x == 3\n        assert after[1].y == 4\n\n        # ----\n        # iterable of KeypointsOnImage\n        # ----\n        before = [\n            ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)], shape=(1, 1, 3)),\n            ia.KeypointsOnImage([ia.Keypoint(x=3, y=4)], shape=(1, 1, 3)),\n        ]\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert isinstance(after[0], ia.KeypointsOnImage)\n        assert isinstance(after[1], ia.KeypointsOnImage)\n        assert after[0].keypoints[0].x == 1\n        assert after[0].keypoints[0].y == 2\n        assert after[1].keypoints[0].x == 3\n        assert after[1].keypoints[0].y == 4\n\n        # ----\n        # iterable of empty interables\n        # ----\n        before = [[]]\n        after = _norm_and_invert(before, [np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert after == [[]]\n\n        # ----\n        # iterable of iterable of (x,y)\n        # ----\n        before = [\n            [(1, 2), (3, 4)],\n            [(5, 6), (7, 8)]\n        ]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8),\n                                         np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert isinstance(after[0], list)\n        assert isinstance(after[1], list)\n        assert after[0][0][0] == 1\n        assert after[0][0][1] == 2\n        assert after[0][1][0] == 3\n        assert after[0][1][1] == 4\n        assert after[1][0][0] == 5\n        assert after[1][0][1] == 6\n        assert after[1][1][0] == 7\n        assert after[1][1][1] == 8\n\n        # ----\n        # iterable of iterable of Keypoint\n        # ----\n        before = [\n            [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)],\n            [ia.Keypoint(x=5, y=6), ia.Keypoint(x=7, y=8)]\n        ]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8),\n                                         np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert isinstance(after[0], list)\n        assert isinstance(after[1], list)\n        assert after[0][0].x == 1\n        assert after[0][0].y == 2\n        assert after[0][1].x == 3\n        assert after[0][1].y == 4\n        assert after[1][0].x == 5\n        assert after[1][0].y == 6\n        assert after[1][1].x == 7\n        assert after[1][1].y == 8\n\n    def test_invert_normalize_bounding_boxes(self):\n        def _norm_and_invert(bbs, images):\n            return normalization.invert_normalize_bounding_boxes(\n                normalization.normalize_bounding_boxes(\n                    bbs, shapes=images),\n                bbs\n            )\n\n        # ----\n        # None\n        # ----\n        observed = normalization.invert_normalize_bounding_boxes(None, None)\n        assert observed is None\n\n        # ----\n        # array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                           np.zeros((1, 1, 1, 3), dtype=np.uint8)]:\n                before = np.zeros((1, 1, 4), dtype=dt) + 1\n                after = _norm_and_invert(before, images=images)\n                assert ia.is_np_array(after)\n                assert after.shape == (1, 1, 4)\n                assert after.dtype.name == dt.name\n                assert np.allclose(after, 1)\n\n        # ----\n        # (x1,y1,x2,y2)\n        # ----\n        before = (1, 2, 3, 4)\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, tuple)\n        assert after == (1, 2, 3, 4)\n\n        # ----\n        # single BoundingBox instance\n        # ----\n        before = ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, ia.BoundingBox)\n        assert after.x1 == 1\n        assert after.y1 == 2\n        assert after.x2 == 3\n        assert after.y2 == 4\n\n        # ----\n        # single BoundingBoxesOnImage instance\n        # ----\n        before = ia.BoundingBoxesOnImage(\n                     [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)],\n                     shape=(1, 1, 3))\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, ia.BoundingBoxesOnImage)\n        assert len(after.bounding_boxes) == 1\n        assert after.bounding_boxes[0].x1 == 1\n        assert after.bounding_boxes[0].y1 == 2\n        assert after.bounding_boxes[0].x2 == 3\n        assert after.bounding_boxes[0].y2 == 4\n        assert after.shape == (1, 1, 3)\n\n        # ----\n        # empty iterable\n        # ----\n        before = []\n        after = _norm_and_invert(before, images=None)\n        assert after == []\n\n        # ----\n        # iterable of array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                           np.zeros((1, 1, 1, 3), dtype=np.uint8)]:\n                before = [np.zeros((1, 4), dtype=dt) + 1]\n                after = _norm_and_invert(before, images=images)\n                assert isinstance(after, list)\n                assert len(after) == 1\n                assert ia.is_np_array(after[0])\n                assert after[0].shape == (1, 4)\n                assert after[0].dtype.name == dt.name\n                assert np.allclose(after[0], 1)\n\n        # ----\n        # iterable of (x1,y1,x2,y2)\n        # ----\n        before = [(1, 2, 3, 4), (5, 6, 7, 8)]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert after == [(1, 2, 3, 4), (5, 6, 7, 8)]\n\n        # ----\n        # iterable of BoundingBox\n        # ----\n        before = [\n            ia.BoundingBox(x1=1, y1=2, x2=3, y2=4),\n            ia.BoundingBox(x1=5, y1=6, x2=7, y2=8)\n        ]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert isinstance(after[0], ia.BoundingBox)\n        assert isinstance(after[1], ia.BoundingBox)\n        assert after[0].x1 == 1\n        assert after[0].y1 == 2\n        assert after[0].x2 == 3\n        assert after[0].y2 == 4\n        assert after[1].x1 == 5\n        assert after[1].y1 == 6\n        assert after[1].x2 == 7\n        assert after[1].y2 == 8\n\n        # ----\n        # iterable of BoundingBoxesOnImage\n        # ----\n        before = [\n            ia.BoundingBoxesOnImage(\n                [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)],\n                shape=(1, 1, 3)),\n            ia.BoundingBoxesOnImage(\n                [ia.BoundingBox(x1=5, y1=6, x2=7, y2=8)],\n                shape=(1, 1, 3))\n        ]\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert isinstance(after[0], ia.BoundingBoxesOnImage)\n        assert isinstance(after[1], ia.BoundingBoxesOnImage)\n        assert isinstance(after[0].bounding_boxes[0], ia.BoundingBox)\n        assert isinstance(after[1].bounding_boxes[0], ia.BoundingBox)\n        assert after[0].bounding_boxes[0].x1 == 1\n        assert after[0].bounding_boxes[0].y1 == 2\n        assert after[0].bounding_boxes[0].x2 == 3\n        assert after[0].bounding_boxes[0].y2 == 4\n        assert after[1].bounding_boxes[0].x1 == 5\n        assert after[1].bounding_boxes[0].y1 == 6\n        assert after[1].bounding_boxes[0].x2 == 7\n        assert after[1].bounding_boxes[0].y2 == 8\n        assert after[0].shape == (1, 1, 3)\n        assert after[1].shape == (1, 1, 3)\n\n        # ----\n        # iterable of empty interables\n        # ----\n        before = [[]]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert after == [[]]\n\n        # ----\n        # iterable of iterable of (x1,y1,x2,y2)\n        # ----\n        before = [\n            [(1, 2, 3, 4)],\n            [(5, 6, 7, 8), (9, 10, 11, 12)]\n        ]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8),\n                                         np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert after == [\n            [(1, 2, 3, 4)],\n            [(5, 6, 7, 8), (9, 10, 11, 12)]\n        ]\n\n        # ----\n        # iterable of iterable of Keypoint\n        # ----\n        before = [\n            [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4),\n             ia.BoundingBox(x1=5, y1=6, x2=7, y2=8)],\n            [ia.BoundingBox(x1=9, y1=10, x2=11, y2=12),\n             ia.BoundingBox(x1=13, y1=14, x2=15, y2=16)]\n        ]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8),\n                                         np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert isinstance(after[0], list)\n        assert isinstance(after[1], list)\n        assert len(after[0]) == 2\n        assert len(after[1]) == 2\n        assert after[0][0].x1 == 1\n        assert after[0][0].y1 == 2\n        assert after[0][0].x2 == 3\n        assert after[0][0].y2 == 4\n        assert after[0][1].x1 == 5\n        assert after[0][1].y1 == 6\n        assert after[0][1].x2 == 7\n        assert after[0][1].y2 == 8\n        assert after[1][0].x1 == 9\n        assert after[1][0].y1 == 10\n        assert after[1][0].x2 == 11\n        assert after[1][0].y2 == 12\n        assert after[1][1].x1 == 13\n        assert after[1][1].y1 == 14\n        assert after[1][1].x2 == 15\n        assert after[1][1].y2 == 16\n\n    def test_invert_normalize_polygons(self):\n        def _norm_and_invert(polys, images):\n            return normalization.invert_normalize_polygons(\n                normalization.normalize_polygons(\n                    polys, shapes=images),\n                polys\n            )\n\n        coords1 = [(0, 0), (10, 0), (10, 10)]\n        coords2 = [(5, 5), (15, 5), (15, 15)]\n        coords3 = [(0, 0), (10, 0), (10, 10), (0, 10)]\n        coords4 = [(5, 5), (15, 5), (15, 15), (5, 15)]\n\n        coords1_kps = [ia.Keypoint(x=x, y=y) for x, y in coords1]\n        coords2_kps = [ia.Keypoint(x=x, y=y) for x, y in coords2]\n        coords3_kps = [ia.Keypoint(x=x, y=y) for x, y in coords3]\n        coords4_kps = [ia.Keypoint(x=x, y=y) for x, y in coords4]\n\n        coords1_arr = np.float32(coords1)\n        coords2_arr = np.float32(coords2)\n        coords3_arr = np.float32(coords3)\n        coords4_arr = np.float32(coords4)\n\n        # ----\n        # None\n        # ----\n        observed = normalization.invert_normalize_polygons(None, None)\n        assert observed is None\n\n        # ----\n        # array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                           np.zeros((1, 1, 1, 3), dtype=np.uint8)]:\n                before = coords1_arr[np.newaxis, np.newaxis, ...].astype(dt)\n                after = _norm_and_invert(before, images=images)\n                assert ia.is_np_array(after)\n                assert after.shape == (1, 1, 3, 2)\n                assert after.dtype.name == dt.name\n                assert np.allclose(after,\n                                   coords1_arr[np.newaxis, np.newaxis, ...])\n\n                before = np.tile(\n                    coords1_arr[np.newaxis, np.newaxis, ...].astype(dt),\n                    (1, 5, 1, 1)\n                )\n                after = _norm_and_invert(before, images=images)\n                assert ia.is_np_array(after)\n                assert after.shape == (1, 5, 3, 2)\n                assert after.dtype.name == dt.name\n                assert np.allclose(after[0],\n                                   coords1_arr[np.newaxis, ...])\n\n        # ----\n        # single Polygon instance\n        # ----\n        before = ia.Polygon(coords1)\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, ia.Polygon)\n        assert after.exterior_almost_equals(coords1)\n\n        # ----\n        # single PolygonsOnImage instance\n        # ----\n        before = ia.PolygonsOnImage([ia.Polygon(coords1)], shape=(1, 1, 3))\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, ia.PolygonsOnImage)\n        assert len(after.polygons) == 1\n        assert after.polygons[0].exterior_almost_equals(coords1)\n        assert after.shape == (1, 1, 3)\n\n        # ----\n        # empty iterable\n        # ----\n        before = []\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, list)\n        assert after == []\n\n        # ----\n        # iterable of array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                           np.zeros((1, 1, 1, 3), dtype=np.uint8)]:\n                before = [coords1_arr[np.newaxis, ...].astype(dt)]\n                after = _norm_and_invert(before, images=images)\n                assert isinstance(after, list)\n                assert len(after) == 1\n                assert ia.is_np_array(after[0])\n                assert after[0].shape == (1, 3, 2)\n                assert after[0].dtype.name == dt.name\n                assert np.allclose(after[0], coords1_arr[np.newaxis, ...])\n\n                before = [np.tile(\n                    coords1_arr[np.newaxis, ...].astype(dt),\n                    (5, 1, 1)\n                )]\n                after = _norm_and_invert(before, images=images)\n                assert isinstance(after, list)\n                assert len(after) == 1\n                assert ia.is_np_array(after[0])\n                assert after[0].shape == (5, 3, 2)\n                assert after[0].dtype.name == dt.name\n                assert np.allclose(after[0][0], coords1_arr)\n\n        # ----\n        # iterable of (x,y)\n        # ----\n        before = coords1\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert after == coords1\n\n        # ----\n        # iterable of Keypoint\n        # ----\n        before = coords1_kps\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert len(after) == len(coords1_kps)\n        assert all([kp_after.x == kp_before.x and kp_after.y == kp_before.y\n                    for kp_after, kp_before in zip(after, coords1_kps)])\n\n        # ----\n        # iterable of Polygon\n        # ----\n        before = [ia.Polygon(coords1), ia.Polygon(coords2)]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert after[0].exterior_almost_equals(coords1)\n        assert after[1].exterior_almost_equals(coords2)\n\n        # ----\n        # iterable of PolygonsOnImage\n        # ----\n        before = [\n            ia.PolygonsOnImage([ia.Polygon(coords1)], shape=(1, 1, 3)),\n            ia.PolygonsOnImage([ia.Polygon(coords2)], shape=(2, 1, 3))\n        ]\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert isinstance(after[0], ia.PolygonsOnImage)\n        assert isinstance(after[1], ia.PolygonsOnImage)\n        assert after[0].polygons[0].exterior_almost_equals(coords1)\n        assert after[1].polygons[0].exterior_almost_equals(coords2)\n        assert after[0].shape == (1, 1, 3)\n        assert after[1].shape == (2, 1, 3)\n\n        # ----\n        # iterable of empty interables\n        # ----\n        before = [[]]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert after == [[]]\n\n        # ----\n        # iterable of iterable of array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                           np.zeros((1, 1, 1, 3), dtype=np.uint8)]:\n                before = [[coords1_arr.astype(dt)]]\n                after = _norm_and_invert(before, images=images)\n                assert isinstance(after, list)\n                assert len(after) == 1\n                assert isinstance(after[0], list)\n                assert len(after[0]) == 1\n                assert ia.is_np_array(after[0][0])\n                assert after[0][0].shape == (3, 2)\n                assert after[0][0].dtype.name == dt.name\n                assert np.allclose(after[0][0], coords1_arr)\n\n                before = [[coords1_arr.astype(dt) for _ in sm.xrange(5)]]\n                after = _norm_and_invert(before, images=images)\n                assert isinstance(after, list)\n                assert len(after) == 1\n                assert isinstance(after[0], list)\n                assert len(after[0]) == 5\n                assert ia.is_np_array(after[0][0])\n                assert after[0][0].shape == (3, 2)\n                assert after[0][0].dtype.name == dt.name\n                assert np.allclose(after[0][0], coords1_arr)\n\n        # ----\n        # iterable of iterable of (x,y)\n        # ----\n        before = [coords1, coords2]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert after[0] == coords1\n        assert after[1] == coords2\n\n        # ----\n        # iterable of iterable of Keypoint\n        # ----\n        before = [coords1_kps, coords2_kps]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert len(after[0]) == len(coords1_kps)\n        assert len(after[1]) == len(coords2_kps)\n        assert all([kp_after.x == kp_before.x and kp_after.y == kp_before.y\n                    for kp_after, kp_before in zip(after[0], coords1_kps)])\n        assert all([kp_after.x == kp_before.x and kp_after.y == kp_before.y\n                    for kp_after, kp_before in zip(after[1], coords2_kps)])\n\n        # ----\n        # iterable of iterable of Polygon\n        # ----\n        before = [\n            [ia.Polygon(coords1), ia.Polygon(coords2)],\n            [ia.Polygon(coords3), ia.Polygon(coords4)]\n        ]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8),\n                                         np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert isinstance(after[0], list)\n        assert isinstance(after[1], list)\n        assert len(after[0]) == 2\n        assert len(after[1]) == 2\n        assert after[0][0].exterior_almost_equals(coords1)\n        assert after[0][1].exterior_almost_equals(coords2)\n        assert after[1][0].exterior_almost_equals(coords3)\n        assert after[1][1].exterior_almost_equals(coords4)\n\n        # ----\n        # iterable of iterable of empty iterable\n        # ----\n        before = [[[]]]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert after == [[[]]]\n\n        # ----\n        # iterable of iterable of iterable of (x,y)\n        # ----\n        before = [[coords1, coords2], [coords3, coords4]]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8),\n                                         np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert len(after[0]) == 2\n        assert len(after[1]) == 2\n        assert after[0][0] == coords1\n        assert after[0][1] == coords2\n        assert after[1][0] == coords3\n        assert after[1][1] == coords4\n\n        # ----\n        # iterable of iterable of iterable of Keypoint\n        # ----\n        before = [[coords1_kps, coords2_kps], [coords3_kps, coords4_kps]]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8),\n                                         np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert len(after[0]) == 2\n        assert len(after[1]) == 2\n        assert all([kp_after.x == kp_before.x and kp_after.y == kp_before.y\n                    for kp_after, kp_before in zip(after[0][0], coords1_kps)])\n        assert all([kp_after.x == kp_before.x and kp_after.y == kp_before.y\n                    for kp_after, kp_before in zip(after[0][1], coords2_kps)])\n        assert all([kp_after.x == kp_before.x and kp_after.y == kp_before.y\n                    for kp_after, kp_before in zip(after[1][0], coords3_kps)])\n        assert all([kp_after.x == kp_before.x and kp_after.y == kp_before.y\n                    for kp_after, kp_before in zip(after[1][1], coords4_kps)])\n\n    # The underlying normalization functions are mostly identical for\n    # LineStrings and Polygons, hence we run only a few tests for LineStrings\n    # here. Most of the code was already tested for Polygons.\n    def test_invert_normalize_line_strings(self):\n        def _norm_and_invert(line_strings, images):\n            return normalization.invert_normalize_line_strings(\n                normalization.normalize_line_strings(\n                    line_strings, shapes=images),\n                line_strings\n            )\n\n        coords1 = [(0, 0), (10, 0), (10, 10)]\n        coords2 = [(5, 5), (15, 5), (15, 15)]\n        coords3 = [(0, 0), (10, 0), (10, 10), (0, 10)]\n        coords4 = [(5, 5), (15, 5), (15, 15), (5, 15)]\n\n        coords1_arr = np.float32(coords1)\n\n        # ----\n        # None\n        # ----\n        observed = normalization.invert_normalize_line_strings(None, None)\n        assert observed is None\n\n        # ----\n        # single LineString instance\n        # ----\n        before = ia.LineString(coords1)\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, ia.LineString)\n        assert np.allclose(after.coords, coords1)\n\n        # ----\n        # single LineStringsOnImage instance\n        # ----\n        before = ia.LineStringsOnImage([ia.LineString(coords1)], shape=(1, 1, 3))\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, ia.LineStringsOnImage)\n        assert len(after.line_strings) == 1\n        assert np.allclose(after.line_strings[0].coords, coords1)\n        assert after.shape == (1, 1, 3)\n\n        # ----\n        # iterable of LineStringsOnImage\n        # ----\n        before = [\n            ia.LineStringsOnImage([ia.LineString(coords1)], shape=(1, 1, 3)),\n            ia.LineStringsOnImage([ia.LineString(coords2)], shape=(2, 1, 3))\n        ]\n        after = _norm_and_invert(before, images=None)\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert isinstance(after[0], ia.LineStringsOnImage)\n        assert isinstance(after[1], ia.LineStringsOnImage)\n        assert np.allclose(after[0].line_strings[0].coords, coords1)\n        assert np.allclose(after[1].line_strings[0].coords, coords2)\n        assert after[0].shape == (1, 1, 3)\n        assert after[1].shape == (2, 1, 3)\n\n        # ----\n        # iterable of iterable of array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            for images in [[np.zeros((1, 1, 3), dtype=np.uint8)],\n                           np.zeros((1, 1, 1, 3), dtype=np.uint8)]:\n                before = [[coords1_arr.astype(dt)]]\n                after = _norm_and_invert(before, images=images)\n                assert isinstance(after, list)\n                assert len(after) == 1\n                assert isinstance(after[0], list)\n                assert len(after[0]) == 1\n                assert ia.is_np_array(after[0][0])\n                assert after[0][0].shape == (3, 2)\n                assert after[0][0].dtype.name == dt.name\n                assert np.allclose(after[0][0], coords1_arr)\n\n                before = [[coords1_arr.astype(dt) for _ in sm.xrange(5)]]\n                after = _norm_and_invert(before, images=images)\n                assert isinstance(after, list)\n                assert len(after) == 1\n                assert isinstance(after[0], list)\n                assert len(after[0]) == 5\n                assert ia.is_np_array(after[0][0])\n                assert after[0][0].shape == (3, 2)\n                assert after[0][0].dtype.name == dt.name\n                assert np.allclose(after[0][0], coords1_arr)\n\n        # ----\n        # iterable of iterable of LineString\n        # ----\n        before = [\n            [ia.LineString(coords1), ia.LineString(coords2)],\n            [ia.LineString(coords3), ia.LineString(coords4)]\n        ]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8),\n                                         np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert isinstance(after[0], list)\n        assert isinstance(after[1], list)\n        assert len(after[0]) == 2\n        assert len(after[1]) == 2\n        assert np.allclose(after[0][0].coords, coords1)\n        assert np.allclose(after[0][1].coords, coords2)\n        assert np.allclose(after[1][0].coords, coords3)\n        assert np.allclose(after[1][1].coords, coords4)\n\n        # ----\n        # iterable of iterable of iterable of (x,y)\n        # ----\n        before = [[coords1, coords2], [coords3, coords4]]\n        after = _norm_and_invert(before,\n                                 images=[np.zeros((1, 1, 3), dtype=np.uint8),\n                                         np.zeros((1, 1, 3), dtype=np.uint8)])\n        assert isinstance(after, list)\n        assert len(after) == 2\n        assert len(after[0]) == 2\n        assert len(after[1]) == 2\n        assert after[0][0] == coords1\n        assert after[0][1] == coords2\n        assert after[1][0] == coords3\n        assert after[1][1] == coords4\n\n    def test_normalize_images(self):\n        assert normalization.normalize_images(None) is None\n\n        arr = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n        observed = normalization.normalize_images(arr)\n        assert ia.is_np_array(observed)\n        assert observed.shape == (1, 4, 4, 3)\n        assert observed.dtype.name == \"uint8\"\n\n        arr = np.zeros((1, 4, 4), dtype=np.uint8)\n        observed = normalization.normalize_images(arr)\n        assert ia.is_np_array(observed)\n        assert observed.shape == (1, 4, 4, 1)\n        assert observed.dtype.name == \"uint8\"\n\n        arr = np.zeros((4, 4), dtype=np.uint8)\n        observed = normalization.normalize_images(arr)\n        assert ia.is_np_array(observed)\n        assert observed.shape == (1, 4, 4, 1)\n        assert observed.dtype.name == \"uint8\"\n\n        observed = normalization.normalize_images([])\n        assert isinstance(observed, list)\n        assert len(observed) == 0\n\n        arr1 = np.zeros((4, 4), dtype=np.uint8)\n        arr2 = np.zeros((5, 5, 3), dtype=np.uint8)\n        observed = normalization.normalize_images([arr1, arr2])\n        assert isinstance(observed, list)\n        assert len(observed) == 2\n        assert ia.is_np_array(observed[0])\n        assert ia.is_np_array(observed[1])\n        assert observed[0].shape == (4, 4, 1)\n        assert observed[1].shape == (5, 5, 3)\n        assert observed[0].dtype.name == \"uint8\"\n        assert observed[1].dtype.name == \"uint8\"\n\n        with self.assertRaises(ValueError):\n            normalization.normalize_images(False)\n\n    def test_normalize_heatmaps(self):\n        # ----\n        # None\n        # ----\n        heatmaps_norm = normalization.normalize_heatmaps(None)\n        assert heatmaps_norm is None\n\n        # ----\n        # array\n        # ----\n        heatmaps_norm = normalization.normalize_heatmaps(\n            np.zeros((1, 1, 1, 1), dtype=np.float32) + 0.1,\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(heatmaps_norm, list)\n        assert isinstance(heatmaps_norm[0], ia.HeatmapsOnImage)\n        assert np.allclose(heatmaps_norm[0].arr_0to1, 0 + 0.1)\n\n        heatmaps_norm = normalization.normalize_heatmaps(\n            np.zeros((1, 1, 1, 1), dtype=np.float32) + 0.1,\n            shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n        )\n        assert isinstance(heatmaps_norm, list)\n        assert isinstance(heatmaps_norm[0], ia.HeatmapsOnImage)\n        assert np.allclose(heatmaps_norm[0].arr_0to1, 0 + 0.1)\n\n        # --> heatmaps for too many images\n        with self.assertRaises(ValueError):\n            _heatmaps_norm = normalization.normalize_heatmaps(\n                np.zeros((2, 1, 1, 1), dtype=np.float32) + 0.1,\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # --> too few heatmaps\n        with self.assertRaises(ValueError):\n            _heatmaps_norm = normalization.normalize_heatmaps(\n                np.zeros((1, 1, 1, 1), dtype=np.float32) + 0.1,\n                np.zeros((2, 1, 1, 3), dtype=np.uint8)\n            )\n\n        # --> wrong channel number\n        with self.assertRaises(ValueError):\n            _heatmaps_norm = normalization.normalize_heatmaps(\n                np.zeros((1, 1, 1), dtype=np.float32) + 0.1,\n                shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n            )\n\n        # --> images None\n        with self.assertRaises(ValueError):\n            _heatmaps_norm = normalization.normalize_heatmaps(\n                np.zeros((1, 1, 1, 1), dtype=np.float32) + 0.1,\n                shapes=None\n            )\n\n        # ----\n        # single HeatmapsOnImage\n        # ----\n        heatmaps_norm = normalization.normalize_heatmaps(\n            ia.HeatmapsOnImage(\n                np.zeros((1, 1, 1), dtype=np.float32) + 0.1,\n                shape=(1, 1, 3)),\n            shapes=None\n        )\n        assert isinstance(heatmaps_norm, list)\n        assert isinstance(heatmaps_norm[0], ia.HeatmapsOnImage)\n        assert np.allclose(heatmaps_norm[0].arr_0to1, 0 + 0.1)\n\n        # ----\n        # empty iterable\n        # ----\n        heatmaps_norm = normalization.normalize_heatmaps(\n            [],\n            shapes=None\n        )\n        assert heatmaps_norm is None\n\n        # ----\n        # iterable of arrays\n        # ----\n        heatmaps_norm = normalization.normalize_heatmaps(\n            [np.zeros((1, 1, 1), dtype=np.float32) + 0.1],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(heatmaps_norm, list)\n        assert isinstance(heatmaps_norm[0], ia.HeatmapsOnImage)\n        assert np.allclose(heatmaps_norm[0].arr_0to1, 0 + 0.1)\n\n        heatmaps_norm = normalization.normalize_heatmaps(\n            [np.zeros((1, 1, 1), dtype=np.float32) + 0.1],\n            shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n        )\n        assert isinstance(heatmaps_norm, list)\n        assert isinstance(heatmaps_norm[0], ia.HeatmapsOnImage)\n        assert np.allclose(heatmaps_norm[0].arr_0to1, 0 + 0.1)\n\n        # --> heatmaps for too many images\n        with self.assertRaises(ValueError):\n            _heatmaps_norm = normalization.normalize_heatmaps(\n                [\n                    np.zeros((1, 1, 1), dtype=np.float32) + 0.1,\n                    np.zeros((1, 1, 1), dtype=np.float32) + 0.1\n                ],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # --> too few heatmaps\n        with self.assertRaises(ValueError):\n            _heatmaps_norm = normalization.normalize_heatmaps(\n                [np.zeros((1, 1, 1), dtype=np.float32) + 0.1],\n                shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n            )\n\n        # --> images None\n        with self.assertRaises(ValueError):\n            _heatmaps_norm = normalization.normalize_heatmaps(\n                [np.zeros((1, 1, 1), dtype=np.float32) + 0.1],\n                shapes=None,\n            )\n\n        # --> wrong number of dimensions\n        with self.assertRaises(ValueError):\n            _heatmaps_norm = normalization.normalize_heatmaps(\n                [np.zeros((1, 1, 1, 1), dtype=np.float32) + 0.1],\n                shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n            )\n\n        # ----\n        # iterable of HeatmapsOnImage\n        # ----\n        heatmaps_norm = normalization.normalize_heatmaps(\n            [ia.HeatmapsOnImage(\n                np.zeros((1, 1, 1), dtype=np.float32) + 0.1,\n                shape=(1, 1, 3))],\n            shapes=None\n        )\n        assert isinstance(heatmaps_norm, list)\n        assert isinstance(heatmaps_norm[0], ia.HeatmapsOnImage)\n        assert np.allclose(heatmaps_norm[0].arr_0to1, 0 + 0.1)\n    \n    def test_normalize_segmentation_maps(self):\n        # ----\n        # None\n        # ----\n        segmaps_norm = normalization.normalize_segmentation_maps(None)\n        assert segmaps_norm is None\n\n        # ----\n        # array\n        # ----\n        for dt in [np.dtype(\"int32\"), np.dtype(\"uint16\"), np.dtype(bool)]:\n            # NOTE: use np.full(shape, 1, dtype=dt) here and below instead of\n            # np.zeros(shape, dtype=dt) + 1, because the latter one converts\n            # dtype bool_ to int64.\n            segmaps_norm = normalization.normalize_segmentation_maps(\n                np.full((1, 1, 1, 1), 1, dtype=dt),\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n            assert isinstance(segmaps_norm, list)\n            assert isinstance(segmaps_norm[0], ia.SegmentationMapsOnImage)\n            assert np.allclose(segmaps_norm[0].arr[..., 0], 1)\n\n            segmaps_norm = normalization.normalize_segmentation_maps(\n                np.full((1, 1, 1, 1), 1, dtype=dt),\n                shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n            )\n            assert isinstance(segmaps_norm, list)\n            assert isinstance(segmaps_norm[0], ia.SegmentationMapsOnImage)\n            assert np.allclose(segmaps_norm[0].arr[..., 0], 1)\n\n            # --> segmaps for too many images\n            with self.assertRaises(ValueError):\n                _segmaps_norm = normalization.normalize_segmentation_maps(\n                    np.full((2, 1, 1), 1, dtype=dt),\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n            # --> too few segmaps\n            with self.assertRaises(ValueError):\n                _segmaps_norm = normalization.normalize_segmentation_maps(\n                    np.full((1, 1, 1), 1, dtype=dt),\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n                )\n\n            # --> images None\n            with self.assertRaises(ValueError):\n                _segmaps_norm = normalization.normalize_segmentation_maps(\n                    np.full((1, 1, 1), 1, dtype=dt),\n                    shapes=None\n                )\n\n        # ----\n        # single SegmentationMapsOnImage\n        # ----\n        segmaps_norm = normalization.normalize_segmentation_maps(\n            ia.SegmentationMapsOnImage(\n                np.full((1, 1, 1), 1, dtype=np.int32),\n                shape=(1, 1, 3)),\n            shapes=None\n        )\n        assert isinstance(segmaps_norm, list)\n        assert isinstance(segmaps_norm[0], ia.SegmentationMapsOnImage)\n        assert np.allclose(segmaps_norm[0].arr[..., 0], 0 + 1)\n\n        # ----\n        # empty iterable\n        # ----\n        segmaps_norm = normalization.normalize_segmentation_maps(\n            [], shapes=None\n        )\n        assert segmaps_norm is None\n\n        # ----\n        # iterable of arrays\n        # ----\n        for dt in [np.dtype(\"int32\"), np.dtype(\"uint16\"), np.dtype(bool)]:\n            segmaps_norm = normalization.normalize_segmentation_maps(\n                [np.full((1, 1, 1), 1, dtype=dt)],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n            assert isinstance(segmaps_norm, list)\n            assert isinstance(segmaps_norm[0], ia.SegmentationMapsOnImage)\n            assert np.allclose(segmaps_norm[0].arr[..., 0], 1)\n\n            segmaps_norm = normalization.normalize_segmentation_maps(\n                [np.full((1, 1, 1), 1, dtype=dt)],\n                shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n            )\n            assert isinstance(segmaps_norm, list)\n            assert isinstance(segmaps_norm[0], ia.SegmentationMapsOnImage)\n            assert np.allclose(segmaps_norm[0].arr[..., 0], 1)\n\n            # --> segmaps for too many images\n            with self.assertRaises(ValueError):\n                _segmaps_norm = normalization.normalize_segmentation_maps(\n                    [\n                        np.full((1, 1, 1), 1, dtype=np.int32),\n                        np.full((1, 1, 1), 1, dtype=np.int32)\n                    ],\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n            # --> too few segmaps\n            with self.assertRaises(ValueError):\n                _segmaps_norm = normalization.normalize_segmentation_maps(\n                    [np.full((1, 1, 1), 1, dtype=np.int32)],\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n                )\n\n            # --> images None\n            with self.assertRaises(ValueError):\n                _segmaps_norm = normalization.normalize_segmentation_maps(\n                    [np.full((1, 1, 1), 1, dtype=np.int32)],\n                    shapes=None\n                )\n\n            # --> wrong number of dimensions\n            with self.assertRaises(ValueError):\n                _segmaps_norm = normalization.normalize_segmentation_maps(\n                    [np.full((1, 1, 1, 1), 1, dtype=np.int32)],\n                    shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n                )\n\n        # ----\n        # iterable of SegmentationMapsOnImage\n        # ----\n        segmaps_norm = normalization.normalize_segmentation_maps(\n            [ia.SegmentationMapsOnImage(\n                np.full((1, 1, 1), 1, dtype=np.int32),\n                shape=(1, 1, 3))],\n            shapes=None\n        )\n        assert isinstance(segmaps_norm, list)\n        assert isinstance(segmaps_norm[0], ia.SegmentationMapsOnImage)\n        assert np.allclose(segmaps_norm[0].arr[..., 0], 1)\n\n    def test_normalize_keypoints(self):\n        def _assert_single_image_expected(inputs):\n            # --> images None\n            with self.assertRaises(ValueError):\n                _keypoints_norm = normalization.normalize_keypoints(\n                    inputs, None)\n\n            # --> too many images\n            with self.assertRaises(ValueError):\n                _keypoints_norm = normalization.normalize_keypoints(\n                    inputs,\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n                )\n\n            # --> too many images\n            with self.assertRaises(ValueError):\n                _keypoints_norm = normalization.normalize_keypoints(\n                    inputs,\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                            np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n        # ----\n        # None\n        # ----\n        keypoints_norm = normalization.normalize_keypoints(None)\n        assert keypoints_norm is None\n\n        # ----\n        # array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            keypoints_norm = normalization.normalize_keypoints(\n                np.zeros((1, 1, 2), dtype=dt) + 1,\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n            assert isinstance(keypoints_norm, list)\n            assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n            assert len(keypoints_norm[0].keypoints) == 1\n            assert np.allclose(keypoints_norm[0].to_xy_array(), 1)\n\n            keypoints_norm = normalization.normalize_keypoints(\n                np.zeros((1, 5, 2), dtype=dt) + 1,\n                shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n            )\n            assert isinstance(keypoints_norm, list)\n            assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n            assert len(keypoints_norm[0].keypoints) == 5\n            assert np.allclose(keypoints_norm[0].to_xy_array(), 1)\n\n            # --> keypoints for too many images\n            with self.assertRaises(ValueError):\n                _keypoints_norm = normalization.normalize_keypoints(\n                    np.zeros((2, 1, 2), dtype=dt) + 1,\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n            # --> too few keypoints\n            with self.assertRaises(ValueError):\n                _keypoints_norm = normalization.normalize_keypoints(\n                    np.zeros((1, 1, 2), dtype=dt) + 1,\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n                )\n\n            # --> wrong keypoints shape\n            with self.assertRaises(ValueError):\n                _keypoints_norm = normalization.normalize_keypoints(\n                    np.zeros((1, 1, 100), dtype=dt) + 1,\n                    shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n                )\n\n            _assert_single_image_expected(np.zeros((1, 1, 2), dtype=dt) + 1)\n\n        # ----\n        # (x,y)\n        # ----\n        keypoints_norm = normalization.normalize_keypoints(\n            (1, 2),\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(keypoints_norm, list)\n        assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n        assert len(keypoints_norm[0].keypoints) == 1\n        assert keypoints_norm[0].keypoints[0].x == 1\n        assert keypoints_norm[0].keypoints[0].y == 2\n\n        _assert_single_image_expected((1, 2))\n\n        # ----\n        # single Keypoint instance\n        # ----\n        keypoints_norm = normalization.normalize_keypoints(\n            ia.Keypoint(x=1, y=2),\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(keypoints_norm, list)\n        assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n        assert len(keypoints_norm[0].keypoints) == 1\n        assert keypoints_norm[0].keypoints[0].x == 1\n        assert keypoints_norm[0].keypoints[0].y == 2\n\n        _assert_single_image_expected(ia.Keypoint(x=1, y=2))\n\n        # ----\n        # single KeypointsOnImage instance\n        # ----\n        keypoints_norm = normalization.normalize_keypoints(\n            ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)], shape=(1, 1, 3)),\n            shapes=None\n        )\n        assert isinstance(keypoints_norm, list)\n        assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n        assert len(keypoints_norm[0].keypoints) == 1\n        assert keypoints_norm[0].keypoints[0].x == 1\n        assert keypoints_norm[0].keypoints[0].y == 2\n\n        # ----\n        # empty iterable\n        # ----\n        keypoints_norm = normalization.normalize_keypoints(\n            [], shapes=None\n        )\n        assert keypoints_norm is None\n\n        # ----\n        # iterable of array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            keypoints_norm = normalization.normalize_keypoints(\n                [np.zeros((1, 2), dtype=dt) + 1],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n            assert isinstance(keypoints_norm, list)\n            assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n            assert len(keypoints_norm[0].keypoints) == 1\n            assert np.allclose(keypoints_norm[0].to_xy_array(), 1)\n\n            keypoints_norm = normalization.normalize_keypoints(\n                [np.zeros((5, 2), dtype=dt) + 1],\n                shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n            )\n            assert isinstance(keypoints_norm, list)\n            assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n            assert len(keypoints_norm[0].keypoints) == 5\n            assert np.allclose(keypoints_norm[0].to_xy_array(), 1)\n\n            # --> keypoints for too many images\n            with self.assertRaises(ValueError):\n                _keypoints_norm = normalization.normalize_keypoints(\n                    [\n                        np.zeros((1, 2), dtype=dt) + 1,\n                        np.zeros((1, 2), dtype=dt) + 1\n                    ],\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n            # --> too few keypoints\n            with self.assertRaises(ValueError):\n                _keypoints_norm = normalization.normalize_keypoints(\n                    [np.zeros((1, 2), dtype=dt) + 1],\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n                )\n\n            # --> images None\n            with self.assertRaises(ValueError):\n                _keypoints_norm = normalization.normalize_keypoints(\n                    [np.zeros((1, 2), dtype=dt) + 1],\n                    shapes=None\n                )\n\n            # --> wrong shape\n            with self.assertRaises(ValueError):\n                _keypoints_norm = normalization.normalize_keypoints(\n                    [np.zeros((1, 100), dtype=dt) + 1],\n                    shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n                )\n\n        # ----\n        # iterable of (x,y)\n        # ----\n        keypoints_norm = normalization.normalize_keypoints(\n            [(1, 2), (3, 4)],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(keypoints_norm, list)\n        assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n        assert len(keypoints_norm[0].keypoints) == 2\n        assert keypoints_norm[0].keypoints[0].x == 1\n        assert keypoints_norm[0].keypoints[0].y == 2\n        assert keypoints_norm[0].keypoints[1].x == 3\n        assert keypoints_norm[0].keypoints[1].y == 4\n\n        # may only be used for single images\n        with self.assertRaises(ValueError):\n            _keypoints_norm = normalization.normalize_keypoints(\n                [(1, 2)],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of Keypoint\n        # ----\n        keypoints_norm = normalization.normalize_keypoints(\n            [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(keypoints_norm, list)\n        assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n        assert len(keypoints_norm[0].keypoints) == 2\n        assert keypoints_norm[0].keypoints[0].x == 1\n        assert keypoints_norm[0].keypoints[0].y == 2\n        assert keypoints_norm[0].keypoints[1].x == 3\n        assert keypoints_norm[0].keypoints[1].y == 4\n\n        # may only be used for single images\n        with self.assertRaises(ValueError):\n            _keypoints_norm = normalization.normalize_keypoints(\n                [ia.Keypoint(x=1, y=2)],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of KeypointsOnImage\n        # ----\n        keypoints_norm = normalization.normalize_keypoints(\n            [\n                ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)], shape=(1, 1, 3)),\n                ia.KeypointsOnImage([ia.Keypoint(x=3, y=4)], shape=(1, 1, 3)),\n            ],\n            shapes=None\n        )\n        assert isinstance(keypoints_norm, list)\n\n        assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n        assert len(keypoints_norm[0].keypoints) == 1\n        assert keypoints_norm[0].keypoints[0].x == 1\n        assert keypoints_norm[0].keypoints[0].y == 2\n\n        assert isinstance(keypoints_norm[1], ia.KeypointsOnImage)\n        assert len(keypoints_norm[1].keypoints) == 1\n        assert keypoints_norm[1].keypoints[0].x == 3\n        assert keypoints_norm[1].keypoints[0].y == 4\n\n        # ----\n        # iterable of empty interables\n        # ----\n        keypoints_norm = normalization.normalize_keypoints(\n            [[]],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert keypoints_norm is None\n\n        # ----\n        # iterable of iterable of (x,y)\n        # ----\n        keypoints_norm = normalization.normalize_keypoints(\n            [\n                [(1, 2), (3, 4)],\n                [(5, 6), (7, 8)]\n            ],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                    np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(keypoints_norm, list)\n        assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n        assert len(keypoints_norm[0].keypoints) == 2\n        assert keypoints_norm[0].keypoints[0].x == 1\n        assert keypoints_norm[0].keypoints[0].y == 2\n        assert keypoints_norm[0].keypoints[1].x == 3\n        assert keypoints_norm[0].keypoints[1].y == 4\n\n        assert len(keypoints_norm[1].keypoints) == 2\n        assert keypoints_norm[1].keypoints[0].x == 5\n        assert keypoints_norm[1].keypoints[0].y == 6\n        assert keypoints_norm[1].keypoints[1].x == 7\n        assert keypoints_norm[1].keypoints[1].y == 8\n\n        # --> images None\n        with self.assertRaises(ValueError):\n            _keypoints_norm = normalization.normalize_keypoints(\n                [\n                    [(1, 2), (3, 4)],\n                    [(5, 6), (7, 8)]\n                ],\n                shapes=None\n            )\n\n        # --> different number of images\n        with self.assertRaises(ValueError):\n            _keypoints_norm = normalization.normalize_keypoints(\n                [\n                    [(1, 2), (3, 4)],\n                    [(5, 6), (7, 8)]\n                ],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of iterable of Keypoint\n        # ----\n        keypoints_norm = normalization.normalize_keypoints(\n            [\n                [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)],\n                [ia.Keypoint(x=5, y=6), ia.Keypoint(x=7, y=8)]\n            ],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                    np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(keypoints_norm, list)\n        assert isinstance(keypoints_norm[0], ia.KeypointsOnImage)\n        assert len(keypoints_norm[0].keypoints) == 2\n        assert keypoints_norm[0].keypoints[0].x == 1\n        assert keypoints_norm[0].keypoints[0].y == 2\n        assert keypoints_norm[0].keypoints[1].x == 3\n        assert keypoints_norm[0].keypoints[1].y == 4\n\n        assert len(keypoints_norm[1].keypoints) == 2\n        assert keypoints_norm[1].keypoints[0].x == 5\n        assert keypoints_norm[1].keypoints[0].y == 6\n        assert keypoints_norm[1].keypoints[1].x == 7\n        assert keypoints_norm[1].keypoints[1].y == 8\n\n        # --> images None\n        with self.assertRaises(ValueError):\n            _keypoints_norm = normalization.normalize_keypoints(\n                [\n                    [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)],\n                    [ia.Keypoint(x=5, y=6), ia.Keypoint(x=7, y=8)]\n                ],\n                shapes=None\n            )\n\n        # --> different number of images\n        with self.assertRaises(ValueError):\n            _keypoints_norm = normalization.normalize_keypoints(\n                [\n                    [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=4)],\n                    [ia.Keypoint(x=5, y=6), ia.Keypoint(x=7, y=8)]\n                ],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n    def test_normalize_bounding_boxes(self):\n        def _assert_single_image_expected(inputs):\n            # --> images None\n            with self.assertRaises(ValueError):\n                _bbs_norm = normalization.normalize_bounding_boxes(\n                    inputs,\n                    shapes=None\n                )\n\n            # --> too many images\n            with self.assertRaises(ValueError):\n                _bbs_norm = normalization.normalize_bounding_boxes(\n                    inputs,\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n                )\n\n            # --> too many images\n            with self.assertRaises(ValueError):\n                _bbs_norm = normalization.normalize_bounding_boxes(\n                    inputs,\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                            np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n        # ----\n        # None\n        # ----\n        bbs_norm = normalization.normalize_bounding_boxes(None)\n        assert bbs_norm is None\n\n        # ----\n        # array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            bbs_norm = normalization.normalize_bounding_boxes(\n                np.zeros((1, 1, 4), dtype=dt) + 1,\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n            assert isinstance(bbs_norm, list)\n            assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n            assert len(bbs_norm[0].bounding_boxes) == 1\n            assert np.allclose(bbs_norm[0].to_xyxy_array(), 1)\n\n            bbs_norm = normalization.normalize_bounding_boxes(\n                np.zeros((1, 5, 4), dtype=dt) + 1,\n                shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n            )\n            assert isinstance(bbs_norm, list)\n            assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n            assert len(bbs_norm[0].bounding_boxes) == 5\n            assert np.allclose(bbs_norm[0].to_xyxy_array(), 1)\n\n            # --> bounding boxes for too many images\n            with self.assertRaises(ValueError):\n                _bbs_norm = normalization.normalize_bounding_boxes(\n                    np.zeros((2, 1, 4), dtype=dt) + 1,\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n            # --> too few bounding boxes\n            with self.assertRaises(ValueError):\n                _bbs_norm = normalization.normalize_bounding_boxes(\n                    np.zeros((1, 1, 4), dtype=dt) + 1,\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n                )\n\n            # --> wrong keypoints shape\n            with self.assertRaises(ValueError):\n                _bbs_norm = normalization.normalize_bounding_boxes(\n                    np.zeros((1, 1, 100), dtype=dt) + 1,\n                    shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n                )\n\n            _assert_single_image_expected(np.zeros((1, 1, 4), dtype=dt) + 1)\n\n        # ----\n        # (x1,y1,x2,y2)\n        # ----\n        bbs_norm = normalization.normalize_bounding_boxes(\n            (1, 2, 3, 4),\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(bbs_norm, list)\n        assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n        assert len(bbs_norm[0].bounding_boxes) == 1\n        assert bbs_norm[0].bounding_boxes[0].x1 == 1\n        assert bbs_norm[0].bounding_boxes[0].y1 == 2\n        assert bbs_norm[0].bounding_boxes[0].x2 == 3\n        assert bbs_norm[0].bounding_boxes[0].y2 == 4\n\n        _assert_single_image_expected((1, 2, 3, 4))\n\n        # ----\n        # single BoundingBox instance\n        # ----\n        bbs_norm = normalization.normalize_bounding_boxes(\n            ia.BoundingBox(x1=1, y1=2, x2=3, y2=4),\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(bbs_norm, list)\n        assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n        assert len(bbs_norm[0].bounding_boxes) == 1\n        assert bbs_norm[0].bounding_boxes[0].x1 == 1\n        assert bbs_norm[0].bounding_boxes[0].y1 == 2\n        assert bbs_norm[0].bounding_boxes[0].x2 == 3\n        assert bbs_norm[0].bounding_boxes[0].y2 == 4\n\n        _assert_single_image_expected(ia.BoundingBox(x1=1, y1=2, x2=3, y2=4))\n\n        # ----\n        # single BoundingBoxesOnImage instance\n        # ----\n        bbs_norm = normalization.normalize_bounding_boxes(\n            ia.BoundingBoxesOnImage(\n                [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)],\n                shape=(1, 1, 3)),\n            shapes=None\n        )\n        assert isinstance(bbs_norm, list)\n        assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n        assert len(bbs_norm[0].bounding_boxes) == 1\n        assert bbs_norm[0].bounding_boxes[0].x1 == 1\n        assert bbs_norm[0].bounding_boxes[0].y1 == 2\n        assert bbs_norm[0].bounding_boxes[0].x2 == 3\n        assert bbs_norm[0].bounding_boxes[0].y2 == 4\n\n        # ----\n        # empty iterable\n        # ----\n        bbs_norm = normalization.normalize_bounding_boxes([], shapes=None)\n        assert bbs_norm is None\n\n        # ----\n        # iterable of array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            bbs_norm = normalization.normalize_bounding_boxes(\n                [np.zeros((1, 4), dtype=dt) + 1],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n            assert isinstance(bbs_norm, list)\n            assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n            assert len(bbs_norm[0].bounding_boxes) == 1\n            assert np.allclose(bbs_norm[0].to_xyxy_array(), 1)\n\n            bbs_norm = normalization.normalize_bounding_boxes(\n                [np.zeros((5, 4), dtype=dt) + 1],\n                shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n            )\n            assert isinstance(bbs_norm, list)\n            assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n            assert len(bbs_norm[0].bounding_boxes) == 5\n            assert np.allclose(bbs_norm[0].to_xyxy_array(), 1)\n\n            # --> bounding boxes for too many images\n            with self.assertRaises(ValueError):\n                _bbs_norm = normalization.normalize_bounding_boxes(\n                    [\n                        np.zeros((1, 4), dtype=dt) + 1,\n                        np.zeros((1, 4), dtype=dt) + 1\n                    ],\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n            # --> too few bounding boxes\n            with self.assertRaises(ValueError):\n                _bbs_norm = normalization.normalize_bounding_boxes(\n                    [np.zeros((1, 4), dtype=dt) + 1],\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n                )\n\n            # --> images None\n            with self.assertRaises(ValueError):\n                _bbs_norm = normalization.normalize_bounding_boxes(\n                    [np.zeros((1, 4), dtype=dt) + 1],\n                    shapes=None\n                )\n\n            # --> wrong shape\n            with self.assertRaises(ValueError):\n                _bbs_norm = normalization.normalize_bounding_boxes(\n                    [np.zeros((1, 100), dtype=dt) + 1],\n                    shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n                )\n\n        # ----\n        # iterable of (x1,y1,x2,y2)\n        # ----\n        bbs_norm = normalization.normalize_bounding_boxes(\n            [(1, 2, 3, 4), (5, 6, 7, 8)],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(bbs_norm, list)\n        assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n        assert len(bbs_norm[0].bounding_boxes) == 2\n        assert bbs_norm[0].bounding_boxes[0].x1 == 1\n        assert bbs_norm[0].bounding_boxes[0].y1 == 2\n        assert bbs_norm[0].bounding_boxes[0].x2 == 3\n        assert bbs_norm[0].bounding_boxes[0].y2 == 4\n        assert bbs_norm[0].bounding_boxes[1].x1 == 5\n        assert bbs_norm[0].bounding_boxes[1].y1 == 6\n        assert bbs_norm[0].bounding_boxes[1].x2 == 7\n        assert bbs_norm[0].bounding_boxes[1].y2 == 8\n\n        # may only be used for single images\n        with self.assertRaises(ValueError):\n            _bbs_norm = normalization.normalize_bounding_boxes(\n                [(1, 2, 3, 4)],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of BoundingBox\n        # ----\n        bbs_norm = normalization.normalize_bounding_boxes(\n            [\n                ia.BoundingBox(x1=1, y1=2, x2=3, y2=4),\n                ia.BoundingBox(x1=5, y1=6, x2=7, y2=8)\n            ],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(bbs_norm, list)\n        assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n        assert len(bbs_norm[0].bounding_boxes) == 2\n        assert bbs_norm[0].bounding_boxes[0].x1 == 1\n        assert bbs_norm[0].bounding_boxes[0].y1 == 2\n        assert bbs_norm[0].bounding_boxes[0].x2 == 3\n        assert bbs_norm[0].bounding_boxes[0].y2 == 4\n        assert bbs_norm[0].bounding_boxes[1].x1 == 5\n        assert bbs_norm[0].bounding_boxes[1].y1 == 6\n        assert bbs_norm[0].bounding_boxes[1].x2 == 7\n        assert bbs_norm[0].bounding_boxes[1].y2 == 8\n\n        # may only be used for single images\n        with self.assertRaises(ValueError):\n            _bbs_norm = normalization.normalize_bounding_boxes(\n                [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of BoundingBoxesOnImage\n        # ----\n        bbs_norm = normalization.normalize_bounding_boxes(\n            [\n                ia.BoundingBoxesOnImage(\n                    [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)],\n                    shape=(1, 1, 3)),\n                ia.BoundingBoxesOnImage(\n                    [ia.BoundingBox(x1=5, y1=6, x2=7, y2=8)],\n                    shape=(1, 1, 3))\n            ],\n            shapes=None\n        )\n        assert isinstance(bbs_norm, list)\n\n        assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n        assert len(bbs_norm[0].bounding_boxes) == 1\n        assert bbs_norm[0].bounding_boxes[0].x1 == 1\n        assert bbs_norm[0].bounding_boxes[0].y1 == 2\n        assert bbs_norm[0].bounding_boxes[0].x2 == 3\n        assert bbs_norm[0].bounding_boxes[0].y2 == 4\n\n        assert isinstance(bbs_norm[1], ia.BoundingBoxesOnImage)\n        assert len(bbs_norm[1].bounding_boxes) == 1\n        assert bbs_norm[1].bounding_boxes[0].x1 == 5\n        assert bbs_norm[1].bounding_boxes[0].y1 == 6\n        assert bbs_norm[1].bounding_boxes[0].x2 == 7\n        assert bbs_norm[1].bounding_boxes[0].y2 == 8\n\n        # ----\n        # iterable of empty interables\n        # ----\n        bbs_norm = normalization.normalize_bounding_boxes(\n            [[]],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert bbs_norm is None\n\n        # ----\n        # iterable of iterable of (x1,y1,x2,y2)\n        # ----\n        bbs_norm = normalization.normalize_bounding_boxes(\n            [\n                [(1, 2, 3, 4)],\n                [(5, 6, 7, 8), (9, 10, 11, 12)]\n            ],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                    np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(bbs_norm, list)\n        assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n        assert len(bbs_norm[0].bounding_boxes) == 1\n        assert bbs_norm[0].bounding_boxes[0].x1 == 1\n        assert bbs_norm[0].bounding_boxes[0].y1 == 2\n        assert bbs_norm[0].bounding_boxes[0].x2 == 3\n        assert bbs_norm[0].bounding_boxes[0].y2 == 4\n\n        assert len(bbs_norm[1].bounding_boxes) == 2\n        assert bbs_norm[1].bounding_boxes[0].x1 == 5\n        assert bbs_norm[1].bounding_boxes[0].y1 == 6\n        assert bbs_norm[1].bounding_boxes[0].x2 == 7\n        assert bbs_norm[1].bounding_boxes[0].y2 == 8\n\n        assert bbs_norm[1].bounding_boxes[1].x1 == 9\n        assert bbs_norm[1].bounding_boxes[1].y1 == 10\n        assert bbs_norm[1].bounding_boxes[1].x2 == 11\n        assert bbs_norm[1].bounding_boxes[1].y2 == 12\n\n        # --> images None\n        with self.assertRaises(ValueError):\n            _bbs_norm = normalization.normalize_bounding_boxes(\n                [\n                    [(1, 2, 3, 4), (3, 4, 5, 6)],\n                    [(5, 6, 7, 8), (7, 8, 9, 10)]\n                ],\n                shapes=None\n            )\n\n        # --> different number of images\n        with self.assertRaises(ValueError):\n            _bbs_norm = normalization.normalize_bounding_boxes(\n                [\n                    [(1, 2, 3, 4)],\n                    [(5, 6, 7, 8)]\n                ],\n                [np.zeros((1, 1, 3), dtype=np.uint8),\n                 np.zeros((1, 1, 3), dtype=np.uint8),\n                 np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of iterable of Keypoint\n        # ----\n        bbs_norm = normalization.normalize_bounding_boxes(\n            [\n                [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4),\n                 ia.BoundingBox(x1=5, y1=6, x2=7, y2=8)],\n                [ia.BoundingBox(x1=9, y1=10, x2=11, y2=12),\n                 ia.BoundingBox(x1=13, y1=14, x2=15, y2=16)]\n            ],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                    np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(bbs_norm, list)\n        assert isinstance(bbs_norm[0], ia.BoundingBoxesOnImage)\n        assert len(bbs_norm[0].bounding_boxes) == 2\n        assert bbs_norm[0].bounding_boxes[0].x1 == 1\n        assert bbs_norm[0].bounding_boxes[0].y1 == 2\n        assert bbs_norm[0].bounding_boxes[0].x2 == 3\n        assert bbs_norm[0].bounding_boxes[0].y2 == 4\n        assert bbs_norm[0].bounding_boxes[1].x1 == 5\n        assert bbs_norm[0].bounding_boxes[1].y1 == 6\n        assert bbs_norm[0].bounding_boxes[1].x2 == 7\n        assert bbs_norm[0].bounding_boxes[1].y2 == 8\n\n        assert len(bbs_norm[1].bounding_boxes) == 2\n        assert bbs_norm[1].bounding_boxes[0].x1 == 9\n        assert bbs_norm[1].bounding_boxes[0].y1 == 10\n        assert bbs_norm[1].bounding_boxes[0].x2 == 11\n        assert bbs_norm[1].bounding_boxes[0].y2 == 12\n        assert bbs_norm[1].bounding_boxes[1].x1 == 13\n        assert bbs_norm[1].bounding_boxes[1].y1 == 14\n        assert bbs_norm[1].bounding_boxes[1].x2 == 15\n        assert bbs_norm[1].bounding_boxes[1].y2 == 16\n\n        # --> images None\n        with self.assertRaises(ValueError):\n            _bbs_norm = normalization.normalize_bounding_boxes(\n                [\n                    [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4),\n                     ia.BoundingBox(x1=5, y1=6, x2=7, y2=8)],\n                    [ia.BoundingBox(x1=9, y1=10, x2=11, y2=12),\n                     ia.BoundingBox(x1=13, y1=14, x2=15, y2=16)]\n                ],\n                shapes=None\n            )\n\n        # --> different number of images\n        with self.assertRaises(ValueError):\n            _bbs_norm = normalization.normalize_bounding_boxes(\n                [\n                    [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4),\n                     ia.BoundingBox(x1=5, y1=6, x2=7, y2=8)],\n                    [ia.BoundingBox(x1=9, y1=10, x2=11, y2=12),\n                     ia.BoundingBox(x1=13, y1=14, x2=15, y2=16)]\n                ],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n    \n    def test_normalize_polygons(self):\n        def _assert_single_image_expected(inputs):\n            # --> images None\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    inputs, shapes=None)\n\n            # --> too many images\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    inputs,\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8))\n\n            # --> too many images\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    inputs,\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                            np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n        coords1 = [(0, 0), (10, 0), (10, 10)]\n        coords2 = [(5, 5), (15, 5), (15, 15)]\n        coords3 = [(0, 0), (10, 0), (10, 10), (0, 10)]\n        coords4 = [(5, 5), (15, 5), (15, 15), (5, 15)]\n\n        coords1_kps = [ia.Keypoint(x=x, y=y) for x, y in coords1]\n        coords2_kps = [ia.Keypoint(x=x, y=y) for x, y in coords2]\n        coords3_kps = [ia.Keypoint(x=x, y=y) for x, y in coords3]\n        coords4_kps = [ia.Keypoint(x=x, y=y) for x, y in coords4]\n\n        coords1_arr = np.float32(coords1)\n        coords2_arr = np.float32(coords2)\n        coords3_arr = np.float32(coords3)\n        coords4_arr = np.float32(coords4)\n\n        # ----\n        # None\n        # ----\n        polygons_norm = normalization.normalize_polygons(None)\n        assert polygons_norm is None\n\n        # ----\n        # array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            polygons_norm = normalization.normalize_polygons(\n                coords1_arr[np.newaxis, np.newaxis, ...].astype(dt),\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n            assert isinstance(polygons_norm, list)\n            assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n            assert len(polygons_norm[0].polygons) == 1\n            assert np.allclose(polygons_norm[0].polygons[0].exterior,\n                               coords1_arr)\n\n            polygons_norm = normalization.normalize_polygons(\n                np.tile(\n                    coords1_arr[np.newaxis, np.newaxis, ...].astype(dt),\n                    (1, 5, 1, 1)\n                ),\n                shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n            )\n            assert isinstance(polygons_norm, list)\n            assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n            assert len(polygons_norm[0].polygons) == 5\n            assert np.allclose(polygons_norm[0].polygons[0].exterior,\n                               coords1_arr)\n\n            # --> polygons for too many images\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    np.tile(\n                        coords1_arr[np.newaxis, np.newaxis, ...].astype(dt),\n                        (2, 1, 1, 1)\n                    ),\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n            # --> too few polygons\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    np.tile(\n                        coords1_arr[np.newaxis, np.newaxis, ...].astype(dt),\n                        (1, 1, 1, 1)\n                    ),\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n                )\n\n            # --> wrong polygons shape\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    np.tile(\n                        coords1_arr[np.newaxis, np.newaxis, ...].astype(dt),\n                        (1, 1, 1, 10)\n                    ),\n                    shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n                )\n\n            _assert_single_image_expected(\n                coords1_arr[np.newaxis, np.newaxis, ...].astype(dt))\n\n        # ----\n        # single Polygon instance\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            ia.Polygon(coords1),\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(polygons_norm, list)\n        assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n        assert len(polygons_norm[0].polygons) == 1\n        assert polygons_norm[0].polygons[0].exterior_almost_equals(coords1)\n\n        _assert_single_image_expected(ia.Polygon(coords1))\n\n        # ----\n        # single PolygonsOnImage instance\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            ia.PolygonsOnImage([ia.Polygon(coords1)], shape=(1, 1, 3)),\n            shapes=None\n        )\n        assert isinstance(polygons_norm, list)\n        assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n        assert len(polygons_norm[0].polygons) == 1\n        assert polygons_norm[0].polygons[0].exterior_almost_equals(coords1)\n\n        # ----\n        # empty iterable\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            [], shapes=None\n        )\n        assert polygons_norm is None\n\n        # ----\n        # iterable of array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            polygons_norm = normalization.normalize_polygons(\n                [coords1_arr[np.newaxis, ...].astype(dt)],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n            assert isinstance(polygons_norm, list)\n            assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n            assert len(polygons_norm[0].polygons) == 1\n            assert np.allclose(polygons_norm[0].polygons[0].exterior,\n                               coords1_arr)\n\n            polygons_norm = normalization.normalize_polygons(\n                [np.tile(\n                    coords1_arr[np.newaxis, ...].astype(dt),\n                    (5, 1, 1)\n                )],\n                shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n            )\n            assert isinstance(polygons_norm, list)\n            assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n            assert len(polygons_norm[0].polygons) == 5\n            assert np.allclose(polygons_norm[0].polygons[0].exterior,\n                               coords1_arr)\n\n            # --> polygons for too many images\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    [coords1_arr[np.newaxis, ...].astype(dt),\n                     coords2_arr[np.newaxis, ...].astype(dt)],\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n            # --> too few polygons\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    [coords1_arr[np.newaxis, ...].astype(dt)],\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n                )\n\n            # --> wrong polygons shape\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    [np.tile(\n                        coords1_arr[np.newaxis, ...].astype(dt),\n                        (1, 1, 10)\n                    )],\n                    shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n                )\n\n            _assert_single_image_expected(\n                [coords1_arr[np.newaxis, ...].astype(dt)]\n            )\n\n        # ----\n        # iterable of (x,y)\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            coords1,\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(polygons_norm, list)\n        assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n        assert len(polygons_norm[0].polygons) == 1\n        assert polygons_norm[0].polygons[0].exterior_almost_equals(coords1)\n\n        # may only be used for single images\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                coords1,\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of Keypoint\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            coords1_kps,\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(polygons_norm, list)\n        assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n        assert len(polygons_norm[0].polygons) == 1\n        assert polygons_norm[0].polygons[0].exterior_almost_equals(coords1)\n\n        # may only be used for single images\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                coords1_kps,\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of Polygon\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            [ia.Polygon(coords1), ia.Polygon(coords2)],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(polygons_norm, list)\n        assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n        assert len(polygons_norm[0].polygons) == 2\n        assert polygons_norm[0].polygons[0].exterior_almost_equals(coords1)\n        assert polygons_norm[0].polygons[1].exterior_almost_equals(coords2)\n\n        # may only be used for single images\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                [ia.Polygon(coords1)],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of PolygonsOnImage\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            [\n                ia.PolygonsOnImage([ia.Polygon(coords1)], shape=(1, 1, 3)),\n                ia.PolygonsOnImage([ia.Polygon(coords2)], shape=(1, 1, 3))\n            ],\n            shapes=None\n        )\n        assert isinstance(polygons_norm, list)\n\n        assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n        assert len(polygons_norm[0].polygons) == 1\n        assert polygons_norm[0].polygons[0].exterior_almost_equals(coords1)\n\n        assert isinstance(polygons_norm[1], ia.PolygonsOnImage)\n        assert len(polygons_norm[1].polygons) == 1\n        assert polygons_norm[1].polygons[0].exterior_almost_equals(coords2)\n\n        # ----\n        # iterable of empty iterables\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            [[]],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert polygons_norm is None\n\n        # ----\n        # iterable of iterable of array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            polygons_norm = normalization.normalize_polygons(\n                [[coords1_arr.astype(dt)]],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n            assert isinstance(polygons_norm, list)\n            assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n            assert len(polygons_norm[0].polygons) == 1\n            assert np.allclose(polygons_norm[0].polygons[0].exterior,\n                               coords1_arr)\n\n            polygons_norm = normalization.normalize_polygons(\n                [[\n                    np.copy(coords1_arr).astype(dt) for _ in sm.xrange(5)\n                ]],\n                shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n            )\n            assert isinstance(polygons_norm, list)\n            assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n            assert len(polygons_norm[0].polygons) == 5\n            assert np.allclose(polygons_norm[0].polygons[0].exterior,\n                               coords1_arr)\n\n            # --> polygons for too many images\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    [[coords1_arr.astype(dt)],\n                     [coords2_arr.astype(dt)]],\n                    shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n                )\n\n            # --> too few polygons\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    [[coords1_arr.astype(dt)]],\n                    shapes=np.zeros((2, 1, 1, 3), dtype=np.uint8)\n                )\n\n            # --> wrong polygons shape\n            with self.assertRaises(ValueError):\n                _polygons_norm = normalization.normalize_polygons(\n                    [[np.tile(\n                        coords1_arr.astype(dt),\n                        (1, 1, 10)\n                    )]],\n                    shapes=np.zeros((1, 1, 1, 3), dtype=np.uint8)\n                )\n\n            _assert_single_image_expected(\n                [[coords1_arr.astype(dt)]]\n            )\n\n        # ----\n        # iterable of iterable of (x,y)\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            [coords1, coords2],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(polygons_norm, list)\n        assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n        assert len(polygons_norm[0].polygons) == 2\n        assert polygons_norm[0].polygons[0].exterior_almost_equals(coords1)\n        assert polygons_norm[0].polygons[1].exterior_almost_equals(coords2)\n\n        # --> images None\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                [coords1, coords2],\n                shapes=None\n            )\n\n        # --> different number of images\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                [coords1, coords2],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of iterable of Keypoint\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            [coords1_kps, coords2_kps],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(polygons_norm, list)\n        assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n        assert len(polygons_norm[0].polygons) == 2\n        assert polygons_norm[0].polygons[0].exterior_almost_equals(coords1)\n        assert polygons_norm[0].polygons[1].exterior_almost_equals(coords2)\n\n        # --> images None\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                [coords1_kps, coords2_kps],\n                shapes=None\n            )\n\n        # --> different number of images\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                [coords1_kps, coords2_kps],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of iterable of Polygon\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            [\n                [ia.Polygon(coords1), ia.Polygon(coords2)],\n                [ia.Polygon(coords3), ia.Polygon(coords4)]\n            ],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                    np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(polygons_norm, list)\n        assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n        assert isinstance(polygons_norm[1], ia.PolygonsOnImage)\n\n        assert len(polygons_norm[0].polygons) == 2\n        assert polygons_norm[0].polygons[0].exterior_almost_equals(coords1)\n        assert polygons_norm[0].polygons[1].exterior_almost_equals(coords2)\n\n        assert len(polygons_norm[1].polygons) == 2\n        assert polygons_norm[1].polygons[0].exterior_almost_equals(coords3)\n        assert polygons_norm[1].polygons[1].exterior_almost_equals(coords4)\n\n        # --> images None\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                [\n                    [ia.Polygon(coords1), ia.Polygon(coords2)],\n                    [ia.Polygon(coords3), ia.Polygon(coords4)]\n                ],\n                shapes=None\n            )\n\n        # --> different number of images\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                [\n                    [ia.Polygon(coords1), ia.Polygon(coords2)],\n                    [ia.Polygon(coords3), ia.Polygon(coords4)]\n                ],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of iterable of empty iterable\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            [[[]]],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert polygons_norm is None\n\n        # ----\n        # iterable of iterable of iterable of (x,y)\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            [[coords1, coords2], [coords3, coords4]],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                    np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(polygons_norm, list)\n        assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n\n        assert len(polygons_norm[0].polygons) == 2\n        assert polygons_norm[0].polygons[0].exterior_almost_equals(coords1)\n        assert polygons_norm[0].polygons[1].exterior_almost_equals(coords2)\n\n        assert len(polygons_norm[0].polygons) == 2\n        assert polygons_norm[1].polygons[0].exterior_almost_equals(coords3)\n        assert polygons_norm[1].polygons[1].exterior_almost_equals(coords4)\n\n        # --> images None\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                [[coords1, coords2]],\n                shapes=None\n            )\n\n        # --> different number of images\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                [[coords1, coords2], [coords3]],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n        # ----\n        # iterable of iterable of iterable of Keypoint\n        # ----\n        polygons_norm = normalization.normalize_polygons(\n            [[coords1_kps, coords2_kps], [coords3_kps, coords4_kps]],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                    np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(polygons_norm, list)\n        assert isinstance(polygons_norm[0], ia.PolygonsOnImage)\n\n        assert len(polygons_norm[0].polygons) == 2\n        assert polygons_norm[0].polygons[0].exterior_almost_equals(coords1)\n        assert polygons_norm[0].polygons[1].exterior_almost_equals(coords2)\n\n        assert len(polygons_norm[0].polygons) == 2\n        assert polygons_norm[1].polygons[0].exterior_almost_equals(coords3)\n        assert polygons_norm[1].polygons[1].exterior_almost_equals(coords4)\n\n        # --> images None\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                [[coords1_kps, coords2_kps]],\n                shapes=None\n            )\n\n        # --> different number of images\n        with self.assertRaises(ValueError):\n            _polygons_norm = normalization.normalize_polygons(\n                [[coords1_kps, coords2_kps], [coords3_kps]],\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8),\n                        np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n\n    # essentially already tested via polygons, as they are based on the\n    # same methods, hence a short test here\n    def test_normalize_line_strings(self):\n        coords1 = [(0, 0), (10, 0), (10, 10)]\n        coords2 = [(5, 5), (15, 5), (15, 15)]\n        coords3 = [(0, 0), (10, 0), (10, 10), (0, 10)]\n        coords4 = [(5, 5), (15, 5), (15, 15), (5, 15)]\n\n        coords1_arr = np.float32(coords1)\n\n        # ----\n        # None\n        # ----\n        lss_norm = normalization.normalize_line_strings(None)\n        assert lss_norm is None\n\n        # ----\n        # array\n        # ----\n        for dt in [np.dtype(\"float32\"), np.dtype(\"int16\"), np.dtype(\"uint16\")]:\n            lss_norm = normalization.normalize_line_strings(\n                coords1_arr[np.newaxis, np.newaxis, ...].astype(dt),\n                shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n            )\n            assert isinstance(lss_norm, list)\n            assert isinstance(lss_norm[0], ia.LineStringsOnImage)\n            assert len(lss_norm[0].line_strings) == 1\n            assert np.allclose(lss_norm[0].line_strings[0].coords, coords1_arr)\n\n        # ----\n        # single LineString instance\n        # ----\n        lss_norm = normalization.normalize_line_strings(\n            ia.LineString(coords1),\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(lss_norm, list)\n        assert isinstance(lss_norm[0], ia.LineStringsOnImage)\n        assert len(lss_norm[0].line_strings) == 1\n        assert np.allclose(lss_norm[0].line_strings[0].coords, coords1)\n\n        # ----\n        # single LineStringOnImage instance\n        # ----\n        lss_norm = normalization.normalize_line_strings(\n            ia.LineStringsOnImage([ia.LineString(coords1)], shape=(1, 1, 3)),\n            shapes=None\n        )\n        assert isinstance(lss_norm, list)\n        assert isinstance(lss_norm[0], ia.LineStringsOnImage)\n        assert len(lss_norm[0].line_strings) == 1\n        assert np.allclose(lss_norm[0].line_strings[0].coords, coords1)\n\n        # ----\n        # empty iterable\n        # ----\n        lss_norm = normalization.normalize_line_strings(\n            [], shapes=None\n        )\n        assert lss_norm is None\n\n        # ----\n        # iterable of LineStringOnImage\n        # ----\n        lss_norm = normalization.normalize_line_strings(\n            [\n                ia.LineStringsOnImage(\n                    [ia.LineString(coords1)], shape=(1, 1, 3)),\n                ia.LineStringsOnImage(\n                    [ia.LineString(coords2)], shape=(1, 1, 3))\n            ],\n            shapes=None\n        )\n        assert isinstance(lss_norm, list)\n\n        assert isinstance(lss_norm[0], ia.LineStringsOnImage)\n        assert len(lss_norm[0].line_strings) == 1\n        assert np.allclose(lss_norm[0].line_strings[0].coords, coords1)\n\n        assert isinstance(lss_norm[1], ia.LineStringsOnImage)\n        assert len(lss_norm[1].line_strings) == 1\n        assert np.allclose(lss_norm[1].line_strings[0].coords, coords2)\n\n        # ----\n        # iterable of iterable of LineString\n        # ----\n        lss_norm = normalization.normalize_line_strings(\n            [\n                [ia.LineString(coords1), ia.LineString(coords2)],\n                [ia.LineString(coords3), ia.LineString(coords4)]\n            ],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                    np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(lss_norm, list)\n        assert isinstance(lss_norm[0], ia.LineStringsOnImage)\n        assert isinstance(lss_norm[1], ia.LineStringsOnImage)\n\n        assert len(lss_norm[0].line_strings) == 2\n        assert np.allclose(lss_norm[0].line_strings[0].coords, coords1)\n        assert np.allclose(lss_norm[0].line_strings[1].coords, coords2)\n\n        assert len(lss_norm[1].line_strings) == 2\n        assert np.allclose(lss_norm[1].line_strings[0].coords, coords3)\n        assert np.allclose(lss_norm[1].line_strings[1].coords, coords4)\n\n        # ----\n        # iterable of iterable of iterable of (x,y)\n        # ----\n        lss_norm = normalization.normalize_line_strings(\n            [[coords1, coords2], [coords3, coords4]],\n            shapes=[np.zeros((1, 1, 3), dtype=np.uint8),\n                    np.zeros((1, 1, 3), dtype=np.uint8)]\n        )\n        assert isinstance(lss_norm, list)\n        assert isinstance(lss_norm[0], ia.LineStringsOnImage)\n\n        assert len(lss_norm[0].line_strings) == 2\n        assert np.allclose(lss_norm[0].line_strings[0].coords, coords1)\n        assert np.allclose(lss_norm[0].line_strings[1].coords, coords2)\n\n        assert len(lss_norm[0].line_strings) == 2\n        assert np.allclose(lss_norm[1].line_strings[0].coords, coords3)\n        assert np.allclose(lss_norm[1].line_strings[1].coords, coords4)\n\n    def test__find_first_nonempty(self):\n        # None\n        observed = normalization.find_first_nonempty(None)\n        assert observed[0] is None\n        assert observed[1] is True\n        assert len(observed[2]) == 0\n\n        # None with parents\n        observed = normalization.find_first_nonempty(None, parents=[\"foo\"])\n        assert observed[0] is None\n        assert observed[1] is True\n        assert len(observed[2]) == 1\n        assert observed[2][0] == \"foo\"\n\n        # array\n        observed = normalization.find_first_nonempty(np.zeros((4, 4, 3)))\n        assert ia.is_np_array(observed[0])\n        assert observed[0].shape == (4, 4, 3)\n        assert observed[1] is True\n        assert len(observed[2]) == 0\n\n        # int\n        observed = normalization.find_first_nonempty(0)\n        assert observed[0] == 0\n        assert observed[1] is True\n        assert len(observed[2]) == 0\n\n        # str\n        observed = normalization.find_first_nonempty(\"foo\")\n        assert observed[0] == \"foo\"\n        assert observed[1] is True\n        assert len(observed[2]) == 0\n\n        # empty list\n        observed = normalization.find_first_nonempty([])\n        assert observed[0] is None\n        assert observed[1] is False\n        assert len(observed[2]) == 0\n\n        # empty list of empty lists\n        observed = normalization.find_first_nonempty([[], [], []])\n        assert observed[0] is None\n        assert observed[1] is False\n        assert len(observed[2]) == 1\n\n        # empty list of empty lists of empty lists\n        observed = normalization.find_first_nonempty([[], [[]], []])\n        assert observed[0] is None\n        assert observed[1] is False\n        assert len(observed[2]) == 2\n\n        # list of None\n        observed = normalization.find_first_nonempty([None, None])\n        assert observed[0] is None\n        assert observed[1] is True\n        assert len(observed[2]) == 1\n\n        # list of array\n        observed = normalization.find_first_nonempty([\n            np.zeros((4, 4, 3)), np.zeros((5, 5, 3))])\n        assert ia.is_np_array(observed[0])\n        assert observed[0].shape == (4, 4, 3)\n        assert observed[1] is True\n        assert len(observed[2]) == 1\n\n        # list of list of array\n        observed = normalization.find_first_nonempty(\n            [[np.zeros((4, 4, 3))], [np.zeros((5, 5, 3))]]\n        )\n        assert ia.is_np_array(observed[0])\n        assert observed[0].shape == (4, 4, 3)\n        assert observed[1] is True\n        assert len(observed[2]) == 2\n\n        # list of tuple of array\n        observed = normalization.find_first_nonempty(\n            [\n                (\n                    np.zeros((4, 4, 3)), np.zeros((5, 5, 3))\n                ), (\n                    np.zeros((6, 6, 3)), np.zeros((7, 7, 3))\n                )\n            ]\n        )\n        assert ia.is_np_array(observed[0])\n        assert observed[0].shape == (4, 4, 3)\n        assert observed[1] is True\n        assert len(observed[2]) == 2\n\n    def test__nonempty_info_to_type_str(self):\n        ntype = normalization._nonempty_info_to_type_str(\n            None, True, [])\n        assert ntype == \"None\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            None, False, [])\n        assert ntype == \"iterable[empty]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            None, False, [[]])\n        assert ntype == \"iterable-iterable[empty]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            None, False, [[], []])\n        assert ntype == \"iterable-iterable-iterable[empty]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            None, False, [tuple(), []])\n        assert ntype == \"iterable-iterable-iterable[empty]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            1, True, [tuple([1, 2])])\n        assert ntype == \"tuple[number,size=2]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            1, True, [[], tuple([1, 2])])\n        assert ntype == \"iterable-tuple[number,size=2]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            1, True, [tuple([1, 2, 3, 4])])\n        assert ntype == \"tuple[number,size=4]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            1, True, [[], tuple([1, 2, 3, 4])])\n        assert ntype == \"iterable-tuple[number,size=4]\"\n\n        with self.assertRaises(AssertionError):\n            ntype = normalization._nonempty_info_to_type_str(\n                1, True, [tuple([1, 2, 3])])\n            assert ntype == \"tuple[number,size=4]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            np.zeros((4, 4, 3), dtype=np.uint8), True, [])\n        assert ntype == \"array[uint]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            np.zeros((4, 4, 3), dtype=np.float32), True, [])\n        assert ntype == \"array[float]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            np.zeros((4, 4, 3), dtype=np.int32), True, [])\n        assert ntype == \"array[int]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            np.zeros((4, 4, 3), dtype=bool), True, [])\n        assert ntype == \"array[bool]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            np.zeros((4, 4, 3), dtype=np.dtype(\"complex\")), True, [])\n        assert ntype == \"array[c]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            np.zeros((4, 4, 3), dtype=np.uint8), True, [[]])\n        assert ntype == \"iterable-array[uint]\"\n\n        ntype = normalization._nonempty_info_to_type_str(\n            np.zeros((4, 4, 3), dtype=np.uint8), True, [[], []])\n        assert ntype == \"iterable-iterable-array[uint]\"\n\n        cls_names = [\"Keypoint\", \"KeypointsOnImage\",\n                     \"BoundingBox\", \"BoundingBoxesOnImage\",\n                     \"Polygon\", \"PolygonsOnImage\",\n                     \"HeatmapsOnImage\", \"SegmentationMapsOnImage\"]\n        clss = [\n            ia.Keypoint(x=1, y=1),\n            ia.KeypointsOnImage([], shape=(1, 1, 3)),\n            ia.BoundingBox(x1=1, y1=2, x2=3, y2=4),\n            ia.BoundingBoxesOnImage([], shape=(1, 1, 3)),\n            ia.Polygon([(1, 1), (1, 2), (2, 2)]),\n            ia.PolygonsOnImage([], shape=(1,)),\n            ia.HeatmapsOnImage(np.zeros((1, 1, 1), dtype=np.float32),\n                               shape=(1, 1, 3)),\n            ia.SegmentationMapsOnImage(np.zeros((1, 1, 1), dtype=np.int32),\n                                       shape=(1, 1, 3))\n        ]\n        for cls_name, cls in zip(cls_names, clss):\n            ntype = normalization._nonempty_info_to_type_str(\n                cls, True, [])\n            assert ntype == cls_name\n\n            ntype = normalization._nonempty_info_to_type_str(\n                cls, True, [[]])\n            assert ntype == \"iterable-%s\" % (cls_name,)\n\n            ntype = normalization._nonempty_info_to_type_str(\n                cls, True, [[], tuple()])\n            assert ntype == \"iterable-iterable-%s\" % (cls_name,)\n\n    def test_estimate_heatmaps_norm_type(self):\n        ntype = normalization.estimate_heatmaps_norm_type(None)\n        assert ntype == \"None\"\n\n        ntype = normalization.estimate_heatmaps_norm_type(\n            np.zeros((1, 1, 1, 1), dtype=np.float32))\n        assert ntype == \"array[float]\"\n\n        ntype = normalization.estimate_heatmaps_norm_type(\n            ia.HeatmapsOnImage(\n                np.zeros((1, 1, 1), dtype=np.float32),\n                shape=(1, 1, 1)\n            )\n        )\n        assert ntype == \"HeatmapsOnImage\"\n\n        ntype = normalization.estimate_heatmaps_norm_type([])\n        assert ntype == \"iterable[empty]\"\n\n        ntype = normalization.estimate_heatmaps_norm_type(\n            [np.zeros((1, 1, 1), dtype=np.float32)])\n        assert ntype == \"iterable-array[float]\"\n\n        ntype = normalization.estimate_heatmaps_norm_type([\n            ia.HeatmapsOnImage(np.zeros((1, 1, 1), dtype=np.float32),\n                               shape=(1, 1, 1))\n        ])\n        assert ntype == \"iterable-HeatmapsOnImage\"\n\n        # --\n        # error cases\n        # --\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_heatmaps_norm_type(1)\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_heatmaps_norm_type(\"foo\")\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_heatmaps_norm_type(\n                np.zeros((1, 1, 1), dtype=np.int32))\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_heatmaps_norm_type([1])\n\n        # wrong class\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_heatmaps_norm_type(\n                ia.KeypointsOnImage([], shape=(1, 1, 1)))\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_heatmaps_norm_type([[]])\n\n        # list of list of Heatmaps, only list of Heatmaps is max\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_heatmaps_norm_type([\n                [ia.HeatmapsOnImage(np.zeros((1, 1, 1), dtype=np.float32),\n                                    shape=(1, 1, 1))]\n            ])\n\n    def test_estimate_segmaps_norm_type(self):\n        ntype = normalization.estimate_segmaps_norm_type(None)\n        assert ntype == \"None\"\n\n        for name, dt in zip([\"int\", \"uint\", \"bool\"],\n                            [np.int32, np.uint16, bool]):\n            ntype = normalization.estimate_segmaps_norm_type(\n                np.zeros((1, 1, 1, 1), dtype=dt))\n            assert ntype == \"array[%s]\" % (name,)\n\n        ntype = normalization.estimate_segmaps_norm_type(\n            ia.SegmentationMapsOnImage(\n                np.zeros((1, 1, 1), dtype=np.int32),\n                shape=(1, 1, 1)\n            )\n        )\n        assert ntype == \"SegmentationMapsOnImage\"\n\n        ntype = normalization.estimate_segmaps_norm_type([])\n        assert ntype == \"iterable[empty]\"\n\n        ntype = normalization.estimate_segmaps_norm_type(\n            [np.zeros((1, 1, 1), dtype=np.int32)])\n        assert ntype == \"iterable-array[int]\"\n\n        ntype = normalization.estimate_segmaps_norm_type([\n            ia.SegmentationMapsOnImage(np.zeros((1, 1, 1), dtype=np.int32),\n                                       shape=(1, 1, 1))\n        ])\n        assert ntype == \"iterable-SegmentationMapsOnImage\"\n\n        # --\n        # error cases\n        # --\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_segmaps_norm_type(1)\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_segmaps_norm_type(\"foo\")\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_segmaps_norm_type([1])\n\n        # wrong class\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_segmaps_norm_type(\n                ia.KeypointsOnImage([], shape=(1, 1, 1)))\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_segmaps_norm_type([[]])\n\n        # list of list of SegMap, only list of SegMap is max\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_segmaps_norm_type([\n                [ia.SegmentationMapsOnImage(\n                    np.zeros((1, 1, 1, 1), dtype=np.int32),\n                    shape=(1, 1, 1))]\n            ])\n\n    def test_estimate_keypoints_norm_type(self):\n        ntype = normalization.estimate_keypoints_norm_type(None)\n        assert ntype == \"None\"\n\n        for name, dt in zip([\"float\", \"int\", \"uint\"],\n                            [np.float32, np.int32, np.uint16]):\n            ntype = normalization.estimate_keypoints_norm_type(\n                np.zeros((1, 5, 2), dtype=dt))\n            assert ntype == \"array[%s]\" % (name,)\n\n        ntype = normalization.estimate_keypoints_norm_type((1, 2))\n        assert ntype == \"tuple[number,size=2]\"\n\n        ntype = normalization.estimate_keypoints_norm_type(\n            ia.Keypoint(x=1, y=2))\n        assert ntype == \"Keypoint\"\n\n        ntype = normalization.estimate_keypoints_norm_type(\n            ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)], shape=(1, 1, 3)))\n        assert ntype == \"KeypointsOnImage\"\n\n        ntype = normalization.estimate_keypoints_norm_type([])\n        assert ntype == \"iterable[empty]\"\n\n        for name, dt in zip([\"float\", \"int\", \"uint\"],\n                            [np.float32, np.int32, np.uint16]):\n            ntype = normalization.estimate_keypoints_norm_type(\n                [np.zeros((5, 2), dtype=dt)])\n            assert ntype == \"iterable-array[%s]\" % (name,)\n\n        ntype = normalization.estimate_keypoints_norm_type([(1, 2)])\n        assert ntype == \"iterable-tuple[number,size=2]\"\n\n        ntype = normalization.estimate_keypoints_norm_type(\n            [ia.Keypoint(x=1, y=2)])\n        assert ntype == \"iterable-Keypoint\"\n\n        ntype = normalization.estimate_keypoints_norm_type([\n            ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)], shape=(1, 1, 3))])\n        assert ntype == \"iterable-KeypointsOnImage\"\n\n        ntype = normalization.estimate_keypoints_norm_type([[]])\n        assert ntype == \"iterable-iterable[empty]\"\n\n        ntype = normalization.estimate_keypoints_norm_type([[(1, 2)]])\n        assert ntype == \"iterable-iterable-tuple[number,size=2]\"\n\n        ntype = normalization.estimate_keypoints_norm_type(\n            [[ia.Keypoint(x=1, y=2)]])\n        assert ntype == \"iterable-iterable-Keypoint\"\n\n        # --\n        # error cases\n        # --\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_keypoints_norm_type(1)\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_keypoints_norm_type(\"foo\")\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_keypoints_norm_type([1])\n\n        # wrong class\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_keypoints_norm_type(\n                ia.HeatmapsOnImage(np.zeros((1, 1, 1), dtype=np.float32),\n                                   shape=(1, 1, 1)))\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_keypoints_norm_type([[[]]])\n\n        # list of list of list of keypoints,\n        # only list of list of keypoints is max\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_keypoints_norm_type(\n                [[[ia.Keypoint(x=1, y=2)]]])\n\n    def test_estimate_bounding_boxes_norm_type(self):\n        ntype = normalization.estimate_bounding_boxes_norm_type(None)\n        assert ntype == \"None\"\n\n        for name, dt in zip([\"float\", \"int\", \"uint\"],\n                            [np.float32, np.int32, np.uint16]):\n            ntype = normalization.estimate_bounding_boxes_norm_type(\n                np.zeros((1, 5, 4), dtype=dt))\n            assert ntype == \"array[%s]\" % (name,)\n\n        ntype = normalization.estimate_bounding_boxes_norm_type((1, 2, 3, 4))\n        assert ntype == \"tuple[number,size=4]\"\n\n        ntype = normalization.estimate_bounding_boxes_norm_type(\n            ia.BoundingBox(x1=1, y1=2, x2=3, y2=4))\n        assert ntype == \"BoundingBox\"\n\n        ntype = normalization.estimate_bounding_boxes_norm_type(\n            ia.BoundingBoxesOnImage(\n                [ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)], shape=(1, 1, 3)))\n        assert ntype == \"BoundingBoxesOnImage\"\n\n        ntype = normalization.estimate_bounding_boxes_norm_type([])\n        assert ntype == \"iterable[empty]\"\n\n        for name, dt in zip([\"float\", \"int\", \"uint\"],\n                            [np.float32, np.int32, np.uint16]):\n            ntype = normalization.estimate_bounding_boxes_norm_type(\n                [np.zeros((5, 4), dtype=dt)])\n            assert ntype == \"iterable-array[%s]\" % (name,)\n\n        ntype = normalization.estimate_bounding_boxes_norm_type([(1, 2, 3, 4)])\n        assert ntype == \"iterable-tuple[number,size=4]\"\n\n        ntype = normalization.estimate_bounding_boxes_norm_type([\n            ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)])\n        assert ntype == \"iterable-BoundingBox\"\n\n        ntype = normalization.estimate_bounding_boxes_norm_type([\n            ia.BoundingBoxesOnImage([ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)],\n                                    shape=(1, 1, 3))])\n        assert ntype == \"iterable-BoundingBoxesOnImage\"\n\n        ntype = normalization.estimate_bounding_boxes_norm_type([[]])\n        assert ntype == \"iterable-iterable[empty]\"\n\n        ntype = normalization.estimate_bounding_boxes_norm_type(\n            [[(1, 2, 3, 4)]])\n        assert ntype == \"iterable-iterable-tuple[number,size=4]\"\n\n        ntype = normalization.estimate_bounding_boxes_norm_type(\n            [[ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)]])\n        assert ntype == \"iterable-iterable-BoundingBox\"\n\n        # --\n        # error cases\n        # --\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_bounding_boxes_norm_type(1)\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_bounding_boxes_norm_type(\"foo\")\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_bounding_boxes_norm_type([1])\n\n        # wrong class\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_bounding_boxes_norm_type(\n                ia.HeatmapsOnImage(\n                    np.zeros((1, 1, 1), dtype=np.float32),\n                    shape=(1, 1, 1))\n            )\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_bounding_boxes_norm_type([[[]]])\n\n        # list of list of list of bounding boxes,\n        # only list of list of bounding boxes is max\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_bounding_boxes_norm_type([[[\n                ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)]]])\n\n    def test_estimate_polygons_norm_type(self):\n        points = [(0, 0), (10, 0), (10, 10)]\n\n        ntype = normalization.estimate_polygons_norm_type(None)\n        assert ntype == \"None\"\n\n        for name, dt in zip([\"float\", \"int\", \"uint\"],\n                            [np.float32, np.int32, np.uint16]):\n            ntype = normalization.estimate_polygons_norm_type(\n                np.zeros((1, 2, 5, 2), dtype=dt)\n            )\n            assert ntype == \"array[%s]\" % (name,)\n\n        ntype = normalization.estimate_polygons_norm_type(\n            ia.Polygon(points)\n        )\n        assert ntype == \"Polygon\"\n\n        ntype = normalization.estimate_polygons_norm_type(\n            ia.PolygonsOnImage(\n                [ia.Polygon(points)], shape=(1, 1, 3))\n        )\n        assert ntype == \"PolygonsOnImage\"\n\n        ntype = normalization.estimate_polygons_norm_type([])\n        assert ntype == \"iterable[empty]\"\n\n        for name, dt in zip([\"float\", \"int\", \"uint\"],\n                            [np.float32, np.int32, np.uint16]):\n            ntype = normalization.estimate_polygons_norm_type(\n                [np.zeros((5, 4), dtype=dt)]\n            )\n            assert ntype == \"iterable-array[%s]\" % (name,)\n\n        ntype = normalization.estimate_polygons_norm_type(points)\n        assert ntype == \"iterable-tuple[number,size=2]\"\n\n        ntype = normalization.estimate_polygons_norm_type(\n            [ia.Keypoint(x=x, y=y) for x, y in points]\n        )\n        assert ntype == \"iterable-Keypoint\"\n\n        ntype = normalization.estimate_polygons_norm_type([ia.Polygon(points)])\n        assert ntype == \"iterable-Polygon\"\n\n        ntype = normalization.estimate_polygons_norm_type(\n            [ia.PolygonsOnImage([ia.Polygon(points)],\n                                shape=(1, 1, 3))]\n        )\n        assert ntype == \"iterable-PolygonsOnImage\"\n\n        ntype = normalization.estimate_polygons_norm_type([[]])\n        assert ntype == \"iterable-iterable[empty]\"\n\n        for name, dt in zip([\"float\", \"int\", \"uint\"],\n                            [np.float32, np.int32, np.uint16]):\n            ntype = normalization.estimate_polygons_norm_type(\n                [[np.zeros((5, 4), dtype=dt)]]\n            )\n            assert ntype == \"iterable-iterable-array[%s]\" % (name,)\n\n        ntype = normalization.estimate_polygons_norm_type([points])\n        assert ntype == \"iterable-iterable-tuple[number,size=2]\"\n\n        ntype = normalization.estimate_polygons_norm_type([[\n            ia.Keypoint(x=x, y=y) for x, y in points\n        ]])\n        assert ntype == \"iterable-iterable-Keypoint\"\n\n        ntype = normalization.estimate_polygons_norm_type(\n            [[ia.Polygon(points)]]\n        )\n        assert ntype == \"iterable-iterable-Polygon\"\n\n        ntype = normalization.estimate_polygons_norm_type([[[]]])\n        assert ntype == \"iterable-iterable-iterable[empty]\"\n\n        ntype = normalization.estimate_polygons_norm_type([[points]])\n        assert ntype == \"iterable-iterable-iterable-tuple[number,size=2]\"\n\n        ntype = normalization.estimate_polygons_norm_type(\n            [[[ia.Keypoint(x=x, y=y) for x, y in points]]]\n        )\n        assert ntype == \"iterable-iterable-iterable-Keypoint\"\n\n        # --\n        # error cases\n        # --\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_polygons_norm_type(1)\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_polygons_norm_type(\"foo\")\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_polygons_norm_type([1])\n\n        # wrong class\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_polygons_norm_type(\n                ia.HeatmapsOnImage(\n                    np.zeros((1, 1, 1), dtype=np.float32),\n                    shape=(1, 1, 1))\n            )\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_polygons_norm_type([[[[]]]])\n\n        # list of list of list of polygons,\n        # only list of list of polygons is max\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_polygons_norm_type([[[\n                ia.Polygon(points)]]]\n            )\n    \n    def test_estimate_line_strings_norm_type(self):\n        points = [(0, 0), (10, 0), (10, 10)]\n\n        ntype = normalization.estimate_line_strings_norm_type(None)\n        assert ntype == \"None\"\n\n        for name, dt in zip([\"float\", \"int\", \"uint\"],\n                            [np.float32, np.int32, np.uint16]):\n            ntype = normalization.estimate_line_strings_norm_type(\n                np.zeros((1, 2, 5, 2), dtype=dt)\n            )\n            assert ntype == \"array[%s]\" % (name,)\n\n        ntype = normalization.estimate_line_strings_norm_type(\n            ia.LineString(points)\n        )\n        assert ntype == \"LineString\"\n\n        ntype = normalization.estimate_line_strings_norm_type(\n            ia.LineStringsOnImage(\n                [ia.LineString(points)], shape=(1, 1, 3))\n        )\n        assert ntype == \"LineStringsOnImage\"\n\n        ntype = normalization.estimate_line_strings_norm_type([])\n        assert ntype == \"iterable[empty]\"\n\n        for name, dt in zip([\"float\", \"int\", \"uint\"],\n                            [np.float32, np.int32, np.uint16]):\n            ntype = normalization.estimate_line_strings_norm_type(\n                [np.zeros((5, 4), dtype=dt)]\n            )\n            assert ntype == \"iterable-array[%s]\" % (name,)\n\n        ntype = normalization.estimate_line_strings_norm_type(points)\n        assert ntype == \"iterable-tuple[number,size=2]\"\n\n        ntype = normalization.estimate_line_strings_norm_type(\n            [ia.Keypoint(x=x, y=y) for x, y in points]\n        )\n        assert ntype == \"iterable-Keypoint\"\n\n        ntype = normalization.estimate_line_strings_norm_type(\n            [ia.LineString(points)])\n        assert ntype == \"iterable-LineString\"\n\n        ntype = normalization.estimate_line_strings_norm_type(\n            [ia.LineStringsOnImage([ia.LineString(points)],\n                                   shape=(1, 1, 3))]\n        )\n        assert ntype == \"iterable-LineStringsOnImage\"\n\n        ntype = normalization.estimate_line_strings_norm_type([[]])\n        assert ntype == \"iterable-iterable[empty]\"\n\n        for name, dt in zip([\"float\", \"int\", \"uint\"],\n                            [np.float32, np.int32, np.uint16]):\n            ntype = normalization.estimate_line_strings_norm_type(\n                [[np.zeros((5, 4), dtype=dt)]]\n            )\n            assert ntype == \"iterable-iterable-array[%s]\" % (name,)\n\n        ntype = normalization.estimate_line_strings_norm_type([points])\n        assert ntype == \"iterable-iterable-tuple[number,size=2]\"\n\n        ntype = normalization.estimate_line_strings_norm_type([[\n            ia.Keypoint(x=x, y=y) for x, y in points\n        ]])\n        assert ntype == \"iterable-iterable-Keypoint\"\n\n        ntype = normalization.estimate_line_strings_norm_type(\n            [[ia.LineString(points)]]\n        )\n        assert ntype == \"iterable-iterable-LineString\"\n\n        ntype = normalization.estimate_line_strings_norm_type([[[]]])\n        assert ntype == \"iterable-iterable-iterable[empty]\"\n\n        ntype = normalization.estimate_line_strings_norm_type([[points]])\n        assert ntype == \"iterable-iterable-iterable-tuple[number,size=2]\"\n\n        ntype = normalization.estimate_line_strings_norm_type(\n            [[[ia.Keypoint(x=x, y=y) for x, y in points]]]\n        )\n        assert ntype == \"iterable-iterable-iterable-Keypoint\"\n\n        # --\n        # error cases\n        # --\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_line_strings_norm_type(1)\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_line_strings_norm_type(\"foo\")\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_line_strings_norm_type([1])\n\n        # wrong class\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_line_strings_norm_type(\n                ia.HeatmapsOnImage(\n                    np.zeros((1, 1, 1), dtype=np.float32),\n                    shape=(1, 1, 1))\n            )\n\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_line_strings_norm_type([[[[]]]])\n\n        # list of list of list of LineStrings,\n        # only list of list of LineStrings is max\n        with self.assertRaises(AssertionError):\n            _ntype = normalization.estimate_line_strings_norm_type([[[\n                ia.LineString(points)]]]\n            )\n"
  },
  {
    "path": "test/augmentables/test_polys.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport warnings\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport six.moves as sm\nimport shapely\nimport shapely.geometry\n\nimport imgaug as ia\nimport imgaug.random as iarandom\nfrom imgaug.testutils import reseed, wrap_shift_deprecation, assertWarns\nfrom imgaug.augmentables.polys import _ConcavePolygonRecoverer\n\n\nclass TestPolygon___init__(unittest.TestCase):\n    def test_exterior_is_list_of_keypoints(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=0.5, y=2.5)]\n\n        poly = ia.Polygon(kps)\n\n        assert poly.exterior.dtype.name == \"float32\"\n        assert np.allclose(\n            poly.exterior,\n            np.float32([\n                [0.0, 0.0],\n                [1.0, 1.0],\n                [0.5, 2.5]\n            ])\n        )\n\n    def test_exterior_is_list_of_tuples_of_floats(self):\n        poly = ia.Polygon([(0.0, 0.0), (1.0, 1.0), (0.5, 2.5)])\n\n        assert poly.exterior.dtype.name == \"float32\"\n        assert np.allclose(\n            poly.exterior,\n            np.float32([\n                [0.0, 0.0],\n                [1.0, 1.0],\n                [0.5, 2.5]\n            ])\n        )\n\n    def test_exterior_is_list_of_tuples_of_ints(self):\n        poly = ia.Polygon([(0, 0), (1, 1), (1, 3)])\n\n        assert poly.exterior.dtype.name == \"float32\"\n        assert np.allclose(\n            poly.exterior,\n            np.float32([\n                [0.0, 0.0],\n                [1.0, 1.0],\n                [1.0, 3.0]\n            ])\n        )\n\n    def test_exterior_is_float32_array(self):\n        poly = ia.Polygon(\n            np.float32([\n                [0.0, 0.0],\n                [1.0, 1.0],\n                [0.5, 2.5]\n            ])\n        )\n\n        assert poly.exterior.dtype.name == \"float32\"\n        assert np.allclose(\n            poly.exterior,\n            np.float32([\n                [0.0, 0.0],\n                [1.0, 1.0],\n                [0.5, 2.5]\n            ])\n        )\n\n    def test_exterior_is_float64_array(self):\n        poly = ia.Polygon(\n            np.float64([\n                [0.0, 0.0],\n                [1.0, 1.0],\n                [0.5, 2.5]\n            ])\n        )\n\n        assert poly.exterior.dtype.name == \"float32\"\n        assert np.allclose(\n            poly.exterior,\n            np.float32([\n                [0.0, 0.0],\n                [1.0, 1.0],\n                [0.5, 2.5]\n            ])\n        )\n\n    def test_exterior_is_empty_list(self):\n        poly = ia.Polygon([])\n\n        assert poly.exterior.dtype.name == \"float32\"\n        assert poly.exterior.shape == (0, 2)\n\n    def test_exterior_is_empty_array(self):\n        poly = ia.Polygon(np.zeros((0, 2), dtype=np.float32))\n\n        assert poly.exterior.dtype.name == \"float32\"\n        assert poly.exterior.shape == (0, 2)\n\n    def test_fails_if_exterior_is_array_with_wrong_shape(self):\n        with self.assertRaises(AssertionError):\n            _ = ia.Polygon(np.zeros((8,), dtype=np.float32))\n\n    def test_label_is_none(self):\n        poly = ia.Polygon([(0, 0)])\n\n        assert poly.label is None\n\n    def test_label_is_string(self):\n        poly = ia.Polygon([(0, 0)], label=\"test\")\n\n        assert poly.label == \"test\"\n\n\nclass TestPolygon_coords(unittest.TestCase):\n    def test_with_three_points(self):\n        poly = ia.Polygon([(0, 0), (1, 0.5), (1.5, 2.0)])\n        assert poly.coords is poly.exterior\n\n\nclass TestPolygon_xx(unittest.TestCase):\n    def test_filled_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1.5, 0), (4.1, 1), (2.9, 2.0)])\n\n        assert poly.xx.dtype.name == \"float32\"\n        assert np.allclose(poly.xx, np.float32([0.0, 1.0, 1.5, 4.1, 2.9]))\n\n    def test_empty_polygon(self):\n        poly = ia.Polygon([])\n\n        assert poly.xx.dtype.name == \"float32\"\n        assert poly.xx.shape == (0,)\n\n\nclass TestPolygon_yy(unittest.TestCase):\n    def test_filled_polygon(self):\n        poly = ia.Polygon([(0, 0), (0, 1), (0, 1.5), (1, 4.1), (2.0, 2.9)])\n\n        assert poly.yy.dtype.name == \"float32\"\n        assert np.allclose(poly.yy, np.float32([0.0, 1.0, 1.5, 4.1, 2.9]))\n\n    def test_empty_polygon(self):\n        poly = ia.Polygon([])\n\n        assert poly.yy.dtype.name == \"float32\"\n        assert poly.yy.shape == (0,)\n\n\nclass TestPolygon_xx_int(unittest.TestCase):\n    def test_filled_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1.5, 0), (4.1, 1), (2.9, 2.0)])\n\n        assert poly.xx_int.dtype.name == \"int32\"\n        assert np.allclose(poly.xx_int, np.int32([0, 1, 2, 4, 3]))\n\n    def test_empty_polygon(self):\n        poly = ia.Polygon([])\n\n        assert poly.xx_int.dtype.name == \"int32\"\n        assert poly.xx_int.shape == (0,)\n\n\nclass TestPolygon_yy_int(unittest.TestCase):\n    def test_filled_polygon(self):\n        poly = ia.Polygon([(0, 0), (0, 1), (0, 1.5), (1, 4.1), (2.0, 2.9)])\n\n        assert poly.yy_int.dtype.name == \"int32\"\n        assert np.allclose(poly.yy_int, np.int32([0, 1, 2, 4, 3]))\n\n    def test_empty_polygon(self):\n        poly = ia.Polygon([])\n\n        assert poly.yy_int.dtype.name == \"int32\"\n        assert poly.yy_int.shape == (0,)\n\n\nclass TestPolygon_is_valid(unittest.TestCase):\n    def test_filled_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert poly.is_valid\n\n    def test_empty_polygon(self):\n        poly = ia.Polygon([])\n        assert not poly.is_valid\n\n    def test_polygon_with_one_point(self):\n        poly = ia.Polygon([(0, 0)])\n        assert not poly.is_valid\n\n    def test_polygon_with_two_points(self):\n        poly = ia.Polygon([(0, 0), (1, 0)])\n        assert not poly.is_valid\n\n    def test_polygon_with_self_intersection(self):\n        # self intersection around the line segment from (0, 1) to (0, 0)\n        poly = ia.Polygon([(0, 0), (1, 0), (-1, 0.5), (1, 1), (0, 1)])\n        assert not poly.is_valid\n\n    def test_polygon_with_consecutive_identical_points(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 0), (1, 1), (0, 1)])\n        assert poly.is_valid\n\n\nclass TestPolygon_area(unittest.TestCase):\n    def test_square_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert 1.0 - 1e-8 < poly.area < 1.0 + 1e-8\n\n    def test_rectangular_polygon(self):\n        poly = ia.Polygon([(0, 0), (2, 0), (2, 1), (0, 1)])\n        assert 2.0 - 1e-8 < poly.area < 2.0 + 1e-8\n\n    def test_triangular_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 1), (0, 1)])\n        assert 1/2 - 1e-8 < poly.area < 1/2 + 1e-8\n\n    def test_polygon_with_two_points(self):\n        poly = ia.Polygon([(0, 0), (1, 1)])\n        assert np.isclose(poly.area, 0.0)\n\n    def test_polygon_with_one_point(self):\n        poly = ia.Polygon([(0, 0)])\n        assert np.isclose(poly.area, 0.0)\n\n    def test_polygon_with_zero_points(self):\n        poly = ia.Polygon([])\n        assert np.isclose(poly.area, 0.0)\n\n\nclass TestPolygon_height(unittest.TestCase):\n    def test_square_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert np.allclose(poly.height, 1.0, atol=1e-8, rtol=0)\n\n    def test_rectangular_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 2), (0, 2)])\n        assert np.allclose(poly.height, 2.0, atol=1e-8, rtol=0)\n\n    def test_triangular_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 1), (0, 1)])\n        assert np.allclose(poly.height, 1.0, atol=1e-8, rtol=0)\n\n    def test_polygon_with_two_points(self):\n        poly = ia.Polygon([(0, 0), (1, 1)])\n        assert np.allclose(poly.height, 1.0, atol=1e-8, rtol=0)\n\n    def test_polygon_with_one_point(self):\n        poly = ia.Polygon([(0, 0)])\n        assert np.allclose(poly.height, 0.0, atol=1e-8, rtol=0)\n\n\nclass TestPolygon_width(unittest.TestCase):\n    def test_square_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert np.allclose(poly.width, 1.0, atol=1e-8, rtol=0)\n\n    def test_rectangular_polygon(self):\n        poly = ia.Polygon([(0, 0), (2, 0), (2, 1), (0, 1)])\n        assert np.allclose(poly.width, 2.0, atol=1e-8, rtol=0)\n\n    def test_triangular_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 1), (0, 1)])\n        assert np.allclose(poly.width, 1.0, atol=1e-8, rtol=0)\n\n    def test_polygon_with_two_points(self):\n        poly = ia.Polygon([(0, 0), (1, 1)])\n        assert np.allclose(poly.width, 1.0, atol=1e-8, rtol=0)\n\n    def test_polygon_with_one_point(self):\n        poly = ia.Polygon([(0, 0)])\n        assert np.allclose(poly.width, 0.0, atol=1e-8, rtol=0)\n\n\nclass TestPolygon_project_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, poly, from_shape, to_shape):\n        return poly.project_(from_shape, to_shape)\n\n    def test_project_square_to_image_of_identical_shape(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n\n        poly_proj = self._func(poly, (1, 1), (1, 1))\n\n        assert poly_proj.exterior.dtype.name == \"float32\"\n        assert poly_proj.exterior.shape == (4, 2)\n        assert np.allclose(\n            poly_proj.exterior,\n            np.float32([\n                [0, 0],\n                [1, 0],\n                [1, 1],\n                [0, 1]\n            ])\n        )\n\n    def test_project_square_to_image_with_twice_the_height_and_width(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n\n        poly_proj = self._func(poly, (1, 1), (2, 2))\n\n        assert poly_proj.exterior.dtype.name == \"float32\"\n        assert poly_proj.exterior.shape == (4, 2)\n        assert np.allclose(\n            poly_proj.exterior,\n            np.float32([\n                [0, 0],\n                [2, 0],\n                [2, 2],\n                [0, 2]\n            ])\n        )\n\n    def test_project_square_to_image_with_twice_the_height_but_same_width(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n\n        poly_proj = self._func(poly, (1, 1), (2, 1))\n\n        assert poly_proj.exterior.dtype.name == \"float32\"\n        assert poly_proj.exterior.shape == (4, 2)\n        assert np.allclose(\n            poly_proj.exterior,\n            np.float32([\n                [0, 0],\n                [1, 0],\n                [1, 2],\n                [0, 2]\n            ])\n        )\n\n    def test_project_empty_exterior(self):\n        poly = ia.Polygon([])\n        poly_proj = self._func(poly, (1, 1), (2, 2))\n        assert poly_proj.exterior.dtype.name == \"float32\"\n        assert poly_proj.exterior.shape == (0, 2)\n\n    def test_inplaceness(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n\n        poly2 = self._func(poly, (1, 1), (1, 1))\n\n        if self._is_inplace:\n            assert poly is poly2\n        else:\n            assert poly is not poly2\n\n\nclass TestPolygon_project(TestPolygon_project_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, poly, from_shape, to_shape):\n        return poly.project(from_shape, to_shape)\n\n\nclass TestPolygon_find_closest_point_idx(unittest.TestCase):\n    def test_without_return_distance(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        coords = [(0, 0), (1, 0), (1.0001, -0.001), (0.2, 0.2)]\n        expected_indices = [0, 1, 1, 0]\n\n        for (x, y), expected_index in zip(coords, expected_indices):\n            with self.subTest(x=x, y=0):\n                closest_idx = poly.find_closest_point_index(x=x, y=y)\n                assert closest_idx == expected_index\n\n    def test_with_return_distance(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        coords = [(0, 0), (0.1, 0.15), (0.9, 0.15)]\n        expected_indices = [0, 0, 1]\n        expected_distances = [\n            0.0,\n            np.sqrt((0.1**2) + (0.15**2)),\n            np.sqrt(((1.0-0.9)**2) + (0.15**2))\n        ]\n\n        gen = zip(coords, expected_indices, expected_distances)\n        for (x, y), expected_index, expected_dist in gen:\n            with self.subTest(x=x, y=y):\n                closest_idx, distance = poly.find_closest_point_index(\n                    x=x, y=y, return_distance=True)\n                assert closest_idx == expected_index\n                assert np.allclose(distance, expected_dist)\n\n    def test_fails_for_empty_exterior(self):\n        poly = ia.Polygon([])\n        with self.assertRaises(AssertionError):\n            _ = poly.find_closest_point_index(x=0, y=0)\n\n\nclass TestPolygon_compute_out_of_image_area(unittest.TestCase):\n    def test_fully_inside_image_plane(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        image_shape = (10, 20, 3)\n        area_ooi = poly.compute_out_of_image_area(image_shape)\n        assert np.isclose(area_ooi, 0.0)\n\n    def test_partially_outside_of_image_plane(self):\n        poly = ia.Polygon([(-1, 0), (1, 0), (1, 2), (-1, 2)])\n        image_shape = (10, 20, 3)\n        area_ooi = poly.compute_out_of_image_area(image_shape)\n        assert np.isclose(area_ooi, 2.0)\n\n    def test_fully_outside_of_image_plane(self):\n        poly = ia.Polygon([(-1, 0), (0, 0), (0, 1), (-1, 1)])\n        image_shape = (10, 20, 3)\n        area_ooi = poly.compute_out_of_image_area(image_shape)\n        assert np.isclose(area_ooi, 1.0)\n\n    def test_multiple_polygons_after_clip(self):\n        # two polygons inside the image area remain after clipping\n        # result is (area - poly1 - poly2) or here the part of the polygon\n        # that is left of the y-axis (x=0.0)\n        poly = ia.Polygon([(-10, 0), (5, 0), (5, 5), (-5, 5),\n                           (-5, 10), (5, 10),\n                           (5, 15), (-10, 15)])\n        image_shape = (15, 10, 3)\n\n        area_ooi = poly.compute_out_of_image_area(image_shape)\n\n        # the part left of the y-axis is not exactly square, but has a hole\n        # on its right (vertically centered), hence we have to subtract 5*5\n        assert np.isclose(area_ooi, 10*15 - 5*5)\n\n\nclass TestPolygon_compute_out_of_image_fraction(unittest.TestCase):\n    def test_polygon_with_zero_points(self):\n        poly = ia.Polygon([])\n        image_shape = (10, 10, 3)\n        factor = poly.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.0)\n\n    def test_polygon_with_one_point(self):\n        poly = ia.Polygon([(1.0, 1.0)])\n        image_shape = (10, 10, 3)\n        factor = poly.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.0)\n\n    def test_polygon_with_one_point_ooi(self):\n        poly = ia.Polygon([(-1.0, 1.0)])\n        image_shape = (10, 10, 3)\n        factor = poly.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 1.0)\n\n    def test_polygon_with_two_points(self):\n        poly = ia.Polygon([(1.0, 1.0), (2.0, 1.0)])\n        image_shape = (10, 10, 3)\n        factor = poly.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.0)\n\n    def test_polygon_with_two_points_one_ooi(self):\n        poly = ia.Polygon([(9.0, 1.0), (11.0, 1.0)])\n        image_shape = (10, 10, 3)\n        factor = poly.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.5, atol=1e-3)\n\n    def test_polygon_with_three_points_as_line(self):\n        poly = ia.Polygon([(9.0, 1.0), (10.0, 1.0), (11.0, 1.0)])\n        image_shape = (10, 10, 3)\n        factor = poly.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.5, atol=1e-3)\n\n    def test_standard_polygon_not_ooi(self):\n        poly = ia.Polygon([(1.0, 1.0), (2.0, 1.0), (2.0, 2.0)])\n        image_shape = (10, 10, 3)\n        factor = poly.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.0)\n\n    def test_standard_polygon_partially_ooi(self):\n        poly = ia.Polygon([(9.0, 1.0), (11.0, 1.0), (11.0, 3.0), (9.0, 3.0)])\n        image_shape = (10, 10, 3)\n        factor = poly.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 0.5, atol=1e-3)\n\n    def test_standard_polygon_fully_ooi(self):\n        poly = ia.Polygon([(11.0, 1.0), (13.0, 1.0), (13.0, 3.0), (11.0, 3.0)])\n        image_shape = (10, 10, 3)\n        factor = poly.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 1.0)\n\n    def test_zero_sized_image_axes(self):\n        poly = ia.Polygon([(1.0, 1.0), (2.0, 1.0), (2.0, 2.0)])\n        image_shape = (0, 0, 3)\n        factor = poly.compute_out_of_image_fraction(image_shape)\n        assert np.isclose(factor, 1.0)\n\n\nclass TestPolygon_is_fully_within_image(unittest.TestCase):\n    def test_barely_within_image__shape_as_3d_tuple(self):\n        poly = ia.Polygon([(0, 0), (0.999, 0), (0.999, 0.999), (0, 0.999)])\n        assert poly.is_fully_within_image((1, 1, 3))\n\n    def test_barely_within_image__shape_as_2d_tuple(self):\n        poly = ia.Polygon([(0, 0), (0.999, 0), (0.999, 0.999), (0, 0.999)])\n        assert poly.is_fully_within_image((1, 1))\n\n    def test_barely_within_image__shape_as_ndarray(self):\n        poly = ia.Polygon([(0, 0), (0.999, 0), (0.999, 0.999), (0, 0.999)])\n        assert poly.is_fully_within_image(\n            np.zeros((1, 1, 3), dtype=np.uint8)\n        )\n\n    def test_right_and_bottom_sides_overlap__shape_as_3d_tuple(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert not poly.is_fully_within_image((1, 1, 3))\n\n    def test_right_and_bottom_sides_overlap__shape_as_2d_tuple(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert not poly.is_fully_within_image((1, 1))\n\n    def test_right_and_bottom_sides_overlap__shape_as_ndarray(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert not poly.is_fully_within_image(\n            np.zeros((1, 1, 3), dtype=np.uint8)\n        )\n\n    def test_far_outside_of_image__shape_as_3d_tuple(self):\n        poly = ia.Polygon([(100, 100), (101, 100), (101, 101), (100, 101)])\n        assert not poly.is_fully_within_image((1, 1, 3))\n\n    def test_exterior_empty_fails(self):\n        poly = ia.Polygon([])\n        with self.assertRaises(Exception):\n            _ = poly.is_fully_within_image((1, 1, 3))\n\n\nclass TestPolygon_is_partly_within_image(unittest.TestCase):\n    def test_barely_within_image__shape_as_3d_tuple(self):\n        poly = ia.Polygon([(0, 0), (0.999, 0), (0.999, 0.999), (0, 0.999)])\n        assert poly.is_partly_within_image((1, 1, 3))\n\n    def test_barely_within_image__shape_as_2d_tuple(self):\n        poly = ia.Polygon([(0, 0), (0.999, 0), (0.999, 0.999), (0, 0.999)])\n        assert poly.is_partly_within_image((1, 1))\n\n    def test_barely_within_image__shape_as_ndarray(self):\n        poly = ia.Polygon([(0, 0), (0.999, 0), (0.999, 0.999), (0, 0.999)])\n        assert poly.is_partly_within_image(\n            np.zeros((1, 1, 3), dtype=np.uint8)\n        )\n\n    def test_right_and_bottom_sides_overlap__shape_as_3d_tuple(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert poly.is_partly_within_image((1, 1, 3))\n\n    def test_right_and_bottom_sides_overlap__shape_as_2d_tuple(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert poly.is_partly_within_image((1, 1))\n\n    def test_right_and_bottom_sides_overlap__shape_as_ndarray(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert poly.is_partly_within_image(np.zeros((1, 1, 3), dtype=np.uint8))\n\n    def test_far_outside_of_image__shape_as_3d_tuple(self):\n        poly = ia.Polygon([(100, 100), (101, 100), (101, 101), (100, 101)])\n        assert not poly.is_partly_within_image((1, 1, 3))\n\n    def test_far_outside_of_image__shape_as_2d_tuple(self):\n        poly = ia.Polygon([(100, 100), (101, 100), (101, 101), (100, 101)])\n        assert not poly.is_partly_within_image((1, 1))\n\n    def test_far_outside_of_image__shape_as_ndarray(self):\n        poly = ia.Polygon([(100, 100), (101, 100), (101, 101), (100, 101)])\n        assert not poly.is_partly_within_image(\n            np.zeros((1, 1, 3), dtype=np.uint8)\n        )\n\n    def test_exterior_empty_fails(self):\n        poly = ia.Polygon([])\n        with self.assertRaises(Exception):\n            _ = poly.is_partly_within_image((1, 1, 3))\n\n\nclass TestPolygon_is_out_of_image(unittest.TestCase):\n    def test_barely_within_image(self):\n        shapes = [(1, 1, 3), (1, 1), np.zeros((1, 1, 3), dtype=np.uint8)]\n        for shape in shapes:\n            shape_str = (\n                str(shape) if isinstance(shape, tuple) else str(shape.shape)\n            )\n            with self.subTest(shape=shape_str):\n                poly = ia.Polygon([(0, 0), (0.999, 0), (0.999, 0.999), (0, 0.999)])\n                is_ooi = poly.is_out_of_image\n                assert not is_ooi(shape, partly=False, fully=False)\n                assert not is_ooi(shape, partly=True, fully=False)\n                assert not is_ooi(shape, partly=False, fully=True)\n                assert not is_ooi(shape, partly=True, fully=True)\n\n    def test_right_and_bottom_sides_overlap__shape_as_ndarray(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        shape = np.zeros((1, 1, 3), dtype=np.uint8)\n\n        assert not poly.is_out_of_image(shape, partly=False, fully=False)\n        assert poly.is_out_of_image(shape, partly=True, fully=False)\n        assert not poly.is_out_of_image(shape, partly=False, fully=True)\n        assert poly.is_out_of_image(shape, partly=True, fully=True)\n\n    def test_far_outside_of_image__shape_as_3d_tuple(self):\n        poly = ia.Polygon([(100, 100), (101, 100), (101, 101), (100, 101)])\n        shape = (1, 1, 3)\n\n        assert not poly.is_out_of_image(shape, partly=False, fully=False)\n        assert not poly.is_out_of_image(shape, partly=True, fully=False)\n        assert poly.is_out_of_image(shape, partly=False, fully=True)\n        assert poly.is_out_of_image(shape, partly=True, fully=True)\n\n    def test_triangle_partially_outside_of_image(self):\n        poly = ia.Polygon([(8, 11), (11, 8), (11, 11)])\n        assert not poly.is_out_of_image((100, 100, 3), fully=True, partly=True)\n        assert not poly.is_out_of_image((10, 10, 3), fully=True, partly=False)\n        assert poly.is_out_of_image((10, 10, 3), fully=False, partly=True)\n\n    def test_rectangle_with_all_corners_outside_of_the_image(self):\n        poly = ia.Polygon([(-1.0, -1.0), (2.0, -1.0), (2.0, 2.0), (-1.0, 2.0)])\n        assert not poly.is_out_of_image((100, 100, 3), fully=True, partly=False)\n        assert poly.is_out_of_image((100, 100, 3), fully=False, partly=True)\n        assert not poly.is_out_of_image((1, 1, 3), fully=True, partly=False)\n        assert poly.is_out_of_image((1, 1, 3), fully=False, partly=True)\n        assert poly.is_out_of_image((1, 1, 3), fully=True, partly=True)\n\n    def test_polygon_with_two_points(self):\n        poly = ia.Polygon([(2.0, 2.0), (10.0, 2.0)])\n        assert not poly.is_out_of_image((100, 100, 3), fully=True, partly=False)\n        assert not poly.is_out_of_image((100, 100, 3), fully=False, partly=True)\n        assert not poly.is_out_of_image((3, 3, 3), fully=True, partly=False)\n        assert poly.is_out_of_image((3, 3, 3), fully=False, partly=True)\n        assert poly.is_out_of_image((1, 1, 3), fully=True, partly=False)\n        assert not poly.is_out_of_image((1, 1, 3), fully=False, partly=True)\n\n    def test_polygon_with_one_point(self):\n        poly = ia.Polygon([(2.0, 2.0)])\n        assert not poly.is_out_of_image((100, 100, 3), fully=True, partly=False)\n        assert not poly.is_out_of_image((100, 100, 3), fully=False, partly=True)\n        assert poly.is_out_of_image((1, 1, 3), fully=True, partly=False)\n        assert not poly.is_out_of_image((1, 1, 3), fully=False, partly=True)\n\n    def test_polygon_with_zero_points_fails(self):\n        poly = ia.Polygon([])\n        got_exception = False\n        try:\n            poly.is_out_of_image((1, 1, 3))\n        except Exception as exc:\n            assert (\n                \"Cannot determine whether the polygon is inside the \"\n                \"image\" in str(exc))\n            got_exception = True\n        assert got_exception\n\n\nclass TestPolygon_cut_out_of_image(unittest.TestCase):\n    @mock.patch(\"imgaug.augmentables.polys.Polygon.clip_out_of_image\")\n    def test_warns_of_deprecation(self, mock_clip):\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            polygon = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n            shape = (1, 1)\n            _ = polygon.cut_out_of_image(shape)\n\n        mock_clip.assert_called_once_with(shape)\n        assert \"is deprecated\" in str(caught_warnings[0].message)\n\n\nclass TestPolygon_clip_out_of_image(unittest.TestCase):\n    def test_polygon_inside_of_image(self):\n        # poly inside image\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)], label=None)\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        multipoly_clipped = poly.clip_out_of_image(image)\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 1\n        assert multipoly_clipped[0].exterior_almost_equals(poly.exterior)\n        assert multipoly_clipped[0].label is None\n\n    def test_polygon_half_outside_of_image(self):\n        # square poly shifted by x=0.5, y=0.5 => half out of image\n        poly = ia.Polygon([(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)],\n                          label=\"test\")\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        multipoly_clipped = poly.clip_out_of_image(image)\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 1\n        assert multipoly_clipped[0].exterior_almost_equals(np.float32([\n            [0.5, 0.5],\n            [1.0, 0.5],\n            [1.0, 1.0],\n            [0.5, 1.0]\n        ]))\n        assert multipoly_clipped[0].label == \"test\"\n\n    def test_single_edge_intersecting_with_image_edge(self):\n        # square poly with a single edge intersecting the image (issue #310)\n        poly = ia.Polygon([(-1.0, 0.0), (0.0, 0.0), (0.0, 1.0), (-1.0, 1.0)])\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        multipoly_clipped = poly.clip_out_of_image(image)\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 0\n\n    def test_tiny_area_around_image_edge_intersecting(self):\n        # square poly with a tiny area on the left image edge intersecting with\n        # the image\n        offset = 1e-4\n        poly = ia.Polygon([(-1.0, 0.0), (0.0+offset, 0.0),\n                           (0.0+offset, 1.0), (-1.0, 1.0)])\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        multipoly_clipped = poly.clip_out_of_image(image)\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 1\n        assert multipoly_clipped[0].exterior_almost_equals(np.float32([\n            [0.0, 0.0],\n            [0.0+offset, 0.0],\n            [0.0+offset, 1.0],\n            [0.0, 1.0]\n        ]))\n\n    def test_single_point_intersecting_with_image(self):\n        # square poly with a single point intersecting the image (issue #310)\n        poly = ia.Polygon([(-1.0, -1.0), (0.0, -1.0), (0.0, 0.0), (-1.0, 0.0)])\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        multipoly_clipped = poly.clip_out_of_image(image)\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 0\n\n    def test_tiny_area_around_image_corner_point_intersecting_with_image(self):\n        # square poly with a tiny area around the top left image corner\n        # intersecting with the the image\n        offset = 1e-4\n        poly = ia.Polygon([(-1.0, -1.0), (0.0, -1.0),\n                           (0.0+offset, 0.0+offset), (-1.0, 0.0)])\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        multipoly_clipped = poly.clip_out_of_image(image)\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 1\n        assert multipoly_clipped[0].exterior_almost_equals(np.float32([\n            [0.0, 0.0],\n            [0.0+offset, 0.0],\n            [0.0+offset, 0.0+offset],\n            [0.0, 0.0+offset]\n        ]))\n\n    def test_polygon_clipped_to_two_separate_polygons(self):\n        # non-square poly, with one rectangle on the left side of the image\n        # and one on the right side, both sides are connected by a thin strip\n        # below the image after clipping it should become two rectangles\n        poly = ia.Polygon([(-0.1, 0.0), (0.4, 0.0), (0.4, 1.1), (0.6, 1.1),\n                           (0.6, 0.0), (1.1, 0.0), (1.1, 1.2), (-0.1, 1.2)],\n                          label=\"test\")\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        multipoly_clipped = poly.clip_out_of_image(image)\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 2\n        assert multipoly_clipped[0].exterior_almost_equals(np.float32([\n            [0.0, 0.0],\n            [0.4, 0.0],\n            [0.4, 1.0],\n            [0.0, 1.0]\n        ]))\n        assert multipoly_clipped[0].label == \"test\"\n        assert multipoly_clipped[1].exterior_almost_equals(np.float32([\n            [0.6, 0.0],\n            [1.0, 0.0],\n            [1.0, 1.0],\n            [0.6, 1.0]\n        ]))\n        assert multipoly_clipped[0].label == \"test\"\n\n    def test_polygon_fully_outside_of_the_image(self):\n        # poly outside of image\n        poly = ia.Polygon([(10.0, 10.0), (11,.0, 10.0), (11.0, 11.0)])\n        multipoly_clipped = poly.clip_out_of_image((5, 5, 3))\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 0\n\n    def test_small_intersection_with_image_one_poly_point_inside_image(self):\n        # poly area partially inside image\n        # and one point is inside the image\n        poly = ia.Polygon([(50, 50), (110, 50), (110, 110), (50, 110)])\n        multipoly_clipped = poly.clip_out_of_image((100, 100, 3))\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 1\n        assert multipoly_clipped[0].exterior_almost_equals(np.float32([\n            [50, 50],\n            [100, 50],\n            [100, 100],\n            [50, 100]\n        ]))\n\n    def test_small_intersection_with_image_no_poly_point_inside_image(self):\n        # poly area partially inside image,\n        # but not a single point is inside the image\n        poly = ia.Polygon([(100+0.5*100, 0),\n                           (100+0.5*100, 100+0.5*100),\n                           (0, 100+0.5*100)])\n        multipoly_clipped = poly.clip_out_of_image((100, 100, 3))\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 1\n        assert multipoly_clipped[0].exterior_almost_equals(np.float32([\n            [100, 0.5*100],\n            [100, 100],\n            [0.5*100, 100]\n        ]))\n\n    def test_polygon_with_two_points_that_is_not_clipped(self):\n        # polygon with two points\n        poly = ia.Polygon([(2.0, 2.0), (10.0, 2.0)])\n        multipoly_clipped = poly.clip_out_of_image((100, 100, 3))\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 1\n        assert multipoly_clipped[0].exterior_almost_equals(np.float32([\n            [2.0, 2.0],\n            [10.0, 2.0]\n        ]))\n\n    def test_polygon_with_two_points_that_is_clipped(self):\n        poly = ia.Polygon([(2.0, 2.0), (10.0, 2.0)])\n        multipoly_clipped = poly.clip_out_of_image((3, 3, 3))\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 1\n        assert multipoly_clipped[0].exterior_almost_equals(np.float32([\n            [2.0, 2.0],\n            [3.0, 2.0]\n        ]), max_distance=1e-3)\n\n    def test_polygon_with_one_point_that_is_not_clipped(self):\n        # polygon with a single point\n        poly = ia.Polygon([(2.0, 2.0)])\n        multipoly_clipped = poly.clip_out_of_image((3, 3, 3))\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 1\n        assert multipoly_clipped[0].exterior_almost_equals(np.float32([\n            [2.0, 2.0]\n        ]))\n\n    def test_polygon_with_one_point_that_is_clipped(self):\n        poly = ia.Polygon([(2.0, 2.0)])\n        multipoly_clipped = poly.clip_out_of_image((1, 1, 3))\n        assert isinstance(multipoly_clipped, list)\n        assert len(multipoly_clipped) == 0\n\n\nclass TestPolygon_shift_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, poly, *args, **kwargs):\n        def _func_impl():\n            return poly.shift_(*args, **kwargs)\n\n        return wrap_shift_deprecation(_func_impl, *args, **kwargs)\n\n    @property\n    def poly(self):\n        return ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)], label=\"test\")\n\n    def test_shift_along_xy(self):\n        poly_shifted = self._func(self.poly, x=1, y=2)\n        assert np.allclose(poly_shifted.exterior, np.float32([\n            [0 + 1, 0 + 2],\n            [1 + 1, 0 + 2],\n            [1 + 1, 1 + 2],\n            [0 + 1, 1 + 2]\n        ]))\n        assert poly_shifted.label == \"test\"\n\n    def test_inplaceness(self):\n        poly = self.poly\n        poly2 = self._func(poly, y=1)\n\n        if self._is_inplace:\n            assert poly is poly2\n        else:\n            assert poly is not poly2\n\n\nclass TestPolygon_shift(TestPolygon_shift_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, poly, *args, **kwargs):\n        def _func_impl():\n            return poly.shift(*args, **kwargs)\n\n        return wrap_shift_deprecation(_func_impl, *args, **kwargs)\n\n    def test_shift_does_not_work_inplace(self):\n        poly = self.poly\n        poly_shifted = self._func(poly, top=1)\n        assert np.allclose(poly.exterior, np.float32([\n            [0, 0],\n            [1, 0],\n            [1, 1],\n            [0, 1]\n        ]))\n        assert np.allclose(poly_shifted.exterior, np.float32([\n            [0, 1],\n            [1, 1],\n            [1, 2],\n            [0, 2]\n        ]))\n\n    def test_shift_from_top(self):\n        for v in [1, 0, -1, 0.5]:\n            with self.subTest(top=v):\n                poly_shifted = self._func(self.poly, top=v)\n                assert np.allclose(poly_shifted.exterior, np.float32([\n                    [0, 0 + v],\n                    [1, 0 + v],\n                    [1, 1 + v],\n                    [0, 1 + v]\n                ]))\n                assert poly_shifted.label == \"test\"\n\n    def test_shift_from_bottom(self):\n        for v in [1, 0, -1, 0.5]:\n            with self.subTest(bottom=v):\n                poly_shifted = self._func(self.poly, bottom=v)\n                assert np.allclose(poly_shifted.exterior, np.float32([\n                    [0, 0 - v],\n                    [1, 0 - v],\n                    [1, 1 - v],\n                    [0, 1 - v]\n                ]))\n                assert poly_shifted.label == \"test\"\n\n    def test_shift_from_top_and_bottom(self):\n        for v in [1, 0, -1, 0.5]:\n            with self.subTest(top=v, bottom=-v):\n                poly_shifted = self._func(self.poly, top=v, bottom=-v)\n                assert np.allclose(poly_shifted.exterior, np.float32([\n                    [0, 0 + 2*v],\n                    [1, 0 + 2*v],\n                    [1, 1 + 2*v],\n                    [0, 1 + 2*v]\n                ]))\n                assert poly_shifted.label == \"test\"\n\n    def test_shift_from_left(self):\n        for v in [1, 0, -1, 0.5]:\n            with self.subTest(left=v):\n                poly_shifted = self._func(self.poly, left=v)\n                assert np.allclose(poly_shifted.exterior, np.float32([\n                    [0 + v, 0],\n                    [1 + v, 0],\n                    [1 + v, 1],\n                    [0 + v, 1]\n                ]))\n                assert poly_shifted.label == \"test\"\n\n    def test_shift_from_right(self):\n        for v in [1, 0, -1, 0.5]:\n            with self.subTest(right=v):\n                poly_shifted = self._func(self.poly, right=v)\n                assert np.allclose(poly_shifted.exterior, np.float32([\n                    [0 - v, 0],\n                    [1 - v, 0],\n                    [1 - v, 1],\n                    [0 - v, 1]\n                ]))\n                assert poly_shifted.label == \"test\"\n\n    def test_shift_from_left_and_right(self):\n        for v in [1, 0, -1, 0.5]:\n            with self.subTest(left=v, right=-v):\n                poly_shifted = self._func(self.poly, left=v, right=-v)\n                assert np.allclose(poly_shifted.exterior, np.float32([\n                    [0 + 2 * v, 0],\n                    [1 + 2 * v, 0],\n                    [1 + 2 * v, 1],\n                    [0 + 2 * v, 1]\n                ]))\n                assert poly_shifted.label == \"test\"\n\n\nclass TestPolygon_draw_on_image(unittest.TestCase):\n    @property\n    def image(self):\n        return np.tile(\n            np.arange(100).reshape((10, 10, 1)),\n            (1, 1, 3)\n        ).astype(np.uint8)\n\n    def test_square_polygon(self):\n        # simple drawing of square\n        poly = ia.Polygon([(2, 2), (8, 2), (8, 8), (2, 8)])\n        image = self.image\n        image_poly = poly.draw_on_image(image,\n                                        color=[32, 128, 32],\n                                        color_face=[32, 128, 32],\n                                        color_lines=[0, 255, 0],\n                                        color_points=[0, 255, 0],\n                                        alpha=1.0,\n                                        alpha_face=1.0,\n                                        alpha_lines=1.0,\n                                        alpha_points=0.0,\n                                        raise_if_out_of_image=False)\n        assert image_poly.dtype.type == np.uint8\n        assert image_poly.shape == (10, 10, 3)\n        # draw did not change original image (copy=True)\n        assert np.sum(image) == 3 * np.sum(np.arange(100))\n\n        for c_idx, value in enumerate([0, 255, 0]):\n            # left boundary\n            assert np.all(image_poly[2:9, 2:3, c_idx]\n                          == np.zeros((7, 1), dtype=np.uint8) + value)\n            # right boundary\n            assert np.all(image_poly[2:9, 8:9, c_idx]\n                          == np.zeros((7, 1), dtype=np.uint8) + value)\n            # top boundary\n            assert np.all(image_poly[2:3, 2:9, c_idx]\n                          == np.zeros((1, 7), dtype=np.uint8) + value)\n            # bottom boundary\n            assert np.all(image_poly[8:9, 2:9, c_idx]\n                          == np.zeros((1, 7), dtype=np.uint8) + value)\n        expected = np.tile(\n            np.uint8([32, 128, 32]).reshape((1, 1, 3)),\n            (5, 5, 1)\n        )\n        assert np.all(image_poly[3:8, 3:8, :] == expected)\n\n    def test_square_polygon_use_no_color_subargs(self):\n        # simple drawing of square, use only \"color\" arg\n        poly = ia.Polygon([(2, 2), (8, 2), (8, 8), (2, 8)])\n        image = self.image\n        image_poly = poly.draw_on_image(image,\n                                        color=[0, 255, 0],\n                                        alpha=1.0,\n                                        alpha_face=1.0,\n                                        alpha_lines=1.0,\n                                        alpha_points=0.0,\n                                        raise_if_out_of_image=False)\n        assert image_poly.dtype.type == np.uint8\n        assert image_poly.shape == (10, 10, 3)\n        # draw did not change original image (copy=True)\n        assert np.sum(image) == 3 * np.sum(np.arange(100))\n\n        for c_idx, value in enumerate([0, 0.5*255, 0]):\n            value = int(np.round(value))\n            # left boundary\n            assert np.all(image_poly[2:9, 2:3, c_idx]\n                          == np.zeros((7, 1), dtype=np.uint8) + value)\n            # right boundary\n            assert np.all(image_poly[2:9, 8:9, c_idx]\n                          == np.zeros((7, 1), dtype=np.uint8) + value)\n            # top boundary\n            assert np.all(image_poly[2:3, 2:9, c_idx]\n                          == np.zeros((1, 7), dtype=np.uint8) + value)\n            # bottom boundary\n            assert np.all(image_poly[8:9, 2:9, c_idx]\n                          == np.zeros((1, 7), dtype=np.uint8) + value)\n        expected = np.tile(\n            np.uint8([0, 255, 0]).reshape((1, 1, 3)),\n            (5, 5, 1)\n        )\n        assert np.all(image_poly[3:8, 3:8, :] == expected)\n\n    def test_square_polygon_on_float32_image(self):\n        # simple drawing of square with float32 input\n        poly = ia.Polygon([(2, 2), (8, 2), (8, 8), (2, 8)])\n        image = self.image\n        image_poly = poly.draw_on_image(image.astype(np.float32),\n                                        color=[32, 128, 32],\n                                        color_face=[32, 128, 32],\n                                        color_lines=[0, 255, 0],\n                                        color_points=[0, 255, 0],\n                                        alpha=1.0,\n                                        alpha_face=1.0,\n                                        alpha_lines=1.0,\n                                        alpha_points=0.0,\n                                        raise_if_out_of_image=False)\n        assert image_poly.dtype.type == np.float32\n        assert image_poly.shape == (10, 10, 3)\n        for c_idx, value in enumerate([0, 255, 0]):\n            # left boundary\n            assert np.allclose(image_poly[2:9, 2:3, c_idx],\n                               np.zeros((7, 1), dtype=np.float32) + value)\n            # right boundary\n            assert np.allclose(image_poly[2:9, 8:9, c_idx],\n                               np.zeros((7, 1), dtype=np.float32) + value)\n            # top boundary\n            assert np.allclose(image_poly[2:3, 2:9, c_idx],\n                               np.zeros((1, 7), dtype=np.float32) + value)\n            # bottom boundary\n            assert np.allclose(image_poly[8:9, 2:9, c_idx],\n                               np.zeros((1, 7), dtype=np.float32) + value)\n        expected = np.tile(\n            np.float32([32, 128, 32]).reshape((1, 1, 3)),\n            (5, 5, 1)\n        )\n        assert np.allclose(image_poly[3:8, 3:8, :], expected)\n\n    def test_square_polygon_half_outside_of_image(self):\n        # drawing of poly that is half out of image\n        poly = ia.Polygon([(2, 2+5), (8, 2+5), (8, 8+5), (2, 8+5)])\n        image = self.image\n        image_poly = poly.draw_on_image(image,\n                                        color=[32, 128, 32],\n                                        color_face=[32, 128, 32],\n                                        color_lines=[0, 255, 0],\n                                        color_points=[0, 255, 0],\n                                        alpha=1.0,\n                                        alpha_face=1.0,\n                                        alpha_lines=1.0,\n                                        alpha_points=0.0,\n                                        raise_if_out_of_image=False)\n        assert image_poly.dtype.type == np.uint8\n        assert image_poly.shape == (10, 10, 3)\n        # draw did not change original image (copy=True)\n        assert np.sum(image) == 3 * np.sum(np.arange(100))\n\n        for c_idx, value in enumerate([0, 255, 0]):\n            # left boundary\n            assert np.all(image_poly[2+5:, 2:3, c_idx]\n                          == np.zeros((3, 1), dtype=np.uint8) + value)\n            # right boundary\n            assert np.all(image_poly[2+5:, 8:9, c_idx]\n                          == np.zeros((3, 1), dtype=np.uint8) + value)\n            # top boundary\n            assert np.all(image_poly[2+5:3+5, 2:9, c_idx]\n                          == np.zeros((1, 7), dtype=np.uint8) + value)\n        expected = np.tile(\n            np.uint8([32, 128, 32]).reshape((1, 1, 3)),\n            (2, 5, 1)\n        )\n        assert np.all(image_poly[3+5:, 3:8, :] == expected)\n\n    def test_square_polygon_half_outside_of_image_with_raise_if_ooi(self):\n        # drawing of poly that is half out of image, with\n        # raise_if_out_of_image=True\n        poly = ia.Polygon([(2, 2+5), (8, 2+5), (8, 8+5), (0, 8+5)])\n        image = self.image\n        got_exception = False\n        try:\n            _ = poly.draw_on_image(image,\n                                   color=[32, 128, 32],\n                                   color_face=[32, 128, 32],\n                                   color_lines=[0, 255, 0],\n                                   color_points=[0, 255, 0],\n                                   alpha=1.0,\n                                   alpha_face=1.0,\n                                   alpha_lines=1.0,\n                                   alpha_points=0.0,\n                                   raise_if_out_of_image=True)\n        except Exception as exc:\n            assert \"Cannot draw polygon\" in str(exc)\n            got_exception = True\n        # only polygons fully outside of the image plane lead to exceptions\n        assert not got_exception\n\n    def test_polygon_fully_outside_of_image(self):\n        # drawing of poly that is fully out of image\n        poly = ia.Polygon([(100, 100), (100+10, 100), (100+10, 100+10),\n                           (100, 100+10)])\n        image = self.image\n        image_poly = poly.draw_on_image(image,\n                                        color=[32, 128, 32],\n                                        color_face=[32, 128, 32],\n                                        color_lines=[0, 255, 0],\n                                        color_points=[0, 255, 0],\n                                        alpha=1.0,\n                                        alpha_face=1.0,\n                                        alpha_lines=1.0,\n                                        alpha_points=0.0,\n                                        raise_if_out_of_image=False)\n        assert np.array_equal(image_poly, image)\n\n    def test_polygon_fully_outside_of_image_with_raise_if_ooi(self):\n        # drawing of poly that is fully out of image,\n        # with raise_if_out_of_image=True\n        poly = ia.Polygon([(100, 100), (100+10, 100), (100+10, 100+10),\n                           (100, 100+10)])\n        image = self.image\n        got_exception = False\n        try:\n            _ = poly.draw_on_image(image,\n                                   color=[32, 128, 32],\n                                   color_face=[32, 128, 32],\n                                   color_lines=[0, 255, 0],\n                                   color_points=[0, 255, 0],\n                                   alpha=1.0,\n                                   alpha_face=1.0,\n                                   alpha_lines=1.0,\n                                   alpha_points=0.0,\n                                   raise_if_out_of_image=True)\n        except Exception as exc:\n            assert \"Cannot draw polygon\" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_only_lines_visible(self):\n        # face+points invisible via alpha\n        poly = ia.Polygon([(2, 2), (8, 2), (8, 8), (2, 8)])\n        image = self.image\n        image_poly = poly.draw_on_image(image,\n                                        color=[32, 128, 32],\n                                        color_face=[32, 128, 32],\n                                        color_lines=[0, 255, 0],\n                                        color_points=[0, 255, 0],\n                                        alpha=1.0,\n                                        alpha_face=0.0,\n                                        alpha_lines=1.0,\n                                        alpha_points=0.0,\n                                        raise_if_out_of_image=False)\n        assert image_poly.dtype.type == np.uint8\n        assert image_poly.shape == (10, 10, 3)\n        # draw did not change original image (copy=True)\n        assert np.sum(image) == 3 * np.sum(np.arange(100))\n        for c_idx, value in enumerate([0, 255, 0]):\n            # left boundary\n            assert np.all(image_poly[2:9, 2:3, c_idx]\n                          == np.zeros((7, 1), dtype=np.uint8) + value)\n        assert np.all(image_poly[3:8, 3:8, :] == image[3:8, 3:8, :])\n\n    def test_only_face_visible(self):\n        # boundary+points invisible via alpha\n        poly = ia.Polygon([(2, 2), (8, 2), (8, 8), (2, 8)])\n        image = self.image\n        image_poly = poly.draw_on_image(image,\n                                        color=[32, 128, 32],\n                                        color_face=[32, 128, 32],\n                                        color_lines=[0, 255, 0],\n                                        color_points=[0, 255, 0],\n                                        alpha=1.0,\n                                        alpha_face=1.0,\n                                        alpha_lines=0.0,\n                                        alpha_points=0.0,\n                                        raise_if_out_of_image=False)\n        assert image_poly.dtype.type == np.uint8\n        assert image_poly.shape == (10, 10, 3)\n        # draw did not change original image (copy=True)\n        assert np.sum(image) == 3 * np.sum(np.arange(100))\n        expected = np.tile(\n            np.uint8([32, 128, 32]).reshape((1, 1, 3)), (6, 6, 1)\n        )\n        assert np.all(image_poly[2:8, 2:8, :] == expected)\n\n    def test_alpha_is_080(self):\n        # alpha=0.8\n        poly = ia.Polygon([(2, 2), (8, 2), (8, 8), (2, 8)])\n        image = self.image\n        image_poly = poly.draw_on_image(image,\n                                        color=[32, 128, 32],\n                                        color_face=[32, 128, 32],\n                                        color_lines=[0, 255, 0],\n                                        color_points=[0, 255, 0],\n                                        alpha=0.8,\n                                        alpha_points=0.0,\n                                        raise_if_out_of_image=False)\n        assert image_poly.dtype.type == np.uint8\n        assert image_poly.shape == (10, 10, 3)\n        for c_idx, value in enumerate([0, 255, 0]):\n            expected = np.round(\n                (1-0.8)*image[2:9, 8:9, c_idx]\n                + np.full((7, 1), 0.8*value, dtype=np.float32)\n            ).astype(np.uint8)\n\n            # right boundary\n            assert np.all(image_poly[2:9, 8:9, c_idx] == expected)\n        expected = (0.8 * 0.5) * np.tile(\n            np.uint8([32, 128, 32]).reshape((1, 1, 3)), (5, 5, 1)\n        ) + (1 - (0.8 * 0.5)) * image[3:8, 3:8, :]\n        assert np.all(image_poly[3:8, 3:8, :]\n                      == np.round(expected).astype(np.uint8))\n\n    def test_face_and_lines_at_half_visibility(self):\n        # alpha of fill and perimeter 0.5\n        poly = ia.Polygon([(2, 2), (8, 2), (8, 8), (2, 8)])\n        image = self.image\n        image_poly = poly.draw_on_image(image,\n                                        color=[32, 128, 32],\n                                        color_face=[32, 128, 32],\n                                        color_lines=[0, 255, 0],\n                                        color_points=[0, 255, 0],\n                                        alpha=1.0,\n                                        alpha_face=0.5,\n                                        alpha_lines=0.5,\n                                        alpha_points=0.0,\n                                        raise_if_out_of_image=False)\n        assert image_poly.dtype.type == np.uint8\n        assert image_poly.shape == (10, 10, 3)\n        for c_idx, value in enumerate([0, 255, 0]):\n            expected = np.round(\n                0.5*image[2:9, 8:9, c_idx]\n                + np.full((7, 1), 0.5*value, dtype=np.float32)\n            ).astype(np.uint8)\n\n            # right boundary\n            assert np.all(image_poly[2:9, 8:9, c_idx] == expected)\n\n        expected = 0.5 * np.tile(\n            np.uint8([32, 128, 32]).reshape((1, 1, 3)), (5, 5, 1)\n        ) + 0.5 * image[3:8, 3:8, :]\n        assert np.all(image_poly[3:8, 3:8, :]\n                      == np.round(expected).astype(np.uint8))\n\n        # copy=False\n        # test deactivated as the function currently does not offer a copy\n        # argument\n        \"\"\"\n        image_cp = np.copy(image)\n        poly = ia.Polygon([(2, 2), (8, 2), (8, 8), (2, 8)])\n        image_poly = poly.draw_on_image(image_cp,\n                                        color_face=[32, 128, 32],\n                                        color_boundary=[0, 255, 0],\n                                        alpha_face=1.0,\n                                        alpha_boundary=1.0,\n                                        raise_if_out_of_image=False)\n        assert image_poly.dtype.type == np.uint8\n        assert image_poly.shape == (10, 10, 3)\n        assert np.all(image_cp == image_poly)\n        assert not np.all(image_cp == image)\n        for c_idx, value in enumerate([0, 255, 0]):\n            # left boundary\n            assert np.all(image_poly[2:9, 2:3, c_idx]\n                          == np.zeros((6, 1, 3), dtype=np.uint8) + value)\n            # left boundary\n            assert np.all(image_cp[2:9, 2:3, c_idx]\n                          == np.zeros((6, 1, 3), dtype=np.uint8) + value)\n        expected = np.tile(\n            np.uint8([32, 128, 32]).reshape((1, 1, 3)),\n            (5, 5, 1)\n        )\n        assert np.all(image_poly[3:8, 3:8, :] == expected)\n        assert np.all(image_cp[3:8, 3:8, :] == expected)\n        \"\"\"\n\n\nclass TestPolygon_extract_from_image(unittest.TestCase):\n    @property\n    def image(self):\n        return np.arange(20*20*2).reshape((20, 20, 2)).astype(np.int32)\n\n    def test_polygon_is_identical_with_image_shape(self):\n        # inside image and completely covers it\n        poly = ia.Polygon([(0, 0), (10, 0), (10, 10), (0, 10)])\n        subimage = poly.extract_from_image(self.image)\n        assert np.array_equal(subimage, self.image[0:10, 0:10, :])\n\n    def test_polygon_is_subpart_of_image(self):\n        # inside image, subpart of it (not all of the image has to be\n        # extracted)\n        poly = ia.Polygon([(1, 1), (9, 1), (9, 9), (1, 9)])\n        subimage = poly.extract_from_image(self.image)\n        assert np.array_equal(subimage, self.image[1:9, 1:9, :])\n\n    def test_polygon_fully_inside_image__no_rectangular_shape(self):\n        # inside image, two image areas that don't belong to the polygon but\n        # have to be extracted\n        poly = ia.Polygon([(0, 0), (10, 0), (10, 5), (20, 5),\n                           (20, 20), (10, 20), (10, 5), (0, 5)])\n        subimage = poly.extract_from_image(self.image)\n        expected = np.copy(self.image)\n        expected[:5, 10:, :] = 0  # top right block\n        expected[5:, :10, :] = 0  # left bottom block\n        assert np.array_equal(subimage, expected)\n\n    def test_polygon_is_partially_outside_of_image(self):\n        # partially out of image\n        poly = ia.Polygon([(-5, 0), (5, 0), (5, 10), (-5, 10)])\n        subimage = poly.extract_from_image(self.image)\n        expected = np.zeros((10, 10, 2), dtype=np.int32)\n        expected[0:10, 5:10, :] = self.image[0:10, 0:5, :]\n        assert np.array_equal(subimage, expected)\n\n    def test_polygon_is_fully_outside_of_image(self):\n        # fully out of image\n        poly = ia.Polygon([(30, 0), (40, 0), (40, 10), (30, 10)])\n        subimage = poly.extract_from_image(self.image)\n        expected = np.zeros((10, 10, 2), dtype=np.int32)\n        assert np.array_equal(subimage, expected)\n\n    def test_polygon_coords_after_rounding_are_identical_with_img_shape(self):\n        # inside image, subpart of it\n        # float coordinates, rounded so that the whole image will be extracted\n        poly = ia.Polygon([(0.4, 0.4), (9.6, 0.4), (9.6, 9.6), (0.4, 9.6)])\n        subimage = poly.extract_from_image(self.image)\n        assert np.array_equal(subimage, self.image[0:10, 0:10, :])\n\n    def test_polygon_coords_after_rounding_are_subpart_of_image(self):\n        # inside image, subpart of it\n        # float coordinates, rounded so that x/y 0<=i<9 will be extracted\n        # (instead of 0<=i<10)\n        poly = ia.Polygon([(0.5, 0.5), (9.4, 0.5), (9.4, 9.4), (0.5, 9.4)])\n        subimage = poly.extract_from_image(self.image)\n        assert np.array_equal(subimage, self.image[0:9, 0:9, :])\n\n    def test_polygon_coords_after_rounding_are_subpart_of_image2(self):\n        # inside image, subpart of it\n        # float coordinates, rounded so that x/y 1<=i<9 will be extracted\n        # (instead of 0<=i<10)\n        poly = ia.Polygon([(0.51, 0.51), (9.4, 0.51), (9.4, 9.4), (0.51, 9.4)])\n        subimage = poly.extract_from_image(self.image)\n        assert np.array_equal(subimage, self.image[1:9, 1:9, :])\n\n    def test_polygon_without_area_fails(self):\n        # error for invalid polygons\n        got_exception = False\n        poly = ia.Polygon([(0.51, 0.51), (9.4, 0.51)])\n        try:\n            _ = poly.extract_from_image(self.image)\n        except Exception as exc:\n            assert \"Polygon must be made up\" in str(exc)\n            got_exception = True\n        assert got_exception\n\n\nclass TestPolygon_change_first_point_by_coords(unittest.TestCase):\n    def test_change_to_first_point_in_exterior(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_reordered = poly.change_first_point_by_coords(x=0, y=0)\n        assert np.allclose(poly.exterior, poly_reordered.exterior)\n\n    def test_change_to_first_second_in_exterior(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_reordered = poly.change_first_point_by_coords(x=1, y=0)\n        # make sure that it does not reorder inplace\n        assert np.allclose(poly.exterior, np.float32([[0, 0], [1, 0], [1, 1]]))\n        assert np.allclose(poly_reordered.exterior,\n                           np.float32([[1, 0], [1, 1], [0, 0]]))\n\n    def test_change_to_third_point_in_exterior(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_reordered = poly.change_first_point_by_coords(x=1, y=1)\n        assert np.allclose(poly_reordered.exterior,\n                           np.float32([[1, 1], [0, 0], [1, 0]]))\n\n    def test_coords_slightly_off_from_target_point_limited_max_distance(self):\n        # inaccurate point, but close enough\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_reordered = poly.change_first_point_by_coords(x=1.0, y=0.01,\n                                                           max_distance=0.1)\n        assert np.allclose(poly_reordered.exterior,\n                           np.float32([[1, 0], [1, 1], [0, 0]]))\n\n    def test_coords_slightly_off_from_target_point_infinite_max_distance(self):\n        # inaccurate point, but close enough (infinite max distance)\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_reordered = poly.change_first_point_by_coords(x=1.0, y=0.01,\n                                                           max_distance=None)\n        assert np.allclose(poly_reordered.exterior,\n                           np.float32([[1, 0], [1, 1], [0, 0]]))\n\n    def test_closest_point_to_coords_exceeds_max_distance(self):\n        # point too far away\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        got_exception = False\n        try:\n            _ = poly.change_first_point_by_coords(x=1.0, y=0.01, max_distance=0.001)\n        except Exception as exc:\n            assert \"Closest found point \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_polygon_with_two_points(self):\n        # reorder with two points\n        poly = ia.Polygon([(0, 0), (1, 0)])\n        poly_reordered = poly.change_first_point_by_coords(x=1, y=0)\n        assert np.allclose(poly_reordered.exterior,\n                           np.float32([[1, 0], [0, 0]]))\n\n    def test_polygon_with_one_point(self):\n        # reorder with one point\n        poly = ia.Polygon([(0, 0)])\n        poly_reordered = poly.change_first_point_by_coords(x=0, y=0)\n        assert np.allclose(poly_reordered.exterior, np.float32([[0, 0]]))\n\n    def test_polygon_with_zero_points_fails(self):\n        # invalid polygon\n        got_exception = False\n        poly = ia.Polygon([])\n        try:\n            _ = poly.change_first_point_by_coords(x=0, y=0)\n        except Exception as exc:\n            assert \"Cannot reorder polygon points\" in str(exc)\n            got_exception = True\n        assert got_exception\n\n\nclass TestPolygon_change_first_point_by_index(unittest.TestCase):\n    def test_change_to_point_with_index_0(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_reordered = poly.change_first_point_by_index(0)\n        assert np.allclose(poly.exterior, poly_reordered.exterior)\n\n    def test_change_to_point_with_index_1(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_reordered = poly.change_first_point_by_index(1)\n        # make sure that it does not reorder inplace\n        assert np.allclose(poly.exterior,\n                           np.float32([[0, 0], [1, 0], [1, 1]]))\n        assert np.allclose(poly_reordered.exterior,\n                           np.float32([[1, 0], [1, 1], [0, 0]]))\n\n    def test_change_to_point_with_index_2(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_reordered = poly.change_first_point_by_index(2)\n        assert np.allclose(poly_reordered.exterior,\n                           np.float32([[1, 1], [0, 0], [1, 0]]))\n\n    def test_polygon_with_two_points(self):\n        # reorder with two points\n        poly = ia.Polygon([(0, 0), (1, 0)])\n        poly_reordered = poly.change_first_point_by_index(1)\n        assert np.allclose(poly_reordered.exterior,\n                           np.float32([[1, 0], [0, 0]]))\n\n    def test_polygon_with_one_point(self):\n        # reorder with one point\n        poly = ia.Polygon([(0, 0)])\n        poly_reordered = poly.change_first_point_by_index(0)\n        assert np.allclose(poly_reordered.exterior, np.float32([[0, 0]]))\n\n    def test_polygon_with_zero_points_fails(self):\n        poly = ia.Polygon([])\n        got_exception = False\n        try:\n            _ = poly.change_first_point_by_index(0)\n        except AssertionError:\n            got_exception = True\n        assert got_exception\n\n    def test_index_beyond_max_index_fails(self):\n        # idx out of bounds\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        got_exception = False\n        try:\n            _ = poly.change_first_point_by_index(3)\n        except AssertionError:\n            got_exception = True\n        assert got_exception\n\n    def test_index_beyond_max_index_fails__single_point_polygon(self):\n        poly = ia.Polygon([(0, 0)])\n        got_exception = False\n        try:\n            _ = poly.change_first_point_by_index(1)\n        except AssertionError:\n            got_exception = True\n        assert got_exception\n\n    def test_index_below_zero_fails(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        got_exception = False\n        try:\n            _ = poly.change_first_point_by_index(-1)\n        except AssertionError:\n            got_exception = True\n        assert got_exception\n\n\nclass TestPolygon_subdivide_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, poly, points_per_edge):\n        return poly.subdivide_(points_per_edge)\n\n    def test_zero_points(self):\n        poly = ia.Polygon([])\n        poly_sub = self._func(poly, 1)\n        assert len(poly_sub.exterior) == 0\n\n    def test_one_point(self):\n        poly = ia.Polygon([(1, 1)])\n        poly_sub = self._func(poly, 1)\n        assert len(poly_sub.exterior) == 1\n\n    def test_two_points(self):\n        poly = ia.Polygon([(1, 2), (2, 4)])\n        poly_sub = self._func(poly, 1)\n        assert len(poly_sub.exterior) == 4\n        assert poly_sub.coords_almost_equals([\n            (1, 2),\n            (1.5, 3.0),\n            (2, 4),\n            (1.5, 3.0)\n        ])\n\n    def test_three_points(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_sub = self._func(poly, 1)\n        assert len(poly_sub.exterior) == 6\n        assert poly_sub.coords_almost_equals([\n            (0, 0),\n            (0.5, 0.0),\n            (1, 0),\n            (1, 0.5),\n            (1, 1),\n            (0.5, 0.5)\n        ])\n\n    def test_three_points__n_points_0(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_sub = self._func(poly, 0)\n        assert len(poly_sub.exterior) == 3\n        assert poly_sub.coords_almost_equals([\n            (0, 0),\n            (1, 0),\n            (1, 1),\n        ])\n\n    def test_three_points__n_points_2(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_sub = self._func(poly, 2)\n        assert len(poly_sub.exterior) == 3+2*3\n        assert poly_sub.coords_almost_equals([\n            (0, 0),\n            (1/3, 0),\n            (2/3, 0),\n            (1, 0),\n            (1, 1/3),\n            (1, 2/3),\n            (1, 1),\n            (2/3, 2/3),\n            (1/3, 1/3)\n        ])\n\n    def test_label_none_is_preserved(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly_sub = self._func(poly, 1)\n        assert poly_sub.label is None\n\n    def test_label_str_is_preserved(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)], label=\"foo\")\n        poly_sub = self._func(poly, 1)\n        assert poly_sub.label == \"foo\"\n\n    def test_inplaceness(self):\n        poly = ia.Polygon([(1, 2), (2, 4)])\n        poly2 = self._func(poly, 1)\n\n        if self._is_inplace:\n            assert poly is poly2\n        else:\n            assert poly is not poly2\n\n\nclass TestPolygon_subdivide(TestPolygon_subdivide_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, poly, points_per_edge):\n        return poly.subdivide(points_per_edge)\n\n\nclass TestPolygon_to_shapely_line_string(unittest.TestCase):\n    def test_three_point_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        ls = poly.to_shapely_line_string()\n        assert np.allclose(ls.coords, np.float32([[0, 0], [1, 0], [1, 1]]))\n\n    def test_two_point_polygon(self):\n        # two point polygon\n        poly = ia.Polygon([(0, 0), (1, 0)])\n        ls = poly.to_shapely_line_string()\n        assert np.allclose(ls.coords, np.float32([[0, 0], [1, 0]]))\n\n    def test_one_point_polygon_fails(self):\n        # one point polygon\n        poly = ia.Polygon([(0, 0)])\n        got_exception = False\n        try:\n            _ = poly.to_shapely_line_string()\n        except Exception as exc:\n            assert (\n                \"Conversion to shapely line string requires at least two \"\n                \"points\" in str(exc))\n            got_exception = True\n        assert got_exception\n\n    def test_zero_point_polygon_fails(self):\n        # zero point polygon\n        poly = ia.Polygon([])\n        got_exception = False\n        try:\n            _ = poly.to_shapely_line_string()\n        except Exception as exc:\n            assert (\n                \"Conversion to shapely line string requires at least two \"\n                \"points\" in str(exc))\n            got_exception = True\n        assert got_exception\n\n    def test_closed_is_true(self):\n        # closed line string\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        ls = poly.to_shapely_line_string(closed=True)\n        assert np.allclose(ls.coords,\n                           np.float32([[0, 0], [1, 0], [1, 1], [0, 0]]))\n\n    def test_interpolate_is_1(self):\n        # interpolation\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        ls = poly.to_shapely_line_string(interpolate=1)\n        assert np.allclose(\n            ls.coords,\n            np.float32([[0, 0], [0.5, 0], [1, 0], [1, 0.5],\n                        [1, 1], [0.5, 0.5]]))\n\n    def test_interpolate_is_2(self):\n        # interpolation with 2 steps\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        ls = poly.to_shapely_line_string(interpolate=2)\n        assert np.allclose(ls.coords, np.float32([\n            [0, 0], [1/3, 0], [2/3, 0],\n            [1, 0], [1, 1/3], [1, 2/3],\n            [1, 1], [2/3, 2/3], [1/3, 1/3]\n        ]))\n\n    def test_closed_is_true_and_interpolate_is_1(self):\n        # interpolation with closed=True\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        ls = poly.to_shapely_line_string(closed=True, interpolate=1)\n        assert np.allclose(\n            ls.coords,\n            np.float32([[0, 0], [0.5, 0], [1, 0], [1, 0.5], [1, 1],\n                        [0.5, 0.5], [0, 0]]))\n\n\nclass TestPolygon_to_shapely_polygon(unittest.TestCase):\n    def test_square_polygon(self):\n        exterior = [(0, 0), (1, 0), (1, 1), (0, 1)]\n        poly = ia.Polygon(exterior)\n        poly_shapely = poly.to_shapely_polygon()\n        gen = zip(exterior, poly_shapely.exterior.coords)\n        for (x_exp, y_exp), (x_obs, y_obs) in gen:\n            assert np.isclose(x_obs, x_exp, rtol=0, atol=1e-8)\n            assert np.isclose(y_obs, y_exp, rtol=0, atol=1e-8)\n\n\nclass TestPolygon_to_bounding_box(unittest.TestCase):\n    def test_square_polygon(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        bb = poly.to_bounding_box()\n        assert 0 - 1e-8 < bb.x1 < 0 + 1e-8\n        assert 0 - 1e-8 < bb.y1 < 0 + 1e-8\n        assert 1 - 1e-8 < bb.x2 < 1 + 1e-8\n        assert 1 - 1e-8 < bb.y2 < 1 + 1e-8\n\n    def test_triangular_polygon(self):\n        poly = ia.Polygon([(0.5, 0), (1, 1), (0, 1)])\n        bb = poly.to_bounding_box()\n        assert 0 - 1e-8 < bb.x1 < 0 + 1e-8\n        assert 0 - 1e-8 < bb.y1 < 0 + 1e-8\n        assert 1 - 1e-8 < bb.x2 < 1 + 1e-8\n        assert 1 - 1e-8 < bb.y2 < 1 + 1e-8\n\n    def test_triangular_polygon2(self):\n        poly = ia.Polygon([(0.5, 0.5), (2, 0.1), (1, 1)])\n        bb = poly.to_bounding_box()\n        assert 0.5 - 1e-8 < bb.x1 < 0.5 + 1e-8\n        assert 0.1 - 1e-8 < bb.y1 < 0.1 + 1e-8\n        assert 2.0 - 1e-8 < bb.x2 < 2.0 + 1e-8\n        assert 1.0 - 1e-8 < bb.y2 < 1.0 + 1e-8\n\n\nclass TestPolygon_to_line_string(unittest.TestCase):\n    def test_polygon_with_zero_points(self):\n        poly = ia.Polygon([])\n        ls = poly.to_line_string(closed=False)\n        assert len(ls.coords) == 0\n        assert ls.label is None\n\n    def test_polygon_with_zero_points__closed_is_true(self):\n        poly = ia.Polygon([])\n        ls = poly.to_line_string(closed=True)\n        assert len(ls.coords) == 0\n        assert ls.label is None\n\n    def test_polygon_with_zero_points__label_set(self):\n        poly = ia.Polygon([], label=\"foo\")\n        ls = poly.to_line_string(closed=False)\n        assert len(ls.coords) == 0\n        assert ls.label == \"foo\"\n\n    def test_polygon_with_one_point(self):\n        poly = ia.Polygon([(0, 0)])\n        ls = poly.to_line_string(closed=False)\n        assert len(ls.coords) == 1\n        assert ls.label is None\n\n    def test_polygon_with_one_point__closed_is_true(self):\n        poly = ia.Polygon([(0, 0)])\n        ls = poly.to_line_string(closed=True)\n        assert len(ls.coords) == 1\n        assert ls.coords_almost_equals([(0, 0)])\n        assert ls.label is None\n\n    def test_polygon_with_two_points(self):\n        poly = ia.Polygon([(0, 0), (1, 1)])\n        ls = poly.to_line_string(closed=False)\n        assert len(ls.coords) == 2\n        assert ls.coords_almost_equals([(0, 0), (1, 1)])\n        assert ls.label is None\n\n    def test_polygon_with_two_point__closed_is_true(self):\n        poly = ia.Polygon([(0, 0), (1, 1)])\n        ls = poly.to_line_string(closed=True)\n        assert len(ls.coords) == 3\n        assert ls.coords_almost_equals([(0, 0), (1, 1), (0, 0)])\n        assert ls.label is None\n\n    def test_polygon_with_two_points__label_is_set(self):\n        poly = ia.Polygon([(0, 0), (1, 1)], label=\"foo\")\n        ls = poly.to_line_string()\n        assert len(ls.coords) == 3\n        assert ls.coords_almost_equals([(0, 0), (1, 1), (0, 0)])\n        assert ls.label == \"foo\"\n\n    def test_polygon_with_two_point__closed_is_true_label_is_set(self):\n        poly = ia.Polygon([(0, 0), (1, 1)], label=\"foo\")\n        ls = poly.to_line_string(closed=True)\n        assert len(ls.coords) == 3\n        assert ls.coords_almost_equals([(0, 0), (1, 1), (0, 0)])\n        assert ls.label == \"foo\"\n\n\nclass TestPolygon_from_shapely(unittest.TestCase):\n    def test_square_polygon(self):\n        exterior = [(0, 0), (1, 0), (1, 1), (0, 1)]\n        poly_shapely = shapely.geometry.Polygon(exterior)\n        poly = ia.Polygon.from_shapely(poly_shapely)\n\n        # shapely messes up the point ordering, so we try to correct it here\n        start_idx = 0\n        for i, (x, y) in enumerate(poly.exterior):\n            dist = np.sqrt((exterior[0][0] - x) ** 2\n                           + (exterior[0][1] - x) ** 2)\n            if dist < 1e-4:\n                start_idx = i\n                break\n        poly = poly.change_first_point_by_index(start_idx)\n\n        for (x_exp, y_exp), (x_obs, y_obs) in zip(exterior, poly.exterior):\n            assert x_exp - 1e-8 < x_obs < x_exp + 1e-8\n            assert y_exp - 1e-8 < y_obs < y_exp + 1e-8\n\n    def test_polygon_with_zero_points(self):\n        poly_shapely = shapely.geometry.Polygon([])\n        poly = ia.Polygon.from_shapely(poly_shapely)\n        assert len(poly.exterior) == 0\n\n\nclass TestPolygon_copy(unittest.TestCase):\n    def test_square_polygon_with_label(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)], label=\"test\")\n        poly_cp = poly.copy()\n        assert poly.exterior.dtype.type == poly_cp.exterior.dtype.type\n        assert poly.exterior.shape == poly_cp.exterior.shape\n        assert np.allclose(poly.exterior, poly_cp.exterior)\n        assert poly.label == poly_cp.label\n\n\nclass TestPolygon_deepcopy(unittest.TestCase):\n    def test_square_polygon_with_label(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)], label=\"test\")\n        poly_cp = poly.deepcopy()\n        assert poly.exterior.dtype.type == poly_cp.exterior.dtype.type\n        assert poly.exterior.shape == poly_cp.exterior.shape\n        assert np.allclose(poly.exterior, poly_cp.exterior)\n        assert poly.label == poly_cp.label\n\n    def test_copy_is_not_shallow(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)], label=\"test\")\n        poly_cp = poly.deepcopy()\n        poly_cp.exterior[0, 0] = 100.0\n        poly_cp.label = \"test2\"\n        assert poly.exterior.dtype.type == poly_cp.exterior.dtype.type\n        assert poly.exterior.shape == poly_cp.exterior.shape\n        assert not np.allclose(poly.exterior, poly_cp.exterior)\n        assert not poly.label == poly_cp.label\n\n\nclass TestPolygon___repr___and___str__(unittest.TestCase):\n    def test_with_int_coordinates_provided_to_init(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)], label=\"test\")\n        expected = (\n            \"Polygon([\"\n            \"(x=0.000, y=0.000), \"\n            \"(x=1.000, y=0.000), \"\n            \"(x=1.000, y=1.000), \"\n            \"(x=0.000, y=1.000)\"\n            \"] (4 points), label=test)\"\n        )\n        assert poly.__repr__() == expected\n        assert poly.__str__() == expected\n\n    def test_with_float_coordinates_provided_to_init(self):\n        poly = ia.Polygon([(0, 0.5), (1.5, 0), (1, 1), (0, 1)], label=\"test\")\n        expected = (\n            \"Polygon([\"\n            \"(x=0.000, y=0.500), \"\n            \"(x=1.500, y=0.000), \"\n            \"(x=1.000, y=1.000), \"\n            \"(x=0.000, y=1.000)\"\n            \"] (4 points), label=test)\"\n        )\n        assert poly.__repr__() == expected\n        assert poly.__str__() == expected\n\n    def test_label_is_none(self):\n        poly = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)], label=None)\n        expected = (\n            \"Polygon([\"\n            \"(x=0.000, y=0.000), \"\n            \"(x=1.000, y=0.000), \"\n            \"(x=1.000, y=1.000), \"\n            \"(x=0.000, y=1.000)\"\n            \"] (4 points), label=None)\"\n        )\n        assert poly.__repr__() == expected\n        assert poly.__str__() == expected\n\n    def test_polygon_has_zero_points(self):\n        poly = ia.Polygon([], label=\"test\")\n        expected = \"Polygon([] (0 points), label=test)\"\n        assert poly.__repr__() == expected\n        assert poly.__str__() == expected\n\n\nclass TestPolygon_coords_almost_equals(unittest.TestCase):\n    @mock.patch(\"imgaug.augmentables.polys.Polygon.exterior_almost_equals\")\n    def test_calls_exterior_almost_equals(self, mock_eae):\n        mock_eae.return_value = \"foo\"\n        poly_a = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        poly_b = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n\n        result = poly_a.coords_almost_equals(poly_b)\n\n        assert result == \"foo\"\n        mock_eae.assert_called_once_with(poly_b, max_distance=1e-4,\n                                         points_per_edge=8)\n\n    @mock.patch(\"imgaug.augmentables.polys.Polygon.exterior_almost_equals\")\n    def test_calls_exterior_almost_equals__no_defaults(self, mock_eae):\n        mock_eae.return_value = \"foo\"\n        poly_a = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        poly_b = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n\n        result = poly_a.coords_almost_equals(poly_b, max_distance=1,\n                                             points_per_edge=2)\n\n        assert result == \"foo\"\n        mock_eae.assert_called_once_with(poly_b, max_distance=1,\n                                         points_per_edge=2)\n\n\nclass TestPolygon_exterior_almost_equals(unittest.TestCase):\n    def test_exactly_same_exterior(self):\n        # exactly same exterior\n        poly_a = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        poly_b = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_point_duplicated(self):\n        # one point duplicated\n        poly_a = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        poly_b = ia.Polygon([(0, 0), (1, 0), (1, 1), (1, 1), (0, 1)])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_several_points_added_without_changing_basic_shape(self):\n        # several points added without changing geometry\n        poly_a = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        poly_b = ia.Polygon([(0, 0), (0.5, 0), (1, 0), (1, 0.5), (1, 1), (0.5, 1), (0, 1), (0, 0.5)])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_different_order(self):\n        # different order\n        poly_a = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        poly_b = ia.Polygon([(0, 1), (1, 1), (1, 0), (0, 0)])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_tiny_shift_below_max_distance(self):\n        # tiny shift below tolerance\n        poly_a = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        poly_b = ia.Polygon([(0+1e-6, 0), (1+1e-6, 0), (1+1e-6, 1),\n                             (0+1e-6, 1)])\n        assert poly_a.exterior_almost_equals(poly_b, max_distance=1e-3)\n\n    def test_tiny_shift_above_max_distance(self):\n        # tiny shift above tolerance\n        poly_a = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        poly_b = ia.Polygon([(0+1e-6, 0), (1+1e-6, 0), (1+1e-6, 1),\n                             (0+1e-6, 1)])\n        assert not poly_a.exterior_almost_equals(poly_b, max_distance=1e-9)\n\n    def test_polygons_with_half_intersection(self):\n        # shifted polygon towards half overlap\n        poly_a = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        poly_b = ia.Polygon([(0.5, 0), (1.5, 0), (1.5, 1), (0.5, 1)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_polygons_without_any_intersection(self):\n        # shifted polygon towards no overlap at all\n        poly_a = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        poly_b = ia.Polygon([(100, 0), (101, 0), (101, 1), (100, 1)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_both_polygons_with_zero_points(self):\n        # both polygons without points\n        poly_a = ia.Polygon([])\n        poly_b = ia.Polygon([])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_both_polygons_with_one_point__both_identical(self):\n        # both polygons with one point\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0)])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_both_polygons_with_zero_points__both_different(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(100, 100)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_both_polygons_with_zero_points__difference_below_max_dist(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0+1e-6, 0)])\n        assert poly_a.exterior_almost_equals(poly_b, max_distance=1e-2)\n\n    def test_both_polygons_with_zero_points_difference_above_max_dist(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0+1, 0)])\n        assert not poly_a.exterior_almost_equals(poly_b, max_distance=1e-2)\n\n    def test_both_polygons_with_two_points__identical(self):\n        # both polygons with two points\n        poly_a = ia.Polygon([(0, 0), (1, 0)])\n        poly_b = ia.Polygon([(0, 0), (1, 0)])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_both_polygons_with_two_points__identical_and_zero_area(self):\n        poly_a = ia.Polygon([(0, 0), (0, 0)])\n        poly_b = ia.Polygon([(0, 0), (0, 0)])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_both_polygons_with_two_points__second_point_different(self):\n        poly_a = ia.Polygon([(0, 0), (1, 0)])\n        poly_b = ia.Polygon([(0, 0), (2, 0)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_both_polygons_with_two_points__difference_below_max_dist(self):\n        poly_a = ia.Polygon([(0, 0), (1, 0)])\n        poly_b = ia.Polygon([(0+1e-6, 0), (1+1e-6, 0)])\n        assert poly_a.exterior_almost_equals(poly_b, max_distance=1e-2)\n\n    def test_both_polygons_with_three_points__identical(self):\n        # both polygons with three points\n        poly_a = ia.Polygon([(0, 0), (1, 0), (0.5, 1)])\n        poly_b = ia.Polygon([(0, 0), (1, 0), (0.5, 1)])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_both_polygons_with_three_points__one_point_differs(self):\n        poly_a = ia.Polygon([(0, 0), (1, 0), (0.5, 1)])\n        poly_b = ia.Polygon([(0, 0), (1, -1), (0.5, 1)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_both_polygons_with_three_points__difference_below_max_dist(self):\n        poly_a = ia.Polygon([(0, 0), (1, 0), (0.5, 1)])\n        poly_b = ia.Polygon([(0, 0), (1+1e-6, 0), (0.5, 1)])\n        assert poly_a.exterior_almost_equals(poly_b, max_distance=1e-2)\n\n    def test_one_polygon_zero_points_other_one_point(self):\n        # one polygon with zero points, other with one\n        poly_a = ia.Polygon([])\n        poly_b = ia.Polygon([(0, 0)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_zero_points(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_two_points(self):\n        # one polygon with one point, other with two\n        poly_a = ia.Polygon([(-10, -20)])\n        poly_b = ia.Polygon([(0, 0), (1, 0)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_two_points_2(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (1, 0)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_two_points_other_one_point(self):\n        poly_a = ia.Polygon([(0, 0), (1, 0)])\n        poly_b = ia.Polygon([(0, 0)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_two_points_other_one_point__all_identical(self):\n        poly_a = ia.Polygon([(0, 0), (0, 0)])\n        poly_b = ia.Polygon([(0, 0)])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_two_points__all_identical(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (0, 0)])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_two_points_other_one_point__diff_below_max_dist(self):\n        poly_a = ia.Polygon([(0, 0), (0+1e-6, 0)])\n        poly_b = ia.Polygon([(0, 0)])\n        assert poly_a.exterior_almost_equals(poly_b, max_distance=1e-2)\n\n    def test_one_polygon_two_points_other_one_point__diff_above_max_dist(self):\n        poly_a = ia.Polygon([(0, 0), (0+1e-4, 0)])\n        poly_b = ia.Polygon([(0, 0)])\n        assert not poly_a.exterior_almost_equals(poly_b, max_distance=1e-9)\n\n    def test_one_polygon_one_point_other_three_points(self):\n        # one polygon with one point, other with three\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (1, 0), (0.5, 1)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_three_points_other_one_point(self):\n        poly_a = ia.Polygon([(0, 0), (1, 0), (0.5, 1)])\n        poly_b = ia.Polygon([(0, 0)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_three_points__all_identical(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (0, 0), (0, 0)])\n        assert poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_three_points__one_point_differs(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (0, 0), (1, 0)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_three_points__one_point_differs2(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (1, 0), (0, 0)])\n        assert not poly_a.exterior_almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_three_points__dist_below_max(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (0+1e-6, 0), (0, 0+1e-6)])\n        assert poly_a.exterior_almost_equals(poly_b, max_distance=1e-2)\n\n    def test_one_polygon_one_point_other_three_points__dist_above_max(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (0+1e-4, 0), (0, 0+1e-4)])\n        assert not poly_a.exterior_almost_equals(poly_b, max_distance=1e-9)\n\n\nclass TestPolygon_almost_equals(unittest.TestCase):\n    def test_both_polygons_empty(self):\n        poly_a = ia.Polygon([])\n        poly_b = ia.Polygon([])\n        assert poly_a.almost_equals(poly_b)\n\n    def test_both_polygons_one_point(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0)])\n        assert poly_a.almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_two_points__all_identical(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (0, 0)])\n        assert poly_a.almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_three_points__all_identical(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (0, 0), (0, 0)])\n        assert poly_a.almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_two_points__diff_below_max_dist(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (0+1e-10, 0)])\n        assert poly_a.almost_equals(poly_b)\n\n    def test_both_polygons_one_point__first_with_label(self):\n        poly_a = ia.Polygon([(0, 0)], label=\"test\")\n        poly_b = ia.Polygon([(0, 0)])\n        assert not poly_a.almost_equals(poly_b)\n\n    def test_both_polygons_one_point__second_with_label(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0)], label=\"test\")\n        assert not poly_a.almost_equals(poly_b)\n\n    def test_both_polygons_one_point__both_with_label(self):\n        poly_a = ia.Polygon([(0, 0)], label=\"test\")\n        poly_b = ia.Polygon([(0, 0)], label=\"test\")\n        assert poly_a.almost_equals(poly_b)\n\n    def test_both_polygons_one_point__both_with_label_but_point_differs(self):\n        poly_a = ia.Polygon([(0, 0)], label=\"test\")\n        poly_b = ia.Polygon([(1, 0)], label=\"test\")\n        assert not poly_a.almost_equals(poly_b)\n\n    def test_both_polygons_one_point__same_point_but_labels_differ(self):\n        poly_a = ia.Polygon([(0, 0)], label=\"testA\")\n        poly_b = ia.Polygon([(0, 0)], label=\"testB\")\n        assert not poly_a.almost_equals(poly_b)\n\n    def test_both_polygons_three_points(self):\n        poly_a = ia.Polygon([(0, 0), (1, 0), (0.5, 1)])\n        poly_b = ia.Polygon([(0, 0), (1, 0), (0.5, 1)])\n        assert poly_a.almost_equals(poly_b)\n\n    def test_one_polygon_one_point_other_three_points(self):\n        poly_a = ia.Polygon([(0, 0)])\n        poly_b = ia.Polygon([(0, 0), (1, 0), (0.5, 1)])\n        assert not poly_a.almost_equals(poly_b)\n\n\nclass TestPolygon___getitem__(unittest.TestCase):\n    def test_with_three_points(self):\n        cba = ia.Polygon([(1, 2), (3, 4), (5, 5)])\n        assert np.allclose(cba[0], (1, 2))\n        assert np.allclose(cba[1], (3, 4))\n        assert np.allclose(cba[2], (5, 5))\n\n    def test_with_three_points_slice(self):\n        cba = ia.Polygon([(1, 2), (3, 4), (5, 5)])\n        assert np.allclose(cba[1:], [(3, 4), (5, 5)])\n\n\nclass TestPolygon___iter__(unittest.TestCase):\n    def test_with_three_points(self):\n        cba = ia.Polygon([(1, 2), (3, 4), (5, 5)])\n        for i, xy in enumerate(cba):\n            assert i in [0, 1, 2]\n            if i == 0:\n                assert np.allclose(xy, (1, 2))\n            elif i == 1:\n                assert np.allclose(xy, (3, 4))\n            elif i == 2:\n                assert np.allclose(xy, (5, 5))\n        assert i == 2\n\n    def test_with_zero_points(self):\n        cba = ia.Polygon([])\n        i = 0\n        for xy in cba:\n            i += 1\n        assert i == 0\n\n\n# TODO add test for _convert_points_to_shapely_line_string\n\n\nclass TestPolygonsOnImage___init__(unittest.TestCase):\n    def test_with_one_polygon(self):\n        # standard case with one polygon\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])],\n            shape=(10, 10, 3)\n        )\n        assert len(poly_oi.polygons) == 1\n        assert np.allclose(\n            poly_oi.polygons[0].exterior,\n            [(0, 0), (1, 0), (1, 1), (0, 1)],\n            rtol=0, atol=1e-4)\n        assert poly_oi.shape == (10, 10, 3)\n\n    def test_with_multiple_polygons(self):\n        # standard case with multiple polygons\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),\n             ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n             ia.Polygon([(0.5, 0), (1, 0.5), (0.5, 1), (0, 0.5)])],\n            shape=(10, 10, 3)\n        )\n        assert len(poly_oi.polygons) == 3\n        assert np.allclose(\n            poly_oi.polygons[0].exterior,\n            [(0, 0), (1, 0), (1, 1), (0, 1)],\n            rtol=0, atol=1e-4)\n        assert np.allclose(\n            poly_oi.polygons[1].exterior,\n            [(0, 0), (1, 0), (1, 1)],\n            rtol=0, atol=1e-4)\n        assert np.allclose(\n            poly_oi.polygons[2].exterior,\n            [(0.5, 0), (1, 0.5), (0.5, 1), (0, 0.5)],\n            rtol=0, atol=1e-4)\n        assert poly_oi.shape == (10, 10, 3)\n\n    def test_with_zero_polygons(self):\n        # list of polygons is empty\n        poly_oi = ia.PolygonsOnImage(\n            [],\n            shape=(10, 10, 3)\n        )\n        assert len(poly_oi.polygons) == 0\n\n    def test_with_invalid_polygon(self):\n        # invalid polygon\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (0.5, 0), (0.5, 1.5), (0, 1), (1, 1), (0, 1)])],\n            shape=(10, 10, 3)\n        )\n        assert len(poly_oi.polygons) == 1\n        assert np.allclose(\n            poly_oi.polygons[0].exterior,\n            [(0, 0), (0.5, 0), (0.5, 1.5), (0, 1), (1, 1), (0, 1)],\n            rtol=0, atol=1e-4)\n\n    def test_with_zero_polygons_and_shape_given_as_array(self):\n        # shape given as numpy array\n        with assertWarns(self, ia.DeprecationWarning):\n            poly_oi = ia.PolygonsOnImage(\n                [],\n                shape=np.zeros((10, 10, 3), dtype=np.uint8)\n            )\n        assert poly_oi.shape == (10, 10, 3)\n\n    def test_with_zero_polygons_and_shape_given_as_2d_tuple(self):\n        # 2D shape\n        poly_oi = ia.PolygonsOnImage(\n            [],\n            shape=(10, 11)\n        )\n        assert poly_oi.shape == (10, 11)\n\n\nclass TestPolygonsOnImage_items(unittest.TestCase):\n    def test_with_two_polygons(self):\n        poly1 = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        poly2 = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        psoi = ia.PolygonsOnImage(\n            [poly1, poly2],\n            shape=(10, 10, 3)\n        )\n\n        items = psoi.items\n\n        assert items == [poly1, poly2]\n\n    def test_items_empty(self):\n        psoi = ia.PolygonsOnImage([], shape=(40, 50, 3))\n\n        items = psoi.items\n\n        assert items == []\n\n\nclass TestPolygonsOnImage_items_setter(unittest.TestCase):\n    def test_with_list_of_polygons(self):\n        ps = [ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n              ia.Polygon([(1, 1), (2, 1), (2, 2)])]\n        psoi = ia.PolygonsOnImage([], shape=(10, 20, 3))\n        psoi.items = ps\n        assert np.all([\n            (np.allclose(ps_i.coords, ps_j.coords))\n            for ps_i, ps_j\n            in zip(psoi.polygons, ps)\n        ])\n\n\nclass TestPolygonsOnImage_empty(unittest.TestCase):\n    def test_with_multiple_polygons(self):\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),\n             ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n             ia.Polygon([(0.5, 0), (1, 0.5), (0.5, 1), (0, 0.5)])],\n            shape=(10, 10, 3)\n        )\n        assert poly_oi.empty is False\n\n    def test_with_zero_polygons(self):\n        # list of polygons is empty\n        poly_oi = ia.PolygonsOnImage([], shape=(10, 10, 3))\n        assert poly_oi.empty is True\n\n\nclass TestPolygonsOnImage_on_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, psoi, image):\n        return psoi.on_(image)\n\n    def test_new_shape_is_identical_to_old_shape(self):\n        # size unchanged\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),\n             ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n             ia.Polygon([(0.5, 0), (1, 0.5), (0.5, 1), (0, 0.5)])],\n            shape=(1, 1, 3)\n        )\n        poly_oi_proj = self._func(poly_oi, (1, 1, 3))\n        assert np.allclose(\n            poly_oi_proj.polygons[0].exterior,\n            [(0, 0), (1, 0), (1, 1), (0, 1)],\n            rtol=0, atol=1e-4)\n        assert np.allclose(\n            poly_oi_proj.polygons[1].exterior,\n            [(0, 0), (1, 0), (1, 1)],\n            rtol=0, atol=1e-4)\n        assert np.allclose(\n            poly_oi_proj.polygons[2].exterior,\n            [(0.5, 0), (1, 0.5), (0.5, 1), (0, 0.5)],\n            rtol=0, atol=1e-4)\n        assert poly_oi_proj.shape == (1, 1, 3)\n\n    def test_new_shape_is_10x_smaller_than_old_shape(self):\n        # 10x decrease in size\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (10, 0), (10, 10), (0, 10)]),\n             ia.Polygon([(0, 0), (10, 0), (10, 10)]),\n             ia.Polygon([(5, 0), (10, 5), (5, 10), (0, 5)])],\n            shape=(10, 10, 3)\n        )\n        poly_oi_proj = self._func(poly_oi, (1, 1, 3))\n        assert np.allclose(\n            poly_oi_proj.polygons[0].exterior,\n            [(0, 0), (1, 0), (1, 1), (0, 1)],\n            rtol=0, atol=1e-4)\n        assert np.allclose(\n            poly_oi_proj.polygons[1].exterior,\n            [(0, 0), (1, 0), (1, 1)],\n            rtol=0, atol=1e-4)\n        assert np.allclose(\n            poly_oi_proj.polygons[2].exterior,\n            [(0.5, 0), (1, 0.5), (0.5, 1), (0, 0.5)],\n            rtol=0, atol=1e-4)\n        assert poly_oi_proj.shape == (1, 1, 3)\n\n    def test_new_shape_is_2x_width_and_10x_height_of_old_shape(self):\n        # 2x increase in width, 10x decrease in height\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (50, 0), (50, 100), (0, 100)])],\n            shape=(100, 100, 3)\n        )\n        poly_oi_proj = self._func(poly_oi, (10, 200, 3))\n        assert np.allclose(\n            poly_oi_proj.polygons[0].exterior,\n            [(0, 0), (100, 0), (100, 10), (0, 10)],\n            rtol=0, atol=1e-4)\n        assert poly_oi_proj.shape == (10, 200, 3)\n\n\nclass TestPolygonsOnImage_on(TestPolygonsOnImage_on_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, psoi, image):\n        return psoi.on(image)\n\n\nclass TestPolygonsOnImage_draw_on_image(unittest.TestCase):\n    def test_with_zero_polygons(self):\n        # no polygons, nothing changed\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n\n        poly_oi = ia.PolygonsOnImage([], shape=image.shape)\n        image_drawn = poly_oi.draw_on_image(image)\n        assert np.sum(image) == 0\n        assert np.sum(image_drawn) == 0\n\n    def test_with_two_polygons(self):\n        # draw two polygons\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (9, 1), (9, 9), (1, 9)]),\n             ia.Polygon([(3, 3), (7, 3), (7, 7), (3, 7)])],\n            shape=image.shape)\n        image_expected = np.copy(image)\n        image_expected = poly_oi.polygons[0].draw_on_image(image_expected)\n        image_expected = poly_oi.polygons[1].draw_on_image(image_expected)\n        image_drawn = poly_oi.draw_on_image(image)\n\n        assert np.sum(image) == 0\n        assert np.sum(image_drawn) > 0\n        assert np.sum(image_expected) > 0\n        assert np.allclose(image_drawn, image_expected)\n\n\nclass TestPolygonsOnImage_remove_out_of_image_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, psoi, fully=True, partly=False):\n        return psoi.remove_out_of_image_(fully, partly)\n\n    def test_with_zero_polygons(self):\n        # no polygons, nothing to remove\n        poly_oi = ia.PolygonsOnImage([], shape=(10, 11, 3))\n        for fully, partly in [(False, False), (False, True),\n                              (True, False), (True, True)]:\n            poly_oi_rm = self._func(poly_oi.deepcopy(),\n                                    fully=fully, partly=partly)\n            assert len(poly_oi_rm.polygons) == 0\n            assert poly_oi_rm.shape == (10, 11, 3)\n\n    def test_one_polygon_fully_inside_image(self):\n        # one polygon, fully inside the image\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (9, 1), (9, 9), (1, 9)])],\n            shape=(10, 11, 3))\n        for fully, partly in [(False, False), (False, True),\n                              (True, False), (True, True)]:\n            poly_oi_rm = self._func(poly_oi.deepcopy(),\n                                    fully=fully, partly=partly)\n            assert len(poly_oi_rm.polygons) == 1\n            assert np.allclose(poly_oi_rm.polygons[0].exterior,\n                               [(1, 1), (9, 1), (9, 9), (1, 9)],\n                               rtol=0, atol=1e-4)\n            assert poly_oi_rm.shape == (10, 11, 3)\n\n    def test_one_poly_partially_ooi_one_fully_ooi(self):\n        # two polygons, one partly outside, one fully outside\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (11, 1), (11, 9), (1, 9)]),\n             ia.Polygon([(100, 100), (200, 100), (200, 200), (100, 200)])],\n            shape=(10, 10, 3))\n\n        poly_oi_rm = self._func(poly_oi.deepcopy(), fully=False, partly=False)\n        assert len(poly_oi.polygons) == 2\n        assert len(poly_oi_rm.polygons) == 2\n        assert np.allclose(poly_oi_rm.polygons[0].exterior,\n                           [(1, 1), (11, 1), (11, 9), (1, 9)],\n                           rtol=0, atol=1e-4)\n        assert np.allclose(poly_oi_rm.polygons[1].exterior,\n                           [(100, 100), (200, 100), (200, 200), (100, 200)],\n                           rtol=0, atol=1e-4)\n        assert poly_oi_rm.shape == (10, 10, 3)\n\n        poly_oi_rm = self._func(poly_oi.deepcopy(), fully=True, partly=False)\n        assert len(poly_oi.polygons) == 2\n        assert len(poly_oi_rm.polygons) == 1\n        assert np.allclose(poly_oi_rm.polygons[0].exterior,\n                           [(1, 1), (11, 1), (11, 9), (1, 9)],\n                           rtol=0, atol=1e-4)\n        assert poly_oi_rm.shape == (10, 10, 3)\n\n        poly_oi_rm = self._func(poly_oi.deepcopy(), fully=False, partly=True)\n        assert len(poly_oi.polygons) == 2\n        assert len(poly_oi_rm.polygons) == 1\n        assert np.allclose(poly_oi_rm.polygons[0].exterior,\n                           [(100, 100), (200, 100), (200, 200), (100, 200)],\n                           rtol=0, atol=1e-4)\n        assert poly_oi_rm.shape == (10, 10, 3)\n\n        poly_oi_rm = self._func(poly_oi.deepcopy(), fully=True, partly=True)\n        assert len(poly_oi.polygons) == 2\n        assert len(poly_oi_rm.polygons) == 0\n        assert poly_oi_rm.shape == (10, 10, 3)\n\n    def test_inplaceness(self):\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (11, 1), (11, 9), (1, 9)]),\n             ia.Polygon([(100, 100), (200, 100), (200, 200), (100, 200)])],\n            shape=(10, 10, 3))\n\n        poly_oi_rm = self._func(poly_oi.deepcopy(), fully=True, partly=False)\n\n        if self._is_inplace:\n            assert poly_oi_rm is poly_oi\n        else:\n            assert poly_oi_rm is not poly_oi\n\n\nclass TestPolygonsOnImage_remove_out_if_image(\n        TestPolygonsOnImage_remove_out_of_image_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, psoi, fully=True, partly=False):\n        return psoi.remove_out_of_image(fully, partly)\n\n\nclass TestPolygonsOnImage_remove_out_of_image_fraction_(unittest.TestCase):\n    def test_three_polygons(self):\n        item1 = ia.Polygon([(5, 1), (9, 1), (9, 2), (5, 2)])\n        item2 = ia.Polygon([(5, 1), (15, 1), (15, 2), (5, 2)])\n        item3 = ia.Polygon([(15, 1), (25, 1), (25, 2), (15, 2)])\n        cbaoi = ia.PolygonsOnImage([item1, item2, item3],\n                                   shape=(10, 10, 3))\n\n        cbaoi_reduced = cbaoi.remove_out_of_image_fraction_(0.6)\n\n        assert len(cbaoi_reduced.items) == 2\n        assert cbaoi_reduced.items == [item1, item2]\n        assert cbaoi_reduced is cbaoi\n\n\nclass TestPolygonsOnImage_remove_out_of_image_fraction(unittest.TestCase):\n    def test_three_polygons(self):\n        item1 = ia.Polygon([(5, 1), (9, 1), (9, 2), (5, 2)])\n        item2 = ia.Polygon([(5, 1), (15, 1), (15, 2), (5, 2)])\n        item3 = ia.Polygon([(15, 1), (25, 1), (25, 2), (15, 2)])\n        cbaoi = ia.PolygonsOnImage([item1, item2, item3],\n                                   shape=(10, 10, 3))\n\n        cbaoi_reduced = cbaoi.remove_out_of_image_fraction(0.6)\n\n        assert len(cbaoi_reduced.items) == 2\n        assert cbaoi_reduced.items == [item1, item2]\n        assert cbaoi_reduced is not cbaoi\n\n\nclass TestPolygonsOnImage_clip_out_of_image_(unittest.TestCase):\n    # NOTE: clip_out_of_image() can change the order of points,\n    # hence we check here for each expected point whether it appears\n    # somewhere in the list of points\n\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, psoi):\n        return psoi.clip_out_of_image_()\n\n    @classmethod\n    def _any_point_close(cls, points, point_search):\n        found = False\n        for point in points:\n            if np.allclose(point, point_search, atol=1e-4, rtol=0):\n                found = True\n        return found\n\n    def test_with_zero_polygons(self):\n        # no polygons\n        poly_oi = ia.PolygonsOnImage([], shape=(10, 11, 3))\n        poly_oi_clip = self._func(poly_oi)\n        assert len(poly_oi_clip.polygons) == 0\n        assert poly_oi_clip.shape == (10, 11, 3)\n\n    def test_with_one_polygon_fully_inside(self):\n        # one polygon, fully inside\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (8, 1), (8, 9), (1, 9)])],\n            shape=(10, 11, 3))\n        poly_oi_clip = self._func(poly_oi)\n        assert len(poly_oi_clip.polygons) == 1\n        for point_search in [(1, 1), (8, 1), (8, 9), (1, 9)]:\n            assert self._any_point_close(poly_oi_clip.polygons[0].exterior,\n                                         point_search)\n        assert poly_oi_clip.shape == (10, 11, 3)\n\n    def test_with_one_polygon_partially_ooi(self):\n        # one polygon, partially outside\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (15, 1), (15, 9), (1, 9)])],\n            shape=(10, 11, 3))\n        poly_oi_clip = self._func(poly_oi)\n        assert len(poly_oi_clip.polygons) == 1\n        for point_search in [(1, 1), (11, 1), (11, 9), (1, 9)]:\n            assert self._any_point_close(poly_oi_clip.polygons[0].exterior,\n                                         point_search)\n        assert poly_oi_clip.shape == (10, 11, 3)\n\n    def test_with_one_polygon_fully_ooi(self):\n        # one polygon, fully outside\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(100, 100), (200, 100), (200, 200), (100, 200)])],\n            shape=(10, 11, 3))\n        poly_oi_clip = self._func(poly_oi)\n        assert len(poly_oi_clip.polygons) == 0\n        assert poly_oi_clip.shape == (10, 11, 3)\n\n    def test_with_three_pols_one_not_ooi_one_partially_ooi_one_fully_ooi(self):\n        # three polygons, one fully inside, one partially outside,\n        # one fully outside\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (8, 1), (8, 9), (1, 9)]),\n             ia.Polygon([(1, 1), (15, 1), (15, 9), (1, 9)]),\n             ia.Polygon([(100, 100), (200, 100), (200, 200), (100, 200)])],\n            shape=(10, 11, 3))\n        poly_oi_clip = self._func(poly_oi)\n        assert len(poly_oi_clip.polygons) == 2\n        for point_search in [(1, 1), (8, 1), (8, 9), (1, 9)]:\n            assert self._any_point_close(poly_oi_clip.polygons[0].exterior,\n                                         point_search)\n        for point_search in [(1, 1), (11, 1), (11, 9), (1, 9)]:\n            assert self._any_point_close(poly_oi_clip.polygons[1].exterior,\n                                         point_search)\n        assert poly_oi_clip.shape == (10, 11, 3)\n\n    def test_inplaceness(self):\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (15, 1), (15, 9), (1, 9)])],\n            shape=(10, 11, 3))\n\n        poly_oi_clip = self._func(poly_oi)\n\n        if self._is_inplace:\n            assert poly_oi_clip is poly_oi\n        else:\n            assert poly_oi_clip is not poly_oi\n\n\nclass TestPolygonsOnImage_clip_out_of_image(\n        TestPolygonsOnImage_clip_out_of_image_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, psoi):\n        return psoi.clip_out_of_image()\n\n\nclass TestPolygonsOnImage_shift_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, psoi, *args, **kwargs):\n        def _func_impl():\n            return psoi.shift_(*args, **kwargs)\n\n        if len(psoi.polygons) == 0:\n            return _func_impl()\n        return wrap_shift_deprecation(_func_impl, *args, **kwargs)\n\n    def test_with_three_polygons_along_xy(self):\n        # three polygons\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (8, 1), (8, 9), (1, 9)]),\n             ia.Polygon([(1, 1), (15, 1), (15, 9), (1, 9)]),\n             ia.Polygon([(100, 100), (200, 100), (200, 200), (100, 200)])],\n            shape=(10, 11, 3))\n        poly_oi_shifted = self._func(poly_oi, x=1, y=2)\n        assert len(poly_oi_shifted.polygons) == 3\n        assert np.allclose(poly_oi_shifted.polygons[0].exterior,\n                           [(1+1, 1+2), (8+1, 1+2), (8+1, 9+2), (1+1, 9+2)],\n                           rtol=0, atol=1e-4)\n        assert np.allclose(poly_oi_shifted.polygons[1].exterior,\n                           [(1+1, 1+2), (15+1, 1+2), (15+1, 9+2), (1+1, 9+2)],\n                           rtol=0, atol=1e-4)\n        assert np.allclose(poly_oi_shifted.polygons[2].exterior,\n                           [(100+1, 100+2), (200+1, 100+2),\n                            (200+1, 200+2), (100+1, 200+2)],\n                           rtol=0, atol=1e-4)\n        assert poly_oi_shifted.shape == (10, 11, 3)\n\n    def test_inplaceness(self):\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (8, 1), (8, 9), (1, 9)]),\n             ia.Polygon([(1, 1), (15, 1), (15, 9), (1, 9)]),\n             ia.Polygon([(100, 100), (200, 100), (200, 200), (100, 200)])],\n            shape=(10, 11, 3))\n\n        poly_oi_shifted = self._func(poly_oi, x=1)\n\n        if self._is_inplace:\n            assert poly_oi_shifted is poly_oi\n        else:\n            assert poly_oi_shifted is not poly_oi\n\n\nclass TestPolygonsOnImage_shift(TestPolygonsOnImage_shift_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, psoi, *args, **kwargs):\n        def _func_impl():\n            return psoi.shift(*args, **kwargs)\n\n        return wrap_shift_deprecation(_func_impl, *args, **kwargs)\n\n    def test_with_zero_polygons(self):\n        # no polygons\n        poly_oi = ia.PolygonsOnImage([], shape=(10, 11, 3))\n        poly_oi_shifted = self._func(poly_oi, top=3, right=0, bottom=1,\n                                     left=-3)\n        assert len(poly_oi_shifted.polygons) == 0\n        assert poly_oi_shifted.shape == (10, 11, 3)\n\n    def test_with_three_polygons(self):\n        # three polygons\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (8, 1), (8, 9), (1, 9)]),\n             ia.Polygon([(1, 1), (15, 1), (15, 9), (1, 9)]),\n             ia.Polygon([(100, 100), (200, 100), (200, 200), (100, 200)])],\n            shape=(10, 11, 3))\n        poly_oi_shifted = self._func(poly_oi, top=3, right=0, bottom=1,\n                                     left=-3)\n        assert len(poly_oi_shifted.polygons) == 3\n        assert np.allclose(poly_oi_shifted.polygons[0].exterior,\n                           [(1-3, 1+2), (8-3, 1+2), (8-3, 9+2), (1-3, 9+2)],\n                           rtol=0, atol=1e-4)\n        assert np.allclose(poly_oi_shifted.polygons[1].exterior,\n                           [(1-3, 1+2), (15-3, 1+2), (15-3, 9+2), (1-3, 9+2)],\n                           rtol=0, atol=1e-4)\n        assert np.allclose(poly_oi_shifted.polygons[2].exterior,\n                           [(100-3, 100+2), (200-3, 100+2),\n                            (200-3, 200+2), (100-3, 200+2)],\n                           rtol=0, atol=1e-4)\n        assert poly_oi_shifted.shape == (10, 11, 3)\n\n\nclass TestPolygonsOnImage_subdivide_(unittest.TestCase):\n    @property\n    def _is_inplace(self):\n        return True\n\n    def _func(self, psoi, points_per_edge):\n        return psoi.subdivide_(points_per_edge)\n\n    def test_mocked(self):\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (8, 1), (8, 9), (1, 9)]),\n             ia.Polygon([(1, 1), (15, 1), (15, 9), (1, 9)])],\n            shape=(10, 11, 3))\n        mock_sub = mock.Mock()\n        mock_sub.return_value = \"foo\"\n        poly_oi.items[0].subdivide_ = mock_sub\n        poly_oi.items[1].subdivide_ = mock_sub\n\n        poly_oi_sub = self._func(poly_oi, 2)\n\n        assert mock_sub.call_count == 2\n        assert mock_sub.call_args_list[0][0][0] == 2\n        assert mock_sub.call_args_list[1][0][0] == 2\n        assert poly_oi_sub.items == [\"foo\", \"foo\"]\n\n    def test_with_zero_polygons(self):\n        poly_oi = ia.PolygonsOnImage([], shape=(10, 11, 3))\n        poly_oi_sub = self._func(poly_oi, 1)\n        assert len(poly_oi_sub.polygons) == 0\n        assert poly_oi_sub.shape == (10, 11, 3)\n\n    def test_with_one_polygon(self):\n        poly_oi = ia.PolygonsOnImage([ia.Polygon([(0, 0), (1, 0)])],\n                                     shape=(10, 11, 3))\n        poly_oi_sub = self._func(poly_oi, 1)\n        assert poly_oi_sub.shape == (10, 11, 3)\n        assert len(poly_oi_sub.items) == 1\n        assert poly_oi_sub.items[0].coords_almost_equals([\n            (0, 0),\n            (0.5, 0),\n            (1, 0),\n            (0.5, 0)\n        ])\n\n    def test_inplaceness(self):\n        poly_oi = ia.PolygonsOnImage([ia.Polygon([(0, 0), (1, 0)])],\n                                     shape=(10, 11, 3))\n\n        poly_oi_sub = self._func(poly_oi, 1)\n\n        if self._is_inplace:\n            assert poly_oi_sub is poly_oi\n        else:\n            assert poly_oi_sub is not poly_oi\n\n\nclass TestPolygonsOnImage_subdivide(TestPolygonsOnImage_subdivide_):\n    @property\n    def _is_inplace(self):\n        return False\n\n    def _func(self, psoi, points_per_edge):\n        return psoi.subdivide(points_per_edge)\n\n    def test_mocked(self):\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (8, 1), (8, 9), (1, 9)]),\n             ia.Polygon([(1, 1), (15, 1), (15, 9), (1, 9)])],\n            shape=(10, 11, 3))\n        mock_sub = mock.Mock()\n        mock_sub.return_value = \"foo\"\n        poly_oi.items[0].subdivide_ = mock_sub\n        poly_oi.items[1].subdivide_ = mock_sub\n\n        poly_oi_sub = self._func(poly_oi, 2)\n\n        # When the PSOI is copied, each polygon is also copied. That leads\n        # to new Polygon instances that have no longer the subdivide_\n        # method overwritten by the mock. Hence, the mock is never actually\n        # called here.\n        assert mock_sub.call_count == 0\n        assert poly_oi_sub.items != [\"foo\", \"foo\"]\n\n\nclass TestPolygonsOnImage_to_xy_array(unittest.TestCase):\n    def test_filled_object(self):\n        psoi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n             ia.Polygon([(10, 10), (20, 0), (20, 20)])],\n            shape=(2, 2, 3))\n\n        xy_out = psoi.to_xy_array()\n\n        expected = np.float32([\n            [0.0, 0.0],\n            [1.0, 0.0],\n            [1.0, 1.0],\n            [10.0, 10.0],\n            [20.0, 0.0],\n            [20.0, 20.0]\n        ])\n        assert xy_out.shape == (6, 2)\n        assert np.allclose(xy_out, expected)\n        assert xy_out.dtype.name == \"float32\"\n\n    def test_empty_object(self):\n        psoi = ia.PolygonsOnImage(\n            [],\n            shape=(1, 2, 3))\n\n        xy_out = psoi.to_xy_array()\n\n        assert xy_out.shape == (0, 2)\n        assert xy_out.dtype.name == \"float32\"\n\n\nclass TestPolygonsOnImage_fill_from_xy_array_(unittest.TestCase):\n    def test_empty_array(self):\n        xy = np.zeros((0, 2), dtype=np.float32)\n        psoi = ia.PolygonsOnImage([], shape=(2, 2, 3))\n\n        psoi = psoi.fill_from_xy_array_(xy)\n\n        assert len(psoi.polygons) == 0\n\n    def test_empty_list(self):\n        xy = []\n        psoi = ia.PolygonsOnImage([], shape=(2, 2, 3))\n\n        psoi = psoi.fill_from_xy_array_(xy)\n\n        assert len(psoi.polygons) == 0\n\n    def test_array_with_two_coords(self):\n        xy = np.array(\n            [(100, 100),\n             (101, 100),\n             (101, 101),\n             (110, 110),\n             (120, 100),\n             (120, 120)], dtype=np.float32)\n        psoi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n             ia.Polygon([(10, 10), (20, 0), (20, 20)])],\n            shape=(2, 2, 3))\n\n        psoi = psoi.fill_from_xy_array_(xy)\n\n        assert len(psoi.polygons) == 2\n        assert np.allclose(\n            psoi.polygons[0].coords,\n            [(100, 100), (101, 100), (101, 101)])\n        assert np.allclose(\n            psoi.polygons[1].coords,\n            [(110, 110), (120, 100), (120, 120)])\n\n    def test_list_with_two_coords(self):\n        xy = [(100, 100),\n              (101, 100),\n              (101, 101),\n              (110, 110),\n              (120, 100),\n              (120, 120)]\n        psoi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n             ia.Polygon([(10, 10), (20, 0), (20, 20)])],\n            shape=(2, 2, 3))\n\n        psoi = psoi.fill_from_xy_array_(xy)\n\n        assert len(psoi.polygons) == 2\n        assert np.allclose(\n            psoi.polygons[0].coords,\n            [(100, 100), (101, 100), (101, 101)])\n        assert np.allclose(\n            psoi.polygons[1].coords,\n            [(110, 110), (120, 100), (120, 120)])\n\n\nclass TestPolygonsOnImage_to_keypoints_on_image(unittest.TestCase):\n    def test_filled_instance(self):\n        psoi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n             ia.Polygon([(10, 10), (20, 0), (20, 20)])],\n            shape=(1, 2, 3))\n\n        kpsoi = psoi.to_keypoints_on_image()\n\n        assert len(kpsoi.keypoints) == 2*3\n        assert kpsoi.keypoints[0].x == 0\n        assert kpsoi.keypoints[0].y == 0\n        assert kpsoi.keypoints[1].x == 1\n        assert kpsoi.keypoints[1].y == 0\n        assert kpsoi.keypoints[2].x == 1\n        assert kpsoi.keypoints[2].y == 1\n        assert kpsoi.keypoints[3].x == 10\n        assert kpsoi.keypoints[3].y == 10\n        assert kpsoi.keypoints[4].x == 20\n        assert kpsoi.keypoints[4].y == 0\n        assert kpsoi.keypoints[5].x == 20\n        assert kpsoi.keypoints[5].y == 20\n\n    def test_empty_instance(self):\n        psoi = ia.PolygonsOnImage([], shape=(1, 2, 3))\n\n        kpsoi = psoi.to_keypoints_on_image()\n\n        assert len(kpsoi.keypoints) == 0\n\n\nclass TestPolygonsOnImage_invert_to_keypoints_on_image(unittest.TestCase):\n    def test_filled_instance(self):\n        psoi = ia.PolygonsOnImage(\n            [ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n             ia.Polygon([(10, 10), (20, 0), (20, 20)])],\n            shape=(1, 2, 3))\n        kpsoi = ia.KeypointsOnImage(\n            [ia.Keypoint(100, 100), ia.Keypoint(101, 100),\n             ia.Keypoint(101, 101),\n             ia.Keypoint(110, 110), ia.Keypoint(120, 100),\n             ia.Keypoint(120, 120)],\n            shape=(10, 20, 30))\n\n        psoi_inv = psoi.invert_to_keypoints_on_image_(kpsoi)\n\n        assert len(psoi_inv.polygons) == 2\n        assert psoi_inv.shape == (10, 20, 30)\n        assert np.allclose(\n            psoi.polygons[0].coords,\n            [(100, 100), (101, 100), (101, 101)])\n        assert np.allclose(\n            psoi.polygons[1].coords,\n            [(110, 110), (120, 100), (120, 120)])\n\n    def test_empty_instance(self):\n        psoi = ia.PolygonsOnImage([], shape=(1, 2, 3))\n        kpsoi = ia.KeypointsOnImage([], shape=(10, 20, 30))\n\n        psoi_inv = psoi.invert_to_keypoints_on_image_(kpsoi)\n\n        assert len(psoi_inv.polygons) == 0\n        assert psoi_inv.shape == (10, 20, 30)\n\n\nclass TestPolygonsOnImage_copy(unittest.TestCase):\n    def test_with_two_polygons(self):\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (8, 1), (8, 9), (1, 9)]),\n             ia.Polygon([(2, 2), (16, 2), (16, 10), (2, 10)])],\n            shape=(10, 11, 3))\n        poly_oi_copy = poly_oi.copy()\n        assert len(poly_oi_copy.polygons) == 2\n        assert np.allclose(poly_oi_copy.polygons[0].exterior,\n                           [(1, 1), (8, 1), (8, 9), (1, 9)],\n                           rtol=0, atol=1e-4)\n        assert np.allclose(poly_oi_copy.polygons[1].exterior,\n                           [(2, 2), (16, 2), (16, 10), (2, 10)],\n                           rtol=0, atol=1e-4)\n\n        poly_oi_copy.shape = (20, 30, 3)\n        assert poly_oi.shape == (10, 11, 3)\n        assert poly_oi_copy.shape == (20, 30, 3)\n\n        # make sure that changing the polygons only affects the copy\n        poly_oi_copy.polygons = [ia.Polygon([(0, 0), (1, 0), (1, 1)])]\n        assert np.allclose(poly_oi.polygons[0].exterior,\n                           [(1, 1), (8, 1), (8, 9), (1, 9)],\n                           rtol=0, atol=1e-4)\n        assert np.allclose(poly_oi_copy.polygons[0].exterior,\n                           [(0, 0), (1, 0), (1, 1)],\n                           rtol=0, atol=1e-4)\n\n    def test_polygons_parameter_set(self):\n        poly1 = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly2 = ia.Polygon([(0+1, 0), (1+1, 0), (1+1, 1)])\n        poly3 = ia.Polygon([(0+2, 0), (1+2, 0), (1+2, 1)])\n        psoi = ia.PolygonsOnImage([poly1, poly2], shape=(40, 50, 3))\n\n        psoi_copy = psoi.copy(polygons=[poly3])\n\n        assert psoi_copy is not psoi\n        assert psoi_copy.shape == (40, 50, 3)\n        assert psoi_copy.polygons == [poly3]\n\n    def test_shape_parameter_set(self):\n        poly1 = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly2 = ia.Polygon([(0+1, 0), (1+1, 0), (1+1, 1)])\n        psoi = ia.PolygonsOnImage([poly1, poly2], shape=(40, 50, 3))\n\n        psoi_copy = psoi.copy(shape=(40+1, 50+1, 3))\n\n        assert psoi_copy is not psoi\n        assert psoi_copy.shape == (40+1, 50+1, 3)\n        assert psoi_copy.polygons == [poly1, poly2]\n\n\nclass TestPolygonsOnImage_deepcopy(unittest.TestCase):\n    def test_with_two_polygons(self):\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (8, 1), (8, 9), (1, 9)]),\n             ia.Polygon([(2, 2), (16, 2), (16, 10), (2, 10)])],\n            shape=(10, 11, 3))\n        poly_oi_copy = poly_oi.deepcopy()\n        assert len(poly_oi_copy.polygons) == 2\n        assert np.allclose(poly_oi_copy.polygons[0].exterior,\n                           [(1, 1), (8, 1), (8, 9), (1, 9)],\n                           rtol=0, atol=1e-4)\n        assert np.allclose(poly_oi_copy.polygons[1].exterior,\n                           [(2, 2), (16, 2), (16, 10), (2, 10)],\n                           rtol=0, atol=1e-4)\n\n        poly_oi_copy.shape = (20, 30, 3)\n        assert poly_oi.shape == (10, 11, 3)\n        assert poly_oi_copy.shape == (20, 30, 3)\n\n        # make sure that changing the polygons only affects the copy\n        poly_oi_copy.polygons[0] = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        assert np.allclose(poly_oi.polygons[0].exterior,\n                           [(1, 1), (8, 1), (8, 9), (1, 9)],\n                           rtol=0, atol=1e-4)\n        assert np.allclose(poly_oi_copy.polygons[0].exterior,\n                           [(0, 0), (1, 0), (1, 1)],\n                           rtol=0, atol=1e-4)\n\n        # make sure that the arrays were also copied\n        poly_oi_copy.polygons[1].exterior[0][0] = 100\n        assert np.allclose(poly_oi.polygons[1].exterior,\n                           [(2, 2), (16, 2), (16, 10), (2, 10)],\n                           rtol=0, atol=1e-4)\n        assert np.allclose(poly_oi_copy.polygons[1].exterior,\n                           [(100, 2), (16, 2), (16, 10), (2, 10)],\n                           rtol=0, atol=1e-4)\n\n    def test_polygons_parameter_set(self):\n        poly1 = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly2 = ia.Polygon([(0+1, 0), (1+1, 0), (1+1, 1)])\n        poly3 = ia.Polygon([(0+2, 0), (1+2, 0), (1+2, 1)])\n        psoi = ia.PolygonsOnImage([poly1, poly2], shape=(40, 50, 3))\n\n        psoi_copy = psoi.deepcopy(polygons=[poly3])\n\n        assert psoi_copy is not psoi\n        assert psoi_copy.shape == (40, 50, 3)\n        assert len(psoi_copy.polygons) == 1\n        assert psoi_copy.polygons[0].coords_almost_equals(poly3)\n\n    def test_shape_parameter_set(self):\n        poly1 = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        poly2 = ia.Polygon([(0+1, 0), (1+1, 0), (1+1, 1)])\n        psoi = ia.PolygonsOnImage([poly1, poly2], shape=(40, 50, 3))\n\n        psoi_copy = psoi.deepcopy(shape=(40+1, 50+1, 3))\n\n        assert psoi_copy is not psoi\n        assert psoi_copy.shape == (40+1, 50+1, 3)\n        assert len(psoi_copy.polygons) == 2\n        assert psoi_copy.polygons[0].coords_almost_equals(poly1)\n        assert psoi_copy.polygons[1].coords_almost_equals(poly2)\n\n\nclass TestPolygonsOnImage___getitem__(unittest.TestCase):\n    def test_with_two_polygons(self):\n        cbas = [\n            ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n            ia.Polygon([(1, 1), (2, 1), (2, 2)])\n        ]\n        cbasoi = ia.PolygonsOnImage(cbas, shape=(3, 4, 3))\n\n        assert cbasoi[0] is cbas[0]\n        assert cbasoi[1] is cbas[1]\n        assert cbasoi[0:2] == cbas\n\n\nclass TestPolygonsOnImage___iter__(unittest.TestCase):\n    def test_with_two_polygons(self):\n        cbas = [ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n                ia.Polygon([(1, 0), (2, 2), (1.5, 3)])]\n        cbasoi = ia.PolygonsOnImage(cbas, shape=(40, 50, 3))\n\n        for i, cba in enumerate(cbasoi):\n            assert cba is cbas[i]\n\n    def test_with_zero_polygons(self):\n        cbasoi = ia.PolygonsOnImage([], shape=(40, 50, 3))\n        i = 0\n        for _cba in cbasoi:\n            i += 1\n        assert i == 0\n\n\nclass TestPolygonsOnImage___len__(unittest.TestCase):\n    def test_with_two_polygons(self):\n        cbas = [ia.Polygon([(0, 0), (1, 0), (1, 1)]),\n                ia.Polygon([(1, 0), (2, 2), (1.5, 3)])]\n        cbasoi = ia.PolygonsOnImage(cbas, shape=(40, 50, 3))\n        assert len(cbasoi) == 2\n\n\nclass TestPolygonsOnImage___repr___and___str__(unittest.TestCase):\n    def test_with_zero_polygons(self):\n        poly_oi = ia.PolygonsOnImage([], shape=(10, 11, 3))\n        expected = \"PolygonsOnImage([], shape=(10, 11, 3))\"\n        assert poly_oi.__repr__() == expected\n        assert poly_oi.__str__() == expected\n\n    def test_with_two_polygons(self):\n        poly_oi = ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (8, 1), (8, 9), (1, 9)]),\n             ia.Polygon([(2, 2), (16, 2), (16, 10), (2, 10)])],\n            shape=(10, 11, 3))\n        expected = (\n            \"PolygonsOnImage([\"\n            \"Polygon([(x=1.000, y=1.000), (x=8.000, y=1.000), \"\n            \"(x=8.000, y=9.000), (x=1.000, y=9.000)] \"\n            \"(4 points), label=None), \"\n            \"Polygon([(x=2.000, y=2.000), (x=16.000, y=2.000), \"\n            \"(x=16.000, y=10.000), (x=2.000, y=10.000)] \"\n            \"(4 points), label=None)\"\n            \"], shape=(10, 11, 3))\"\n        )\n        assert poly_oi.__repr__() == expected\n        assert poly_oi.__str__() == expected\n\n\nclass Test_ConcavePolygonRecoverer(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @classmethod\n    def _assert_points_are_identical(cls, observed, expected, atol=1e-8,\n                                     rtol=0):\n        assert len(observed) == len(expected)\n        for i, (ps_obs, ps_exp) in enumerate(zip(observed, expected)):\n            assert len(ps_obs) == len(ps_exp), \"Failed at point %d\" % (i,)\n            for p_obs, p_exp in zip(ps_obs, ps_exp):\n                assert len(p_obs) == 2\n                assert len(p_exp) == 2\n                assert np.allclose(p_obs, p_exp, atol=atol, rtol=rtol), (\n                    \"Unexpected coords at %d\" % (i,))\n\n    # TODO split into multiple tests\n    def test_recover_from_fails_for_less_than_three_points(self):\n        old_polygon = ia.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])\n        cpr = _ConcavePolygonRecoverer()\n        with self.assertRaises(AssertionError):\n            _poly = cpr.recover_from([], old_polygon)\n\n        with self.assertRaises(AssertionError):\n            _poly = cpr.recover_from([(0, 0)], old_polygon)\n\n        with self.assertRaises(AssertionError):\n            _poly = cpr.recover_from([(0, 0), (1, 0)], old_polygon)\n\n        _poly = cpr.recover_from([(0, 0), (1, 0), (1, 1)], old_polygon)\n\n    def test_recover_from_concave_polygons(self):\n        cpr = _ConcavePolygonRecoverer()\n\n        polys = [\n            [(0, 0), (1, 0), (1, 1)],\n            [(0, 0), (1, 0), (1, 1), (0, 1)],\n            [(0, 0), (0.5, 0), (1, 0), (1, 0.5), (1, 1), (0.5, 1.0), (0, 1)],\n        ]\n\n        for poly in polys:\n            with self.subTest(poly=str(poly)):\n                old_polygon = ia.Polygon(poly)\n                poly_concave = cpr.recover_from(poly, old_polygon)\n                assert poly_concave.is_valid\n                found = [False] * len(poly)\n                for i, point in enumerate(poly):\n                    for point_ext in poly_concave.exterior:\n                        dist = np.sqrt((point[0] - point_ext[0])**2\n                                       + (point[1] - point_ext[1])**2)\n                        if dist < 0.01:\n                            found[i] = True\n                assert np.all(found)\n\n    def test_recover_from_line(self):\n        cpr = _ConcavePolygonRecoverer()\n\n        poly = [(0, 0), (1, 0), (2, 0)]\n        old_polygon = ia.Polygon(poly)\n        poly_concave = cpr.recover_from(poly, old_polygon)\n        assert poly_concave.is_valid\n        found = [False] * len(poly)\n        for i, point in enumerate(poly):\n            for point_ext in poly_concave.exterior:\n                dist = np.sqrt((point[0] - point_ext[0])**2\n                               + (point[1] - point_ext[1])**2)\n                if dist < 0.025:\n                    found[i] = True\n        assert np.all(found)\n\n    def test_recover_from_polygon_with_duplicate_points(self):\n        cpr = _ConcavePolygonRecoverer()\n\n        poly = [(0, 0), (1, 0), (1, 0), (1, 1)]\n        old_polygon = ia.Polygon(poly)\n        poly_concave = cpr.recover_from(poly, old_polygon)\n        assert poly_concave.is_valid\n        found = [False] * len(poly)\n        for i, point in enumerate(poly):\n            for point_ext in poly_concave.exterior:\n                dist = np.sqrt((point[0] - point_ext[0])**2\n                               + (point[1] - point_ext[1])**2)\n                if dist < 0.01:\n                    found[i] = True\n        assert np.all(found)\n\n    def test_recover_from_invalid_polygon(self):\n        cpr = _ConcavePolygonRecoverer()\n        poly = [(0, 0), (0.5, 0), (0.5, 1.2), (1, 0), (1, 1), (0, 1)]\n        old_polygon = ia.Polygon(poly)\n        poly_concave = cpr.recover_from(poly, old_polygon)\n        assert poly_concave.is_valid\n        found = [False] * len(poly)\n        for i, point in enumerate(poly):\n            for point_ext in poly_concave.exterior:\n                dist = np.sqrt(\n                    (point[0] - point_ext[0])**2\n                    + (point[1] - point_ext[1])**2\n                )\n                if dist < 0.025:\n                    found[i] = True\n        assert np.all(found)\n\n    def test_recover_from_random_polygons(self):\n        cpr = _ConcavePolygonRecoverer()\n        nb_iterations = 10\n        height, width = 10, 20\n        nb_points_matrix = np.random.randint(3, 30, size=(nb_iterations,))\n        for nb_points in nb_points_matrix:\n            points = np.random.random(size=(nb_points, 2))\n            points[:, 0] *= width\n            points[:, 1] *= height\n            # currently mainly used to copy the label, so not a significant\n            # issue that it is not concave\n            old_polygon = ia.Polygon(points)\n            poly_concave = cpr.recover_from(points, old_polygon)\n            assert poly_concave.is_valid\n            # test if all points are in BB around returned polygon\n            # would be better to directly call a polygon.contains(point) method\n            # but that does not yet exist\n            xx = poly_concave.exterior[:, 0]\n            yy = poly_concave.exterior[:, 1]\n            bb_x1, bb_x2 = min(xx), max(xx)\n            bb_y1, bb_y2 = min(yy), max(yy)\n            bb = ia.BoundingBox(\n                x1=bb_x1-1e-4, y1=bb_y1-1e-4,\n                x2=bb_x2+1e-4, y2=bb_y2+1e-4)\n            for point in points:\n                assert bb.contains(ia.Keypoint(x=point[0], y=point[1]))\n\n    def test__remove_consecutive_duplicate_points(self):\n        recoverer = _ConcavePolygonRecoverer()\n\n        points = [\n            [(0, 0), (1, 1)],\n            [(0.0, 0.5), (1.0, 1.0)],\n            np.float32([(0.0, 0.5), (1.0, 1.0)]),\n            [(0, 0), (0, 0)],\n            [(0, 0), (0, 0), (1, 0)],\n            [(0, 0), (1, 0), (1, 0)],\n            [(0, 0), (1, 0), (1, 0), (2, 0), (0, 0)]\n        ]\n        expected = [\n            [(0, 0), (1, 1)],\n            [(0.0, 0.5), (1.0, 1.0)],\n            [(0.0, 0.5), (1.0, 1.0)],\n            [(0, 0)],\n            [(0, 0), (1, 0)],\n            [(0, 0), (1, 0)],\n            [(0, 0), (1, 0), (2, 0)]\n        ]\n\n        for points_i, expected_i in zip(points, expected):\n            with self.subTest(points=points_i):\n                points_deduplicated = \\\n                    recoverer._remove_consecutive_duplicate_points(points_i)\n                assert np.allclose(points_deduplicated, expected_i)\n\n    # TODO split into multiple tests\n    def test__jitter_duplicate_points(self):\n        def _norm(a, b):\n            return np.linalg.norm(np.float32(a) - np.float32(b))\n\n        cpr = _ConcavePolygonRecoverer(threshold_duplicate_points=1e-4)\n        rng = iarandom.RNG(0)\n\n        points = [(0, 0), (1, 0), (1, 1), (0, 1)]\n        points_jittered = cpr._jitter_duplicate_points(points, rng.copy())\n        assert np.allclose(points, points_jittered, rtol=0, atol=1e-4)\n\n        points = [(0, 0), (1, 0), (0, 1)]\n        points_jittered = cpr._jitter_duplicate_points(points, rng.copy())\n        assert np.allclose(points, points_jittered, rtol=0, atol=1e-4)\n\n        points = [(0, 0), (0.01, 0), (0.01, 0.01), (0, 0.01)]\n        points_jittered = cpr._jitter_duplicate_points(points, rng.copy())\n        assert np.allclose(points, points_jittered, rtol=0, atol=1e-4)\n\n        points = [(0, 0), (1, 0), (1 + 1e-6, 0), (1, 1), (0, 1)]\n        points_jittered = cpr._jitter_duplicate_points(points, rng.copy())\n        assert np.allclose(\n            [point\n             for i, point\n             in enumerate(points_jittered)\n             if i in [0, 1, 3, 4]],\n            [(0, 0), (1, 0), (1, 1), (0, 1)],\n            rtol=0,\n            atol=1e-5\n        )\n        assert _norm([1, 0], points_jittered[2]) >= 1e-4\n\n        points = [(0, 0), (1, 0), (1, 1), (1 + 1e-6, 0), (0, 1)]\n        points_jittered = cpr._jitter_duplicate_points(points, rng.copy())\n        assert np.allclose(\n            [point\n             for i, point\n             in enumerate(points_jittered)\n             if i in [0, 1, 2, 4]],\n            [(0, 0), (1, 0), (1, 1), (0, 1)],\n            rtol=0,\n            atol=1e-5\n        )\n        assert _norm([1, 0], points_jittered[3]) >= 1e-4\n\n        points = [(0, 0), (1, 0), (1, 1), (0, 1), (1 + 1e-6, 0)]\n        points_jittered = cpr._jitter_duplicate_points(points, rng.copy())\n        assert np.allclose(\n            [point\n             for i, point\n             in enumerate(points_jittered)\n             if i in [0, 1, 2, 3]],\n            [(0, 0), (1, 0), (1, 1), (0, 1)],\n            rtol=0,\n            atol=1e-5\n        )\n        assert _norm([1, 0], points_jittered[4]) >= 1e-4\n\n        points = [(0, 0), (1, 0), (1 + 1e-6, 0), (1, 1), (1 + 1e-6, 0), (0, 1),\n                  (1 + 1e-6, 0), (1 + 1e-6, 0 + 1e-6), (1 + 1e-6, 0 + 2e-6)]\n        points_jittered = cpr._jitter_duplicate_points(points, rng.copy())\n        assert np.allclose(\n            [point\n             for i, point\n             in enumerate(points_jittered)\n             if i in [0, 1, 3, 5]],\n            [(0, 0), (1, 0), (1, 1), (0, 1)],\n            rtol=0,\n            atol=1e-5\n        )\n        assert _norm([1, 0], points_jittered[2]) >= 1e-4\n        assert _norm([1, 0], points_jittered[4]) >= 1e-4\n        assert _norm([1, 0], points_jittered[6]) >= 1e-4\n        assert _norm([1, 0], points_jittered[7]) >= 1e-4\n        assert _norm([1, 0], points_jittered[8]) >= 1e-4\n\n        points = [(0, 0), (1, 0), (0 + 1e-6, 0 - 1e-6), (1 + 1e-6, 0), (1, 1),\n                  (1 + 1e-6, 0), (0, 1), (1 + 1e-6, 0), (1 + 1e-6, 0 + 1e-6),\n                  (1 + 1e-6, 0 + 2e-6)]\n        points_jittered = cpr._jitter_duplicate_points(points, rng.copy())\n        assert np.allclose(\n            [point\n             for i, point\n             in enumerate(points_jittered)\n             if i in [0, 1, 4, 6]],\n            [(0, 0), (1, 0), (1, 1), (0, 1)],\n            rtol=0,\n            atol=1e-5\n        )\n        assert _norm([0, 0], points_jittered[2]) >= 1e-4\n        assert _norm([1, 0], points_jittered[3]) >= 1e-4\n        assert _norm([1, 0], points_jittered[5]) >= 1e-4\n        assert _norm([1, 0], points_jittered[7]) >= 1e-4\n        assert _norm([1, 0], points_jittered[8]) >= 1e-4\n        assert _norm([1, 0], points_jittered[9]) >= 1e-4\n\n    # TODO split into multiple tests\n    def test__calculate_circumference(self):\n        points = [(0, 0), (1, 0), (1, 1), (0, 1)]\n        circ = _ConcavePolygonRecoverer._calculate_circumference(points)\n        assert np.allclose(circ, 4)\n\n        points = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)]\n        circ = _ConcavePolygonRecoverer._calculate_circumference(points)\n        assert np.allclose(circ, 4)\n\n        points = np.float32([(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)])\n        circ = _ConcavePolygonRecoverer._calculate_circumference(points)\n        assert np.allclose(circ, 4)\n\n        points = [(0, 0), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0)]\n        circ = _ConcavePolygonRecoverer._calculate_circumference(points)\n        assert np.allclose(circ, 6)\n\n    # TODO split into multiple tests\n    def test__fit_best_valid_polygon(self):\n        def _assert_ids_match(observed, expected):\n            assert len(observed) == len(expected), (\n                \"len mismatch: %d vs %d\" % (len(observed), len(expected)))\n\n            max_count = 0\n            for i in range(len(observed)):\n                counter = 0\n                for j in range(i, i+len(expected)):\n                    observed_item = observed[(i+j) % len(observed)]\n                    expected_item = expected[j % len(expected)]\n                    if observed_item == expected_item:\n                        counter += 1\n                    else:\n                        break\n\n                max_count = max(max_count, counter)\n\n            assert max_count == len(expected), (\n                \"count mismatch: %d vs %d\" % (max_count, len(expected)))\n\n        cpr = _ConcavePolygonRecoverer()\n        rng = iarandom.RNG(0)\n\n        points = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)]\n        points_fit = cpr._fit_best_valid_polygon(\n            points, random_state=rng.copy())\n        # doing this without the list(.) wrappers fails on python2.7\n        assert list(points_fit) == list(sm.xrange(len(points)))\n\n        # square-like, but top line has one point in its center which's\n        # y-coordinate is below the bottom line\n        points = [(0.0, 0.0), (0.45, 0.0), (0.5, 1.5), (0.55, 0.0), (1.0, 0.0),\n                  (1.0, 1.0), (0.0, 1.0)]\n        points_fit = cpr._fit_best_valid_polygon(\n            points, random_state=rng.copy())\n        _assert_ids_match(points_fit, [0, 1, 3, 4, 5, 2, 6])\n        assert ia.Polygon([points[idx] for idx in points_fit]).is_valid\n\n        # |--|  |--|\n        # |  |  |  |\n        # |  |  |  |\n        # |--|--|--|\n        #    |  |\n        #    ----\n        # the intersection points on the bottom line are not provided,\n        # hence the result is expected to have triangles at the bottom left\n        # and right\n        points = [(0.0, 0), (0.25, 0), (0.25, 1.25),\n                  (0.75, 1.25), (0.75, 0), (1.0, 0),\n                  (1.0, 1.0), (0.0, 1.0)]\n        points_fit = cpr._fit_best_valid_polygon(\n            points, random_state=rng.copy())\n        _assert_ids_match(points_fit, [0, 1, 4, 5, 6, 3, 2, 7])\n        poly_observed = ia.Polygon([points[idx] for idx in points_fit])\n        assert poly_observed.is_valid\n\n        # same as above, but intersection points at the bottom line are\n        # provided without oversampling, i.e. incorporating these points\n        # would lead to an invalid polygon\n        points = [(0.0, 0), (0.25, 0), (0.25, 1.0), (0.25, 1.25),\n                  (0.75, 1.25), (0.75, 1.0), (0.75, 0), (1.0, 0),\n                  (1.0, 1.0), (0.0, 1.0)]\n        points_fit = cpr._fit_best_valid_polygon(\n            points, random_state=rng.copy())\n        assert len(points_fit) >= len(points) - 2  # TODO add IoU check here\n        poly_observed = ia.Polygon([points[idx] for idx in points_fit])\n        assert poly_observed.is_valid\n\n    # TODO split into multiple tests\n    def test__fix_polygon_is_line(self):\n        cpr = _ConcavePolygonRecoverer()\n        rng = iarandom.RNG(0)\n\n        points = [(0, 0), (1, 0), (1, 1)]\n        points_fixed = cpr._fix_polygon_is_line(points, rng.copy())\n        assert np.allclose(points_fixed, points, atol=0, rtol=0)\n\n        points = [(0, 0), (1, 0), (2, 0)]\n        points_fixed = cpr._fix_polygon_is_line(points, rng.copy())\n        assert not np.allclose(points_fixed, points, atol=0, rtol=0)\n        assert not cpr._is_polygon_line(points_fixed)\n        assert np.allclose(points_fixed, points, rtol=0, atol=1e-2)\n\n        points = [(0, 0), (0, 1), (0, 2)]\n        points_fixed = cpr._fix_polygon_is_line(points, rng.copy())\n        assert not np.allclose(points_fixed, points, atol=0, rtol=0)\n        assert not cpr._is_polygon_line(points_fixed)\n        assert np.allclose(points_fixed, points, rtol=0, atol=1e-2)\n\n        points = [(0, 0), (1, 1), (2, 2)]\n        points_fixed = cpr._fix_polygon_is_line(points, rng.copy())\n        assert not np.allclose(points_fixed, points, atol=0, rtol=0)\n        assert not cpr._is_polygon_line(points_fixed)\n        assert np.allclose(points_fixed, points, rtol=0, atol=1e-2)\n\n    # TODO split into multiple tests\n    def test__is_polygon_line(self):\n        points = [(0, 0), (1, 0), (1, 1)]\n        assert not _ConcavePolygonRecoverer._is_polygon_line(points)\n\n        points = [(0, 0), (1, 0), (1, 1), (0, 1)]\n        assert not _ConcavePolygonRecoverer._is_polygon_line(points)\n\n        points = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)]\n        assert not _ConcavePolygonRecoverer._is_polygon_line(points)\n\n        points = np.float32([(0, 0), (1, 0), (1, 1), (0, 1)])\n        assert not _ConcavePolygonRecoverer._is_polygon_line(points)\n\n        points = [(0, 0), (1, 0)]\n        assert _ConcavePolygonRecoverer._is_polygon_line(points)\n\n        points = [(0, 0), (1, 0), (2, 0)]\n        assert _ConcavePolygonRecoverer._is_polygon_line(points)\n\n        points = [(0, 0), (1, 0), (1, 0)]\n        assert _ConcavePolygonRecoverer._is_polygon_line(points)\n\n        points = [(0, 0), (1, 0), (1, 0), (2, 0)]\n        assert _ConcavePolygonRecoverer._is_polygon_line(points)\n\n        points = [(0, 0), (1, 0), (1, 0), (2, 0), (0.5, 0)]\n        assert _ConcavePolygonRecoverer._is_polygon_line(points)\n\n        points = [(0, 0), (1, 0), (1, 0), (2, 0), (1, 1)]\n        assert not _ConcavePolygonRecoverer._is_polygon_line(points)\n\n    # TODO split into multiple tests\n    def test__generate_intersection_points(self):\n        cpr = _ConcavePolygonRecoverer()\n\n        # triangle\n        points = [(0.5, 0), (1, 1), (0, 1)]\n        points_inter = cpr._generate_intersection_points(\n            points, one_point_per_intersection=False)\n        assert points_inter == [[], [], []]\n\n        # rotated square\n        points = [(0.5, 0), (1, 0.5), (0.5, 1), (0, 0.5)]\n        points_inter = cpr._generate_intersection_points(\n            points, one_point_per_intersection=False)\n        assert points_inter == [[], [], [], []]\n\n        # square\n        points = [(0, 0), (1, 0), (1, 1), (0, 1)]\n        points_inter = cpr._generate_intersection_points(\n            points, one_point_per_intersection=False)\n        assert points_inter == [[], [], [], []]\n\n        # |--|  |--|\n        # |  |__|  |\n        # |        |\n        # |--------|\n        points = [(0.0, 0), (0.25, 0), (0.25, 0.25),\n                  (0.75, 0.25), (0.75, 0), (1.0, 0),\n                  (1.0, 1.0), (0.0, 1.0)]\n        points_inter = cpr._generate_intersection_points(\n            points, one_point_per_intersection=False)\n        assert points_inter == [[], [], [], [], [], [], [], []]\n\n        # same as above, but middle part goes much further down,\n        # crossing the bottom line\n        points = [(0.0, 0), (0.25, 0), (0.25, 1.25),\n                  (0.75, 1.25), (0.75, 0), (1.0, 0),\n                  (1.0, 1.0), (0.0, 1.0)]\n        points_inter = cpr._generate_intersection_points(\n            points, one_point_per_intersection=False)\n        self._assert_points_are_identical(\n            points_inter,\n            [[], [(0.25, 1.0)], [], [(0.75, 1.0)], [], [],\n             [(0.75, 1.0), (0.25, 1.0)], []])\n\n        # square-like structure with intersections in top right area\n        points = [(0, 0), (0.5, 0), (1.01, 0.5), (1.0, 0), (1, 1), (0, 1),\n                  (0, 0)]\n        points_inter = cpr._generate_intersection_points(\n            points, one_point_per_intersection=False)\n        self._assert_points_are_identical(\n            points_inter,\n            [[], [(1.0, 0.4902)], [], [(1.0, 0.4902)], [], [], []],\n            atol=1e-2)\n\n        # same as above, but with a second intersection in bottom left\n        points = [(0, 0), (0.5, 0), (1.01, 0.5), (1.0, 0), (1, 1), (-0.25, 1),\n                  (0, 1.25)]\n        points_inter = cpr._generate_intersection_points(\n            points, one_point_per_intersection=False)\n        self._assert_points_are_identical(\n            points_inter,\n            [[], [(1.0, 0.4902)], [], [(1.0, 0.4902)], [(0, 1.0)], [],\n             [(0, 1.0)]],\n            atol=1e-2)\n\n        # double triangle with point in center that is shared by both triangles\n        points = [(0, 0), (0.5, 0.5), (1.0, 0), (1.0, 1.0), (0.5, 0.5),\n                  (0, 1.0)]\n        points_inter = cpr._generate_intersection_points(\n            points, one_point_per_intersection=False)\n        self._assert_points_are_identical(\n            points_inter,\n            [[], [], [], [], [], []])\n\n    # TODO split into multiple tests\n    def test__oversample_intersection_points(self):\n        cpr = _ConcavePolygonRecoverer()\n        cpr.oversampling = 0.1\n\n        points = [(0.0, 0.0), (1.0, 0.0)]\n        segment_add_points_sorted = [[(0.5, 0.0)], []]\n        points_oversampled = cpr._oversample_intersection_points(\n            points, segment_add_points_sorted)\n        self._assert_points_are_identical(\n            points_oversampled,\n            [[(0.45, 0.0), (0.5, 0.0), (0.55, 0.0)], []],\n            atol=1e-4\n        )\n\n        points = [(0.0, 0.0), (2.0, 0.0)]\n        segment_add_points_sorted = [[(0.5, 0.0)], []]\n        points_oversampled = cpr._oversample_intersection_points(\n            points, segment_add_points_sorted)\n        self._assert_points_are_identical(\n            points_oversampled,\n            [[(0.45, 0.0), (0.5, 0.0), (0.65, 0.0)], []],\n            atol=1e-4\n        )\n\n        points = [(0.0, 0.0), (1.0, 0.0)]\n        segment_add_points_sorted = [[(0.5, 0.0), (0.6, 0.0)], []]\n        points_oversampled = cpr._oversample_intersection_points(\n            points, segment_add_points_sorted)\n        self._assert_points_are_identical(\n            points_oversampled,\n            [[(0.45, 0.0), (0.5, 0.0), (0.51, 0.0), (0.59, 0.0), (0.6, 0.0),\n              (0.64, 0.0)], []],\n            atol=1e-4\n        )\n\n        points = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)]\n        segment_add_points_sorted = [[(0.5, 0.0)], [], [(0.8, 1.0)],\n                                     [(0.0, 0.7)]]\n        points_oversampled = cpr._oversample_intersection_points(\n            points, segment_add_points_sorted)\n        self._assert_points_are_identical(\n            points_oversampled,\n            [[(0.45, 0.0), (0.5, 0.0), (0.55, 0.0)],\n             [],\n             [(0.82, 1.0), (0.8, 1.0), (0.72, 1.0)],\n             [(0.0, 0.73), (0.0, 0.7), (0.0, 0.63)]],\n            atol=1e-4\n        )\n\n    # TODO split into multiple tests\n    def test__insert_intersection_points(self):\n        points = [(0, 0), (1, 0), (2, 0)]\n        segments_add_point_sorted = [[], [], []]\n        points_inserted = _ConcavePolygonRecoverer._insert_intersection_points(\n            points, segments_add_point_sorted)\n        assert points_inserted == points\n\n        segments_add_point_sorted = [[(0.5, 0)], [], []]\n        points_inserted = _ConcavePolygonRecoverer._insert_intersection_points(\n            points, segments_add_point_sorted)\n        assert points_inserted == [(0, 0), (0.5, 0), (1, 0), (2, 0)]\n\n        segments_add_point_sorted = [[(0.5, 0), (0.75, 0)], [], []]\n        points_inserted = _ConcavePolygonRecoverer._insert_intersection_points(\n            points, segments_add_point_sorted)\n        assert points_inserted == [(0, 0), (0.5, 0), (0.75, 0), (1, 0), (2, 0)]\n\n        segments_add_point_sorted = [[(0.5, 0)], [(1.5, 0)], []]\n        points_inserted = _ConcavePolygonRecoverer._insert_intersection_points(\n            points, segments_add_point_sorted)\n        assert points_inserted == [(0, 0), (0.5, 0), (1, 0), (1.5, 0), (2, 0)]\n\n        segments_add_point_sorted = [[(0.5, 0)], [(1.5, 0)], [(2.5, 0)]]\n        points_inserted = _ConcavePolygonRecoverer._insert_intersection_points(\n            points, segments_add_point_sorted)\n        assert points_inserted == [(0, 0), (0.5, 0), (1, 0), (1.5, 0), (2, 0),\n                                   (2.5, 0)]\n"
  },
  {
    "path": "test/augmentables/test_segmaps.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport itertools\nimport warnings\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\n\nimport imgaug as ia\nimport imgaug.augmentables.segmaps as segmapslib\n\n\n# old style segmentation maps (class name differs to new style by \"Map\"\n# instead of \"Maps\")\nclass TestSegmentationMapOnImage(unittest.TestCase):\n    def test_warns_that_it_is_deprecated(self):\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            segmap = segmapslib.SegmentationMapOnImage(\n                np.zeros((1, 1, 1), dtype=np.int32),\n                shape=(1, 1, 3)\n            )\n            assert segmap.arr.dtype.name == \"int32\"\n            assert segmap.arr.shape == (1, 1, 1)\n            assert segmap.shape == (1, 1, 3)\n        assert len(caught_warnings) == 1\n        assert \"is deprecated\" in str(caught_warnings[0].message)\n\n\nclass TestSegmentationMapsOnImage___init__(unittest.TestCase):\n    def test_uint_int_arrs(self):\n        dtypes = [\"int8\", \"int16\", \"int32\", \"uint8\", \"uint16\"]\n        ndims = [2, 3]\n        img_shapes = [(3, 3), (3, 3, 3), (4, 5, 3)]\n\n        gen = itertools.product(dtypes, ndims, img_shapes)\n        for dtype, ndim, img_shape in gen:\n            with self.subTest(dtype=dtype, ndim=ndim, shape=img_shape):\n                dtype = np.dtype(dtype)\n                shape = (3, 3) if ndim == 2 else (3, 3, 1)\n                arr = np.array([\n                    [0, 0, 1],\n                    [0, 2, 1],\n                    [1, 3, 1]\n                ], dtype=dtype).reshape(shape)\n                segmap = ia.SegmentationMapsOnImage(arr, shape=img_shape)\n                assert segmap.shape == img_shape\n                assert segmap.arr.dtype.name == \"int32\"\n                assert segmap.arr.shape == (3, 3, 1)\n                assert np.array_equal(segmap.arr,\n                                      arr.reshape((3, 3, 1)).astype(np.int32))\n\n                if ndim == 3:\n                    arr = np.array([\n                        [0, 0, 1],\n                        [0, 2, 1],\n                        [1, 3, 1]\n                    ], dtype=dtype).reshape((3, 3, 1))\n                    arr = np.tile(arr, (1, 1, 5))\n                    segmap = ia.SegmentationMapsOnImage(arr, shape=img_shape)\n                    assert segmap.shape == img_shape\n                    assert segmap.arr.dtype.name == \"int32\"\n                    assert segmap.arr.shape == (3, 3, 5)\n                    assert np.array_equal(segmap.arr, arr.astype(np.int32))\n\n    def test_bool_arr_2d(self):\n        arr = np.array([\n            [0, 0, 1],\n            [0, 1, 1],\n            [1, 1, 1]\n        ], dtype=bool).reshape((3, 3))\n\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n\n        assert segmap.shape == (3, 3)\n        assert segmap.arr.dtype.name == \"int32\"\n        assert segmap.arr.shape == (3, 3, 1)\n        assert np.array_equal(segmap.arr,\n                              arr.reshape((3, 3, 1)).astype(np.int32))\n\n    def test_bool_arr_3d(self):\n        arr = np.array([\n            [0, 0, 1],\n            [0, 1, 1],\n            [1, 1, 1]\n        ], dtype=bool).reshape((3, 3, 1))\n        arr = np.tile(arr, (1, 1, 5))\n\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n\n        assert segmap.shape == (3, 3)\n        assert segmap.arr.dtype.name == \"int32\"\n        assert segmap.arr.shape == (3, 3, 5)\n        assert np.array_equal(segmap.arr, arr.astype(np.int32))\n\n    # is this different from the test_bool_* tests?\n    def test_boolean_masks(self):\n        # Test for #189 (boolean mask inputs into SegmentationMapsOnImage not\n        # working)\n        for dt in [bool, np.bool]:\n            arr = np.array([\n                [0, 0, 0],\n                [0, 1, 0],\n                [0, 0, 0]\n            ], dtype=dt)\n            assert arr.dtype.kind == \"b\"\n            segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n            assert np.array_equal(\n                segmap.arr,\n                np.int32([\n                    [0, 0, 0],\n                    [0, 1, 0],\n                    [0, 0, 0]\n                ])[:, :, np.newaxis]\n            )\n            assert segmap.get_arr().dtype.name == arr.dtype.name\n            assert np.array_equal(segmap.get_arr(), arr)\n\n    def test_uint32_fails(self):\n        got_exception = False\n        try:\n            arr = np.array([\n                [0, 0, 1],\n                [0, 2, 1],\n                [1, 3, 1]\n            ], dtype=np.uint32)\n            _segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3, 3))\n        except Exception as exc:\n            assert \"only uint8 and uint16 \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_uint64_fails(self):\n        got_exception = False\n        try:\n            arr = np.array([\n                [0, 0, 1],\n                [0, 2, 1],\n                [1, 3, 1]\n            ], dtype=np.int64)\n            _segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3, 3))\n        except Exception as exc:\n            assert \"only int8, int16 and int32 \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_legacy_support_for_float32_2d(self):\n        arr = np.array([0.4, 0.6], dtype=np.float32).reshape((1, 2))\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            segmap = segmapslib.SegmentationMapsOnImage(arr, shape=(1, 1, 3))\n\n            arr_expected = np.array([0, 1], dtype=np.int32).reshape((1, 2, 1))\n            assert np.array_equal(segmap.arr, arr_expected)\n            assert segmap.shape == (1, 1, 3)\n\n        assert len(caught_warnings) == 1\n        assert (\n            \"Got a float array as the segmentation map in\"\n            in str(caught_warnings[0].message)\n        )\n\n    def test_legacy_support_for_float32_3d(self):\n        arr = np.array([\n            [\n                [0.4, 0.6],\n                [0.2, 0.1]\n            ]\n        ], dtype=np.float32).reshape((1, 2, 2))\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            segmap = segmapslib.SegmentationMapsOnImage(arr, shape=(1, 1, 3))\n\n            arr_expected = np.array([\n                [1, 0]\n            ], dtype=np.int32).reshape((1, 2, 1))\n            assert np.array_equal(segmap.arr, arr_expected)\n            assert segmap.shape == (1, 1, 3)\n\n        assert len(caught_warnings) == 1\n        assert (\n            \"Got a float array as the segmentation map in\"\n            in str(caught_warnings[0].message)\n        )\n\n\nclass TestSegmentationMapsOnImage_get_arr(unittest.TestCase):\n    def test_uint_int(self):\n        dtypes = [\"int8\", \"int16\", \"int32\", \"uint8\", \"uint16\"]\n        ndims = [2, 3]\n\n        for dtype, ndim in itertools.product(dtypes, ndims):\n            with self.subTest(dtype=dtype, ndim=ndim):\n                dtype = np.dtype(dtype)\n                shape = (3, 3) if ndim == 2 else (3, 3, 1)\n                arr = np.array([\n                    [0, 0, 1],\n                    [0, 2, 1],\n                    [1, 3, 1]\n                ], dtype=dtype).reshape(shape)\n                segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n\n                observed = segmap.get_arr()\n\n                assert segmap.arr.dtype.name == \"int32\"\n                assert segmap.arr.ndim == 3\n                assert np.array_equal(observed, arr)\n                assert observed.dtype.name == dtype.name\n                assert observed.ndim == ndim\n                assert np.array_equal(observed, arr)\n\n    def test_bool(self):\n        ndims = [2, 3]\n        for ndim in ndims:\n            with self.subTest(ndim=ndim):\n                shape = (3, 3) if ndim == 2 else (3, 3, 1)\n                arr = np.array([\n                    [0, 0, 1],\n                    [0, 1, 1],\n                    [1, 1, 1]\n                ], dtype=bool).reshape(shape)\n                segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n\n                observed = segmap.get_arr()\n\n                assert segmap.arr.dtype.name == \"int32\"\n                assert segmap.arr.ndim == 3\n                assert np.array_equal(observed, arr)\n                assert observed.dtype.kind == \"b\"\n                assert observed.ndim == ndim\n                assert np.array_equal(observed, arr)\n\n\nclass TestSegmentationMapsOnImage_draw(unittest.TestCase):\n    @property\n    def segmap(self):\n        arr = np.int32([\n            [0, 1, 1],\n            [0, 1, 1],\n            [0, 1, 1]\n        ])\n        return ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n\n    @classmethod\n    def col(cls, idx):\n        return ia.SegmentationMapsOnImage.DEFAULT_SEGMENT_COLORS[idx]\n\n    def test_with_two_classes(self):\n        # simple example with 2 classes\n        col0 = self.col(0)\n        col1 = self.col(1)\n        expected = np.uint8([\n            [col0, col1, col1],\n            [col0, col1, col1],\n            [col0, col1, col1]\n        ])\n\n        observed = self.segmap.draw()\n\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert np.array_equal(observed[0], expected)\n\n    def test_use_size_arg_to_resize_to_2x(self):\n        # same example, with resizing to 2x the size\n        double_size_args = [\n            (6, 6),\n            (2.0, 2.0),\n            6,\n            2.0\n        ]\n\n        col0 = self.col(0)\n        col1 = self.col(1)\n        expected = np.uint8([\n            [col0, col1, col1],\n            [col0, col1, col1],\n            [col0, col1, col1]\n        ])\n        expected = ia.imresize_single_image(expected,\n                                            (6, 6),\n                                            interpolation=\"nearest\")\n\n        for double_size_arg in double_size_args:\n            with self.subTest(size=double_size_arg):\n                observed = self.segmap.draw(size=double_size_arg)\n\n                assert isinstance(observed, list)\n                assert len(observed) == 1\n                assert np.array_equal(observed[0], expected)\n\n    def test_use_size_arg_to_keep_at_same_size(self):\n        # same example, keeps size at 3x3 via None and (int)3 or (float)1.0\n        size_args = [\n            None,\n            (None, None),\n            (3, None),\n            (None, 3),\n            (1.0, None),\n            (None, 1.0)\n        ]\n\n        col0 = self.col(0)\n        col1 = self.col(1)\n        expected = np.uint8([\n            [col0, col1, col1],\n            [col0, col1, col1],\n            [col0, col1, col1]\n        ])\n        expected = ia.imresize_single_image(expected,\n                                            (3, 3),\n                                            interpolation=\"nearest\")\n\n        for size_arg in size_args:\n            with self.subTest(size=size_arg):\n                observed = self.segmap.draw(size=size_arg)\n\n                assert isinstance(observed, list)\n                assert len(observed) == 1\n                assert np.array_equal(observed[0], expected)\n\n    def test_colors(self):\n        # custom choice of colors\n        col0 = (10, 10, 10)\n        col1 = (50, 51, 52)\n        expected = np.uint8([\n            [col0, col1, col1],\n            [col0, col1, col1],\n            [col0, col1, col1]\n        ])\n\n        observed = self.segmap.draw(colors=[col0, col1])\n\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert np.array_equal(observed[0], expected)\n\n    def test_segmap_with_more_than_one_channel(self):\n        # test segmentation maps with multiple channels\n        arr_channel_1 = np.int32([\n            [0, 1, 5],\n            [0, 1, 1],\n            [0, 4, 1]\n        ])\n        arr_channel_2 = np.int32([\n            [1, 1, 0],\n            [2, 2, 0],\n            [1, 1, 0]\n        ])\n        arr_channel_3 = np.int32([\n            [1, 0, 0],\n            [0, 1, 0],\n            [0, 0, 3]\n        ])\n        arr_multi = np.stack(\n            [arr_channel_1, arr_channel_2, arr_channel_3],\n            axis=-1)\n\n        col = ia.SegmentationMapsOnImage.DEFAULT_SEGMENT_COLORS\n        expected_channel_1 = np.uint8([\n            [col[0], col[1], col[5]],\n            [col[0], col[1], col[1]],\n            [col[0], col[4], col[1]]\n        ])\n        expected_channel_2 = np.uint8([\n            [col[1], col[1], col[0]],\n            [col[2], col[2], col[0]],\n            [col[1], col[1], col[0]]\n        ])\n        expected_channel_3 = np.uint8([\n            [col[1], col[0], col[0]],\n            [col[0], col[1], col[0]],\n            [col[0], col[0], col[3]]\n        ])\n\n        segmap = ia.SegmentationMapsOnImage(arr_multi, shape=(3, 3, 3))\n\n        observed = segmap.draw()\n\n        assert isinstance(observed, list)\n        assert len(observed) == 3\n        assert np.array_equal(observed[0], expected_channel_1)\n        assert np.array_equal(observed[1], expected_channel_2)\n        assert np.array_equal(observed[2], expected_channel_3)\n\n\nclass TestSegmentationMapsOnImage_draw_on_image(unittest.TestCase):\n    @property\n    def segmap(self):\n        arr = np.int32([\n            [0, 1, 1],\n            [0, 1, 1],\n            [0, 1, 1]\n        ])\n        return ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n\n    @property\n    def image(self):\n        image = np.uint8([\n            [0, 10, 20],\n            [30, 40, 50],\n            [60, 70, 80]\n        ])\n        return np.tile(image[:, :, np.newaxis], (1, 1, 3))\n\n    @classmethod\n    def col(cls, idx):\n        return ia.SegmentationMapsOnImage.DEFAULT_SEGMENT_COLORS[idx]\n\n    def test_alpha_only_image_is_visible(self):\n        # only image visible\n        observed = self.segmap.draw_on_image(self.image, alpha=0)\n\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert np.array_equal(observed[0], self.image)\n\n    def test_alpha_only_segmap_is_visible(self):\n        # only segmap visible\n        observed = self.segmap.draw_on_image(self.image, alpha=1.0,\n                                             draw_background=True)\n        col0 = self.col(0)\n        col1 = self.col(1)\n        expected = np.uint8([\n            [col0, col1, col1],\n            [col0, col1, col1],\n            [col0, col1, col1]\n        ])\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert np.array_equal(observed[0], expected)\n\n    def test_alpha_with_draw_background(self):\n        # only segmap visible - in foreground\n        image = self.image\n\n        observed = self.segmap.draw_on_image(image, alpha=1.0,\n                                             draw_background=False)\n\n        col1 = self.col(1)\n        expected = np.uint8([\n            [image[0, 0, :], col1, col1],\n            [image[1, 0, :], col1, col1],\n            [image[2, 0, :], col1, col1]\n        ])\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert np.array_equal(observed[0], expected)\n\n    def test_alpha_with_draw_background_and_more_than_one_channel(self):\n        # only segmap visible in foreground + multiple channels in segmap\n        image = self.image\n\n        arr_channel_1 = np.int32([\n            [0, 1, 5],\n            [0, 1, 1],\n            [0, 4, 1]\n        ])\n        arr_channel_2 = np.int32([\n            [1, 1, 0],\n            [2, 2, 0],\n            [1, 1, 0]\n        ])\n        arr_channel_3 = np.int32([\n            [1, 0, 0],\n            [0, 1, 0],\n            [0, 0, 3]\n        ])\n        arr_multi = np.stack(\n            [arr_channel_1, arr_channel_2, arr_channel_3],\n            axis=-1)\n\n        col = ia.SegmentationMapsOnImage.DEFAULT_SEGMENT_COLORS\n        expected_channel_1 = np.uint8([\n            [image[0, 0, :], col[1], col[5]],\n            [image[1, 0, :], col[1], col[1]],\n            [image[2, 0, :], col[4], col[1]]\n        ])\n        expected_channel_2 = np.uint8([\n            [col[1], col[1], image[0, 2, :]],\n            [col[2], col[2], image[1, 2, :]],\n            [col[1], col[1], image[2, 2, :]]\n        ])\n        expected_channel_3 = np.uint8([\n            [col[1], image[0, 1, :], image[0, 2, :]],\n            [image[1, 0, :], col[1], image[1, 2, :]],\n            [image[2, 0, :], image[2, 1, :], col[3]]\n        ])\n\n        segmap_multi = ia.SegmentationMapsOnImage(arr_multi, shape=(3, 3, 3))\n\n        observed = segmap_multi.draw_on_image(\n            image, alpha=1.0, draw_background=False)\n\n        assert isinstance(observed, list)\n        assert len(observed) == 3\n        assert np.array_equal(observed[0], expected_channel_1)\n        assert np.array_equal(observed[1], expected_channel_2)\n        assert np.array_equal(observed[2], expected_channel_3)\n\n    def test_non_binary_alpha_with_draw_background(self):\n        # overlay without background drawn\n        im = self.image\n        segmap = self.segmap\n\n        a1 = 0.7\n        a0 = 1.0 - a1\n\n        observed = segmap.draw_on_image(im, alpha=a1, draw_background=False)\n\n        col1 = np.uint8(self.col(1))\n        expected = np.float32([\n            [im[0, 0, :], a0*im[0, 1, :] + a1*col1, a0*im[0, 2, :] + a1*col1],\n            [im[1, 0, :], a0*im[1, 1, :] + a1*col1, a0*im[1, 2, :] + a1*col1],\n            [im[2, 0, :], a0*im[2, 1, :] + a1*col1, a0*im[2, 2, :] + a1*col1]\n        ])\n        d_max = np.max(np.abs(observed[0].astype(np.float32) - expected))\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert observed[0].shape == expected.shape\n        assert d_max <= 1.0 + 1e-4\n\n    def test_non_binary_alpha_with_draw_background_and_bg_class_id(self):\n        # overlay without background drawn\n        # different background class id\n        image = self.image\n        segmap = self.segmap\n\n        a1 = 0.7\n        a0 = 1.0 - a1\n\n        observed = segmap.draw_on_image(image, alpha=a1, draw_background=False,\n                                        background_class_id=1)\n\n        col0 = np.uint8(self.col(0))\n        expected = np.float32([\n            [a0*image[0, 0, :] + a1*col0, image[0, 1, :], image[0, 2, :]],\n            [a0*image[1, 0, :] + a1*col0, image[1, 1, :], image[1, 2, :]],\n            [a0*image[2, 0, :] + a1*col0, image[2, 1, :], image[2, 2, :]]\n        ])\n        d_max = np.max(np.abs(observed[0].astype(np.float32) - expected))\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert observed[0].shape == expected.shape\n        assert d_max <= 1.0 + 1e-4\n\n    def test_non_binary_alpha_with_draw_background_true(self):\n        # overlay with background drawn\n        segmap = self.segmap\n        image = self.image\n\n        a1 = 0.7\n        a0 = 1.0 - a1\n\n        observed = segmap.draw_on_image(image, alpha=a1, draw_background=True)\n\n        col0 = self.col(0)\n        col1 = self.col(1)\n        expected = np.uint8([\n            [col0, col1, col1],\n            [col0, col1, col1],\n            [col0, col1, col1]\n        ])\n        expected = a0 * image + a1 * expected\n        d_max = np.max(\n            np.abs(\n                observed[0].astype(np.float32)\n                - expected.astype(np.float32)\n            )\n        )\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert observed[0].shape == expected.shape\n        assert d_max <= 1.0 + 1e-4\n\n    def test_resize_segmentation_map_to_image(self):\n        # resizing of segmap to image\n        arr = np.int32([\n            [0, 1, 1]\n        ])\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n\n        image = np.uint8([\n            [0, 10, 20],\n            [30, 40, 50],\n            [60, 70, 80]\n        ])\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 3))\n\n        a1 = 0.7\n        a0 = 1.0 - a1\n\n        observed = segmap.draw_on_image(image, alpha=a1, draw_background=True,\n                                        resize=\"segmentation_map\")\n\n        col0 = self.col(0)\n        col1 = self.col(1)\n        expected = np.uint8([\n            [col0, col1, col1],\n            [col0, col1, col1],\n            [col0, col1, col1]\n        ])\n        expected = a0 * image + a1 * expected\n        d_max = np.max(\n            np.abs(\n                observed[0].astype(np.float32)\n                - expected.astype(np.float32)\n            )\n        )\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert observed[0].shape == expected.shape\n        assert d_max <= 1.0 + 1e-4\n\n    def test_resize_image_to_segmentation_map(self):\n        # resizing of image to segmap\n        arr = np.int32([\n            [0, 1, 1],\n            [0, 1, 1],\n            [0, 1, 1]\n        ])\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(1, 3))\n\n        image = np.uint8([[0, 10, 20]])\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 3))\n        image_rs = ia.imresize_single_image(\n            image, arr.shape[0:2], interpolation=\"cubic\")\n\n        a1 = 0.7\n        a0 = 1.0 - a1\n\n        observed = segmap.draw_on_image(image, alpha=a1, draw_background=True,\n                                        resize=\"image\")\n\n        col0 = self.col(0)\n        col1 = self.col(1)\n        expected = np.uint8([\n            [col0, col1, col1],\n            [col0, col1, col1],\n            [col0, col1, col1]\n        ])\n        expected = a0 * image_rs + a1 * expected\n        d_max = np.max(\n            np.abs(\n                observed[0].astype(np.float32)\n                - expected.astype(np.float32)\n            )\n        )\n        assert isinstance(observed, list)\n        assert len(observed) == 1\n        assert observed[0].shape == expected.shape\n        assert d_max <= 1.0 + 1e-4\n\n    def test_background_threshold_leads_to_deprecation_warning(self):\n        arr = np.zeros((1, 1, 1), dtype=np.int32)\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            _ = segmap.draw_on_image(image, background_threshold=0.01)\n\n        assert len(caught_warnings) == 1\n        assert (\n            \"The argument `background_threshold` is deprecated\"\n            in str(caught_warnings[0].message)\n        )\n\n\nclass TestSegmentationMapsOnImage_pad(unittest.TestCase):\n    @property\n    def segmap(self):\n        arr = np.int32([\n            [0, 1, 1],\n            [0, 2, 1],\n            [0, 1, 3]\n        ])\n        return ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n\n    def test_default_pad_mode_and_cval(self):\n        segmap_padded = self.segmap.pad(top=1, right=2, bottom=3, left=4)\n        observed = segmap_padded.arr\n\n        expected = np.pad(\n            self.segmap.arr,\n            ((1, 3), (4, 2), (0, 0)),\n            mode=\"constant\",\n            constant_values=0)\n        assert np.array_equal(observed, expected)\n\n    def test_default_pad_mode(self):\n        segmap_padded = self.segmap.pad(top=1, right=2, bottom=3, left=4,\n                                        cval=1.0)\n        observed = segmap_padded.arr\n\n        expected = np.pad(\n            self.segmap.arr,\n            ((1, 3), (4, 2), (0, 0)),\n            mode=\"constant\",\n            constant_values=1.0)\n        assert np.array_equal(observed, expected)\n\n    def test_default_cval(self):\n        segmap_padded = self.segmap.pad(top=1, right=2, bottom=3, left=4,\n                                        mode=\"edge\")\n        observed = segmap_padded.arr\n\n        expected = np.pad(\n            self.segmap.arr,\n            ((1, 3), (4, 2), (0, 0)),\n            mode=\"edge\")\n        assert np.array_equal(observed, expected)\n\n\nclass TestSegmentationMapsOnImage_pad_to_aspect_ratio(unittest.TestCase):\n    @property\n    def segmap(self):\n        arr = np.int32([\n            [0, 1, 1],\n            [0, 2, 1]\n        ])\n        return ia.SegmentationMapsOnImage(arr, shape=(2, 3))\n\n    def test_square_ratio_with_default_pad_mode_and_cval(self):\n        segmap_padded = self.segmap.pad_to_aspect_ratio(1.0)\n        observed = segmap_padded.arr\n\n        expected = np.pad(\n            self.segmap.arr,\n            ((0, 1), (0, 0), (0, 0)),\n            mode=\"constant\",\n            constant_values=0)\n        assert np.array_equal(observed, expected)\n\n    def test_square_ratio_with_cval_set(self):\n        segmap_padded = self.segmap.pad_to_aspect_ratio(1.0, cval=1.0)\n        observed = segmap_padded.arr\n\n        expected = np.pad(\n            self.segmap.arr,\n            ((0, 1), (0, 0), (0, 0)),\n            mode=\"constant\",\n            constant_values=1.0)\n        assert np.array_equal(observed, expected)\n\n    def test_square_ratio_with_pad_mode_edge(self):\n        segmap_padded = self.segmap.pad_to_aspect_ratio(1.0, mode=\"edge\")\n        observed = segmap_padded.arr\n\n        expected = np.pad(\n            self.segmap.arr,\n            ((0, 1), (0, 0), (0, 0)),\n            mode=\"edge\")\n        assert np.array_equal(observed, expected)\n\n    def test_higher_than_wide_ratio_with_default_pad_mode_and_cval(self):\n        segmap_padded = self.segmap.pad_to_aspect_ratio(0.5)\n        observed = segmap_padded.arr\n\n        expected = np.pad(\n            self.segmap.arr,\n            ((2, 2), (0, 0), (0, 0)),\n            mode=\"constant\",\n            constant_values=0)\n        assert np.array_equal(observed, expected)\n\n    def test_return_pad_amounts(self):\n        segmap_padded, pad_amounts = self.segmap.pad_to_aspect_ratio(\n            0.5, return_pad_amounts=True)\n        observed = segmap_padded.arr\n\n        expected = np.pad(\n            self.segmap.arr,\n            ((2, 2), (0, 0), (0, 0)),\n            mode=\"constant\",\n            constant_values=0)\n        assert np.array_equal(observed, expected)\n        assert pad_amounts == (2, 0, 2, 0)\n\n\nclass TestSegmentationMapsOnImage_resize(unittest.TestCase):\n    @property\n    def segmap(self):\n        arr = np.int32([\n            [0, 1],\n            [0, 2]\n        ])\n        return ia.SegmentationMapsOnImage(arr, shape=(2, 2))\n\n    def test_resize_to_twice_the_size(self):\n        for sizes in [(4, 4), 2.0]:\n            with self.subTest(sizes=sizes):\n                # TODO also test other interpolation modes\n                segmap_scaled = self.segmap.resize(sizes)\n                observed = segmap_scaled.arr\n\n                expected = np.int32([\n                    [0, 0, 1, 1],\n                    [0, 0, 1, 1],\n                    [0, 0, 2, 2],\n                    [0, 0, 2, 2],\n                ]).reshape((4, 4, 1))\n                assert np.array_equal(observed, expected)\n\n\nclass TestSegmentationMapsOnImage_copy(unittest.TestCase):\n    @property\n    def segmap(self):\n        arr = np.int32([\n            [0, 1],\n            [2, 3]\n        ]).reshape((2, 2, 1))\n        return ia.SegmentationMapsOnImage(arr, shape=(2, 2))\n\n    def test_copy(self):\n        segmap = self.segmap\n\n        observed = segmap.copy()\n\n        assert np.array_equal(observed.arr, segmap.arr)\n        assert observed.shape == (2, 2)\n        assert observed._input_was == segmap._input_was\n\n        # ensure shallow copy\n        observed.arr[0, 0, 0] = 10\n        assert segmap.arr[0, 0, 0] == 10\n\n    def test_set_new_arr(self):\n        segmap = self.segmap\n\n        observed = segmap.copy(np.int32([[10]]).reshape((1, 1, 1)))\n\n        assert observed.arr.shape == (1, 1, 1)\n        assert observed.arr[0, 0, 0] == 10\n        assert observed._input_was == segmap._input_was\n\n    def test_set_new_shape(self):\n        segmap = self.segmap\n\n        observed = segmap.copy(shape=(10, 11, 3))\n\n        assert observed.shape == (10, 11, 3)\n        assert segmap.shape != (10, 11, 3)\n        assert observed._input_was == segmap._input_was\n\n\nclass TestSegmentationMapsOnImage_deepcopy(unittest.TestCase):\n    @property\n    def segmap(self):\n        arr = np.int32([\n            [0, 1],\n            [2, 3]\n        ]).reshape((2, 2, 1))\n        return ia.SegmentationMapsOnImage(arr, shape=(2, 2))\n\n    def test_deepcopy(self):\n        segmap = self.segmap\n\n        observed = segmap.deepcopy()\n\n        assert np.array_equal(observed.arr, segmap.arr)\n        assert observed.shape == (2, 2)\n        assert observed._input_was == segmap._input_was\n\n        observed.arr[0, 0, 0] = 10\n        assert segmap.arr[0, 0, 0] != 10\n\n    def test_set_new_arr(self):\n        segmap = self.segmap\n\n        observed = segmap.deepcopy(np.int32([[10]]).reshape((1, 1, 1)))\n\n        assert observed.arr.shape == (1, 1, 1)\n        assert observed.arr[0, 0, 0] == 10\n        assert segmap.arr[0, 0, 0] != 10\n        assert observed._input_was == segmap._input_was\n\n    def test_set_new_shape(self):\n        segmap = self.segmap\n\n        observed = segmap.deepcopy(shape=(10, 11, 3))\n\n        assert observed.shape == (10, 11, 3)\n        assert segmap.shape != (10, 11, 3)\n        assert observed._input_was == segmap._input_was\n"
  },
  {
    "path": "test/augmentables/test_utils.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\n\nfrom imgaug.augmentables.utils import (\n    interpolate_points,\n    interpolate_point_pair,\n    interpolate_points_by_max_distance,\n    normalize_shape,\n    normalize_imglike_shape\n)\n\n\nclass Test_interpolate_point_pair(unittest.TestCase):\n    def test_1_step(self):\n        point_a = (0, 0)\n        point_b = (1, 2)\n        inter = interpolate_point_pair(point_a, point_b, 1)\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0.5, 1.0]\n            ])\n        )\n\n    def test_2_steps(self):\n        point_a = (0, 0)\n        point_b = (1, 2)\n        inter = interpolate_point_pair(point_a, point_b, 2)\n        assert np.allclose(\n            inter,\n            np.float32([\n                [1*1/3, 1*2/3],\n                [2*1/3, 2*2/3]\n            ])\n        )\n\n    def test_0_steps(self):\n        point_a = (0, 0)\n        point_b = (1, 2)\n        inter = interpolate_point_pair(point_a, point_b, 0)\n        assert len(inter) == 0\n\n\nclass Test_interpolate_points(unittest.TestCase):\n    def test_2_points_0_steps(self):\n        points = [\n            (0, 0),\n            (1, 2)\n        ]\n\n        inter = interpolate_points(points, 0)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0],\n                [1, 2]\n            ])\n        )\n\n    def test_2_points_1_step(self):\n        points = [\n            (0, 0),\n            (1, 2)\n        ]\n\n        inter = interpolate_points(points, 1)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0],\n                [0.5, 1.0],\n                [1, 2],\n                [0.5, 1.0]\n            ])\n        )\n\n    def test_2_points_1_step_not_closed(self):\n        points = [\n            (0, 0),\n            (1, 2)\n        ]\n\n        inter = interpolate_points(points, 1, closed=False)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0],\n                [0.5, 1.0],\n                [1, 2]\n            ])\n        )\n\n    def test_3_points_0_steps(self):\n        points = [\n            (0, 0),\n            (1, 2),\n            (0.5, 3)\n        ]\n\n        inter = interpolate_points(points, 0)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0],\n                [1, 2],\n                [0.5, 3]\n            ])\n        )\n\n    def test_3_points_1_step(self):\n        points = [\n            (0, 0),\n            (1, 2),\n            (0.5, 3)\n        ]\n\n        inter = interpolate_points(points, 1)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0],\n                [0.5, 1.0],\n                [1, 2],\n                [0.75, 2.5],\n                [0.5, 3],\n                [0.25, 1.5]\n            ])\n        )\n\n    def test_3_points_1_step_not_closed(self):\n        points = [\n            (0, 0),\n            (1, 2),\n            (0.5, 3)\n        ]\n\n        inter = interpolate_points(points, 1, closed=False)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0],\n                [0.5, 1.0],\n                [1, 2],\n                [0.75, 2.5],\n                [0.5, 3]\n            ])\n        )\n\n    def test_0_points_1_step(self):\n        points = []\n\n        inter = interpolate_points(points, 1)\n\n        assert len(inter) == 0\n\n    def test_1_point_0_steps(self):\n        points = [(0, 0)]\n\n        inter = interpolate_points(points, 0)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0]\n            ])\n        )\n\n    def test_1_point_1_step(self):\n        points = [(0, 0)]\n\n        inter = interpolate_points(points, 1)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0]\n            ])\n        )\n\n\nclass Test_interpolate_points_by_max_distance(unittest.TestCase):\n    def test_2_points_dist_10000(self):\n        points = [\n            (0, 0),\n            (0, 2)\n        ]\n\n        inter = interpolate_points_by_max_distance(points, 10000)\n\n        assert np.allclose(\n            inter,\n            points\n        )\n\n    def test_2_points_dist_1(self):\n        points = [\n            (0, 0),\n            (0, 2)\n        ]\n\n        inter = interpolate_points_by_max_distance(points, 1.0)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0],\n                [0, 1.0],\n                [0, 2],\n                [0, 1.0]\n            ])\n        )\n\n    def test_2_points_dist_1_not_closed(self):\n        points = [\n            (0, 0),\n            (0, 2)\n        ]\n\n        inter = interpolate_points_by_max_distance(points, 1.0, closed=False)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0],\n                [0, 1.0],\n                [0, 2]\n            ])\n        )\n\n    def test_3_points_dist_1(self):\n        points = [\n            (0, 0),\n            (0, 2),\n            (2, 0)\n        ]\n\n        inter = interpolate_points_by_max_distance(points, 1.0)\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0],\n                [0, 1.0],\n                [0, 2],\n                [1.0, 1.0],\n                [2, 0],\n                [1.0, 0]\n            ])\n        )\n\n    def test_3_points_dist_1_not_closed(self):\n        points = [\n            (0, 0),\n            (0, 2),\n            (2, 0)\n        ]\n\n        inter = interpolate_points_by_max_distance(points, 1.0, closed=False)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0],\n                [0, 1.0],\n                [0, 2],\n                [1.0, 1.0],\n                [2, 0]\n            ])\n        )\n\n    def test_0_points_dist_1(self):\n        points = []\n\n        inter = interpolate_points_by_max_distance(points, 1.0)\n\n        assert len(inter) == 0\n\n    def test_1_point_dist_1(self):\n        points = [(0, 0)]\n\n        inter = interpolate_points_by_max_distance(points, 1.0)\n\n        assert np.allclose(\n            inter,\n            np.float32([\n                [0, 0]\n            ])\n        )\n\n\nclass Test_normalize_shape(unittest.TestCase):\n    def test_shape_tuple(self):\n        shape_out = normalize_shape((1, 2))\n        assert shape_out == (1, 2)\n\n    def test_shape_tuple_3d(self):\n        shape_out = normalize_shape((1, 2, 3))\n        assert shape_out == (1, 2, 3)\n\n    def test_array_1d(self):\n        arr = np.zeros((5,), dtype=np.uint8)\n        shape_out = normalize_shape(arr)\n        assert shape_out == (5,)\n\n    def test_array_2d(self):\n        arr = np.zeros((1, 2), dtype=np.uint8)\n        shape_out = normalize_shape(arr)\n        assert shape_out == (1, 2)\n\n    def test_array_3d(self):\n        arr = np.zeros((1, 2, 3), dtype=np.uint8)\n        shape_out = normalize_shape(arr)\n        assert shape_out == (1, 2, 3)\n\n\nclass Test_normalize_imglike_shape(unittest.TestCase):\n    def test_shape_tuple(self):\n        shape_out = normalize_imglike_shape((1, 2))\n        assert shape_out == (1, 2)\n\n    def test_shape_tuple_3d(self):\n        shape_out = normalize_imglike_shape((1, 2, 3))\n        assert shape_out == (1, 2, 3)\n\n    def test_array_1d_fails(self):\n        arr = np.zeros((5,), dtype=np.uint8)\n        with self.assertRaises(AssertionError):\n            _ = normalize_imglike_shape(arr)\n\n    def test_array_2d(self):\n        arr = np.zeros((1, 2), dtype=np.uint8)\n        shape_out = normalize_imglike_shape(arr)\n        assert shape_out == (1, 2)\n\n    def test_array_3d(self):\n        arr = np.zeros((1, 2, 3), dtype=np.uint8)\n        shape_out = normalize_imglike_shape(arr)\n        assert shape_out == (1, 2, 3)\n\n    def test_array_4d_fails(self):\n        arr = np.zeros((1, 2, 3, 4), dtype=np.uint8)\n        with self.assertRaises(AssertionError):\n            _ = normalize_imglike_shape(arr)\n"
  },
  {
    "path": "test/augmenters/test_arithmetic.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport functools\nimport sys\nimport warnings\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport cv2\nimport six.moves as sm\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug import dtypes as iadt\nfrom imgaug import random as iarandom\nfrom imgaug.testutils import (\n    array_equal_lists,\n    keypoints_equal,\n    reseed,\n    runtest_pickleable_uint8_img,\n    assertWarns,\n    is_parameter_instance\n)\nimport imgaug.augmenters.arithmetic as arithmetic_lib\nimport imgaug.augmenters.contrast as contrast_lib\nfrom imgaug.augmenters.arithmetic import (\n    _add_elementwise_cv2_to_uint8,\n    _multiply_scalar_to_uint8_cv2_mul_,\n    _multiply_elementwise_to_uint8_,\n    _invert_uint8_subtract_\n)\n\n\nclass Test__add_elementwise_cv2_to_uint8(unittest.TestCase):\n    def test_image_is_hw(self):\n        image_shape = (3, 4)\n        values_shape = (3, 4)\n\n        image = np.ones(image_shape, dtype=np.uint8)\n        values = np.ones(values_shape, dtype=np.float32)\n\n        result = _add_elementwise_cv2_to_uint8(image, values)\n\n        assert np.array_equal(result, image + 1)\n        assert result.shape == image_shape\n        assert result.dtype.name == \"uint8\"\n        assert result is not image\n\n    def test_image_is_hwn(self):\n        for nb_channels in [1, 2, 3, 4, 5, 10]:\n            for values_nb_channels in [None, 1, nb_channels]:\n                image_shape = (3, 4, nb_channels)\n                values_shape = (3, 4)\n                if values_nb_channels is not None:\n                    values_shape = values_shape + (values_nb_channels,)\n\n                with self.subTest(image_shape=image_shape,\n                                  values_shape=values_shape):\n                    image = np.ones(image_shape, dtype=np.uint8)\n                    values = np.ones(values_shape, dtype=np.float32)\n\n                    result = _add_elementwise_cv2_to_uint8(image, values)\n\n                    assert np.array_equal(result, image + 1)\n                    assert result.shape == image_shape\n                    assert result.dtype.name == \"uint8\"\n                    assert result is not image\n\n    def test_image_is_view(self):\n        for shape in [(4, 3), (4, 3, 3)]:\n            with self.subTest(shape=shape):\n                image = np.ones(shape, dtype=np.uint8)\n                values = np.ones((shape[0]-1, shape[1]), dtype=np.float32)\n\n                image = image[0:3, :]\n                assert image.flags[\"OWNDATA\"] is False\n                assert image.flags[\"C_CONTIGUOUS\"] is True\n\n                result = _add_elementwise_cv2_to_uint8(image, values)\n\n                assert np.array_equal(result, image + 1)\n                assert result.shape == (shape[0]-1, shape[1]) + shape[2:]\n                assert result.dtype.name == \"uint8\"\n                assert result is not image\n\n    def test_image_is_non_contiguous(self):\n        for shape in [(3, 4), (3, 4, 3)]:\n            with self.subTest(shape=shape):\n                image = np.ones(shape, dtype=np.uint8, order=\"F\")\n                values = np.ones(shape, dtype=np.float32)\n\n                assert image.flags[\"OWNDATA\"] is True\n                assert image.flags[\"C_CONTIGUOUS\"] is False\n\n                result = _add_elementwise_cv2_to_uint8(image, values)\n\n                assert np.array_equal(result, image + 1)\n                assert result.shape == shape\n                assert result.dtype.name == \"uint8\"\n                assert result is not image\n\n    def test_floats_with_decimal_points(self):\n        image_shape = (3, 4, 3)\n        values_shape = (3, 4)\n\n        image = np.ones(image_shape, dtype=np.uint8)\n        values = np.full(values_shape, 1.7, dtype=np.float32)\n\n        result = _add_elementwise_cv2_to_uint8(image, values)\n        # cv2.add() performs rounding\n        assert np.array_equal(result, image + 2)\n        assert result.shape == image_shape\n        assert result.dtype.name == \"uint8\"\n        assert result is not image\n\n    def test_is_saturating(self):\n        image_shape = (3, 4, 3)\n        values_shape = (3, 4)\n\n        for value in [-1000.5, 1000.5]:\n            with self.subTest(value=value):\n                image = np.ones(image_shape, dtype=np.uint8)\n                values = np.full(values_shape, value, dtype=np.float32)\n\n                result = _add_elementwise_cv2_to_uint8(image, values)\n                if value < 0:\n                    assert np.all(result == 0)\n                else:\n                    assert np.all(result == 255)\n                assert result.shape == image_shape\n                assert result.dtype.name == \"uint8\"\n                assert result is not image\n\n    def test_values_is_int_uint(self):\n        image_shape = (3, 4, 3)\n        values_shape = (3, 4)\n\n        dtypes = [\"int8\", \"int16\", \"int32\", \"uint8\", \"uint16\"]\n        values = [\"min\", -10, -1, 0, 1, 10, \"max\"]\n\n        for dt in dtypes:\n            for value in values:\n                vmin, _, vmax = iadt.get_value_range_of_dtype(dt)\n                if value == \"min\":\n                    value = max(vmin, -1000)\n                elif value == \"max\":\n                    value = min(1000, vmax)\n                elif value < 0:\n                    value = 0 if dt.startswith(\"uint\") else value\n\n                with self.subTest(dtype=dt, value=value):\n                    image = np.full(image_shape, 127, dtype=np.uint8)\n                    values_arr = np.full(values_shape, value, dtype=dt)\n\n                    result = _add_elementwise_cv2_to_uint8(image, values_arr)\n\n                    expected_value = min(max(127 + value, 0), 255)\n                    assert np.all(result == expected_value)\n                    assert result.shape == image_shape\n                    assert result.dtype.name == \"uint8\"\n                    assert result is not image\n\n    def test_values_is_float(self):\n        image_shape = (3, 4, 3)\n        values_shape = (3, 4)\n\n        dtypes = [\"float32\", \"float64\"]\n        values = [\n            [-1000.0, -255.0, -1.0, 0.0, 1.0, 255.0, 1000.0],\n            [-1000.0, -255.0, -1.0, 0.0, 1.0, 255.0, 1000.0]\n        ]\n\n        for dt, values_dt in zip(dtypes, values):\n            for value in values_dt:\n                with self.subTest(dtype=dt, value=value):\n                    image = np.full(image_shape, 127, dtype=np.uint8)\n                    values = np.full(values_shape, value, dtype=dt)\n\n                    result = _add_elementwise_cv2_to_uint8(image, values)\n\n                    if value < -1.01:\n                        expected_value = 0\n                    elif np.isclose(value, -1.0):\n                        expected_value = 126\n                    elif np.isclose(value, 0.0):\n                        expected_value = 127\n                    elif np.isclose(value, 1.0):\n                        expected_value = 128\n                    else:\n                        expected_value = 255\n\n                    assert np.all(result == expected_value)\n                    assert result.shape == image_shape\n                    assert result.dtype.name == \"uint8\"\n                    assert result is not image\n\n\nclass Test__multiply_scalar_to_uint8_cv2_mul_(unittest.TestCase):\n    def test_single_multiplier_image_hw(self):\n        image = np.full((3, 4), 10, dtype=np.uint8)\n        image_cp = np.copy(image)\n        multiplier = np.float32(2.67)\n\n        observed = _multiply_scalar_to_uint8_cv2_mul_(image_cp, multiplier)\n\n        expected = np.full((3, 4), 27, dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n        assert observed.shape == image.shape\n        assert observed.dtype.name == \"uint8\"\n        assert observed is image_cp\n\n    def test_single_multiplier_image_hwc(self):\n        for nb_channels in [1, 2, 3, 4, 5, 10, 512, 513]:\n            with self.subTest(nb_channels=nb_channels):\n                image = np.full((3, 4, nb_channels), 10, dtype=np.uint8)\n                image_cp = np.copy(image)\n                multiplier = np.float32(2.6)\n\n                observed = _multiply_scalar_to_uint8_cv2_mul_(image_cp,\n                                                              multiplier)\n\n                expected = np.full((3, 4, nb_channels), 26, dtype=np.uint8)\n                assert np.array_equal(observed, expected)\n                assert observed.shape == image.shape\n                assert observed.dtype.name == \"uint8\"\n                assert observed is image_cp\n\n    def test_single_multiplier_saturating(self):\n        for value in [-0.1, 0, 30, 100.5]:\n            with self.subTest(value=value):\n                image = np.full((3, 4), 10, dtype=np.uint8)\n                image_cp = np.copy(image)\n                multiplier = np.float32(value)\n\n                observed = _multiply_scalar_to_uint8_cv2_mul_(image_cp,\n                                                              multiplier)\n\n                if value <= 0+1e-4:\n                    expected = np.zeros_like(image)\n                else:\n                    expected = np.full(image.shape, 255, dtype=np.uint8)\n                assert np.array_equal(observed, expected)\n                assert observed.shape == image.shape\n                assert observed.dtype.name == \"uint8\"\n                assert observed is image_cp\n\n    def test_channelwise_multiplier_image_hw(self):\n        image = np.full((3, 4), 10, dtype=np.uint8)\n        image_cp = np.copy(image)\n        multiplier = np.array([2.6], dtype=np.float32)\n\n        observed = _multiply_scalar_to_uint8_cv2_mul_(image_cp, multiplier)\n\n        expected = np.full((3, 4), 26, dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n        assert observed.shape == image.shape\n        assert observed.dtype.name == \"uint8\"\n        assert observed is image_cp\n\n    def test_channelwise_multiplier_image_hwc(self):\n        for nb_channels in [1, 2, 3, 4, 5, 10, 512, 513]:\n            with self.subTest(nb_channels=nb_channels):\n                image = np.full((3, 4, nb_channels), 10, dtype=np.uint8)\n                image_cp = np.copy(image)\n                multiplier = np.ones((nb_channels,), dtype=np.float32)\n                multiplier[0] = 2.6\n                if nb_channels >= 2:\n                    multiplier[1] = 4.0\n                if nb_channels >= 3:\n                    multiplier[2] = 5.6\n                if nb_channels >= 4:\n                    multiplier[nb_channels-1] = 7.1\n\n                observed = _multiply_scalar_to_uint8_cv2_mul_(image_cp,\n                                                              multiplier)\n\n                expected = image\n                expected[:, :, 0] = 26\n                if nb_channels >= 2:\n                    expected[:, :, 1] = 40\n                if nb_channels >= 3:\n                    expected[:, :, 2] = 56\n                if nb_channels >= 4:\n                    expected[:, :, nb_channels-1] = 71\n                assert np.array_equal(observed, expected)\n                assert observed.shape == image.shape\n                assert observed.dtype.name == \"uint8\"\n                assert observed is image_cp\n\n    def test_image_is_view(self):\n        image = np.full((4, 3), 10, dtype=np.uint8)\n        image_cp = np.copy(image)[0:3, :]\n        multiplier = np.float32(2.6)\n\n        observed = _multiply_scalar_to_uint8_cv2_mul_(image_cp, multiplier)\n\n        expected = np.full((3, 3), 26, dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (3, 3)\n        assert observed.dtype.name == \"uint8\"\n\n    def test_image_is_non_contiguous(self):\n        image = np.full((3, 4), 10, dtype=np.uint8)\n        image_cp = np.full((3, 4), 10, dtype=np.uint8, order=\"F\")\n        multiplier = np.float32(2.6)\n\n        observed = _multiply_scalar_to_uint8_cv2_mul_(image_cp, multiplier)\n\n        expected = np.full((3, 4), 26, dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n        assert observed.shape == image.shape\n        assert observed.dtype.name == \"uint8\"\n\n\nclass Test_multiply_elementwise_to_non_uint8(unittest.TestCase):\n    def test_image_is_hw(self):\n        image = np.full((4, 3), 10, dtype=np.uint8)\n        image_cp = np.copy(image)\n        multipliers = np.full((4, 3), 2.7, dtype=np.float32)\n\n        observed = _multiply_elementwise_to_uint8_(image_cp, multipliers)\n\n        expected = np.full((4, 3), 27, dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (4, 3)\n        assert observed.dtype.name == \"uint8\"\n        assert observed is image_cp\n\n    def test_image_is_hwn(self):\n        for nb_channels in [1, 2, 3, 4, 5, 10, 512, 513]:\n            with self.subTest(nb_channels=nb_channels):\n                image = np.full((4, 3, nb_channels), 10, dtype=np.uint8)\n                image_cp = np.copy(image)\n                multipliers = np.full((4, 3, nb_channels), 1, dtype=np.float32)\n                multipliers[:, :, 0] = 2.7\n                if nb_channels >= 2:\n                    multipliers[:, :, 1] = 4.0\n                if nb_channels >= 3:\n                    multipliers[:, :, 2] = 6.4\n                if nb_channels >= 4:\n                    multipliers[:, :, -1] = 8.3\n\n                observed = _multiply_elementwise_to_uint8_(image_cp,\n                                                           multipliers)\n\n                expected = np.full((4, 3, nb_channels), 10, dtype=np.uint8)\n                expected[:, :, 0] = 27\n                if nb_channels >= 2:\n                    expected[:, :, 1] = 40\n                if nb_channels >= 3:\n                    expected[:, :, 2] = 64\n                if nb_channels >= 4:\n                    expected[:, :, -1] = 83\n                assert np.array_equal(observed, expected)\n                assert observed.shape == (4, 3, nb_channels)\n                assert observed.dtype.name == \"uint8\"\n                assert observed is image_cp\n\n    def test_multipliers_hw(self):\n        nb_channels = 3\n        image = np.full((4, 3, nb_channels), 10, dtype=np.uint8)\n        image_cp = np.copy(image)\n        multipliers = np.full((4, 3), 2.7, dtype=np.float32)\n\n        observed = _multiply_elementwise_to_uint8_(image_cp,\n                                                   multipliers)\n\n        expected = np.full((4, 3, nb_channels), 27, dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (4, 3, nb_channels)\n        assert observed.dtype.name == \"uint8\"\n        assert observed is image_cp\n\n    def test_multipliers_hw1(self):\n        nb_channels = 3\n        image = np.full((4, 3, nb_channels), 10, dtype=np.uint8)\n        image_cp = np.copy(image)\n        multipliers = np.full((4, 3, 1), 2.7, dtype=np.float32)\n\n        observed = _multiply_elementwise_to_uint8_(image_cp,\n                                                   multipliers)\n\n        expected = np.full((4, 3, nb_channels), 27, dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (4, 3, nb_channels)\n        assert observed.dtype.name == \"uint8\"\n        assert observed is image_cp\n\n    def test_multipliers_is_float(self):\n        dtypes = [\"float16\", \"float32\", \"float64\"]\n        for dt in dtypes:\n            image = np.full((4, 3, 3), 10, dtype=np.uint8)\n            image_cp = np.copy(image)\n            multipliers = np.full((4, 3, 3), 1, dtype=dt)\n            multipliers[:, :, 0] = 2.7\n            multipliers[:, :, 1] = 4.0\n            multipliers[:, :, 2] = 6.4\n\n            observed = _multiply_elementwise_to_uint8_(image_cp,\n                                                       multipliers)\n\n            expected = np.full((4, 3, 3), 10, dtype=np.uint8)\n            expected[:, :, 0] = 27\n            expected[:, :, 1] = 40\n            expected[:, :, 2] = 64\n            assert np.array_equal(observed, expected)\n            assert observed.shape == (4, 3, 3)\n            assert observed.dtype.name == \"uint8\"\n            assert observed is image_cp\n\n    def test_multipliers_is_uint_int(self):\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int16\", \"int32\", \"int64\"]\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                image = np.full((4, 3, 3), 10, dtype=np.uint8)\n                image_cp = np.copy(image)\n                multipliers = np.full((4, 3, 3), 1, dtype=dt)\n                multipliers[:, :, 0] = 2\n                multipliers[:, :, 1] = 4\n                multipliers[:, :, 2] = 5\n\n                observed = _multiply_elementwise_to_uint8_(image_cp,\n                                                           multipliers)\n\n                expected = np.full((4, 3, 3), 10, dtype=np.uint8)\n                expected[:, :, 0] = 20\n                expected[:, :, 1] = 40\n                expected[:, :, 2] = 50\n                assert np.array_equal(observed, expected)\n                assert observed.shape == (4, 3, 3)\n                assert observed.dtype.name == \"uint8\"\n                assert observed is image_cp\n\n    def test_image_is_view(self):\n        nb_channels = 3\n        image = np.full((4, 3, nb_channels), 10, dtype=np.uint8)\n        image_cp = np.copy(image)[0:3, :, :]\n        assert image_cp.flags[\"OWNDATA\"] is False\n        assert image_cp.flags[\"C_CONTIGUOUS\"] is True\n        multipliers = np.full((3, 3, 1), 2.7, dtype=np.float32)\n\n        observed = _multiply_elementwise_to_uint8_(image_cp,\n                                                   multipliers)\n\n        expected = np.full((3, 3, nb_channels), 27, dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (3, 3, nb_channels)\n        assert observed.dtype.name == \"uint8\"\n\n    def test_image_is_noncontiguous(self):\n        nb_channels = 3\n        image = np.full((4, 3, nb_channels), 10, dtype=np.uint8, order=\"F\")\n        assert image.flags[\"OWNDATA\"] is True\n        assert image.flags[\"C_CONTIGUOUS\"] is False\n        multipliers = np.full((4, 3, 1), 2.7, dtype=np.float32)\n\n        observed = _multiply_elementwise_to_uint8_(image,\n                                                   multipliers)\n\n        expected = np.full((4, 3, nb_channels), 27, dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (4, 3, nb_channels)\n        assert observed.dtype.name == \"uint8\"\n\n\nclass Test_cutout(unittest.TestCase):\n    @mock.patch(\"imgaug.augmenters.arithmetic.cutout_\")\n    def test_mocked(self, mock_inplace):\n        image = np.mod(np.arange(100*100*3), 255).astype(np.uint8).reshape(\n            (100, 100, 3))\n        mock_inplace.return_value = \"foo\"\n\n        rng = iarandom.RNG(0)\n        image_aug = iaa.cutout(image,\n                               x1=10,\n                               y1=20,\n                               x2=30,\n                               y2=40,\n                               fill_mode=\"gaussian\",\n                               cval=1,\n                               fill_per_channel=0.5,\n                               seed=rng)\n\n        assert mock_inplace.call_count == 1\n        assert image_aug == \"foo\"\n\n        args = mock_inplace.call_args_list[0][0]\n        assert args[0] is not image\n        assert np.array_equal(args[0], image)\n        assert np.isclose(args[1], 10)\n        assert np.isclose(args[2], 20)\n        assert np.isclose(args[3], 30)\n        assert np.isclose(args[4], 40)\n        assert args[5] == \"gaussian\"\n        assert args[6] == 1\n        assert np.isclose(args[7], 0.5)\n        assert args[8] is rng\n\n\nclass Test_cutout_(unittest.TestCase):\n    def test_with_simple_image(self):\n        image = np.mod(np.arange(100*100*3), 255).astype(np.uint8).reshape(\n            (100, 100, 3))\n        image = 1 + image\n\n        image_aug = iaa.cutout_(image,\n                                x1=10,\n                                y1=20,\n                                x2=30,\n                                y2=40,\n                                fill_mode=\"constant\",\n                                cval=0,\n                                fill_per_channel=False,\n                                seed=None)\n\n        mask = np.zeros(image.shape, dtype=bool)\n        mask[20:40, 10:30, :] = True\n        overlap_inside = np.sum(image_aug[mask] == 0) / np.sum(mask)\n        overlap_outside = np.sum(image_aug[~mask] > 0) / np.sum(~mask)\n        assert image_aug is image\n        assert overlap_inside >= 1.0 - 1e-4\n        assert overlap_outside >= 1.0 - 1e-4\n\n    @mock.patch(\"imgaug.augmenters.arithmetic._fill_rectangle_constant_\")\n    def test_fill_mode_constant_mocked(self, mock_fill):\n        self._test_with_fill_mode_mocked(\"constant\", mock_fill)\n\n    @mock.patch(\"imgaug.augmenters.arithmetic._fill_rectangle_gaussian_\")\n    def test_fill_mode_gaussian_mocked(self, mock_fill):\n        self._test_with_fill_mode_mocked(\"gaussian\", mock_fill)\n\n    @classmethod\n    def _test_with_fill_mode_mocked(cls, fill_mode, mock_fill):\n        image = np.mod(np.arange(100*100*3), 256).astype(np.uint8).reshape(\n            (100, 100, 3))\n        mock_fill.return_value = image\n\n        seed = iarandom.RNG(0)\n\n        image_aug = iaa.cutout_(image,\n                                x1=10,\n                                y1=20,\n                                x2=30,\n                                y2=40,\n                                fill_mode=fill_mode,\n                                cval=0,\n                                fill_per_channel=False,\n                                seed=seed)\n\n        assert mock_fill.call_count == 1\n        args = mock_fill.call_args_list[0][0]\n        kwargs = mock_fill.call_args_list[0][1]\n        assert image_aug is image\n        assert args[0] is image\n        assert kwargs[\"x1\"] == 10\n        assert kwargs[\"y1\"] == 20\n        assert kwargs[\"x2\"] == 30\n        assert kwargs[\"y2\"] == 40\n        assert kwargs[\"cval\"] == 0\n        assert kwargs[\"per_channel\"] is False\n        assert kwargs[\"random_state\"] is seed\n\n    def test_zero_height(self):\n        image = np.mod(np.arange(100*100*3), 255).astype(np.uint8).reshape(\n            (100, 100, 3))\n        image = 1 + image\n        image_cp = np.copy(image)\n\n        image_aug = iaa.cutout_(image,\n                                x1=10,\n                                y1=20,\n                                x2=30,\n                                y2=20,\n                                fill_mode=\"constant\",\n                                cval=0,\n                                fill_per_channel=False,\n                                seed=None)\n\n        assert np.array_equal(image_aug, image_cp)\n\n    def test_zero_height_width(self):\n        image = np.mod(np.arange(100*100*3), 255).astype(np.uint8).reshape(\n            (100, 100, 3))\n        image = 1 + image\n        image_cp = np.copy(image)\n\n        image_aug = iaa.cutout_(image,\n                                x1=10,\n                                y1=20,\n                                x2=10,\n                                y2=40,\n                                fill_mode=\"constant\",\n                                cval=0,\n                                fill_per_channel=False,\n                                seed=None)\n\n        assert np.array_equal(image_aug, image_cp)\n\n    def test_position_outside_of_image_rect_fully_outside(self):\n        image = np.mod(np.arange(100*100*3), 255).astype(np.uint8).reshape(\n            (100, 100, 3))\n        image = 1 + image\n        image_cp = np.copy(image)\n\n        image_aug = iaa.cutout_(image,\n                                x1=-50,\n                                y1=150,\n                                x2=-1,\n                                y2=200,\n                                fill_mode=\"constant\",\n                                cval=0,\n                                fill_per_channel=False,\n                                seed=None)\n\n        assert np.array_equal(image_aug, image_cp)\n\n    def test_position_outside_of_image_rect_partially_inside(self):\n        image = np.mod(np.arange(100*100*3), 255).astype(np.uint8).reshape(\n            (100, 100, 3))\n        image = 1 + image\n\n        image_aug = iaa.cutout_(image,\n                                x1=-25,\n                                y1=-25,\n                                x2=25,\n                                y2=25,\n                                fill_mode=\"constant\",\n                                cval=0,\n                                fill_per_channel=False,\n                                seed=None)\n\n        assert np.all(image_aug[0:25, 0:25] == 0)\n        assert np.all(image_aug[0:25, 25:] > 0)\n        assert np.all(image_aug[25:, :] > 0)\n\n    def test_zero_sized_axes(self):\n        shapes = [(0, 0, 0),\n                  (1, 0, 0),\n                  (0, 1, 0),\n                  (0, 1, 1),\n                  (1, 1, 0),\n                  (1, 0, 1),\n                  (1, 0),\n                  (0, 1),\n                  (0, 0)]\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                image_cp = np.copy(image)\n\n                image_aug = iaa.cutout_(image,\n                                        x1=-5,\n                                        y1=-5,\n                                        x2=5,\n                                        y2=5,\n                                        fill_mode=\"constant\",\n                                        cval=0)\n\n                assert np.array_equal(image_aug, image_cp)\n\n\nclass Test_fill_rectangle_gaussian_(unittest.TestCase):\n    def test_simple_image(self):\n        image = np.mod(np.arange(100*100*3), 256).astype(np.uint8).reshape(\n            (100, 100, 3))\n        image_cp = np.copy(image)\n        rng = iarandom.RNG(0)\n\n        image_aug = arithmetic_lib._fill_rectangle_gaussian_(\n            image,\n            x1=10,\n            y1=20,\n            x2=60,\n            y2=70,\n            cval=0,\n            per_channel=False,\n            random_state=rng)\n\n        assert np.array_equal(image_aug[:20, :],\n                              image_cp[:20, :])\n        assert not np.array_equal(image_aug[20:70, 10:60],\n                                  image_cp[20:70, 10:60])\n        assert np.isclose(np.average(image_aug[20:70, 10:60]), 127.5,\n                          rtol=0, atol=5.0)\n        assert np.isclose(np.std(image_aug[20:70, 10:60]), 255.0/2.0/3.0,\n                          rtol=0, atol=2.5)\n\n    def test_per_channel(self):\n        image = np.uint8([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])\n        image = np.tile(image.reshape((1, 10, 1)), (1, 1, 3))\n\n        image_aug = arithmetic_lib._fill_rectangle_gaussian_(\n            np.copy(image),\n            x1=0, y1=0, x2=10, y2=1,\n            cval=0,\n            per_channel=False,\n            random_state=iarandom.RNG(0))\n\n        image_aug_pc = arithmetic_lib._fill_rectangle_gaussian_(\n            np.copy(image),\n            x1=0, y1=0, x2=10, y2=1,\n            cval=0,\n            per_channel=True,\n            random_state=iarandom.RNG(0))\n\n        diff11 = (image_aug[..., 0] != image_aug[..., 1])\n        diff12 = (image_aug[..., 0] != image_aug[..., 2])\n        diff21 = (image_aug_pc[..., 0] != image_aug_pc[..., 1])\n        diff22 = (image_aug_pc[..., 0] != image_aug_pc[..., 2])\n\n        assert not np.any(diff11)\n        assert not np.any(diff12)\n        assert np.any(diff21)\n        assert np.any(diff22)\n\n    def test_deterministic_with_same_seed(self):\n        image = np.uint8([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])\n        image = np.tile(image.reshape((1, 10, 1)), (1, 1, 3))\n\n        image_aug_pc1 = arithmetic_lib._fill_rectangle_gaussian_(\n            np.copy(image),\n            x1=0, y1=0, x2=10, y2=1,\n            cval=0,\n            per_channel=True,\n            random_state=iarandom.RNG(0))\n\n        image_aug_pc2 = arithmetic_lib._fill_rectangle_gaussian_(\n            np.copy(image),\n            x1=0, y1=0, x2=10, y2=1,\n            cval=0,\n            per_channel=True,\n            random_state=iarandom.RNG(0))\n\n        image_aug_pc3 = arithmetic_lib._fill_rectangle_gaussian_(\n            np.copy(image),\n            x1=0, y1=0, x2=10, y2=1,\n            cval=0,\n            per_channel=True,\n            random_state=iarandom.RNG(1))\n\n        assert np.array_equal(image_aug_pc1, image_aug_pc2)\n        assert not np.array_equal(image_aug_pc2, image_aug_pc3)\n\n    def test_no_channels(self):\n        for per_channel in [False, True]:\n            with self.subTest(per_channel=per_channel):\n                image = np.uint8([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])\n                image = image.reshape((1, 10))\n\n                image_aug = arithmetic_lib._fill_rectangle_gaussian_(\n                    np.copy(image),\n                    x1=0, y1=0, x2=10, y2=1,\n                    cval=0,\n                    per_channel=per_channel,\n                    random_state=iarandom.RNG(0))\n\n                assert not np.array_equal(image_aug, image)\n\n    def test_unusual_channel_numbers(self):\n        for nb_channels in [1, 2, 3, 4, 5, 511, 512, 513]:\n            for per_channel in [False, True]:\n                with self.subTest(nb_channels=nb_channels,\n                                  per_channel=per_channel):\n                    image = np.uint8([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])\n                    image = np.tile(image.reshape((1, 10, 1)),\n                                    (1, 1, nb_channels))\n\n                    image_aug = arithmetic_lib._fill_rectangle_gaussian_(\n                        np.copy(image),\n                        x1=0, y1=0, x2=10, y2=1,\n                        cval=0,\n                        per_channel=True,\n                        random_state=iarandom.RNG(0))\n\n                    assert not np.array_equal(image_aug, image)\n\n    def test_other_dtypes_bool(self):\n        for per_channel in [False, True]:\n            with self.subTest(per_channel=per_channel):\n                image = np.array([0, 1], dtype=bool)\n                image = np.tile(image, (int(3*300*300/2),))\n                image = image.reshape((300, 300, 3))\n                image_cp = np.copy(image)\n                rng = iarandom.RNG(0)\n\n                image_aug = arithmetic_lib._fill_rectangle_gaussian_(\n                    image,\n                    x1=10,\n                    y1=10,\n                    x2=300-10,\n                    y2=300-10,\n                    cval=0,\n                    per_channel=per_channel,\n                    random_state=rng)\n\n                rect = image_aug[10:-10, 10:-10]\n                p_true = np.sum(rect) / rect.size\n                assert np.array_equal(image_aug[:10, :], image_cp[:10, :])\n                assert not np.array_equal(rect, image_cp[10:-10, 10:-10])\n                assert np.isclose(p_true, 0.5, rtol=0, atol=0.1)\n                if per_channel:\n                    for c in np.arange(1, image.shape[2]):\n                        assert not np.array_equal(image_aug[..., 0],\n                                                  image_aug[..., c])\n\n    def test_other_dtypes_int_uint(self):\n        try:\n            high_res_dt = np.float128\n        except AttributeError:\n            high_res_dt = np.float64\n\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int16\", \"int32\", \"int64\"]\n        for dtype in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n            dynamic_range = int(max_value) - int(min_value)\n\n            gaussian_min = iarandom.RNG(0).normal(min_value, 0.0001,\n                                                  size=(1,))\n            gaussian_max = iarandom.RNG(0).normal(max_value, 0.0001,\n                                                  size=(1,))\n            assert min_value - 1.0 <= gaussian_min <= min_value + 1.0\n            assert max_value - 1.0 <= gaussian_max <= max_value + 1.0\n\n            for per_channel in [False, True]:\n                with self.subTest(dtype=dtype, per_channel=per_channel):\n                    # dont generate image from choice() here, that seems\n                    # to not support uint64 (max value not in result)\n                    image = np.array([min_value, min_value+1,\n                                      int(center_value),\n                                      max_value-1, max_value], dtype=dtype)\n                    image = np.tile(image, (int(3*300*300/5),))\n                    image = image.reshape((300, 300, 3))\n                    assert min_value in image\n                    assert max_value in image\n                    image_cp = np.copy(image)\n                    rng = iarandom.RNG(0)\n\n                    image_aug = arithmetic_lib._fill_rectangle_gaussian_(\n                        image, x1=10, y1=10, x2=300-10, y2=300-10,\n                        cval=0, per_channel=per_channel, random_state=rng)\n\n                    rect = image_aug[10:-10, 10:-10]\n                    mean = np.average(high_res_dt(rect))\n                    std = np.std(high_res_dt(rect) - center_value)\n                    assert np.array_equal(image_aug[:10, :], image_cp[:10, :])\n                    assert not np.array_equal(rect,\n                                              image_cp[10:-10, 10:-10])\n                    assert np.isclose(mean, center_value, rtol=0,\n                                      atol=0.05*dynamic_range)\n                    assert np.isclose(std, dynamic_range/2.0/3.0, rtol=0,\n                                      atol=0.05*dynamic_range/2.0/3.0)\n                    assert np.min(rect) < min_value + 0.2 * dynamic_range\n                    assert np.max(rect) > max_value - 0.2 * dynamic_range\n\n                    if per_channel:\n                        for c in np.arange(1, image.shape[2]):\n                            assert not np.array_equal(image_aug[..., 0],\n                                                      image_aug[..., c])\n\n    def test_other_dtypes_float(self):\n        try:\n            high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n        except AttributeError:\n            high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n\n        for dtype in dtypes:\n            min_value = 0.0\n            center_value = 0.5\n            max_value = 1.0\n            dynamic_range = high_res_dt(max_value) - high_res_dt(min_value)\n\n            gaussian_min = iarandom.RNG(0).normal(min_value, 0.0001,\n                                                  size=(1,))\n            gaussian_max = iarandom.RNG(0).normal(max_value, 0.0001,\n                                                  size=(1,))\n            assert min_value - 1.0 <= gaussian_min <= min_value + 1.0\n            assert max_value - 1.0 <= gaussian_max <= max_value + 1.0\n\n            for per_channel in [False, True]:\n                with self.subTest(dtype=dtype, per_channel=per_channel):\n                    # dont generate image from choice() here, that seems\n                    # to not support uint64 (max value not in result)\n                    image = np.array([min_value, min_value+1,\n                                      int(center_value),\n                                      max_value-1, max_value], dtype=dtype)\n                    image = np.tile(image, (int(3*300*300/5),))\n                    image = image.reshape((300, 300, 3))\n                    assert np.any(np.isclose(image, min_value,\n                                             rtol=0, atol=1e-4))\n                    assert np.any(np.isclose(image, max_value,\n                                             rtol=0, atol=1e-4))\n                    image_cp = np.copy(image)\n                    rng = iarandom.RNG(0)\n\n                    image_aug = arithmetic_lib._fill_rectangle_gaussian_(\n                        image, x1=10, y1=10, x2=300-10, y2=300-10,\n                        cval=0, per_channel=per_channel, random_state=rng)\n\n                    rect = image_aug[10:-10, 10:-10]\n                    mean = np.average(high_res_dt(rect))\n                    std = np.std(high_res_dt(rect) - center_value)\n                    assert np.allclose(image_aug[:10, :], image_cp[:10, :],\n                                       rtol=0, atol=1e-4)\n                    assert not np.allclose(rect, image_cp[10:-10, 10:-10],\n                                           rtol=0, atol=1e-4)\n                    assert np.isclose(mean, center_value, rtol=0,\n                                      atol=0.05*dynamic_range)\n                    assert np.isclose(std, dynamic_range/2.0/3.0, rtol=0,\n                                      atol=0.05*dynamic_range/2.0/3.0)\n                    assert np.min(rect) < min_value + 0.2 * dynamic_range\n                    assert np.max(rect) > max_value - 0.2 * dynamic_range\n\n                    if per_channel:\n                        for c in np.arange(1, image.shape[2]):\n                            assert not np.allclose(image_aug[..., 0],\n                                                   image_aug[..., c],\n                                                   rtol=0, atol=1e-4)\n\n\nclass Test_fill_rectangle_constant_(unittest.TestCase):\n    def test_simple_image(self):\n        image = np.mod(np.arange(100*100*3), 256).astype(np.uint8).reshape(\n            (100, 100, 3))\n        image_cp = np.copy(image)\n\n        image_aug = arithmetic_lib._fill_rectangle_constant_(\n            image,\n            x1=10, y1=20, x2=60, y2=70,\n            cval=17, per_channel=False, random_state=None)\n\n        assert np.array_equal(image_aug[:20, :], image_cp[:20, :])\n        assert np.all(image_aug[20:70, 10:60] == 17)\n\n    def test_iterable_cval_but_per_channel_is_false(self):\n        image = np.mod(np.arange(100*100*3), 256).astype(np.uint8).reshape(\n            (100, 100, 3))\n        image_cp = np.copy(image)\n\n        image_aug = arithmetic_lib._fill_rectangle_constant_(\n            image,\n            x1=10, y1=20, x2=60, y2=70,\n            cval=[17, 21, 25], per_channel=False, random_state=None)\n\n        assert np.array_equal(image_aug[:20, :], image_cp[:20, :])\n        assert np.all(image_aug[20:70, 10:60] == 17)\n\n    def test_iterable_cval_with_per_channel_is_true(self):\n        image = np.mod(np.arange(100*100*3), 256).astype(np.uint8).reshape(\n            (100, 100, 3))\n        image_cp = np.copy(image)\n\n        image_aug = arithmetic_lib._fill_rectangle_constant_(\n            image,\n            x1=10, y1=20, x2=60, y2=70,\n            cval=[17, 21, 25], per_channel=True, random_state=None)\n\n        assert np.array_equal(image_aug[:20, :], image_cp[:20, :])\n        assert np.all(image_aug[20:70, 10:60, 0] == 17)\n        assert np.all(image_aug[20:70, 10:60, 1] == 21)\n        assert np.all(image_aug[20:70, 10:60, 2] == 25)\n\n    def test_iterable_cval_with_per_channel_is_true_channel_mismatch(self):\n        image = np.mod(np.arange(100*100*5), 256).astype(np.uint8).reshape(\n            (100, 100, 5))\n        image_cp = np.copy(image)\n\n        image_aug = arithmetic_lib._fill_rectangle_constant_(\n            image,\n            x1=10, y1=20, x2=60, y2=70,\n            cval=[17, 21], per_channel=True, random_state=None)\n\n        assert np.array_equal(image_aug[:20, :], image_cp[:20, :])\n        assert np.all(image_aug[20:70, 10:60, 0] == 17)\n        assert np.all(image_aug[20:70, 10:60, 1] == 21)\n        assert np.all(image_aug[20:70, 10:60, 2] == 17)\n        assert np.all(image_aug[20:70, 10:60, 3] == 21)\n        assert np.all(image_aug[20:70, 10:60, 4] == 17)\n\n    def test_single_cval_with_per_channel_is_true(self):\n        image = np.mod(np.arange(100*100*3), 256).astype(np.uint8).reshape(\n            (100, 100, 3))\n        image_cp = np.copy(image)\n\n        image_aug = arithmetic_lib._fill_rectangle_constant_(\n            image,\n            x1=10, y1=20, x2=60, y2=70,\n            cval=17, per_channel=True, random_state=None)\n\n        assert np.array_equal(image_aug[:20, :], image_cp[:20, :])\n        assert np.all(image_aug[20:70, 10:60, 0] == 17)\n        assert np.all(image_aug[20:70, 10:60, 1] == 17)\n        assert np.all(image_aug[20:70, 10:60, 2] == 17)\n\n    def test_no_channels_single_cval(self):\n        for per_channel in [False, True]:\n            with self.subTest(per_channel=per_channel):\n                image = np.mod(\n                    np.arange(100*100), 256\n                ).astype(np.uint8).reshape((100, 100))\n                image_cp = np.copy(image)\n\n                image_aug = arithmetic_lib._fill_rectangle_constant_(\n                    image,\n                    x1=10, y1=20, x2=60, y2=70,\n                    cval=17, per_channel=per_channel, random_state=None)\n\n                assert np.array_equal(image_aug[:20, :], image_cp[:20, :])\n                assert np.all(image_aug[20:70, 10:60] == 17)\n\n    def test_no_channels_iterable_cval(self):\n        for per_channel in [False, True]:\n            with self.subTest(per_channel=per_channel):\n                image = np.mod(\n                    np.arange(100*100), 256\n                ).astype(np.uint8).reshape((100, 100))\n                image_cp = np.copy(image)\n\n                image_aug = arithmetic_lib._fill_rectangle_constant_(\n                    image,\n                    x1=10, y1=20, x2=60, y2=70,\n                    cval=[17, 21, 25], per_channel=per_channel,\n                    random_state=None)\n\n                assert np.array_equal(image_aug[:20, :], image_cp[:20, :])\n                assert np.all(image_aug[20:70, 10:60] == 17)\n\n    def test_unusual_channel_numbers(self):\n        for nb_channels in [1, 2, 4, 5, 511, 512, 513]:\n            for per_channel in [False, True]:\n                with self.subTest(per_channel=per_channel):\n                    image = np.mod(\n                        np.arange(100*100*nb_channels), 256\n                    ).astype(np.uint8).reshape((100, 100, nb_channels))\n                    image_cp = np.copy(image)\n\n                    image_aug = arithmetic_lib._fill_rectangle_constant_(\n                        image,\n                        x1=10, y1=20, x2=60, y2=70,\n                        cval=[17, 21], per_channel=per_channel,\n                        random_state=None)\n\n                    assert np.array_equal(image_aug[:20, :], image_cp[:20, :])\n                    if per_channel:\n                        for c in np.arange(nb_channels):\n                            val = 17 if c % 2 == 0 else 21\n                            assert np.all(image_aug[20:70, 10:60, c] == val)\n                    else:\n                        assert np.all(image_aug[20:70, 10:60, :] == 17)\n\n    def test_other_dtypes_bool(self):\n        for per_channel in [False, True]:\n            with self.subTest(per_channel=per_channel):\n                image = np.array([0, 1], dtype=bool)\n                image = np.tile(image, (int(3*300*300/2),))\n                image = image.reshape((300, 300, 3))\n                image_cp = np.copy(image)\n\n                image_aug = arithmetic_lib._fill_rectangle_constant_(\n                    image,\n                    x1=10, y1=10, x2=300-10, y2=300-10,\n                    cval=[0, 1], per_channel=per_channel,\n                    random_state=None)\n\n                rect = image_aug[10:-10, 10:-10]\n                assert np.array_equal(image_aug[:10, :], image_cp[:10, :])\n                if per_channel:\n                    assert np.all(image_aug[10:-10, 10:-10, 0] == 0)\n                    assert np.all(image_aug[10:-10, 10:-10, 1] == 1)\n                    assert np.all(image_aug[10:-10, 10:-10, 2] == 0)\n                else:\n                    assert np.all(image_aug[20:70, 10:60] == 0)\n\n    def test_other_dtypes_uint_int(self):\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int16\", \"int32\", \"int64\"]\n        for dtype in dtypes:\n            for per_channel in [False, True]:\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                with self.subTest(dtype=dtype, per_channel=per_channel):\n                    image = np.array([min_value, min_value+1,\n                                      int(center_value),\n                                      max_value-1, max_value], dtype=dtype)\n                    image = np.tile(image, (int(3*300*300/5),))\n                    image = image.reshape((300, 300, 3))\n                    assert min_value in image\n                    assert max_value in image\n                    image_cp = np.copy(image)\n\n                    image_aug = arithmetic_lib._fill_rectangle_constant_(\n                        image,\n                        x1=10, y1=10, x2=300-10, y2=300-10,\n                        cval=[min_value, 10, max_value],\n                        per_channel=per_channel,\n                        random_state=None)\n\n                    assert np.array_equal(image_aug[:10, :], image_cp[:10, :])\n                    if per_channel:\n                        assert np.all(image_aug[10:-10, 10:-10, 0]\n                                      == min_value)\n                        assert np.all(image_aug[10:-10, 10:-10, 1]\n                                      == 10)\n                        assert np.all(image_aug[10:-10, 10:-10, 2]\n                                      == max_value)\n                    else:\n                        assert np.all(image_aug[-10:-10, 10:-10] == min_value)\n\n    def test_other_dtypes_float(self):\n        try:\n            high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n        except AttributeError:\n            high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n\n        for dtype in dtypes:\n            for per_channel in [False, True]:\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                with self.subTest(dtype=dtype, per_channel=per_channel):\n                    image = np.array([min_value, min_value+1,\n                                      int(center_value),\n                                      max_value-1, max_value], dtype=dtype)\n                    image = np.tile(image, (int(3*300*300/5),))\n                    image = image.reshape((300, 300, 3))\n\n                    # Use this here instead of any(isclose(...)) because\n                    # the latter one leads to overflow warnings.\n                    assert image.flat[0] <= high_res_dt(min_value) + 1.0\n                    assert image.flat[4] >= high_res_dt(max_value) - 1.0\n\n                    image_cp = np.copy(image)\n\n                    image_aug = arithmetic_lib._fill_rectangle_constant_(\n                        image,\n                        x1=10, y1=10, x2=300-10, y2=300-10,\n                        cval=[min_value, 10, max_value],\n                        per_channel=per_channel,\n                        random_state=None)\n\n                    assert image_aug.dtype.name == dtype\n                    assert np.allclose(image_aug[:10, :], image_cp[:10, :],\n                                       rtol=0, atol=1e-4)\n                    if per_channel:\n                        assert np.allclose(image_aug[10:-10, 10:-10, 0],\n                                           high_res_dt(min_value),\n                                           rtol=0, atol=1e-4)\n                        assert np.allclose(image_aug[10:-10, 10:-10, 1],\n                                           high_res_dt(10),\n                                           rtol=0, atol=1e-4)\n                        assert np.allclose(image_aug[10:-10, 10:-10, 2],\n                                           high_res_dt(max_value),\n                                           rtol=0, atol=1e-4)\n                    else:\n                        assert np.allclose(image_aug[-10:-10, 10:-10],\n                                           high_res_dt(min_value),\n                                           rtol=0, atol=1e-4)\n\n\nclass TestAdd(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___bad_datatypes(self):\n        # test exceptions for wrong parameter types\n        got_exception = False\n        try:\n            _ = iaa.Add(value=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        got_exception = False\n        try:\n            _ = iaa.Add(value=1, per_channel=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test_add_zero(self):\n        # no add, shouldnt change anything\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.Add(value=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n    def test_add_one(self):\n        # add > 0\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.Add(value=1)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = images + 1\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = [images_list[0] + 1]\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = images + 1\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = [images_list[0] + 1]\n        assert array_equal_lists(observed, expected)\n\n    def test_minus_one(self):\n        # add < 0\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.Add(value=-1)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = images - 1\n        assert np.array_equal(observed, expected)\n\n        observed = aug.augment_images(images_list)\n        expected = [images_list[0] - 1]\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = images - 1\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = [images_list[0] - 1]\n        assert array_equal_lists(observed, expected)\n\n    def test_uint8_every_possible_value(self):\n        # uint8, every possible addition for base value 127\n        for value_type in [float, int]:\n            for per_channel in [False, True]:\n                for value in np.arange(-255, 255+1):\n                    aug = iaa.Add(value=value_type(value), per_channel=per_channel)\n                    expected = np.clip(127 + value_type(value), 0, 255)\n\n                    img = np.full((1, 1), 127, dtype=np.uint8)\n                    img_aug = aug.augment_image(img)\n                    assert img_aug.item(0) == expected\n\n                    img = np.full((1, 1, 3), 127, dtype=np.uint8)\n                    img_aug = aug.augment_image(img)\n                    assert np.all(img_aug == expected)\n\n    def test_add_floats(self):\n        # specific tests with floats\n        aug = iaa.Add(value=0.75)\n        img = np.full((1, 1), 1, dtype=np.uint8)\n        img_aug = aug.augment_image(img)\n        assert img_aug.item(0) == 2\n\n        img = np.full((1, 1), 1, dtype=np.uint16)\n        img_aug = aug.augment_image(img)\n        assert img_aug.item(0) == 2\n\n        aug = iaa.Add(value=0.45)\n        img = np.full((1, 1), 1, dtype=np.uint8)\n        img_aug = aug.augment_image(img)\n        assert img_aug.item(0) == 1\n\n        img = np.full((1, 1), 1, dtype=np.uint16)\n        img_aug = aug.augment_image(img)\n        assert img_aug.item(0) == 1\n\n    def test_stochastic_parameters_as_value(self):\n        # test other parameters\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n\n        aug = iaa.Add(value=iap.DiscreteUniform(1, 10))\n        observed = aug.augment_images(images)\n        assert 100 + 1 <= np.average(observed) <= 100 + 10\n\n        aug = iaa.Add(value=iap.Uniform(1, 10))\n        observed = aug.augment_images(images)\n        assert 100 + 1 <= np.average(observed) <= 100 + 10\n\n        aug = iaa.Add(value=iap.Clip(iap.Normal(1, 1), -3, 3))\n        observed = aug.augment_images(images)\n        assert 100 - 3 <= np.average(observed) <= 100 + 3\n\n        aug = iaa.Add(value=iap.Discretize(iap.Clip(iap.Normal(1, 1), -3, 3)))\n        observed = aug.augment_images(images)\n        assert 100 - 3 <= np.average(observed) <= 100 + 3\n\n    def test_keypoints_dont_change(self):\n        # keypoints shouldnt be changed\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n                                          ia.Keypoint(x=2, y=2)], shape=base_img.shape)]\n\n        aug = iaa.Add(value=1)\n        aug_det = iaa.Add(value=1).to_deterministic()\n        observed = aug.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n    def test_tuple_as_value(self):\n        # varying values\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n\n        aug = iaa.Add(value=(0, 10))\n        aug_det = aug.to_deterministic()\n\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n        assert nb_changed_aug >= int(nb_iterations * 0.7)\n        assert nb_changed_aug_det == 0\n\n    def test_per_channel(self):\n        # test channelwise\n        aug = iaa.Add(value=iap.Choice([0, 1]), per_channel=True)\n        observed = aug.augment_image(np.zeros((1, 1, 100), dtype=np.uint8))\n        uq = np.unique(observed)\n        assert observed.shape == (1, 1, 100)\n        assert 0 in uq\n        assert 1 in uq\n        assert len(uq) == 2\n\n    def test_per_channel_with_probability(self):\n        # test channelwise with probability\n        aug = iaa.Add(value=iap.Choice([0, 1]), per_channel=0.5)\n        seen = [0, 0]\n        for _ in sm.xrange(400):\n            observed = aug.augment_image(np.zeros((1, 1, 20), dtype=np.uint8))\n            assert observed.shape == (1, 1, 20)\n\n            uq = np.unique(observed)\n            per_channel = (len(uq) == 2)\n            if per_channel:\n                seen[0] += 1\n            else:\n                seen[1] += 1\n        assert 150 < seen[0] < 250\n        assert 150 < seen[1] < 250\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Add(1)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Add(1)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_get_parameters(self):\n        # test get_parameters()\n        aug = iaa.Add(value=1, per_channel=False)\n        params = aug.get_parameters()\n        is_parameter_instance(params[0], iap.Deterministic)\n        is_parameter_instance(params[1], iap.Deterministic)\n        assert params[0].value == 1\n        assert params[1].value == 0\n\n    def test_heatmaps(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.Add(value=10)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_other_dtypes_bool(self):\n        image = np.zeros((3, 3), dtype=bool)\n        aug = iaa.Add(value=1)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 1)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.Add(value=1)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 1)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.Add(value=-1)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.Add(value=-2)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n    def test_other_dtypes_uint_int(self):\n        for dtype in [np.uint8, np.uint16, np.int8, np.int16]:\n            min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n            image = np.full((3, 3), min_value, dtype=dtype)\n            aug = iaa.Add(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == min_value + 1)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.Add(11)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == min_value + 21)\n\n            image = np.full((3, 3), max_value - 2, dtype=dtype)\n            aug = iaa.Add(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == max_value - 1)\n\n            image = np.full((3, 3), max_value - 1, dtype=dtype)\n            aug = iaa.Add(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == max_value)\n\n            image = np.full((3, 3), max_value - 1, dtype=dtype)\n            aug = iaa.Add(2)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == max_value)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.Add(-9)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == min_value + 1)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.Add(-10)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == min_value)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.Add(-11)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == min_value)\n\n            for _ in sm.xrange(10):\n                image = np.full((1, 1, 3), 20, dtype=dtype)\n                aug = iaa.Add(iap.Uniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                assert len(np.unique(image_aug)) == 1\n\n                image = np.full((1, 1, 100), 20, dtype=dtype)\n                aug = iaa.Add(iap.Uniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                assert len(np.unique(image_aug)) > 1\n\n                image = np.full((1, 1, 3), 20, dtype=dtype)\n                aug = iaa.Add(iap.DiscreteUniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                assert len(np.unique(image_aug)) == 1\n\n                image = np.full((1, 1, 100), 20, dtype=dtype)\n                aug = iaa.Add(iap.DiscreteUniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                assert len(np.unique(image_aug)) > 1\n\n    def test_other_dtypes_float(self):\n        # float\n        for dtype in [np.float16, np.float32]:\n            min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n            if dtype == np.float16:\n                atol = 1e-3 * max_value\n            else:\n                atol = 1e-9 * max_value\n            _allclose = functools.partial(np.allclose, atol=atol, rtol=0)\n\n            image = np.full((3, 3), min_value, dtype=dtype)\n            aug = iaa.Add(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, min_value + 1)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.Add(11)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, min_value + 21)\n\n            image = np.full((3, 3), max_value - 2, dtype=dtype)\n            aug = iaa.Add(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, max_value - 1)\n\n            image = np.full((3, 3), max_value - 1, dtype=dtype)\n            aug = iaa.Add(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, max_value)\n\n            image = np.full((3, 3), max_value - 1, dtype=dtype)\n            aug = iaa.Add(2)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, max_value)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.Add(-9)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, min_value + 1)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.Add(-10)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, min_value)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.Add(-11)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, min_value)\n\n            for _ in sm.xrange(10):\n                image = np.full((50, 1, 3), 0, dtype=dtype)\n                aug = iaa.Add(iap.Uniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-10 - 1e-2 < image_aug, image_aug < 10 + 1e-2))\n                assert np.allclose(image_aug[1:, :, 0], image_aug[:-1, :, 0])\n                assert np.allclose(image_aug[..., 0], image_aug[..., 1])\n\n                image = np.full((1, 1, 100), 0, dtype=dtype)\n                aug = iaa.Add(iap.Uniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-10 - 1e-2 < image_aug, image_aug < 10 + 1e-2))\n                assert not np.allclose(image_aug[:, :, 1:], image_aug[:, :, :-1])\n\n                image = np.full((50, 1, 3), 0, dtype=dtype)\n                aug = iaa.Add(iap.DiscreteUniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-10 - 1e-2 < image_aug, image_aug < 10 + 1e-2))\n                assert np.allclose(image_aug[1:, :, 0], image_aug[:-1, :, 0])\n                assert np.allclose(image_aug[..., 0], image_aug[..., 1])\n\n                image = np.full((1, 1, 100), 0, dtype=dtype)\n                aug = iaa.Add(iap.DiscreteUniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-10 - 1e-2 < image_aug, image_aug < 10 + 1e-2))\n                assert not np.allclose(image_aug[:, :, 1:], image_aug[:, :, :-1])\n\n    def test_pickleable(self):\n        aug = iaa.Add((0, 50), per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\nclass TestAddElementwise(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___bad_datatypes(self):\n        # test exceptions for wrong parameter types\n        got_exception = False\n        try:\n            _aug = iaa.AddElementwise(value=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        got_exception = False\n        try:\n            _aug = iaa.AddElementwise(value=1, per_channel=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test_add_zero(self):\n        # no add, shouldnt change anything\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.AddElementwise(value=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n    def test_add_one(self):\n        # add > 0\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.AddElementwise(value=1)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = images + 1\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = [images_list[0] + 1]\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = images + 1\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = [images_list[0] + 1]\n        assert array_equal_lists(observed, expected)\n\n    def test_add_minus_one(self):\n        # add < 0\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.AddElementwise(value=-1)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = images - 1\n        assert np.array_equal(observed, expected)\n\n        observed = aug.augment_images(images_list)\n        expected = [images_list[0] - 1]\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = images - 1\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = [images_list[0] - 1]\n        assert array_equal_lists(observed, expected)\n\n    def test_uint8_every_possible_value(self):\n        # uint8, every possible addition for base value 127\n        for value_type in [int]:\n            for per_channel in [False, True]:\n                for value in np.arange(-255, 255+1):\n                    aug = iaa.AddElementwise(value=value_type(value), per_channel=per_channel)\n                    expected = np.clip(127 + value_type(value), 0, 255)\n\n                    img = np.full((1, 1), 127, dtype=np.uint8)\n                    img_aug = aug.augment_image(img)\n                    assert img_aug.item(0) == expected\n\n                    img = np.full((1, 1, 3), 127, dtype=np.uint8)\n                    img_aug = aug.augment_image(img)\n                    assert np.all(img_aug == expected)\n\n    def test_stochastic_parameters_as_value(self):\n        # test other parameters\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n\n        aug = iaa.AddElementwise(value=iap.DiscreteUniform(1, 10))\n        observed = aug.augment_images(images)\n        assert np.min(observed) >= 100 + 1\n        assert np.max(observed) <= 100 + 10\n\n        aug = iaa.AddElementwise(value=iap.Uniform(1, 10))\n        observed = aug.augment_images(images)\n        assert np.min(observed) >= 100 + 1\n        assert np.max(observed) <= 100 + 10\n\n        aug = iaa.AddElementwise(value=iap.Clip(iap.Normal(1, 1), -3, 3))\n        observed = aug.augment_images(images)\n        assert np.min(observed) >= 100 - 3\n        assert np.max(observed) <= 100 + 3\n\n        aug = iaa.AddElementwise(value=iap.Discretize(iap.Clip(iap.Normal(1, 1), -3, 3)))\n        observed = aug.augment_images(images)\n        assert np.min(observed) >= 100 - 3\n        assert np.max(observed) <= 100 + 3\n\n    def test_keypoints_dont_change(self):\n        # keypoints shouldnt be changed\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n                                          ia.Keypoint(x=2, y=2)], shape=base_img.shape)]\n\n        aug = iaa.AddElementwise(value=1)\n        aug_det = iaa.AddElementwise(value=1).to_deterministic()\n        observed = aug.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n    def test_tuple_as_value(self):\n        # varying values\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n\n        aug = iaa.AddElementwise(value=(0, 10))\n        aug_det = aug.to_deterministic()\n\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n        assert nb_changed_aug >= int(nb_iterations * 0.7)\n        assert nb_changed_aug_det == 0\n\n    def test_samples_change_by_spatial_location(self):\n        # values should change between pixels\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n\n        aug = iaa.AddElementwise(value=(-50, 50))\n\n        nb_same = 0\n        nb_different = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_flat = observed_aug.flatten()\n            last = None\n            for j in sm.xrange(observed_aug_flat.size):\n                if last is not None:\n                    v = observed_aug_flat[j]\n                    if v - 0.0001 <= last <= v + 0.0001:\n                        nb_same += 1\n                    else:\n                        nb_different += 1\n                last = observed_aug_flat[j]\n        assert nb_different > 0.9 * (nb_different + nb_same)\n\n    def test_per_channel(self):\n        # test channelwise\n        aug = iaa.AddElementwise(value=iap.Choice([0, 1]), per_channel=True)\n        observed = aug.augment_image(np.zeros((100, 100, 3), dtype=np.uint8))\n        sums = np.sum(observed, axis=2)\n        values = np.unique(sums)\n        assert all([(value in values) for value in [0, 1, 2, 3]])\n\n    def test_per_channel_with_probability(self):\n        # test channelwise with probability\n        aug = iaa.AddElementwise(value=iap.Choice([0, 1]), per_channel=0.5)\n        seen = [0, 0]\n        for _ in sm.xrange(400):\n            observed = aug.augment_image(np.zeros((20, 20, 3), dtype=np.uint8))\n            sums = np.sum(observed, axis=2)\n            values = np.unique(sums)\n            all_values_found = all([(value in values) for value in [0, 1, 2, 3]])\n            if all_values_found:\n                seen[0] += 1\n            else:\n                seen[1] += 1\n        assert 150 < seen[0] < 250\n        assert 150 < seen[1] < 250\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.AddElementwise(1)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.AddElementwise(1)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_get_parameters(self):\n        # test get_parameters()\n        aug = iaa.AddElementwise(value=1, per_channel=False)\n        params = aug.get_parameters()\n        is_parameter_instance(params[0], iap.Deterministic)\n        is_parameter_instance(params[1], iap.Deterministic)\n        assert params[0].value == 1\n        assert params[1].value == 0\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.AddElementwise(value=10)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_other_dtypes_bool(self):\n        # bool\n        image = np.zeros((3, 3), dtype=bool)\n        aug = iaa.AddElementwise(value=1)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 1)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.AddElementwise(value=1)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 1)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.AddElementwise(value=-1)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.AddElementwise(value=-2)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n    def test_other_dtypes_uint_int(self):\n        # uint, int\n        for dtype in [np.uint8, np.uint16, np.int8, np.int16]:\n            min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n            image = np.full((3, 3), min_value, dtype=dtype)\n            aug = iaa.AddElementwise(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == min_value + 1)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.AddElementwise(11)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == min_value + 21)\n\n            image = np.full((3, 3), max_value - 2, dtype=dtype)\n            aug = iaa.AddElementwise(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == max_value - 1)\n\n            image = np.full((3, 3), max_value - 1, dtype=dtype)\n            aug = iaa.AddElementwise(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == max_value)\n\n            image = np.full((3, 3), max_value - 1, dtype=dtype)\n            aug = iaa.AddElementwise(2)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == max_value)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.AddElementwise(-9)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == min_value + 1)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.AddElementwise(-10)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == min_value)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.AddElementwise(-11)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == min_value)\n\n            for _ in sm.xrange(10):\n                image = np.full((5, 5, 3), 20, dtype=dtype)\n                aug = iaa.AddElementwise(iap.Uniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                assert len(np.unique(image_aug)) > 1\n                assert np.all(image_aug[..., 0] == image_aug[..., 1])\n\n                image = np.full((1, 1, 100), 20, dtype=dtype)\n                aug = iaa.AddElementwise(iap.Uniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                assert len(np.unique(image_aug)) > 1\n\n                image = np.full((5, 5, 3), 20, dtype=dtype)\n                aug = iaa.AddElementwise(iap.DiscreteUniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                assert len(np.unique(image_aug)) > 1\n                assert np.all(image_aug[..., 0] == image_aug[..., 1])\n\n                image = np.full((1, 1, 100), 20, dtype=dtype)\n                aug = iaa.AddElementwise(iap.DiscreteUniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                assert len(np.unique(image_aug)) > 1\n\n    def test_other_dtypes_float(self):\n        # float\n        for dtype in [np.float16, np.float32]:\n            min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n            if dtype == np.float16:\n                atol = 1e-3 * max_value\n            else:\n                atol = 1e-9 * max_value\n            _allclose = functools.partial(np.allclose, atol=atol, rtol=0)\n\n            image = np.full((3, 3), min_value, dtype=dtype)\n            aug = iaa.AddElementwise(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, min_value + 1)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.AddElementwise(11)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, min_value + 21)\n\n            image = np.full((3, 3), max_value - 2, dtype=dtype)\n            aug = iaa.AddElementwise(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, max_value - 1)\n\n            image = np.full((3, 3), max_value - 1, dtype=dtype)\n            aug = iaa.AddElementwise(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, max_value)\n\n            image = np.full((3, 3), max_value - 1, dtype=dtype)\n            aug = iaa.AddElementwise(2)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, max_value)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.AddElementwise(-9)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, min_value + 1)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.AddElementwise(-10)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, min_value)\n\n            image = np.full((3, 3), min_value + 10, dtype=dtype)\n            aug = iaa.AddElementwise(-11)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, min_value)\n\n            for _ in sm.xrange(10):\n                image = np.full((50, 1, 3), 0, dtype=dtype)\n                aug = iaa.AddElementwise(iap.Uniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-10 - 1e-2 < image_aug, image_aug < 10 + 1e-2))\n                assert not np.allclose(image_aug[1:, :, 0], image_aug[:-1, :, 0])\n                assert np.allclose(image_aug[..., 0], image_aug[..., 1])\n\n                image = np.full((1, 1, 100), 0, dtype=dtype)\n                aug = iaa.AddElementwise(iap.Uniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-10 - 1e-2 < image_aug, image_aug < 10 + 1e-2))\n                assert not np.allclose(image_aug[:, :, 1:], image_aug[:, :, :-1])\n\n                image = np.full((50, 1, 3), 0, dtype=dtype)\n                aug = iaa.AddElementwise(iap.DiscreteUniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-10 - 1e-2 < image_aug, image_aug < 10 + 1e-2))\n                assert not np.allclose(image_aug[1:, :, 0], image_aug[:-1, :, 0])\n                assert np.allclose(image_aug[..., 0], image_aug[..., 1])\n\n                image = np.full((1, 1, 100), 0, dtype=dtype)\n                aug = iaa.AddElementwise(iap.DiscreteUniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-10 - 1e-2 < image_aug, image_aug < 10 + 1e-2))\n                assert not np.allclose(image_aug[:, :, 1:], image_aug[:, :, :-1])\n\n    def test_pickleable(self):\n        aug = iaa.AddElementwise((0, 50), per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=2)\n\n\nclass AdditiveGaussianNoise(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_loc_zero_scale_zero(self):\n        # no noise, shouldnt change anything\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 128\n        images = np.array([base_img])\n\n        aug = iaa.AdditiveGaussianNoise(loc=0, scale=0)\n\n        observed = aug.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n\n    def test_loc_zero_scale_nonzero(self):\n        # zero-centered noise\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 128\n        images = np.array([base_img])\n        images_list = [base_img]\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n                                          ia.Keypoint(x=2, y=2)], shape=base_img.shape)]\n\n        aug = iaa.AdditiveGaussianNoise(loc=0, scale=0.2 * 255)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        assert not np.array_equal(observed, images)\n\n        observed = aug_det.augment_images(images)\n        assert not np.array_equal(observed, images)\n\n        observed = aug.augment_images(images_list)\n        assert not array_equal_lists(observed, images_list)\n\n        observed = aug_det.augment_images(images_list)\n        assert not array_equal_lists(observed, images_list)\n\n        observed = aug.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints)\n\n    def test_std_dev_of_added_noise_matches_scale(self):\n        # std correct?\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 128\n\n        aug = iaa.AdditiveGaussianNoise(loc=0, scale=0.2 * 255)\n        images = np.ones((1, 1, 1, 1), dtype=np.uint8) * 128\n        nb_iterations = 1000\n        values = []\n        for i in sm.xrange(nb_iterations):\n            images_aug = aug.augment_images(images)\n            values.append(images_aug[0, 0, 0, 0])\n        values = np.array(values)\n        assert np.min(values) == 0\n        assert 0.1 < np.std(values) / 255.0 < 0.4\n\n    def test_nonzero_loc(self):\n        # non-zero loc\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 128\n\n        aug = iaa.AdditiveGaussianNoise(loc=0.25 * 255, scale=0.01 * 255)\n        images = np.ones((1, 1, 1, 1), dtype=np.uint8) * 128\n        nb_iterations = 1000\n        values = []\n        for i in sm.xrange(nb_iterations):\n            images_aug = aug.augment_images(images)\n            values.append(images_aug[0, 0, 0, 0] - 128)\n        values = np.array(values)\n        assert 54 < np.average(values) < 74 # loc=0.25 should be around 255*0.25=64 average\n\n    def test_tuple_as_loc(self):\n        # varying locs\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 128\n\n        aug = iaa.AdditiveGaussianNoise(loc=(0, 0.5 * 255), scale=0.0001 * 255)\n        aug_det = aug.to_deterministic()\n        images = np.ones((1, 1, 1, 1), dtype=np.uint8) * 128\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n        assert nb_changed_aug >= int(nb_iterations * 0.95)\n        assert nb_changed_aug_det == 0\n\n    def test_stochastic_parameter_as_loc(self):\n        # varying locs by stochastic param\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 128\n\n        aug = iaa.AdditiveGaussianNoise(loc=iap.Choice([-20, 20]), scale=0.0001 * 255)\n        images = np.ones((1, 1, 1, 1), dtype=np.uint8) * 128\n        seen = [0, 0]\n        for i in sm.xrange(200):\n            observed = aug.augment_images(images)\n            mean = np.mean(observed)\n            diff_m20 = abs(mean - (128-20))\n            diff_p20 = abs(mean - (128+20))\n            if diff_m20 <= 1:\n                seen[0] += 1\n            elif diff_p20 <= 1:\n                seen[1] += 1\n            else:\n                assert False\n        assert 75 < seen[0] < 125\n        assert 75 < seen[1] < 125\n\n    def test_tuple_as_scale(self):\n        # varying stds\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 128\n\n        aug = iaa.AdditiveGaussianNoise(loc=0, scale=(0.01 * 255, 0.2 * 255))\n        aug_det = aug.to_deterministic()\n        images = np.ones((1, 1, 1, 1), dtype=np.uint8) * 128\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n        assert nb_changed_aug >= int(nb_iterations * 0.95)\n        assert nb_changed_aug_det == 0\n\n    def test_stochastic_parameter_as_scale(self):\n        # varying stds by stochastic param\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 128\n\n        aug = iaa.AdditiveGaussianNoise(loc=0, scale=iap.Choice([1, 20]))\n        images = np.ones((1, 20, 20, 1), dtype=np.uint8) * 128\n        seen = [0, 0, 0]\n        for i in sm.xrange(200):\n            observed = aug.augment_images(images)\n            std = np.std(observed.astype(np.int32) - 128)\n            diff_1 = abs(std - 1)\n            diff_20 = abs(std - 20)\n            if diff_1 <= 2:\n                seen[0] += 1\n            elif diff_20 <= 5:\n                seen[1] += 1\n            else:\n                seen[2] += 1\n        assert seen[2] <= 5\n        assert 75 < seen[0] < 125\n        assert 75 < seen[1] < 125\n\n    def test___init___bad_datatypes(self):\n        # test exceptions for wrong parameter types\n        got_exception = False\n        try:\n            _ = iaa.AdditiveGaussianNoise(loc=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        got_exception = False\n        try:\n            _ = iaa.AdditiveGaussianNoise(scale=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 128\n\n        aug = iaa.AdditiveGaussianNoise(loc=0.5, scale=10)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_pickleable(self):\n        aug = iaa.AdditiveGaussianNoise(scale=(0.1, 10), per_channel=True,\n                                        seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=2)\n\n\nclass TestCutout(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___defaults(self):\n        aug = iaa.Cutout()\n        assert aug.nb_iterations.value == 1\n        assert is_parameter_instance(aug.position[0], iap.Uniform)\n        assert is_parameter_instance(aug.position[1], iap.Uniform)\n        assert np.isclose(aug.size.value, 0.2)\n        assert aug.squared.value == 1\n        assert aug.fill_mode.value == \"constant\"\n        assert aug.cval.value == 128\n        assert aug.fill_per_channel.value == 0\n\n    def test___init___custom(self):\n        aug = iaa.Cutout(\n            nb_iterations=1,\n            position=(0.5, 0.5),\n            size=0.1,\n            squared=0.6,\n            fill_mode=[\"gaussian\", \"constant\"],\n            cval=(0, 255),\n            fill_per_channel=0.5\n        )\n        assert aug.nb_iterations.value == 1\n        assert np.isclose(aug.position[0].value, 0.5)\n        assert np.isclose(aug.position[1].value, 0.5)\n        assert np.isclose(aug.size.value, 0.1)\n        assert np.isclose(aug.squared.p.value, 0.6)\n        assert aug.fill_mode.a == [\"gaussian\", \"constant\"]\n        assert np.isclose(aug.cval.a.value, 0)\n        assert np.isclose(aug.cval.b.value, 255)\n        assert np.isclose(aug.fill_per_channel.p.value, 0.5)\n\n    def test___init___fill_mode_is_stochastic_param(self):\n        param = iap.Deterministic(\"constant\")\n        aug = iaa.Cutout(fill_mode=param)\n        assert aug.fill_mode is param\n\n    @mock.patch(\"imgaug.augmenters.arithmetic.cutout_\")\n    def test_mocked__squared_false(self, mock_apply):\n        aug = iaa.Cutout(nb_iterations=2,\n                         position=(0.5, 0.6),\n                         size=iap.DeterministicList([0.1, 0.2]),\n                         squared=False,\n                         fill_mode=\"gaussian\",\n                         cval=1,\n                         fill_per_channel=True)\n        image = np.zeros((10, 30, 3), dtype=np.uint8)\n\n        # dont return image itself, otherwise the loop below will fail\n        # at its second iteration as the method is expected to handle\n        # internally a copy of the image and not the image itself\n        mock_apply.return_value = np.copy(image)\n\n        _ = aug(image=image)\n\n        assert mock_apply.call_count == 2\n\n        for call_idx in np.arange(2):\n            args = mock_apply.call_args_list[call_idx][0]\n            kwargs = mock_apply.call_args_list[call_idx][1]\n            assert args[0] is not image\n            assert np.array_equal(args[0], image)\n            assert np.isclose(kwargs[\"x1\"], 0.5*30 - 0.5 * (0.2*30))\n            assert np.isclose(kwargs[\"y1\"], 0.6*10 - 0.5 * (0.1*10))\n            assert np.isclose(kwargs[\"x2\"], 0.5*30 + 0.5 * (0.2*30))\n            assert np.isclose(kwargs[\"y2\"], 0.6*10 + 0.5 * (0.1*10))\n            assert kwargs[\"fill_mode\"] == \"gaussian\"\n            assert np.array_equal(kwargs[\"cval\"], [1, 1, 1])\n            assert np.isclose(kwargs[\"fill_per_channel\"], 1.0)\n            assert isinstance(kwargs[\"seed\"], iarandom.RNG)\n\n    @mock.patch(\"imgaug.augmenters.arithmetic.cutout_\")\n    def test_mocked__squared_true(self, mock_apply):\n        aug = iaa.Cutout(nb_iterations=2,\n                         position=(0.5, 0.6),\n                         size=iap.DeterministicList([0.1, 0.2]),\n                         squared=True,\n                         fill_mode=\"gaussian\",\n                         cval=1,\n                         fill_per_channel=True)\n        image = np.zeros((10, 30, 3), dtype=np.uint8)\n\n        # dont return image itself, otherwise the loop below will fail\n        # at its second iteration as the method is expected to handle\n        # internally a copy of the image and not the image itself\n        mock_apply.return_value = np.copy(image)\n\n        _ = aug(image=image)\n\n        assert mock_apply.call_count == 2\n\n        for call_idx in np.arange(2):\n            args = mock_apply.call_args_list[call_idx][0]\n            kwargs = mock_apply.call_args_list[call_idx][1]\n            assert args[0] is not image\n            assert np.array_equal(args[0], image)\n            assert np.isclose(kwargs[\"x1\"], 0.5*30 - 0.5 * (0.1*10))\n            assert np.isclose(kwargs[\"y1\"], 0.6*10 - 0.5 * (0.1*10))\n            assert np.isclose(kwargs[\"x2\"], 0.5*30 + 0.5 * (0.1*10))\n            assert np.isclose(kwargs[\"y2\"], 0.6*10 + 0.5 * (0.1*10))\n            assert kwargs[\"fill_mode\"] == \"gaussian\"\n            assert np.array_equal(kwargs[\"cval\"], [1, 1, 1])\n            assert np.isclose(kwargs[\"fill_per_channel\"], 1.0)\n            assert isinstance(kwargs[\"seed\"], iarandom.RNG)\n\n    def test_simple_image(self):\n        aug = iaa.Cutout(nb_iterations=2,\n                         position=(\n                             iap.DeterministicList([0.2, 0.8]),\n                             iap.DeterministicList([0.2, 0.8])\n                         ),\n                         size=0.2,\n                         fill_mode=\"constant\",\n                         cval=iap.DeterministicList([0, 0, 0, 1, 1, 1]))\n        image = np.full((100, 100, 3), 255, dtype=np.uint8)\n\n        for _ in np.arange(3):\n            images_aug = aug(images=[image, image])\n            for image_aug in images_aug:\n                values = np.unique(image_aug)\n                assert len(values) == 3\n                assert 0 in values\n                assert 1 in values\n                assert 255 in values\n\n    def test_batch_contains_only_non_image_data(self):\n        aug = iaa.Cutout()\n        segmap_arr = np.ones((3, 3, 1), dtype=np.int32)\n        segmap = ia.SegmentationMapsOnImage(segmap_arr, shape=(3, 3, 3))\n        segmap_aug = aug.augment_segmentation_maps(segmap)\n        assert np.array_equal(segmap.get_arr(), segmap_aug.get_arr())\n\n    def test_sampling_when_position_is_stochastic_parameter(self):\n        # sampling of position works slightly differently when it is a single\n        # parameter instead of tuple (paramX, paramY), so we have an extra\n        # test for that situation here\n        param = iap.DeterministicList([0.5, 0.6])\n        aug = iaa.Cutout(position=param)\n        samples = aug._draw_samples([\n            np.zeros((3, 3, 3), dtype=np.uint8),\n            np.zeros((3, 3, 3), dtype=np.uint8)\n        ], iarandom.RNG(0))\n        assert np.allclose(samples.pos_x, [0.5, 0.5])\n        assert np.allclose(samples.pos_y, [0.6, 0.6])\n\n    def test_by_comparison_to_official_implementation(self):\n        image = np.ones((10, 8, 2), dtype=np.uint8)\n        aug = iaa.Cutout(1, position=\"uniform\", size=0.2, squared=True,\n                         cval=0)\n        aug_official = _CutoutOfficial(n_holes=1, length=int(10*0.2))\n\n        dropped = np.zeros((10, 8, 2), dtype=np.int32)\n        dropped_official = np.copy(dropped)\n        height = np.zeros((10, 8, 2), dtype=np.int32)\n        width = np.copy(height)\n        height_official = np.copy(height)\n        width_official = np.copy(width)\n\n        nb_iterations = 3 * 1000\n\n        images_aug = aug(images=[image] * nb_iterations)\n        for image_aug in images_aug:\n            image_aug_off = aug_official(image)\n\n            mask = (image_aug == 0)\n            mask_off = (image_aug_off == 0)\n\n            dropped += mask\n            dropped_official += mask_off\n\n            ydrop = np.max(mask, axis=(2, 1))\n            xdrop = np.max(mask, axis=(2, 0))\n            wx = np.where(xdrop)\n            wy = np.where(ydrop)\n            x1 = wx[0][0]\n            x2 = wx[0][-1]\n            y1 = wy[0][0]\n            y2 = wy[0][-1]\n\n            ydrop_off = np.max(mask_off, axis=(2, 1))\n            xdrop_off = np.max(mask_off, axis=(2, 0))\n            wx_off = np.where(xdrop_off)\n            wy_off = np.where(ydrop_off)\n            x1_off = wx_off[0][0]\n            x2_off = wx_off[0][-1]\n            y1_off = wy_off[0][0]\n            y2_off = wy_off[0][-1]\n\n            height += (\n                np.full(height.shape, 1 + (y2 - y1), dtype=np.int32)\n                * mask)\n            width += (\n                np.full(width.shape, 1 + (x2 - x1), dtype=np.int32)\n                * mask)\n            height_official += (\n                np.full(height_official.shape, 1 + (y2_off - y1_off),\n                        dtype=np.int32)\n                * mask_off)\n            width_official += (\n                np.full(width_official.shape, 1 + (x2_off - x1_off),\n                        dtype=np.int32)\n                * mask_off)\n\n        dropped_prob = dropped / nb_iterations\n        dropped_prob_off = dropped_official / nb_iterations\n        height_avg = height / (dropped + 1e-4)\n        height_avg_off = height_official / (dropped_official + 1e-4)\n        width_avg = width / (dropped + 1e-4)\n        width_avg_off = width_official / (dropped_official + 1e-4)\n\n        prob_max_diff = np.max(np.abs(dropped_prob - dropped_prob_off))\n        height_avg_max_diff = np.max(np.abs(height_avg - height_avg_off))\n        width_avg_max_diff = np.max(np.abs(width_avg - width_avg_off))\n\n        assert prob_max_diff < 0.04\n        assert height_avg_max_diff < 0.3\n        assert width_avg_max_diff < 0.3\n\n    def test_determinism(self):\n        aug = iaa.Cutout(nb_iterations=(1, 3),\n                         size=(0.1, 0.2),\n                         fill_mode=[\"gaussian\", \"constant\"],\n                         cval=(0, 255))\n        image = np.mod(\n            np.arange(100*100*3), 256\n        ).reshape((100, 100, 3)).astype(np.uint8)\n\n        sums = []\n        for _ in np.arange(10):\n            aug_det = aug.to_deterministic()\n            image_aug1 = aug_det(image=image)\n            image_aug2 = aug_det(image=image)\n            assert np.array_equal(image_aug1, image_aug2)\n            sums.append(np.sum(image_aug1))\n        assert len(np.unique(sums)) > 1\n\n    def test_get_parameters(self):\n        aug = iaa.Cutout(\n            nb_iterations=1,\n            position=(0.5, 0.5),\n            size=0.1,\n            squared=0.6,\n            fill_mode=[\"gaussian\", \"constant\"],\n            cval=(0, 255),\n            fill_per_channel=0.5\n        )\n        params = aug.get_parameters()\n        assert params[0] is aug.nb_iterations\n        assert params[1] is aug.position\n        assert params[2] is aug.size\n        assert params[3] is aug.squared\n        assert params[4] is aug.fill_mode\n        assert params[5] is aug.cval\n        assert params[6] is aug.fill_per_channel\n\n    def test_pickleable(self):\n        aug = iaa.Cutout(\n            nb_iterations=1,\n            position=(0.5, 0.5),\n            size=0.1,\n            squared=0.6,\n            fill_mode=[\"gaussian\", \"constant\"],\n            cval=(0, 255),\n            fill_per_channel=0.5\n        )\n        runtest_pickleable_uint8_img(aug)\n\n\n# this is mostly copy-pasted cutout code from\n# https://github.com/uoguelph-mlrg/Cutout/blob/master/util/cutout.py\n# we use this to compare our implementation against\n# we changed some pytorch to numpy stuff\nclass _CutoutOfficial(object):\n    \"\"\"Randomly mask out one or more patches from an image.\n    Args:\n        n_holes (int): Number of patches to cut out of each image.\n        length (int): The length (in pixels) of each square patch.\n    \"\"\"\n    def __init__(self, n_holes, length):\n        self.n_holes = n_holes\n        self.length = length\n\n    def __call__(self, img):\n        \"\"\"\n        Args:\n            img (Tensor): Tensor image of size (C, H, W).\n        Returns:\n            Tensor: Image with n_holes of dimension length x length cut out of\n            it.\n        \"\"\"\n        # h = img.size(1)\n        # w = img.size(2)\n        h = img.shape[0]\n        w = img.shape[1]\n\n        mask = np.ones((h, w), np.float32)\n\n        for n in range(self.n_holes):\n            y = np.random.randint(h)\n            x = np.random.randint(w)\n\n            y1 = np.clip(y - self.length // 2, 0, h)\n            y2 = np.clip(y + self.length // 2, 0, h)\n            x1 = np.clip(x - self.length // 2, 0, w)\n            x2 = np.clip(x + self.length // 2, 0, w)\n\n            # note that in the paper they normalize to 0-mean,\n            # i.e. 0 here is actually not black but grayish pixels\n            mask[y1: y2, x1: x2] = 0\n\n        # mask = torch.from_numpy(mask)\n        # mask = mask.expand_as(img)\n        if img.ndim != 2:\n            mask = np.tile(mask[:, :, np.newaxis], (1, 1, img.shape[-1]))\n        img = img * mask\n\n        return img\n\n\nclass TestDropout(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_p_is_zero(self):\n        # no dropout, shouldnt change anything\n        base_img = np.ones((512, 512, 1), dtype=np.uint8) * 255\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.Dropout(p=0)\n        observed = aug.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n\n        observed = aug.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n        # 100% dropout, should drop everything\n        aug = iaa.Dropout(p=1.0)\n        observed = aug.augment_images(images)\n        expected = np.zeros((1, 512, 512, 1), dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n\n        observed = aug.augment_images(images_list)\n        expected = [np.zeros((512, 512, 1), dtype=np.uint8)]\n        assert array_equal_lists(observed, expected)\n\n    def test_p_is_50_percent(self):\n        # 50% dropout\n        base_img = np.ones((512, 512, 1), dtype=np.uint8) * 255\n        images = np.array([base_img])\n        images_list = [base_img]\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n                                          ia.Keypoint(x=2, y=2)], shape=base_img.shape)]\n\n        aug = iaa.Dropout(p=0.5)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        assert not np.array_equal(observed, images)\n        percent_nonzero = len(observed.flatten().nonzero()[0]) \\\n                          / (base_img.shape[0] * base_img.shape[1] * base_img.shape[2])\n        assert 0.35 <= (1 - percent_nonzero) <= 0.65\n\n        observed = aug_det.augment_images(images)\n        assert not np.array_equal(observed, images)\n        percent_nonzero = len(observed.flatten().nonzero()[0]) \\\n                          / (base_img.shape[0] * base_img.shape[1] * base_img.shape[2])\n        assert 0.35 <= (1 - percent_nonzero) <= 0.65\n\n        observed = aug.augment_images(images_list)\n        assert not array_equal_lists(observed, images_list)\n        percent_nonzero = len(observed[0].flatten().nonzero()[0]) \\\n                          / (base_img.shape[0] * base_img.shape[1] * base_img.shape[2])\n        assert 0.35 <= (1 - percent_nonzero) <= 0.65\n\n        observed = aug_det.augment_images(images_list)\n        assert not array_equal_lists(observed, images_list)\n        percent_nonzero = len(observed[0].flatten().nonzero()[0]) \\\n                          / (base_img.shape[0] * base_img.shape[1] * base_img.shape[2])\n        assert 0.35 <= (1 - percent_nonzero) <= 0.65\n\n        observed = aug.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints)\n\n    def test_tuple_as_p(self):\n        # varying p\n        aug = iaa.Dropout(p=(0.0, 1.0))\n        aug_det = aug.to_deterministic()\n        images = np.ones((1, 8, 8, 1), dtype=np.uint8) * 255\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n        assert nb_changed_aug >= int(nb_iterations * 0.95)\n        assert nb_changed_aug_det == 0\n\n    def test_list_as_p(self):\n        aug = iaa.Dropout(p=[0.0, 0.5, 1.0])\n        images = np.ones((1, 20, 20, 1), dtype=np.uint8) * 255\n        nb_seen = [0, 0, 0, 0]\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n\n            n_dropped = np.sum(observed_aug == 0)\n            p_observed = n_dropped / observed_aug.size\n            if 0 <= p_observed <= 0.01:\n                nb_seen[0] += 1\n            elif 0.5 - 0.05 <= p_observed <= 0.5 + 0.05:\n                nb_seen[1] += 1\n            elif 1.0-0.01 <= p_observed <= 1.0:\n                nb_seen[2] += 1\n            else:\n                nb_seen[3] += 1\n        assert np.allclose(nb_seen[0:3], nb_iterations*0.33, rtol=0, atol=75)\n        assert nb_seen[3] < 30\n\n    def test_stochastic_parameter_as_p(self):\n        # varying p by stochastic parameter\n        aug = iaa.Dropout(p=iap.Binomial(1-iap.Choice([0.0, 0.5])))\n        images = np.ones((1, 20, 20, 1), dtype=np.uint8) * 255\n        seen = [0, 0, 0]\n        for i in sm.xrange(400):\n            observed = aug.augment_images(images)\n            p = np.mean(observed == 0)\n            if 0.4 < p < 0.6:\n                seen[0] += 1\n            elif p < 0.1:\n                seen[1] += 1\n            else:\n                seen[2] += 1\n        assert seen[2] <= 10\n        assert 150 < seen[0] < 250\n        assert 150 < seen[1] < 250\n\n    def test___init___bad_datatypes(self):\n        # test exception for wrong parameter datatype\n        got_exception = False\n        try:\n            _aug = iaa.Dropout(p=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.Dropout(p=1.0)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_pickleable(self):\n        aug = iaa.Dropout(p=0.5, per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\nclass TestCoarseDropout(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_p_is_zero(self):\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 100\n        aug = iaa.CoarseDropout(p=0, size_px=4, size_percent=None, per_channel=False, min_size=4)\n        observed = aug.augment_image(base_img)\n        expected = base_img\n        assert np.array_equal(observed, expected)\n\n    def test_p_is_one(self):\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 100\n        aug = iaa.CoarseDropout(p=1.0, size_px=4, size_percent=None, per_channel=False, min_size=4)\n        observed = aug.augment_image(base_img)\n        expected = np.zeros_like(base_img)\n        assert np.array_equal(observed, expected)\n\n    def test_p_is_50_percent(self):\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 100\n        aug = iaa.CoarseDropout(p=0.5, size_px=1, size_percent=None, per_channel=False, min_size=1)\n        averages = []\n        for _ in sm.xrange(50):\n            observed = aug.augment_image(base_img)\n            averages.append(np.average(observed))\n        assert all([v in [0, 100] for v in averages])\n        assert 50 - 20 < np.average(averages) < 50 + 20\n\n    def test_size_percent(self):\n        base_img = np.ones((16, 16, 1), dtype=np.uint8) * 100\n        aug = iaa.CoarseDropout(p=0.5, size_px=None, size_percent=0.001, per_channel=False, min_size=1)\n        averages = []\n        for _ in sm.xrange(50):\n            observed = aug.augment_image(base_img)\n            averages.append(np.average(observed))\n        assert all([v in [0, 100] for v in averages])\n        assert 50 - 20 < np.average(averages) < 50 + 20\n\n    def test_per_channel(self):\n        aug = iaa.CoarseDropout(p=0.5, size_px=1, size_percent=None, per_channel=True, min_size=1)\n        base_img = np.ones((4, 4, 3), dtype=np.uint8) * 100\n        found = False\n        for _ in sm.xrange(100):\n            observed = aug.augment_image(base_img)\n            avgs = np.average(observed, axis=(0, 1))\n            if len(set(avgs)) >= 2:\n                found = True\n                break\n        assert found\n\n    def test_stochastic_parameter_as_p(self):\n        # varying p by stochastic parameter\n        aug = iaa.CoarseDropout(p=iap.Binomial(1-iap.Choice([0.0, 0.5])), size_px=50)\n        images = np.ones((1, 100, 100, 1), dtype=np.uint8) * 255\n        seen = [0, 0, 0]\n        for i in sm.xrange(400):\n            observed = aug.augment_images(images)\n            p = np.mean(observed == 0)\n            if 0.4 < p < 0.6:\n                seen[0] += 1\n            elif p < 0.1:\n                seen[1] += 1\n            else:\n                seen[2] += 1\n        assert seen[2] <= 10\n        assert 150 < seen[0] < 250\n        assert 150 < seen[1] < 250\n\n    def test___init___bad_datatypes(self):\n        # test exception for bad parameters\n        got_exception = False\n        try:\n            _ = iaa.CoarseDropout(p=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test___init___size_px_and_size_percent_both_none(self):\n        aug = iaa.CoarseDropout(p=0.5, size_px=None, size_percent=None)\n        assert np.isclose(aug.mul.size_px.a.value, 3)\n        assert np.isclose(aug.mul.size_px.b.value, 8)\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.CoarseDropout(p=1.0, size_px=2)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_pickleable(self):\n        aug = iaa.CoarseDropout(p=0.5, size_px=10, per_channel=True,\n                                seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(40, 40, 3))\n\n\nclass TestDropout2d(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___defaults(self):\n        aug = iaa.Dropout2d()\n        assert is_parameter_instance(aug.p, iap.Binomial)\n        assert np.isclose(aug.p.p.value, 1-0.1)\n        assert aug.nb_keep_channels == 1\n\n    def test___init___p_is_float(self):\n        aug = iaa.Dropout2d(p=0.7)\n        assert is_parameter_instance(aug.p, iap.Binomial)\n        assert np.isclose(aug.p.p.value, 0.3)\n        assert aug.nb_keep_channels == 1\n\n    def test___init___nb_keep_channels_is_int(self):\n        aug = iaa.Dropout2d(p=0, nb_keep_channels=2)\n        assert is_parameter_instance(aug.p, iap.Binomial)\n        assert np.isclose(aug.p.p.value, 1.0)\n        assert aug.nb_keep_channels == 2\n\n    def test_no_images_in_batch(self):\n        aug = iaa.Dropout2d(p=0.0, nb_keep_channels=0)\n        heatmaps = np.float32([\n            [0.0, 1.0],\n            [0.0, 1.0]\n        ])\n        heatmaps = ia.HeatmapsOnImage(heatmaps, shape=(2, 2, 3))\n\n        heatmaps_aug = aug(heatmaps=heatmaps)\n\n        assert np.allclose(heatmaps_aug.arr_0to1, heatmaps.arr_0to1)\n\n    def test_p_is_1(self):\n        image = np.full((1, 2, 3), 255, dtype=np.uint8)\n        aug = iaa.Dropout2d(p=1.0, nb_keep_channels=0)\n\n        image_aug = aug(image=image)\n\n        assert image_aug.shape == image.shape\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.sum(image_aug) == 0\n\n    def test_p_is_1_heatmaps(self):\n        aug = iaa.Dropout2d(p=1.0, nb_keep_channels=0)\n        arr = np.float32([\n            [0.0, 1.0],\n            [0.0, 1.0]\n        ])\n        hm = ia.HeatmapsOnImage(arr, shape=(2, 2, 3))\n\n        heatmaps_aug = aug(heatmaps=hm)\n\n        assert np.allclose(heatmaps_aug.arr_0to1, 0.0)\n\n    def test_p_is_1_segmentation_maps(self):\n        aug = iaa.Dropout2d(p=1.0, nb_keep_channels=0)\n        arr = np.int32([\n            [0, 1],\n            [0, 1]\n        ])\n        segmaps = ia.SegmentationMapsOnImage(arr, shape=(2, 2, 3))\n\n        segmaps_aug = aug(segmentation_maps=segmaps)\n\n        assert np.allclose(segmaps_aug.arr, 0.0)\n\n    def test_p_is_1_cbaois(self):\n        cbaois = [\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=1)], shape=(2, 2, 3)),\n            ia.BoundingBoxesOnImage([ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)],\n                                    shape=(2, 2, 3)),\n            ia.PolygonsOnImage([ia.Polygon([(0, 0), (1, 0), (1, 1)])],\n                               shape=(2, 2, 3)),\n            ia.LineStringsOnImage([ia.LineString([(0, 0), (1, 0)])],\n                                  shape=(2, 2, 3))\n        ]\n\n        cbaoi_names = [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                       \"line_strings\"]\n\n        aug = iaa.Dropout2d(p=1.0, nb_keep_channels=0)\n        for name, cbaoi in zip(cbaoi_names, cbaois):\n            with self.subTest(datatype=name):\n                cbaoi_aug = aug(**{name: cbaoi})\n\n                assert cbaoi_aug.shape == (2, 2, 3)\n                assert cbaoi_aug.items == []\n\n    def test_p_is_1_heatmaps__keep_one_channel(self):\n        aug = iaa.Dropout2d(p=1.0, nb_keep_channels=1)\n        arr = np.float32([\n            [0.0, 1.0],\n            [0.0, 1.0]\n        ])\n        hm = ia.HeatmapsOnImage(arr, shape=(2, 2, 3))\n\n        heatmaps_aug = aug(heatmaps=hm)\n\n        assert np.allclose(heatmaps_aug.arr_0to1, hm.arr_0to1)\n\n    def test_p_is_1_segmentation_maps__keep_one_channel(self):\n        aug = iaa.Dropout2d(p=1.0, nb_keep_channels=1)\n        arr = np.int32([\n            [0, 1],\n            [0, 1]\n        ])\n        segmaps = ia.SegmentationMapsOnImage(arr, shape=(2, 2, 3))\n\n        segmaps_aug = aug(segmentation_maps=segmaps)\n\n        assert np.allclose(segmaps_aug.arr, segmaps.arr)\n\n    def test_p_is_1_cbaois__keep_one_channel(self):\n        cbaois = [\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=1)], shape=(2, 2, 3)),\n            ia.BoundingBoxesOnImage([ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)],\n                                    shape=(2, 2, 3)),\n            ia.PolygonsOnImage([ia.Polygon([(0, 0), (1, 0), (1, 1)])],\n                               shape=(2, 2, 3)),\n            ia.LineStringsOnImage([ia.LineString([(0, 0), (1, 0)])],\n                                  shape=(2, 2, 3))\n        ]\n\n        cbaoi_names = [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                       \"line_strings\"]\n\n        aug = iaa.Dropout2d(p=1.0, nb_keep_channels=1)\n        for name, cbaoi in zip(cbaoi_names, cbaois):\n            with self.subTest(datatype=name):\n                cbaoi_aug = aug(**{name: cbaoi})\n\n                assert cbaoi_aug.shape == (2, 2, 3)\n                assert np.allclose(\n                    cbaoi_aug.items[0].coords,\n                    cbaoi.items[0].coords\n                )\n\n    def test_p_is_0(self):\n        image = np.full((1, 2, 3), 255, dtype=np.uint8)\n        aug = iaa.Dropout2d(p=0.0, nb_keep_channels=0)\n\n        image_aug = aug(image=image)\n\n        assert image_aug.shape == image.shape\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.array_equal(image_aug, image)\n\n    def test_p_is_0_heatmaps(self):\n        aug = iaa.Dropout2d(p=0.0, nb_keep_channels=0)\n        arr = np.float32([\n            [0.0, 1.0],\n            [0.0, 1.0]\n        ])\n        hm = ia.HeatmapsOnImage(arr, shape=(2, 2, 3))\n\n        heatmaps_aug = aug(heatmaps=hm)\n\n        assert np.allclose(heatmaps_aug.arr_0to1, hm.arr_0to1)\n\n    def test_p_is_0_segmentation_maps(self):\n        aug = iaa.Dropout2d(p=0.0, nb_keep_channels=0)\n        arr = np.int32([\n            [0, 1],\n            [0, 1]\n        ])\n        segmaps = ia.SegmentationMapsOnImage(arr, shape=(2, 2, 3))\n\n        segmaps_aug = aug(segmentation_maps=segmaps)\n\n        assert np.allclose(segmaps_aug.arr, segmaps.arr)\n\n    def test_p_is_0_cbaois(self):\n        cbaois = [\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=1)], shape=(2, 2, 3)),\n            ia.BoundingBoxesOnImage([ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)],\n                                    shape=(2, 2, 3)),\n            ia.PolygonsOnImage([ia.Polygon([(0, 0), (1, 0), (1, 1)])],\n                               shape=(2, 2, 3)),\n            ia.LineStringsOnImage([ia.LineString([(0, 0), (1, 0)])],\n                                  shape=(2, 2, 3))\n        ]\n\n        cbaoi_names = [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                       \"line_strings\"]\n\n        aug = iaa.Dropout2d(p=0.0, nb_keep_channels=0)\n        for name, cbaoi in zip(cbaoi_names, cbaois):\n            with self.subTest(datatype=name):\n                cbaoi_aug = aug(**{name: cbaoi})\n\n                assert cbaoi_aug.shape == (2, 2, 3)\n                assert np.allclose(\n                    cbaoi_aug.items[0].coords,\n                    cbaoi.items[0].coords\n                )\n\n    def test_p_is_075(self):\n        image = np.full((1, 1, 3000), 255, dtype=np.uint8)\n        aug = iaa.Dropout2d(p=0.75, nb_keep_channels=0)\n\n        image_aug = aug(image=image)\n\n        nb_kept = np.sum(image_aug == 255)\n        nb_dropped = image.shape[2] - nb_kept\n        assert image_aug.shape == image.shape\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.isclose(nb_dropped, image.shape[2]*0.75, atol=75)\n\n    def test_force_nb_keep_channels(self):\n        image = np.full((1, 1, 3), 255, dtype=np.uint8)\n        images = np.array([image] * 1000)\n        aug = iaa.Dropout2d(p=1.0, nb_keep_channels=1)\n\n        images_aug = aug(images=images)\n\n        ids_kept = [np.nonzero(image[0, 0, :]) for image in images_aug]\n        ids_kept_uq = np.unique(ids_kept)\n        nb_kept = np.sum(images_aug == 255)\n        nb_dropped = (len(images) * images.shape[3]) - nb_kept\n\n        assert images_aug.shape == images.shape\n        assert images_aug.dtype.name == images.dtype.name\n\n        # on average, keep 1 of 3 channels\n        # due to p=1.0 we expect to get exactly 2/3 dropped\n        assert np.isclose(nb_dropped,\n                          (len(images)*images.shape[3])*(2/3), atol=1)\n\n        # every channel dropped at least once, i.e. which one is kept is random\n        assert sorted(ids_kept_uq.tolist()) == [0, 1, 2]\n\n    def test_some_images_below_nb_keep_channels(self):\n        image_2c = np.full((1, 1, 2), 255, dtype=np.uint8)\n        image_3c = np.full((1, 1, 3), 255, dtype=np.uint8)\n        images = [image_2c if i % 2 == 0 else image_3c\n                  for i in sm.xrange(100)]\n        aug = iaa.Dropout2d(p=1.0, nb_keep_channels=2)\n\n        images_aug = aug(images=images)\n\n        for i, image_aug in enumerate(images_aug):\n            assert np.sum(image_aug == 255) == 2\n            if i % 2 == 0:\n                assert np.sum(image_aug == 0) == 0\n            else:\n                assert np.sum(image_aug == 0) == 1\n\n    def test_all_images_below_nb_keep_channels(self):\n        image = np.full((1, 1, 2), 255, dtype=np.uint8)\n        images = np.array([image] * 100)\n        aug = iaa.Dropout2d(p=1.0, nb_keep_channels=3)\n\n        images_aug = aug(images=images)\n\n        nb_kept = np.sum(images_aug == 255)\n        nb_dropped = (len(images) * images.shape[3]) - nb_kept\n        assert nb_dropped == 0\n\n    def test_get_parameters(self):\n        aug = iaa.Dropout2d(p=0.7, nb_keep_channels=2)\n        params = aug.get_parameters()\n        is_parameter_instance(params[0], iap.Binomial)\n        assert np.isclose(params[0].p.value, 0.3)\n        assert params[1] == 2\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 255, dtype=np.uint8)\n                aug = iaa.Dropout2d(1.0, nb_keep_channels=0)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_other_dtypes_bool(self):\n        image = np.full((1, 1, 10), 1, dtype=bool)\n        aug = iaa.Dropout2d(p=1.0, nb_keep_channels=3)\n\n        image_aug = aug(image=image)\n\n        assert image_aug.shape == image.shape\n        assert image_aug.dtype.name == \"bool\"\n        assert np.sum(image_aug == 1) == 3\n        assert np.sum(image_aug == 0) == 7\n\n    def test_other_dtypes_uint_int(self):\n        dts = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n               \"int8\", \"int16\", \"int32\", \"int64\"]\n\n        for dt in dts:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dt)\n            values = [min_value, int(center_value), max_value]\n\n            for value in values:\n                with self.subTest(dtype=dt, value=value):\n                    image = np.full((1, 1, 10), value, dtype=dt)\n                    aug = iaa.Dropout2d(p=1.0, nb_keep_channels=3)\n\n                    image_aug = aug(image=image)\n\n                    assert image_aug.shape == image.shape\n                    assert image_aug.dtype.name == dt\n                    if value == 0:\n                        assert np.sum(image_aug == value) == 10\n                    else:\n                        assert np.sum(image_aug == value) == 3\n                        assert np.sum(image_aug == 0) == 7\n\n    def test_other_dtypes_float(self):\n        try:\n            high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n        except AttributeError:\n            high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n\n        for dt in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dt)\n            values = [min_value, -10.0, center_value, 10.0, max_value]\n\n            atol = 1e-3*max_value if dt == \"float16\" else 1e-9 * max_value\n            _isclose = functools.partial(np.isclose, atol=atol, rtol=0)\n\n            for value in values:\n                with self.subTest(dtype=dt, value=value):\n                    image = np.full((1, 1, 10), value, dtype=dt)\n                    aug = iaa.Dropout2d(p=1.0, nb_keep_channels=3)\n\n                    image_aug = aug(image=image)\n\n                    assert image_aug.shape == image.shape\n                    assert image_aug.dtype.name == dt\n                    if _isclose(value, 0.0):\n                        assert np.sum(_isclose(image_aug, value)) == 10\n                    else:\n                        assert (\n                            np.sum(_isclose(image_aug, high_res_dt(value)))\n                            == 3)\n                        assert np.sum(image_aug == 0) == 7\n\n    def test_pickleable(self):\n        aug = iaa.Dropout2d(p=0.5, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3, shape=(1, 1, 50))\n\n\nclass TestTotalDropout(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___p(self):\n        aug = iaa.TotalDropout(p=0)\n        assert is_parameter_instance(aug.p, iap.Binomial)\n        assert np.isclose(aug.p.p.value, 1.0)\n\n    def test_p_is_1(self):\n        image = np.full((1, 2, 3), 255, dtype=np.uint8)\n        aug = iaa.TotalDropout(p=1.0)\n\n        image_aug = aug(image=image)\n\n        assert image_aug.shape == image.shape\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.sum(image_aug) == 0\n\n    def test_p_is_1_multiple_images_list(self):\n        image = np.full((1, 2, 3), 255, dtype=np.uint8)\n        images = [image, image, image]\n        aug = iaa.TotalDropout(p=1.0)\n\n        images_aug = aug(images=images)\n\n        for image_aug, image_ in zip(images_aug, images):\n            assert image_aug.shape == image_.shape\n            assert image_aug.dtype.name == image_.dtype.name\n            assert np.sum(image_aug) == 0\n\n    def test_p_is_1_multiple_images_array(self):\n        image = np.full((1, 2, 3), 255, dtype=np.uint8)\n        images = np.array([image, image, image], dtype=np.uint8)\n        aug = iaa.TotalDropout(p=1.0)\n\n        images_aug = aug(images=images)\n\n        assert images_aug.shape == images.shape\n        assert images_aug.dtype.name == images.dtype.name\n        assert np.sum(images_aug) == 0\n\n    def test_p_is_1_heatmaps(self):\n        aug = iaa.TotalDropout(p=1.0)\n        arr = np.float32([\n            [0.0, 1.0],\n            [0.0, 1.0]\n        ])\n        hm = ia.HeatmapsOnImage(arr, shape=(2, 2, 3))\n\n        heatmaps_aug = aug(heatmaps=hm)\n\n        assert np.allclose(heatmaps_aug.arr_0to1, 0.0)\n\n    def test_p_is_1_segmentation_maps(self):\n        aug = iaa.TotalDropout(p=1.0)\n        arr = np.int32([\n            [0, 1],\n            [0, 1]\n        ])\n        segmaps = ia.SegmentationMapsOnImage(arr, shape=(2, 2, 3))\n\n        segmaps_aug = aug(segmentation_maps=segmaps)\n\n        assert np.allclose(segmaps_aug.arr, 0.0)\n\n    def test_p_is_1_cbaois(self):\n        cbaois = [\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=1)], shape=(2, 2, 3)),\n            ia.BoundingBoxesOnImage([ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)],\n                                    shape=(2, 2, 3)),\n            ia.PolygonsOnImage([ia.Polygon([(0, 0), (1, 0), (1, 1)])],\n                               shape=(2, 2, 3)),\n            ia.LineStringsOnImage([ia.LineString([(0, 0), (1, 0)])],\n                                  shape=(2, 2, 3))\n        ]\n\n        cbaoi_names = [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                       \"line_strings\"]\n\n        aug = iaa.TotalDropout(p=1.0)\n        for name, cbaoi in zip(cbaoi_names, cbaois):\n            with self.subTest(datatype=name):\n                cbaoi_aug = aug(**{name: cbaoi})\n\n                assert cbaoi_aug.shape == (2, 2, 3)\n                assert cbaoi_aug.items == []\n\n    def test_p_is_0(self):\n        image = np.full((1, 2, 3), 255, dtype=np.uint8)\n        aug = iaa.TotalDropout(p=0.0)\n\n        image_aug = aug(image=image)\n\n        assert image_aug.shape == image.shape\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.array_equal(image_aug, image)\n\n    def test_p_is_0_multiple_images_list(self):\n        image = np.full((1, 2, 3), 255, dtype=np.uint8)\n        images = [image, image, image]\n        aug = iaa.TotalDropout(p=0.0)\n\n        images_aug = aug(images=images)\n\n        for image_aug, image_ in zip(images_aug, images):\n            assert image_aug.shape == image_.shape\n            assert image_aug.dtype.name == image_.dtype.name\n            assert np.array_equal(image_aug, image_)\n\n    def test_p_is_0_multiple_images_array(self):\n        image = np.full((1, 2, 3), 255, dtype=np.uint8)\n        images = np.array([image, image, image], dtype=np.uint8)\n        aug = iaa.TotalDropout(p=0.0)\n\n        images_aug = aug(images=images)\n\n        for image_aug, image_ in zip(images_aug, images):\n            assert image_aug.shape == image_.shape\n            assert image_aug.dtype.name == image_.dtype.name\n            assert np.array_equal(image_aug, image_)\n\n    def test_p_is_0_heatmaps(self):\n        aug = iaa.TotalDropout(p=0.0)\n        arr = np.float32([\n            [0.0, 1.0],\n            [0.0, 1.0]\n        ])\n        hm = ia.HeatmapsOnImage(arr, shape=(2, 2, 3))\n\n        heatmaps_aug = aug(heatmaps=hm)\n\n        assert np.allclose(heatmaps_aug.arr_0to1, hm.arr_0to1)\n\n    def test_p_is_0_segmentation_maps(self):\n        aug = iaa.TotalDropout(p=0.0)\n        arr = np.int32([\n            [0, 1],\n            [0, 1]\n        ])\n        segmaps = ia.SegmentationMapsOnImage(arr, shape=(2, 2, 3))\n\n        segmaps_aug = aug(segmentation_maps=segmaps)\n\n        assert np.allclose(segmaps_aug.arr, segmaps.arr)\n\n    def test_p_is_0_cbaois(self):\n        cbaois = [\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=1)], shape=(2, 2, 3)),\n            ia.BoundingBoxesOnImage([ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)],\n                                    shape=(2, 2, 3)),\n            ia.PolygonsOnImage([ia.Polygon([(0, 0), (1, 0), (1, 1)])],\n                               shape=(2, 2, 3)),\n            ia.LineStringsOnImage([ia.LineString([(0, 0), (1, 0)])],\n                                  shape=(2, 2, 3))\n        ]\n\n        cbaoi_names = [\"keypoints\", \"bounding_boxes\", \"polygons\",\n                       \"line_strings\"]\n\n        aug = iaa.TotalDropout(p=0.0)\n        for name, cbaoi in zip(cbaoi_names, cbaois):\n            with self.subTest(datatype=name):\n                cbaoi_aug = aug(**{name: cbaoi})\n\n                assert cbaoi_aug.shape == (2, 2, 3)\n                assert np.allclose(\n                    cbaoi_aug.items[0].coords,\n                    cbaoi.items[0].coords\n                )\n\n    def test_p_is_075_multiple_images_list(self):\n        images = [np.full((1, 1, 1), 255, dtype=np.uint8)] * 3000\n        aug = iaa.TotalDropout(p=0.75)\n\n        images_aug = aug(images=images)\n\n        nb_kept = np.sum([np.sum(image_aug == 255) for image_aug in images_aug])\n        nb_dropped = len(images) - nb_kept\n        for image_aug in images_aug:\n            assert image_aug.shape == images[0].shape\n            assert image_aug.dtype.name == images[0].dtype.name\n        assert np.isclose(nb_dropped, len(images)*0.75, atol=75)\n\n    def test_p_is_075_multiple_images_array(self):\n        images = np.full((3000, 1, 1, 1), 255, dtype=np.uint8)\n        aug = iaa.TotalDropout(p=0.75)\n\n        images_aug = aug(images=images)\n\n        nb_kept = np.sum(images_aug == 255)\n        nb_dropped = len(images) - nb_kept\n        assert images_aug.shape == images.shape\n        assert images_aug.dtype.name == images.dtype.name\n        assert np.isclose(nb_dropped, len(images)*0.75, atol=75)\n\n    def test_get_parameters(self):\n        aug = iaa.TotalDropout(p=0.0)\n        params = aug.get_parameters()\n        assert params[0] is aug.p\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (5, 1, 1, 4),\n            (5, 1, 1, 5),\n            (5, 1, 1, 512),\n            (5, 1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                images = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.TotalDropout(1.0)\n\n                images_aug = aug(images=images)\n\n                assert np.all(images_aug == 0)\n                assert images_aug.dtype.name == \"uint8\"\n                assert images_aug.shape == shape\n\n    def test_zero_sized_axes(self):\n        with assertWarns(self, iaa.SuspiciousMultiImageShapeWarning):\n            shapes = [\n                (5, 0, 0),\n                (5, 0, 1),\n                (5, 1, 0),\n                (5, 0, 1, 0),\n                (5, 1, 0, 0),\n                (5, 0, 1, 1),\n                (5, 1, 0, 1)\n            ]\n\n            for shape in shapes:\n                with self.subTest(shape=shape):\n                    images = np.full(shape, 255, dtype=np.uint8)\n                    aug = iaa.TotalDropout(1.0)\n\n                    images_aug = aug(images=images)\n\n                    assert images_aug.dtype.name == \"uint8\"\n                    assert images_aug.shape == images.shape\n\n    def test_other_dtypes_bool(self):\n        image = np.full((1, 1, 10), 1, dtype=bool)\n        aug = iaa.TotalDropout(p=1.0)\n\n        image_aug = aug(image=image)\n\n        assert image_aug.shape == image.shape\n        assert image_aug.dtype.name == \"bool\"\n        assert np.sum(image_aug == 1) == 0\n\n    def test_other_dtypes_uint_int(self):\n        dts = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n               \"int8\", \"int16\", \"int32\", \"int64\"]\n\n        for dt in dts:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dt)\n            values = [min_value, int(center_value), max_value]\n\n            for value in values:\n                for p in [1.0, 0.0]:\n                    with self.subTest(dtype=dt, value=value, p=p):\n                        images = np.full((5, 1, 1, 3), value, dtype=dt)\n                        aug = iaa.TotalDropout(p=p)\n\n                        images_aug = aug(images=images)\n\n                        assert images_aug.shape == images.shape\n                        assert images_aug.dtype.name == dt\n                        if np.isclose(p, 1.0) or value == 0:\n                            assert np.sum(images_aug == 0) == 5*3\n                        else:\n                            assert np.sum(images_aug == value) == 5*3\n\n    def test_other_dtypes_float(self):\n        try:\n            high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n        except AttributeError:\n            high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n\n        for dt in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dt)\n            values = [min_value, -10.0, center_value, 10.0, max_value]\n\n            atol = 1e-3*max_value if dt == \"float16\" else 1e-9 * max_value\n            _isclose = functools.partial(np.isclose, atol=atol, rtol=0)\n\n            for value in values:\n                for p in [1.0, 0.0]:\n                    with self.subTest(dtype=dt, value=value, p=p):\n                        images = np.full((5, 1, 1, 3), value, dtype=dt)\n                        aug = iaa.TotalDropout(p=p)\n\n                        images_aug = aug(images=images)\n\n                        assert images_aug.shape == images.shape\n                        assert images_aug.dtype.name == dt\n                        if np.isclose(p, 1.0):\n                            assert np.sum(_isclose(images_aug, 0.0)) == 5*3\n                        else:\n                            assert (\n                                np.sum(_isclose(images_aug, high_res_dt(value)))\n                                == 5*3)\n\n    def test_pickleable(self):\n        aug = iaa.TotalDropout(p=0.5, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=30, shape=(4, 4, 2))\n\n\nclass TestMultiply(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_mul_is_one(self):\n        # no multiply, shouldnt change anything\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.Multiply(mul=1.0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n    def test_mul_is_above_one(self):\n        # multiply >1.0\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.Multiply(mul=1.2)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = np.ones((1, 3, 3, 1), dtype=np.uint8) * 120\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = [np.ones((3, 3, 1), dtype=np.uint8) * 120]\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = np.ones((1, 3, 3, 1), dtype=np.uint8) * 120\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = [np.ones((3, 3, 1), dtype=np.uint8) * 120]\n        assert array_equal_lists(observed, expected)\n\n    def test_mul_is_below_one(self):\n        # multiply <1.0\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.Multiply(mul=0.8)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = np.ones((1, 3, 3, 1), dtype=np.uint8) * 80\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = [np.ones((3, 3, 1), dtype=np.uint8) * 80]\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = np.ones((1, 3, 3, 1), dtype=np.uint8) * 80\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = [np.ones((3, 3, 1), dtype=np.uint8) * 80]\n        assert array_equal_lists(observed, expected)\n\n    def test_keypoints_dont_change(self):\n        # keypoints shouldnt be changed\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n                                          ia.Keypoint(x=2, y=2)], shape=base_img.shape)]\n\n        aug = iaa.Multiply(mul=1.2)\n        aug_det = iaa.Multiply(mul=1.2).to_deterministic()\n        observed = aug.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n    def test_tuple_as_mul(self):\n        # varying multiply factors\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n\n        aug = iaa.Multiply(mul=(0, 2.0))\n        aug_det = aug.to_deterministic()\n\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n        assert nb_changed_aug >= int(nb_iterations * 0.95)\n        assert nb_changed_aug_det == 0\n\n    def test_per_channel(self):\n        aug = iaa.Multiply(mul=iap.Choice([0, 2]), per_channel=True)\n        observed = aug.augment_image(np.ones((1, 1, 100), dtype=np.uint8))\n        uq = np.unique(observed)\n        assert observed.shape == (1, 1, 100)\n        assert 0 in uq\n        assert 2 in uq\n        assert len(uq) == 2\n\n    def test_per_channel_with_probability(self):\n        # test channelwise with probability\n        aug = iaa.Multiply(mul=iap.Choice([0, 2]), per_channel=0.5)\n        seen = [0, 0]\n        for _ in sm.xrange(400):\n            observed = aug.augment_image(np.ones((1, 1, 20), dtype=np.uint8))\n            assert observed.shape == (1, 1, 20)\n\n            uq = np.unique(observed)\n            per_channel = (len(uq) == 2)\n            if per_channel:\n                seen[0] += 1\n            else:\n                seen[1] += 1\n        assert 150 < seen[0] < 250\n        assert 150 < seen[1] < 250\n\n    def test___init___bad_datatypes(self):\n        # test exceptions for wrong parameter types\n        got_exception = False\n        try:\n            _ = iaa.Multiply(mul=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        got_exception = False\n        try:\n            _ = iaa.Multiply(mul=1, per_channel=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.ones(shape, dtype=np.uint8)\n                aug = iaa.Multiply(1)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 2)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.ones(shape, dtype=np.uint8)\n                aug = iaa.Multiply(2)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 2)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_get_parameters(self):\n        # test get_parameters()\n        aug = iaa.Multiply(mul=1, per_channel=False)\n        params = aug.get_parameters()\n        is_parameter_instance(params[0], iap.Deterministic)\n        is_parameter_instance(params[1], iap.Deterministic)\n        assert params[0].value == 1\n        assert params[1].value == 0\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.Multiply(mul=2)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_other_dtypes_bool(self):\n        # bool\n        image = np.zeros((3, 3), dtype=bool)\n        aug = iaa.Multiply(1.0)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.Multiply(1.0)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 1)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.Multiply(2.0)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 1)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.Multiply(0.0)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.Multiply(-1.0)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n    def test_other_dtypes_uint_int(self):\n        # uint, int\n        for dtype in [np.uint8, np.uint16, np.int8, np.int16]:\n            dtype = np.dtype(dtype)\n            min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n            image = np.full((3, 3), 10, dtype=dtype)\n            aug = iaa.Multiply(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == 10)\n\n            image = np.full((3, 3), 10, dtype=dtype)\n            aug = iaa.Multiply(10)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == 100)\n\n            image = np.full((3, 3), 10, dtype=dtype)\n            aug = iaa.Multiply(0.5)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == 5)\n\n            image = np.full((3, 3), 0, dtype=dtype)\n            aug = iaa.Multiply(0)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == 0)\n\n            if np.dtype(dtype).kind == \"u\":\n                image = np.full((3, 3), 10, dtype=dtype)\n                aug = iaa.Multiply(-1)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(image_aug == 0)\n            else:\n                image = np.full((3, 3), 10, dtype=dtype)\n                aug = iaa.Multiply(-1)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(image_aug == -10)\n\n            image = np.full((3, 3), int(center_value), dtype=dtype)\n            aug = iaa.Multiply(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == int(center_value))\n\n            image = np.full((3, 3), int(center_value), dtype=dtype)\n            aug = iaa.Multiply(1.2)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == int(1.2 * int(center_value)))\n\n            if np.dtype(dtype).kind == \"u\":\n                image = np.full((3, 3), int(center_value), dtype=dtype)\n                aug = iaa.Multiply(100)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(image_aug == max_value)\n\n            image = np.full((3, 3), max_value, dtype=dtype)\n            aug = iaa.Multiply(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == max_value)\n\n            # non-uint8 currently don't increase the itemsize\n            if dtype.name == \"uint8\":\n                image = np.full((3, 3), max_value, dtype=dtype)\n                aug = iaa.Multiply(10)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(image_aug == max_value)\n\n            image = np.full((3, 3), max_value, dtype=dtype)\n            aug = iaa.Multiply(0)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == 0)\n\n            # non-uint8 currently don't increase the itemsize\n            if dtype.name == \"uint8\":\n                image = np.full((3, 3), max_value, dtype=dtype)\n                aug = iaa.Multiply(-2)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(image_aug == min_value)\n\n            # non-uint8 currently don't increase the itemsize\n            if dtype.name == \"uint8\":\n                for _ in sm.xrange(10):\n                    image = np.full((1, 1, 3), 10, dtype=dtype)\n                    aug = iaa.Multiply(iap.Uniform(0.5, 1.5))\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.type == dtype\n                    assert np.all(np.logical_and(5 <= image_aug, image_aug <= 15))\n                    assert len(np.unique(image_aug)) == 1\n\n                    image = np.full((1, 1, 100), 10, dtype=dtype)\n                    aug = iaa.Multiply(iap.Uniform(0.5, 1.5), per_channel=True)\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.type == dtype\n                    assert np.all(np.logical_and(5 <= image_aug, image_aug <= 15))\n                    assert len(np.unique(image_aug)) > 1\n\n                    image = np.full((1, 1, 3), 10, dtype=dtype)\n                    aug = iaa.Multiply(iap.DiscreteUniform(1, 3))\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.type == dtype\n                    assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                    assert len(np.unique(image_aug)) == 1\n\n                    image = np.full((1, 1, 100), 10, dtype=dtype)\n                    aug = iaa.Multiply(iap.DiscreteUniform(1, 3), per_channel=True)\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.type == dtype\n                    assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                    assert len(np.unique(image_aug)) > 1\n\n    def test_other_dtypes_float(self):\n        # float\n        for dtype in [np.float16, np.float32]:\n            min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n            if dtype == np.float16:\n                atol = 1e-3 * max_value\n            else:\n                atol = 1e-9 * max_value\n            _allclose = functools.partial(np.allclose, atol=atol, rtol=0)\n\n            image = np.full((3, 3), 10.0, dtype=dtype)\n            aug = iaa.Multiply(1.0)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, 10.0)\n\n            image = np.full((3, 3), 10.0, dtype=dtype)\n            aug = iaa.Multiply(2.0)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, 20.0)\n\n            # deactivated, because itemsize increase was deactivated\n            # image = np.full((3, 3), max_value, dtype=dtype)\n            # aug = iaa.Multiply(-10)\n            # image_aug = aug.augment_image(image)\n            # assert image_aug.dtype.type == dtype\n            # assert _allclose(image_aug, min_value)\n\n            image = np.full((3, 3), max_value, dtype=dtype)\n            aug = iaa.Multiply(0.0)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, 0.0)\n\n            image = np.full((3, 3), max_value, dtype=dtype)\n            aug = iaa.Multiply(0.5)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, 0.5*max_value)\n\n            # deactivated, because itemsize increase was deactivated\n            # image = np.full((3, 3), min_value, dtype=dtype)\n            # aug = iaa.Multiply(-2.0)\n            # image_aug = aug.augment_image(image)\n            # assert image_aug.dtype.type == dtype\n            # assert _allclose(image_aug, max_value)\n\n            image = np.full((3, 3), min_value, dtype=dtype)\n            aug = iaa.Multiply(0.0)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, 0.0)\n\n            # using tolerances of -100 - 1e-2 and 100 + 1e-2 is not enough for float16, had to be increased to -/+ 1e-1\n            # deactivated, because itemsize increase was deactivated\n            \"\"\"\n            for _ in sm.xrange(10):\n                image = np.full((1, 1, 3), 10.0, dtype=dtype)\n                aug = iaa.Multiply(iap.Uniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-100 - 1e-1 < image_aug, image_aug < 100 + 1e-1))\n                assert np.allclose(image_aug[1:, :, 0], image_aug[:-1, :, 0])\n                assert np.allclose(image_aug[..., 0], image_aug[..., 1])\n\n                image = np.full((1, 1, 100), 10.0, dtype=dtype)\n                aug = iaa.Multiply(iap.Uniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-100 - 1e-1 < image_aug, image_aug < 100 + 1e-1))\n                assert not np.allclose(image_aug[:, :, 1:], image_aug[:, :, :-1])\n\n                image = np.full((1, 1, 3), 10.0, dtype=dtype)\n                aug = iaa.Multiply(iap.DiscreteUniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-100 - 1e-1 < image_aug, image_aug < 100 + 1e-1))\n                assert np.allclose(image_aug[1:, :, 0], image_aug[:-1, :, 0])\n                assert np.allclose(image_aug[..., 0], image_aug[..., 1])\n\n                image = np.full((1, 1, 100), 10.0, dtype=dtype)\n                aug = iaa.Multiply(iap.DiscreteUniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-100 - 1e-1 < image_aug, image_aug < 100 + 1e-1))\n                assert not np.allclose(image_aug[:, :, 1:], image_aug[:, :, :-1])\n            \"\"\"\n\n    def test_pickleable(self):\n        aug = iaa.Multiply((0.5, 1.5), per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\nclass TestMultiplyElementwise(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_mul_is_one(self):\n        # no multiply, shouldnt change anything\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.MultiplyElementwise(mul=1.0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n    def test_mul_is_above_one(self):\n        # multiply >1.0\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.MultiplyElementwise(mul=1.2)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = np.ones((1, 3, 3, 1), dtype=np.uint8) * 120\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = [np.ones((3, 3, 1), dtype=np.uint8) * 120]\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = np.ones((1, 3, 3, 1), dtype=np.uint8) * 120\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = [np.ones((3, 3, 1), dtype=np.uint8) * 120]\n        assert array_equal_lists(observed, expected)\n\n    def test_mul_is_below_one(self):\n        # multiply <1.0\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.MultiplyElementwise(mul=0.8)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = np.ones((1, 3, 3, 1), dtype=np.uint8) * 80\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = [np.ones((3, 3, 1), dtype=np.uint8) * 80]\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = np.ones((1, 3, 3, 1), dtype=np.uint8) * 80\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = [np.ones((3, 3, 1), dtype=np.uint8) * 80]\n        assert array_equal_lists(observed, expected)\n\n    def test_keypoints_dont_change(self):\n        # keypoints shouldnt be changed\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n                                          ia.Keypoint(x=2, y=2)], shape=base_img.shape)]\n\n        aug = iaa.MultiplyElementwise(mul=1.2)\n        aug_det = iaa.Multiply(mul=1.2).to_deterministic()\n        observed = aug.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n    def test_tuple_as_mul(self):\n        # varying multiply factors\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n\n        aug = iaa.MultiplyElementwise(mul=(0, 2.0))\n        aug_det = aug.to_deterministic()\n\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n        assert nb_changed_aug >= int(nb_iterations * 0.95)\n        assert nb_changed_aug_det == 0\n\n    def test_samples_change_by_spatial_location(self):\n        # values should change between pixels\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) * 100\n        images = np.array([base_img])\n\n        aug = iaa.MultiplyElementwise(mul=(0.5, 1.5))\n\n        nb_same = 0\n        nb_different = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_flat = observed_aug.flatten()\n            last = None\n            for j in sm.xrange(observed_aug_flat.size):\n                if last is not None:\n                    v = observed_aug_flat[j]\n                    if v - 0.0001 <= last <= v + 0.0001:\n                        nb_same += 1\n                    else:\n                        nb_different += 1\n                last = observed_aug_flat[j]\n        assert nb_different > 0.95 * (nb_different + nb_same)\n\n    def test_per_channel(self):\n        # test channelwise\n        aug = iaa.MultiplyElementwise(mul=iap.Choice([0, 1]), per_channel=True)\n        observed = aug.augment_image(np.ones((100, 100, 3), dtype=np.uint8))\n        sums = np.sum(observed, axis=2)\n        values = np.unique(sums)\n        assert all([(value in values) for value in [0, 1, 2, 3]])\n        assert observed.shape == (100, 100, 3)\n\n    def test_per_channel_with_probability(self):\n        # test channelwise with probability\n        aug = iaa.MultiplyElementwise(mul=iap.Choice([0, 1]), per_channel=0.5)\n        seen = [0, 0]\n        for _ in sm.xrange(400):\n            observed = aug.augment_image(np.ones((20, 20, 3), dtype=np.uint8))\n            assert observed.shape == (20, 20, 3)\n\n            sums = np.sum(observed, axis=2)\n            values = np.unique(sums)\n            all_values_found = all([(value in values) for value in [0, 1, 2, 3]])\n            if all_values_found:\n                seen[0] += 1\n            else:\n                seen[1] += 1\n        assert 150 < seen[0] < 250\n        assert 150 < seen[1] < 250\n\n    def test___init___bad_datatypes(self):\n        # test exceptions for wrong parameter types\n        got_exception = False\n        try:\n            _aug = iaa.MultiplyElementwise(mul=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        got_exception = False\n        try:\n            _aug = iaa.MultiplyElementwise(mul=1, per_channel=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.ones(shape, dtype=np.uint8)\n                aug = iaa.MultiplyElementwise(2)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 2)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.ones(shape, dtype=np.uint8)\n                aug = iaa.MultiplyElementwise(2)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 2)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_get_parameters(self):\n        # test get_parameters()\n        aug = iaa.MultiplyElementwise(mul=1, per_channel=False)\n        params = aug.get_parameters()\n        is_parameter_instance(params[0], iap.Deterministic)\n        is_parameter_instance(params[1], iap.Deterministic)\n        assert params[0].value == 1\n        assert params[1].value == 0\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.MultiplyElementwise(mul=2)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_other_dtypes_bool(self):\n        # bool\n        image = np.zeros((3, 3), dtype=bool)\n        aug = iaa.MultiplyElementwise(1.0)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.MultiplyElementwise(1.0)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 1)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.MultiplyElementwise(2.0)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 1)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.MultiplyElementwise(0.0)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n        image = np.full((3, 3), True, dtype=bool)\n        aug = iaa.MultiplyElementwise(-1.0)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n    def test_other_dtypes_uint_int(self):\n        # uint, int\n        for dtype in [np.uint8, np.uint16, np.int8, np.int16]:\n            dtype = np.dtype(dtype)\n            min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n            image = np.full((3, 3), 10, dtype=dtype)\n            aug = iaa.MultiplyElementwise(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == 10)\n\n            # deactivated, because itemsize increase was deactivated\n            # image = np.full((3, 3), 10, dtype=dtype)\n            # aug = iaa.MultiplyElementwise(10)\n            # image_aug = aug.augment_image(image)\n            # assert image_aug.dtype.type == dtype\n            # assert np.all(image_aug == 100)\n\n            image = np.full((3, 3), 10, dtype=dtype)\n            aug = iaa.MultiplyElementwise(0.5)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == 5)\n\n            image = np.full((3, 3), 0, dtype=dtype)\n            aug = iaa.MultiplyElementwise(0)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == 0)\n\n            # partially deactivated, because itemsize increase was deactivated\n            if dtype.name == \"uint8\":\n                if dtype.kind == \"u\":\n                    image = np.full((3, 3), 10, dtype=dtype)\n                    aug = iaa.MultiplyElementwise(-1)\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.type == dtype\n                    assert np.all(image_aug == 0)\n                else:\n                    image = np.full((3, 3), 10, dtype=dtype)\n                    aug = iaa.MultiplyElementwise(-1)\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.type == dtype\n                    assert np.all(image_aug == -10)\n\n            image = np.full((3, 3), int(center_value), dtype=dtype)\n            aug = iaa.MultiplyElementwise(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == int(center_value))\n\n            # deactivated, because itemsize increase was deactivated\n            # image = np.full((3, 3), int(center_value), dtype=dtype)\n            # aug = iaa.MultiplyElementwise(1.2)\n            # image_aug = aug.augment_image(image)\n            # assert image_aug.dtype.type == dtype\n            # assert np.all(image_aug == int(1.2 * int(center_value)))\n\n            # deactivated, because itemsize increase was deactivated\n            if dtype.name == \"uint8\":\n                if dtype.kind == \"u\":\n                    image = np.full((3, 3), int(center_value), dtype=dtype)\n                    aug = iaa.MultiplyElementwise(100)\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.type == dtype\n                    assert np.all(image_aug == max_value)\n\n            image = np.full((3, 3), max_value, dtype=dtype)\n            aug = iaa.MultiplyElementwise(1)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == max_value)\n\n            # deactivated, because itemsize increase was deactivated\n            # image = np.full((3, 3), max_value, dtype=dtype)\n            # aug = iaa.MultiplyElementwise(10)\n            # image_aug = aug.augment_image(image)\n            # assert image_aug.dtype.type == dtype\n            # assert np.all(image_aug == max_value)\n\n            image = np.full((3, 3), max_value, dtype=dtype)\n            aug = iaa.MultiplyElementwise(0)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == 0)\n\n            # deactivated, because itemsize increase was deactivated\n            # image = np.full((3, 3), max_value, dtype=dtype)\n            # aug = iaa.MultiplyElementwise(-2)\n            # image_aug = aug.augment_image(image)\n            # assert image_aug.dtype.type == dtype\n            # assert np.all(image_aug == min_value)\n\n            # partially deactivated, because itemsize increase was deactivated\n            if dtype.name == \"uint8\":\n                for _ in sm.xrange(10):\n                    image = np.full((5, 5, 3), 10, dtype=dtype)\n                    aug = iaa.MultiplyElementwise(iap.Uniform(0.5, 1.5))\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.type == dtype\n                    assert np.all(np.logical_and(5 <= image_aug, image_aug <= 15))\n                    assert len(np.unique(image_aug)) > 1\n                    assert np.all(image_aug[..., 0] == image_aug[..., 1])\n\n                    image = np.full((1, 1, 100), 10, dtype=dtype)\n                    aug = iaa.MultiplyElementwise(iap.Uniform(0.5, 1.5), per_channel=True)\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.type == dtype\n                    assert np.all(np.logical_and(5 <= image_aug, image_aug <= 15))\n                    assert len(np.unique(image_aug)) > 1\n\n                    image = np.full((5, 5, 3), 10, dtype=dtype)\n                    aug = iaa.MultiplyElementwise(iap.DiscreteUniform(1, 3))\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.type == dtype\n                    assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                    assert len(np.unique(image_aug)) > 1\n                    assert np.all(image_aug[..., 0] == image_aug[..., 1])\n\n                    image = np.full((1, 1, 100), 10, dtype=dtype)\n                    aug = iaa.MultiplyElementwise(iap.DiscreteUniform(1, 3), per_channel=True)\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.type == dtype\n                    assert np.all(np.logical_and(10 <= image_aug, image_aug <= 30))\n                    assert len(np.unique(image_aug)) > 1\n\n    def test_other_dtypes_float(self):\n        # float\n        for dtype in [np.float16, np.float32]:\n            dtype = np.dtype(dtype)\n            min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n            if dtype == np.float16:\n                atol = 1e-3 * max_value\n            else:\n                atol = 1e-9 * max_value\n            _allclose = functools.partial(np.allclose, atol=atol, rtol=0)\n\n            image = np.full((3, 3), 10.0, dtype=dtype)\n            aug = iaa.MultiplyElementwise(1.0)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, 10.0)\n\n            # deactivated, because itemsize increase was deactivated\n            # image = np.full((3, 3), 10.0, dtype=dtype)\n            # aug = iaa.MultiplyElementwise(2.0)\n            # image_aug = aug.augment_image(image)\n            # assert image_aug.dtype.type == dtype\n            # assert _allclose(image_aug, 20.0)\n\n            # deactivated, because itemsize increase was deactivated\n            # image = np.full((3, 3), max_value, dtype=dtype)\n            # aug = iaa.MultiplyElementwise(-10)\n            # image_aug = aug.augment_image(image)\n            # assert image_aug.dtype.type == dtype\n            # assert _allclose(image_aug, min_value)\n\n            image = np.full((3, 3), max_value, dtype=dtype)\n            aug = iaa.MultiplyElementwise(0.0)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, 0.0)\n\n            image = np.full((3, 3), max_value, dtype=dtype)\n            aug = iaa.MultiplyElementwise(0.5)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, 0.5*max_value)\n\n            # deactivated, because itemsize increase was deactivated\n            # image = np.full((3, 3), min_value, dtype=dtype)\n            # aug = iaa.MultiplyElementwise(-2.0)\n            # image_aug = aug.augment_image(image)\n            # assert image_aug.dtype.type == dtype\n            # assert _allclose(image_aug, max_value)\n\n            image = np.full((3, 3), min_value, dtype=dtype)\n            aug = iaa.MultiplyElementwise(0.0)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert _allclose(image_aug, 0.0)\n\n            # using tolerances of -100 - 1e-2 and 100 + 1e-2 is not enough for float16, had to be increased to -/+ 1e-1\n            # deactivated, because itemsize increase was deactivated\n            \"\"\"\n            for _ in sm.xrange(10):\n                image = np.full((50, 1, 3), 10.0, dtype=dtype)\n                aug = iaa.MultiplyElementwise(iap.Uniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-100 - 1e-1 < image_aug, image_aug < 100 + 1e-1))\n                assert not np.allclose(image_aug[1:, :, 0], image_aug[:-1, :, 0])\n                assert np.allclose(image_aug[..., 0], image_aug[..., 1])\n\n                image = np.full((1, 1, 100), 10.0, dtype=dtype)\n                aug = iaa.MultiplyElementwise(iap.Uniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-100 - 1e-1 < image_aug, image_aug < 100 + 1e-1))\n                assert not np.allclose(image_aug[:, :, 1:], image_aug[:, :, :-1])\n\n                image = np.full((50, 1, 3), 10.0, dtype=dtype)\n                aug = iaa.MultiplyElementwise(iap.DiscreteUniform(-10, 10))\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-100 - 1e-1 < image_aug, image_aug < 100 + 1e-1))\n                assert not np.allclose(image_aug[1:, :, 0], image_aug[:-1, :, 0])\n                assert np.allclose(image_aug[..., 0], image_aug[..., 1])\n\n                image = np.full((1, 1, 100), 10, dtype=dtype)\n                aug = iaa.MultiplyElementwise(iap.DiscreteUniform(-10, 10), per_channel=True)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(np.logical_and(-100 - 1e-1 < image_aug, image_aug < 100 + 1e-1))\n                assert not np.allclose(image_aug[:, :, 1:], image_aug[:, :, :-1])\n            \"\"\"\n\n    def test_pickleable(self):\n        aug = iaa.MultiplyElementwise((0.5, 1.5), per_channel=True,\n                                      seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\nclass TestReplaceElementwise(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_mask_is_always_zero(self):\n        # no replace, shouldnt change anything\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) + 99\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.ReplaceElementwise(mask=0, replacement=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n    def test_mask_is_always_one(self):\n        # replace at 100 percent prob., should change everything\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) + 99\n        images = np.array([base_img])\n        images_list = [base_img]\n\n        aug = iaa.ReplaceElementwise(mask=1, replacement=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = np.zeros((1, 3, 3, 1), dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n        assert observed.shape == (1, 3, 3, 1)\n\n        observed = aug.augment_images(images_list)\n        expected = [np.zeros((3, 3, 1), dtype=np.uint8)]\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = np.zeros((1, 3, 3, 1), dtype=np.uint8)\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = [np.zeros((3, 3, 1), dtype=np.uint8)]\n        assert array_equal_lists(observed, expected)\n\n    def test_mask_is_stochastic_parameter(self):\n        # replace half\n        aug = iaa.ReplaceElementwise(mask=iap.Binomial(p=0.5), replacement=0)\n        img = np.ones((100, 100, 1), dtype=np.uint8)\n\n        nb_iterations = 100\n        nb_diff_all = 0\n        for i in sm.xrange(nb_iterations):\n            observed = aug.augment_image(img)\n            nb_diff = np.sum(img != observed)\n            nb_diff_all += nb_diff\n        p = nb_diff_all / (nb_iterations * 100 * 100)\n        assert 0.45 <= p <= 0.55\n\n    def test_mask_is_list(self):\n        # mask is list\n        aug = iaa.ReplaceElementwise(mask=[0.2, 0.7], replacement=1)\n        img = np.zeros((20, 20, 1), dtype=np.uint8)\n\n        seen = [0, 0, 0]\n        for i in sm.xrange(400):\n            observed = aug.augment_image(img)\n            p = np.mean(observed)\n            if 0.1 < p < 0.3:\n                seen[0] += 1\n            elif 0.6 < p < 0.8:\n                seen[1] += 1\n            else:\n                seen[2] += 1\n        assert seen[2] <= 10\n        assert 150 < seen[0] < 250\n        assert 150 < seen[1] < 250\n\n    def test_keypoints_dont_change(self):\n        # keypoints shouldnt be changed\n        base_img = np.ones((3, 3, 1), dtype=np.uint8) + 99\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n                                          ia.Keypoint(x=2, y=2)], shape=base_img.shape)]\n\n        aug = iaa.ReplaceElementwise(mask=iap.Binomial(p=0.5), replacement=0)\n        aug_det = iaa.ReplaceElementwise(mask=iap.Binomial(p=0.5), replacement=0).to_deterministic()\n        observed = aug.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n    def test_replacement_is_stochastic_parameter(self):\n        # different replacements\n        aug = iaa.ReplaceElementwise(mask=1, replacement=iap.Choice([100, 200]))\n        img = np.zeros((1000, 1000, 1), dtype=np.uint8)\n        img100 = img + 100\n        img200 = img + 200\n        observed = aug.augment_image(img)\n        nb_diff_100 = np.sum(img100 != observed)\n        nb_diff_200 = np.sum(img200 != observed)\n        p100 = nb_diff_100 / (1000 * 1000)\n        p200 = nb_diff_200 / (1000 * 1000)\n        assert 0.45 <= p100 <= 0.55\n        assert 0.45 <= p200 <= 0.55\n        # test channelwise\n        aug = iaa.MultiplyElementwise(mul=iap.Choice([0, 1]), per_channel=True)\n        observed = aug.augment_image(np.ones((100, 100, 3), dtype=np.uint8))\n        sums = np.sum(observed, axis=2)\n        values = np.unique(sums)\n        assert all([(value in values) for value in [0, 1, 2, 3]])\n\n    def test_per_channel_with_probability(self):\n        # test channelwise with probability\n        aug = iaa.ReplaceElementwise(mask=iap.Choice([0, 1]), replacement=1, per_channel=0.5)\n        seen = [0, 0]\n        for _ in sm.xrange(400):\n            observed = aug.augment_image(np.zeros((20, 20, 3), dtype=np.uint8))\n            assert observed.shape == (20, 20, 3)\n\n            sums = np.sum(observed, axis=2)\n            values = np.unique(sums)\n            all_values_found = all([(value in values) for value in [0, 1, 2, 3]])\n            if all_values_found:\n                seen[0] += 1\n            else:\n                seen[1] += 1\n        assert 150 < seen[0] < 250\n        assert 150 < seen[1] < 250\n\n    def test___init___bad_datatypes(self):\n        # test exceptions for wrong parameter types\n        got_exception = False\n        try:\n            _aug = iaa.ReplaceElementwise(mask=\"test\", replacement=1)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        got_exception = False\n        try:\n            _aug = iaa.ReplaceElementwise(mask=1, replacement=1, per_channel=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.ReplaceElementwise(1.0, 1)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.ReplaceElementwise(1.0, 1)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_get_parameters(self):\n        # test get_parameters()\n        aug = iaa.ReplaceElementwise(mask=0.5, replacement=2, per_channel=False)\n        params = aug.get_parameters()\n        is_parameter_instance(params[0], iap.Binomial)\n        is_parameter_instance(params[0].p, iap.Deterministic)\n        is_parameter_instance(params[1], iap.Deterministic)\n        is_parameter_instance(params[2], iap.Deterministic)\n        assert 0.5 - 1e-6 < params[0].p.value < 0.5 + 1e-6\n        assert params[1].value == 2\n        assert params[2].value == 0\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.ReplaceElementwise(mask=1, replacement=0.5)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_other_dtypes_bool(self):\n        # bool\n        aug = iaa.ReplaceElementwise(mask=1, replacement=0)\n        image = np.full((3, 3), False, dtype=bool)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n        aug = iaa.ReplaceElementwise(mask=1, replacement=1)\n        image = np.full((3, 3), False, dtype=bool)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 1)\n\n        aug = iaa.ReplaceElementwise(mask=1, replacement=0)\n        image = np.full((3, 3), True, dtype=bool)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n        aug = iaa.ReplaceElementwise(mask=1, replacement=1)\n        image = np.full((3, 3), True, dtype=bool)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 1)\n\n        aug = iaa.ReplaceElementwise(mask=1, replacement=0.7)\n        image = np.full((3, 3), False, dtype=bool)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 1)\n\n        aug = iaa.ReplaceElementwise(mask=1, replacement=0.2)\n        image = np.full((3, 3), False, dtype=bool)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == 0)\n\n    def test_other_dtypes_uint_int(self):\n        # uint, int\n        for dtype in [np.uint8, np.uint16, np.uint32, np.int8, np.int16, np.int32]:\n            dtype = np.dtype(dtype)\n            min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n            aug = iaa.ReplaceElementwise(mask=1, replacement=1)\n            image = np.full((3, 3), 0, dtype=dtype)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == 1)\n\n            aug = iaa.ReplaceElementwise(mask=1, replacement=2)\n            image = np.full((3, 3), 1, dtype=dtype)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == 2)\n\n            # deterministic stochastic parameters are by default int32 for\n            # any integer value and hence cannot cover the full uint32 value\n            # range\n            if dtype.name != \"uint32\":\n                aug = iaa.ReplaceElementwise(mask=1, replacement=max_value)\n                image = np.full((3, 3), min_value, dtype=dtype)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(image_aug == max_value)\n\n                aug = iaa.ReplaceElementwise(mask=1, replacement=min_value)\n                image = np.full((3, 3), max_value, dtype=dtype)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert np.all(image_aug == min_value)\n\n            aug = iaa.ReplaceElementwise(mask=1, replacement=iap.Uniform(1.0, 10.0))\n            image = np.full((100, 1), 0, dtype=dtype)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(np.logical_and(1 <= image_aug, image_aug <= 10))\n            assert len(np.unique(image_aug)) > 1\n\n            aug = iaa.ReplaceElementwise(mask=1, replacement=iap.DiscreteUniform(1, 10))\n            image = np.full((100, 1), 0, dtype=dtype)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(np.logical_and(1 <= image_aug, image_aug <= 10))\n            assert len(np.unique(image_aug)) > 1\n\n            aug = iaa.ReplaceElementwise(mask=0.5, replacement=iap.DiscreteUniform(1, 10), per_channel=True)\n            image = np.full((1, 1, 100), 0, dtype=dtype)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(np.logical_and(0 <= image_aug, image_aug <= 10))\n            assert len(np.unique(image_aug)) > 2\n\n    def test_other_dtypes_float(self):\n        # float\n        for dtype in [np.float16, np.float32, np.float64]:\n            dtype = np.dtype(dtype)\n            min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n            atol = 1e-3*max_value if dtype == np.float16 else 1e-9 * max_value\n            _allclose = functools.partial(np.allclose, atol=atol, rtol=0)\n\n            aug = iaa.ReplaceElementwise(mask=1, replacement=1.0)\n            image = np.full((3, 3), 0.0, dtype=dtype)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.allclose(image_aug, 1.0)\n\n            aug = iaa.ReplaceElementwise(mask=1, replacement=2.0)\n            image = np.full((3, 3), 1.0, dtype=dtype)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.allclose(image_aug, 2.0)\n\n            # deterministic stochastic parameters are by default float32 for\n            # any float value and hence cannot cover the full float64 value\n            # range\n            if dtype.name != \"float64\":\n                aug = iaa.ReplaceElementwise(mask=1, replacement=max_value)\n                image = np.full((3, 3), min_value, dtype=dtype)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert _allclose(image_aug, max_value)\n\n                aug = iaa.ReplaceElementwise(mask=1, replacement=min_value)\n                image = np.full((3, 3), max_value, dtype=dtype)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.type == dtype\n                assert _allclose(image_aug, min_value)\n\n            aug = iaa.ReplaceElementwise(mask=1, replacement=iap.Uniform(1.0, 10.0))\n            image = np.full((100, 1), 0, dtype=dtype)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(np.logical_and(1 <= image_aug, image_aug <= 10))\n            assert not np.allclose(image_aug[1:, :], image_aug[:-1, :], atol=0.01)\n\n            aug = iaa.ReplaceElementwise(mask=1, replacement=iap.DiscreteUniform(1, 10))\n            image = np.full((100, 1), 0, dtype=dtype)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(np.logical_and(1 <= image_aug, image_aug <= 10))\n            assert not np.allclose(image_aug[1:, :], image_aug[:-1, :], atol=0.01)\n\n            aug = iaa.ReplaceElementwise(mask=0.5, replacement=iap.DiscreteUniform(1, 10), per_channel=True)\n            image = np.full((1, 1, 100), 0, dtype=dtype)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(np.logical_and(0 <= image_aug, image_aug <= 10))\n            assert not np.allclose(image_aug[:, :, 1:], image_aug[:, :, :-1], atol=0.01)\n\n    def test_pickleable(self):\n        aug = iaa.ReplaceElementwise(mask=0.5, replacement=(0, 255),\n                                     per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\n# not more tests necessary here as SaltAndPepper is just a tiny wrapper around\n# ReplaceElementwise\nclass TestSaltAndPepper(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_p_is_fifty_percent(self):\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        aug = iaa.SaltAndPepper(p=0.5)\n        observed = aug.augment_image(base_img)\n        p = np.mean(observed != 128)\n        assert 0.4 < p < 0.6\n\n    def test_p_is_one(self):\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        aug = iaa.SaltAndPepper(p=1.0)\n        observed = aug.augment_image(base_img)\n        nb_pepper = np.sum(observed < 40)\n        nb_salt = np.sum(observed > 255 - 40)\n        assert nb_pepper > 200\n        assert nb_salt > 200\n\n    def test_pickleable(self):\n        aug = iaa.SaltAndPepper(p=0.5, per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\nclass TestCoarseSaltAndPepper(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_p_is_fifty_percent(self):\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        aug = iaa.CoarseSaltAndPepper(p=0.5, size_px=100)\n        observed = aug.augment_image(base_img)\n        p = np.mean(observed != 128)\n        assert 0.4 < p < 0.6\n\n    def test_size_px(self):\n        aug1 = iaa.CoarseSaltAndPepper(p=0.5, size_px=100)\n        aug2 = iaa.CoarseSaltAndPepper(p=0.5, size_px=10)\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        ps1 = []\n        ps2 = []\n        for _ in sm.xrange(100):\n            observed1 = aug1.augment_image(base_img)\n            observed2 = aug2.augment_image(base_img)\n            p1 = np.mean(observed1 != 128)\n            p2 = np.mean(observed2 != 128)\n            ps1.append(p1)\n            ps2.append(p2)\n        assert 0.4 < np.mean(ps2) < 0.6\n        assert np.std(ps1)*1.5 < np.std(ps2)\n\n    def test_p_is_list(self):\n        aug = iaa.CoarseSaltAndPepper(p=[0.2, 0.5], size_px=100)\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        seen = [0, 0, 0]\n        for _ in sm.xrange(200):\n            observed = aug.augment_image(base_img)\n            p = np.mean(observed != 128)\n            diff_020 = abs(0.2 - p)\n            diff_050 = abs(0.5 - p)\n            if diff_020 < 0.025:\n                seen[0] += 1\n            elif diff_050 < 0.025:\n                seen[1] += 1\n            else:\n                seen[2] += 1\n        assert seen[2] < 10\n        assert 75 < seen[0] < 125\n        assert 75 < seen[1] < 125\n\n    def test_p_is_tuple(self):\n        aug = iaa.CoarseSaltAndPepper(p=(0.0, 1.0), size_px=50)\n        base_img = np.zeros((50, 50, 1), dtype=np.uint8) + 128\n        ps = []\n        for _ in sm.xrange(200):\n            observed = aug.augment_image(base_img)\n            p = np.mean(observed != 128)\n            ps.append(p)\n\n        nb_bins = 5\n        hist, _ = np.histogram(ps, bins=nb_bins, range=(0.0, 1.0), density=False)\n        tolerance = 0.05\n        for nb_seen in hist:\n            density = nb_seen / len(ps)\n            assert density - tolerance < density < density + tolerance\n\n    def test___init___bad_datatypes(self):\n        # test exceptions for wrong parameter types\n        got_exception = False\n        try:\n            _ = iaa.CoarseSaltAndPepper(p=\"test\", size_px=100)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test___init___size_px_and_size_percent_both_none(self):\n        aug = iaa.CoarseSaltAndPepper(p=0.5, size_px=None, size_percent=None)\n        assert np.isclose(aug.mask.size_px.a.value, 3)\n        assert np.isclose(aug.mask.size_px.b.value, 8)\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.CoarseSaltAndPepper(p=1.0, size_px=2)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_pickleable(self):\n        aug = iaa.CoarseSaltAndPepper(p=0.5, size_px=(4, 15),\n                                      per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\n# not more tests necessary here as Salt is just a tiny wrapper around\n# ReplaceElementwise\nclass TestSalt(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_p_is_fifty_percent(self):\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        aug = iaa.Salt(p=0.5)\n        observed = aug.augment_image(base_img)\n        p = np.mean(observed != 128)\n        assert 0.4 < p < 0.6\n        # Salt() occasionally replaces with 127, which probably should be the center-point here anyways\n        assert np.all(observed >= 127)\n\n    def test_p_is_one(self):\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        aug = iaa.Salt(p=1.0)\n        observed = aug.augment_image(base_img)\n        nb_pepper = np.sum(observed < 40)\n        nb_salt = np.sum(observed > 255 - 40)\n        assert nb_pepper == 0\n        assert nb_salt > 200\n\n    def test_pickleable(self):\n        aug = iaa.Salt(p=0.5, per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\nclass TestCoarseSalt(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_p_is_fifty_percent(self):\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        aug = iaa.CoarseSalt(p=0.5, size_px=100)\n        observed = aug.augment_image(base_img)\n        p = np.mean(observed != 128)\n        assert 0.4 < p < 0.6\n\n    def test_size_px(self):\n        aug1 = iaa.CoarseSalt(p=0.5, size_px=100)\n        aug2 = iaa.CoarseSalt(p=0.5, size_px=10)\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        ps1 = []\n        ps2 = []\n        for _ in sm.xrange(100):\n            observed1 = aug1.augment_image(base_img)\n            observed2 = aug2.augment_image(base_img)\n            p1 = np.mean(observed1 != 128)\n            p2 = np.mean(observed2 != 128)\n            ps1.append(p1)\n            ps2.append(p2)\n        assert 0.4 < np.mean(ps2) < 0.6\n        assert np.std(ps1)*1.5 < np.std(ps2)\n\n    def test_p_is_list(self):\n        aug = iaa.CoarseSalt(p=[0.2, 0.5], size_px=100)\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        seen = [0, 0, 0]\n        for _ in sm.xrange(200):\n            observed = aug.augment_image(base_img)\n            p = np.mean(observed != 128)\n            diff_020 = abs(0.2 - p)\n            diff_050 = abs(0.5 - p)\n            if diff_020 < 0.025:\n                seen[0] += 1\n            elif diff_050 < 0.025:\n                seen[1] += 1\n            else:\n                seen[2] += 1\n        assert seen[2] < 10\n        assert 75 < seen[0] < 125\n        assert 75 < seen[1] < 125\n\n    def test_p_is_tuple(self):\n        aug = iaa.CoarseSalt(p=(0.0, 1.0), size_px=50)\n        base_img = np.zeros((50, 50, 1), dtype=np.uint8) + 128\n        ps = []\n        for _ in sm.xrange(200):\n            observed = aug.augment_image(base_img)\n            p = np.mean(observed != 128)\n            ps.append(p)\n\n        nb_bins = 5\n        hist, _ = np.histogram(ps, bins=nb_bins, range=(0.0, 1.0), density=False)\n        tolerance = 0.05\n        for nb_seen in hist:\n            density = nb_seen / len(ps)\n            assert density - tolerance < density < density + tolerance\n\n    def test___init___bad_datatypes(self):\n        # test exceptions for wrong parameter types\n        got_exception = False\n        try:\n            _ = iaa.CoarseSalt(p=\"test\", size_px=100)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test___init___size_px_and_size_percent_both_none(self):\n        aug = iaa.CoarseSalt(p=0.5, size_px=None, size_percent=None)\n        assert np.isclose(aug.mask.size_px.a.value, 3)\n        assert np.isclose(aug.mask.size_px.b.value, 8)\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.CoarseSalt(p=1.0, size_px=2)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_pickleable(self):\n        aug = iaa.CoarseSalt(p=0.5, size_px=(4, 15),\n                             per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\n# not more tests necessary here as Salt is just a tiny wrapper around\n# ReplaceElementwise\nclass TestPepper(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_probability_is_fifty_percent(self):\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        aug = iaa.Pepper(p=0.5)\n        observed = aug.augment_image(base_img)\n        p = np.mean(observed != 128)\n        assert 0.4 < p < 0.6\n        assert np.all(observed <= 128)\n\n    def test_probability_is_one(self):\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        aug = iaa.Pepper(p=1.0)\n        observed = aug.augment_image(base_img)\n        nb_pepper = np.sum(observed < 40)\n        nb_salt = np.sum(observed > 255 - 40)\n        assert nb_pepper > 200\n        assert nb_salt == 0\n\n    def test_pickleable(self):\n        aug = iaa.Pepper(p=0.5, per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\nclass TestCoarsePepper(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_p_is_fifty_percent(self):\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        aug = iaa.CoarsePepper(p=0.5, size_px=100)\n        observed = aug.augment_image(base_img)\n        p = np.mean(observed != 128)\n        assert 0.4 < p < 0.6\n\n    def test_size_px(self):\n        aug1 = iaa.CoarsePepper(p=0.5, size_px=100)\n        aug2 = iaa.CoarsePepper(p=0.5, size_px=10)\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        ps1 = []\n        ps2 = []\n        for _ in sm.xrange(100):\n            observed1 = aug1.augment_image(base_img)\n            observed2 = aug2.augment_image(base_img)\n            p1 = np.mean(observed1 != 128)\n            p2 = np.mean(observed2 != 128)\n            ps1.append(p1)\n            ps2.append(p2)\n        assert 0.4 < np.mean(ps2) < 0.6\n        assert np.std(ps1)*1.5 < np.std(ps2)\n\n    def test_p_is_list(self):\n        aug = iaa.CoarsePepper(p=[0.2, 0.5], size_px=100)\n        base_img = np.zeros((100, 100, 1), dtype=np.uint8) + 128\n        seen = [0, 0, 0]\n        for _ in sm.xrange(200):\n            observed = aug.augment_image(base_img)\n            p = np.mean(observed != 128)\n            diff_020 = abs(0.2 - p)\n            diff_050 = abs(0.5 - p)\n            if diff_020 < 0.025:\n                seen[0] += 1\n            elif diff_050 < 0.025:\n                seen[1] += 1\n            else:\n                seen[2] += 1\n        assert seen[2] < 10\n        assert 75 < seen[0] < 125\n        assert 75 < seen[1] < 125\n\n    def test_p_is_tuple(self):\n        aug = iaa.CoarsePepper(p=(0.0, 1.0), size_px=50)\n        base_img = np.zeros((50, 50, 1), dtype=np.uint8) + 128\n        ps = []\n        for _ in sm.xrange(200):\n            observed = aug.augment_image(base_img)\n            p = np.mean(observed != 128)\n            ps.append(p)\n\n        nb_bins = 5\n        hist, _ = np.histogram(ps, bins=nb_bins, range=(0.0, 1.0), density=False)\n        tolerance = 0.05\n        for nb_seen in hist:\n            density = nb_seen / len(ps)\n            assert density - tolerance < density < density + tolerance\n\n    def test___init___bad_datatypes(self):\n        # test exceptions for wrong parameter types\n        got_exception = False\n        try:\n            _ = iaa.CoarsePepper(p=\"test\", size_px=100)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test___init___size_px_and_size_percent_both_none(self):\n        aug = iaa.CoarsePepper(p=0.5, size_px=None, size_percent=None)\n        assert np.isclose(aug.mask.size_px.a.value, 3)\n        assert np.isclose(aug.mask.size_px.b.value, 8)\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.CoarsePepper(p=1.0, size_px=2)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_pickleable(self):\n        aug = iaa.CoarsePepper(p=0.5, size_px=(4, 15),\n                               per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\nclass Test_invert(unittest.TestCase):\n    @mock.patch(\"imgaug.augmenters.arithmetic.invert_\")\n    def test_mocked_defaults(self, mock_invert):\n        mock_invert.return_value = \"foo\"\n        arr = np.zeros((1,), dtype=np.uint8)\n        observed = iaa.invert(arr)\n\n        assert observed == \"foo\"\n        args = mock_invert.call_args_list[0]\n        assert np.array_equal(mock_invert.call_args_list[0][0][0], arr)\n        assert args[1][\"min_value\"] is None\n        assert args[1][\"max_value\"] is None\n        assert args[1][\"threshold\"] is None\n        assert args[1][\"invert_above_threshold\"] is True\n\n    @mock.patch(\"imgaug.augmenters.arithmetic.invert_\")\n    def test_mocked(self, mock_invert):\n        mock_invert.return_value = \"foo\"\n        arr = np.zeros((1,), dtype=np.uint8)\n        observed = iaa.invert(arr, min_value=1, max_value=10, threshold=5,\n                              invert_above_threshold=False)\n\n        assert observed == \"foo\"\n        args = mock_invert.call_args_list[0]\n        assert np.array_equal(mock_invert.call_args_list[0][0][0], arr)\n        assert args[1][\"min_value\"] == 1\n        assert args[1][\"max_value\"] == 10\n        assert args[1][\"threshold\"] == 5\n        assert args[1][\"invert_above_threshold\"] is False\n\n    def test_uint8(self):\n        values = np.array([0, 20, 45, 60, 128, 255], dtype=np.uint8)\n        expected = np.array([\n            255,\n            255-20,\n            255-45,\n            255-60,\n            255-128,\n            255-255\n        ], dtype=np.uint8)\n\n        observed = iaa.invert(values)\n\n        assert np.array_equal(observed, expected)\n        assert observed is not values\n\n\n# most parts of this function are tested via Invert\nclass Test_invert_(unittest.TestCase):\n    def test_arr_is_noncontiguous_uint8(self):\n        zeros = np.zeros((4, 4, 3), dtype=np.uint8)\n        max_vr_flipped = np.fliplr(np.copy(zeros + 255))\n\n        observed = iaa.invert_(max_vr_flipped)\n        expected = zeros\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_arr_is_view_uint8(self):\n        zeros = np.zeros((4, 4, 3), dtype=np.uint8)\n        max_vr_view = np.copy(zeros + 255)[:, :, [0, 2]]\n\n        observed = iaa.invert_(max_vr_view)\n        expected = zeros[:, :, [0, 2]]\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_uint(self):\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\"]\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dt)\n                center_value = int(center_value)\n\n                values = np.array([0, 20, 45, 60, center_value, max_value],\n                                  dtype=dt)\n                expected = np.array([\n                    max_value - 0,\n                    max_value - 20,\n                    max_value - 45,\n                    max_value - 60,\n                    max_value - center_value,\n                    min_value\n                ], dtype=dt)\n\n                observed = iaa.invert_(np.copy(values))\n\n                assert np.array_equal(observed, expected)\n\n    def test_uint_with_threshold_50_inv_above(self):\n        threshold = 50\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\"]\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dt)\n                center_value = int(center_value)\n\n                values = np.array([0, 20, 45, 60, center_value, max_value],\n                                  dtype=dt)\n                expected = np.array([\n                    0,\n                    20,\n                    45,\n                    max_value - 60,\n                    max_value - center_value,\n                    min_value\n                ], dtype=dt)\n\n                observed = iaa.invert_(np.copy(values),\n                                       threshold=threshold,\n                                       invert_above_threshold=True)\n\n                assert np.array_equal(observed, expected)\n\n    def test_uint_with_threshold_0_inv_above(self):\n        threshold = 0\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\"]\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dt)\n                center_value = int(center_value)\n\n                values = np.array([0, 20, 45, 60, center_value, max_value],\n                                  dtype=dt)\n                expected = np.array([\n                    max_value - 0,\n                    max_value - 20,\n                    max_value - 45,\n                    max_value - 60,\n                    max_value - center_value,\n                    min_value\n                ], dtype=dt)\n\n                observed = iaa.invert_(np.copy(values),\n                                       threshold=threshold,\n                                       invert_above_threshold=True)\n\n                assert np.array_equal(observed, expected)\n\n    def test_uint8_with_threshold_255_inv_above(self):\n        threshold = 255\n        dtypes = [\"uint8\"]\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dt)\n                center_value = int(center_value)\n\n                values = np.array([0, 20, 45, 60, center_value, max_value],\n                                  dtype=dt)\n                expected = np.array([\n                    0,\n                    20,\n                    45,\n                    60,\n                    center_value,\n                    min_value\n                ], dtype=dt)\n\n                observed = iaa.invert_(np.copy(values),\n                                       threshold=threshold,\n                                       invert_above_threshold=True)\n\n                assert np.array_equal(observed, expected)\n\n    def test_uint8_with_threshold_256_inv_above(self):\n        threshold = 256\n        dtypes = [\"uint8\"]\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dt)\n                center_value = int(center_value)\n\n                values = np.array([0, 20, 45, 60, center_value, max_value],\n                                  dtype=dt)\n                expected = np.array([\n                    0,\n                    20,\n                    45,\n                    60,\n                    center_value,\n                    max_value\n                ], dtype=dt)\n\n                observed = iaa.invert_(np.copy(values),\n                                       threshold=threshold,\n                                       invert_above_threshold=True)\n\n                assert np.array_equal(observed, expected)\n\n    def test_uint_with_threshold_50_inv_below(self):\n        threshold = 50\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\"]\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dt)\n                center_value = int(center_value)\n\n                values = np.array([0, 20, 45, 60, center_value, max_value],\n                                  dtype=dt)\n                expected = np.array([\n                    max_value - 0,\n                    max_value - 20,\n                    max_value - 45,\n                    60,\n                    center_value,\n                    max_value\n                ], dtype=dt)\n\n                observed = iaa.invert_(np.copy(values),\n                                       threshold=threshold,\n                                       invert_above_threshold=False)\n\n                assert np.array_equal(observed, expected)\n\n    def test_uint_with_threshold_50_inv_above_with_min_max(self):\n        threshold = 50\n        # uint64 does not support custom min/max, hence removed it here\n        dtypes = [\"uint8\", \"uint16\", \"uint32\"]\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dt)\n                center_value = int(center_value)\n\n                values = np.array([0, 20, 45, 60, center_value, max_value],\n                                  dtype=dt)\n                expected = np.array([\n                    0,  # not clipped to 10 as only >thresh affected\n                    20,\n                    45,\n                    100 - 50,\n                    100 - 90,\n                    100 - 90\n                ], dtype=dt)\n\n                observed = iaa.invert_(np.copy(values),\n                                       min_value=10,\n                                       max_value=100,\n                                       threshold=threshold,\n                                       invert_above_threshold=True)\n\n                assert np.array_equal(observed, expected)\n\n    def test_int_with_threshold_50_inv_above(self):\n        threshold = 50\n        dtypes = [\"int8\", \"int16\", \"int32\", \"int64\"]\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dt)\n                center_value = int(center_value)\n\n                values = np.array([-45, -20, center_value, 20, 45, max_value],\n                                  dtype=dt)\n                expected = np.array([\n                    -45,\n                    -20,\n                    center_value,\n                    20,\n                    45,\n                    min_value\n                ], dtype=dt)\n\n                observed = iaa.invert_(np.copy(values),\n                                       threshold=threshold,\n                                       invert_above_threshold=True)\n\n                assert np.array_equal(observed, expected)\n\n    def test_int_with_threshold_50_inv_below(self):\n        threshold = 50\n        dtypes = [\"int8\", \"int16\", \"int32\", \"int64\"]\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dt)\n                center_value = int(center_value)\n\n                values = np.array([-45, -20, center_value, 20, 45, max_value],\n                                  dtype=dt)\n                expected = np.array([\n                    (-1) * (-45) - 1,\n                    (-1) * (-20) - 1,\n                    (-1) * center_value - 1,\n                    (-1) * 20 - 1,\n                    (-1) * 45 - 1,\n                    max_value\n                ], dtype=dt)\n\n                observed = iaa.invert_(np.copy(values),\n                                       threshold=threshold,\n                                       invert_above_threshold=False)\n\n                assert np.array_equal(observed, expected)\n\n    def test_float_with_threshold_50_inv_above(self):\n        threshold = 50\n\n        try:\n            _high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n        except AttributeError:\n            _high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dt)\n                center_value = center_value\n\n                values = np.array([-45.5, -20.5, center_value, 20.5, 45.5,\n                                   max_value],\n                                  dtype=dt)\n                expected = np.array([\n                    -45.5,\n                    -20.5,\n                    center_value,\n                    20.5,\n                    45.5,\n                    min_value\n                ], dtype=dt)\n\n                observed = iaa.invert_(np.copy(values),\n                                       threshold=threshold,\n                                       invert_above_threshold=True)\n\n                assert np.allclose(observed, expected, rtol=0, atol=1e-4)\n\n    def test_float_with_threshold_50_inv_below(self):\n        threshold = 50\n\n        try:\n            _high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n        except AttributeError:\n            _high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dt)\n                center_value = center_value\n\n                values = np.array([-45.5, -20.5, center_value, 20.5, 45.5,\n                                   max_value],\n                                  dtype=dt)\n                expected = np.array([\n                    (-1) * (-45.5),\n                    (-1) * (-20.5),\n                    (-1) * center_value,\n                    (-1) * 20.5,\n                    (-1) * 45.5,\n                    max_value\n                ], dtype=dt)\n\n                observed = iaa.invert_(np.copy(values),\n                                       threshold=threshold,\n                                       invert_above_threshold=False)\n\n                assert np.allclose(observed, expected, rtol=0, atol=1e-4)\n\n\nclass Test__invert_uint8_subtract_(unittest.TestCase):\n    def test_fails_with_size_4(self):\n        # cv2 seems to fail in same cases when input arrays have exactly 4\n        # components.\n        # We verify here that this is the case.\n        # If this test method fails, it means that the code runs merely\n        # sub-optimally as cv2 could be used for such arrays. The code is then\n        # not wrong though.\n        for shape in [(1, 2, 2), (2, 1, 2), (4, 1)]:\n            with self.subTest(shape=shape):\n                zeros = np.zeros(shape, dtype=np.uint8)\n\n                with self.assertRaises(cv2.error):\n                    _ = _invert_uint8_subtract_(zeros, 255)\n\n    def test_0_inverted_to_255_all_small_shapes(self):\n        # this includes zero-sized axes\n        for height in np.arange(8):\n            for width in np.arange(8):\n                # cv2 fails on area==4, we use a LUT function for that case\n                if height * width == 4:\n                    continue\n\n                for nb_channels in [None, 1, 3]:\n                    channels_tpl = (nb_channels,)\n                    if nb_channels is None:\n                        channels_tpl = tuple()\n                    shape = (height, width) + channels_tpl\n\n                    with self.subTest(shape=shape):\n                        zeros = np.zeros(shape, dtype=np.uint8)\n\n                        observed = _invert_uint8_subtract_(np.copy(zeros), 255)\n\n                        expected = np.full(shape, 255, dtype=np.uint8)\n                        assert observed.dtype.name == \"uint8\"\n                        assert np.array_equal(observed, expected)\n\n    def test_0_inverted_to_255(self):\n        for shape_hw in [(4, 8), (4, 4), (2, 4), (4, 2), (1, 16), (16, 1),\n                         (1, 2), (2, 1), (1, 1), (40, 60)]:\n            shapes_2d3d = [shape_hw]\n            for nb_channels in [1, 2, 3, 4, 5, 10, 512, 513]:\n                shapes_2d3d.append(shape_hw + (nb_channels,))\n\n            for shape in shapes_2d3d:\n                if np.prod(shape) == 4:\n                    continue\n\n                with self.subTest(shape=shape):\n                    zeros = np.zeros(shape, dtype=np.uint8)\n\n                    observed = _invert_uint8_subtract_(np.copy(zeros), 255)\n\n                    expected = np.full(shape, 255, dtype=np.uint8)\n                    assert observed.dtype.name == \"uint8\"\n                    assert np.array_equal(observed, expected)\n\n    def test_nonzero_values(self):\n        arr = np.array([0, 10, 20, 30, 40, 50], dtype=np.uint8).reshape((3, 2))\n\n        observed = _invert_uint8_subtract_(np.copy(arr), 255)\n\n        expected = np.array(\n            [255-0, 255-10, 255-20, 255-30, 255-40, 255-50],\n            dtype=np.uint8\n        ).reshape((3, 2))\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_noncontiguous(self):\n        for shape_hw in [(3, 2), (4, 8)]:\n            shapes_2d3d = [shape_hw]\n            for nb_channels in [1, 2, 3, 4, 5, 10, 512, 513]:\n                shapes_2d3d.append(shape_hw + (nb_channels,))\n\n            for shape in shapes_2d3d:\n                with self.subTest(shape=shape):\n                    zeros = np.zeros(shape, dtype=np.uint8, order=\"F\")\n                    assert zeros.flags[\"C_CONTIGUOUS\"] is False\n\n                    observed = _invert_uint8_subtract_(np.copy(zeros), 255)\n\n                    expected = np.full(shape, 255, dtype=np.uint8)\n                    assert observed.dtype.name == \"uint8\"\n                    assert np.array_equal(observed, expected)\n\n    def test_unusual_base_shapes(self):\n        for shape in [(5, 10, 514), (5, 1, 514), (1, 5, 514)]:\n            zeros = np.zeros(shape, dtype=np.uint8)\n            for nb_selected in [1, 2, 3, 5, 10, 512, 513]:\n                with self.subTest(shape=shape, nb_selected=nb_selected):\n                    mask = [False] * shape[-1]\n                    for c in np.arange(nb_selected):\n                        mask[c] = True\n                    zeros_view = np.copy(zeros)[:, :, mask]\n                    assert zeros_view.flags[\"OWNDATA\"] is False\n                    assert zeros_view.base is not None\n                    assert (\n                        zeros_view.base.shape\n                        == (nb_selected, shape[0], shape[1])\n                    ), zeros_view.base.shape\n\n                    observed = _invert_uint8_subtract_(zeros_view, 255)\n\n                    expected = np.full(\n                        (shape[0], shape[1], nb_selected), 255, dtype=np.uint8\n                    )\n                    assert observed.dtype.name == \"uint8\"\n                    assert np.array_equal(observed, expected)\n\n    def test_view(self):\n        for shape_hw in [(1, 1), (1, 2), (2, 1), (4, 8), (40, 60)]:\n            shapes_2d3d = [shape_hw]\n            for nb_channels in [1, 2, 3, 4, 5, 10, 512, 513]:\n                shapes_2d3d.append(shape_hw + (nb_channels,))\n\n            for shape in shapes_2d3d:\n                if np.prod(shape) == 4:\n                    continue\n\n                with self.subTest(shape=shape):\n                    shape_pad = (shape[0] + 2,) + shape[1:]\n                    zeros = np.zeros(shape_pad, dtype=np.uint8)\n                    zeros_view = np.copy(zeros)[0:-2, ...]\n                    assert zeros_view.flags[\"OWNDATA\"] is False\n\n                    observed = _invert_uint8_subtract_(zeros_view, 255)\n\n                    expected = np.full(shape, 255, dtype=np.uint8)\n                    assert observed.dtype.name == \"uint8\"\n                    assert np.array_equal(observed, expected)\n\n\nclass Test_solarize(unittest.TestCase):\n    @mock.patch(\"imgaug.augmenters.arithmetic.solarize_\")\n    def test_mocked_defaults(self, mock_sol):\n        arr = np.zeros((1,), dtype=np.uint8)\n        mock_sol.return_value = \"foo\"\n\n        observed = iaa.solarize(arr)\n\n        args = mock_sol.call_args_list[0][0]\n        kwargs = mock_sol.call_args_list[0][1]\n        assert args[0] is not arr\n        assert np.array_equal(args[0], arr)\n        assert kwargs[\"threshold\"] == 128\n        assert observed == \"foo\"\n\n    @mock.patch(\"imgaug.augmenters.arithmetic.solarize_\")\n    def test_mocked(self, mock_sol):\n        arr = np.zeros((1,), dtype=np.uint8)\n        mock_sol.return_value = \"foo\"\n\n        observed = iaa.solarize(arr, threshold=5)\n\n        args = mock_sol.call_args_list[0][0]\n        kwargs = mock_sol.call_args_list[0][1]\n        assert args[0] is not arr\n        assert np.array_equal(args[0], arr)\n        assert kwargs[\"threshold\"] == 5\n        assert observed == \"foo\"\n\n    def test_uint8(self):\n        arr = np.array([0, 10, 50, 150, 200, 255], dtype=np.uint8)\n        arr = arr.reshape((2, 3, 1))\n\n        observed = iaa.solarize(arr)\n\n        expected = np.array([0, 10, 50, 255-150, 255-200, 255-255],\n                            dtype=np.uint8).reshape((2, 3, 1))\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n\nclass Test_solarize_(unittest.TestCase):\n    @mock.patch(\"imgaug.augmenters.arithmetic.invert_\")\n    def test_mocked_defaults(self, mock_sol):\n        arr = np.zeros((1,), dtype=np.uint8)\n        mock_sol.return_value = \"foo\"\n\n        observed = iaa.solarize_(arr)\n\n        args = mock_sol.call_args_list[0][0]\n        kwargs = mock_sol.call_args_list[0][1]\n        assert args[0] is arr\n        assert kwargs[\"threshold\"] == 128\n        assert observed == \"foo\"\n\n    @mock.patch(\"imgaug.augmenters.arithmetic.invert_\")\n    def test_mocked(self, mock_sol):\n        arr = np.zeros((1,), dtype=np.uint8)\n        mock_sol.return_value = \"foo\"\n\n        observed = iaa.solarize_(arr, threshold=5)\n\n        args = mock_sol.call_args_list[0][0]\n        kwargs = mock_sol.call_args_list[0][1]\n        assert args[0] is arr\n        assert kwargs[\"threshold\"] == 5\n        assert observed == \"foo\"\n\n\nclass TestInvert(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_p_is_one(self):\n        zeros = np.zeros((4, 4, 3), dtype=np.uint8)\n\n        observed = iaa.Invert(p=1.0).augment_image(zeros + 255)\n        expected = zeros\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_p_is_zero(self):\n        zeros = np.zeros((4, 4, 3), dtype=np.uint8)\n\n        observed = iaa.Invert(p=0.0).augment_image(zeros + 255)\n        expected = zeros + 255\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_max_value_set(self):\n        zeros = np.zeros((4, 4, 3), dtype=np.uint8)\n\n        observed = iaa.Invert(p=1.0, max_value=200).augment_image(zeros + 200)\n        expected = zeros\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_min_value_and_max_value_set(self):\n        zeros = np.zeros((4, 4, 3), dtype=np.uint8)\n\n        observed = iaa.Invert(p=1.0, max_value=200, min_value=100).augment_image(zeros + 200)\n        expected = zeros + 100\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n        observed = iaa.Invert(p=1.0, max_value=200, min_value=100).augment_image(zeros + 100)\n        expected = zeros + 200\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_min_value_and_max_value_set_with_float_image(self):\n        # with min/max and float inputs\n        zeros = np.zeros((4, 4, 3), dtype=np.uint8)\n\n        zeros_f32 = zeros.astype(np.float32)\n        observed = iaa.Invert(p=1.0, max_value=200, min_value=100).augment_image(zeros_f32 + 200)\n        expected = zeros_f32 + 100\n        assert observed.dtype.name == \"float32\"\n        assert np.array_equal(observed, expected)\n\n        observed = iaa.Invert(p=1.0, max_value=200, min_value=100).augment_image(zeros_f32 + 100)\n        expected = zeros_f32 + 200\n        assert observed.dtype.name == \"float32\"\n        assert np.array_equal(observed, expected)\n\n    def test_p_is_80_percent(self):\n        nb_iterations = 1000\n        nb_inverted = 0\n        aug = iaa.Invert(p=0.8)\n        img = np.zeros((1, 1, 1), dtype=np.uint8) + 255\n        expected = np.zeros((1, 1, 1), dtype=np.uint8)\n        for i in sm.xrange(nb_iterations):\n            observed = aug.augment_image(img)\n            if np.array_equal(observed, expected):\n                nb_inverted += 1\n        pinv = nb_inverted / nb_iterations\n        assert 0.75 <= pinv <= 0.85\n\n        nb_iterations = 1000\n        nb_inverted = 0\n        aug = iaa.Invert(p=iap.Binomial(0.8))\n        img = np.zeros((1, 1, 1), dtype=np.uint8) + 255\n        expected = np.zeros((1, 1, 1), dtype=np.uint8)\n        for i in sm.xrange(nb_iterations):\n            observed = aug.augment_image(img)\n            if np.array_equal(observed, expected):\n                nb_inverted += 1\n        pinv = nb_inverted / nb_iterations\n        assert 0.75 <= pinv <= 0.85\n\n    def test_per_channel(self):\n        aug = iaa.Invert(p=0.5, per_channel=True)\n        img = np.zeros((1, 1, 100), dtype=np.uint8) + 255\n        observed = aug.augment_image(img)\n        assert len(np.unique(observed)) == 2\n\n    # TODO split into two tests\n    def test_p_is_stochastic_parameter_per_channel_is_probability(self):\n        nb_iterations = 1000\n        aug = iaa.Invert(p=iap.Binomial(0.8), per_channel=0.7)\n        img = np.zeros((1, 1, 20), dtype=np.uint8) + 255\n        seen = [0, 0]\n        for i in sm.xrange(nb_iterations):\n            observed = aug.augment_image(img)\n            uq = np.unique(observed)\n            if len(uq) == 1:\n                seen[0] += 1\n            elif len(uq) == 2:\n                seen[1] += 1\n            else:\n                assert False\n        assert 300 - 75 < seen[0] < 300 + 75\n        assert 700 - 75 < seen[1] < 700 + 75\n\n    def test_threshold(self):\n        arr = np.array([0, 10, 50, 150, 200, 255], dtype=np.uint8)\n        arr = arr.reshape((2, 3, 1))\n        aug = iaa.Invert(p=1.0, threshold=128, invert_above_threshold=True)\n\n        observed = aug.augment_image(arr)\n\n        expected = np.array([0, 10, 50, 255-150, 255-200, 255-255],\n                            dtype=np.uint8).reshape((2, 3, 1))\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_threshold_inv_below(self):\n        arr = np.array([0, 10, 50, 150, 200, 255], dtype=np.uint8)\n        arr = arr.reshape((2, 3, 1))\n        aug = iaa.Invert(p=1.0, threshold=128, invert_above_threshold=False)\n\n        observed = aug.augment_image(arr)\n\n        expected = np.array([255-0, 255-10, 255-50, 150, 200, 255],\n                            dtype=np.uint8).reshape((2, 3, 1))\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_keypoints_dont_change(self):\n        # keypoints shouldnt be changed\n        zeros = np.zeros((4, 4, 3), dtype=np.uint8)\n\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n                                          ia.Keypoint(x=2, y=2)], shape=zeros.shape)]\n\n        aug = iaa.Invert(p=1.0)\n        aug_det = iaa.Invert(p=1.0).to_deterministic()\n        observed = aug.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n    def test___init___bad_datatypes(self):\n        # test exceptions for wrong parameter types\n        got_exception = False\n        try:\n            _ = iaa.Invert(p=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        got_exception = False\n        try:\n            _ = iaa.Invert(p=0.5, per_channel=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Invert(1.0)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 255)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Invert(1.0)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 255)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_get_parameters(self):\n        # test get_parameters()\n        aug = iaa.Invert(p=0.5, per_channel=False, min_value=10, max_value=20)\n        params = aug.get_parameters()\n        assert params[0] is aug.p\n        assert params[1] is aug.per_channel\n        assert params[2] == 10\n        assert params[3] == 20\n        assert params[4] is aug.threshold\n        assert params[5] is aug.invert_above_threshold\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.Invert(p=1.0)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_other_dtypes_p_is_zero(self):\n        # with p=0.0\n        aug = iaa.Invert(p=0.0)\n\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\n            bool,\n            np.uint8, np.uint16, np.uint32, np.uint64,\n            np.int8, np.int16, np.int32, np.int64,\n            np.float16, np.float32, np.float64\n        ] + f128\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                kind = np.dtype(dtype).kind\n                image_min = np.full((3, 3), min_value, dtype=dtype)\n                if dtype is not bool:\n                    image_center = (\n                        np.full(\n                            (3, 3),\n                            center_value if kind == \"f\" else int(center_value),\n                            dtype=dtype\n                        )\n                    )\n                image_max = np.full((3, 3), max_value, dtype=dtype)\n                image_min_aug = aug.augment_image(image_min)\n                image_center_aug = None\n                if dtype is not bool:\n                    image_center_aug = aug.augment_image(image_center)\n                image_max_aug = aug.augment_image(image_max)\n\n                assert image_min_aug.dtype == np.dtype(dtype)\n                if image_center_aug is not None:\n                    assert image_center_aug.dtype == np.dtype(dtype)\n                assert image_max_aug.dtype == np.dtype(dtype)\n\n                if dtype is bool:\n                    assert np.all(image_min_aug == image_min)\n                    assert np.all(image_max_aug == image_max)\n                elif np.dtype(dtype).kind in [\"i\", \"u\"]:\n                    assert np.array_equal(image_min_aug, image_min)\n                    assert np.array_equal(image_center_aug, image_center)\n                    assert np.array_equal(image_max_aug, image_max)\n                else:\n                    assert np.allclose(image_min_aug, image_min)\n                    assert np.allclose(image_center_aug, image_center)\n                    assert np.allclose(image_max_aug, image_max)\n\n    def test_other_dtypes_p_is_one(self):\n        # with p=1.0\n        aug = iaa.Invert(p=1.0)\n\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\n            bool,\n            np.uint8, np.uint16, np.uint32, np.uint64,\n            np.int8, np.int16, np.int32, np.int64,\n            np.float16, np.float32, np.float64\n        ] + f128\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                kind = np.dtype(dtype).kind\n                image_min = np.full((3, 3), min_value, dtype=dtype)\n                if dtype is not bool:\n                    image_center = (\n                        np.full(\n                            (3, 3),\n                            center_value if kind == \"f\" else int(center_value),\n                            dtype=dtype\n                        )\n                    )\n                image_max = np.full((3, 3), max_value, dtype=dtype)\n                image_min_aug = aug.augment_image(image_min)\n                image_center_aug = None\n                if dtype is not bool:\n                    image_center_aug = aug.augment_image(image_center)\n                image_max_aug = aug.augment_image(image_max)\n\n                assert image_min_aug.dtype == np.dtype(dtype)\n                if image_center_aug is not None:\n                    assert image_center_aug.dtype == np.dtype(dtype)\n                assert image_max_aug.dtype == np.dtype(dtype)\n\n                if dtype is bool:\n                    assert np.all(image_min_aug == image_max)\n                    assert np.all(image_max_aug == image_min)\n                elif np.dtype(dtype).kind in [\"i\", \"u\"]:\n                    assert np.array_equal(image_min_aug, image_max)\n                    assert np.allclose(image_center_aug, image_center, atol=1.0+1e-4, rtol=0)\n                    assert np.array_equal(image_max_aug, image_min)\n                else:\n                    assert np.allclose(image_min_aug, image_max)\n                    assert np.allclose(image_center_aug, image_center)\n                    assert np.allclose(image_max_aug, image_min)\n\n    def test_other_dtypes_p_is_one_with_min_value(self):\n        # with p=1.0 and min_value\n        aug = iaa.Invert(p=1.0, min_value=1)\n        dtypes = [np.uint8, np.uint16, np.uint32,\n                  np.int8, np.int16, np.int32,\n                  np.float16, np.float32]\n        for dtype in dtypes:\n            _min_value, _center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n            min_value = 1\n            kind = np.dtype(dtype).kind\n            center_value = min_value + 0.5 * (max_value - min_value)\n            image_min = np.full((3, 3), min_value, dtype=dtype)\n            if dtype is not bool:\n                image_center = np.full((3, 3), center_value if kind == \"f\" else int(center_value), dtype=dtype)\n            image_max = np.full((3, 3), max_value, dtype=dtype)\n            image_min_aug = aug.augment_image(image_min)\n            image_center_aug = None\n            if dtype is not bool:\n                image_center_aug = aug.augment_image(image_center)\n            image_max_aug = aug.augment_image(image_max)\n\n            assert image_min_aug.dtype == np.dtype(dtype)\n            if image_center_aug is not None:\n                assert image_center_aug.dtype == np.dtype(dtype)\n            assert image_max_aug.dtype == np.dtype(dtype)\n\n            if dtype is bool:\n                assert np.all(image_min_aug == 1)\n                assert np.all(image_max_aug == 1)\n            elif np.dtype(dtype).kind in [\"i\", \"u\"]:\n                assert np.array_equal(image_min_aug, image_max)\n                assert np.allclose(image_center_aug, image_center, atol=1.0+1e-4, rtol=0)\n                assert np.array_equal(image_max_aug, image_min)\n            else:\n                assert np.allclose(image_min_aug, image_max)\n                assert np.allclose(image_center_aug, image_center)\n                assert np.allclose(image_max_aug, image_min)\n\n    def test_other_dtypes_p_is_one_with_max_value(self):\n        # with p=1.0 and max_value\n        aug = iaa.Invert(p=1.0, max_value=16)\n        dtypes = [np.uint8, np.uint16, np.uint32,\n                  np.int8, np.int16, np.int32,\n                  np.float16, np.float32]\n        for dtype in dtypes:\n            min_value, _center_value, _max_value = iadt.get_value_range_of_dtype(dtype)\n            max_value = 16\n            kind = np.dtype(dtype).kind\n            center_value = min_value + 0.5 * (max_value - min_value)\n            image_min = np.full((3, 3), min_value, dtype=dtype)\n            if dtype is not bool:\n                image_center = np.full((3, 3), center_value if kind == \"f\" else int(center_value), dtype=dtype)\n            image_max = np.full((3, 3), max_value, dtype=dtype)\n            image_min_aug = aug.augment_image(image_min)\n            image_center_aug = None\n            if dtype is not bool:\n                image_center_aug = aug.augment_image(image_center)\n            image_max_aug = aug.augment_image(image_max)\n\n            assert image_min_aug.dtype == np.dtype(dtype)\n            if image_center_aug is not None:\n                assert image_center_aug.dtype == np.dtype(dtype)\n            assert image_max_aug.dtype == np.dtype(dtype)\n\n            if dtype is bool:\n                assert not np.any(image_min_aug == 1)\n                assert not np.any(image_max_aug == 1)\n            elif np.dtype(dtype).kind in [\"i\", \"u\"]:\n                assert np.array_equal(image_min_aug, image_max)\n                assert np.allclose(image_center_aug, image_center, atol=1.0+1e-4, rtol=0)\n                assert np.array_equal(image_max_aug, image_min)\n            else:\n                assert np.allclose(image_min_aug, image_max)\n                if dtype is np.float16:\n                    # for float16, this is off by about 10\n                    assert np.allclose(image_center_aug, image_center, atol=0.001*np.finfo(dtype).max)\n                else:\n                    assert np.allclose(image_center_aug, image_center)\n                assert np.allclose(image_max_aug, image_min)\n\n    def test_pickleable(self):\n        aug = iaa.Invert(p=0.5, per_channel=True, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20, shape=(2, 2, 5))\n\n\nclass TestSolarize(unittest.TestCase):\n    def test_p_is_one(self):\n        zeros = np.zeros((4, 4, 3), dtype=np.uint8)\n\n        observed = iaa.Solarize(p=1.0).augment_image(zeros)\n\n        expected = zeros\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_p_is_one_some_values_above_threshold(self):\n        arr = np.array([0, 99, 111, 200]).astype(np.uint8).reshape((2, 2, 1))\n\n        observed = iaa.Solarize(p=1.0, threshold=(100, 110))(image=arr)\n\n        expected = np.array([0, 99, 255-111, 255-200])\\\n            .astype(np.uint8).reshape((2, 2, 1))\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_pickleable(self):\n        aug = iaa.pillike.Solarize(p=1.0, threshold=(100, 110))\n        runtest_pickleable_uint8_img(aug)\n\n\nclass TestContrastNormalization(unittest.TestCase):\n    @unittest.skipIf(sys.version_info[0] <= 2,\n                     \"Warning is not generated in 2.7 on travis, but locally \"\n                     \"in 2.7 it is?!\")\n    def test_deprecation_warning(self):\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            aug = arithmetic_lib.ContrastNormalization((0.9, 1.1))\n            assert isinstance(aug, contrast_lib._ContrastFuncWrapper)\n\n        assert len(caught_warnings) == 1\n        assert (\n            \"deprecated\"\n            in str(caught_warnings[-1].message)\n        )\n\n\n# TODO use this in test_contrast.py or remove it?\n\"\"\"\ndef deactivated_test_ContrastNormalization():\n    reseed()\n\n    zeros = np.zeros((4, 4, 3), dtype=np.uint8)\n    keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n                                      ia.Keypoint(x=2, y=2)], shape=zeros.shape)]\n\n    # contrast stays the same\n    observed = iaa.ContrastNormalization(alpha=1.0).augment_image(zeros + 50)\n    expected = zeros + 50\n    assert np.array_equal(observed, expected)\n\n    # image with mean intensity (ie 128), contrast cannot be changed\n    observed = iaa.ContrastNormalization(alpha=2.0).augment_image(zeros + 128)\n    expected = zeros + 128\n    assert np.array_equal(observed, expected)\n\n    # increase contrast\n    observed = iaa.ContrastNormalization(alpha=2.0).augment_image(zeros + 128 + 10)\n    expected = zeros + 128 + 20\n    assert np.array_equal(observed, expected)\n\n    observed = iaa.ContrastNormalization(alpha=2.0).augment_image(zeros + 128 - 10)\n    expected = zeros + 128 - 20\n    assert np.array_equal(observed, expected)\n\n    # decrease contrast\n    observed = iaa.ContrastNormalization(alpha=0.5).augment_image(zeros + 128 + 10)\n    expected = zeros + 128 + 5\n    assert np.array_equal(observed, expected)\n\n    observed = iaa.ContrastNormalization(alpha=0.5).augment_image(zeros + 128 - 10)\n    expected = zeros + 128 - 5\n    assert np.array_equal(observed, expected)\n\n    # increase contrast by stochastic parameter\n    observed = iaa.ContrastNormalization(alpha=iap.Choice([2.0, 3.0])).augment_image(zeros + 128 + 10)\n    expected1 = zeros + 128 + 20\n    expected2 = zeros + 128 + 30\n    assert np.array_equal(observed, expected1) or np.array_equal(observed, expected2)\n\n    # change contrast by tuple\n    nb_iterations = 1000\n    nb_changed = 0\n    last = None\n    for i in sm.xrange(nb_iterations):\n        observed = iaa.ContrastNormalization(alpha=(0.5, 2.0)).augment_image(zeros + 128 + 40)\n        if last is None:\n            last = observed\n        else:\n            if not np.array_equal(observed, last):\n                nb_changed += 1\n    p_changed = nb_changed / (nb_iterations-1)\n    assert p_changed > 0.5\n\n    # per_channel=True\n    aug = iaa.ContrastNormalization(alpha=(1.0, 6.0), per_channel=True)\n    img = np.zeros((1, 1, 100), dtype=np.uint8) + 128 + 10\n    observed = aug.augment_image(img)\n    uq = np.unique(observed)\n    assert len(uq) > 5\n\n    # per_channel with probability\n    aug = iaa.ContrastNormalization(alpha=(1.0, 4.0), per_channel=0.7)\n    img = np.zeros((1, 1, 100), dtype=np.uint8) + 128 + 10\n    seen = [0, 0]\n    for _ in sm.xrange(1000):\n        observed = aug.augment_image(img)\n        uq = np.unique(observed)\n        if len(uq) == 1:\n            seen[0] += 1\n        elif len(uq) >= 2:\n            seen[1] += 1\n    assert 300 - 75 < seen[0] < 300 + 75\n    assert 700 - 75 < seen[1] < 700 + 75\n\n    # keypoints shouldnt be changed\n    aug = iaa.ContrastNormalization(alpha=2.0)\n    aug_det = iaa.ContrastNormalization(alpha=2.0).to_deterministic()\n    observed = aug.augment_keypoints(keypoints)\n    expected = keypoints\n    assert keypoints_equal(observed, expected)\n\n    observed = aug_det.augment_keypoints(keypoints)\n    expected = keypoints\n    assert keypoints_equal(observed, expected)\n\n    # test exceptions for wrong parameter types\n    got_exception = False\n    try:\n        _ = iaa.ContrastNormalization(alpha=\"test\")\n    except Exception:\n        got_exception = True\n    assert got_exception\n\n    got_exception = False\n    try:\n        _ = iaa.ContrastNormalization(alpha=1.5, per_channel=\"test\")\n    except Exception:\n        got_exception = True\n    assert got_exception\n\n    # test get_parameters()\n    aug = iaa.ContrastNormalization(alpha=1, per_channel=False)\n    params = aug.get_parameters()\n    assert isinstance(params[0], iap.Deterministic)\n    assert isinstance(params[1], iap.Deterministic)\n    assert params[0].value == 1\n    assert params[1].value == 0\n\n    # test heatmaps (not affected by augmenter)\n    aug = iaa.ContrastNormalization(alpha=2)\n    hm = ia.data.quokka_heatmap()\n    hm_aug = aug.augment_heatmaps([hm])[0]\n    assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\"\"\"\n\n\nclass TestJpegCompression(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_compression_is_zero(self):\n        # basic test at 0 compression\n        img = ia.data.quokka(extract=\"square\", size=(64, 64))\n        aug = iaa.JpegCompression(0)\n        img_aug = aug.augment_image(img)\n        diff = np.average(np.abs(img.astype(np.float32) - img_aug.astype(np.float32)))\n        assert diff < 1.0\n\n    def test_compression_is_90(self):\n        # basic test at 90 compression\n        img = ia.data.quokka(extract=\"square\", size=(64, 64))\n        aug = iaa.JpegCompression(90)\n        img_aug = aug.augment_image(img)\n        diff = np.average(np.abs(img.astype(np.float32) - img_aug.astype(np.float32)))\n        assert 1.0 < diff < 50.0\n\n    def test___init__(self):\n        aug = iaa.JpegCompression([0, 100])\n        assert is_parameter_instance(aug.compression, iap.Choice)\n        assert len(aug.compression.a) == 2\n        assert aug.compression.a[0] == 0\n        assert aug.compression.a[1] == 100\n\n    def test_get_parameters(self):\n        aug = iaa.JpegCompression([0, 100])\n        assert len(aug.get_parameters()) == 1\n        assert aug.get_parameters()[0] == aug.compression\n\n    def test_compression_is_stochastic_parameter(self):\n        # test if stochastic parameters are used by augmentation\n        img = ia.data.quokka(extract=\"square\", size=(64, 64))\n\n        class _TwoValueParam(iap.StochasticParameter):\n            def __init__(self, v1, v2):\n                super(_TwoValueParam, self).__init__()\n                self.v1 = v1\n                self.v2 = v2\n\n            def _draw_samples(self, size, random_state):\n                arr = np.full(size, self.v1, dtype=np.float32)\n                arr[1::2] = self.v2\n                return arr\n\n        param = _TwoValueParam(0, 100)\n        aug = iaa.JpegCompression(param)\n        img_aug_c0 = iaa.JpegCompression(0).augment_image(img)\n        img_aug_c100 = iaa.JpegCompression(100).augment_image(img)\n        imgs_aug = aug.augment_images([img] * 4)\n        assert np.array_equal(imgs_aug[0], img_aug_c0)\n        assert np.array_equal(imgs_aug[1], img_aug_c100)\n        assert np.array_equal(imgs_aug[2], img_aug_c0)\n        assert np.array_equal(imgs_aug[3], img_aug_c100)\n\n    def test_keypoints_dont_change(self):\n        # test keypoints (not affected by augmenter)\n        aug = iaa.JpegCompression(50)\n        kps = ia.data.quokka_keypoints()\n        kps_aug = aug.augment_keypoints([kps])[0]\n        for kp, kp_aug in zip(kps.keypoints, kps_aug.keypoints):\n            assert np.allclose([kp.x, kp.y], [kp_aug.x, kp_aug.y])\n\n    def test_heatmaps_dont_change(self):\n        # test heatmaps (not affected by augmenter)\n        aug = iaa.JpegCompression(50)\n        hm = ia.data.quokka_heatmap()\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0, 3),\n            (0, 1, 3),\n            (1, 0, 3)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.JpegCompression(100)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == image.shape\n\n    def test_pickleable(self):\n        aug = iaa.JpegCompression((0, 100), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n"
  },
  {
    "path": "test/augmenters/test_artistic.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport cv2\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import random as iarandom\nfrom imgaug.testutils import reseed, runtest_pickleable_uint8_img\nimport imgaug.augmenters.color as colorlib\nimport imgaug.augmenters.artistic as artisticlib\n\n\nclass Test_stylize_cartoon(unittest.TestCase):\n    @classmethod\n    def _test_integrationtest(cls, size, validate_grads):\n        image = ia.data.quokka_square((size, size))\n\n        image_cartoon = iaa.stylize_cartoon(image, blur_ksize=5,\n                                            segmentation_size=2.0)\n\n        image_avg = np.average(image.astype(np.float32), axis=2)\n        image_cartoon_avg = np.average(image_cartoon.astype(np.float32), axis=2)\n\n        if validate_grads:\n            gradx_image = image_avg[:, :-1] - image_avg[:, 1:]\n            grady_image = image_avg[:-1, :] - image_avg[1:, :]\n            gradx_cartoon = image_cartoon_avg[:, :-1] - image_cartoon_avg[:, 1:]\n            grady_cartoon = image_cartoon_avg[:-1, :] - image_cartoon_avg[1:, :]\n\n            assert (\n                (\n                    np.average(np.abs(gradx_cartoon))\n                    + np.average(np.abs(grady_cartoon))\n                )\n                <\n                (\n                    np.average(np.abs(gradx_image))\n                    + np.average(np.abs(grady_image))\n                )\n            )\n\n        # average saturation of cartoon image should be increased\n        image_hsv = colorlib.change_colorspace_(np.copy(image),\n                                                to_colorspace=iaa.CSPACE_HSV)\n        cartoon_hsv = colorlib.change_colorspace_(np.copy(image_cartoon),\n                                                  to_colorspace=iaa.CSPACE_HSV)\n        assert (\n            np.average(cartoon_hsv[:, :, 1])\n            > np.average(image_hsv[:, :, 1])\n        )\n\n        # as edges are all drawn in completely black, there should be more\n        # completely black pixels in the cartoon image\n        image_black = np.sum(image_avg <= 0.01)\n        cartoon_black = np.sum(image_cartoon_avg <= 0.01)\n\n        assert cartoon_black > image_black\n\n    def test_integrationtest(self):\n        self._test_integrationtest(128, True)\n\n    def test_integrationtest_large_image(self):\n        # TODO the validation of gradients currently doesn't work well\n        #      for the laplacian edge method, but it should\n        self._test_integrationtest(400, False)\n\n    @mock.patch(\"cv2.medianBlur\")\n    def test_blur_ksize_is_1(self, mock_blur):\n        def _side_effect(image, ksize):\n            return image\n\n        mock_blur.side_effect = _side_effect\n        image = np.arange(4*4*3).astype(np.uint8).reshape((4, 4, 3))\n\n        _ = iaa.stylize_cartoon(image, blur_ksize=1)\n\n        # median blur is called another time in _find_edge_laplacian, but\n        # that function is only called if the image is larger\n        assert mock_blur.call_count == 0\n\n    @mock.patch(\"cv2.medianBlur\")\n    def test_blur_ksize_gt_1(self, mock_blur):\n        def _side_effect(image, ksize):\n            return image\n\n        mock_blur.side_effect = _side_effect\n        image = np.arange(4*4*3).astype(np.uint8).reshape((4, 4, 3))\n\n        _ = iaa.stylize_cartoon(image, blur_ksize=7)\n\n        assert mock_blur.call_count == 1\n        assert mock_blur.call_args_list[0][0][1] == 7\n\n    @mock.patch(\"cv2.pyrMeanShiftFiltering\")\n    def test_segmentation_size_is_0(self, mock_msf):\n        def _side_effect(image, sp, sr, dst):\n            dst[...] = image\n\n        mock_msf.side_effect = _side_effect\n        image = np.arange(4*4*3).astype(np.uint8).reshape((4, 4, 3))\n\n        _ = iaa.stylize_cartoon(image, segmentation_size=0.0)\n\n        assert mock_msf.call_count == 0\n\n    @mock.patch(\"cv2.pyrMeanShiftFiltering\")\n    def test_segmentation_size_gt_0(self, mock_msf):\n        def _side_effect(image, sp, sr, dst):\n            dst[...] = image\n\n        mock_msf.side_effect = _side_effect\n        image = np.arange(4*4*3).astype(np.uint8).reshape((4, 4, 3))\n\n        _ = iaa.stylize_cartoon(image, segmentation_size=0.5)\n\n        assert mock_msf.call_count == 1\n        assert np.allclose(mock_msf.call_args_list[0][1][\"sp\"], 10*0.5)\n        assert np.allclose(mock_msf.call_args_list[0][1][\"sr\"], 20*0.5)\n\n    @mock.patch(\"imgaug.augmenters.artistic._suppress_edge_blobs\")\n    def test_suppress_edges_true(self, mock_seb):\n        image = np.arange(4*4*3).astype(np.uint8).reshape((4, 4, 3))\n        mock_seb.return_value = np.copy(image[..., 0])\n\n        _ = iaa.stylize_cartoon(image, suppress_edges=True)\n\n        assert mock_seb.call_count == 2\n\n    @mock.patch(\"imgaug.augmenters.artistic._suppress_edge_blobs\")\n    def test_suppress_edges_false(self, mock_seb):\n        image = np.arange(4*4*3).astype(np.uint8).reshape((4, 4, 3))\n\n        _ = iaa.stylize_cartoon(image, suppress_edges=False)\n\n        assert mock_seb.call_count == 0\n\n    @mock.patch(\"imgaug.augmenters.artistic._find_edges_laplacian\")\n    def test_large_image(self, mock_fel):\n        def _side_effect_fel(image, edge_multiplier, from_colorspace):\n            return image[..., 0]\n\n        mock_fel.side_effect = _side_effect_fel\n        image = np.zeros((10, 401, 3), dtype=np.uint8)\n\n        _ = iaa.stylize_cartoon(image, segmentation_size=0)\n\n        assert mock_fel.call_count == 1\n\n\nclass Test__saturate(unittest.TestCase):\n    def _get_avg_saturation(self, image):\n        hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)\n        return np.average(hsv[..., 1])\n\n    def test_saturation_is_1(self):\n        image = np.array([\n            [10, 20, 30],\n            [40, 50, 60],\n            [70, 80, 90],\n            [100, 110, 120],\n            [10, 10, 10],\n            [100, 0, 0],\n            [0, 100, 0],\n            [0, 0, 100]\n        ], dtype=np.uint8).reshape((1, 8, 3))\n\n        observed_1 = artisticlib._saturate(image, 1.0, colorlib.CSPACE_RGB)\n        observed_2 = artisticlib._saturate(image, 2.0, colorlib.CSPACE_RGB)\n\n        sat_img = self._get_avg_saturation(image)\n        sat_1 = self._get_avg_saturation(observed_1)\n        sat_2 = self._get_avg_saturation(observed_2)\n        assert sat_img < sat_2\n        assert sat_1 < sat_2\n\n\nclass TestCartoon(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___defaults(self):\n        aug = iaa.Cartoon()\n        assert aug.blur_ksize.a.value == 1\n        assert aug.blur_ksize.b.value == 5\n        assert np.isclose(aug.segmentation_size.a.value, 0.8)\n        assert np.isclose(aug.segmentation_size.b.value, 1.2)\n        assert np.isclose(aug.edge_prevalence.a.value, 0.9)\n        assert np.isclose(aug.edge_prevalence.b.value, 1.1)\n        assert np.isclose(aug.saturation.a.value, 1.5)\n        assert np.isclose(aug.saturation.b.value, 2.5)\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test_draw_samples(self):\n        mock_batch = mock.Mock()\n        mock_batch.nb_rows = 50\n        aug = iaa.Cartoon()\n        rs = iarandom.RNG(0)\n\n        samples = aug._draw_samples(mock_batch, rs)\n\n        assert len(np.unique(np.round(samples[0]*100, decimals=0))) > 1\n        assert len(np.unique(np.round(samples[1]*100, decimals=0))) > 1\n        assert len(np.unique(np.round(samples[2]*100, decimals=0))) > 1\n        assert len(np.unique(np.round(samples[3]*100, decimals=0))) > 1\n\n    @mock.patch(\"imgaug.augmenters.artistic.stylize_cartoon\")\n    def test_call_of_stylize_cartoon(self, mock_sc):\n        image = np.arange(4*4*3).astype(np.uint8).reshape((4, 4, 3))\n        aug = iaa.Cartoon()\n\n        mock_sc.return_value = np.copy(image)\n\n        _ = aug(images=[image, image])\n\n        assert mock_sc.call_count == 2\n\n    def test_get_parameters(self):\n        aug = iaa.Cartoon()\n        params = aug.get_parameters()\n        assert params[0] is aug.blur_ksize\n        assert params[1] is aug.segmentation_size\n        assert params[2] is aug.saturation\n        assert params[3] is aug.edge_prevalence\n        assert params[4] == iaa.CSPACE_RGB\n\n    def test_pickleable(self):\n        aug = iaa.Cartoon(seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=6)\n"
  },
  {
    "path": "test/augmenters/test_blend.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport warnings\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\ntry:\n    import cPickle as pickle\nexcept ImportError:\n    import pickle\n\nimport numpy as np\nimport six.moves as sm\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug import dtypes as iadt\nfrom imgaug.augmenters import blend\nfrom imgaug.testutils import (\n    keypoints_equal, reseed, assert_cbaois_equal,\n    runtest_pickleable_uint8_img, is_parameter_instance)\nfrom imgaug.augmentables.heatmaps import HeatmapsOnImage\nfrom imgaug.augmentables.segmaps import SegmentationMapsOnImage\nfrom imgaug.augmentables.batches import _BatchInAugmentation\n\n\nclass Test_blend_alpha(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_alpha_is_1(self):\n        for dtype in [\"uint8\", \"float32\"]:\n            with self.subTest(dtype=dtype):\n                img_fg = np.full((3, 3, 1), 0, dtype=dtype)\n                img_bg = np.full((3, 3, 1), 255, dtype=dtype)\n                img_blend = blend.blend_alpha(img_fg, img_bg, 1.0, eps=0)\n                assert img_blend.dtype.name == dtype\n                assert img_blend.shape == (3, 3, 1)\n                assert np.all(img_blend == 0)\n\n    def test_alpha_is_1_2d_arrays(self):\n        for dtype in [\"uint8\", \"float32\"]:\n            with self.subTest(dtype=dtype):\n                img_fg = np.full((3, 3), 0, dtype=dtype)\n                img_bg = np.full((3, 3), 255, dtype=dtype)\n                img_blend = blend.blend_alpha(img_fg, img_bg, 1.0, eps=0)\n                assert img_blend.dtype.name == dtype\n                assert img_blend.shape == (3, 3)\n                assert np.all(img_blend == 0)\n\n    def test_alpha_is_0(self):\n        for dtype in [\"uint8\", \"float32\"]:\n            with self.subTest(dtype=dtype):\n                img_fg = np.full((3, 3, 1), 0, dtype=dtype)\n                img_bg = np.full((3, 3, 1), 255, dtype=dtype)\n                img_blend = blend.blend_alpha(img_fg, img_bg, 0.0, eps=0)\n                assert img_blend.dtype.name == dtype\n                assert img_blend.shape == (3, 3, 1)\n                assert np.all(img_blend == 255)\n\n    def test_alpha_is_0_2d_arrays(self):\n        for dtype in [\"uint8\", \"float32\"]:\n            with self.subTest(dtype=dtype):\n                img_fg = np.full((3, 3), 0, dtype=dtype)\n                img_bg = np.full((3, 3), 255, dtype=dtype)\n                img_blend = blend.blend_alpha(img_fg, img_bg, 0.0, eps=0)\n                assert img_blend.dtype.name == dtype\n                assert img_blend.shape == (3, 3)\n                assert np.all(img_blend == 255)\n\n    def test_alpha_is_030(self):\n        for dtype in [\"uint8\", \"float32\"]:\n            with self.subTest(dtype=dtype):\n                img_fg = np.full((3, 3, 1), 0, dtype=dtype)\n                img_bg = np.full((3, 3, 1), 255, dtype=dtype)\n                img_blend = blend.blend_alpha(img_fg, img_bg, 0.3, eps=0)\n                assert img_blend.dtype.name == dtype\n                assert img_blend.shape == (3, 3, 1)\n                assert np.allclose(img_blend, 0.7*255, atol=1.01, rtol=0)\n\n    def test_alpha_is_030_2d_arrays(self):\n        for dtype in [\"uint8\", \"float32\"]:\n            with self.subTest(dtype=dtype):\n                img_fg = np.full((3, 3), 0, dtype=dtype)\n                img_bg = np.full((3, 3), 255, dtype=dtype)\n                img_blend = blend.blend_alpha(img_fg, img_bg, 0.3, eps=0)\n                assert img_blend.dtype.name == dtype\n                assert img_blend.shape == (3, 3)\n                assert np.allclose(img_blend, 0.7*255, atol=1.01, rtol=0)\n\n    def test_alpha_is_030_with_11c_arrays(self):\n        for dtype in [\"uint8\", \"float32\"]:\n            for nb_channels in [None, 1, 3]:\n                with self.subTest(dtype=dtype, nb_channels=nb_channels):\n                    shape = (1, 1, nb_channels)\n                    if nb_channels is None:\n                        shape = shape[0:2]\n\n                    img_fg = np.full(shape, 0, dtype=dtype)\n                    img_bg = np.full(shape, 255, dtype=dtype)\n                    img_blend = blend.blend_alpha(img_fg, img_bg, 0.3, eps=0)\n                    assert img_blend.dtype.name == dtype\n                    assert img_blend.shape == shape\n                    assert np.allclose(img_blend, 0.7*255, atol=1.01, rtol=0)\n\n    def test_channelwise_alpha(self):\n        for dtype in [\"uint8\", \"float32\"]:\n            with self.subTest(dtype=dtype):\n                img_fg = np.full((3, 3, 2), 0, dtype=dtype)\n                img_bg = np.full((3, 3, 2), 255, dtype=dtype)\n                img_blend = blend.blend_alpha(\n                    img_fg, img_bg, [1.0, 0.0], eps=0)\n                assert img_blend.dtype.name == dtype\n                assert img_blend.shape == (3, 3, 2)\n                assert np.all(img_blend[:, :, 0] == 0)\n                assert np.all(img_blend[:, :, 1] == 255)\n\n    def test_larger_images(self):\n        sizes = [(4, 4), (16, 16), (64, 64), (128, 128)]\n        for dtype in [\"uint8\", \"float32\"]:\n            for size in sizes:\n                shape = size + (3,)\n                for alphas_shape in [size, size + (1,), size + (3,)]:\n                    with self.subTest(dtype=dtype, shape=shape,\n                                      alphas_shape=alphas_shape):\n                        alphas = np.full(alphas_shape, 0.5, dtype=np.float32)\n                        img_fg = np.full(shape, 0, dtype=dtype)\n                        img_bg = np.full(shape, 255, dtype=dtype)\n                        img_blend = blend.blend_alpha(\n                            img_fg, img_bg, alphas, eps=0)\n                        assert img_blend.dtype.name == dtype\n                        assert img_blend.shape == shape\n                        assert np.allclose(img_blend, 128, rtol=0, atol=1.01)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for dtype in [\"uint8\", \"float32\"]:\n            for shape in shapes:\n                with self.subTest(dtype=dtype, shape=shape):\n                    image_fg = np.full(shape, 0, dtype=dtype)\n                    image_bg = np.full(shape, 255, dtype=dtype)\n\n                    image_aug = blend.blend_alpha(image_fg, image_bg, 1.0)\n\n                    assert np.all(image_aug == 0)\n                    assert image_aug.dtype.name == dtype\n                    assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for dtype in [\"uint8\", \"float32\"]:\n            for shape in shapes:\n                with self.subTest(dtype=dtype, shape=shape):\n                    image_fg = np.full(shape, 0, dtype=dtype)\n                    image_bg = np.full(shape, 255, dtype=dtype)\n\n                    image_aug = blend.blend_alpha(image_fg, image_bg, 1.0)\n\n                    assert np.all(image_aug == 0)\n                    assert image_aug.dtype.name == dtype\n                    assert image_aug.shape == shape\n\n    def test_other_dtypes_bool(self):\n        img_fg = np.full((3, 3, 1), 0, dtype=bool)\n        img_bg = np.full((3, 3, 1), 1, dtype=bool)\n        img_blend = blend.blend_alpha(img_fg, img_bg, 0.3, eps=0)\n        assert img_blend.dtype.name == \"bool\"\n        assert img_blend.shape == (3, 3, 1)\n        assert np.all(img_blend == 1)\n\n    def test_other_dtypes_bool_2d_arrays(self):\n        img_fg = np.full((3, 3), 0, dtype=bool)\n        img_bg = np.full((3, 3), 1, dtype=bool)\n        img_blend = blend.blend_alpha(img_fg, img_bg, 0.3, eps=0)\n        assert img_blend.dtype.name == \"bool\"\n        assert img_blend.shape == (3, 3)\n        assert np.all(img_blend == 1)\n\n    # TODO split this up into multiple tests\n    def test_other_dtypes_uint_int(self):\n        try:\n            high_res_dt = np.float128\n            dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                      \"int8\", \"int16\", \"int32\", \"int64\"]\n        except AttributeError:\n            # uint64 and int64 require float128 in their computation\n            high_res_dt = np.float64\n            dtypes = [\"uint8\", \"uint16\", \"uint32\",\n                      \"int8\", \"int16\", \"int32\"]\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                dtype = np.dtype(dtype)\n\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                values = [\n                    (0, 0),\n                    (0, 10),\n                    (10, 20),\n                    (min_value, min_value),\n                    (max_value, max_value),\n                    (min_value, max_value),\n                    (min_value, int(center_value)),\n                    (int(center_value), max_value),\n                    (int(center_value + 0.20 * max_value), max_value),\n                    (int(center_value + 0.27 * max_value), max_value),\n                    (int(center_value + 0.40 * max_value), max_value),\n                    (min_value, 0),\n                    (0, max_value)\n                ]\n                values = values + [(v2, v1) for v1, v2 in values]\n\n                for v1, v2 in values:\n                    v1_scalar = np.full((), v1, dtype=dtype)\n                    v2_scalar = np.full((), v2, dtype=dtype)\n                    \n                    img_fg = np.full((3, 3, 1), v1, dtype=dtype)\n                    img_bg = np.full((3, 3, 1), v2, dtype=dtype)\n                    img_blend = blend.blend_alpha(img_fg, img_bg, 1.0, eps=0)\n                    assert img_blend.dtype.name == np.dtype(dtype)\n                    assert img_blend.shape == (3, 3, 1)\n                    assert np.all(img_blend == v1_scalar)\n\n                    img_fg = np.full((3, 3, 1), v1, dtype=dtype)\n                    img_bg = np.full((3, 3, 1), v2, dtype=dtype)\n                    img_blend = blend.blend_alpha(img_fg, img_bg, 0.99, eps=0.1)\n                    assert img_blend.dtype.name == np.dtype(dtype)\n                    assert img_blend.shape == (3, 3, 1)\n                    assert np.all(img_blend == v1_scalar)\n\n                    img_fg = np.full((3, 3, 1), v1, dtype=dtype)\n                    img_bg = np.full((3, 3, 1), v2, dtype=dtype)\n                    img_blend = blend.blend_alpha(img_fg, img_bg, 0.0, eps=0)\n                    assert img_blend.dtype.name == np.dtype(dtype)\n                    assert img_blend.shape == (3, 3, 1)\n                    assert np.all(img_blend == v2_scalar)\n\n                    # TODO this test breaks for numpy <1.15 -- why?\n                    for c in sm.xrange(3):\n                        img_fg = np.full((3, 3, c), v1, dtype=dtype)\n                        img_bg = np.full((3, 3, c), v2, dtype=dtype)\n                        img_blend = blend.blend_alpha(\n                            img_fg, img_bg, 0.75, eps=0)\n                        assert img_blend.dtype.name == np.dtype(dtype)\n                        assert img_blend.shape == (3, 3, c)\n                        for ci in sm.xrange(c):\n                            v_blend = min(\n                                max(\n                                    int(\n                                        0.75*high_res_dt(v1)\n                                        + 0.25*high_res_dt(v2)\n                                    ),\n                                    min_value\n                                ),\n                                max_value)\n                            diff = (\n                                v_blend - img_blend\n                                if v_blend > img_blend[0, 0, ci]\n                                else img_blend - v_blend)\n                            assert np.all(diff < 1.01)\n\n                    img_fg = np.full((3, 3, 2), v1, dtype=dtype)\n                    img_bg = np.full((3, 3, 2), v2, dtype=dtype)\n                    img_blend = blend.blend_alpha(img_fg, img_bg, 0.75, eps=0)\n                    assert img_blend.dtype.name == np.dtype(dtype)\n                    assert img_blend.shape == (3, 3, 2)\n                    v_blend = min(\n                        max(\n                            int(\n                                0.75 * high_res_dt(v1)\n                                + 0.25 * high_res_dt(v2)\n                            ),\n                            min_value\n                        ),\n                        max_value)\n                    diff = (\n                        v_blend - img_blend\n                        if v_blend > img_blend[0, 0, 0]\n                        else img_blend - v_blend\n                    )\n                    assert np.all(diff < 1.01)\n\n                    img_fg = np.full((3, 3, 2), v1, dtype=dtype)\n                    img_bg = np.full((3, 3, 2), v2, dtype=dtype)\n                    img_blend = blend.blend_alpha(\n                        img_fg, img_bg, [1.0, 0.0], eps=0.1)\n                    assert img_blend.dtype.name == np.dtype(dtype).name\n                    assert img_blend.shape == (3, 3, 2)\n                    assert np.all(img_blend[:, :, 0] == v1_scalar)\n                    assert np.all(img_blend[:, :, 1] == v2_scalar)\n\n                    # elementwise, alphas.shape = (1, 2)\n                    img_fg = np.full((1, 2, 3), v1, dtype=dtype)\n                    img_bg = np.full((1, 2, 3), v2, dtype=dtype)\n                    alphas = np.zeros((1, 2), dtype=np.float64)\n                    alphas[:, :] = [1.0, 0.0]\n                    img_blend = blend.blend_alpha(img_fg, img_bg, alphas, eps=0)\n                    assert img_blend.dtype.name == np.dtype(dtype).name\n                    assert img_blend.shape == (1, 2, 3)\n                    assert np.all(img_blend[0, 0, :] == v1_scalar)\n                    assert np.all(img_blend[0, 1, :] == v2_scalar)\n\n                    # elementwise, alphas.shape = (1, 2, 1)\n                    img_fg = np.full((1, 2, 3), v1, dtype=dtype)\n                    img_bg = np.full((1, 2, 3), v2, dtype=dtype)\n                    alphas = np.zeros((1, 2, 1), dtype=np.float64)\n                    alphas[:, :, 0] = [1.0, 0.0]\n                    img_blend = blend.blend_alpha(img_fg, img_bg, alphas, eps=0)\n                    assert img_blend.dtype.name == np.dtype(dtype).name\n                    assert img_blend.shape == (1, 2, 3)\n                    assert np.all(img_blend[0, 0, :] == v1_scalar)\n                    assert np.all(img_blend[0, 1, :] == v2_scalar)\n\n                    # elementwise, alphas.shape = (1, 2, 3)\n                    img_fg = np.full((1, 2, 3), v1, dtype=dtype)\n                    img_bg = np.full((1, 2, 3), v2, dtype=dtype)\n                    alphas = np.zeros((1, 2, 3), dtype=np.float64)\n                    alphas[:, :, 0] = [1.0, 0.0]\n                    alphas[:, :, 1] = [0.0, 1.0]\n                    alphas[:, :, 2] = [1.0, 0.0]\n                    img_blend = blend.blend_alpha(img_fg, img_bg, alphas, eps=0)\n                    assert img_blend.dtype.name == np.dtype(dtype).name\n                    assert img_blend.shape == (1, 2, 3)\n                    assert np.all(img_blend[0, 0, [0, 2]] == v1_scalar)\n                    assert np.all(img_blend[0, 1, [0, 2]] == v2_scalar)\n                    assert np.all(img_blend[0, 0, 1] == v2_scalar)\n                    assert np.all(img_blend[0, 1, 1] == v1_scalar)\n\n    # TODO split this up into multiple tests\n    def test_other_dtypes_float(self):\n        try:\n            high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n        except AttributeError:\n            # float64 requires float128 in the computation\n            high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\"]\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                dtype = np.dtype(dtype)\n\n                def _allclose(a, b):\n                    atol = 1e-4 if dtype == np.float16 else 1e-8\n                    return np.allclose(a, b, atol=atol, rtol=0)\n\n                isize = np.dtype(dtype).itemsize\n                max_value = 1000 ** (isize - 1)\n                min_value = -max_value\n                center_value = 0\n                values = [\n                    (0, 0),\n                    (0, 10),\n                    (10, 20),\n                    (min_value, min_value),\n                    (max_value, max_value),\n                    (min_value, max_value),\n                    (min_value, center_value),\n                    (center_value, max_value),\n                    (center_value + 0.20 * max_value, max_value),\n                    (center_value + 0.27 * max_value, max_value),\n                    (center_value + 0.40 * max_value, max_value),\n                    (min_value, 0),\n                    (0, max_value)\n                ]\n                values = values + [(v2, v1) for v1, v2 in values]\n\n                max_float_dt = high_res_dt\n\n                for v1, v2 in values:\n                    v1_scalar = np.full((), v1, dtype=dtype)\n                    v2_scalar = np.full((), v2, dtype=dtype)\n\n                    img_fg = np.full((3, 3, 1), v1, dtype=dtype)\n                    img_bg = np.full((3, 3, 1), v2, dtype=dtype)\n                    img_blend = blend.blend_alpha(img_fg, img_bg, 1.0, eps=0)\n                    assert img_blend.dtype.name == np.dtype(dtype)\n                    assert img_blend.shape == (3, 3, 1)\n                    assert _allclose(img_blend, max_float_dt(v1))\n\n                    img_fg = np.full((3, 3, 1), v1, dtype=dtype)\n                    img_bg = np.full((3, 3, 1), v2, dtype=dtype)\n                    img_blend = blend.blend_alpha(\n                        img_fg, img_bg, 0.99, eps=0.1)\n                    assert img_blend.dtype.name == np.dtype(dtype)\n                    assert img_blend.shape == (3, 3, 1)\n                    assert _allclose(img_blend, max_float_dt(v1))\n\n                    img_fg = np.full((3, 3, 1), v1, dtype=dtype)\n                    img_bg = np.full((3, 3, 1), v2, dtype=dtype)\n                    img_blend = blend.blend_alpha(img_fg, img_bg, 0.0, eps=0)\n                    assert img_blend.dtype.name == np.dtype(dtype)\n                    assert img_blend.shape == (3, 3, 1)\n                    assert _allclose(img_blend, max_float_dt(v2))\n\n                    for c in sm.xrange(3):\n                        img_fg = np.full((3, 3, c), v1, dtype=dtype)\n                        img_bg = np.full((3, 3, c), v2, dtype=dtype)\n                        img_blend = blend.blend_alpha(\n                            img_fg, img_bg, 0.75, eps=0)\n                        assert img_blend.dtype.name == np.dtype(dtype)\n                        assert img_blend.shape == (3, 3, c)\n                        assert _allclose(\n                            img_blend,\n                            0.75*max_float_dt(v1) + 0.25*max_float_dt(v2)\n                        )\n\n                    img_fg = np.full((3, 3, 2), v1, dtype=dtype)\n                    img_bg = np.full((3, 3, 2), v2, dtype=dtype)\n                    img_blend = blend.blend_alpha(\n                        img_fg, img_bg, [1.0, 0.0], eps=0.1)\n                    assert img_blend.dtype.name == np.dtype(dtype)\n                    assert img_blend.shape == (3, 3, 2)\n                    assert _allclose(img_blend[:, :, 0], max_float_dt(v1))\n                    assert _allclose(img_blend[:, :, 1], max_float_dt(v2))\n\n                    # elementwise, alphas.shape = (1, 2)\n                    img_fg = np.full((1, 2, 3), v1, dtype=dtype)\n                    img_bg = np.full((1, 2, 3), v2, dtype=dtype)\n                    alphas = np.zeros((1, 2), dtype=np.float64)\n                    alphas[:, :] = [1.0, 0.0]\n                    img_blend = blend.blend_alpha(\n                        img_fg, img_bg, alphas, eps=0)\n                    assert img_blend.dtype.name == np.dtype(dtype)\n                    assert img_blend.shape == (1, 2, 3)\n                    assert _allclose(img_blend[0, 0, :], v1_scalar)\n                    assert _allclose(img_blend[0, 1, :], v2_scalar)\n\n                    # elementwise, alphas.shape = (1, 2, 1)\n                    img_fg = np.full((1, 2, 3), v1, dtype=dtype)\n                    img_bg = np.full((1, 2, 3), v2, dtype=dtype)\n                    alphas = np.zeros((1, 2, 1), dtype=np.float64)\n                    alphas[:, :, 0] = [1.0, 0.0]\n                    img_blend = blend.blend_alpha(\n                        img_fg, img_bg, alphas, eps=0)\n                    assert img_blend.dtype.name == np.dtype(dtype)\n                    assert img_blend.shape == (1, 2, 3)\n                    assert _allclose(img_blend[0, 0, :], v1_scalar)\n                    assert _allclose(img_blend[0, 1, :], v2_scalar)\n\n                    # elementwise, alphas.shape = (1, 2, 3)\n                    img_fg = np.full((1, 2, 3), v1, dtype=dtype)\n                    img_bg = np.full((1, 2, 3), v2, dtype=dtype)\n                    alphas = np.zeros((1, 2, 3), dtype=np.float64)\n                    alphas[:, :, 0] = [1.0, 0.0]\n                    alphas[:, :, 1] = [0.0, 1.0]\n                    alphas[:, :, 2] = [1.0, 0.0]\n                    img_blend = blend.blend_alpha(\n                        img_fg, img_bg, alphas, eps=0)\n                    assert img_blend.dtype.name == np.dtype(dtype)\n                    assert img_blend.shape == (1, 2, 3)\n                    assert _allclose(img_blend[0, 0, [0, 2]], v1_scalar)\n                    assert _allclose(img_blend[0, 1, [0, 2]], v2_scalar)\n                    assert _allclose(img_blend[0, 0, 1], v2_scalar)\n                    assert _allclose(img_blend[0, 1, 1], v1_scalar)\n\n\nclass TestAlpha(unittest.TestCase):\n    def test_deprecation_warning(self):\n        aug1 = iaa.Sequential([])\n        aug2 = iaa.Sequential([])\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            aug = iaa.Alpha(0.75, first=aug1, second=aug2)\n\n            assert (\n                \"is deprecated\"\n                in str(caught_warnings[-1].message)\n            )\n\n        assert isinstance(aug, iaa.BlendAlpha)\n        assert np.isclose(aug.factor.value, 0.75)\n        assert aug.foreground is aug1\n        assert aug.background is aug2\n\n\nclass TestBlendAlpha(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        base_img = np.zeros((3, 3, 1), dtype=np.uint8)\n        return base_img\n\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([[0.0, 0.0, 1.0],\n                                   [0.0, 0.0, 1.0],\n                                   [0.0, 1.0, 1.0]])\n        return HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def heatmaps_r1(self):\n        heatmaps_arr_r1 = np.float32([[0.0, 0.0, 0.0],\n                                      [0.0, 0.0, 0.0],\n                                      [0.0, 0.0, 1.0]])\n        return HeatmapsOnImage(heatmaps_arr_r1, shape=(3, 3, 3))\n\n    @property\n    def heatmaps_l1(self):\n        heatmaps_arr_l1 = np.float32([[0.0, 1.0, 0.0],\n                                      [0.0, 1.0, 0.0],\n                                      [1.0, 1.0, 0.0]])\n        return HeatmapsOnImage(heatmaps_arr_l1, shape=(3, 3, 3))\n\n    @property\n    def segmaps(self):\n        segmaps_arr = np.int32([[0, 0, 1],\n                                [0, 0, 1],\n                                [0, 1, 1]])\n        return SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def segmaps_r1(self):\n        segmaps_arr_r1 = np.int32([[0, 0, 0],\n                                   [0, 0, 0],\n                                   [0, 0, 1]])\n        return SegmentationMapsOnImage(segmaps_arr_r1, shape=(3, 3, 3))\n\n    @property\n    def segmaps_l1(self):\n        segmaps_arr_l1 = np.int32([[0, 1, 0],\n                                   [0, 1, 0],\n                                   [1, 1, 0]])\n        return SegmentationMapsOnImage(segmaps_arr_l1, shape=(3, 3, 3))\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=5, y=10), ia.Keypoint(x=6, y=11)]\n        return ia.KeypointsOnImage(kps, shape=(20, 20, 3))\n\n    @property\n    def psoi(self):\n        ps = [ia.Polygon([(5, 5), (10, 5), (10, 10)])]\n        return ia.PolygonsOnImage(ps, shape=(20, 20, 3))\n\n    @property\n    def lsoi(self):\n        lss = [ia.LineString([(5, 5), (10, 5), (10, 10)])]\n        return ia.LineStringsOnImage(lss, shape=(20, 20, 3))\n\n    @property\n    def bbsoi(self):\n        bbs = [ia.BoundingBox(x1=5, y1=6, x2=7, y2=8)]\n        return ia.BoundingBoxesOnImage(bbs, shape=(20, 20, 3))\n\n    def test_images_factor_is_1(self):\n        aug = iaa.BlendAlpha(1, iaa.Add(10), iaa.Add(20))\n        observed = aug.augment_image(self.image)\n        expected = np.round(self.image + 10).astype(np.uint8)\n        assert np.allclose(observed, expected)\n\n    def test_heatmaps_factor_is_1_with_affines_and_per_channel(self):\n        for per_channel in [False, True]:\n            with self.subTest(per_channel=per_channel):\n                aug = iaa.BlendAlpha(\n                    1,\n                    iaa.Affine(translate_px={\"x\": 1}),\n                    iaa.Affine(translate_px={\"x\": -1}),\n                    per_channel=per_channel)\n                observed = aug.augment_heatmaps([self.heatmaps])[0]\n                assert observed.shape == self.heatmaps.shape\n                assert 0 - 1e-6 < self.heatmaps.min_value < 0 + 1e-6\n                assert 1 - 1e-6 < self.heatmaps.max_value < 1 + 1e-6\n                assert np.allclose(observed.get_arr(),\n                                   self.heatmaps_r1.get_arr())\n\n    def test_segmaps_factor_is_1_with_affines_and_per_channel(self):\n        for per_channel in [False, True]:\n            with self.subTest(per_channel=per_channel):\n                aug = iaa.BlendAlpha(\n                    1,\n                    iaa.Affine(translate_px={\"x\": 1}),\n                    iaa.Affine(translate_px={\"x\": -1}),\n                    per_channel=per_channel)\n                observed = aug.augment_segmentation_maps([self.segmaps])[0]\n                assert observed.shape == self.segmaps.shape\n                assert np.array_equal(observed.get_arr(),\n                                      self.segmaps_r1.get_arr())\n\n    def test_images_factor_is_0(self):\n        aug = iaa.BlendAlpha(0, iaa.Add(10), iaa.Add(20))\n        observed = aug.augment_image(self.image)\n        expected = np.round(self.image + 20).astype(np.uint8)\n        assert np.allclose(observed, expected)\n\n    def test_heatmaps_factor_is_0_with_affines_and_per_channel(self):\n        for per_channel in [False, True]:\n            with self.subTest(per_channel=per_channel):\n                aug = iaa.BlendAlpha(\n                    0,\n                    iaa.Affine(translate_px={\"x\": 1}),\n                    iaa.Affine(translate_px={\"x\": -1}),\n                    per_channel=per_channel)\n                observed = aug.augment_heatmaps([self.heatmaps])[0]\n                assert observed.shape == self.heatmaps.shape\n                assert 0 - 1e-6 < self.heatmaps.min_value < 0 + 1e-6\n                assert 1 - 1e-6 < self.heatmaps.max_value < 1 + 1e-6\n                assert np.allclose(observed.get_arr(),\n                                   self.heatmaps_l1.get_arr())\n\n    def test_segmaps_factor_is_0_with_affines_and_per_channel(self):\n        for per_channel in [False, True]:\n            with self.subTest(per_channel=per_channel):\n                aug = iaa.BlendAlpha(\n                    0,\n                    iaa.Affine(translate_px={\"x\": 1}),\n                    iaa.Affine(translate_px={\"x\": -1}),\n                    per_channel=per_channel)\n                observed = aug.augment_segmentation_maps([self.segmaps])[0]\n                assert observed.shape == self.segmaps.shape\n                assert np.array_equal(observed.get_arr(),\n                                      self.segmaps_l1.get_arr())\n\n    def test_images_factor_is_075(self):\n        aug = iaa.BlendAlpha(0.75, iaa.Add(10), iaa.Add(20))\n        observed = aug.augment_image(self.image)\n        expected = np.round(\n            self.image\n            + 0.75 * 10\n            + 0.25 * 20\n        ).astype(np.uint8)\n        assert np.allclose(observed, expected)\n\n    def test_images_factor_is_075_fg_branch_is_none(self):\n        aug = iaa.BlendAlpha(0.75, None, iaa.Add(20))\n        observed = aug.augment_image(self.image + 10)\n        expected = np.round(\n            self.image\n            + 0.75 * 10\n            + 0.25 * (10 + 20)\n        ).astype(np.uint8)\n        assert np.allclose(observed, expected)\n\n    def test_images_factor_is_075_bg_branch_is_none(self):\n        aug = iaa.BlendAlpha(0.75, iaa.Add(10), None)\n        observed = aug.augment_image(self.image + 10)\n        expected = np.round(\n            self.image\n            + 0.75 * (10 + 10)\n            + 0.25 * 10\n        ).astype(np.uint8)\n        assert np.allclose(observed, expected)\n\n    def test_images_factor_is_tuple(self):\n        image = np.zeros((1, 2, 1), dtype=np.uint8)\n        nb_iterations = 1000\n        aug = iaa.BlendAlpha((0.0, 1.0), iaa.Add(10), iaa.Add(110))\n        values = []\n        for _ in sm.xrange(nb_iterations):\n            observed = aug.augment_image(image)\n            observed_val = np.round(np.average(observed)) - 10\n            values.append(observed_val / 100)\n\n        nb_bins = 5\n        hist, _ = np.histogram(values, bins=nb_bins, range=(0.0, 1.0),\n                               density=False)\n        density_expected = 1.0/nb_bins\n        density_tolerance = 0.05\n        for nb_samples in hist:\n            density = nb_samples / nb_iterations\n            assert np.isclose(density, density_expected,\n                              rtol=0, atol=density_tolerance)\n\n    def test_bad_datatype_for_factor_fails(self):\n        got_exception = False\n        try:\n            _ = iaa.BlendAlpha(False, iaa.Add(10), None)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_images_with_per_channel_in_both_alpha_and_child(self):\n        image = np.zeros((1, 1, 1000), dtype=np.uint8)\n        aug = iaa.BlendAlpha(\n            1.0,\n            iaa.Add((0, 100), per_channel=True),\n            None,\n            per_channel=True)\n        observed = aug.augment_image(image)\n        uq = np.unique(observed)\n        assert len(uq) > 1\n        assert np.max(observed) > 80\n        assert np.min(observed) < 20\n\n    def test_images_with_per_channel_in_alpha_and_tuple_as_factor(self):\n        image = np.zeros((1, 1, 1000), dtype=np.uint8)\n        aug = iaa.BlendAlpha(\n            (0.0, 1.0),\n            iaa.Add(100),\n            None,\n            per_channel=True)\n        observed = aug.augment_image(image)\n        uq = np.unique(observed)\n        assert len(uq) > 1\n        assert np.max(observed) > 80\n        assert np.min(observed) < 20\n\n    def test_images_float_as_per_channel_tuple_as_factor_two_branches(self):\n        aug = iaa.BlendAlpha(\n            (0.0, 1.0),\n            iaa.Add(100),\n            iaa.Add(0),\n            per_channel=0.5)\n        seen = [0, 0]\n        for _ in sm.xrange(200):\n            observed = aug.augment_image(np.zeros((1, 1, 100), dtype=np.uint8))\n            uq = np.unique(observed)\n            if len(uq) == 1:\n                seen[0] += 1\n            elif len(uq) > 1:\n                seen[1] += 1\n            else:\n                assert False\n        assert 100 - 50 < seen[0] < 100 + 50\n        assert 100 - 50 < seen[1] < 100 + 50\n\n    def test_bad_datatype_for_per_channel_fails(self):\n        # bad datatype for per_channel\n        got_exception = False\n        try:\n            _ = iaa.BlendAlpha(0.5, iaa.Add(10), None, per_channel=\"test\")\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_hooks_limiting_propagation(self):\n        aug = iaa.BlendAlpha(0.5, iaa.Add(100), iaa.Add(50), name=\"AlphaTest\")\n\n        def propagator(images, augmenter, parents, default):\n            if \"Alpha\" in augmenter.name:\n                return False\n            else:\n                return default\n\n        hooks = ia.HooksImages(propagator=propagator)\n        image = np.zeros((10, 10, 3), dtype=np.uint8) + 1\n        observed = aug.augment_image(image, hooks=hooks)\n        assert np.array_equal(observed, image)\n\n    def test_keypoints_factor_is_1(self):\n        self._test_cba_factor_is_1(\"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_0501(self):\n        self._test_cba_factor_is_0501(\"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_0(self):\n        self._test_cba_factor_is_0(\"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_0499(self):\n        self._test_cba_factor_is_0499(\"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_1_with_per_channel(self):\n        self._test_cba_factor_is_1_and_per_channel(\n            \"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_0_with_per_channel(self):\n        self._test_cba_factor_is_0_and_per_channel(\n            \"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_choice_of_vals_close_to_050_per_channel(self):\n        self._test_cba_factor_is_choice_around_050_and_per_channel(\n            \"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_are_empty(self):\n        self._test_empty_cba(\n            \"augment_keypoints\", ia.KeypointsOnImage([], shape=(1, 2, 3)))\n\n    def test_keypoints_hooks_limit_propagation(self):\n        self._test_cba_hooks_limit_propagation(\n            \"augment_keypoints\", self.kpsoi)\n\n    def test_polygons_factor_is_1(self):\n        self._test_cba_factor_is_1(\"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_0501(self):\n        self._test_cba_factor_is_0501(\"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_0(self):\n        self._test_cba_factor_is_0(\"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_0499(self):\n        self._test_cba_factor_is_0499(\"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_1_and_per_channel(self):\n        self._test_cba_factor_is_1_and_per_channel(\n            \"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_0_and_per_channel(self):\n        self._test_cba_factor_is_0_and_per_channel(\n            \"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_choice_around_050_and_per_channel(self):\n        self._test_cba_factor_is_choice_around_050_and_per_channel(\n            \"augment_polygons\", self.psoi\n        )\n\n    def test_empty_polygons(self):\n        return self._test_empty_cba(\n            \"augment_polygons\", ia.PolygonsOnImage([], shape=(1, 2, 3)))\n\n    def test_polygons_hooks_limit_propagation(self):\n        return self._test_cba_hooks_limit_propagation(\n            \"augment_polygons\", self.psoi)\n\n    def test_line_strings_factor_is_1(self):\n        self._test_cba_factor_is_1(\"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_0501(self):\n        self._test_cba_factor_is_0501(\"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_0(self):\n        self._test_cba_factor_is_0(\"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_0499(self):\n        self._test_cba_factor_is_0499(\"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_1_and_per_channel(self):\n        self._test_cba_factor_is_1_and_per_channel(\n            \"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_0_and_per_channel(self):\n        self._test_cba_factor_is_0_and_per_channel(\n            \"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_choice_around_050_and_per_channel(self):\n        self._test_cba_factor_is_choice_around_050_and_per_channel(\n            \"augment_line_strings\", self.lsoi\n        )\n\n    def test_empty_line_strings(self):\n        return self._test_empty_cba(\n            \"augment_line_strings\",\n            ia.LineStringsOnImage([], shape=(1, 2, 3)))\n\n    def test_line_strings_hooks_limit_propagation(self):\n        return self._test_cba_hooks_limit_propagation(\n            \"augment_line_strings\", self.lsoi)\n\n    def test_bounding_boxes_factor_is_1(self):\n        self._test_cba_factor_is_1(\"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_0501(self):\n        self._test_cba_factor_is_0501(\"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_0(self):\n        self._test_cba_factor_is_0(\"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_0499(self):\n        self._test_cba_factor_is_0499(\"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_1_and_per_channel(self):\n        self._test_cba_factor_is_1_and_per_channel(\n            \"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_0_and_per_channel(self):\n        self._test_cba_factor_is_0_and_per_channel(\n            \"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_choice_around_050_and_per_channel(self):\n        self._test_cba_factor_is_choice_around_050_and_per_channel(\n            \"augment_bounding_boxes\", self.bbsoi\n        )\n\n    def test_empty_bounding_boxes(self):\n        return self._test_empty_cba(\n            \"augment_bounding_boxes\",\n            ia.BoundingBoxesOnImage([], shape=(1, 2, 3)))\n\n    def test_bounding_boxes_hooks_limit_propagation(self):\n        return self._test_cba_hooks_limit_propagation(\n            \"augment_bounding_boxes\", self.bbsoi)\n\n    # Tests for CBA (=coordinate based augmentable) below. This currently\n    # covers keypoints, polygons and bounding boxes.\n\n    @classmethod\n    def _test_cba_factor_is_1(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlpha(\n            1.0, iaa.Identity(), iaa.Affine(translate_px={\"x\": 1}))\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        assert_cbaois_equal(observed[0], cbaoi)\n\n    @classmethod\n    def _test_cba_factor_is_0501(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlpha(0.501,\n                             iaa.Identity(),\n                             iaa.Affine(translate_px={\"x\": 1}))\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        assert_cbaois_equal(observed[0], cbaoi)\n\n    @classmethod\n    def _test_cba_factor_is_0(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlpha(\n            0.0, iaa.Identity(), iaa.Affine(translate_px={\"x\": 1}))\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        expected = cbaoi.shift(x=1)\n        assert_cbaois_equal(observed[0], expected)\n\n    @classmethod\n    def _test_cba_factor_is_0499(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlpha(0.499,\n                             iaa.Identity(),\n                             iaa.Affine(translate_px={\"x\": 1}))\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        expected = cbaoi.shift(x=1)\n        assert_cbaois_equal(observed[0], expected)\n\n    @classmethod\n    def _test_cba_factor_is_1_and_per_channel(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlpha(\n            1.0,\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}),\n            per_channel=True)\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        assert_cbaois_equal(observed[0], cbaoi)\n\n    @classmethod\n    def _test_cba_factor_is_0_and_per_channel(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlpha(\n            0.0,\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}),\n            per_channel=True)\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        expected = cbaoi.shift(x=1)\n        assert_cbaois_equal(observed[0], expected)\n\n    @classmethod\n    def _test_cba_factor_is_choice_around_050_and_per_channel(\n            cls, augf_name, cbaoi):\n        aug = iaa.BlendAlpha(\n            iap.Choice([0.49, 0.51]),\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}),\n            per_channel=True)\n        expected_same = cbaoi.deepcopy()\n        expected_shifted = cbaoi.shift(x=1)\n        seen = [0, 0, 0]\n        for _ in sm.xrange(200):\n            observed = getattr(aug, augf_name)([cbaoi])[0]\n\n            assert len(observed.items) == len(expected_same.items)\n            assert len(observed.items) == len(expected_shifted.items)\n\n            # We use here allclose() instead of coords_almost_equals()\n            # as the latter one is much slower for polygons and we don't have\n            # to deal with tricky geometry changes here, just naive shifting.\n            if np.allclose(observed.items[0].coords,\n                           expected_same.items[0].coords,\n                           rtol=0, atol=0.1):\n                seen[0] += 1\n            elif np.allclose(observed.items[0].coords,\n                             expected_shifted.items[0].coords,\n                             rtol=0, atol=0.1):\n                seen[1] += 1\n            else:\n                seen[2] += 1\n        assert 100 - 50 < seen[0] < 100 + 50\n        assert 100 - 50 < seen[1] < 100 + 50\n        assert seen[2] == 0\n\n    @classmethod\n    def _test_empty_cba(cls, augf_name, cbaoi):\n        # empty CBAs\n        aug = iaa.BlendAlpha(0.501,\n                             iaa.Identity(),\n                             iaa.Affine(translate_px={\"x\": 1}))\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert len(observed.items) == 0\n        assert observed.shape == cbaoi.shape\n\n    @classmethod\n    def _test_cba_hooks_limit_propagation(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlpha(\n            0.0,\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Affine(translate_px={\"y\": 1}),\n            name=\"AlphaTest\")\n\n        def propagator(cbaoi_to_aug, augmenter, parents, default):\n            if \"Alpha\" in augmenter.name:\n                return False\n            else:\n                return default\n\n        # no hooks for polygons yet, so we use HooksKeypoints\n        hooks = ia.HooksKeypoints(propagator=propagator)\n        observed = getattr(aug, augf_name)([cbaoi], hooks=hooks)[0]\n        assert observed.items[0].coords_almost_equals(cbaoi.items[0])\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 0, dtype=np.uint8)\n                aug = iaa.BlendAlpha(1.0, iaa.Add(1), iaa.Add(100))\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 0, dtype=np.uint8)\n                aug = iaa.BlendAlpha(1.0, iaa.Add(1), iaa.Add(100))\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_get_parameters(self):\n        fg = iaa.Identity()\n        bg = iaa.Sequential([iaa.Add(1)])\n        aug = iaa.BlendAlpha(0.65, fg, bg, per_channel=1)\n        params = aug.get_parameters()\n        assert params[0] is aug.factor\n        assert params[1] is aug.per_channel\n        assert 0.65 - 1e-6 < params[0].value < 0.65 + 1e-6\n        assert params[1].value == 1\n\n    def test_get_children_lists(self):\n        fg = iaa.Identity()\n        bg = iaa.Sequential([iaa.Add(1)])\n        aug = iaa.BlendAlpha(0.65, fg, bg, per_channel=1)\n        children_lsts = aug.get_children_lists()\n        assert len(children_lsts) == 2\n        assert ia.is_iterable([lst for lst in children_lsts])\n        assert fg in children_lsts[0]\n        assert bg == children_lsts[1]\n\n    def test_to_deterministic(self):\n        class _DummyAugmenter(iaa.Identity):\n            def __init__(self, *args, **kwargs):\n                super(_DummyAugmenter, self).__init__(*args, **kwargs)\n                self.deterministic_called = False\n\n            def _to_deterministic(self):\n                self.deterministic_called = True\n                return self\n\n        identity1 = _DummyAugmenter()\n        identity2 = _DummyAugmenter()\n        aug = iaa.BlendAlpha(0.5, identity1, identity2)\n\n        aug_det = aug.to_deterministic()\n\n        assert aug_det.deterministic\n        assert aug_det.random_state is not aug.random_state\n        assert aug_det.foreground.deterministic\n        assert aug_det.background.deterministic\n        assert identity1.deterministic_called is True\n        assert identity2.deterministic_called is True\n\n    def test_pickleable(self):\n        aug = iaa.BlendAlpha(\n            (0.1, 0.9),\n            iaa.Add((1, 10), seed=1),\n            iaa.Add((11, 20), seed=2),\n            per_channel=True,\n            seed=3)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\nclass _DummyMaskParameter(iap.StochasticParameter):\n    def __init__(self, inverted=False):\n        super(_DummyMaskParameter, self).__init__()\n        self.inverted = inverted\n\n    def _draw_samples(self, size, random_state):\n        h, w = size[0:2]\n        nb_channels = 1 if len(size) == 2 else size[2]\n        assert nb_channels <= 3\n        result = []\n        for i in np.arange(nb_channels):\n            if i == 0:\n                result.append(np.zeros((h, w), dtype=np.float32))\n            else:\n                result.append(np.ones((h, w), dtype=np.float32))\n        result = np.stack(result, axis=-1)\n        if len(size) == 2:\n            result = result[:, :, 0]\n        if self.inverted:\n            result = 1.0 - result\n        return result\n\n\nclass TestAlphaElementwise(unittest.TestCase):\n    def test_deprecation_warning(self):\n        aug1 = iaa.Sequential([])\n        aug2 = iaa.Sequential([])\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            aug = iaa.AlphaElementwise(factor=0.5, first=aug1, second=aug2)\n\n            assert (\n                \"is deprecated\"\n                in str(caught_warnings[-1].message)\n            )\n\n        assert isinstance(aug, iaa.BlendAlphaElementwise)\n        assert np.isclose(aug.factor.value, 0.5)\n        assert aug.foreground is aug1\n        assert aug.background is aug2\n\n\n# TODO add tests for heatmaps and segmaps that differ from the image size\nclass TestBlendAlphaElementwise(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        base_img = np.zeros((3, 3, 1), dtype=np.uint8)\n        return base_img\n\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([[0.0, 0.0, 1.0],\n                                   [0.0, 0.0, 1.0],\n                                   [0.0, 1.0, 1.0]])\n        return HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def heatmaps_r1(self):\n        heatmaps_arr_r1 = np.float32([[0.0, 0.0, 0.0],\n                                      [0.0, 0.0, 0.0],\n                                      [0.0, 0.0, 1.0]])\n        return HeatmapsOnImage(heatmaps_arr_r1, shape=(3, 3, 3))\n\n    @property\n    def heatmaps_l1(self):\n        heatmaps_arr_l1 = np.float32([[0.0, 1.0, 0.0],\n                                      [0.0, 1.0, 0.0],\n                                      [1.0, 1.0, 0.0]])\n\n        return HeatmapsOnImage(heatmaps_arr_l1, shape=(3, 3, 3))\n\n    @property\n    def segmaps(self):\n        segmaps_arr = np.int32([[0, 0, 1],\n                                [0, 0, 1],\n                                [0, 1, 1]])\n        return SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def segmaps_r1(self):\n        segmaps_arr_r1 = np.int32([[0, 0, 0],\n                                   [0, 0, 0],\n                                   [0, 0, 1]])\n        return SegmentationMapsOnImage(segmaps_arr_r1, shape=(3, 3, 3))\n\n    @property\n    def segmaps_l1(self):\n        segmaps_arr_l1 = np.int32([[0, 1, 0],\n                                   [0, 1, 0],\n                                   [1, 1, 0]])\n        return SegmentationMapsOnImage(segmaps_arr_l1, shape=(3, 3, 3))\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=5, y=10), ia.Keypoint(x=6, y=11)]\n        return ia.KeypointsOnImage(kps, shape=(20, 20, 3))\n\n    @property\n    def psoi(self):\n        ps = [ia.Polygon([(5, 5), (10, 5), (10, 10)])]\n        return ia.PolygonsOnImage(ps, shape=(20, 20, 3))\n\n    @property\n    def lsoi(self):\n        lss = [ia.LineString([(5, 5), (10, 5), (10, 10)])]\n        return ia.LineStringsOnImage(lss, shape=(20, 20, 3))\n\n    @property\n    def bbsoi(self):\n        bbs = [ia.BoundingBox(x1=5, y1=6, x2=7, y2=8)]\n        return ia.BoundingBoxesOnImage(bbs, shape=(20, 20, 3))\n\n    def test_images_factor_is_1(self):\n        aug = iaa.BlendAlphaElementwise(1, iaa.Add(10), iaa.Add(20))\n        observed = aug.augment_image(self.image)\n        expected = self.image + 10\n        assert np.allclose(observed, expected)\n\n    def test_heatmaps_factor_is_1_with_affines(self):\n        aug = iaa.BlendAlphaElementwise(\n            1,\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Affine(translate_px={\"x\": -1}))\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n        assert observed.shape == (3, 3, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps_r1.get_arr())\n\n    def test_segmaps_factor_is_1_with_affines(self):\n        aug = iaa.BlendAlphaElementwise(\n            1,\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Affine(translate_px={\"x\": -1}))\n        observed = aug.augment_segmentation_maps([self.segmaps])[0]\n        assert observed.shape == (3, 3, 3)\n        assert np.array_equal(observed.get_arr(), self.segmaps_r1.get_arr())\n\n    def test_images_factor_is_0(self):\n        aug = iaa.BlendAlphaElementwise(0, iaa.Add(10), iaa.Add(20))\n        observed = aug.augment_image(self.image)\n        expected = self.image + 20\n        assert np.allclose(observed, expected)\n\n    def test_heatmaps_factor_is_0_with_affines(self):\n        aug = iaa.BlendAlphaElementwise(\n            0,\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Affine(translate_px={\"x\": -1}))\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n        assert observed.shape == (3, 3, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps_l1.get_arr())\n\n    def test_segmaps_factor_is_0_with_affines(self):\n        aug = iaa.BlendAlphaElementwise(\n            0,\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Affine(translate_px={\"x\": -1}))\n        observed = aug.augment_segmentation_maps([self.segmaps])[0]\n        assert observed.shape == (3, 3, 3)\n        assert np.array_equal(observed.get_arr(), self.segmaps_l1.get_arr())\n\n    def test_images_factor_is_075(self):\n        aug = iaa.BlendAlphaElementwise(0.75, iaa.Add(10), iaa.Add(20))\n        observed = aug.augment_image(self.image)\n        expected = np.round(\n            self.image + 0.75 * 10 + 0.25 * 20\n        ).astype(np.uint8)\n        assert np.allclose(observed, expected, atol=1.01)\n\n    def test_images_factor_is_075_fg_branch_is_none(self):\n        aug = iaa.BlendAlphaElementwise(0.75, None, iaa.Add(20))\n        observed = aug.augment_image(self.image + 10)\n        expected = np.round(\n            self.image + 0.75 * 10 + 0.25 * (10 + 20)\n        ).astype(np.uint8)\n        assert np.allclose(observed, expected, atol=1.01)\n\n    def test_images_factor_is_075_bg_branch_is_none(self):\n        aug = iaa.BlendAlphaElementwise(0.75, iaa.Add(10), None)\n        observed = aug.augment_image(self.image + 10)\n        expected = np.round(\n            self.image + 0.75 * (10 + 10) + 0.25 * 10\n        ).astype(np.uint8)\n        assert np.allclose(observed, expected, atol=1.01)\n\n    def test_images_factor_is_tuple(self):\n        image = np.zeros((100, 100), dtype=np.uint8)\n        aug = iaa.BlendAlphaElementwise((0.0, 1.0), iaa.Add(10), iaa.Add(110))\n        observed = (aug.augment_image(image) - 10) / 100\n        nb_bins = 10\n        hist, _ = np.histogram(\n            observed.flatten(), bins=nb_bins, range=(0.0, 1.0), density=False)\n        density_expected = 1.0/nb_bins\n        density_tolerance = 0.05\n        for nb_samples in hist:\n            density = nb_samples / observed.size\n            assert np.isclose(density, density_expected,\n                              rtol=0, atol=density_tolerance)\n\n    def test_bad_datatype_for_factor_fails(self):\n        got_exception = False\n        try:\n            _ = iaa.BlendAlphaElementwise(False, iaa.Add(10), None)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_images_with_per_channel_in_alpha_and_tuple_as_factor(self):\n        image = np.zeros((1, 1, 100), dtype=np.uint8)\n        aug = iaa.BlendAlphaElementwise(\n            (0.0, 1.0),\n            iaa.Add(10),\n            iaa.Add(110),\n            per_channel=True)\n        observed = aug.augment_image(image)\n        assert len(set(observed.flatten())) > 1\n\n    def test_bad_datatype_for_per_channel_fails(self):\n        got_exception = False\n        try:\n            _ = iaa.BlendAlphaElementwise(\n                0.5,\n                iaa.Add(10),\n                None,\n                per_channel=\"test\")\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_hooks_limiting_propagation(self):\n        aug = iaa.BlendAlphaElementwise(\n            0.5,\n            iaa.Add(100),\n            iaa.Add(50),\n            name=\"AlphaElementwiseTest\")\n\n        def propagator(images, augmenter, parents, default):\n            if \"AlphaElementwise\" in augmenter.name:\n                return False\n            else:\n                return default\n\n        hooks = ia.HooksImages(propagator=propagator)\n        image = np.zeros((10, 10, 3), dtype=np.uint8) + 10\n        observed = aug.augment_image(image, hooks=hooks)\n        assert np.array_equal(observed, image)\n\n    def test_heatmaps_and_per_channel_factor_is_zeros(self):\n        aug = iaa.BlendAlphaElementwise(\n            _DummyMaskParameter(inverted=False),\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Affine(translate_px={\"x\": -1}),\n            per_channel=True)\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n        assert observed.shape == (3, 3, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps_r1.get_arr())\n\n    def test_heatmaps_and_per_channel_factor_is_ones(self):\n        aug = iaa.BlendAlphaElementwise(\n            _DummyMaskParameter(inverted=True),\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Affine(translate_px={\"x\": -1}),\n            per_channel=True)\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n        assert observed.shape == (3, 3, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps_l1.get_arr())\n\n    def test_segmaps_and_per_channel_factor_is_zeros(self):\n        aug = iaa.BlendAlphaElementwise(\n            _DummyMaskParameter(inverted=False),\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Affine(translate_px={\"x\": -1}),\n            per_channel=True)\n        observed = aug.augment_segmentation_maps([self.segmaps])[0]\n        assert observed.shape == (3, 3, 3)\n        assert np.array_equal(observed.get_arr(), self.segmaps_r1.get_arr())\n\n    def test_segmaps_and_per_channel_factor_is_ones(self):\n        aug = iaa.BlendAlphaElementwise(\n            _DummyMaskParameter(inverted=True),\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Affine(translate_px={\"x\": -1}),\n            per_channel=True)\n        observed = aug.augment_segmentation_maps([self.segmaps])[0]\n        assert observed.shape == (3, 3, 3)\n        assert np.array_equal(observed.get_arr(), self.segmaps_l1.get_arr())\n\n    def test_keypoints_factor_is_1(self):\n        self._test_cba_factor_is_1(\"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_0501(self):\n        self._test_cba_factor_is_0501(\"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_0(self):\n        self._test_cba_factor_is_0(\"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_0499(self):\n        self._test_cba_factor_is_0499(\"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_1_with_per_channel(self):\n        self._test_cba_factor_is_1_and_per_channel(\n            \"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_0_with_per_channel(self):\n        self._test_cba_factor_is_0_and_per_channel(\n            \"augment_keypoints\", self.kpsoi)\n\n    def test_keypoints_factor_is_choice_of_vals_close_050_per_channel(self):\n        # TODO can this somehow be integrated into the CBA functions below?\n        aug = iaa.BlendAlpha(\n            iap.Choice([0.49, 0.51]),\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}),\n            per_channel=True)\n        kpsoi = self.kpsoi\n\n        expected_same = kpsoi.deepcopy()\n        expected_both_shifted = kpsoi.shift(x=1)\n        expected_fg_shifted = ia.KeypointsOnImage(\n            [kpsoi.keypoints[0].shift(x=1), kpsoi.keypoints[1]],\n            shape=self.kpsoi.shape)\n        expected_bg_shifted = ia.KeypointsOnImage(\n            [kpsoi.keypoints[0], kpsoi.keypoints[1].shift(x=1)],\n            shape=self.kpsoi.shape)\n\n        seen = [0, 0]\n        for _ in sm.xrange(200):\n            observed = aug.augment_keypoints([kpsoi])[0]\n            if keypoints_equal([observed], [expected_same]):\n                seen[0] += 1\n            elif keypoints_equal([observed], [expected_both_shifted]):\n                seen[1] += 1\n            elif keypoints_equal([observed], [expected_fg_shifted]):\n                seen[2] += 1\n            elif keypoints_equal([observed], [expected_bg_shifted]):\n                seen[3] += 1\n            else:\n                assert False\n        assert 100 - 50 < seen[0] < 100 + 50\n        assert 100 - 50 < seen[1] < 100 + 50\n\n    def test_keypoints_are_empty(self):\n        kpsoi = ia.KeypointsOnImage([], shape=(1, 2, 3))\n        self._test_empty_cba(\"augment_keypoints\", kpsoi)\n\n    def test_keypoints_hooks_limit_propagation(self):\n        self._test_cba_hooks_limit_propagation(\"augment_keypoints\", self.kpsoi)\n\n    def test_polygons_factor_is_1(self):\n        self._test_cba_factor_is_1(\"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_0501(self):\n        self._test_cba_factor_is_0501(\"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_0(self):\n        self._test_cba_factor_is_0(\"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_0499(self):\n        self._test_cba_factor_is_0499(\"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_1_and_per_channel(self):\n        self._test_cba_factor_is_1_and_per_channel(\n            \"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_0_and_per_channel(self):\n        self._test_cba_factor_is_0_and_per_channel(\n            \"augment_polygons\", self.psoi)\n\n    def test_polygons_factor_is_choice_around_050_and_per_channel(self):\n        # We use more points here to verify the\n        # either-or-mode (pointwise=False). The probability that all points\n        # move in the same way be coincidence is extremely low for so many.\n        ps = [ia.Polygon([(0, 0), (15, 0), (10, 0), (10, 5), (10, 10),\n                          (5, 10), (5, 5), (0, 10), (0, 5), (0, 0)])]\n        psoi = ia.PolygonsOnImage(ps, shape=(15, 15, 3))\n        self._test_cba_factor_is_choice_around_050_and_per_channel(\n            \"augment_polygons\", psoi, pointwise=False\n        )\n\n    def test_empty_polygons(self):\n        psoi = ia.PolygonsOnImage([], shape=(1, 2, 3))\n        self._test_empty_cba(\"augment_polygons\", psoi)\n\n    def test_polygons_hooks_limit_propagation(self):\n        self._test_cba_hooks_limit_propagation(\"augment_polygons\", self.psoi)\n\n    def test_line_strings_factor_is_1(self):\n        self._test_cba_factor_is_1(\"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_0501(self):\n        self._test_cba_factor_is_0501(\"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_0(self):\n        self._test_cba_factor_is_0(\"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_0499(self):\n        self._test_cba_factor_is_0499(\"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_1_and_per_channel(self):\n        self._test_cba_factor_is_1_and_per_channel(\n            \"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_0_and_per_channel(self):\n        self._test_cba_factor_is_0_and_per_channel(\n            \"augment_line_strings\", self.lsoi)\n\n    def test_line_strings_factor_is_choice_around_050_and_per_channel(self):\n        # see same polygons test for why self.lsoi is not used here\n        lss = [ia.LineString([(0, 0), (15, 0), (10, 0), (10, 5), (10, 10),\n                              (5, 10), (5, 5), (0, 10), (0, 5), (0, 0)])]\n        lsoi = ia.LineStringsOnImage(lss, shape=(15, 15, 3))\n        self._test_cba_factor_is_choice_around_050_and_per_channel(\n            \"augment_line_strings\", lsoi, pointwise=False\n        )\n\n    def test_empty_line_strings(self):\n        lsoi = ia.LineStringsOnImage([], shape=(1, 2, 3))\n        self._test_empty_cba(\"augment_line_strings\", lsoi)\n\n    def test_line_strings_hooks_limit_propagation(self):\n        self._test_cba_hooks_limit_propagation(\n            \"augment_line_strings\", self.lsoi)\n\n    def test_bounding_boxes_factor_is_1(self):\n        self._test_cba_factor_is_1(\"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_0501(self):\n        self._test_cba_factor_is_0501(\"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_0(self):\n        self._test_cba_factor_is_0(\"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_0499(self):\n        self._test_cba_factor_is_0499(\"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_1_and_per_channel(self):\n        self._test_cba_factor_is_1_and_per_channel(\n            \"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_0_and_per_channel(self):\n        self._test_cba_factor_is_0_and_per_channel(\n            \"augment_bounding_boxes\", self.bbsoi)\n\n    def test_bounding_boxes_factor_is_choice_around_050_and_per_channel(self):\n        # TODO pointwise=True or False makes no difference here, because\n        #      there aren't enough points (see corresponding polygon test)\n        self._test_cba_factor_is_choice_around_050_and_per_channel(\n            \"augment_bounding_boxes\", self.bbsoi, pointwise=False\n        )\n\n    def test_empty_bounding_boxes(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(1, 2, 3))\n        self._test_empty_cba(\"augment_bounding_boxes\", bbsoi)\n\n    def test_bounding_boxes_hooks_limit_propagation(self):\n        self._test_cba_hooks_limit_propagation(\n            \"augment_bounding_boxes\", self.bbsoi)\n\n    @classmethod\n    def _test_cba_factor_is_1(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlphaElementwise(\n            1.0,\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}))\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        assert_cbaois_equal(observed[0], cbaoi)\n\n    @classmethod\n    def _test_cba_factor_is_0501(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlphaElementwise(\n            0.501,\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}))\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        assert_cbaois_equal(observed[0], cbaoi)\n\n    @classmethod\n    def _test_cba_factor_is_0(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlphaElementwise(\n            0.0,\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}))\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        expected = cbaoi.shift(x=1)\n        assert_cbaois_equal(observed[0], expected)\n\n    @classmethod\n    def _test_cba_factor_is_0499(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlphaElementwise(\n            0.499,\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}))\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        expected = cbaoi.shift(x=1)\n        assert_cbaois_equal(observed[0], expected)\n\n    @classmethod\n    def _test_cba_factor_is_1_and_per_channel(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlphaElementwise(\n            1.0,\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}),\n            per_channel=True)\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        assert_cbaois_equal(observed[0], cbaoi)\n\n    @classmethod\n    def _test_cba_factor_is_0_and_per_channel(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlphaElementwise(\n            0.0,\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}),\n            per_channel=True)\n\n        observed = getattr(aug, augf_name)([cbaoi])\n\n        expected = cbaoi.shift(x=1)\n        assert_cbaois_equal(observed[0], expected)\n\n    @classmethod\n    def _test_cba_factor_is_choice_around_050_and_per_channel(\n            cls, augf_name, cbaoi, pointwise):\n        aug = iaa.BlendAlphaElementwise(\n            iap.Choice([0.49, 0.51]),\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}),\n            per_channel=True)\n\n        expected_same = cbaoi.deepcopy()\n        expected_shifted = cbaoi.shift(x=1)\n\n        nb_iterations = 400\n        seen = [0, 0, 0]\n        for _ in sm.xrange(nb_iterations):\n            observed = getattr(aug, augf_name)([cbaoi])[0]\n            # We use here allclose() instead of coords_almost_equals()\n            # as the latter one is much slower for polygons and we don't have\n            # to deal with tricky geometry changes here, just naive shifting.\n            if np.allclose(observed.items[0].coords,\n                           expected_same.items[0].coords,\n                           rtol=0, atol=0.1):\n                seen[0] += 1\n            elif np.allclose(observed.items[0].coords,\n                             expected_shifted.items[0].coords,\n                             rtol=0, atol=0.1):\n                seen[1] += 1\n            else:\n                seen[2] += 1\n\n        if pointwise:\n            # This code can be used if the polygon augmentation mode is\n            # AlphaElementwise._MODE_POINTWISE. Currently it is _MODE_EITHER_OR.\n            nb_points = len(cbaoi.items[0].coords)\n            p_all_same = 2 * ((1/2)**nb_points)  # all points moved in same way\n            expected_iter = nb_iterations*p_all_same\n            expected_iter_notsame = nb_iterations*(1-p_all_same)\n            atol = nb_iterations * (5*p_all_same)\n\n            assert np.isclose(seen[0], expected_iter, rtol=0, atol=atol)\n            assert np.isclose(seen[1], expected_iter, rtol=0, atol=atol)\n            assert np.isclose(seen[2], expected_iter_notsame, rtol=0, atol=atol)\n        else:\n            expected_iter = nb_iterations*0.5\n            atol = nb_iterations*0.15\n            assert np.isclose(seen[0], expected_iter, rtol=0, atol=atol)\n            assert np.isclose(seen[1], expected_iter, rtol=0, atol=atol)\n            assert seen[2] == 0\n\n    @classmethod\n    def _test_empty_cba(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlphaElementwise(\n            0.501,\n            iaa.Identity(),\n            iaa.Affine(translate_px={\"x\": 1}))\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert len(observed.items) == 0\n        assert observed.shape == (1, 2, 3)\n\n    @classmethod\n    def _test_cba_hooks_limit_propagation(cls, augf_name, cbaoi):\n        aug = iaa.BlendAlphaElementwise(\n            0.0,\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Affine(translate_px={\"y\": 1}),\n            name=\"AlphaTest\")\n\n        def propagator(cbaoi_to_aug, augmenter, parents, default):\n            if \"Alpha\" in augmenter.name:\n                return False\n            else:\n                return default\n\n        # no hooks for polygons yet, so we use HooksKeypoints\n        hooks = ia.HooksKeypoints(propagator=propagator)\n        observed = getattr(aug, augf_name)([cbaoi], hooks=hooks)[0]\n        assert observed.items[0].coords_almost_equals(cbaoi.items[0])\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 0, dtype=np.uint8)\n                aug = iaa.BlendAlpha(1.0, iaa.Add(1), iaa.Add(100))\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 0, dtype=np.uint8)\n                aug = iaa.BlendAlpha(1.0, iaa.Add(1), iaa.Add(100))\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.BlendAlphaElementwise(\n            (0.1, 0.9),\n            iaa.Add((1, 10), seed=1),\n            iaa.Add((11, 20), seed=2),\n            per_channel=True,\n            seed=3)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\nclass TestBlendAlphaSomeColors(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        child1 = iaa.Sequential([])\n        child2 = iaa.Sequential([])\n        aug = iaa.BlendAlphaSomeColors(child1, child2)\n        assert aug.foreground is child1\n        assert aug.background is child2\n        assert isinstance(aug.mask_generator, iaa.SomeColorsMaskGen)\n\n    def test_grayscale_drops_different_colors(self):\n        image = np.uint8([\n            [255, 0, 0],\n            [0, 255, 0],\n            [0, 0, 255],\n            [255, 255, 0],\n            [255, 0, 255],\n            [0, 255, 255],\n            [255, 128, 128],\n            [128, 255, 128],\n            [128, 128, 255]\n        ]).reshape((1, 9, 3))\n        image_gray = iaa.Grayscale(1.0)(image=image)\n        aug = iaa.BlendAlphaSomeColors(iaa.Grayscale(1.0),\n                                       nb_bins=256, smoothness=0)\n\n        nb_grayscaled = []\n        for _ in sm.xrange(50):\n            image_aug = aug(image=image)\n            grayscaled = np.sum((image_aug == image_gray).astype(np.int32),\n                                axis=2)\n            assert np.all(np.logical_or(grayscaled == 0, grayscaled == 3))\n            nb_grayscaled.append(np.sum(grayscaled == 3))\n\n        assert len(set(nb_grayscaled)) >= 5\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0, 3),\n            (0, 1, 3),\n            (1, 0, 3)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 0, dtype=np.uint8)\n                aug = iaa.BlendAlphaSomeColors(iaa.Add(1), iaa.Add(100))\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.BlendAlphaSomeColors(\n            iaa.Add((1, 10), seed=1),\n            iaa.Add((11, 20), seed=2),\n            seed=3)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\nclass TestBlendAlphaHorizontalLinearGradient(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        child1 = iaa.Sequential([])\n        child2 = iaa.Sequential([])\n        aug = iaa.BlendAlphaHorizontalLinearGradient(child1, child2)\n        assert aug.foreground is child1\n        assert aug.background is child2\n        assert isinstance(aug.mask_generator,\n                          iaa.HorizontalLinearGradientMaskGen)\n\n    def test_single_image(self):\n        image = np.full((2, 100, 3), 255, dtype=np.uint8)\n        image_drop = iaa.TotalDropout(1.0)(image=image)\n\n        aug = iaa.BlendAlphaHorizontalLinearGradient(iaa.TotalDropout(1.0),\n                                                     min_value=0.0,\n                                                     max_value=1.0,\n                                                     start_at=0.2,\n                                                     end_at=0.8)\n        image_aug = aug(image=image)\n\n        assert np.array_equal(image_aug[0, :, :], image_aug[1, :, :])\n        assert np.array_equal(image_aug[:, :20, :], image[:, :20, :])\n        assert np.array_equal(image_aug[:, 80:, :], image_drop[:, 80:, :])\n        assert not np.array_equal(image_aug[:, 20:80, :], image[:, 20:80, :])\n        assert not np.array_equal(image_aug[:, 20:80, :],\n                                  image_drop[:, 20:80, :])\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 0, dtype=np.uint8)\n                aug = iaa.BlendAlphaHorizontalLinearGradient(\n                    iaa.TotalDropout(1.0))\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.BlendAlphaHorizontalLinearGradient(\n            iaa.Add((1, 10), seed=1),\n            iaa.Add((11, 20), seed=2),\n            seed=3)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\nclass TestBlendAlphaVerticalLinearGradient(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        child1 = iaa.Sequential([])\n        child2 = iaa.Sequential([])\n        aug = iaa.BlendAlphaVerticalLinearGradient(child1, child2)\n        assert aug.foreground is child1\n        assert aug.background is child2\n        assert isinstance(aug.mask_generator,\n                          iaa.VerticalLinearGradientMaskGen)\n\n    def test_single_image(self):\n        image = np.full((100, 2, 3), 255, dtype=np.uint8)\n        image_drop = iaa.TotalDropout(1.0)(image=image)\n\n        aug = iaa.BlendAlphaVerticalLinearGradient(iaa.TotalDropout(1.0),\n                                                   min_value=0.0,\n                                                   max_value=1.0,\n                                                   start_at=0.2,\n                                                   end_at=0.8)\n        image_aug = aug(image=image)\n\n        assert np.array_equal(image_aug[:, 0, :], image_aug[:, 0, :])\n        assert np.array_equal(image_aug[:20, :, :], image[:20, :, :])\n        assert np.array_equal(image_aug[80:, :, :], image_drop[80:, :, :])\n        assert not np.array_equal(image_aug[20:80, :, :], image[20:80, :, :])\n        assert not np.array_equal(image_aug[20:80, :, :],\n                                  image_drop[20:80, :, :])\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 0, dtype=np.uint8)\n                aug = iaa.BlendAlphaVerticalLinearGradient(\n                    iaa.TotalDropout(1.0))\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.BlendAlphaVerticalLinearGradient(\n            iaa.Add((1, 10), seed=1),\n            iaa.Add((11, 20), seed=2),\n            seed=3)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\nclass TestBlendAlphaRegularGrid(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        child1 = iaa.Sequential([])\n        child2 = iaa.Sequential([])\n        aug = iaa.BlendAlphaRegularGrid(2, 3, child1, child2, alpha=0.7)\n        assert aug.mask_generator.nb_rows.value == 2\n        assert aug.mask_generator.nb_cols.value == 3\n        assert aug.foreground is child1\n        assert aug.background is child2\n        assert isinstance(aug.mask_generator, iaa.RegularGridMaskGen)\n        assert np.isclose(aug.mask_generator.alpha.value, 0.7)\n\n    def test_single_image(self):\n        image = np.full((2, 6, 3), 255, dtype=np.uint8)\n\n        aug = iaa.BlendAlphaRegularGrid(\n            nb_rows=1, nb_cols=3,\n            foreground=iaa.TotalDropout(1.0),\n            alpha=iap.DeterministicList([1.0, 0.0, 1.0]))\n        image_aug = aug(image=image)\n\n        expected = np.copy(image)\n        expected[:, 0:2, :] = 0\n        expected[:, 4:6, :] = 0\n        assert np.array_equal(image_aug, expected)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 0, dtype=np.uint8)\n                aug = iaa.BlendAlphaRegularGrid(\n                    nb_rows=2, nb_cols=3, foreground=iaa.TotalDropout(1.0))\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.BlendAlphaRegularGrid(\n            2, 3,\n            iaa.Add((1, 10), seed=1),\n            iaa.Add((11, 20), seed=2),\n            seed=3)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\nclass TestBlendAlphaCheckerboard(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        child1 = iaa.Sequential([])\n        child2 = iaa.Sequential([])\n        aug = iaa.BlendAlphaCheckerboard(2, 3, child1, child2)\n        assert aug.mask_generator.nb_rows.value == 2\n        assert aug.mask_generator.nb_cols.value == 3\n        assert aug.foreground is child1\n        assert aug.background is child2\n        assert isinstance(aug.mask_generator, iaa.CheckerboardMaskGen)\n\n    def test_single_image(self):\n        image = np.full((2, 6, 3), 255, dtype=np.uint8)\n\n        aug = iaa.BlendAlphaCheckerboard(nb_rows=1, nb_cols=3,\n                                         foreground=iaa.TotalDropout(1.0))\n        image_aug = aug(image=image)\n\n        expected = np.copy(image)\n        expected[:, 0:2, :] = 0\n        expected[:, 4:6, :] = 0\n        assert np.array_equal(image_aug, expected)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 0, dtype=np.uint8)\n                aug = iaa.BlendAlphaCheckerboard(\n                    nb_rows=2, nb_cols=3, foreground=iaa.TotalDropout(1.0))\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.BlendAlphaCheckerboard(\n            2, 3,\n            iaa.Add((1, 10), seed=1),\n            iaa.Add((11, 20), seed=2),\n            seed=3)\n        runtest_pickleable_uint8_img(aug, iterations=3)\n\n\nclass TestBlendAlphaSegMapClassIds(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        child1 = iaa.Sequential([])\n        child2 = iaa.Sequential([])\n        aug = iaa.BlendAlphaSegMapClassIds(\n            2,\n            nb_sample_classes=1,\n            foreground=child1,\n            background=child2\n        )\n        assert aug.foreground is child1\n        assert aug.background is child2\n        assert isinstance(aug.mask_generator,\n                          iaa.SegMapClassIdsMaskGen)\n        assert aug.mask_generator.class_ids.value == 2\n        assert aug.mask_generator.nb_sample_classes.value == 1\n\n    def test_single_image(self):\n        image = np.full((10, 10, 3), 255, dtype=np.uint8)\n        segmap_arr = np.zeros((5, 10, 1), dtype=np.int32)\n        segmap_arr[0:2, :] = 1\n        aug = iaa.BlendAlphaSegMapClassIds(\n            1,\n            nb_sample_classes=1,\n            foreground=iaa.TotalDropout(1.0)\n        )\n\n        image_aug, segmap_aug = aug(image=image,\n                                    segmentation_maps=[segmap_arr])\n\n        assert np.allclose(image_aug[0:4, :, :], 0, rtol=0, atol=1.01)\n        assert np.allclose(image_aug[4:, :, :], 255, rtol=0, atol=1.01)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 255, dtype=np.uint8)\n                segmap_arr = np.zeros((2, 2, 1), dtype=np.int32)\n                segmap_arr[0, 0] = 2\n                aug = iaa.BlendAlphaSegMapClassIds(\n                    2,\n                    foreground=iaa.TotalDropout(1.0))\n\n                image_aug, segmap_aug = aug(\n                    image=image, segmentation_maps=[segmap_arr])\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        shape = (15, 15, 3)\n        iterations = 3\n        augmenter = iaa.BlendAlphaSegMapClassIds(\n            [1, 2],\n            foreground=iaa.Add((1, 10), seed=1),\n            background=iaa.Add((11, 20), seed=2),\n            nb_sample_classes=1,\n            seed=3)\n        image = np.mod(np.arange(int(np.prod(shape))), 256).astype(np.uint8)\n        image = image.reshape(shape)\n        segmap_arr = np.zeros((15, 15, 1), dtype=np.int32)\n        segmap_arr[0:2, 0:2] = 1\n        segmap_arr[4:6, 5:8] = 2\n\n        augmenter_pkl = pickle.loads(pickle.dumps(augmenter, protocol=-1))\n\n        for _ in np.arange(iterations):\n            image_aug, sm_aug = augmenter(\n                image=image, segmentation_maps=[segmap_arr])\n            image_aug_pkl, sm_aug_pkl = augmenter_pkl(\n                image=image, segmentation_maps=[segmap_arr])\n            assert np.array_equal(image_aug, image_aug_pkl)\n            assert np.array_equal(sm_aug, sm_aug_pkl)\n\n\nclass TestBlendAlphaBoundingBoxes(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        child1 = iaa.Sequential([])\n        child2 = iaa.Sequential([])\n        aug = iaa.BlendAlphaBoundingBoxes(\n            \"person\",\n            nb_sample_labels=1,\n            foreground=child1,\n            background=child2\n        )\n        assert aug.foreground is child1\n        assert aug.background is child2\n        assert isinstance(aug.mask_generator,\n                          iaa.BoundingBoxesMaskGen)\n        assert aug.mask_generator.labels.value == \"person\"\n        assert aug.mask_generator.nb_sample_labels.value == 1\n\n    def test_single_image(self):\n        image = np.full((10, 10, 3), 255, dtype=np.uint8)\n        bbs = [ia.BoundingBox(x1=1, y1=1, x2=5, y2=5, label=\"bb1\"),\n               ia.BoundingBox(x1=-3, y1=4, x2=20, y2=8, label=\"bb2\")]\n\n        aug = iaa.BlendAlphaBoundingBoxes(\n            [\"bb1\"],\n            nb_sample_labels=1,\n            foreground=iaa.Multiply(0.0)\n        )\n\n        image_aug, segmap_aug = aug(image=image,\n                                    bounding_boxes=[bbs])\n\n        assert np.allclose(image_aug[1:5, 1:5, :], 0, rtol=0, atol=1.01)\n        assert np.allclose(image_aug[0:1, 0:1, :], 255, rtol=0, atol=1.01)\n        assert np.allclose(image_aug[5:10, 5:10, :], 255, rtol=0, atol=1.01)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 255, dtype=np.uint8)\n                bbs = [ia.BoundingBox(x1=1, y1=1, x2=5, y2=5, label=\"bb1\"),\n                       ia.BoundingBox(x1=-3, y1=4, x2=20, y2=8, label=\"bb2\")]\n                aug = iaa.BlendAlphaBoundingBoxes(\n                    [\"bb1\"],\n                    foreground=iaa.Multiply(0.0))\n\n                image_aug, segmap_aug = aug(\n                    image=image, bounding_boxes=[bbs])\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        shape = (15, 15, 3)\n        iterations = 3\n        augmenter = iaa.BlendAlphaBoundingBoxes(\n            [\"bb1\", \"bb2\", \"bb3\"],\n            foreground=iaa.Add((1, 10), seed=1),\n            background=iaa.Add((11, 20), seed=2),\n            nb_sample_labels=1,\n            seed=3)\n        image = np.mod(np.arange(int(np.prod(shape))), 256).astype(np.uint8)\n        image = image.reshape(shape)\n        bbs = [ia.BoundingBox(x1=1, y1=1, x2=5, y2=5, label=\"bb1\"),\n               ia.BoundingBox(x1=-3, y1=4, x2=20, y2=8, label=\"bb2\")]\n\n        augmenter_pkl = pickle.loads(pickle.dumps(augmenter, protocol=-1))\n\n        for _ in np.arange(iterations):\n            image_aug, bbs_aug = augmenter(\n                image=image, bounding_boxes=[bbs])\n            image_aug_pkl, bbs_aug_pkl = augmenter_pkl(\n                image=image, bounding_boxes=[bbs])\n            assert np.array_equal(image_aug, image_aug_pkl)\n\n\nclass TestStochasticParameterMaskGen(unittest.TestCase):\n    @classmethod\n    def _test_draw_masks_nhwc(cls, shape):\n        batch = _BatchInAugmentation(\n            images=np.zeros(shape, dtype=np.uint8)\n        )\n        values = np.float32([\n            [0.1, 0.2, 0.3],\n            [0.4, 0.5, 0.6]\n        ])\n        param = iap.DeterministicList(values.flatten())\n\n        gen = iaa.StochasticParameterMaskGen(param, per_channel=False)\n\n        masks = gen.draw_masks(batch, random_state=0)\n\n        for i in np.arange(shape[0]):\n            assert np.allclose(masks[i], values)\n\n    def test_draw_masks_hw3_images(self):\n        self._test_draw_masks_nhwc((2, 2, 3, 3))\n\n    def test_draw_masks_hw1_images(self):\n        self._test_draw_masks_nhwc((2, 2, 3, 1))\n\n    def test_draw_masks_hw_images(self):\n        self._test_draw_masks_nhwc((2, 2, 3))\n\n    def test_draw_masks_batch_without_images(self):\n        bb = ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)\n        bbsoi1 = ia.BoundingBoxesOnImage([bb], shape=(2, 3, 3))\n        bbsoi2 = ia.BoundingBoxesOnImage([], shape=(3, 3, 3))\n        batch = _BatchInAugmentation(\n            bounding_boxes=[bbsoi1, bbsoi2]\n        )\n        # sampling for shape of bbsoi1 will cover row1 and row2, then\n        # sampling for bbsoi2 will cover row1, row2, row3\n        # masks are sampled independently per row/image, so it starts over\n        # again for bbsoi2\n        values = np.float32([\n            [0.1, 0.2, 0.3],\n            [0.4, 0.5, 0.6],\n            [0.7, 0.8, 0.9]\n        ])\n        param = iap.DeterministicList(values.flatten())\n\n        gen = iaa.StochasticParameterMaskGen(param, per_channel=False)\n\n        masks = gen.draw_masks(batch, random_state=0)\n\n        expected1 = values[0:2]\n        expected2 = values[0:3]\n        assert np.allclose(masks[0], expected1)\n        assert np.allclose(masks[1], expected2)\n\n    def test_per_channel(self):\n        for per_channel in [True, iap.Deterministic(0.51)]:\n            batch = _BatchInAugmentation(\n                images=np.zeros((1, 2, 3, 2), dtype=np.uint8)\n            )\n            values = np.float32([\n                [0.1, 0.2, 0.3],\n                [0.4, 0.5, 0.6],\n                [0.7, 0.8, 0.9],\n                [0.10, 0.11, 0.12]\n            ])\n            param = iap.DeterministicList(values.flatten())\n\n            gen = iaa.StochasticParameterMaskGen(param,\n                                                 per_channel=per_channel)\n\n            masks = gen.draw_masks(batch, random_state=0)\n\n            assert np.allclose(masks[0], values.reshape((2, 3, 2)))\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for per_channel in [False, True]:\n            for shape in shapes:\n                with self.subTest(per_channel=per_channel, shape=shape):\n                    batch = _BatchInAugmentation(\n                        images=[np.zeros(shape, dtype=np.uint8)]\n                    )\n                    param = iap.Deterministic(1.0)\n                    gen = iaa.StochasticParameterMaskGen(\n                        param, per_channel=per_channel)\n\n                    masks = gen.draw_masks(batch, random_state=0)\n\n                    assert len(masks) == 1\n                    if not per_channel:\n                        assert masks[0].shape == shape[0:2]\n                    else:\n                        assert masks[0].shape == shape\n\n\nclass TestSomeColorsMaskGen(unittest.TestCase):\n    def test___init___defaults(self):\n        gen = iaa.SomeColorsMaskGen()\n        assert np.isclose(gen.nb_bins.a.value, 5)\n        assert np.isclose(gen.nb_bins.b.value, 15)\n        assert np.isclose(gen.smoothness.a.value, 0.1)\n        assert np.isclose(gen.smoothness.b.value, 0.3)\n        assert np.isclose(gen.alpha.a[0], 0.0)\n        assert np.isclose(gen.alpha.a[1], 1.0)\n        assert np.isclose(gen.rotation_deg.a.value, 0)\n        assert np.isclose(gen.rotation_deg.b.value, 360)\n        assert gen.from_colorspace == iaa.CSPACE_RGB\n\n    def test___init___custom_settings(self):\n        gen = iaa.SomeColorsMaskGen(\n            nb_bins=100,\n            smoothness=0.5,\n            alpha=0.7,\n            rotation_deg=123,\n            from_colorspace=iaa.CSPACE_HSV\n        )\n        assert gen.nb_bins.value == 100\n        assert np.isclose(gen.smoothness.value, 0.5)\n        assert np.isclose(gen.alpha.value, 0.7)\n        assert np.isclose(gen.rotation_deg.value, 123)\n        assert gen.from_colorspace == iaa.CSPACE_HSV\n\n    def test_draw_masks_marks_different_colors(self):\n        image = np.uint8([\n            [255, 0, 0],\n            [0, 255, 0],\n            [0, 0, 255],\n            [255, 255, 0],\n            [255, 0, 255],\n            [0, 255, 255],\n            [255, 128, 128],\n            [128, 255, 128],\n            [128, 128, 255]\n        ]).reshape((9, 1, 3))\n        image = np.tile(image, (9, 50, 1))\n        batch = _BatchInAugmentation(images=[image])\n        gen = iaa.SomeColorsMaskGen(nb_bins=256, smoothness=0,\n                                    alpha=[0, 1])\n        expected_mask_sums = np.arange(1 + image.shape[0]) * image.shape[1]\n        expected_mask_sums = expected_mask_sums.astype(np.float32)\n\n        mask_sums = []\n        for i in sm.xrange(50):\n            mask = gen.draw_masks(batch, random_state=i)[0]\n\n            mask_sum = int(np.sum(mask))\n            mask_sums.append(mask_sum)\n\n            assert np.any(\n                np.isclose(\n                    np.min(np.abs(expected_mask_sums - mask_sum)),\n                    0.0,\n                    rtol=0,\n                    atol=0.01)\n            )\n            assert mask.shape == image.shape[0:2]\n            assert mask.dtype.name == \"float32\"\n\n        assert len(np.unique(mask_sums)) >= 4\n\n    def test_draw_masks_marks_alpha_is_0(self):\n        image = np.uint8([\n            [255, 0, 0],\n            [0, 255, 0],\n            [0, 0, 255],\n            [255, 255, 0],\n            [255, 0, 255],\n            [0, 255, 255],\n            [255, 128, 128],\n            [128, 255, 128],\n            [128, 128, 255]\n        ]).reshape((1, 9, 3))\n        batch = _BatchInAugmentation(images=[image])\n        gen = iaa.SomeColorsMaskGen(alpha=0.0)\n\n        mask = gen.draw_masks(batch)[0]\n\n        assert np.allclose(mask, 0.0)\n\n    def test_draw_masks_alpha_is_1(self):\n        image = np.uint8([\n            [255, 0, 0],\n            [0, 255, 0],\n            [0, 0, 255],\n            [255, 255, 0],\n            [255, 0, 255],\n            [0, 255, 255],\n            [255, 128, 128],\n            [128, 255, 128],\n            [128, 128, 255]\n        ]).reshape((1, 9, 3))\n        batch = _BatchInAugmentation(images=[image])\n        gen = iaa.SomeColorsMaskGen(alpha=1.0)\n\n        mask = gen.draw_masks(batch)[0]\n\n        assert np.allclose(mask, 1.0)\n\n    @mock.patch(\"imgaug.augmenters.color.change_colorspace_\")\n    def test_from_colorspace(self, mock_cc):\n        image = np.uint8([\n            [255, 0, 0],\n            [0, 255, 0],\n            [0, 0, 255],\n            [255, 255, 0],\n            [255, 0, 255],\n            [0, 255, 255],\n            [255, 128, 128],\n            [128, 255, 128],\n            [128, 128, 255]\n        ]).reshape((1, 9, 3))\n        batch = _BatchInAugmentation(images=[image])\n        mock_cc.return_value = np.copy(image)\n        gen = iaa.SomeColorsMaskGen(alpha=1.0, from_colorspace=iaa.CSPACE_BGR)\n\n        _ = gen.draw_masks(batch)\n\n        assert mock_cc.call_count == 1\n        assert np.array_equal(mock_cc.call_args_list[0][0][0], image)\n        assert (mock_cc.call_args_list[0][1][\"to_colorspace\"]\n                == iaa.CSPACE_HSV)\n        assert (mock_cc.call_args_list[0][1][\"from_colorspace\"]\n                == iaa.CSPACE_BGR)\n\n    def test__upscale_to_256_alpha_bins__1_to_256(self):\n        alphas = np.float32([0.5])\n\n        alphas_up = iaa.SomeColorsMaskGen._upscale_to_256_alpha_bins(alphas)\n\n        assert alphas_up.shape == (256,)\n        assert np.allclose(alphas_up, 0.5)\n\n    def test__upscale_to_256_alpha_bins__2_to_256(self):\n        alphas = np.float32([1.0, 0.5])\n\n        alphas_up = iaa.SomeColorsMaskGen._upscale_to_256_alpha_bins(alphas)\n\n        assert alphas_up.shape == (256,)\n        assert np.allclose(alphas_up[0:128], 1.0)\n        assert np.allclose(alphas_up[128:], 0.5)\n\n    def test__upscale_to_256_alpha_bins__255_to_256(self):\n        alphas = np.zeros((255,), dtype=np.float32)\n        alphas[0] = 0.25\n        alphas[1:254] = 0.5\n        alphas[254] = 1.0\n\n        alphas_up = iaa.SomeColorsMaskGen._upscale_to_256_alpha_bins(alphas)\n\n        assert alphas_up.shape == (256,)\n        assert np.allclose(alphas_up[0:2], 0.25)\n        assert np.allclose(alphas_up[2:], 0.5)\n\n    def test__upscale_to_256_alpha_bins__256_to_256(self):\n        alphas = np.full((256,), 0.5, dtype=np.float32)\n\n        alphas_up = iaa.SomeColorsMaskGen._upscale_to_256_alpha_bins(alphas)\n\n        assert alphas_up.shape == (256,)\n        assert np.allclose(alphas, 0.5)\n\n    def test__rotate_alpha_bins__by_0(self):\n        alphas = np.linspace(0.0, 1.0, 256)\n\n        alphas_rot = iaa.SomeColorsMaskGen._rotate_alpha_bins(alphas, 0)\n\n        assert np.allclose(alphas_rot, alphas)\n\n    def test__rotate_alpha_bins__by_1(self):\n        alphas = np.linspace(0.0, 1.0, 256)\n\n        alphas_rot = iaa.SomeColorsMaskGen._rotate_alpha_bins(alphas, 1)\n\n        assert np.allclose(alphas_rot[:-1], alphas[1:])\n        assert np.allclose(alphas_rot[-1:], alphas[:1])\n\n    def test__rotate_alpha_bins__by_255(self):\n        alphas = np.linspace(0.0, 1.0, 256)\n\n        alphas_rot = iaa.SomeColorsMaskGen._rotate_alpha_bins(alphas, 255)\n\n        assert np.allclose(alphas_rot[:-255], alphas[255:])\n        assert np.allclose(alphas_rot[-255:], alphas[:255])\n\n    def test__rotate_alpha_bins__by_256(self):\n        alphas = np.linspace(0.0, 1.0, 256)\n\n        alphas_rot = iaa.SomeColorsMaskGen._rotate_alpha_bins(alphas, 256)\n\n        assert np.allclose(alphas_rot, alphas)\n\n    def test__smoothen_alphas__0(self):\n        alphas = np.zeros((11,), dtype=np.float32)\n        alphas[5-3:5+3+1] = 1.0\n\n        alphas_smooth = iaa.SomeColorsMaskGen._smoothen_alphas(alphas, 0.0)\n\n        assert np.allclose(alphas_smooth, alphas)\n\n    def test__smoothen_alphas__002(self):\n        alphas = np.zeros((11,), dtype=np.float32)\n        alphas[5-3:5+3+1] = 1.0\n\n        alphas_smooth = iaa.SomeColorsMaskGen._smoothen_alphas(alphas, 0.02)\n\n        assert np.allclose(alphas_smooth, alphas, atol=0.02)\n\n    def test__smoothen_alphas__1(self):\n        alphas = np.zeros((11,), dtype=np.float32)\n        alphas[5-3:5+3+1] = 1.0\n\n        alphas_smooth = iaa.SomeColorsMaskGen._smoothen_alphas(alphas, 1.0)\n\n        assert np.isclose(alphas_smooth[0], 0.0, atol=0.01)\n        assert not np.isclose(alphas_smooth[2], 1.0, atol=0.1)\n        assert np.isclose(alphas_smooth[5], 1.0, atol=0.01)\n\n    def test__generate_pixelwise_alpha_map(self):\n        image_hsv = np.uint8([\n            [0, 0, 0],\n            [50, 0, 0],\n            [100, 0, 0],\n            [150, 0, 0],\n            [200, 0, 0],\n            [250, 0, 0],\n            [255, 0, 0]\n        ]).reshape((1, 7, 3))\n        hue_to_alpha = np.zeros((256,), dtype=np.float32)\n        hue_to_alpha[0] = 0.1\n        hue_to_alpha[50] = 0.2\n        hue_to_alpha[100] = 0.3\n        hue_to_alpha[150] = 0.4\n        hue_to_alpha[200] = 0.5\n        hue_to_alpha[250] = 0.6\n        hue_to_alpha[255] = 0.7\n\n        mask = iaa.SomeColorsMaskGen._generate_pixelwise_alpha_mask(\n            image_hsv, hue_to_alpha)\n\n        # a bit of tolerance here due to the mask being converted from\n        # [0, 255] to [0.0, 1.0]\n        assert np.allclose(\n            mask.flatten(),\n            [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7],\n            atol=0.05)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0, 3),\n            (0, 1, 3),\n            (1, 0, 3)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                batch = _BatchInAugmentation(images=[image])\n                gen = iaa.SomeColorsMaskGen()\n\n                mask = gen.draw_masks(batch)[0]\n\n                assert mask.shape == shape[0:2]\n                assert mask.dtype.name == \"float32\"\n\n    def test_batch_contains_no_images(self):\n        hms = ia.HeatmapsOnImage(np.zeros((5, 5), dtype=np.float32),\n                                 shape=(10, 10, 3))\n        batch = _BatchInAugmentation(heatmaps=[hms])\n        gen = iaa.SomeColorsMaskGen()\n\n        with self.assertRaises(AssertionError):\n            _masks = gen.draw_masks(batch)\n\n\nclass TestHorizontalLinearGradientMaskGen(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        gen = iaa.HorizontalLinearGradientMaskGen(min_value=0.1,\n                                                  max_value=1.0,\n                                                  start_at=0.1,\n                                                  end_at=0.9)\n        assert gen.axis == 1\n        assert np.isclose(gen.min_value.value, 0.1)\n        assert np.isclose(gen.max_value.value, 1.0)\n        assert np.isclose(gen.start_at.value, 0.1)\n        assert np.isclose(gen.end_at.value, 0.9)\n\n    def test_draw_masks(self):\n        image1 = np.zeros((5, 100, 3), dtype=np.uint8)\n        image2 = np.zeros((7, 200, 3), dtype=np.uint8)\n        batch = _BatchInAugmentation(images=[image1, image2])\n\n        gen = iaa.HorizontalLinearGradientMaskGen(min_value=0.1,\n                                                  max_value=0.75,\n                                                  start_at=0.1,\n                                                  end_at=0.9)\n\n        masks = gen.draw_masks(batch, random_state=1)\n\n        assert masks[0].shape == image1.shape[0:2]\n        assert masks[1].shape == image2.shape[0:2]\n        assert masks[0].dtype.name == \"float32\"\n        assert masks[1].dtype.name == \"float32\"\n        assert np.allclose(masks[0][:, 0:10], 0.1)\n        assert np.allclose(masks[1][:, 0:20], 0.1)\n        assert np.allclose(masks[0][:, 90:], 0.75)\n        assert np.allclose(masks[1][:, 180:], 0.75)\n        assert np.allclose(masks[0][:, 10+40], 0.1 + 0.5 * (0.75 - 0.1),\n                           rtol=0, atol=0.05)\n        assert np.allclose(masks[1][:, 20+80], 0.1 + 0.5 * (0.75 - 0.1),\n                           rtol=0, atol=0.025)\n\n    def test_generate_mask__min_value_below_max_value(self):\n        mask = iaa.HorizontalLinearGradientMaskGen.generate_mask(\n            (1, 100, 3), min_value=0.75, max_value=0.25,\n            start_at=0.0, end_at=1.0)\n\n        assert mask.shape == (1, 100)\n        assert np.isclose(mask[0, 0], 0.75)\n        assert np.isclose(mask[0, -1], 0.25)\n        assert np.isclose(mask[0, 50], 0.25 + 0.5 * (0.75 - 0.25),\n                          rtol=0, atol=0.05)\n\n    def test_generate_mask__end_at_is_before_start_at(self):\n        mask = iaa.HorizontalLinearGradientMaskGen.generate_mask(\n            (1, 100, 3), min_value=0.25, max_value=0.75,\n            start_at=1.0, end_at=0.0)\n\n        # like test_generate_mask__min_value_below_max_value(),\n        # because end < start leads to inversion and we also inverted the\n        # min and max value above\n        assert mask.shape == (1, 100)\n        assert np.isclose(mask[0, 0], 0.75)\n        assert np.isclose(mask[0, -1], 0.25)\n        assert np.isclose(mask[0, 50], 0.25 + 0.5 * (0.75 - 0.25),\n                          rtol=0, atol=0.05)\n\n    def test_generate_mask__start_at_is_end_at(self):\n        mask = iaa.HorizontalLinearGradientMaskGen.generate_mask(\n            (1, 100, 3), min_value=0.0, max_value=1.0,\n            start_at=0.5, end_at=0.5)\n\n        assert mask.shape == (1, 100)\n        assert np.allclose(mask[:, 0:50], 0.0)\n        assert np.allclose(mask[:, 50:], 1.0)\n\n    def test_generate_mask__min_value_is_max_value(self):\n        mask = iaa.HorizontalLinearGradientMaskGen.generate_mask(\n            (1, 100, 3), min_value=0.5, max_value=0.5,\n            start_at=0.1, end_at=0.8)\n\n        assert mask.shape == (1, 100)\n        assert np.allclose(mask, 0.5)\n\n    def test_generate_mask__start_at_and_end_at_are_outside_of_image(self):\n        mask = iaa.HorizontalLinearGradientMaskGen.generate_mask(\n            (1, 100, 3), min_value=0.25, max_value=0.75,\n            start_at=-0.5, end_at=-0.1)\n\n        assert mask.shape == (1, 100)\n        assert np.allclose(mask, 0.75)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 0, 0),\n            (1, 0, 0),\n            (0, 1, 0),\n            (0, 0, 1),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                batch = _BatchInAugmentation(images=[image])\n                gen = iaa.HorizontalLinearGradientMaskGen()\n\n                mask = gen.draw_masks(batch)[0]\n\n                assert mask.shape == shape[0:2]\n                assert mask.dtype.name == \"float32\"\n\n    def test_batch_contains_no_images(self):\n        hms = ia.HeatmapsOnImage(np.zeros((5, 5), dtype=np.float32),\n                                 shape=(10, 10, 3))\n        batch = _BatchInAugmentation(heatmaps=[hms])\n        gen = iaa.HorizontalLinearGradientMaskGen(min_value=0.25,\n                                                  max_value=0.75,\n                                                  start_at=0.5,\n                                                  end_at=0.5)\n\n        mask = gen.draw_masks(batch)[0]\n        assert np.allclose(mask[:, 0:5], 0.25)\n        assert np.allclose(mask[:, 5:], 0.75)\n\n\nclass TestVerticalLinearGradientMaskGen(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        gen = iaa.VerticalLinearGradientMaskGen(min_value=0.1,\n                                                max_value=1.0,\n                                                start_at=0.1,\n                                                end_at=0.9)\n        assert gen.axis == 0\n        assert np.isclose(gen.min_value.value, 0.1)\n        assert np.isclose(gen.max_value.value, 1.0)\n        assert np.isclose(gen.start_at.value, 0.1)\n        assert np.isclose(gen.end_at.value, 0.9)\n\n    def test_draw_masks(self):\n        # we transpose the axes in this test, because that way the test is\n        # essentially identical to the one for HorizontalLinearGradientMaskGen\n        image1 = np.zeros((5, 100, 3), dtype=np.uint8)\n        image2 = np.zeros((7, 200, 3), dtype=np.uint8)\n        image1 = image1.transpose((1, 0, 2))\n        image2 = image2.transpose((1, 0, 2))\n        batch = _BatchInAugmentation(images=[image1, image2])\n\n        gen = iaa.VerticalLinearGradientMaskGen(min_value=0.1,\n                                                max_value=0.75,\n                                                start_at=0.1,\n                                                end_at=0.9)\n\n        masks = gen.draw_masks(batch, random_state=1)\n\n        image1 = image1.transpose((1, 0, 2))\n        image2 = image2.transpose((1, 0, 2))\n        masks[0] = masks[0].transpose((1, 0))\n        masks[1] = masks[1].transpose((1, 0))\n        assert masks[0].shape == image1.shape[0:2]\n        assert masks[1].shape == image2.shape[0:2]\n        assert masks[0].dtype.name == \"float32\"\n        assert masks[1].dtype.name == \"float32\"\n        assert np.allclose(masks[0][:, 0:10], 0.1)\n        assert np.allclose(masks[1][:, 0:20], 0.1)\n        assert np.allclose(masks[0][:, 90:], 0.75)\n        assert np.allclose(masks[1][:, 180:], 0.75)\n        assert np.allclose(masks[0][:, 10+40], 0.1 + 0.5 * (0.75 - 0.1),\n                           rtol=0, atol=0.05)\n        assert np.allclose(masks[1][:, 20+80], 0.1 + 0.5 * (0.75 - 0.1),\n                           rtol=0, atol=0.025)\n\n\nclass TestRegularGridMaskGen(unittest.TestCase):\n    def test___init__(self):\n        gen = iaa.RegularGridMaskGen(nb_rows=2, nb_cols=[1, 3], alpha=0.6)\n        assert gen.nb_rows.value == 2\n        assert gen.nb_cols.a == [1, 3]\n        assert np.isclose(gen.alpha.value, 0.6)\n\n    def test_draw_masks(self):\n        gen = iaa.RegularGridMaskGen(\n            nb_rows=2,\n            nb_cols=iap.DeterministicList([1, 4]),\n            alpha=iap.DeterministicList([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7,\n                                         0.8, 0.9, 1.0]))\n        image = np.zeros((6, 8, 3), dtype=np.uint8)\n        batch = _BatchInAugmentation(images=[image, image])\n\n        masks = gen.draw_masks(batch, random_state=1)\n\n        expected1 = np.full((6, 8), 1.0, dtype=np.float32)\n        expected1[0:3, :] = 0.1\n        expected1[3:6, :] = 0.2\n        expected2 = np.full((6, 8), 1.0, dtype=np.float32)\n        expected2[0:3, 0:2] = 0.3\n        expected2[0:3, 2:4] = 0.4\n        expected2[0:3, 4:6] = 0.5\n        expected2[0:3, 6:8] = 0.6\n        expected2[3:6, 0:2] = 0.7\n        expected2[3:6, 2:4] = 0.8\n        expected2[3:6, 4:6] = 0.9\n        expected2[3:6, 6:8] = 1.0\n\n        assert np.allclose(masks[0], expected1)\n        assert np.allclose(masks[1], expected2)\n\n    def test_draw_masks__random_alphas(self):\n        gen = iaa.RegularGridMaskGen(\n            nb_rows=1,\n            nb_cols=2,\n            alpha=[0.1, 0.9])\n        image = np.zeros((2, 4, 3), dtype=np.uint8)\n        batch = _BatchInAugmentation(images=[image, image])\n\n        expected1 = np.full((2, 4), 0.1, dtype=np.float32)\n        expected2 = np.full((2, 4), 0.1, dtype=np.float32)\n        expected3 = np.full((2, 4), 0.1, dtype=np.float32)\n        expected4 = np.full((2, 4), 0.9, dtype=np.float32)\n        expected1[:, 0:2] = 0.9\n        expected2[:, 2:4] = 0.9\n\n        seen = [False, False, False, False]\n        for i in np.arange(50):\n            masks = gen.draw_masks(batch, random_state=i)\n            for mask in masks:\n                if np.allclose(mask, expected1):\n                    seen[0] = True\n                elif np.allclose(mask, expected2):\n                    seen[1] = True\n                elif np.allclose(mask, expected3):\n                    seen[2] = True\n                elif np.allclose(mask, expected4):\n                    seen[3] = True\n                else:\n                    assert False\n            if np.all(seen):\n                break\n\n        assert np.all(seen)\n\n    def test_generate_mask_rows_1_cols_1(self):\n        mask = iaa.RegularGridMaskGen.generate_mask(\n            (5, 7),\n            nb_rows=1, nb_cols=1,\n            alphas=np.float32([1, 0]))\n        assert np.allclose(mask, 1.0)\n\n    def test_generate_mask_rows_1_cols_n(self):\n        mask = iaa.RegularGridMaskGen.generate_mask(\n            (5, 8),\n            nb_rows=1, nb_cols=4,\n            alphas=np.float32([[1, 0, 1, 0]]))\n        expected = np.full((5, 8), 1.0, dtype=np.float32)\n        expected[:, 2:4] = 0.0\n        expected[:, 6:8] = 0.0\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask_rows_n_cols_1(self):\n        mask = iaa.RegularGridMaskGen.generate_mask(\n            (8, 5),\n            nb_rows=4, nb_cols=1,\n            alphas=np.float32([[1],\n                               [0],\n                               [1],\n                               [0]]))\n        expected = np.full((8, 5), 1.0, dtype=np.float32)\n        expected[2:4, :] = 0.0\n        expected[6:8, :] = 0.0\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask_rows_n_cols_n(self):\n        mask = iaa.RegularGridMaskGen.generate_mask(\n            (6, 8),\n            nb_rows=3, nb_cols=2,\n            alphas=np.float32([[1, 0],\n                               [0, 1],\n                               [1, 0]]))\n        expected = np.full((6, 8), 1.0, dtype=np.float32)\n        expected[0:2, 0:4] = 1.0\n        expected[0:2, 4:8] = 0.0\n        expected[2:4, 0:4] = 0.0\n        expected[2:4, 4:8] = 1.0\n        expected[4:6, 0:4] = 1.0\n        expected[4:6, 4:8] = 0.0\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask_with_leftover_pixels(self):\n        mask = iaa.RegularGridMaskGen.generate_mask(\n            (15, 15),\n            nb_rows=4, nb_cols=4,\n            alphas=np.float32([[1, 0, 1, 0],\n                               [0, 1, 0, 1],\n                               [1, 0, 1, 0],\n                               [0, 1, 0, 1]]))\n        expected = np.full((12, 12), 0.0, dtype=np.float32)\n\n        expected[0:3, 0:3] = 1.0\n        expected[0:3, 3:6] = 0.0\n        expected[0:3, 6:9] = 1.0\n        expected[0:3, 9:12] = 0.0\n\n        expected[3:6, 0:3] = 0.0\n        expected[3:6, 3:6] = 1.0\n        expected[3:6, 6:9] = 0.0\n        expected[3:6, 9:12] = 1.0\n\n        expected[6:9, 0:3] = 1.0\n        expected[6:9, 3:6] = 0.0\n        expected[6:9, 6:9] = 1.0\n        expected[6:9, 9:12] = 0.0\n\n        expected[9:12, 0:3] = 0.0\n        expected[9:12, 3:6] = 1.0\n        expected[9:12, 6:9] = 0.0\n        expected[9:12, 9:12] = 1.0\n\n        expected = np.pad(expected, ((1, 2), (1, 2)), mode=\"reflect\")\n\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask_with_more_columns_than_pixels(self):\n        mask = iaa.RegularGridMaskGen.generate_mask(\n            (5, 4),\n            nb_rows=1, nb_cols=10,\n            alphas=np.float32([[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]))\n        expected = np.full((5, 4), 1.0, dtype=np.float32)\n        expected[:, 1:2] = 0.0\n        expected[:, 3:4] = 0.0\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask_with_more_rows_than_pixels(self):\n        mask = iaa.RegularGridMaskGen.generate_mask(\n            (4, 5),\n            nb_rows=6, nb_cols=1,\n            alphas=np.float32([[1],\n                               [0],\n                               [1],\n                               [0],\n                               [1],\n                               [0]]))\n        expected = np.full((4, 5), 1.0, dtype=np.float32)\n        expected[1:2, :] = 0.0\n        expected[3:4, :] = 0.0\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask__alphas_is_1d_array(self):\n        mask = iaa.RegularGridMaskGen.generate_mask(\n            (5, 8),\n            nb_rows=1, nb_cols=4,\n            alphas=np.float32([1, 0, 1, 0]))\n        expected = np.full((5, 8), 1.0, dtype=np.float32)\n        expected[:, 2:4] = 0.0\n        expected[:, 6:8] = 0.0\n        assert np.allclose(mask, expected)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 0, 0),\n            (1, 0, 0),\n            (0, 1, 0),\n            (0, 0, 1),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                batch = _BatchInAugmentation(images=[image])\n                gen = iaa.RegularGridMaskGen(2, 2)\n\n                mask = gen.draw_masks(batch)[0]\n\n                assert mask.shape == shape[0:2]\n                assert mask.dtype.name == \"float32\"\n\n    def test_batch_contains_no_images(self):\n        hms = ia.HeatmapsOnImage(np.zeros((5, 5), dtype=np.float32),\n                                 shape=(6, 8, 3))\n        batch = _BatchInAugmentation(heatmaps=[hms])\n        gen = iaa.CheckerboardMaskGen(nb_rows=3, nb_cols=2)\n        mask = gen.draw_masks(batch, random_state=1)[0]\n\n        expected = np.full((6, 8), 1.0, dtype=np.float32)\n        expected[0:2, 0:4] = 1.0\n        expected[0:2, 4:8] = 0.0\n        expected[2:4, 0:4] = 0.0\n        expected[2:4, 4:8] = 1.0\n        expected[4:6, 0:4] = 1.0\n        expected[4:6, 4:8] = 0.0\n        assert np.allclose(mask, expected)\n\n\nclass TestCheckerboardMaskGen(unittest.TestCase):\n    def test___init__(self):\n        gen = iaa.CheckerboardMaskGen(nb_rows=2, nb_cols=[1, 3])\n        assert gen.nb_rows.value == 2\n        assert gen.nb_cols.a == [1, 3]\n\n    def test_draw_masks(self):\n        gen = iaa.CheckerboardMaskGen(nb_rows=2,\n                                      nb_cols=iap.DeterministicList([1, 4]))\n        image = np.zeros((6, 8, 3), dtype=np.uint8)\n        batch = _BatchInAugmentation(images=[image, image])\n\n        masks = gen.draw_masks(batch, random_state=1)\n\n        expected1 = np.full((6, 8), 1.0, dtype=np.float32)\n        expected1[3:6, :] = 0.0\n        expected2 = np.full((6, 8), 1.0, dtype=np.float32)\n        expected2[0:3, 2:4] = 0.0\n        expected2[0:3, 6:8] = 0.0\n        expected2[3:6, 0:2] = 0.0\n        expected2[3:6, 4:6] = 0.0\n\n        assert np.allclose(masks[0], expected1)\n        assert np.allclose(masks[1], expected2)\n\n    def test_generate_mask_rows_1_cols_1(self):\n        mask = iaa.CheckerboardMaskGen.generate_mask((5, 7),\n                                                     nb_rows=1, nb_cols=1)\n        assert np.allclose(mask, 1.0)\n\n    def test_generate_mask_rows_1_cols_n(self):\n        mask = iaa.CheckerboardMaskGen.generate_mask((5, 8),\n                                                     nb_rows=1, nb_cols=4)\n        expected = np.full((5, 8), 1.0, dtype=np.float32)\n        expected[:, 2:4] = 0.0\n        expected[:, 6:8] = 0.0\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask_rows_n_cols_1(self):\n        mask = iaa.CheckerboardMaskGen.generate_mask((8, 5),\n                                                     nb_rows=4, nb_cols=1)\n        expected = np.full((8, 5), 1.0, dtype=np.float32)\n        expected[2:4, :] = 0.0\n        expected[6:8, :] = 0.0\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask_rows_n_cols_n(self):\n        mask = iaa.CheckerboardMaskGen.generate_mask((6, 8),\n                                                     nb_rows=3, nb_cols=2)\n        expected = np.full((6, 8), 1.0, dtype=np.float32)\n        expected[0:2, 0:4] = 1.0\n        expected[0:2, 4:8] = 0.0\n        expected[2:4, 0:4] = 0.0\n        expected[2:4, 4:8] = 1.0\n        expected[4:6, 0:4] = 1.0\n        expected[4:6, 4:8] = 0.0\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask_with_leftover_pixels(self):\n        mask = iaa.CheckerboardMaskGen.generate_mask((15, 15),\n                                                     nb_rows=4, nb_cols=4)\n        expected = np.full((12, 12), 0.0, dtype=np.float32)\n\n        expected[0:3, 0:3] = 1.0\n        expected[0:3, 3:6] = 0.0\n        expected[0:3, 6:9] = 1.0\n        expected[0:3, 9:12] = 0.0\n\n        expected[3:6, 0:3] = 0.0\n        expected[3:6, 3:6] = 1.0\n        expected[3:6, 6:9] = 0.0\n        expected[3:6, 9:12] = 1.0\n\n        expected[6:9, 0:3] = 1.0\n        expected[6:9, 3:6] = 0.0\n        expected[6:9, 6:9] = 1.0\n        expected[6:9, 9:12] = 0.0\n\n        expected[9:12, 0:3] = 0.0\n        expected[9:12, 3:6] = 1.0\n        expected[9:12, 6:9] = 0.0\n        expected[9:12, 9:12] = 1.0\n\n        expected = np.pad(expected, ((1, 2), (1, 2)), mode=\"reflect\")\n\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask_with_more_columns_than_pixels(self):\n        mask = iaa.CheckerboardMaskGen.generate_mask((5, 4),\n                                                     nb_rows=1, nb_cols=10)\n        expected = np.full((5, 4), 1.0, dtype=np.float32)\n        expected[:, 1:2] = 0.0\n        expected[:, 3:4] = 0.0\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask_with_more_rows_than_pixels(self):\n        mask = iaa.CheckerboardMaskGen.generate_mask((4, 5),\n                                                     nb_rows=6, nb_cols=1)\n        expected = np.full((4, 5), 1.0, dtype=np.float32)\n        expected[1:2, :] = 0.0\n        expected[3:4, :] = 0.0\n        assert np.allclose(mask, expected)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 0, 0),\n            (1, 0, 0),\n            (0, 1, 0),\n            (0, 0, 1),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                batch = _BatchInAugmentation(images=[image])\n                gen = iaa.CheckerboardMaskGen(2, 2)\n\n                mask = gen.draw_masks(batch)[0]\n\n                assert mask.shape == shape[0:2]\n                assert mask.dtype.name == \"float32\"\n\n    def test_batch_contains_no_images(self):\n        hms = ia.HeatmapsOnImage(np.zeros((5, 5), dtype=np.float32),\n                                 shape=(6, 8, 3))\n        batch = _BatchInAugmentation(heatmaps=[hms])\n        gen = iaa.CheckerboardMaskGen(nb_rows=3, nb_cols=2)\n        mask = gen.draw_masks(batch, random_state=1)[0]\n\n        expected = np.full((6, 8), 1.0, dtype=np.float32)\n        expected[0:2, 0:4] = 1.0\n        expected[0:2, 4:8] = 0.0\n        expected[2:4, 0:4] = 0.0\n        expected[2:4, 4:8] = 1.0\n        expected[4:6, 0:4] = 1.0\n        expected[4:6, 4:8] = 0.0\n        assert np.allclose(mask, expected)\n\n\nclass TestSegMapClassIdsMaskGen(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___fixed_class_ids_int(self):\n        gen = iaa.SegMapClassIdsMaskGen(0)\n        assert gen.class_ids == [0]\n        assert gen.nb_sample_classes is None\n\n    def test___init___fixed_class_ids_list(self):\n        gen = iaa.SegMapClassIdsMaskGen([0, 1, 3])\n        assert gen.class_ids == [0, 1, 3]\n        assert gen.nb_sample_classes is None\n\n    def test___init___class_ids_stochastic(self):\n        gen = iaa.SegMapClassIdsMaskGen([0, 1, 3], nb_sample_classes=2)\n        assert is_parameter_instance(gen.class_ids, iap.Choice)\n        assert is_parameter_instance(gen.nb_sample_classes, iap.Deterministic)\n\n    def test_draw_masks__fixed_class_ids(self):\n        segmap_arr = np.zeros((3, 2, 2), dtype=np.int32)\n        segmap_arr[0, 0, 0] = 1\n        segmap_arr[0, 1, 0] = 2\n        segmap_arr[1, 0, 0] = 1\n        segmap_arr[0, 0, 1] = 3\n        segmap_arr[1, 1, 1] = 3\n        segmap = ia.SegmentationMapsOnImage(segmap_arr, shape=(3, 2, 3))\n        batch = _BatchInAugmentation(segmentation_maps=[segmap])\n        gen = iaa.SegMapClassIdsMaskGen([2, 3])\n\n        mask = gen.draw_masks(batch, random_state=1)[0]\n\n        assert mask.shape == segmap_arr.shape[0:2]\n        assert mask.dtype.name == \"float32\"\n        assert np.isclose(mask[0, 0], 1.0)  # class id 1 and 3\n        assert np.isclose(mask[0, 1], 1.0)  # class id 2 and 0\n        assert np.isclose(mask[1, 1], 1.0)  # class id 0 and 3\n        assert np.isclose(mask[1, 0], 0.0)  # class id 1 and 0\n        assert np.allclose(mask[2, :], 0.0)  # class id 0 in whole row\n\n    def test_draw_masks__stochastic_class_ids(self):\n        segmap_arr = np.zeros((3, 2, 2), dtype=np.int32)\n        segmap_arr[0, 0, 0] = 1\n        segmap_arr[0, 1, 0] = 2\n        segmap_arr[1, 0, 0] = 1\n        segmap_arr[0, 0, 1] = 3\n        segmap_arr[1, 1, 1] = 3\n        segmap = ia.SegmentationMapsOnImage(segmap_arr, shape=(3, 2, 3))\n        batch = _BatchInAugmentation(segmentation_maps=[segmap])\n        gen = iaa.SegMapClassIdsMaskGen([2, 3], nb_sample_classes=1)\n\n        expected_class_2 = np.float32([\n            [0, 1],\n            [0, 0],\n            [0, 0]\n        ])\n        expected_class_3 = np.float32([\n            [1, 0],\n            [0, 1],\n            [0, 0]\n        ])\n        seen = [False, False]\n        for i in np.arange(50):\n            mask = gen.draw_masks(batch, random_state=i)[0]\n\n            if np.allclose(mask, expected_class_2):\n                seen[0] = True\n            elif np.allclose(mask, expected_class_3):\n                seen[1] = True\n            else:\n                assert False\n\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_generate_mask(self):\n        segmap_arr = np.zeros((3, 2, 2), dtype=np.int32)\n        segmap_arr[0, 0, 0] = 1\n        segmap_arr[0, 1, 0] = 2\n        segmap_arr[1, 0, 0] = 1\n        segmap_arr[0, 0, 1] = 3\n        segmap_arr[1, 1, 1] = 3\n        segmap = ia.SegmentationMapsOnImage(segmap_arr, shape=(3, 2, 3))\n\n        mask = iaa.SegMapClassIdsMaskGen.generate_mask(segmap, [1, 2])\n\n        expected = np.float32([\n            [1.0, 1.0],\n            [1.0, 0.0],\n            [0.0, 0.0]\n        ])\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask__smaller_than_image(self):\n        segmap_arr = np.zeros((3, 2, 2), dtype=np.int32)\n        segmap_arr[0, 0, 0] = 1\n        segmap_arr[0, 1, 0] = 2\n        segmap_arr[1, 0, 0] = 1\n        segmap_arr[0, 0, 1] = 3\n        segmap_arr[1, 1, 1] = 3\n        segmap = ia.SegmentationMapsOnImage(segmap_arr, shape=(3, 4))\n\n        mask = iaa.SegMapClassIdsMaskGen.generate_mask(segmap, [1, 2])\n\n        expected = np.float32([\n            [1.0, 1.0, 1.0, 1.0],\n            [1.0, 1.0, 0.0, 0.0],\n            [0.0, 0.0, 0.0, 0.0]\n        ])\n        assert np.allclose(mask, expected, rtol=0.0, atol=0.1)\n\n    def test_zero_sized_axes(self):\n        # zero-sized segmap arrays currently crash when creating\n        # SegmentationMapsOnImage and that's probably better that way\n        segmap_shapes = [\n            (2, 2, 1)\n        ]\n\n        image_shapes = [\n            (2, 3, 3),\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for segmap_shape in segmap_shapes:\n            for image_shape in image_shapes:\n                with self.subTest(segmap_shape=segmap_shape,\n                                  image_shape=image_shape):\n                    segmap_arr = np.zeros(segmap_shape, dtype=np.int32)\n                    segmap = ia.SegmentationMapsOnImage(segmap_arr,\n                                                        shape=image_shape)\n                    batch = _BatchInAugmentation(segmentation_maps=[segmap])\n\n                    gen = iaa.SegMapClassIdsMaskGen(1)\n                    mask = gen.draw_masks(batch)[0]\n                    assert mask.shape == image_shape[0:2]\n                    assert mask.dtype.name == \"float32\"\n                    assert np.allclose(mask, 0.0)\n\n    def test_batch_contains_no_segmaps(self):\n        hms = ia.HeatmapsOnImage(np.zeros((5, 5), dtype=np.float32),\n                                 shape=(10, 10, 3))\n        batch = _BatchInAugmentation(heatmaps=[hms])\n        gen = iaa.SegMapClassIdsMaskGen(class_ids=[1])\n\n        with self.assertRaises(AssertionError):\n            _mask = gen.draw_masks(batch)[0]\n\n\nclass TestBoundingBoxesMaskGen(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___no_labels(self):\n        gen = iaa.BoundingBoxesMaskGen()\n        assert gen.labels is None\n        assert gen.nb_sample_labels is None\n\n    def test___init___fixed_labels_single_str(self):\n        gen = iaa.BoundingBoxesMaskGen(\"person\")\n        assert gen.labels == [\"person\"]\n        assert gen.nb_sample_labels is None\n\n    def test___init___fixed_labels_list(self):\n        gen = iaa.BoundingBoxesMaskGen([\"person\", \"car\"])\n        assert gen.labels == [\"person\", \"car\"]\n        assert gen.nb_sample_labels is None\n\n    def test___init___labels_stochastic(self):\n        gen = iaa.BoundingBoxesMaskGen([\"person\", \"car\"], nb_sample_labels=2)\n        assert is_parameter_instance(gen.labels, iap.Choice)\n        assert is_parameter_instance(gen.nb_sample_labels, iap.Deterministic)\n\n    def test_draw_masks__labels_is_none(self):\n        bbs = [ia.BoundingBox(x1=1, y1=1, x2=5, y2=5, label=\"bb1\"),\n               ia.BoundingBox(x1=-3, y1=4, x2=20, y2=8, label=\"bb2\"),\n               ia.BoundingBox(x1=2, y1=2, x2=10, y2=10, label=\"bb3\")]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(10, 14, 3))\n\n        batch = _BatchInAugmentation(bounding_boxes=[bbsoi])\n        gen = iaa.BoundingBoxesMaskGen()\n\n        mask = gen.draw_masks(batch, random_state=1)[0]\n\n        expected = np.zeros((10, 14), dtype=np.float32)\n        expected[1:5, 1:5] = 1.0  # bb1\n        expected[4:8, 0:14] = 1.0  # bb2 clipped to image shape\n        expected[2:10, 2:10] = 1.0  # bb3\n        assert mask.shape == (10, 14)\n        assert mask.dtype.name == \"float32\"\n        assert np.allclose(mask, expected)\n\n    def test_draw_masks__fixed_labels(self):\n        bbs = [ia.BoundingBox(x1=1, y1=1, x2=5, y2=5, label=\"bb1\"),\n               ia.BoundingBox(x1=-3, y1=4, x2=20, y2=8, label=\"bb2\"),\n               ia.BoundingBox(x1=2, y1=2, x2=10, y2=10, label=\"bb3\")]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(10, 14, 3))\n\n        batch = _BatchInAugmentation(bounding_boxes=[bbsoi])\n        gen = iaa.BoundingBoxesMaskGen([\"bb1\", \"bb2\"])\n\n        mask = gen.draw_masks(batch, random_state=1)[0]\n\n        expected = np.zeros((10, 14), dtype=np.float32)\n        expected[1:5, 1:5] = 1.0  # bb1\n        expected[4:8, 0:14] = 1.0  # bb2 clipped to image shape\n        assert mask.shape == (10, 14)\n        assert mask.dtype.name == \"float32\"\n        assert np.allclose(mask, expected)\n\n    def test_draw_masks__stochastic_labels(self):\n        bbs = [ia.BoundingBox(x1=1, y1=1, x2=5, y2=5, label=\"bb1\"),\n               ia.BoundingBox(x1=-3, y1=4, x2=20, y2=8, label=\"bb2\"),\n               ia.BoundingBox(x1=2, y1=2, x2=10, y2=10, label=\"bb3\")]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(10, 14, 3))\n\n        batch = _BatchInAugmentation(bounding_boxes=[bbsoi])\n        gen = iaa.BoundingBoxesMaskGen(\n            iap.DeterministicList([\"bb1\", \"bb2\"]),\n            nb_sample_labels=3)\n\n        mask = gen.draw_masks(batch, random_state=1)[0]\n\n        expected = np.zeros((10, 14), dtype=np.float32)\n        expected[1:5, 1:5] = 1.0  # bb1\n        expected[4:8, 0:14] = 1.0  # bb2 clipped to image shape\n        assert mask.shape == (10, 14)\n        assert mask.dtype.name == \"float32\"\n        assert np.allclose(mask, expected)\n\n    def test_generate_mask(self):\n        bbs = [ia.BoundingBox(x1=1, y1=1, x2=5, y2=5, label=\"bb1\"),\n               ia.BoundingBox(x1=-3, y1=4, x2=20, y2=8, label=\"bb2\"),\n               ia.BoundingBox(x1=2, y1=2, x2=10, y2=10, label=\"bb3\")]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(10, 14, 3))\n\n        mask = iaa.BoundingBoxesMaskGen.generate_mask(bbsoi, [\"bb1\", \"bb2\"])\n\n        expected = np.zeros((10, 14), dtype=np.float32)\n        expected[1:5, 1:5] = 1.0  # bb1\n        expected[4:8, 0:14] = 1.0  # bb2 clipped to image shape\n        assert mask.shape == (10, 14)\n        assert mask.dtype.name == \"float32\"\n        assert np.allclose(mask, expected)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                bbs = [ia.BoundingBox(x1=1, y1=1, x2=5, y2=5, label=\"bb1\"),\n                       ia.BoundingBox(x1=-3, y1=4, x2=20, y2=8, label=\"bb2\"),\n                       ia.BoundingBox(x1=2, y1=2, x2=10, y2=10, label=\"bb3\")]\n                bbsoi = ia.BoundingBoxesOnImage(bbs, shape=shape)\n                batch = _BatchInAugmentation(bounding_boxes=[bbsoi])\n                gen = iaa.BoundingBoxesMaskGen(\"bb1\")\n\n                mask = gen.draw_masks(batch)[0]\n\n                assert mask.shape == shape[0:2]\n                assert mask.dtype.name == \"float32\"\n                assert np.allclose(mask, 0.0)\n\n    def test_batch_contains_no_bounding_boxes(self):\n        hms = ia.HeatmapsOnImage(np.zeros((5, 5), dtype=np.float32),\n                                 shape=(10, 10, 3))\n        batch = _BatchInAugmentation(heatmaps=[hms])\n        gen = iaa.SegMapClassIdsMaskGen(class_ids=[1])\n\n        with self.assertRaises(AssertionError):\n            _mask = gen.draw_masks(batch)[0]\n\n\nclass InvertMaskGen(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        child = iaa.HorizontalLinearGradientMaskGen()\n        gen = iaa.InvertMaskGen(0.5, child)\n        assert np.isclose(gen.p.p.value, 0.5)\n        assert gen.child is child\n\n    def test_draw_masks(self):\n        image = np.zeros((1, 20), dtype=np.uint8)\n        batch = _BatchInAugmentation(images=[image] * 200)\n\n        child = iaa.HorizontalLinearGradientMaskGen(min_value=0.0,\n                                                    max_value=1.0,\n                                                    start_at=0.0,\n                                                    end_at=1.0)\n        gen = iaa.InvertMaskGen(0.5, child)\n\n        masks = gen.draw_masks(batch, random_state=1)\n\n        hgrad = iaa.HorizontalLinearGradientMaskGen.generate_mask(\n            (1, 20), min_value=0.0, max_value=1.0, start_at=0.0, end_at=1.0)\n        expected1 = hgrad\n        expected2 = 1.0 - hgrad\n        seen = [0, 0]\n        for mask in masks:\n            if np.allclose(mask, expected1):\n                seen[0] += 1\n            elif np.allclose(mask, expected2):\n                seen[1] += 1\n            else:\n                assert False\n        assert np.allclose(seen, 0.5*200, rtol=0, atol=20)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 0, 0),\n            (1, 0, 0),\n            (0, 1, 0),\n            (0, 0, 1),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                batch = _BatchInAugmentation(images=[image])\n                child = iaa.HorizontalLinearGradientMaskGen()\n                gen = iaa.InvertMaskGen(0.5, child)\n\n                mask = gen.draw_masks(batch)[0]\n\n                assert mask.shape == shape[0:2]\n                assert mask.dtype.name == \"float32\"\n\n\nclass TestSimplexNoiseAlpha(unittest.TestCase):\n    def test_deprecation_warning(self):\n        aug1 = iaa.Sequential([])\n        aug2 = iaa.Sequential([])\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            aug = iaa.SimplexNoiseAlpha(first=aug1, second=aug2)\n\n            assert (\n                \"is deprecated\"\n                in str(caught_warnings[-1].message)\n            )\n\n        assert isinstance(aug, iaa.BlendAlphaSimplexNoise)\n        assert aug.foreground is aug1\n        assert aug.background is aug2\n\n\nclass TestFrequencyNoiseAlpha(unittest.TestCase):\n    def test_deprecation_warning(self):\n        aug1 = iaa.Sequential([])\n        aug2 = iaa.Sequential([])\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            aug = iaa.FrequencyNoiseAlpha(first=aug1, second=aug2)\n\n            assert (\n                \"is deprecated\"\n                in str(caught_warnings[-1].message)\n            )\n\n        assert isinstance(aug, iaa.BlendAlphaFrequencyNoise)\n        assert aug.foreground is aug1\n        assert aug.background is aug2\n"
  },
  {
    "path": "test/augmenters/test_blur.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport warnings\nimport sys\nimport itertools\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport six.moves as sm\nimport cv2\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug import dtypes as iadt\nfrom imgaug import random as iarandom\nfrom imgaug.testutils import keypoints_equal, reseed, runtest_pickleable_uint8_img\n\n\nclass Test_blur_gaussian_(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_integration(self):\n        backends = [\"auto\", \"scipy\", \"cv2\"]\n        nb_channels_lst = [None, 1, 3, 4, 5, 10]\n\n        gen = itertools.product(backends, nb_channels_lst)\n        for backend, nb_channels in gen:\n            with self.subTest(backend=backend, nb_channels=nb_channels):\n                image = np.zeros((5, 5), dtype=np.uint8)\n                if nb_channels is not None:\n                    image = np.tile(image[..., np.newaxis], (1, 1, nb_channels))\n\n                image[2, 2] = 255\n                mask = image < 255\n                observed = iaa.blur_gaussian_(\n                    np.copy(image), sigma=5.0, backend=backend)\n                assert observed.shape == image.shape\n                assert observed.dtype.name == \"uint8\"\n                assert np.all(observed[2, 2] < 255)\n                assert np.sum(observed[mask]) > (5*5-1)\n                if nb_channels is not None and nb_channels > 1:\n                    for c in sm.xrange(1, observed.shape[2]):\n                        assert np.array_equal(observed[..., c],\n                                              observed[..., 0])\n\n    def test_sigma_zero(self):\n        image = np.arange(4*4).astype(np.uint8).reshape((4, 4))\n        observed = iaa.blur_gaussian_(np.copy(image), 0)\n        assert np.array_equal(observed, image)\n\n        image = np.arange(4*4).astype(np.uint8).reshape((4, 4, 1))\n        observed = iaa.blur_gaussian_(np.copy(image), 0)\n        assert np.array_equal(observed, image)\n\n        image = np.arange(4*4*3).astype(np.uint8).reshape((4, 4, 3))\n        observed = iaa.blur_gaussian_(np.copy(image), 0)\n        assert np.array_equal(observed, image)\n\n    def test_eps(self):\n        image = np.arange(4*4).astype(np.uint8).reshape((4, 4))\n        observed_no_eps = iaa.blur_gaussian_(np.copy(image), 1.0, eps=0)\n        observed_with_eps = iaa.blur_gaussian_(np.copy(image), 1.0, eps=1e10)\n        assert not np.array_equal(observed_no_eps, observed_with_eps)\n        assert np.array_equal(observed_with_eps, image)\n\n    def test_ksize(self):\n        def side_effect(image, ksize, sigmaX, sigmaY, borderType):\n            return image + 1\n\n        sigmas = [5.0, 5.0]\n        ksizes = [None, 3]\n        ksizes_expected = [2.6*5.0, 3]\n        gen = zip(sigmas, ksizes, ksizes_expected)\n\n        for (sigma, ksize, ksize_expected) in gen:\n            with self.subTest(sigma=sigma, ksize=ksize):\n                mock_GaussianBlur = mock.Mock(side_effect=side_effect)\n                image = np.arange(4*4).astype(np.uint8).reshape((4, 4))\n                with mock.patch('cv2.GaussianBlur', mock_GaussianBlur):\n                    observed = iaa.blur_gaussian_(\n                        np.copy(image),\n                        sigma=sigma,\n                        ksize=ksize,\n                        backend=\"cv2\")\n                    assert np.array_equal(observed, image+1)\n\n                cargs = mock_GaussianBlur.call_args\n                assert mock_GaussianBlur.call_count == 1\n                assert np.array_equal(cargs[0][0], image)\n                assert isinstance(cargs[0][1], tuple)\n                assert np.allclose(\n                    np.float32(cargs[0][1]),\n                    np.float32([ksize_expected, ksize_expected]))\n                assert np.isclose(cargs[1][\"sigmaX\"], sigma)\n                assert np.isclose(cargs[1][\"sigmaY\"], sigma)\n                assert cargs[1][\"borderType\"] == cv2.BORDER_REFLECT_101\n\n    def test_more_than_four_channels(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.blur_gaussian_(np.copy(image), 1.0)\n\n                assert image_aug.shape == image.shape\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.blur_gaussian_(np.copy(image), 1.0)\n\n                assert image_aug.shape == image.shape\n\n    def test_backends_called(self):\n        def side_effect_cv2(image, ksize, sigmaX, sigmaY, borderType):\n            return image + 1\n\n        def side_effect_scipy(image, sigma, mode):\n            return image + 1\n\n        mock_GaussianBlur = mock.Mock(side_effect=side_effect_cv2)\n        mock_gaussian_filter = mock.Mock(side_effect=side_effect_scipy)\n        image = np.arange(4*4).astype(np.uint8).reshape((4, 4))\n        with mock.patch('cv2.GaussianBlur', mock_GaussianBlur):\n            _observed = iaa.blur_gaussian_(\n                np.copy(image), sigma=1.0, eps=0, backend=\"cv2\")\n        assert mock_GaussianBlur.call_count == 1\n\n        with mock.patch('scipy.ndimage.gaussian_filter', mock_gaussian_filter):\n            _observed = iaa.blur_gaussian_(\n                np.copy(image), sigma=1.0, eps=0, backend=\"scipy\")\n        assert mock_gaussian_filter.call_count == 1\n\n    def test_backends_similar(self):\n        with self.subTest(nb_channels=None):\n            size = 10\n            image = np.arange(\n                0, size*size).astype(np.uint8).reshape((size, size))\n            image_cv2 = iaa.blur_gaussian_(\n                np.copy(image), sigma=3.0, ksize=20, backend=\"cv2\")\n            image_scipy = iaa.blur_gaussian_(\n                np.copy(image), sigma=3.0, backend=\"scipy\")\n            diff = np.abs(image_cv2.astype(np.int32)\n                          - image_scipy.astype(np.int32))\n            assert np.average(diff) < 0.05 * (size * size)\n\n        with self.subTest(nb_channels=3):\n            size = 10\n            image = np.arange(\n                0, size*size).astype(np.uint8).reshape((size, size))\n            image = np.tile(image[..., np.newaxis], (1, 1, 3))\n            image[1] += 1\n            image[2] += 2\n            image_cv2 = iaa.blur_gaussian_(\n                np.copy(image), sigma=3.0, ksize=20, backend=\"cv2\")\n            image_scipy = iaa.blur_gaussian_(\n                np.copy(image), sigma=3.0, backend=\"scipy\")\n            diff = np.abs(image_cv2.astype(np.int32)\n                          - image_scipy.astype(np.int32))\n            assert np.average(diff) < 0.05 * (size * size)\n            for c in sm.xrange(3):\n                diff = np.abs(image_cv2[..., c].astype(np.int32)\n                              - image_scipy[..., c].astype(np.int32))\n                assert np.average(diff) < 0.05 * (size * size)\n\n    def test_view(self):\n        for backend in [\"auto\", \"scipy\", \"cv2\"]:\n            image = np.array([\n                [0, 0, 0, 0, 0],\n                [0, 0, 0, 0, 0],\n                [0, 0, 255, 0, 0],\n                [0, 0, 0, 0, 0],\n                [0, 0, 0, 0, 0],\n                [1, 1, 1, 1, 1]\n            ], dtype=np.uint8)\n            image_cp = np.copy(image[0:5, :])\n\n            image_aug = iaa.blur_gaussian_(image[0:5, :], 3.0, backend=backend)\n\n            assert image_aug.shape == (5, 5)\n            assert image_aug.dtype.name == \"uint8\"\n            assert np.all(image_aug[image_cp == 0] > 0)\n            assert np.all(image_aug[image_cp == 255] < 255)\n\n    def test_non_contiguous(self):\n        for backend in [\"auto\", \"scipy\", \"cv2\"]:\n            image = np.array([\n                [0, 0, 0, 0, 0],\n                [0, 0, 0, 0, 0],\n                [0, 0, 255, 0, 0],\n                [0, 0, 0, 0, 0],\n                [0, 0, 0, 0, 0]\n            ], dtype=np.uint8, order=\"F\")\n            image_cp = np.copy(image)\n\n            image_aug = iaa.blur_gaussian_(image, 3.0, backend=backend)\n\n            assert image_aug.shape == (5, 5)\n            assert image_aug.dtype.name == \"uint8\"\n            assert np.all(image_aug[image_cp == 0] > 0)\n            assert np.all(image_aug[image_cp == 255] < 255)\n\n    def test_warnings(self):\n        # note that self.assertWarningRegex does not exist in python 2.7\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            _ = iaa.blur_gaussian_(\n                np.zeros((1, 1), dtype=np.uint32),\n                sigma=3.0,\n                ksize=11,\n                backend=\"scipy\")\n\n            assert len(caught_warnings) == 1\n            assert (\n                \"but also provided 'ksize' argument\"\n                in str(caught_warnings[-1].message))\n\n    def test_other_dtypes_sigma_0(self):\n        try:\n            f128 = [np.dtype(\"float128\").name]\n        except TypeError:\n            f128 = []\n\n        dtypes_to_test_list = [\n            [\"bool\",\n             \"uint8\", \"uint16\", \"uint32\", \"uint64\",\n             \"int8\", \"int16\", \"int32\", \"int64\",\n             \"float16\", \"float32\", \"float64\"] + f128,\n            [\"bool\",\n             \"uint8\", \"uint16\", \"uint32\", \"uint64\",\n             \"int8\", \"int16\", \"int32\", \"int64\",\n             \"float16\", \"float32\", \"float64\"] + f128\n        ]\n        gen = zip([\"scipy\", \"cv2\"], dtypes_to_test_list)\n\n        for backend, dtypes_to_test in gen:\n            # bool\n            if \"bool\" in dtypes_to_test:\n                with self.subTest(backend=backend, dtype=\"bool\"):\n                    image = np.zeros((3, 3), dtype=bool)\n                    image[1, 1] = True\n                    image_aug = iaa.blur_gaussian_(\n                        np.copy(image), sigma=0, backend=backend)\n                    assert image_aug.dtype.name == \"bool\"\n                    assert np.all(image_aug == image)\n\n            # uint, int\n            uint_dts = [np.uint8, np.uint16, np.uint32, np.uint64]\n            int_dts = [np.int8, np.int16, np.int32, np.int64]\n            for dtype in uint_dts + int_dts:\n                dtype = np.dtype(dtype)\n                if dtype.name in dtypes_to_test:\n                    with self.subTest(backend=backend, dtype=dtype.name):\n                        _min_value, center_value, _max_value = \\\n                            iadt.get_value_range_of_dtype(dtype)\n                        image = np.zeros((3, 3), dtype=dtype)\n                        image[1, 1] = int(center_value)\n                        image_aug = iaa.blur_gaussian_(\n                            np.copy(image), sigma=0, backend=backend)\n                        assert image_aug.dtype.name == dtype.name\n                        assert np.all(image_aug == image)\n\n            # float\n            float_dts = [np.float16, np.float32, np.float64] + f128\n            for dtype in float_dts:\n                dtype = np.dtype(dtype)\n                if dtype.name in dtypes_to_test:\n                    with self.subTest(backend=backend, dtype=dtype.name):\n                        _min_value, center_value, _max_value = \\\n                            iadt.get_value_range_of_dtype(dtype)\n                        image = np.zeros((3, 3), dtype=dtype)\n                        image[1, 1] = center_value\n                        image_aug = iaa.blur_gaussian_(\n                            np.copy(image), sigma=0, backend=backend)\n                        assert image_aug.dtype.name == dtype.name\n                        assert np.allclose(image_aug, image)\n\n    def test_other_dtypes_sigma_075(self):\n        # prototype kernel, generated via:\n        # mask = np.zeros((5, 5), dtype=np.int32)\n        # mask[2, 2] = 1000 * 1000\n        # kernel = ndimage.gaussian_filter(mask, 0.75)\n        mask = np.float64([\n           [   923,   6650,  16163,   6650,    923],\n           [  6650,  47896, 116408,  47896,   6650],\n           [ 16163, 116408, 282925, 116408,  16163],\n           [  6650,  47896, 116408,  47896,   6650],\n           [   923,   6650,  16163,   6650,    923]\n        ]) / (1000.0 * 1000.0)\n\n        dtypes_to_test_list = [\n            # scipy\n            [\"bool\",\n             \"uint8\", \"uint16\", \"uint32\", \"uint64\",\n             \"int8\", \"int16\", \"int32\", \"int64\",\n             \"float16\", \"float32\", \"float64\"],\n            # cv2\n            [\"bool\",\n             \"uint8\", \"uint16\",\n             \"int8\", \"int16\", \"int32\",\n             \"float16\", \"float32\", \"float64\"]\n        ]\n        gen = zip([\"scipy\", \"cv2\"], dtypes_to_test_list)\n\n        for backend, dtypes_to_test in gen:\n            # bool\n            if \"bool\" in dtypes_to_test:\n                with self.subTest(backend=backend, dtype=\"bool\"):\n                    image = np.zeros((5, 5), dtype=bool)\n                    image[2, 2] = True\n                    image_aug = iaa.blur_gaussian_(\n                        np.copy(image), sigma=0.75, backend=backend)\n                    assert image_aug.dtype.name == \"bool\"\n                    assert np.all(image_aug == (mask > 0.5))\n\n            # uint, int\n            uint_dts = [np.uint8, np.uint16, np.uint32, np.uint64]\n            int_dts = [np.int8, np.int16, np.int32, np.int64]\n            for dtype in uint_dts + int_dts:\n                dtype = np.dtype(dtype)\n                if dtype.name in dtypes_to_test:\n                    with self.subTest(backend=backend, dtype=dtype.name):\n                        min_value, center_value, max_value = \\\n                            iadt.get_value_range_of_dtype(dtype)\n                        dynamic_range = max_value - min_value\n\n                        value = int(center_value + 0.4 * max_value)\n                        image = np.zeros((5, 5), dtype=dtype)\n                        image[2, 2] = value\n                        image_aug = iaa.blur_gaussian_(\n                            image, sigma=0.75, backend=backend)\n                        expected = (mask * value).astype(dtype)\n                        diff = np.abs(image_aug.astype(np.int64)\n                                      - expected.astype(np.int64))\n                        assert image_aug.shape == mask.shape\n                        assert image_aug.dtype.type == dtype\n                        if dtype.itemsize <= 1:\n                            assert np.max(diff) <= 4\n                        else:\n                            assert np.max(diff) <= 0.01 * dynamic_range\n\n            # float\n            float_dts = [np.float16, np.float32, np.float64]\n            values = [5000, 1000**1, 1000**2, 1000**3]\n            for dtype, value in zip(float_dts, values):\n                dtype = np.dtype(dtype)\n                if dtype.name in dtypes_to_test:\n                    with self.subTest(backend=backend, dtype=dtype.name):\n                        image = np.zeros((5, 5), dtype=dtype)\n                        image[2, 2] = value\n                        image_aug = iaa.blur_gaussian_(\n                            image, sigma=0.75, backend=backend)\n                        expected = (mask * value).astype(dtype)\n                        diff = np.abs(image_aug.astype(np.float64)\n                                      - expected.astype(np.float64))\n                        assert image_aug.shape == mask.shape\n                        assert image_aug.dtype.type == dtype\n                        # accepts difference of 2.0, 4.0, 8.0, 16.0 (at 1,\n                        # 2, 4, 8 bytes, i.e. 8, 16, 32, 64 bit)\n                        max_diff = (\n                            np.dtype(dtype).itemsize\n                            * 0.01\n                            * np.float64(value)\n                        )\n                        assert np.max(diff) < max_diff\n\n    def test_other_dtypes_bool_at_sigma_06(self):\n        # --\n        # blur of bool input at sigma=0.6\n        # --\n        # here we use a special mask and sigma as otherwise the only values\n        # ending up with >0.5 would be the ones that\n        # were before the blur already at >0.5\n        # prototype kernel, generated via:\n        #  mask = np.zeros((5, 5), dtype=np.float64)\n        #  mask[1, 0] = 255\n        #  mask[2, 0] = 255\n        #  mask[2, 2] = 255\n        #  mask[2, 4] = 255\n        #  mask[3, 0] = 255\n        #  mask = ndimage.gaussian_filter(mask, 1.0, mode=\"mirror\")\n        mask_bool = np.float64([\n           [ 57,  14,   2,   1,   1],\n           [142,  42,  29,  14,  28],\n           [169,  69, 114,  56, 114],\n           [142,  42,  29,  14,  28],\n           [ 57,  14,   2,   1,   1]\n        ]) / 255.0\n\n        image = np.zeros((5, 5), dtype=bool)\n        image[1, 0] = True\n        image[2, 0] = True\n        image[2, 2] = True\n        image[2, 4] = True\n        image[3, 0] = True\n\n        for backend in [\"scipy\", \"cv2\"]:\n            image_aug = iaa.blur_gaussian_(\n                np.copy(image), sigma=0.6, backend=backend)\n            expected = mask_bool > 0.5\n            assert image_aug.shape == mask_bool.shape\n            assert image_aug.dtype.type == np.bool_\n            assert np.all(image_aug == expected)\n\n\nclass Test_blur_avg_(unittest.TestCase):\n    @classmethod\n    def _avg(cls, values):\n        return int(np.round(np.average(values)))\n\n    def test_kernel_size_is_int(self):\n        # reflection padded:\n        # [6, 5, 6, 7, 8, 7],\n        # [2, 1, 2, 3, 4, 3],\n        # [6, 5, 6, 7, 8, 7],\n        # [10, 9, 10, 11, 12, 11],\n        # [14, 13, 14, 15, 16, 15]\n        # [10, 9, 10, 11, 12, 11],\n        image = np.array([\n            [1, 2, 3, 4],\n            [5, 6, 7, 8],\n            [9, 10, 11, 12],\n            [13, 14, 15, 16]\n        ], dtype=np.uint8)\n\n        image_aug = iaa.blur_avg_(np.copy(image), 3)\n\n        assert image_aug[0, 0] == self._avg([6, 5, 6, 2, 1, 2, 6, 5, 6])\n        assert image_aug[0, 1] == self._avg([5, 6, 7, 1, 2, 3, 5, 6, 7])\n        assert image_aug[3, 3] == self._avg([11, 12, 11, 15, 16, 15, 11, 12,\n                                             11])\n\n    def test_kernel_size_is_tuple(self):\n        # reflection padded:\n        # [6, 5, 6, 7, 8, 7],\n        # [2, 1, 2, 3, 4, 3],\n        # [6, 5, 6, 7, 8, 7],\n        # [10, 9, 10, 11, 12, 11],\n        # [14, 13, 14, 15, 16, 15]\n        # [10, 9, 10, 11, 12, 11],\n        image = np.array([\n            [1, 2, 3, 4],\n            [5, 6, 7, 8],\n            [9, 10, 11, 12],\n            [13, 14, 15, 16]\n        ], dtype=np.uint8)\n\n        image_aug = iaa.blur_avg_(np.copy(image), (3, 1))\n\n        assert image_aug[0, 0] == self._avg([5, 1, 5])\n        assert image_aug[0, 1] == self._avg([6, 2, 6])\n        assert image_aug[3, 3] == self._avg([12, 16, 12])\n\n    def test_view(self):\n        # reflection padded (after crop):\n        # [6, 5, 6, 7, 8, 7],\n        # [2, 1, 2, 3, 4, 3],\n        # [6, 5, 6, 7, 8, 7],\n        # [10, 9, 10, 11, 12, 11],\n        # [14, 13, 14, 15, 16, 15]\n        # [10, 9, 10, 11, 12, 11],\n        image = np.array([\n            [1, 2, 3, 4],\n            [5, 6, 7, 8],\n            [9, 10, 11, 12],\n            [13, 14, 15, 16],\n            [0, 0, 0, 0]\n        ], dtype=np.uint8)\n\n        image_aug = iaa.blur_avg_(np.copy(image)[0:4, :], 3)\n\n        assert image_aug[0, 0] == self._avg([6, 5, 6, 2, 1, 2, 6, 5, 6])\n        assert image_aug[0, 1] == self._avg([5, 6, 7, 1, 2, 3, 5, 6, 7])\n        assert image_aug[3, 3] == self._avg([11, 12, 11, 15, 16, 15, 11, 12,\n                                             11])\n\n    def test_noncontiguous(self):\n        # reflection padded:\n        # [6, 5, 6, 7, 8, 7],\n        # [2, 1, 2, 3, 4, 3],\n        # [6, 5, 6, 7, 8, 7],\n        # [10, 9, 10, 11, 12, 11],\n        # [14, 13, 14, 15, 16, 15]\n        # [10, 9, 10, 11, 12, 11],\n        image = np.array([\n            [1, 2, 3, 4],\n            [5, 6, 7, 8],\n            [9, 10, 11, 12],\n            [13, 14, 15, 16]\n        ], dtype=np.uint8, order=\"F\")\n\n        image_aug = iaa.blur_avg_(image, 3)\n\n        assert image_aug[0, 0] == self._avg([6, 5, 6, 2, 1, 2, 6, 5, 6])\n        assert image_aug[0, 1] == self._avg([5, 6, 7, 1, 2, 3, 5, 6, 7])\n        assert image_aug[3, 3] == self._avg([11, 12, 11, 15, 16, 15, 11, 12,\n                                             11])\n\n\nclass Test_blur_mean_shift_(unittest.TestCase):\n    @property\n    def image(self):\n        image = [\n            [1, 2, 3, 4, 200, 201, 202, 203],\n            [1, 2, 3, 4, 200, 201, 202, 203],\n            [1, 2, 3, 4, 200, 201, 202, 203],\n            [1, 2, 3, 4, 200, 201, 202, 203]\n        ]\n        image = np.array(image, dtype=np.uint8).reshape((4, 2*4, 1))\n        image = np.tile(image, (1, 1, 3))\n        return image\n\n    def test_simple_image(self):\n        image = self.image\n\n        image_blurred = iaa.blur_mean_shift_(np.copy(image), 0.5, 0.5)\n\n        assert image_blurred.shape == image.shape\n        assert image_blurred.dtype.name == \"uint8\"\n        assert not np.array_equal(image_blurred, image)\n        assert 0 <= np.average(image[:, 0:4, :]) <= 5\n        assert 199 <= np.average(image[:, 4:, :]) <= 203\n\n    def test_hw_image(self):\n        image = self.image[:, :, 0]\n\n        image_blurred = iaa.blur_mean_shift_(np.copy(image), 0.5, 0.5)\n\n        assert image_blurred.shape == image.shape\n        assert image_blurred.dtype.name == \"uint8\"\n        assert not np.array_equal(image_blurred, image)\n\n    def test_hw1_image(self):\n        image = self.image[:, :, 0:1]\n\n        image_blurred = iaa.blur_mean_shift_(np.copy(image), 0.5, 0.5)\n\n        assert image_blurred.ndim == 3\n        assert image_blurred.shape == image.shape\n        assert image_blurred.dtype.name == \"uint8\"\n        assert not np.array_equal(image_blurred, image)\n\n    def test_non_contiguous_image(self):\n        image = self.image\n        image_cp = np.copy(np.fliplr(image))\n        image = np.fliplr(image)\n        assert image.flags[\"C_CONTIGUOUS\"] is False\n\n        image_blurred = iaa.blur_mean_shift_(image, 0.5, 0.5)\n\n        assert image_blurred.shape == image_cp.shape\n        assert image_blurred.dtype.name == \"uint8\"\n        assert not np.array_equal(image_blurred, image_cp)\n\n    def test_both_parameters_are_zero(self):\n        image = self.image[:, :, 0]\n\n        image_blurred = iaa.blur_mean_shift_(np.copy(image), 0, 0)\n\n        assert image_blurred.shape == image.shape\n        assert image_blurred.dtype.name == \"uint8\"\n        assert not np.array_equal(image_blurred, image)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.blur_mean_shift_(np.copy(image), 1.0, 1.0)\n\n                assert image_aug.shape == image.shape\n\n\nclass TestGaussianBlur(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_sigma_is_zero(self):\n        # no blur, shouldnt change anything\n        base_img = np.array([[0, 0, 0],\n                         [0, 255, 0],\n                         [0, 0, 0]], dtype=np.uint8)\n        base_img = base_img[:, :, np.newaxis]\n        images = np.array([base_img])\n\n        aug = iaa.GaussianBlur(sigma=0)\n\n        observed = aug.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n\n    def test_low_sigma(self):\n        base_img = np.array([[0, 0, 0],\n                         [0, 255, 0],\n                         [0, 0, 0]], dtype=np.uint8)\n        base_img = base_img[:, :, np.newaxis]\n\n        images = np.array([base_img])\n        images_list = [base_img]\n        outer_pixels = ([], [])\n        for i in sm.xrange(base_img.shape[0]):\n            for j in sm.xrange(base_img.shape[1]):\n                if i != j:\n                    outer_pixels[0].append(i)\n                    outer_pixels[1].append(j)\n\n        # weak blur of center pixel\n        aug = iaa.GaussianBlur(sigma=0.5)\n        aug_det = aug.to_deterministic()\n\n        # images as numpy array\n        observed = aug.augment_images(images)\n        assert 100 < observed[0][1, 1] < 255\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 0).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 50).all()\n\n        observed = aug_det.augment_images(images)\n        assert 100 < observed[0][1, 1] < 255\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 0).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 50).all()\n\n        # images as list\n        observed = aug.augment_images(images_list)\n        assert 100 < observed[0][1, 1] < 255\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 0).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 50).all()\n\n        observed = aug_det.augment_images(images_list)\n        assert 100 < observed[0][1, 1] < 255\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 0).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 50).all()\n\n    def test_keypoints_dont_change(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        kpsoi = [ia.KeypointsOnImage(kps, shape=(3, 3, 1))]\n\n        aug = iaa.GaussianBlur(sigma=0.5)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_keypoints(kpsoi)\n        expected = kpsoi\n        assert keypoints_equal(observed, expected)\n\n        observed = aug_det.augment_keypoints(kpsoi)\n        expected = kpsoi\n        assert keypoints_equal(observed, expected)\n\n    def test_sigma_is_tuple(self):\n        # varying blur sigmas\n        base_img = np.array([[0, 0, 0],\n                         [0, 255, 0],\n                         [0, 0, 0]], dtype=np.uint8)\n        base_img = base_img[:, :, np.newaxis]\n        images = np.array([base_img])\n\n        aug = iaa.GaussianBlur(sigma=(0, 1))\n        aug_det = aug.to_deterministic()\n\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n        assert nb_changed_aug >= int(nb_iterations * 0.8)\n        assert nb_changed_aug_det == 0\n\n    def test_other_dtypes_bool_at_sigma_0(self):\n        # bool\n        aug = iaa.GaussianBlur(sigma=0)\n\n        image = np.zeros((3, 3), dtype=bool)\n        image[1, 1] = True\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == image)\n\n    def test_other_dtypes_uint_int_at_sigma_0(self):\n        aug = iaa.GaussianBlur(sigma=0)\n        dts = [np.uint8, np.uint16, np.uint32,\n               np.int8, np.int16, np.int32]\n\n        for dtype in dts:\n            _min_value, center_value, _max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = int(center_value)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == image)\n\n    def test_other_dtypes_float_at_sigma_0(self):\n        aug = iaa.GaussianBlur(sigma=0)\n        dts = [np.float16, np.float32, np.float64]\n\n        for dtype in dts:\n            _min_value, center_value, _max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = center_value\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.allclose(image_aug, image)\n\n    def test_other_dtypes_bool_at_sigma_060(self):\n        # --\n        # blur of bool input at sigma=0.6\n        # --\n        # here we use a special mask and sigma as otherwise the only values\n        # ending up with >0.5 would be the ones that\n        # were before the blur already at >0.5\n        # prototype kernel, generated via:\n        #  mask = np.zeros((5, 5), dtype=np.float64)\n        #  mask[1, 0] = 255\n        #  mask[2, 0] = 255\n        #  mask[2, 2] = 255\n        #  mask[2, 4] = 255\n        #  mask[3, 0] = 255\n        #  mask = ndimage.gaussian_filter(mask, 1.0, mode=\"mirror\")\n        aug = iaa.GaussianBlur(sigma=0.6)\n\n        mask_bool = np.float64([\n           [ 57,  14,   2,   1,   1],\n           [142,  42,  29,  14,  28],\n           [169,  69, 114,  56, 114],\n           [142,  42,  29,  14,  28],\n           [ 57,  14,   2,   1,   1]\n        ]) / 255.0\n\n        image = np.zeros((5, 5), dtype=bool)\n        image[1, 0] = True\n        image[2, 0] = True\n        image[2, 2] = True\n        image[2, 4] = True\n        image[3, 0] = True\n        image_aug = aug.augment_image(image)\n        expected = mask_bool > 0.5\n        assert image_aug.shape == mask_bool.shape\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == expected)\n\n    def test_other_dtypes_at_sigma_1(self):\n        # --\n        # blur of various dtypes at sigma=1.0\n        # and using an example value of 100 for int/uint/float and True for\n        # bool\n        # --\n        # prototype kernel, generated via:\n        #  mask = np.zeros((5, 5), dtype=np.float64)\n        #  mask[2, 2] = 100\n        #  mask = ndimage.gaussian_filter(mask, 1.0, mode=\"mirror\")\n        aug = iaa.GaussianBlur(sigma=1.0)\n\n        mask = np.float64([\n            [1, 2, 3, 2, 1],\n            [2, 5, 9, 5, 2],\n            [4, 9, 15, 9, 4],\n            [2, 5, 9, 5, 2],\n            [1, 2, 3, 2, 1]\n        ])\n\n        # uint, int\n        uint_dts = [np.uint8, np.uint16, np.uint32]\n        int_dts = [np.int8, np.int16, np.int32]\n        for dtype in uint_dts + int_dts:\n            image = np.zeros((5, 5), dtype=dtype)\n            image[2, 2] = 100\n            image_aug = aug.augment_image(image)\n            expected = mask.astype(dtype)\n            diff = np.abs(image_aug.astype(np.int64)\n                          - expected.astype(np.int64))\n            assert image_aug.shape == mask.shape\n            assert image_aug.dtype.type == dtype\n            assert np.max(diff) <= 4\n            assert np.average(diff) <= 2\n\n        # float\n        float_dts = [np.float16, np.float32, np.float64]\n        for dtype in float_dts:\n            image = np.zeros((5, 5), dtype=dtype)\n            image[2, 2] = 100.0\n            image_aug = aug.augment_image(image)\n            expected = mask.astype(dtype)\n            diff = np.abs(image_aug.astype(np.float64)\n                          - expected.astype(np.float64))\n            assert image_aug.shape == mask.shape\n            assert image_aug.dtype.type == dtype\n            assert np.max(diff) < 4\n            assert np.average(diff) < 2.0\n\n    def test_other_dtypes_at_sigma_040(self):\n        # --\n        # blur of various dtypes at sigma=0.4\n        # and using an example value of 100 for int/uint/float and True for\n        # bool\n        # --\n        aug = iaa.GaussianBlur(sigma=0.4)\n\n        # prototype kernel, generated via:\n        #  mask = np.zeros((5, 5), dtype=np.uint8)\n        #  mask[2, 2] = 100\n        #  kernel = ndimage.gaussian_filter(mask, 0.4, mode=\"mirror\")\n        mask = np.float64([\n            [0,  0,  0,  0,  0],\n            [0,  0,  3,  0,  0],\n            [0,  3, 83,  3,  0],\n            [0,  0,  3,  0,  0],\n            [0,  0,  0,  0,  0]\n        ])\n\n        # uint, int\n        uint_dts = [np.uint8, np.uint16, np.uint32]\n        int_dts = [np.int8, np.int16, np.int32]\n        for dtype in uint_dts + int_dts:\n            image = np.zeros((5, 5), dtype=dtype)\n            image[2, 2] = 100\n            image_aug = aug.augment_image(image)\n            expected = mask.astype(dtype)\n            diff = np.abs(image_aug.astype(np.int64)\n                          - expected.astype(np.int64))\n            assert image_aug.shape == mask.shape\n            assert image_aug.dtype.type == dtype\n            assert np.max(diff) <= 4\n\n        # float\n        float_dts = [np.float16, np.float32, np.float64]\n        for dtype in float_dts:\n            image = np.zeros((5, 5), dtype=dtype)\n            image[2, 2] = 100.0\n            image_aug = aug.augment_image(image)\n            expected = mask.astype(dtype)\n            diff = np.abs(image_aug.astype(np.float64)\n                          - expected.astype(np.float64))\n            assert image_aug.shape == mask.shape\n            assert image_aug.dtype.type == dtype\n            assert np.max(diff) < 4.0\n\n    def test_other_dtypes_at_sigma_075(self):\n        # --\n        # blur of various dtypes at sigma=0.75\n        # and values being half-way between center and maximum for each dtype\n        # The goal of this test is to verify that no major loss of resolution\n        # happens for large dtypes.\n        # Such inaccuracies appear for float64 if used.\n        # --\n        aug = iaa.GaussianBlur(sigma=0.75)\n\n        # prototype kernel, generated via:\n        # mask = np.zeros((5, 5), dtype=np.int32)\n        # mask[2, 2] = 1000 * 1000\n        # kernel = ndimage.gaussian_filter(mask, 0.75)\n        mask = np.float64([\n           [   923,   6650,  16163,   6650,    923],\n           [  6650,  47896, 116408,  47896,   6650],\n           [ 16163, 116408, 282925, 116408,  16163],\n           [  6650,  47896, 116408,  47896,   6650],\n           [   923,   6650,  16163,   6650,    923]\n        ]) / (1000.0 * 1000.0)\n\n        # uint, int\n        uint_dts = [np.uint8, np.uint16, np.uint32]\n        int_dts = [np.int8, np.int16, np.int32]\n        for dtype in uint_dts + int_dts:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n            dynamic_range = max_value - min_value\n\n            value = int(center_value + 0.4 * max_value)\n            image = np.zeros((5, 5), dtype=dtype)\n            image[2, 2] = value\n            image_aug = aug.augment_image(image)\n            expected = (mask * value).astype(dtype)\n            diff = np.abs(image_aug.astype(np.int64)\n                          - expected.astype(np.int64))\n            assert image_aug.shape == mask.shape\n            assert image_aug.dtype.type == dtype\n            if np.dtype(dtype).itemsize <= 1:\n                assert np.max(diff) <= 4\n            else:\n                assert np.max(diff) <= 0.01 * dynamic_range\n\n        # float\n        float_dts = [np.float16, np.float32, np.float64]\n        values = [5000, 1000*1000, 1000*1000*1000]\n        for dtype, value in zip(float_dts, values):\n            image = np.zeros((5, 5), dtype=dtype)\n            image[2, 2] = value\n            image_aug = aug.augment_image(image)\n            expected = (mask * value).astype(dtype)\n            diff = np.abs(image_aug.astype(np.float64)\n                          - expected.astype(np.float64))\n            assert image_aug.shape == mask.shape\n            assert image_aug.dtype.type == dtype\n            # accepts difference of 2.0, 4.0, 8.0, 16.0 (at 1, 2, 4, 8 bytes,\n            # i.e. 8, 16, 32, 64 bit)\n            max_diff = np.dtype(dtype).itemsize * 0.01 * np.float64(value)\n            assert np.max(diff) < max_diff\n\n    # float128 is the only unsupported dtype (excluding non-numerics and\n    # complex dtypes)\n    @unittest.skipIf(\n        not hasattr(np, \"float128\"),\n        \"Test can only be executed on systems that know numpy.float128\"\n    )\n    def test_failure_on_invalid_dtypes(self):\n        # assert failure on invalid dtypes\n        aug = iaa.GaussianBlur(sigma=1.0)\n        for dt in [np.float128]:\n            got_exception = False\n            try:\n                _ = aug.augment_image(np.zeros((1, 1), dtype=dt))\n            except Exception as exc:\n                assert \"forbidden dtype\" in str(exc)\n                got_exception = True\n            assert got_exception\n\n    def test_pickleable(self):\n        aug = iaa.GaussianBlur((0.1, 3.0), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\nclass TestAverageBlur(unittest.TestCase):\n    def __init__(self, *args, **kwargs):\n        super(TestAverageBlur, self).__init__(*args, **kwargs)\n\n        base_img = np.zeros((11, 11, 1), dtype=np.uint8)\n        base_img[5, 5, 0] = 200\n        base_img[4, 5, 0] = 100\n        base_img[6, 5, 0] = 100\n        base_img[5, 4, 0] = 100\n        base_img[5, 6, 0] = 100\n\n        blur3x3 = [\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 11, 11, 11, 0, 0, 0, 0],\n            [0, 0, 0, 11, 44, 56, 44, 11, 0, 0, 0],\n            [0, 0, 0, 11, 56, 67, 56, 11, 0, 0, 0],\n            [0, 0, 0, 11, 44, 56, 44, 11, 0, 0, 0],\n            [0, 0, 0, 0, 11, 11, 11, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n        ]\n        blur3x3 = np.array(blur3x3, dtype=np.uint8)[..., np.newaxis]\n\n        blur4x4 = [\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0],\n            [0, 0, 0, 6, 25, 31, 31, 25, 6, 0, 0],\n            [0, 0, 0, 6, 31, 38, 38, 31, 6, 0, 0],\n            [0, 0, 0, 6, 31, 38, 38, 31, 6, 0, 0],\n            [0, 0, 0, 6, 25, 31, 31, 25, 6, 0, 0],\n            [0, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n        ]\n        blur4x4 = np.array(blur4x4, dtype=np.uint8)[..., np.newaxis]\n\n        blur5x5 = [\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0],\n            [0, 0, 4, 16, 20, 20, 20, 16, 4, 0, 0],\n            [0, 0, 4, 20, 24, 24, 24, 20, 4, 0, 0],\n            [0, 0, 4, 20, 24, 24, 24, 20, 4, 0, 0],\n            [0, 0, 4, 20, 24, 24, 24, 20, 4, 0, 0],\n            [0, 0, 4, 16, 20, 20, 20, 16, 4, 0, 0],\n            [0, 0, 0, 4, 4, 4, 4, 4, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n        ]\n        blur5x5 = np.array(blur5x5, dtype=np.uint8)[..., np.newaxis]\n\n        self.base_img = base_img\n        self.blur3x3 = blur3x3\n        self.blur4x4 = blur4x4\n        self.blur5x5 = blur5x5\n\n    def setUp(self):\n        reseed()\n\n    def test_kernel_size_0(self):\n        # no blur, shouldnt change anything\n        aug = iaa.AverageBlur(k=0)\n        observed = aug.augment_image(self.base_img)\n        assert np.array_equal(observed, self.base_img)\n\n    def test_kernel_size_3(self):\n        # k=3\n        aug = iaa.AverageBlur(k=3)\n        observed = aug.augment_image(self.base_img)\n        assert np.array_equal(observed, self.blur3x3)\n\n    def test_kernel_size_5(self):\n        # k=5\n        aug = iaa.AverageBlur(k=5)\n        observed = aug.augment_image(self.base_img)\n        assert np.array_equal(observed, self.blur5x5)\n\n    def test_kernel_size_is_tuple(self):\n        # k as (3, 4)\n        aug = iaa.AverageBlur(k=(3, 4))\n        nb_iterations = 100\n        nb_seen = [0, 0]\n        for i in sm.xrange(nb_iterations):\n            observed = aug.augment_image(self.base_img)\n            if np.array_equal(observed, self.blur3x3):\n                nb_seen[0] += 1\n            elif np.array_equal(observed, self.blur4x4):\n                nb_seen[1] += 1\n            else:\n                raise Exception(\"Unexpected result in AverageBlur@1\")\n        p_seen = [v/nb_iterations for v in nb_seen]\n        assert 0.4 <= p_seen[0] <= 0.6\n        assert 0.4 <= p_seen[1] <= 0.6\n\n    def test_kernel_size_is_tuple_with_wider_range(self):\n        # k as (3, 5)\n        aug = iaa.AverageBlur(k=(3, 5))\n        nb_iterations = 200\n        nb_seen = [0, 0, 0]\n        for i in sm.xrange(nb_iterations):\n            observed = aug.augment_image(self.base_img)\n            if np.array_equal(observed, self.blur3x3):\n                nb_seen[0] += 1\n            elif np.array_equal(observed, self.blur4x4):\n                nb_seen[1] += 1\n            elif np.array_equal(observed, self.blur5x5):\n                nb_seen[2] += 1\n            else:\n                raise Exception(\"Unexpected result in AverageBlur@2\")\n        p_seen = [v/nb_iterations for v in nb_seen]\n        assert 0.23 <= p_seen[0] <= 0.43\n        assert 0.23 <= p_seen[1] <= 0.43\n        assert 0.23 <= p_seen[2] <= 0.43\n\n    def test_kernel_size_is_stochastic_parameter(self):\n        # k as stochastic parameter\n        aug = iaa.AverageBlur(k=iap.Choice([3, 5]))\n        nb_iterations = 100\n        nb_seen = [0, 0]\n        for i in sm.xrange(nb_iterations):\n            observed = aug.augment_image(self.base_img)\n            if np.array_equal(observed, self.blur3x3):\n                nb_seen[0] += 1\n            elif np.array_equal(observed, self.blur5x5):\n                nb_seen[1] += 1\n            else:\n                raise Exception(\"Unexpected result in AverageBlur@3\")\n        p_seen = [v/nb_iterations for v in nb_seen]\n        assert 0.4 <= p_seen[0] <= 0.6\n        assert 0.4 <= p_seen[1] <= 0.6\n\n    def test_kernel_size_is_tuple_of_tuples(self):\n        # k as ((3, 5), (3, 5))\n        aug = iaa.AverageBlur(k=((3, 5), (3, 5)))\n\n        possible = dict()\n        for kh in [3, 4, 5]:\n            for kw in [3, 4, 5]:\n                key = (kh, kw)\n                if kh == 0 or kw == 0:\n                    possible[key] = np.copy(self.base_img)\n                else:\n                    possible[key] = cv2.blur(\n                        self.base_img, (kh, kw))[..., np.newaxis]\n\n        nb_iterations = 250\n        nb_seen = dict([(key, 0) for key, val in possible.items()])\n        for i in sm.xrange(nb_iterations):\n            observed = aug.augment_image(self.base_img)\n            for key, img_aug in possible.items():\n                if np.array_equal(observed, img_aug):\n                    nb_seen[key] += 1\n        # dont check sum here, because 0xX and Xx0 are all the same, i.e. much\n        # higher sum than nb_iterations\n        assert np.all([v > 0 for v in nb_seen.values()])\n\n    def test_more_than_four_channels(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.AverageBlur(k=3)(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.AverageBlur(k=3)(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_keypoints_dont_change(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        kpsoi = [ia.KeypointsOnImage(kps, shape=(11, 11, 1))]\n\n        aug = iaa.AverageBlur(k=3)\n        aug_det = aug.to_deterministic()\n        observed = aug.augment_keypoints(kpsoi)\n        expected = kpsoi\n        assert keypoints_equal(observed, expected)\n\n        observed = aug_det.augment_keypoints(kpsoi)\n        expected = kpsoi\n        assert keypoints_equal(observed, expected)\n\n    def test_other_dtypes_k0(self):\n        aug = iaa.AverageBlur(k=0)\n\n        # bool\n        image = np.zeros((3, 3), dtype=bool)\n        image[1, 1] = True\n        image[2, 2] = True\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == image)\n\n        # uint, int\n        uint_dts = [np.uint8, np.uint16]\n        int_dts = [np.int8, np.int16]\n\n        for dtype in uint_dts + int_dts:\n            _min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = int(center_value + 0.4 * max_value)\n            image[2, 2] = int(center_value + 0.4 * max_value)\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.all(image_aug == image)\n\n        # float\n        float_dts = [np.float16, np.float32, np.float64]\n        values = [5000, 1000*1000, 1000*1000*1000]\n\n        for dtype, value in zip(float_dts, values):\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = value\n            image[2, 2] = value\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.type == dtype\n            assert np.allclose(image_aug, image)\n\n    def test_other_dtypes_k3_value_100(self):\n        # --\n        # blur of various dtypes at k=3\n        # and using an example value of 100 for int/uint/float and True for\n        # bool\n        # --\n        aug = iaa.AverageBlur(k=3)\n\n        # prototype mask\n        # we place values in a 3x3 grid at positions (row=1, col=1) and\n        # (row=2, col=2) (beginning with 0)\n        # AverageBlur uses cv2.blur(), which uses BORDER_REFLECT_101 as its\n        # default padding mode,\n        # see https://docs.opencv.org/3.1.0/d2/de8/group__core__array.html\n        # the matrix below shows the 3x3 grid and the padded row/col values\n        # around it\n        # [1, 0, 1, 0, 1]\n        # [0, 0, 0, 0, 0]\n        # [1, 0, 1, 0, 1]\n        # [0, 0, 0, 1, 0]\n        # [1, 0, 1, 0, 1]\n        mask = np.float64([\n            [4/9, 2/9, 4/9],\n            [2/9, 2/9, 3/9],\n            [4/9, 3/9, 5/9]\n        ])\n\n        # bool\n        image = np.zeros((3, 3), dtype=bool)\n        image[1, 1] = True\n        image[2, 2] = True\n        image_aug = aug.augment_image(image)\n        expected = mask > 0.5\n        assert image_aug.dtype.type == np.bool_\n        assert np.all(image_aug == expected)\n\n        # uint, int\n        uint_dts = [np.uint8, np.uint16]\n        int_dts = [np.int8, np.int16]\n\n        for dtype in uint_dts + int_dts:\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = 100\n            image[2, 2] = 100\n            image_aug = aug.augment_image(image)\n            # cv2.blur() applies rounding for int/uint dtypes\n            expected = np.round(mask * 100).astype(dtype)\n            diff = np.abs(image_aug.astype(np.int64)\n                          - expected.astype(np.int64))\n            assert image_aug.dtype.type == dtype\n            assert np.max(diff) <= 2\n\n        # float\n        float_dts = [np.float16, np.float32, np.float64]\n        for dtype in float_dts:\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = 100.0\n            image[2, 2] = 100.0\n            image_aug = aug.augment_image(image)\n            expected = (mask * 100.0).astype(dtype)\n            diff = np.abs(image_aug.astype(np.float64)\n                          - expected.astype(np.float64))\n            assert image_aug.dtype.type == dtype\n            assert np.max(diff) < 1.0\n\n    def test_other_dtypes_k3_dynamic_value(self):\n        # --\n        # blur of various dtypes at k=3\n        # and values being half-way between center and maximum for each\n        # dtype (bool is skipped as it doesnt make any sense here)\n        # The goal of this test is to verify that no major loss of resolution\n        # happens for large dtypes.\n        # --\n        aug = iaa.AverageBlur(k=3)\n\n        # prototype mask (see above)\n        mask = np.float64([\n            [4/9, 2/9, 4/9],\n            [2/9, 2/9, 3/9],\n            [4/9, 3/9, 5/9]\n        ])\n\n        # uint, int\n        uint_dts = [np.uint8, np.uint16]\n        int_dts = [np.int8, np.int16]\n\n        for dtype in uint_dts + int_dts:\n            _min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n            value = int(center_value + 0.4 * max_value)\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = value\n            image[2, 2] = value\n            image_aug = aug.augment_image(image)\n            expected = (mask * value).astype(dtype)\n            diff = np.abs(image_aug.astype(np.int64)\n                          - expected.astype(np.int64))\n            assert image_aug.dtype.type == dtype\n            # accepts difference of 4, 8, 16 (at 1, 2, 4 bytes, i.e. 8, 16,\n            # 32 bit)\n            assert np.max(diff) <= 2**(1 + np.dtype(dtype).itemsize)\n\n        # float\n        float_dts = [np.float16, np.float32, np.float64]\n        values = [5000, 1000*1000, 1000*1000*1000]\n\n        for dtype, value in zip(float_dts, values):\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = value\n            image[2, 2] = value\n            image_aug = aug.augment_image(image)\n            expected = (mask * value).astype(dtype)\n            diff = np.abs(image_aug.astype(np.float64)\n                          - expected.astype(np.float64))\n            assert image_aug.dtype.type == dtype\n            # accepts difference of 2.0, 4.0, 8.0, 16.0 (at 1, 2, 4, 8 bytes,\n            # i.e. 8, 16, 32, 64 bit)\n            assert np.max(diff) < 2**(1 + np.dtype(dtype).itemsize)\n\n    def test_failure_on_invalid_dtypes(self):\n        # assert failure on invalid dtypes\n        aug = iaa.AverageBlur(k=3)\n        for dt in [np.uint32, np.uint64, np.int32, np.int64]:\n            got_exception = False\n            try:\n                _ = aug.augment_image(np.zeros((1, 1), dtype=dt))\n            except Exception as exc:\n                assert \"forbidden dtype\" in str(exc)\n                got_exception = True\n            assert got_exception\n\n    def test_pickleable(self):\n        aug = iaa.AverageBlur((1, 11), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\nclass TestMedianBlur(unittest.TestCase):\n    def __init__(self, *args, **kwargs):\n        super(TestMedianBlur, self).__init__(*args, **kwargs)\n\n        base_img = np.zeros((11, 11, 1), dtype=np.uint8)\n        base_img[3:8, 3:8, 0] = 1\n        base_img[4:7, 4:7, 0] = 2\n        base_img[5:6, 5:6, 0] = 3\n\n        blur3x3 = np.zeros_like(base_img)\n        blur3x3[3:8, 3:8, 0] = 1\n        blur3x3[4:7, 4:7, 0] = 2\n        blur3x3[4, 4, 0] = 1\n        blur3x3[4, 6, 0] = 1\n        blur3x3[6, 4, 0] = 1\n        blur3x3[6, 6, 0] = 1\n        blur3x3[3, 3, 0] = 0\n        blur3x3[3, 7, 0] = 0\n        blur3x3[7, 3, 0] = 0\n        blur3x3[7, 7, 0] = 0\n\n        blur5x5 = np.copy(blur3x3)\n        blur5x5[4, 3, 0] = 0\n        blur5x5[3, 4, 0] = 0\n        blur5x5[6, 3, 0] = 0\n        blur5x5[7, 4, 0] = 0\n        blur5x5[4, 7, 0] = 0\n        blur5x5[3, 6, 0] = 0\n        blur5x5[6, 7, 0] = 0\n        blur5x5[7, 6, 0] = 0\n        blur5x5[blur5x5 > 1] = 1\n\n        self.base_img = base_img\n        self.blur3x3 = blur3x3\n        self.blur5x5 = blur5x5\n\n    def setUp(self):\n        reseed()\n\n    def test_k_is_1(self):\n        # no blur, shouldnt change anything\n        aug = iaa.MedianBlur(k=1)\n        observed = aug.augment_image(self.base_img)\n        assert np.array_equal(observed, self.base_img)\n\n    def test_k_is_3(self):\n        # k=3\n        aug = iaa.MedianBlur(k=3)\n        observed = aug.augment_image(self.base_img)\n        assert np.array_equal(observed, self.blur3x3)\n\n    def test_k_is_5(self):\n        # k=5\n        aug = iaa.MedianBlur(k=5)\n        observed = aug.augment_image(self.base_img)\n        assert np.array_equal(observed, self.blur5x5)\n\n    def test_k_is_tuple(self):\n        # k as (3, 5)\n        aug = iaa.MedianBlur(k=(3, 5))\n        seen = [False, False]\n        for i in sm.xrange(100):\n            observed = aug.augment_image(self.base_img)\n            if np.array_equal(observed, self.blur3x3):\n                seen[0] = True\n            elif np.array_equal(observed, self.blur5x5):\n                seen[1] = True\n            else:\n                raise Exception(\"Unexpected result in MedianBlur@1\")\n            if all(seen):\n                break\n        assert np.all(seen)\n\n    def test_k_is_stochastic_parameter(self):\n        # k as stochastic parameter\n        aug = iaa.MedianBlur(k=iap.Choice([3, 5]))\n        seen = [False, False]\n        for i in sm.xrange(100):\n            observed = aug.augment_image(self.base_img)\n            if np.array_equal(observed, self.blur3x3):\n                seen[0] += True\n            elif np.array_equal(observed, self.blur5x5):\n                seen[1] += True\n            else:\n                raise Exception(\"Unexpected result in MedianBlur@2\")\n            if all(seen):\n                break\n        assert np.all(seen)\n\n    def test_more_than_four_channels(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.MedianBlur(k=3)(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.MedianBlur(k=3)(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_keypoints_not_changed(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        kpsoi = [ia.KeypointsOnImage(kps, shape=(11, 11, 1))]\n\n        aug = iaa.MedianBlur(k=3)\n        aug_det = aug.to_deterministic()\n        observed = aug.augment_keypoints(kpsoi)\n        expected = kpsoi\n        assert keypoints_equal(observed, expected)\n\n        observed = aug_det.augment_keypoints(kpsoi)\n        expected = kpsoi\n        assert keypoints_equal(observed, expected)\n\n    def test_pickleable(self):\n        aug = iaa.MedianBlur((1, 11), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\n# TODO extend these tests\nclass TestBilateralBlur(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0, 3),\n            (0, 1, 3),\n            (1, 0, 3)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.BilateralBlur(3)(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_pickleable(self):\n        aug = iaa.BilateralBlur((1, 11), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\nclass TestMotionBlur(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_simple_parameters(self):\n        # simple scenario\n        aug = iaa.MotionBlur(k=3, angle=0, direction=0.0)\n        matrix_func = aug.matrix\n        matrices = [\n            matrix_func(\n                np.zeros((128, 128, 3), dtype=np.uint8),\n                3,\n                iarandom.RNG(i)\n            ) for i in range(10)\n        ]\n        expected = np.float32([\n            [0, 1.0/3, 0],\n            [0, 1.0/3, 0],\n            [0, 1.0/3, 0]\n        ])\n        for matrices_image in matrices:\n            for matrix_channel in matrices_image:\n                assert np.allclose(matrix_channel, expected)\n\n    def test_simple_parameters_angle_is_90(self):\n        # 90deg angle\n        aug = iaa.MotionBlur(k=3, angle=90, direction=0.0)\n        matrix_func = aug.matrix\n        matrices = [\n            matrix_func(\n                np.zeros((128, 128, 3), dtype=np.uint8),\n                3,\n                iarandom.RNG(i)\n            ) for i in range(10)\n        ]\n        expected = np.float32([\n            [0, 0, 0],\n            [1.0/3, 1.0/3, 1.0/3],\n            [0, 0, 0]\n        ])\n        for matrices_image in matrices:\n            for matrix_channel in matrices_image:\n                assert np.allclose(matrix_channel, expected)\n\n    def test_simple_parameters_angle_is_45(self):\n        # 45deg angle\n        aug = iaa.MotionBlur(k=3, angle=45, direction=0.0, order=0)\n        matrix_func = aug.matrix\n        matrices = [\n            matrix_func(\n                np.zeros((128, 128, 3), dtype=np.uint8),\n                3,\n                iarandom.RNG(i)\n            ) for i in range(10)\n        ]\n        expected = np.float32([\n            [0, 0, 1.0/3],\n            [0, 1.0/3, 0],\n            [1.0/3, 0, 0]\n        ])\n        for matrices_image in matrices:\n            for matrix_channel in matrices_image:\n                assert np.allclose(matrix_channel, expected)\n\n    def test_simple_parameters_angle_is_list(self):\n        # random angle\n        aug = iaa.MotionBlur(k=3, angle=[0, 90], direction=0.0)\n        matrix_func = aug.matrix\n        matrices = [\n            matrix_func(\n                np.zeros((128, 128, 3), dtype=np.uint8),\n                3,\n                iarandom.RNG(i)\n            ) for i in range(50)\n        ]\n        expected1 = np.float32([\n            [0, 1.0/3, 0],\n            [0, 1.0/3, 0],\n            [0, 1.0/3, 0]\n        ])\n        expected2 = np.float32([\n            [0, 0, 0],\n            [1.0/3, 1.0/3, 1.0/3],\n            [0, 0, 0],\n        ])\n        nb_seen = [0, 0]\n        for matrices_image in matrices:\n            assert np.allclose(matrices_image[0], matrices_image[1])\n            assert np.allclose(matrices_image[1], matrices_image[2])\n            for matrix_channel in matrices_image:\n                if np.allclose(matrix_channel, expected1):\n                    nb_seen[0] += 1\n                elif np.allclose(matrix_channel, expected2):\n                    nb_seen[1] += 1\n        assert nb_seen[0] > 0\n        assert nb_seen[1] > 0\n\n    def test_k_is_5_angle_90(self):\n        # 5x5\n        aug = iaa.MotionBlur(k=5, angle=90, direction=0.0)\n        matrix_func = aug.matrix\n        matrices = [\n            matrix_func(\n                np.zeros((128, 128, 3), dtype=np.uint8),\n                3,\n                iarandom.RNG(i)\n            ) for i in range(10)\n        ]\n        expected = np.float32([\n            [0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0],\n            [1.0/5, 1.0/5, 1.0/5, 1.0/5, 1.0/5],\n            [0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0],\n        ])\n        for matrices_image in matrices:\n            for matrix_channel in matrices_image:\n                assert np.allclose(matrix_channel, expected)\n\n    def test_k_is_list_angle_90(self):\n        # random k\n        aug = iaa.MotionBlur(k=[3, 5], angle=90, direction=0.0)\n        matrix_func = aug.matrix\n        matrices = [\n            matrix_func(\n                np.zeros((128, 128, 3), dtype=np.uint8),\n                3,\n                iarandom.RNG(i)\n            ) for i in range(50)\n        ]\n        expected1 = np.float32([\n            [0, 0, 0],\n            [1.0/3, 1.0/3, 1.0/3],\n            [0, 0, 0],\n        ])\n        expected2 = np.float32([\n            [0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0],\n            [1.0/5, 1.0/5, 1.0/5, 1.0/5, 1.0/5],\n            [0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0],\n        ])\n        nb_seen = [0, 0]\n        for matrices_image in matrices:\n            assert np.allclose(matrices_image[0], matrices_image[1])\n            assert np.allclose(matrices_image[1], matrices_image[2])\n            for matrix_channel in matrices_image:\n                if (matrix_channel.shape == expected1.shape\n                        and np.allclose(matrix_channel, expected1)):\n                    nb_seen[0] += 1\n                elif (matrix_channel.shape == expected2.shape\n                      and np.allclose(matrix_channel, expected2)):\n                    nb_seen[1] += 1\n        assert nb_seen[0] > 0\n        assert nb_seen[1] > 0\n\n    def test_failure_on_continuous_kernel_sizes(self):\n        # k with choice [a, b, c, ...] must error in case of non-discrete\n        # values\n        got_exception = False\n        try:\n            _ = iaa.MotionBlur(k=[3, 3.5, 4])\n        except Exception as exc:\n            assert \"to only contain integer\" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    # TODO extend this to test sampled kernel sizes\n    def test_k_is_tuple(self):\n        # no error in case of (a, b), checks for #215\n        aug = iaa.MotionBlur(k=(3, 7))\n        for _ in range(10):\n            _ = aug.augment_image(np.zeros((11, 11, 3), dtype=np.uint8))\n\n    def test_direction_is_1(self):\n        # direction 1.0\n        aug = iaa.MotionBlur(k=3, angle=0, direction=1.0)\n        matrix_func = aug.matrix\n        matrices = [\n            matrix_func(\n                np.zeros((128, 128, 3), dtype=np.uint8),\n                3,\n                iarandom.RNG(i)\n            ) for i in range(10)\n        ]\n        expected = np.float32([\n            [0, 1.0/1.5, 0],\n            [0, 0.5/1.5, 0],\n            [0, 0.0/1.5, 0]\n        ])\n        for matrices_image in matrices:\n            for matrix_channel in matrices_image:\n                assert np.allclose(matrix_channel, expected, rtol=0, atol=1e-2)\n\n    def test_direction_is_minus_1(self):\n        # direction -1.0\n        aug = iaa.MotionBlur(k=3, angle=0, direction=-1.0)\n        matrix_func = aug.matrix\n        matrices = [\n            matrix_func(\n                np.zeros((128, 128, 3), dtype=np.uint8),\n                3,\n                iarandom.RNG(i)\n            ) for i in range(10)\n        ]\n        expected = np.float32([\n            [0, 0.0/1.5, 0],\n            [0, 0.5/1.5, 0],\n            [0, 1.0/1.5, 0]\n        ])\n        for matrices_image in matrices:\n            for matrix_channel in matrices_image:\n                assert np.allclose(matrix_channel, expected, rtol=0, atol=1e-2)\n\n    def test_direction_is_list(self):\n        # random direction\n        aug = iaa.MotionBlur(k=3, angle=[0, 90], direction=[-1.0, 1.0])\n        matrix_func = aug.matrix\n        matrices = [\n            matrix_func(\n                np.zeros((128, 128, 3), dtype=np.uint8),\n                3,\n                iarandom.RNG(i)\n            ) for i in range(50)\n        ]\n        expected1 = np.float32([\n            [0, 1.0/1.5, 0],\n            [0, 0.5/1.5, 0],\n            [0, 0.0/1.5, 0]\n        ])\n        expected2 = np.float32([\n            [0, 0.0/1.5, 0],\n            [0, 0.5/1.5, 0],\n            [0, 1.0/1.5, 0]\n        ])\n        nb_seen = [0, 0]\n        for matrices_image in matrices:\n            assert np.allclose(matrices_image[0], matrices_image[1])\n            assert np.allclose(matrices_image[1], matrices_image[2])\n            for matrix_channel in matrices_image:\n                if np.allclose(matrix_channel, expected1, rtol=0, atol=1e-2):\n                    nb_seen[0] += 1\n                elif np.allclose(matrix_channel, expected2, rtol=0, atol=1e-2):\n                    nb_seen[1] += 1\n        assert nb_seen[0] > 0\n        assert nb_seen[1] > 0\n\n    def test_k_is_3_angle_is_90_verify_results(self):\n        # test of actual augmenter\n        img = np.zeros((7, 7, 3), dtype=np.uint8)\n        img[3-1:3+2, 3-1:3+2, :] = 255\n        aug = iaa.MotionBlur(k=3, angle=90, direction=0.0)\n        img_aug = aug.augment_image(img)\n        v1 = (255*(1/3))\n        v2 = (255*(1/3)) * 2\n        v3 = (255*(1/3)) * 3\n        expected = np.float32([\n            [0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0],\n            [0, v1, v2, v3, v2, v1, 0],\n            [0, v1, v2, v3, v2, v1, 0],\n            [0, v1, v2, v3, v2, v1, 0],\n            [0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 0, 0, 0, 0]\n        ]).astype(np.uint8)\n        expected = np.tile(expected[..., np.newaxis], (1, 1, 3))\n        assert np.allclose(img_aug, expected)\n\n    def test_pickleable(self):\n        aug = iaa.MotionBlur((3, 11), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\nclass TestMeanShiftBlur(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___defaults(self):\n        aug = iaa.MeanShiftBlur()\n        assert np.isclose(aug.spatial_window_radius.a.value, 5.0)\n        assert np.isclose(aug.spatial_window_radius.b.value, 40.0)\n        assert np.isclose(aug.color_window_radius.a.value, 5.0)\n        assert np.isclose(aug.color_window_radius.b.value, 40.0)\n\n    def test___init___custom(self):\n        aug = iaa.MeanShiftBlur(\n            spatial_radius=[1.0, 2.0, 3.0],\n            color_radius=iap.Deterministic(5)\n        )\n        assert np.allclose(aug.spatial_window_radius.a, [1.0, 2.0, 3.0])\n        assert aug.color_window_radius.value == 5\n\n    def test_draw_samples(self):\n        aug = iaa.MeanShiftBlur(\n            spatial_radius=[1.0, 2.0, 3.0],\n            color_radius=(1.0, 2.0)\n        )\n        batch = mock.Mock()\n        batch.nb_rows = 100\n\n        samples = aug._draw_samples(batch, iarandom.RNG(0))\n\n        assert np.all(\n            np.isclose(samples[0], 1.0)\n            | np.isclose(samples[0], 2.0)\n            | np.isclose(samples[0], 3.0)\n        )\n        assert np.all((1.0 <= samples[1]) | (samples[1] <= 2.0))\n\n    @mock.patch(\"imgaug.augmenters.blur.blur_mean_shift_\")\n    def test_mocked(self, mock_ms):\n        aug = iaa.MeanShiftBlur(\n            spatial_radius=1,\n            color_radius=2\n        )\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        mock_ms.return_value = image\n\n        _image_aug = aug(image=image)\n\n        kwargs = mock_ms.call_args_list[0][1]\n        assert mock_ms.call_count == 1\n        assert np.isclose(kwargs[\"spatial_window_radius\"], 1.0)\n        assert np.isclose(kwargs[\"color_window_radius\"], 2.0)\n\n    def test_batch_without_images(self):\n        aug = iaa.MeanShiftBlur()\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=0, y=1)], shape=(5, 5, 3))\n\n        kps_aug = aug(keypoints=kpsoi)\n\n        assert kps_aug.keypoints[0].x == 0\n        assert kps_aug.keypoints[0].y == 1\n\n    def test_get_parameters(self):\n        aug = iaa.MeanShiftBlur()\n        params = aug.get_parameters()\n        assert params[0] is aug.spatial_window_radius\n        assert params[1] is aug.color_window_radius\n\n    def test_pickleable(self):\n        aug = iaa.MeanShiftBlur(seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(40, 40, 3))\n"
  },
  {
    "path": "test/augmenters/test_collections.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\n\nfrom imgaug import augmenters as iaa\nfrom imgaug.testutils import reseed, runtest_pickleable_uint8_img\n\n\nclass TestRandAugment(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    # for some reason these mocks don't work with\n    # imgaug.augmenters.collections.(...)\n    @mock.patch(\"imgaug.augmenters.RandAugment._create_initial_augmenters_list\")\n    @mock.patch(\"imgaug.augmenters.RandAugment._create_main_augmenters_list\")\n    def test_n(self, mock_main, mock_initial):\n        mock_main.return_value = [iaa.Add(1), iaa.Add(2), iaa.Add(4)]\n        mock_initial.return_value = []\n\n        img = np.zeros((1, 1, 3), dtype=np.uint8)\n        expected = {\n            0: [0],\n            1: [1, 2, 4],\n            2: [1+1, 1+2, 1+4, 2+2, 2+4, 4+4]\n        }\n\n        for n in [0, 1, 2]:\n            with self.subTest(n=n):\n                aug = iaa.RandAugment(n=n)\n                img_aug = aug(image=img)\n                assert img_aug[0, 0, 0] in expected[n]\n\n    # for some reason these mocks don't work with\n    # imgaug.augmenters.collections.(...)\n    @mock.patch(\"imgaug.augmenters.RandAugment._create_initial_augmenters_list\")\n    @mock.patch(\"imgaug.augmenters.RandAugment._create_main_augmenters_list\")\n    def test_m(self, mock_main, mock_initial):\n        def _create_main_list(m, _cval):\n            return [iaa.Add(m)]\n\n        mock_main.side_effect = _create_main_list\n        mock_initial.return_value = []\n\n        img = np.zeros((1, 1, 3), dtype=np.uint8)\n\n        for m in [0, 1, 2]:\n            with self.subTest(m=m):\n                aug = iaa.RandAugment(m=m)\n                img_aug = aug(image=img)\n                assert img_aug[0, 0, 0] == m\n\n    def test_cval(self):\n        cval = 200\n        aug = iaa.RandAugment(n=1, m=30, cval=cval)\n        img = np.zeros((20, 20, 3), dtype=np.uint8)\n\n        x_cval = False\n        y_cval = False\n\n        # lots of iterations here, because only in some iterations an affine\n        # translation is actually applied\n        for _ in np.arange(500):\n            img_aug = aug(image=img)\n\n            x_cval = x_cval or np.all(img_aug[:, :1] == cval)\n            x_cval = x_cval or np.all(img_aug[:, -1:] == cval)\n            y_cval = y_cval or np.all(img_aug[:1, :] == cval)\n            y_cval = y_cval or np.all(img_aug[-1:, :] == cval)\n\n            if np.all([x_cval, y_cval]):\n                break\n\n        assert np.all([x_cval, y_cval])\n\n    def test_get_parameters(self):\n        aug = iaa.RandAugment(n=1, m=30, cval=100)\n        params = aug.get_parameters()\n        assert params[0] is aug[1].n\n        assert params[1] is aug._m\n        assert params[2] is aug._cval\n\n    def test_pickleable(self):\n        aug = iaa.RandAugment(m=(0, 10), n=(1, 2))\n        runtest_pickleable_uint8_img(aug, iterations=50)\n"
  },
  {
    "path": "test/augmenters/test_color.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport itertools\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\nimport copy as copylib\n\nimport numpy as np\nimport six.moves as sm\nimport cv2\n\nimport imgaug as ia\nimport imgaug.random as iarandom\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nimport imgaug.augmenters.meta as meta\nfrom imgaug.testutils import (reseed, runtest_pickleable_uint8_img,\n                              is_parameter_instance)\nimport imgaug.augmenters.color as colorlib\n\n\nclass Test_change_colorspace_(unittest.TestCase):\n    def test_non_uint8_fails(self):\n        image = np.arange(4*5*3).astype(np.uint8).reshape((4, 5, 3))\n        image = np.copy(image)  # reshape sets flag OWNDATA=False\n        image_float = image.astype(np.float32) / 255.0\n        with self.assertRaises(ValueError) as cm:\n            _ = iaa.change_colorspace_(image_float, iaa.CSPACE_BGR)\n        assert \"which is a forbidden dtype\" in str(cm.exception)\n\n    def test_unknown_to_colorspace_fails(self):\n        image = np.arange(4*5*3).astype(np.uint8).reshape((4, 5, 3))\n        image = np.copy(image)  # reshape sets flag OWNDATA=False\n        from_cspace = iaa.CSPACE_RGB\n        to_cspace = \"foo\"\n        with self.assertRaises(AssertionError) as cm:\n            _ = iaa.change_colorspace_(\n                image, to_colorspace=to_cspace, from_colorspace=from_cspace)\n        assert \"Expected `to_colorspace` to be one of\" in str(cm.exception)\n\n    def test_unknown_from_colorspace_fails(self):\n        image = np.arange(4*5*3).astype(np.uint8).reshape((4, 5, 3))\n        image = np.copy(image)  # reshape sets flag OWNDATA=False\n        from_cspace = \"foo\"\n        to_cspace = iaa.CSPACE_RGB\n        with self.assertRaises(AssertionError) as cm:\n            _ = iaa.change_colorspace_(\n                image, to_colorspace=to_cspace, from_colorspace=from_cspace)\n        assert \"Expected `from_colorspace` to be one of\" in str(cm.exception)\n\n    def test_change_to_same_colorspace_does_nothing(self):\n        image = np.arange(4*5*3).astype(np.uint8).reshape((4, 5, 3))\n        image = np.copy(image)  # reshape sets flag OWNDATA=False\n        from_cspace = iaa.CSPACE_RGB\n        to_cspace = iaa.CSPACE_RGB\n        image_out = iaa.change_colorspace_(\n            np.copy(image),\n            to_colorspace=to_cspace, from_colorspace=from_cspace)\n        assert np.array_equal(image_out, image)\n\n    def test_function_works_inplace(self):\n        image = np.arange(4*5*3).astype(np.uint8).reshape((4, 5, 3))\n        image = np.copy(image)  # reshape sets flag OWNDATA=False\n        image_orig = np.copy(image)\n        from_cspace = iaa.CSPACE_RGB\n        to_cspace = iaa.CSPACE_BGR\n        image_out = iaa.change_colorspace_(\n            image,\n            to_colorspace=to_cspace, from_colorspace=from_cspace)\n        assert image_out is image\n        assert np.array_equal(image_out, image)\n        assert not np.array_equal(image_out, image_orig)\n\n    def test_image_is_view(self):\n        image = np.arange(4*5*4).astype(np.uint8).reshape((4, 5, 4))\n        image = np.copy(image)  # reshape sets flag OWNDATA=False\n        image_copy = np.copy(image)\n        image_view = image[..., 0:3]\n        assert image_view.flags[\"OWNDATA\"] is False\n\n        from_cspace = iaa.CSPACE_RGB\n        to_cspace = iaa.CSPACE_BGR\n        image_out = iaa.change_colorspace_(\n            image_view,\n            to_colorspace=to_cspace, from_colorspace=from_cspace)\n\n        expected = self._generate_expected_image(\n            np.ascontiguousarray(image_copy[..., 0:3]),\n            from_cspace, to_cspace)\n        assert np.array_equal(image_out, expected)\n\n    def test_image_is_noncontiguous(self):\n        image = np.arange(4*5*3).astype(np.uint8).reshape((4, 5, 3))\n        image = np.copy(image)  # reshape sets flag OWNDATA=False\n        image_copy = np.copy(np.ascontiguousarray(np.fliplr(image)))\n        image_noncontiguous = np.fliplr(image)\n        assert image_noncontiguous.flags[\"C_CONTIGUOUS\"] is False\n\n        from_cspace = iaa.CSPACE_RGB\n        to_cspace = iaa.CSPACE_BGR\n        image_out = iaa.change_colorspace_(\n            image_noncontiguous,\n            to_colorspace=to_cspace, from_colorspace=from_cspace)\n\n        expected = self._generate_expected_image(image_copy, from_cspace,\n                                                 to_cspace)\n        assert np.array_equal(image_out, expected)\n\n    def test_cannot_transform_from_grayscale_to_another_cspace(self):\n        image = np.arange(4*5*3).astype(np.uint8).reshape((4, 5, 3))\n        image = np.copy(image)  # reshape sets flag OWNDATA=False\n        from_cspace = iaa.CSPACE_GRAY\n        to_cspace = iaa.CSPACE_RGB\n        with self.assertRaises(AssertionError) as cm:\n            _ = iaa.change_colorspace_(\n                np.copy(image),\n                from_colorspace=from_cspace, to_colorspace=to_cspace)\n        assert (\n            \"Cannot convert from grayscale to another colorspace\"\n            in str(cm.exception))\n\n    def test_image_without_channels_fails(self):\n        image = np.arange(4*5).astype(np.uint8).reshape((4, 5))\n        image = np.copy(image)  # reshape sets flag OWNDATA=False\n        from_cspace = iaa.CSPACE_RGB\n        to_cspace = iaa.CSPACE_BGR\n        with self.assertRaises(AssertionError) as cm:\n            _ = iaa.change_colorspace_(\n                np.copy(image),\n                from_colorspace=from_cspace, to_colorspace=to_cspace)\n        assert (\n            \"Expected image shape to be three-dimensional\"\n            in str(cm.exception))\n\n    def test_image_with_four_channels_fails(self):\n        image = np.arange(4*5*4).astype(np.uint8).reshape((4, 5, 4))\n        image = np.copy(image)  # reshape sets flag OWNDATA=False\n        from_cspace = iaa.CSPACE_RGB\n        to_cspace = iaa.CSPACE_BGR\n        with self.assertRaises(AssertionError) as cm:\n            _ = iaa.change_colorspace_(\n                np.copy(image),\n                from_colorspace=from_cspace, to_colorspace=to_cspace)\n        assert (\n            \"Expected number of channels to be three\"\n            in str(cm.exception))\n\n    def test_colorspace_combinations(self):\n        image = np.arange(4*5*3).astype(np.uint8).reshape((4, 5, 3))\n        image = np.copy(image)  # reshape sets flag OWNDATA=False\n        from_cspaces = iaa.CSPACE_ALL\n        to_cspaces = iaa.CSPACE_ALL\n        gen = itertools.product(from_cspaces, to_cspaces)\n        for from_cspace, to_cspace in gen:\n            if from_cspace == iaa.CSPACE_GRAY:\n                continue\n\n            with self.subTest(from_colorspace=from_cspace,\n                              to_colorspace=to_cspace):\n                image_out = iaa.change_colorspace_(np.copy(image), to_cspace,\n                                                   from_cspace)\n\n                if from_cspace == to_cspace:\n                    expected = np.copy(image)\n                else:\n                    expected = self._generate_expected_image(image, from_cspace,\n                                                             to_cspace)\n\n                if to_cspace == iaa.CSPACE_GRAY:\n                    expected = np.tile(expected[..., np.newaxis], (1, 1, 3))\n\n                assert np.array_equal(image_out, expected)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0, 3),\n            (0, 1, 3),\n            (1, 0, 3)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.change_colorspace_(\n                    np.copy(image), from_colorspace=\"RGB\", to_colorspace=\"BGR\")\n\n                assert image_aug.shape == image.shape\n\n    @classmethod\n    def _generate_expected_image(cls, image, from_colorspace, to_colorspace):\n        cv_vars = colorlib._CSPACE_OPENCV_CONV_VARS\n        if from_colorspace == iaa.CSPACE_RGB:\n            from2rgb = None\n        else:\n            from2rgb = cv_vars[(from_colorspace, iaa.CSPACE_RGB)]\n\n        if to_colorspace == iaa.CSPACE_RGB:\n            rgb2to = None\n        else:\n            rgb2to = cv_vars[(iaa.CSPACE_RGB, to_colorspace)]\n\n        image_rgb = image\n        if from2rgb is not None:\n            image_rgb = cv2.cvtColor(image, from2rgb)\n\n        image_out = image_rgb\n        if rgb2to is not None:\n            image_out = cv2.cvtColor(image_rgb, rgb2to)\n\n        return image_out\n\n\nclass Test_change_color_temperatures_(unittest.TestCase):\n    def test_single_image(self):\n        image = np.full((1, 1, 3), 255, dtype=np.uint8)\n\n        multipliers = [\n            (1000, [255, 56, 0]),\n            (1100, [255, 71, 0]),\n            (1200, [255, 83, 0]),\n            (1300, [255, 93, 0]),\n            (4300, [255, 215, 177]),\n            (4400, [255, 217, 182]),\n            (4500, [255, 219, 186]),\n            (4600, [255, 221, 190]),\n            (11100, [196, 214, 255]),\n            (11200, [195, 214, 255]),\n            (11300, [195, 214, 255]),\n            (11400, [194, 213, 255]),\n            (17200, [173, 200, 255]),\n            (17300, [173, 200, 255]),\n            (17400, [173, 200, 255]),\n            (21900, [166, 195, 255]),\n            (31300, [158, 190, 255]),\n            (39700, [155, 188, 255]),\n            (39800, [155, 188, 255]),\n            (39900, [155, 188, 255]),\n            (40000, [155, 188, 255])\n        ]\n\n        for kelvin, multiplier in multipliers:\n            with self.subTest(kelvin=kelvin):\n                image_temp = iaa.change_color_temperatures_(\n                    [np.copy(image)],\n                    kelvins=kelvin)[0]\n\n                expected = np.uint8(multiplier).reshape((1, 1, 3))\n                assert np.array_equal(image_temp, expected)\n\n    def test_three_images_as_list(self):\n        # separate tests for three and four images due to possible\n        # broadcasting errors, see issue #646\n        image = np.full((1, 1, 3), 255, dtype=np.uint8)\n\n        images_temp = iaa.change_color_temperatures_(\n            [np.copy(image), np.copy(image), np.copy(image)],\n            [11100, 11200, 11300]\n        )\n\n        expected = np.array([\n            [196, 214, 255],\n            [195, 214, 255],\n            [195, 214, 255]\n        ], dtype=np.uint8).reshape((3, 1, 1, 3))\n        assert isinstance(images_temp, list)\n        assert np.array_equal(images_temp[0], expected[0])\n        assert np.array_equal(images_temp[1], expected[1])\n        assert np.array_equal(images_temp[2], expected[2])\n\n    def test_four_images_as_list(self):\n        # separate tests for three and four images due to possible\n        # broadcasting errors, see issue #646\n        image = np.full((1, 1, 3), 255, dtype=np.uint8)\n        \n        images_temp = iaa.change_color_temperatures_(\n            [np.copy(image), np.copy(image), np.copy(image), np.copy(image)],\n            [11100, 11200, 11300, 11100]\n        )\n\n        expected = np.array([\n            [196, 214, 255],\n            [195, 214, 255],\n            [195, 214, 255],\n            [196, 214, 255]\n        ], dtype=np.uint8).reshape((4, 1, 1, 3))\n        assert isinstance(images_temp, list)\n        assert np.array_equal(images_temp[0], expected[0])\n        assert np.array_equal(images_temp[1], expected[1])\n        assert np.array_equal(images_temp[2], expected[2])\n\n    def test_three_images_as_array(self):\n        # separate tests for three and four images due to possible\n        # broadcasting errors, see issue #646\n        image = np.full((1, 1, 3), 255, dtype=np.uint8)\n\n        images_temp = iaa.change_color_temperatures_(\n            np.uint8([np.copy(image), np.copy(image), np.copy(image)]),\n            np.float32([11100, 11200, 11300])\n        )\n\n        expected = np.array([\n            [196, 214, 255],\n            [195, 214, 255],\n            [195, 214, 255]\n        ], dtype=np.uint8).reshape((3, 1, 1, 3))\n        assert ia.is_np_array(images_temp)\n        assert np.array_equal(images_temp, expected)\n\n    def test_four_images_as_array(self):\n        # separate tests for three and four images due to possible\n        # broadcasting errors, see issue #646\n        image = np.full((1, 1, 3), 255, dtype=np.uint8)\n\n        images_temp = iaa.change_color_temperatures_(\n            np.uint8([np.copy(image), np.copy(image), np.copy(image),\n                      np.copy(image)]),\n            np.float32([11100, 11200, 11300, 11100])\n        )\n\n        expected = np.array([\n            [196, 214, 255],\n            [195, 214, 255],\n            [195, 214, 255],\n            [196, 214, 255]\n        ], dtype=np.uint8).reshape((4, 1, 1, 3))\n        assert ia.is_np_array(images_temp)\n        assert np.array_equal(images_temp, expected)\n\n    def test_interpolation_of_kelvins(self):\n        # at 1000: [255, 56, 0]\n        # at 1100: [255, 71, 0]\n        at1050 = [255, 56 + (71-56)/2, 0]\n\n        image = np.full((1, 1, 3), 255, dtype=np.uint8)\n\n        image_temp = iaa.change_color_temperatures_(\n            [np.copy(image)],\n            kelvins=1050)[0]\n\n        expected = np.uint8(at1050).reshape((1, 1, 3))\n        diff = np.abs(image_temp.astype(np.int32) - expected.astype(np.int32))\n        assert np.all(diff <= 1)\n\n    def test_from_colorspace(self):\n        image_bgr = np.uint8([100, 255, 0]).reshape((1, 1, 3))\n\n        image_temp = iaa.change_color_temperatures_(\n            [np.copy(image_bgr)],\n            kelvins=1000,\n            from_colorspaces=iaa.CSPACE_BGR\n        )[0]\n\n        multiplier_rgb = np.float32(\n            [255/255.0, 56/255.0, 0/255.0]\n        ).reshape((1, 1, 3))\n        expected = (\n            image_bgr[:, :, ::-1].astype(np.float32)\n            * multiplier_rgb\n        ).astype(np.uint8)[:, :, ::-1]\n        diff = np.abs(image_temp.astype(np.int32) - expected.astype(np.int32))\n        assert np.all(diff <= 1)\n\n\nclass Test_change_color_temperature_(unittest.TestCase):\n    @mock.patch(\"imgaug.augmenters.color.change_color_temperatures_\")\n    def test_calls_batch_function(self, mock_ccts):\n        image = np.full((1, 1, 3), 255, dtype=np.uint8)\n        mock_ccts.return_value = [\"example\"]\n\n        image_temp = iaa.change_color_temperature(\n            image, 1000, from_colorspace=\"foo\")\n\n        assert image_temp == \"example\"\n        assert np.array_equal(mock_ccts.call_args_list[0][0][0],\n                              image[np.newaxis, ...])\n        assert mock_ccts.call_args_list[0][0][1] == [1000]\n        assert mock_ccts.call_args_list[0][1][\"from_colorspaces\"] == [\"foo\"]\n\n    def test_single_image(self):\n        image = np.full((1, 1, 3), 255, dtype=np.uint8)\n\n        image_temp = iaa.change_color_temperature(np.copy(image), 1000)\n\n        expected = np.uint8([255, 56, 0]).reshape((1, 1, 3))\n        assert np.array_equal(image_temp, expected)\n\n\nclass _BatchCapturingDummyAugmenter(iaa.Augmenter):\n    def __init__(self):\n        super(_BatchCapturingDummyAugmenter, self).__init__()\n        self.last_batch = None\n\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        self.last_batch = copylib.deepcopy(batch.deepcopy())\n        return batch\n\n    def get_parameters(self):\n        return []\n\n\nclass TestWithBrightnessChannels(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def valid_colorspaces(self):\n        return iaa.WithBrightnessChannels._VALID_COLORSPACES\n\n    def test___init___defaults(self):\n        aug = iaa.WithBrightnessChannels()\n        assert isinstance(aug.children, iaa.Augmenter)\n        assert len(aug.to_colorspace.a) == len(self.valid_colorspaces)\n        for cspace in self.valid_colorspaces:\n            assert cspace in aug.to_colorspace.a\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test___init___to_colorspace_is_all(self):\n        aug = iaa.WithBrightnessChannels(to_colorspace=ia.ALL)\n        assert isinstance(aug.children, iaa.Augmenter)\n        assert len(aug.to_colorspace.a) == len(self.valid_colorspaces)\n        for cspace in self.valid_colorspaces:\n            assert cspace in aug.to_colorspace.a\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test___init___to_colorspace_is_cspace(self):\n        aug = iaa.WithBrightnessChannels(to_colorspace=iaa.CSPACE_YUV)\n        assert isinstance(aug.children, iaa.Augmenter)\n        assert aug.to_colorspace.value == iaa.CSPACE_YUV\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test___init___to_colorspace_is_stochastic_parameter(self):\n        aug = iaa.WithBrightnessChannels(\n            to_colorspace=iap.Deterministic(iaa.CSPACE_YUV))\n        assert isinstance(aug.children, iaa.Augmenter)\n        assert aug.to_colorspace.value == iaa.CSPACE_YUV\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test_every_colorspace(self):\n        def _image_to_channel(image, cspace):\n            if cspace == iaa.CSPACE_YCrCb:\n                image_cvt = cv2.cvtColor(image, cv2.COLOR_RGB2YCR_CB)\n                return image_cvt[:, :, 0:0+1]\n            elif cspace == iaa.CSPACE_HSV:\n                image_cvt = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)\n                return image_cvt[:, :, 2:2+1]\n            elif cspace == iaa.CSPACE_HLS:\n                image_cvt = cv2.cvtColor(image, cv2.COLOR_RGB2HLS)\n                return image_cvt[:, :, 1:1+1]\n            elif cspace == iaa.CSPACE_Lab:\n                if hasattr(cv2, \"COLOR_RGB2Lab\"):\n                    image_cvt = cv2.cvtColor(image, cv2.COLOR_RGB2Lab)\n                else:\n                    image_cvt = cv2.cvtColor(image, cv2.COLOR_RGB2LAB)\n                return image_cvt[:, :, 0:0+1]\n            elif cspace == iaa.CSPACE_Luv:\n                if hasattr(cv2, \"COLOR_RGB2Luv\"):\n                    image_cvt = cv2.cvtColor(image, cv2.COLOR_RGB2Luv)\n                else:\n                    image_cvt = cv2.cvtColor(image, cv2.COLOR_RGB2LUV)\n                return image_cvt[:, :, 0:0+1]\n            else:\n                assert cspace == iaa.CSPACE_YUV\n                image_cvt = cv2.cvtColor(image, cv2.COLOR_RGB2YUV)\n                return image_cvt[:, :, 0:0+1]\n\n        # Max differences between input image and image after augmentation\n        # when no child augmenter is used (for the given example image below).\n        # For some colorspaces the conversion to input colorspace isn't\n        # perfect.\n        # Values were manually checked.\n        max_diff_expected = {\n            iaa.CSPACE_YCrCb: 1,\n            iaa.CSPACE_HSV: 0,\n            iaa.CSPACE_HLS: 0,\n            iaa.CSPACE_Lab: 2,\n            iaa.CSPACE_Luv: 4,\n            iaa.CSPACE_YUV: 1\n        }\n\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n\n        for cspace in self.valid_colorspaces:\n            with self.subTest(colorspace=cspace):\n                child = _BatchCapturingDummyAugmenter()\n                aug = iaa.WithBrightnessChannels(\n                    children=child,\n                    to_colorspace=cspace)\n\n                image_aug = aug(image=image)\n\n                expected = _image_to_channel(image, cspace)\n                diff = np.abs(\n                    image.astype(np.int32) - image_aug.astype(np.int32))\n                assert np.all(diff <= max_diff_expected[cspace])\n                assert np.array_equal(child.last_batch.images[0], expected)\n\n    def test_random_colorspace(self):\n        def _images_to_cspaces(images, choices):\n            result = np.full((len(images),), -1, dtype=np.int32)\n            for i, image_aug in enumerate(images):\n                for j, choice in enumerate(choices):\n                    if np.array_equal(image_aug, choice):\n                        result[i] = j\n                        break\n            assert np.all(result != -1)\n            return result\n\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n        expected_hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)[:, :, 2:2+1]\n        expected_hls = cv2.cvtColor(image, cv2.COLOR_RGB2HLS)[:, :, 1:1+1]\n\n        child = _BatchCapturingDummyAugmenter()\n        aug = iaa.WithBrightnessChannels(\n            children=child,\n            to_colorspace=[iaa.CSPACE_HSV, iaa.CSPACE_HLS])\n\n        images = [np.copy(image) for _ in sm.xrange(100)]\n\n        _ = aug(images=images)\n        images_aug1 = child.last_batch.images\n\n        _ = aug(images=images)\n        images_aug2 = child.last_batch.images\n\n        cspaces1 = _images_to_cspaces(images_aug1, [expected_hsv, expected_hls])\n        cspaces2 = _images_to_cspaces(images_aug2, [expected_hsv, expected_hls])\n\n        assert np.any(cspaces1 != cspaces2)\n        assert len(np.unique(cspaces1)) > 1\n        assert len(np.unique(cspaces2)) > 1\n\n    def test_from_colorspace_is_not_rgb(self):\n        child = _BatchCapturingDummyAugmenter()\n        aug = iaa.WithBrightnessChannels(\n            children=child,\n            to_colorspace=iaa.CSPACE_HSV,\n            from_colorspace=iaa.CSPACE_BGR)\n\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n        expected_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)[:, :, 2:2+1]\n\n        _ = aug(image=image)\n        observed = child.last_batch.images\n\n        assert np.array_equal(observed[0], expected_hsv)\n\n    def test_changes_from_child_propagate(self):\n        aug = iaa.WithBrightnessChannels(\n            children=iaa.Add(100),\n            to_colorspace=iaa.CSPACE_HSV)\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n\n        image_aug = aug(image=image)\n\n        assert not np.array_equal(image_aug, image)\n\n    def test_using_hooks_to_deactivate_propagation(self):\n        def _propagator(images, augmenter, parents, default):\n            return False if augmenter.name == \"foo\" else default\n\n        aug = iaa.WithBrightnessChannels(\n            children=iaa.Add(100),\n            to_colorspace=iaa.CSPACE_HSV,\n            name=\"foo\")\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n\n        image_aug = aug(image=image,\n                        hooks=ia.HooksImages(propagator=_propagator))\n\n        assert np.array_equal(image_aug, image)\n\n    def test_batch_without_images(self):\n        aug = iaa.WithBrightnessChannels(\n            children=iaa.Affine(translate_px={\"x\": 1}))\n\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=0, y=2)],\n                                    shape=(1, 1, 3))\n        kpsoi_aug = aug(keypoints=kpsoi)\n\n        assert np.isclose(kpsoi_aug.keypoints[0].x, 1.0)\n        assert np.isclose(kpsoi_aug.keypoints[0].y, 2.0)\n\n    def test_to_deterministic(self):\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n        aug = iaa.WithBrightnessChannels(iaa.Add((-100, 100)))\n        aug_det = aug.to_deterministic()\n\n        images = np.tile(image[np.newaxis, ...], (50, 1, 1, 1))\n\n        images_aug1 = aug_det(images=images)\n        images_aug2 = aug_det(images=images)\n\n        assert not np.array_equal(images_aug1, images)\n        assert np.array_equal(images_aug1, images_aug2)\n\n    def test_get_parameters(self):\n        aug = iaa.WithBrightnessChannels(to_colorspace=iaa.CSPACE_HSV)\n\n        params = aug.get_parameters()\n\n        assert params[0].value == iaa.CSPACE_HSV\n        assert params[1] == iaa.CSPACE_RGB\n\n    def test___str__(self):\n        child = iaa.Identity()\n        aug = iaa.WithBrightnessChannels(\n            child,\n            from_colorspace=iaa.CSPACE_RGB,\n            to_colorspace=iaa.CSPACE_HSV,\n            name=\"foo\")\n\n        aug_str = aug.__str__()\n\n        expected_child = iaa.Sequential([child], name=\"foo-then\")\n        expected = (\n            \"WithBrightnessChannels(\"\n            \"to_colorspace=%s, \"\n            \"from_colorspace=RGB, \"\n            \"name=foo, \"\n            \"children=%s, \"\n            \"deterministic=False)\" % (\n                str(aug.to_colorspace),\n                str(expected_child),\n            )\n        )\n        assert aug_str == expected\n\n    def test_get_children_lists(self):\n        child = iaa.Identity()\n        aug = iaa.WithBrightnessChannels([child])\n        children_lsts = aug.get_children_lists()\n        assert len(children_lsts) == 1\n        assert len(children_lsts[0]) == 1\n        assert children_lsts[0][0] is child\n\n    # TODO this test exists two times\n    def test_to_deterministic2(self):\n        child = iaa.Identity()\n        aug = iaa.WithBrightnessChannels([child])\n\n        aug_det = aug.to_deterministic()\n\n        assert aug_det.deterministic\n        assert aug_det.random_state is not aug.random_state\n        assert aug_det.children.deterministic\n        assert aug_det.children[0].deterministic\n\n    def test_pickleable(self):\n        aug = iaa.WithBrightnessChannels(iaa.Add((0, 50), seed=1), seed=2)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\n# MultiplyBrightness re-used MultiplyAndAddToBrightness, so we don't have\n# to test much here.\nclass TestMultiplyBrightness(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def valid_colorspaces(self):\n        return iaa.WithBrightnessChannels._VALID_COLORSPACES\n\n    def test___init___defaults(self):\n        aug = iaa.MultiplyBrightness()\n        assert isinstance(aug.children, iaa.Augmenter)\n        assert isinstance(aug.children[0], iaa.Multiply)\n        assert len(aug.to_colorspace.a) == len(self.valid_colorspaces)\n        for cspace in self.valid_colorspaces:\n            assert cspace in aug.to_colorspace.a\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test_single_image(self):\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n        aug = iaa.MultiplyBrightness(2.0)\n\n        image_aug = aug(image=image)\n\n        assert np.average(image_aug) > np.average(image)\n\n    def test_pickleable(self):\n        aug = iaa.MultiplyBrightness((0.5, 1.5), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\n# AddToBrightness re-used MultiplyAndAddToBrightness, so we don't have\n# to test much here.\nclass TestAddToBrightness(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def valid_colorspaces(self):\n        return iaa.WithBrightnessChannels._VALID_COLORSPACES\n\n    def test___init___defaults(self):\n        aug = iaa.AddToBrightness()\n        assert isinstance(aug.children, iaa.Augmenter)\n        assert isinstance(aug.children[1], iaa.Add)\n        assert len(aug.to_colorspace.a) == len(self.valid_colorspaces)\n        for cspace in self.valid_colorspaces:\n            assert cspace in aug.to_colorspace.a\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test_single_image(self):\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n        aug = iaa.AddToBrightness(100)\n\n        image_aug = aug(image=image)\n\n        assert np.average(image_aug) > np.average(image)\n\n    def test_pickleable(self):\n        aug = iaa.AddToBrightness((0, 50), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\nclass TestMultiplyAndAddToBrightness(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___defaults(self):\n        aug = iaa.MultiplyAndAddToBrightness()\n        assert aug.children.random_order is True\n        assert isinstance(aug.children[0], iaa.Multiply)\n        assert isinstance(aug.children[1], iaa.Add)\n        assert iaa.CSPACE_HSV in aug.to_colorspace.a\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test___init___add_is_zero(self):\n        aug = iaa.MultiplyAndAddToBrightness(add=0)\n        assert aug.children.random_order is True\n        assert isinstance(aug.children[0], iaa.Multiply)\n        assert isinstance(aug.children[1], iaa.Identity)\n        assert iaa.CSPACE_HSV in aug.to_colorspace.a\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test___init___mul_is_1(self):\n        aug = iaa.MultiplyAndAddToBrightness(mul=1.0)\n        assert aug.children.random_order is True\n        assert isinstance(aug.children[0], iaa.Identity)\n        assert isinstance(aug.children[1], iaa.Add)\n        assert iaa.CSPACE_HSV in aug.to_colorspace.a\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test_add_to_example_image(self):\n        aug = iaa.MultiplyAndAddToBrightness(mul=1.0, add=10,\n                                             to_colorspace=iaa.CSPACE_HSV,\n                                             random_order=False)\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n\n        image_aug = aug(image=image)\n\n        expected = cv2.cvtColor(image, cv2.COLOR_RGB2HSV).astype(np.float32)\n        expected[:, :, 2] += 10\n        expected = cv2.cvtColor(expected.astype(np.uint8), cv2.COLOR_HSV2RGB)\n        assert np.array_equal(image_aug, expected)\n\n    def test_multiply_example_image(self):\n        aug = iaa.MultiplyAndAddToBrightness(mul=1.2, add=0,\n                                             to_colorspace=iaa.CSPACE_HSV,\n                                             random_order=False)\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n\n        image_aug = aug(image=image)\n\n        expected = cv2.cvtColor(image, cv2.COLOR_RGB2HSV).astype(np.float32)\n        expected[:, :, 2] *= 1.2\n        expected = cv2.cvtColor(expected.astype(np.uint8), cv2.COLOR_HSV2RGB)\n        assert np.allclose(image_aug, expected, rtol=0, atol=1.01)\n\n    def test_multiply_and_add_example_image(self):\n        aug = iaa.MultiplyAndAddToBrightness(mul=1.2, add=10,\n                                             to_colorspace=iaa.CSPACE_HSV,\n                                             random_order=False)\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n\n        image_aug = aug(image=image)\n\n        expected = cv2.cvtColor(image, cv2.COLOR_RGB2HSV).astype(np.float32)\n        expected[:, :, 2] *= 1.2\n        expected[:, :, 2] += 10\n        expected = cv2.cvtColor(expected.astype(np.uint8), cv2.COLOR_HSV2RGB)\n        assert np.allclose(image_aug, expected, rtol=0, atol=1.01)\n\n    def test___str__(self):\n        params = [\n            (1.01, 1, iaa.Multiply(1.01), iaa.Add(1)),\n            (1.00, 1, iaa.Identity(), iaa.Add(1)),\n            (1.01, 0, iaa.Multiply(1.01), iaa.Identity()),\n            (1.00, 0, iaa.Identity(), iaa.Identity()),\n        ]\n\n        for mul, add, exp_mul, exp_add in params:\n            with self.subTest(mul=mul, add=add):\n                aug = iaa.MultiplyAndAddToBrightness(\n                    mul=mul,\n                    add=add,\n                    from_colorspace=iaa.CSPACE_RGB,\n                    to_colorspace=iaa.CSPACE_HSV,\n                    name=\"foo\")\n\n                aug_str = aug.__str__()\n\n                expected = (\n                    \"MultiplyAndAddToBrightness(\"\n                    \"mul=%s, \"\n                    \"add=%s, \"\n                    \"to_colorspace=%s, \"\n                    \"from_colorspace=RGB, \"\n                    \"random_order=True, \"\n                    \"name=foo, \"\n                    \"deterministic=False)\" % (\n                        exp_mul,\n                        exp_add,\n                        str(aug.to_colorspace)\n                    )\n                )\n                assert aug_str == expected\n\n    def test_pickleable(self):\n        aug = iaa.MultiplyAndAddToBrightness(mul=(0.5, 1.5), add=(0, 50),\n                                             seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\n# TODO add tests for prop hooks\nclass TestWithHueAndSaturation(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        child = iaa.Identity()\n        aug = iaa.WithHueAndSaturation(child, from_colorspace=\"BGR\")\n        assert isinstance(aug.children, list)\n        assert len(aug.children) == 1\n        assert aug.children[0] is child\n        assert aug.from_colorspace == \"BGR\"\n\n        aug = iaa.WithHueAndSaturation([child])\n        assert isinstance(aug.children, list)\n        assert len(aug.children) == 1\n        assert aug.children[0] is child\n        assert aug.from_colorspace == \"RGB\"\n\n    def test_augment_images(self):\n        class _DummyAugmenter(meta.Augmenter):\n            def __init__(self):\n                super(_DummyAugmenter, self).__init__()\n                self.call_count = 0\n\n            def _augment_batch_(self, batch, random_state, parents, hooks):\n                assert batch.images[0].dtype.name == \"int16\"\n                self.call_count += 1\n                return batch\n\n            def get_parameters(self):\n                return []\n\n        aug_dummy = _DummyAugmenter()\n        aug = iaa.WithHueAndSaturation(aug_dummy)\n\n        image = np.zeros((4, 4, 3), dtype=np.uint8)\n        image_aug = aug.augment_images([image])[0]\n        assert image_aug.dtype.name == \"uint8\"\n        assert np.array_equal(image_aug, image)\n        assert aug_dummy.call_count == 1\n\n    def test_augment_images__hue(self):\n        def augment_images(images, random_state, parents, hooks):\n            assert images[0].dtype.name == \"int16\"\n            images = np.copy(images)\n            images[..., 0] += 10\n            return images\n\n        aug = iaa.WithHueAndSaturation(iaa.Lambda(func_images=augment_images))\n\n        # example image\n        image = np.arange(0, 255).reshape((1, 255, 1)).astype(np.uint8)\n        image = np.tile(image, (1, 1, 3))\n        image[..., 0] += 0\n        image[..., 1] += 1\n        image[..., 2] += 2\n\n        # compute expected output\n        image_hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)\n        image_hsv = image_hsv.astype(np.int16)\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/180)*255).astype(np.int16)\n        image_hsv[..., 0] += 10\n        image_hsv[..., 0] = np.mod(image_hsv[..., 0], 255)\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/255)*180).astype(np.int16)\n        image_hsv = image_hsv.astype(np.uint8)\n        image_expected = cv2.cvtColor(image_hsv, cv2.COLOR_HSV2RGB)\n        assert not np.array_equal(image_expected, image)\n\n        # augment and verify\n        images_aug = aug.augment_images(np.stack([image, image], axis=0))\n        assert ia.is_np_array(images_aug)\n        for image_aug in images_aug:\n            assert image_aug.shape == (1, 255, 3)\n            assert np.array_equal(image_aug, image_expected)\n\n    def test_augment_images__saturation(self):\n        def augment_images(images, random_state, parents, hooks):\n            assert images[0].dtype.name == \"int16\"\n            images = np.copy(images)\n            images[..., 1] += 10\n            return images\n\n        aug = iaa.WithHueAndSaturation(iaa.Lambda(func_images=augment_images))\n\n        # example image\n        image = np.arange(0, 255).reshape((1, 255, 1)).astype(np.uint8)\n        image = np.tile(image, (1, 1, 3))\n        image[..., 0] += 0\n        image[..., 1] += 1\n        image[..., 2] += 2\n\n        # compute expected output\n        image_hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)\n        image_hsv = image_hsv.astype(np.int16)\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/180)*255).astype(np.int16)\n        image_hsv[..., 1] += 10\n        image_hsv[..., 1] = np.clip(image_hsv[..., 1], 0, 255)\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/255)*180).astype(np.int16)\n        image_hsv = image_hsv.astype(np.uint8)\n        image_expected = cv2.cvtColor(image_hsv, cv2.COLOR_HSV2RGB)\n        assert not np.array_equal(image_expected, image)\n\n        # augment and verify\n        images_aug = aug.augment_images(np.stack([image, image], axis=0))\n        assert ia.is_np_array(images_aug)\n        for image_aug in images_aug:\n            assert image_aug.shape == (1, 255, 3)\n            assert np.array_equal(image_aug, image_expected)\n\n    def test_augment_heatmaps(self):\n        from imgaug.augmentables.heatmaps import HeatmapsOnImage\n\n        class _DummyAugmenter(meta.Augmenter):\n            def __init__(self):\n                super(_DummyAugmenter, self).__init__()\n                self.call_count = 0\n\n            def _augment_batch_(self, batch, random_state, parents, hooks):\n                self.call_count += 1\n                return batch\n\n            def get_parameters(self):\n                return []\n\n        aug_dummy = _DummyAugmenter()\n        hm = np.ones((8, 12, 1), dtype=np.float32)\n        hmoi = HeatmapsOnImage(hm, shape=(16, 24, 3))\n\n        aug = iaa.WithHueAndSaturation(aug_dummy)\n        hmoi_aug = aug.augment_heatmaps(hmoi)\n        assert hmoi_aug.shape == (16, 24, 3)\n        assert hmoi_aug.arr_0to1.shape == (8, 12, 1)\n\n        assert aug_dummy.call_count == 1\n\n    def test_augment_keypoints(self):\n        from imgaug.augmentables.kps import KeypointsOnImage\n\n        class _DummyAugmenter(meta.Augmenter):\n            def __init__(self):\n                super(_DummyAugmenter, self).__init__()\n                self.call_count = 0\n\n            def _augment_batch_(self, batch, random_state, parents, hooks):\n                self.call_count += 1\n                return batch\n\n            def get_parameters(self):\n                return []\n\n        aug_dummy = _DummyAugmenter()\n        kpsoi = KeypointsOnImage.from_xy_array(np.float32([\n            [0, 0],\n            [5, 1]\n        ]), shape=(16, 24, 3))\n\n        aug = iaa.WithHueAndSaturation(aug_dummy)\n        kpsoi_aug = aug.augment_keypoints(kpsoi)\n        assert kpsoi_aug.shape == (16, 24, 3)\n        assert kpsoi.keypoints[0].x == 0\n        assert kpsoi.keypoints[0].y == 0\n        assert kpsoi.keypoints[1].x == 5\n        assert kpsoi.keypoints[1].y == 1\n\n        assert aug_dummy.call_count == 1\n\n    def test__to_deterministic(self):\n        aug = iaa.WithHueAndSaturation([iaa.Identity()], from_colorspace=\"BGR\")\n        aug_det = aug.to_deterministic()\n\n        assert not aug.deterministic  # ensure copy\n        assert not aug.children[0].deterministic\n\n        assert aug_det.deterministic\n        assert isinstance(aug_det.children[0], iaa.Identity)\n        assert aug_det.children[0].deterministic\n\n    def test_get_parameters(self):\n        aug = iaa.WithHueAndSaturation([iaa.Identity()], from_colorspace=\"BGR\")\n        assert aug.get_parameters()[0] == \"BGR\"\n\n    def test_get_children_lists(self):\n        child = iaa.Identity()\n        aug = iaa.WithHueAndSaturation(child)\n        children_lists = aug.get_children_lists()\n        assert len(children_lists) == 1\n        assert len(children_lists[0]) == 1\n        assert children_lists[0][0] is child\n\n        child = iaa.Identity()\n        aug = iaa.WithHueAndSaturation([child])\n        children_lists = aug.get_children_lists()\n        assert len(children_lists) == 1\n        assert len(children_lists[0]) == 1\n        assert children_lists[0][0] is child\n\n    def test___str__(self):\n        child = iaa.Sequential([iaa.Identity(name=\"foo\")])\n        aug = iaa.WithHueAndSaturation(child)\n        observed = aug.__str__()\n        expected = (\n            \"WithHueAndSaturation(\"\n            \"from_colorspace=RGB, \"\n            \"name=UnnamedWithHueAndSaturation, \"\n            \"children=[%s], \"\n            \"deterministic=False\"\n            \")\" % (child.__str__(),)\n        )\n        assert observed == expected\n\n    def test_pickleable(self):\n        aug = iaa.WithHueAndSaturation(iaa.Add((0, 50), seed=1), seed=2)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\nclass TestMultiplyHueAndSaturation(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_returns_correct_objects__mul(self):\n        aug = iaa.MultiplyHueAndSaturation(\n            (0.9, 1.1), per_channel=True)\n        assert isinstance(aug, iaa.WithHueAndSaturation)\n        assert isinstance(aug.children, iaa.Sequential)\n        assert len(aug.children) == 1\n        assert isinstance(aug.children[0], iaa.Multiply)\n        assert is_parameter_instance(aug.children[0].mul, iap.Uniform)\n        assert np.isclose(aug.children[0].mul.a.value, 0.9)\n        assert np.isclose(aug.children[0].mul.b.value, 1.1)\n        assert is_parameter_instance(aug.children[0].per_channel,\n                                     iap.Deterministic)\n        assert aug.children[0].per_channel.value == 1\n\n    def test_returns_correct_objects__mul_hue(self):\n        aug = iaa.MultiplyHueAndSaturation(mul_hue=(0.9, 1.1))\n        assert isinstance(aug, iaa.WithHueAndSaturation)\n        assert isinstance(aug.children, iaa.Sequential)\n        assert len(aug.children) == 1\n        assert isinstance(aug.children[0], iaa.WithChannels)\n        assert aug.children[0].channels == [0]\n        assert len(aug.children[0].children) == 1\n        assert isinstance(aug.children[0].children[0], iaa.Multiply)\n        assert is_parameter_instance(aug.children[0].children[0].mul,\n                                     iap.Uniform)\n        assert np.isclose(aug.children[0].children[0].mul.a.value, 0.9)\n        assert np.isclose(aug.children[0].children[0].mul.b.value, 1.1)\n\n    def test_returns_correct_objects__mul_saturation(self):\n        aug = iaa.MultiplyHueAndSaturation(mul_saturation=(0.9, 1.1))\n        assert isinstance(aug, iaa.WithHueAndSaturation)\n        assert isinstance(aug.children, iaa.Sequential)\n        assert len(aug.children) == 1\n        assert isinstance(aug.children[0], iaa.WithChannels)\n        assert aug.children[0].channels == [1]\n        assert len(aug.children[0].children) == 1\n        assert isinstance(aug.children[0].children[0], iaa.Multiply)\n        assert is_parameter_instance(aug.children[0].children[0].mul,\n                                     iap.Uniform)\n        assert np.isclose(aug.children[0].children[0].mul.a.value, 0.9)\n        assert np.isclose(aug.children[0].children[0].mul.b.value, 1.1)\n\n    def test_returns_correct_objects__mul_hue_and_mul_saturation(self):\n        aug = iaa.MultiplyHueAndSaturation(mul_hue=(0.9, 1.1),\n                                           mul_saturation=(0.8, 1.2))\n        assert isinstance(aug, iaa.WithHueAndSaturation)\n        assert isinstance(aug.children, iaa.Sequential)\n        assert len(aug.children) == 2\n\n        assert isinstance(aug.children[0], iaa.WithChannels)\n        assert aug.children[0].channels == [0]\n        assert len(aug.children[0].children) == 1\n        assert isinstance(aug.children[0].children[0], iaa.Multiply)\n        assert is_parameter_instance(aug.children[0].children[0].mul,\n                                     iap.Uniform)\n        assert np.isclose(aug.children[0].children[0].mul.a.value, 0.9)\n        assert np.isclose(aug.children[0].children[0].mul.b.value, 1.1)\n\n        assert isinstance(aug.children[1], iaa.WithChannels)\n        assert aug.children[1].channels == [1]\n        assert len(aug.children[0].children) == 1\n        assert isinstance(aug.children[1].children[0], iaa.Multiply)\n        assert is_parameter_instance(aug.children[1].children[0].mul,\n                                     iap.Uniform)\n        assert np.isclose(aug.children[1].children[0].mul.a.value, 0.8)\n        assert np.isclose(aug.children[1].children[0].mul.b.value, 1.2)\n\n    def test_augment_images__mul(self):\n        aug = iaa.MultiplyHueAndSaturation(1.5)\n\n        # example image\n        image = np.arange(0, 255).reshape((1, 255, 1)).astype(np.uint8)\n        image = np.tile(image, (1, 1, 3))\n        image[..., 0] += 0\n        image[..., 1] += 5\n        image[..., 2] += 10\n\n        # compute expected output\n        image_hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)\n        image_hsv = image_hsv.astype(np.int16)  # simulate WithHueAndSaturation\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/180)*255).astype(np.int16)\n        image_hsv = image_hsv.astype(np.float32)  # simulate Multiply\n\n        image_hsv[..., 0] *= 1.5\n        image_hsv[..., 1] *= 1.5\n        image_hsv = np.round(image_hsv).astype(np.int16)\n\n        image_hsv[..., 0] = np.mod(image_hsv[..., 0], 255)\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/255)*180).astype(np.int16)\n        image_hsv[..., 1] = np.clip(image_hsv[..., 1], 0, 255)\n\n        image_hsv = image_hsv.astype(np.uint8)\n        image_expected = cv2.cvtColor(image_hsv, cv2.COLOR_HSV2RGB)\n        assert not np.array_equal(image_expected, image)\n\n        # augment and verify\n        images_aug = aug.augment_images(np.stack([image, image], axis=0))\n        assert ia.is_np_array(images_aug)\n        for image_aug in images_aug:\n            assert image_aug.shape == (1, 255, 3)\n            diff = np.abs(image_aug.astype(np.int16) - image_expected)\n            assert np.all(diff <= 1)\n\n    def test_augment_images__mul_hue(self):\n        # this is almost identical to test_augment_images__mul\n        # only\n        #     aug = ...\n        # and\n        #     image_hsv[...] *= 1.2\n        # have been changed\n\n        aug = iaa.MultiplyHueAndSaturation(mul_hue=1.5)  # changed over __mul\n\n        # example image\n        image = np.arange(0, 255).reshape((1, 255, 1)).astype(np.uint8)\n        image = np.tile(image, (1, 1, 3))\n        image[..., 0] += 0\n        image[..., 1] += 5\n        image[..., 2] += 10\n\n        # compute expected output\n        image_hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)\n        image_hsv = image_hsv.astype(np.int16)  # simulate WithHueAndSaturation\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/180)*255).astype(np.int16)\n        image_hsv = image_hsv.astype(np.float32)  # simulate Multiply\n\n        image_hsv[..., 0] *= 1.5\n        image_hsv[..., 1] *= 1.0  # changed over __mul\n        image_hsv = np.round(image_hsv).astype(np.int16)\n\n        image_hsv[..., 0] = np.mod(image_hsv[..., 0], 255)\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/255)*180).astype(np.int16)\n        image_hsv[..., 1] = np.clip(image_hsv[..., 1], 0, 255)\n\n        image_hsv = image_hsv.astype(np.uint8)\n        image_expected = cv2.cvtColor(image_hsv, cv2.COLOR_HSV2RGB)\n        assert not np.array_equal(image_expected, image)\n\n        # augment and verify\n        images_aug = aug.augment_images(np.stack([image, image], axis=0))\n        assert ia.is_np_array(images_aug)\n        for image_aug in images_aug:\n            assert image_aug.shape == (1, 255, 3)\n            diff = np.abs(image_aug.astype(np.int16) - image_expected)\n            assert np.all(diff <= 1)\n\n    def test_augment_images__mul_saturation(self):\n        # this is almost identical to test_augment_images__mul\n        # only\n        #     aug = ...\n        # and\n        #     image_hsv[...] *= 1.2\n        # have been changed\n\n        aug = iaa.MultiplyHueAndSaturation(mul_saturation=1.5)  # changed\n\n        # example image\n        image = np.arange(0, 255).reshape((1, 255, 1)).astype(np.uint8)\n        image = np.tile(image, (1, 1, 3))\n        image[..., 0] += 0\n        image[..., 1] += 5\n        image[..., 2] += 10\n\n        # compute expected output\n        image_hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)\n        image_hsv = image_hsv.astype(np.int16)  # simulate WithHueAndSaturation\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/180)*255).astype(np.int16)\n        image_hsv = image_hsv.astype(np.float32)  # simulate Multiply\n\n        image_hsv[..., 0] *= 1.0  # changed over __mul\n        image_hsv[..., 1] *= 1.5\n        image_hsv = np.round(image_hsv).astype(np.int16)\n\n        image_hsv[..., 0] = np.mod(image_hsv[..., 0], 255)\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/255)*180).astype(np.int16)\n        image_hsv[..., 1] = np.clip(image_hsv[..., 1], 0, 255)\n\n        image_hsv = image_hsv.astype(np.uint8)\n        image_expected = cv2.cvtColor(image_hsv, cv2.COLOR_HSV2RGB)\n        assert not np.array_equal(image_expected, image)\n\n        # augment and verify\n        images_aug = aug.augment_images(np.stack([image, image], axis=0))\n        assert ia.is_np_array(images_aug)\n        for image_aug in images_aug:\n            assert image_aug.shape == (1, 255, 3)\n            diff = np.abs(image_aug.astype(np.int16) - image_expected)\n            assert np.all(diff <= 1)\n\n    def test_augment_images__mul_hue_and_mul_saturation(self):\n        # this is almost identical to test_augment_images__mul\n        # only\n        #     aug = ...\n        # and\n        #     image_hsv[...] *= 1.2\n        # have been changed\n\n        aug = iaa.MultiplyHueAndSaturation(mul_hue=1.5,\n                                           mul_saturation=1.6)  # changed\n\n        # example image\n        image = np.arange(0, 255).reshape((1, 255, 1)).astype(np.uint8)\n        image = np.tile(image, (1, 1, 3))\n        image[..., 0] += 0\n        image[..., 1] += 5\n        image[..., 2] += 10\n\n        # compute expected output\n        image_hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)\n        image_hsv = image_hsv.astype(np.int16)  # simulate WithHueAndSaturation\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/180)*255).astype(np.int16)\n        image_hsv = image_hsv.astype(np.float32)  # simulate Multiply\n\n        image_hsv[..., 0] *= 1.5\n        image_hsv[..., 1] *= 1.6  # changed over __mul\n        image_hsv = np.round(image_hsv).astype(np.int16)\n\n        image_hsv[..., 0] = np.mod(image_hsv[..., 0], 255)\n        image_hsv[..., 0] = (\n            (image_hsv[..., 0].astype(np.float32)/255)*180).astype(np.int16)\n        image_hsv[..., 1] = np.clip(image_hsv[..., 1], 0, 255)\n\n        image_hsv = image_hsv.astype(np.uint8)\n        image_expected = cv2.cvtColor(image_hsv, cv2.COLOR_HSV2RGB)\n        assert not np.array_equal(image_expected, image)\n\n        # augment and verify\n        images_aug = aug.augment_images(np.stack([image, image], axis=0))\n        assert ia.is_np_array(images_aug)\n        for image_aug in images_aug:\n            assert image_aug.shape == (1, 255, 3)\n            diff = np.abs(image_aug.astype(np.int16) - image_expected)\n            assert np.all(diff <= 1)\n\n    def test_augment_images__deterministic(self):\n        rs = iarandom.RNG(1)\n        images = rs.integers(0, 255, size=(32, 4, 4, 3), dtype=np.uint8)\n\n        for deterministic in [False, True]:\n            aug = iaa.MultiplyHueAndSaturation(\n                mul=(0.1, 5.0))\n            if deterministic:\n                aug = aug.to_deterministic()\n            images_aug1 = aug.augment_images(images)\n            images_aug2 = aug.augment_images(images)\n            equal = np.array_equal(images_aug1, images_aug2)\n            if deterministic:\n                assert equal\n            else:\n                assert not equal\n\n            aug = iaa.MultiplyHueAndSaturation(\n                mul_hue=(0.1, 5.0), mul_saturation=(0.1, 5.0))\n            if deterministic:\n                aug = aug.to_deterministic()\n            images_aug1 = aug.augment_images(images)\n            images_aug2 = aug.augment_images(images)\n            equal = np.array_equal(images_aug1, images_aug2)\n            if deterministic:\n                assert equal\n            else:\n                assert not equal\n\n    def test_pickleable(self):\n        aug = iaa.MultiplyHueAndSaturation(mul_hue=(0.5, 1.5),\n                                           mul_saturation=(0.5, 1.5),\n                                           seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\nclass TestMultiplyHue(unittest.TestCase):\n    def test_returns_correct_class(self):\n        # this test is practically identical to\n        # TestMultiplyToHueAndSaturation.test_returns_correct_objects__mul_hue\n        aug = iaa.MultiplyHue((0.9, 1.1))\n        assert isinstance(aug, iaa.WithHueAndSaturation)\n        assert isinstance(aug.children, iaa.Sequential)\n        assert len(aug.children) == 1\n        assert isinstance(aug.children[0], iaa.WithChannels)\n        assert aug.children[0].channels == [0]\n        assert len(aug.children[0].children) == 1\n        assert isinstance(aug.children[0].children[0], iaa.Multiply)\n        assert is_parameter_instance(aug.children[0].children[0].mul,\n                                     iap.Uniform)\n        assert np.isclose(aug.children[0].children[0].mul.a.value, 0.9)\n        assert np.isclose(aug.children[0].children[0].mul.b.value, 1.1)\n\n    def test_pickleable(self):\n        aug = iaa.MultiplyHue((0.5, 1.5), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(50, 50, 3))\n\n\nclass TestMultiplySaturation(unittest.TestCase):\n    def test_returns_correct_class(self):\n        # this test is practically identical to\n        # TestMultiplyToHueAndSaturation\n        #     .test_returns_correct_objects__mul_saturation\n        aug = iaa.MultiplySaturation((0.9, 1.1))\n        assert isinstance(aug, iaa.WithHueAndSaturation)\n        assert isinstance(aug.children, iaa.Sequential)\n        assert len(aug.children) == 1\n        assert isinstance(aug.children[0], iaa.WithChannels)\n        assert aug.children[0].channels == [1]\n        assert len(aug.children[0].children) == 1\n        assert isinstance(aug.children[0].children[0], iaa.Multiply)\n        assert is_parameter_instance(aug.children[0].children[0].mul,\n                                     iap.Uniform)\n        assert np.isclose(aug.children[0].children[0].mul.a.value, 0.9)\n        assert np.isclose(aug.children[0].children[0].mul.b.value, 1.1)\n\n    def test_pickleable(self):\n        aug = iaa.MultiplySaturation((0.5, 1.5), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(50, 50, 3))\n\n\nclass TestRemoveSaturation(unittest.TestCase):\n    @classmethod\n    def _compute_average_saturation(cls, image_rgb):\n        image_hsv = iaa.change_colorspace_(np.copy(image_rgb),\n                                           from_colorspace=iaa.CSPACE_RGB,\n                                           to_colorspace=iaa.CSPACE_HSV)\n        return np.average(image_hsv[:, :, 1])\n\n    def test___init___defaults(self):\n        aug = iaa.RemoveSaturation()\n        multiply = aug.children[0].children[0]\n        assert isinstance(multiply.mul, iap.Subtract)\n        assert np.isclose(multiply.mul.other_param.value, 1.0)\n        assert np.isclose(multiply.mul.val.value, 1.0)\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test___init___custom(self):\n        aug = iaa.RemoveSaturation(0.7, from_colorspace=iaa.CSPACE_HSV)\n        multiply = aug.children[0].children[0]\n        assert isinstance(multiply.mul, iap.Subtract)\n        assert np.isclose(multiply.mul.other_param.value, 1.0)\n        assert np.isclose(multiply.mul.val.value, 0.7)\n        assert aug.from_colorspace == iaa.CSPACE_HSV\n\n    def test_on_images(self):\n        image = np.mod(\n            np.arange(20*20*3),\n            255\n        ).astype(np.uint8).reshape((20, 20, 3))\n\n        # add 100 to the red channel here to make the image more saturated\n        image[..., 0] = np.clip((image[..., 0].astype(np.int32) + 100), 0, 255)\n\n        image_sat = self._compute_average_saturation(image)\n        aug = iaa.RemoveSaturation((0.2, 0.6))\n\n        images_aug = aug(images=[image] * 100)\n\n        saturations = []\n        for image_aug in images_aug:\n            sat = self._compute_average_saturation(image_aug)\n            saturations.append(sat)\n\n        assert len(set(np.int32(saturations))) > 10\n        # correct here to not use 1.0-x, as these are the saturations remaining\n        # after applying (1.0-x)*sat\n        # we add 0.02 here due to integer-float rounding effects\n        assert np.all(np.float32(saturations) <= (0.8 + 0.02) * image_sat)\n        assert np.any(np.float32(saturations) <= 0.5 * image_sat)\n\n    def test_pickleable(self):\n        aug = iaa.RemoveSaturation((0.1, 0.9), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(100, 100, 3))\n\n\nclass TestAddToHueAndSaturation(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @classmethod\n    def create_base_image(cls):\n        base_img = np.zeros((4, 2, 3), dtype=np.uint8)\n\n        base_img[0, :, 0] += 0\n        base_img[0, :, 1] += 1\n        base_img[0, :, 2] += 2\n\n        base_img[1, :, 0] += 20\n        base_img[1, :, 1] += 40\n        base_img[1, :, 2] += 60\n\n        base_img[2, :, 0] += 255\n        base_img[2, :, 1] += 128\n        base_img[2, :, 2] += 0\n\n        base_img[3, :, 0] += 255\n        base_img[3, :, 1] += 255\n        base_img[3, :, 2] += 255\n\n        return base_img\n\n    # interestingly, when using this RGB2HSV and HSV2RGB conversion from\n    # skimage, the results differ quite a bit from the cv2 ones\n    \"\"\"\n    def _add_hue_saturation(img, value):\n        img_hsv = color.rgb2hsv(img / 255.0)\n        img_hsv[..., 0:2] += (value / 255.0)\n        return color.hsv2rgb(img_hsv) * 255\n    \"\"\"\n\n    @classmethod\n    def _add_hue_saturation(cls, img, value=None, value_hue=None,\n                            value_saturation=None):\n        if value is not None:\n            assert value_hue is None and value_saturation is None\n        else:\n            assert value_hue is not None or value_saturation is not None\n\n        if value is not None:\n            value_hue = value\n            value_saturation = value\n        else:\n            value_hue = value_hue or 0\n            value_saturation = value_saturation or 0\n\n        img_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)\n        img_hsv = img_hsv.astype(np.int32)\n        img_hsv[..., 0] = np.mod(\n            img_hsv[..., 0] + int((value_hue/255.0) * (360/2)), 180)\n        img_hsv[..., 1] = np.clip(\n            img_hsv[..., 1] + value_saturation, 0, 255)\n        img_hsv = img_hsv.astype(np.uint8)\n        return cv2.cvtColor(img_hsv, cv2.COLOR_HSV2RGB)\n\n    def test___init__(self):\n        aug = iaa.AddToHueAndSaturation((-20, 20))\n        assert is_parameter_instance(aug.value, iap.DiscreteUniform)\n        assert aug.value.a.value == -20\n        assert aug.value.b.value == 20\n        assert aug.value_hue is None\n        assert aug.value_saturation is None\n        assert is_parameter_instance(aug.per_channel, iap.Deterministic)\n        assert aug.per_channel.value == 0\n\n    def test___init___value_none(self):\n        aug = iaa.AddToHueAndSaturation(value_hue=(-20, 20),\n                                        value_saturation=[0, 5, 10])\n        assert aug.value is None\n        assert is_parameter_instance(aug.value_hue, iap.DiscreteUniform)\n        assert is_parameter_instance(aug.value_saturation, iap.Choice)\n        assert aug.value_hue.a.value == -20\n        assert aug.value_hue.b.value == 20\n        assert aug.value_saturation.a == [0, 5, 10]\n        assert is_parameter_instance(aug.per_channel, iap.Deterministic)\n        assert aug.per_channel.value == 0\n\n    def test___init___per_channel(self):\n        aug = iaa.AddToHueAndSaturation(per_channel=0.5)\n        assert aug.value is None\n        assert aug.value_hue is not None\n        assert aug.value_saturation is not None\n        assert is_parameter_instance(aug.per_channel, iap.Binomial)\n        assert np.isclose(aug.per_channel.p.value, 0.5)\n\n    def test__generate_lut_table(self):\n        def _hue(v):\n            return np.mod(v, 180)\n\n        def _sat(v):\n            return np.clip(v, 0, 255)\n\n        tables = iaa.AddToHueAndSaturation._generate_lut_table()\n        table_hue, table_saturation = tables\n\n        intensity_values = [0, 1, 128, 254, 255]  # = pixel values\n        for iv in intensity_values:\n            with self.subTest(intensity=iv):\n                assert table_hue[0, iv] == _hue(iv-255)  # add value: -255\n                assert table_hue[1, iv] == _hue(iv-254)  # add value: -254\n                assert table_hue[254, iv] == _hue(iv-1)  # add value: -1\n                assert table_hue[255, iv] == _hue(iv-0)  # add value: 0\n                assert table_hue[256, iv] == _hue(iv+1)  # add value: 1\n                assert table_hue[509, iv] == _hue(iv+254)  # add value: 254\n                assert table_hue[510, iv] == _hue(iv+255)  # add value: 255\n\n                assert table_saturation[0, iv] == _sat(iv-255)  # input: -255\n                assert table_saturation[1, iv] == _sat(iv-254)  # input: -254\n                assert table_saturation[254, iv] == _sat(iv-1)  # input: -1\n                assert table_saturation[255, iv] == _sat(iv+0)  # input: 0\n                assert table_saturation[256, iv] == _sat(iv+1)  # input: 1\n                assert table_saturation[509, iv] == _sat(iv+254)  # input: 254\n                assert table_saturation[510, iv] == _sat(iv+255)  # input: 255\n\n    def test_augment_images_compare_backends(self):\n        base_img = self.create_base_image()\n        gen = itertools.product([False, True], [-255, -100, -1, 0, 1, 100, 255])\n        for per_channel, value in gen:\n            with self.subTest(value=value, per_channel=per_channel):\n                aug_cv2 = iaa.AddToHueAndSaturation(value,\n                                                    per_channel=per_channel)\n                aug_cv2.backend = \"cv2\"\n\n                aug_numpy = iaa.AddToHueAndSaturation(value,\n                                                      per_channel=per_channel)\n                aug_numpy.backend = \"numpy\"\n\n                img_observed1 = aug_cv2(image=base_img)\n                img_observed2 = aug_numpy(image=base_img)\n\n                assert np.array_equal(img_observed1, img_observed2)\n\n    def test_augment_images(self):\n        base_img = self.create_base_image()\n\n        gen = itertools.product([False, True], [\"cv2\", \"numpy\"])\n        for per_channel, backend in gen:\n            with self.subTest(per_channel=per_channel, backend=backend):\n                aug = iaa.AddToHueAndSaturation(0, per_channel=per_channel)\n                aug.backend = backend\n                observed = aug.augment_image(base_img)\n                expected = base_img\n                assert np.allclose(observed, expected)\n\n                aug = iaa.AddToHueAndSaturation(30, per_channel=per_channel)\n                aug.backend = backend\n                observed = aug.augment_image(base_img)\n                expected = self._add_hue_saturation(base_img, 30)\n                diff = np.abs(observed.astype(np.float32) - expected)\n                assert np.all(diff <= 1)\n\n                aug = iaa.AddToHueAndSaturation(255, per_channel=per_channel)\n                aug.backend = backend\n                observed = aug.augment_image(base_img)\n                expected = self._add_hue_saturation(base_img, 255)\n                diff = np.abs(observed.astype(np.float32) - expected)\n                assert np.all(diff <= 1)\n\n                aug = iaa.AddToHueAndSaturation(-255, per_channel=per_channel)\n                aug.backend = backend\n                observed = aug.augment_image(base_img)\n                expected = self._add_hue_saturation(base_img, -255)\n                diff = np.abs(observed.astype(np.float32) - expected)\n                assert np.all(diff <= 1)\n\n    def test_augment_images__different_hue_and_saturation__no_per_channel(self):\n        base_img = self.create_base_image()\n\n        class _DummyParam(iap.StochasticParameter):\n            def _draw_samples(self, size, random_state):\n                arr = np.float32([10, 20])\n                return np.tile(arr[np.newaxis, :], (size[0], 1))\n\n        aug = iaa.AddToHueAndSaturation(value=_DummyParam(), per_channel=False)\n        img_expected = self._add_hue_saturation(base_img, value=10)\n        img_observed = aug.augment_image(base_img)\n\n        assert np.array_equal(img_observed, img_expected)\n\n    def test_augment_images__different_hue_and_saturation__per_channel(self):\n        base_img = self.create_base_image()\n\n        class _DummyParam(iap.StochasticParameter):\n            def _draw_samples(self, size, random_state):\n                arr = np.float32([10, 20])\n                return np.tile(arr[np.newaxis, :], (size[0], 1))\n\n        aug = iaa.AddToHueAndSaturation(value=_DummyParam(), per_channel=True)\n        img_expected = self._add_hue_saturation(\n            base_img, value_hue=10, value_saturation=20)\n        img_observed = aug.augment_image(base_img)\n\n        assert np.array_equal(img_observed, img_expected)\n\n    def test_augment_images__different_hue_and_saturation__mixed_perchan(self):\n        base_img = self.create_base_image()\n\n        class _DummyParamValue(iap.StochasticParameter):\n            def _draw_samples(self, size, random_state):\n                arr = np.float32([10, 20])\n                return np.tile(arr[np.newaxis, :], (size[0], 1))\n\n        class _DummyParamPerChannel(iap.StochasticParameter):\n            def _draw_samples(self, size, random_state):\n                assert size == (3,)\n                return np.float32([1.0, 0.0, 1.0])\n\n        aug = iaa.AddToHueAndSaturation(\n            value=_DummyParamValue(), per_channel=_DummyParamPerChannel())\n\n        img_expected1 = self._add_hue_saturation(\n            base_img, value_hue=10, value_saturation=20)\n        img_expected2 = self._add_hue_saturation(\n            base_img, value_hue=10, value_saturation=10)\n        img_expected3 = self._add_hue_saturation(\n            base_img, value_hue=10, value_saturation=20)\n\n        img_observed1, img_observed2, img_observed3, = \\\n            aug.augment_images([base_img] * 3)\n\n        assert np.array_equal(img_observed1, img_expected1)\n        assert np.array_equal(img_observed2, img_expected2)\n        assert np.array_equal(img_observed3, img_expected3)\n\n    def test_augment_images__list_as_value(self):\n        base_img = self.create_base_image()\n\n        aug = iaa.AddToHueAndSaturation([0, 10, 20])\n        base_img = base_img[1:2, 0:1, :]\n        expected_imgs = [\n            iaa.AddToHueAndSaturation(0).augment_image(base_img),\n            iaa.AddToHueAndSaturation(10).augment_image(base_img),\n            iaa.AddToHueAndSaturation(20).augment_image(base_img)\n        ]\n\n        assert not np.array_equal(expected_imgs[0], expected_imgs[1])\n        assert not np.array_equal(expected_imgs[1], expected_imgs[2])\n        assert not np.array_equal(expected_imgs[0], expected_imgs[2])\n\n        nb_iterations = 300\n        seen = dict([(i, 0) for i, _ in enumerate(expected_imgs)])\n        for _ in sm.xrange(nb_iterations):\n            observed = aug.augment_image(base_img)\n            for i, expected_img in enumerate(expected_imgs):\n                if np.allclose(observed, expected_img):\n                    seen[i] += 1\n        assert np.sum(list(seen.values())) == nb_iterations\n        n_exp = nb_iterations / 3\n        n_exp_tol = nb_iterations * 0.1\n        assert all([n_exp - n_exp_tol < v < n_exp + n_exp_tol\n                    for v in seen.values()])\n\n    def test_augment_images__value_hue(self):\n        base_img = self.create_base_image()\n\n        for value_hue in [-255, -254, -128, -64, -10, -1,\n                          0, 1, 10, 64, 128, 254, 255]:\n            with self.subTest(value_hue=value_hue):\n                aug = iaa.AddToHueAndSaturation(value_hue=value_hue)\n                img_expected = self._add_hue_saturation(\n                    base_img, value_hue=value_hue)\n\n                img_observed = aug(image=base_img)\n\n                assert np.array_equal(img_observed, img_expected)\n\n    def test_augment_images__value_hue__multi_image_sampling(self):\n        base_img = self.create_base_image()\n\n        class _DummyParam(iap.StochasticParameter):\n            def _draw_samples(self, size, random_state):\n                return np.float32([10, 20, 30])\n\n        aug = iaa.AddToHueAndSaturation(value_hue=_DummyParam())\n\n        img_expected1 = self._add_hue_saturation(base_img, value_hue=10)\n        img_expected2 = self._add_hue_saturation(base_img, value_hue=20)\n        img_expected3 = self._add_hue_saturation(base_img, value_hue=30)\n\n        img_observed1, img_observed2, img_observed3 = \\\n            aug.augment_images([base_img] * 3)\n\n        assert np.array_equal(img_observed1, img_expected1)\n        assert np.array_equal(img_observed2, img_expected2)\n        assert np.array_equal(img_observed3, img_expected3)\n\n    def test_augment_images__value_saturation(self):\n        base_img = self.create_base_image()\n\n        for value_saturation in [-255, -254, -128, -64, -10,\n                                 0, 10, 64, 128, 254, 255]:\n            with self.subTest(value_hue=value_saturation):\n                aug = iaa.AddToHueAndSaturation(\n                    value_saturation=value_saturation)\n                img_expected = self._add_hue_saturation(\n                    base_img, value_saturation=value_saturation)\n\n                img_observed = aug(image=base_img)\n\n                assert np.array_equal(img_observed, img_expected)\n\n    def test_augment_images__value_saturation__multi_image_sampling(self):\n        base_img = self.create_base_image()\n\n        class _DummyParam(iap.StochasticParameter):\n            def _draw_samples(self, size, random_state):\n                return np.float32([10, 20, 30])\n\n        aug = iaa.AddToHueAndSaturation(value_saturation=_DummyParam())\n\n        img_expected1 = self._add_hue_saturation(base_img, value_saturation=10)\n        img_expected2 = self._add_hue_saturation(base_img, value_saturation=20)\n        img_expected3 = self._add_hue_saturation(base_img, value_saturation=30)\n\n        img_observed1, img_observed2, img_observed3 = \\\n            aug.augment_images([base_img] * 3)\n\n        assert np.array_equal(img_observed1, img_expected1)\n        assert np.array_equal(img_observed2, img_expected2)\n        assert np.array_equal(img_observed3, img_expected3)\n\n    def test_augment_images__value_hue_and_value_saturation(self):\n        base_img = self.create_base_image()\n\n        class _DummyParam(iap.StochasticParameter):\n            def _draw_samples(self, size, random_state):\n                return np.float32([10, 20, 30])\n\n        aug = iaa.AddToHueAndSaturation(value_hue=_DummyParam(),\n                                        value_saturation=_DummyParam()+40)\n\n        img_expected1 = self._add_hue_saturation(base_img, value_hue=10,\n                                                 value_saturation=40+10)\n        img_expected2 = self._add_hue_saturation(base_img, value_hue=20,\n                                                 value_saturation=40+20)\n        img_expected3 = self._add_hue_saturation(base_img, value_hue=30,\n                                                 value_saturation=40+30)\n\n        img_observed1, img_observed2, img_observed3 = \\\n            aug.augment_images([base_img] * 3)\n\n        assert np.array_equal(img_observed1, img_expected1)\n        assert np.array_equal(img_observed2, img_expected2)\n        assert np.array_equal(img_observed3, img_expected3)\n\n    def test_get_parameters(self):\n        aug = iaa.AddToHueAndSaturation((-20, 20), per_channel=0.5)\n        params = aug.get_parameters()\n        assert is_parameter_instance(params[0], iap.DiscreteUniform)\n        assert params[0].a.value == -20\n        assert params[0].b.value == 20\n        assert params[1] is None\n        assert params[2] is None\n        assert is_parameter_instance(params[3], iap.Binomial)\n        assert np.isclose(params[3].p.value, 0.5)\n\n    def test_get_parameters_value_hue_and_value_saturation(self):\n        aug = iaa.AddToHueAndSaturation(value_hue=(-20, 20),\n                                        value_saturation=5)\n        params = aug.get_parameters()\n        assert params[0] is None\n        assert is_parameter_instance(params[1], iap.DiscreteUniform)\n        assert params[1].a.value == -20\n        assert params[1].b.value == 20\n        assert is_parameter_instance(params[2], iap.Deterministic)\n        assert params[2].value == 5\n        assert is_parameter_instance(params[3], iap.Deterministic)\n        assert params[3].value == 0\n\n    def test_pickleable(self):\n        aug = iaa.AddToHueAndSaturation(value_hue=(-50, 50),\n                                        value_saturation=(-50, 50),\n                                        seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(50, 50, 3))\n\n\nclass TestAddToHue(unittest.TestCase):\n    def test_returns_correct_class(self):\n        aug = iaa.AddToHue((-20, 20))\n        assert isinstance(aug, iaa.AddToHueAndSaturation)\n        assert is_parameter_instance(aug.value_hue, iap.DiscreteUniform)\n        assert aug.value_hue.a.value == -20\n        assert aug.value_hue.b.value == 20\n\n    def test_pickleable(self):\n        aug = iaa.AddToHue((-50, 50), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(50, 50, 3))\n\n\nclass TestAddToSaturation(unittest.TestCase):\n    def test_returns_correct_class(self):\n        aug = iaa.AddToSaturation((-20, 20))\n        assert isinstance(aug, iaa.AddToHueAndSaturation)\n        assert is_parameter_instance(aug.value_saturation, iap.DiscreteUniform)\n        assert aug.value_saturation.a.value == -20\n        assert aug.value_saturation.b.value == 20\n\n    def test_pickleable(self):\n        aug = iaa.AddToSaturation((-50, 50), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(50, 50, 3))\n\n\nclass TestGrayscale(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def base_img(self):\n        base_img = np.zeros((4, 4, 3), dtype=np.uint8)\n        base_img[..., 0] += 10\n        base_img[..., 1] += 20\n        base_img[..., 2] += 30\n        return base_img\n\n    @classmethod\n    def _compute_luminosity(cls, r, g, b):\n        return 0.21 * r + 0.72 * g + 0.07 * b\n\n    def test_alpha_is_0(self):\n        aug = iaa.Grayscale(0.0)\n        observed = aug.augment_image(self.base_img)\n        expected = np.copy(self.base_img)\n        assert np.allclose(observed, expected)\n\n    def test_alpha_is_1(self):\n        aug = iaa.Grayscale(1.0)\n        observed = aug.augment_image(self.base_img)\n        luminosity = self._compute_luminosity(10, 20, 30)\n        expected = np.zeros_like(self.base_img) + luminosity\n        assert np.allclose(observed, expected.astype(np.uint8))\n\n    def test_alpha_is_050(self):\n        aug = iaa.Grayscale(0.5)\n        observed = aug.augment_image(self.base_img)\n        luminosity = self._compute_luminosity(10, 20, 30)\n        expected = 0.5 * self.base_img + 0.5 * luminosity\n        assert np.allclose(observed, expected.astype(np.uint8))\n\n    def test_alpha_is_tuple(self):\n        aug = iaa.Grayscale((0.0, 1.0))\n        base_img = np.uint8([255, 0, 0]).reshape((1, 1, 3))\n        base_img_float = base_img.astype(np.float64) / 255.0\n        base_img_gray = iaa.Grayscale(1.0)\\\n            .augment_image(base_img)\\\n            .astype(np.float64) / 255.0\n        distance_max = np.linalg.norm(base_img_gray.flatten()\n                                      - base_img_float.flatten())\n        nb_iterations = 1000\n        distances = []\n        for _ in sm.xrange(nb_iterations):\n            observed = aug.augment_image(base_img).astype(np.float64) / 255.0\n            distance = np.linalg.norm(\n                observed.flatten() - base_img_float.flatten()) / distance_max\n            distances.append(distance)\n\n        assert 0 - 1e-4 < min(distances) < 0.1\n        assert 0.4 < np.average(distances) < 0.6\n        assert 0.9 < max(distances) < 1.0 + 1e-4\n\n        nb_bins = 5\n        hist, _ = np.histogram(\n            distances, bins=nb_bins, range=(0.0, 1.0), density=False)\n        density_expected = 1.0/nb_bins\n        density_tolerance = 0.05\n        for nb_samples in hist:\n            density = nb_samples / nb_iterations\n            assert np.isclose(density, density_expected,\n                              rtol=0, atol=density_tolerance)\n\n    def test_pickleable(self):\n        aug = iaa.Grayscale((0.1, 0.9), seed=1)\n        runtest_pickleable_uint8_img(aug)\n\n\nclass TestChangeColorTemperature(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___defaults(self):\n        aug = iaa.ChangeColorTemperature()\n        assert is_parameter_instance(aug.kelvin, iap.Uniform)\n        assert aug.kelvin.a.value == 1000\n        assert aug.kelvin.b.value == 11000\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n\n    def test___init___kelvin_is_deterministic(self):\n        aug = iaa.ChangeColorTemperature(1000)\n        assert aug.kelvin.value == 1000\n\n    def test___init___kelvin_is_tuple(self):\n        aug = iaa.ChangeColorTemperature((2000, 3000))\n        assert is_parameter_instance(aug.kelvin, iap.Uniform)\n        assert aug.kelvin.a.value == 2000\n        assert aug.kelvin.b.value == 3000\n\n    def test___init___kelvin_is_list(self):\n        aug = iaa.ChangeColorTemperature([1000, 2000, 3000])\n        assert is_parameter_instance(aug.kelvin, iap.Choice)\n        assert aug.kelvin.a == [1000, 2000, 3000]\n\n    def test___init___kelvin_is_stochastic_param(self):\n        param = iap.Deterministic(5000)\n        aug = iaa.ChangeColorTemperature(param)\n        assert is_parameter_instance(aug.kelvin, iap.Deterministic)\n        assert aug.kelvin.value == 5000\n\n    @mock.patch(\"imgaug.augmenters.color.change_color_temperatures_\")\n    def test_mocked(self, mock_ccts):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        aug = iaa.ChangeColorTemperature((1000, 40000),\n                                         from_colorspace=iaa.CSPACE_HLS)\n\n        def _side_effect(images, kelvins, from_colorspaces):\n            return images\n\n        mock_ccts.side_effect = _side_effect\n\n        _image_aug = aug(images=[image, image])\n\n        assert mock_ccts.call_count == 1\n        assert np.array_equal(mock_ccts.call_args_list[0][0][0],\n                              [image, image])\n        assert not np.isclose(\n            mock_ccts.call_args_list[0][0][1][0],  # kelvin img 1\n            mock_ccts.call_args_list[0][0][1][1],  # kelvin img 2\n        )\n        assert (mock_ccts.call_args_list[0][1][\"from_colorspaces\"]\n                == iaa.CSPACE_HLS)\n\n    def test_single_image(self):\n        image = np.full((1, 1, 3), 255, dtype=np.uint8)\n        aug = iaa.ChangeColorTemperature(1000)\n\n        image_aug = aug(image=image)\n\n        expected = np.uint8([255, 56, 0]).reshape((1, 1, 3))\n        assert np.array_equal(image_aug, expected)\n\n    def test_get_parameters(self):\n        aug = iaa.ChangeColorTemperature(1111,\n                                         from_colorspace=iaa.CSPACE_HLS)\n        params = aug.get_parameters()\n        assert params[0].value == 1111\n        assert params[1] == iaa.CSPACE_HLS\n\n    def test_pickleable(self):\n        aug = iaa.ChangeColorTemperature(seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10)\n\n\n# Note that TestUniformColorQuantization inherits from this class,\n# which is why it contains the overwriteable @property functions\nclass TestKMeansColorQuantization(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def augmenter(self):\n        return iaa.KMeansColorQuantization\n\n    @property\n    def quantization_func_name(self):\n        return \"imgaug.augmenters.color.quantize_kmeans\"\n\n    def test___init___defaults(self):\n        aug = self.augmenter()\n        assert is_parameter_instance(aug.n_colors, iap.DiscreteUniform)\n        assert aug.n_colors.a.value == 2\n        assert aug.n_colors.b.value == 16\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n        assert isinstance(aug.to_colorspace, list)\n        assert aug.to_colorspace == [iaa.CSPACE_RGB,\n                                     iaa.CSPACE_Lab]\n        assert aug.max_size == 128\n        assert aug.interpolation == \"linear\"\n\n    def test___init___custom_parameters(self):\n        aug = self.augmenter(\n            n_colors=(5, 8),\n            from_colorspace=iaa.CSPACE_BGR,\n            to_colorspace=[iaa.CSPACE_HSV, iaa.CSPACE_Lab],\n            max_size=None,\n            interpolation=\"cubic\"\n        )\n        assert is_parameter_instance(aug.n_colors, iap.DiscreteUniform)\n        assert aug.n_colors.a.value == 5\n        assert aug.n_colors.b.value == 8\n        assert aug.from_colorspace == iaa.CSPACE_BGR\n        assert isinstance(aug.to_colorspace, list)\n        assert aug.to_colorspace == [iaa.CSPACE_HSV,\n                                     iaa.CSPACE_Lab]\n        assert aug.max_size is None\n        assert aug.interpolation == \"cubic\"\n\n    def test_n_colors_deterministic(self):\n        aug = self.augmenter(n_colors=5)\n        mock_quantize_func = mock.MagicMock(\n            return_value=np.zeros((4, 4, 3), dtype=np.uint8))\n\n        fname = self.quantization_func_name\n        with mock.patch(fname, mock_quantize_func):\n            _ = aug.augment_image(np.zeros((4, 4, 3), dtype=np.uint8))\n\n        # call 0, args, argument 1\n        assert mock_quantize_func.call_args_list[0][0][1] == 5\n\n    def test_n_colors_tuple(self):\n        aug = self.augmenter(n_colors=(2, 1000))\n        mock_quantize_func = mock.MagicMock(\n            return_value=np.zeros((4, 4, 3), dtype=np.uint8))\n\n        n_images = 10\n        fname = self.quantization_func_name\n        with mock.patch(fname, mock_quantize_func):\n            image = np.zeros((4, 4, 3), dtype=np.uint8)\n            _ = aug.augment_images([image] * n_images)\n\n        # call i, args, argument 1\n        n_colors = [mock_quantize_func.call_args_list[i][0][1]\n                    for i in sm.xrange(n_images)]\n        assert all([2 <= n_colors_i <= 1000 for n_colors_i in n_colors])\n        assert len(set(n_colors)) > 1\n\n    def test_to_colorspace(self):\n        image = np.arange(3*3*3, dtype=np.uint8).reshape((3, 3, 3))\n        aug = self.augmenter(to_colorspace=\"HSV\")\n        mock_quantize_func = mock.MagicMock(\n            return_value=np.zeros((4, 4, 3), dtype=np.uint8))\n\n        fname = self.quantization_func_name\n        with mock.patch(fname, mock_quantize_func):\n            _ = aug.augment_image(image)\n\n        # call 0, kwargs, argument 'to_colorspace'\n        expected = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)\n        assert np.array_equal(mock_quantize_func.call_args_list[0][0][0],\n                              expected)\n\n    def test_to_colorspace_is_none(self):\n        image = np.arange(3*3*3, dtype=np.uint8).reshape((3, 3, 3))\n        aug = self.augmenter(to_colorspace=None)\n        mock_quantize_func = mock.MagicMock(\n            return_value=np.zeros((4, 4, 3), dtype=np.uint8))\n\n        fname = self.quantization_func_name\n        with mock.patch(fname, mock_quantize_func):\n            _ = aug.augment_image(image)\n\n        # call 0, kwargs, argument 'to_colorspace'\n        assert np.array_equal(mock_quantize_func.call_args_list[0][0][0],\n                              image)\n\n    def test_from_colorspace(self):\n        def _noop(img):\n            return img\n\n        aug = self.augmenter(from_colorspace=\"BGR\")\n        mock_change_colorspace = mock.MagicMock()\n        mock_change_colorspace.return_value = mock_change_colorspace\n        mock_change_colorspace.augment_image.side_effect = _noop\n        mock_change_colorspace._draw_samples.return_value = (None, [\"foo\"])\n\n        fname = \"imgaug.augmenters.color.ChangeColorspace\"\n        with mock.patch(fname, mock_change_colorspace):\n            _ = aug.augment_image(np.zeros((4, 4, 3), dtype=np.uint8))\n\n        # call 0, kwargs, argument 'from_colorspace'\n        assert (\n            mock_change_colorspace.call_args_list[0][1][\"from_colorspace\"]\n            == \"BGR\")\n        # call 1, kwargs, argument 'from_colorspace' (inverse transform)\n        assert (\n            mock_change_colorspace.call_args_list[1][1][\"from_colorspace\"]\n            == \"foo\")\n\n    def test_max_size_is_none(self):\n        image = np.zeros((1000, 4, 3), dtype=np.uint8)\n        aug = iaa.KMeansColorQuantization(max_size=None)\n        mock_imresize = mock.MagicMock(return_value=image)\n\n        fname = \"imgaug.imresize_single_image\"\n        with mock.patch(fname, mock_imresize):\n            image_aug = aug.augment_image(image)\n            assert image_aug.shape == image.shape\n\n        assert mock_imresize.call_count == 0\n\n    def test_max_size_is_int_and_resize_necessary(self):\n        image = np.zeros((200, 100, 3), dtype=np.uint8)\n        aug = self.augmenter(max_size=100)\n\n        class _ImresizeSideEffect(object):\n            def __init__(self):\n                self.nth_call = 0\n\n            def __call__(self, *_args, **_kwargs):\n                if self.nth_call == 0:\n                    self.nth_call += 1\n                    return np.zeros((100, 50, 3), dtype=np.uint8)\n                else:\n                    return np.zeros((200, 100, 3), dtype=np.uint8)\n\n        mock_imresize = mock.Mock()\n        mock_imresize.side_effect = _ImresizeSideEffect()\n\n        fname = \"imgaug.imresize_single_image\"\n        with mock.patch(fname, mock_imresize):\n            _ = aug.augment_image(image)\n\n        # call 0, args, argument 1 (size)\n        # call 1, args, argument 1 (size)\n        assert mock_imresize.call_count == 2\n        assert mock_imresize.call_args_list[0][0][1] == (100, 50)\n        assert mock_imresize.call_args_list[1][0][1] == image.shape[0:2]\n\n    def test_max_size_is_int_and_resize_not_necessary(self):\n        image = np.zeros((99, 4, 3), dtype=np.uint8)\n        aug = self.augmenter(max_size=100)\n        mock_imresize = mock.MagicMock(return_value=image)\n\n        fname = \"imgaug.imresize_single_image\"\n        with mock.patch(fname, mock_imresize):\n            image_aug = aug.augment_image(image)\n            assert image_aug.shape == image.shape\n\n        assert mock_imresize.call_count == 0\n\n    def test_interpolation(self):\n        image = np.zeros((200, 100, 3), dtype=np.uint8)\n        aug = self.augmenter(max_size=100, interpolation=\"cubic\")\n\n        class _ImresizeSideEffect(object):\n            def __init__(self):\n                self.nth_call = 0\n\n            def __call__(self, *_args, **_kwargs):\n                if self.nth_call == 0:\n                    self.nth_call += 1\n                    return np.zeros((100, 50, 3), dtype=np.uint8)\n                else:\n                    return np.zeros((200, 100, 3), dtype=np.uint8)\n\n        mock_imresize = mock.Mock()\n        mock_imresize.side_effect = _ImresizeSideEffect()\n\n        fname = \"imgaug.imresize_single_image\"\n        with mock.patch(fname, mock_imresize):\n            _ = aug.augment_image(image)\n\n        assert mock_imresize.call_count == 2\n        # downscaling\n        # call 0, args, argument 1 (sizes)\n        # call 0, kwargs, argument \"interpolation\"\n        assert mock_imresize.call_args_list[0][0][1] == (100, 50)\n        assert mock_imresize.call_args_list[0][1][\"interpolation\"] == \"cubic\"\n\n        # upscaling\n        # call 1, args, argument 1 (sizes)\n        # call 1, kwargs, argument \"interpolation\"\n        assert mock_imresize.call_args_list[1][0][1] == image.shape[0:2]\n        assert mock_imresize.call_args_list[1][1][\"interpolation\"] == \"cubic\"\n\n    def test_images_with_1_channel_integrationtest(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.uint8([\n            [0, 0, 255, 255],\n            [0, 0, 255, 255],\n        ])\n\n        aug = self.augmenter(\n            n_colors=2,\n            from_colorspace=\"RGB\",\n            to_colorspace=\"RGB\",\n            max_size=None)\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_3_channels_integrationtest(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.uint8([\n            [0, 0, 255, 255],\n            [0, 0, 255, 255],\n        ])\n\n        image = np.tile(image[..., np.newaxis], (1, 1, 3))\n        expected = np.tile(expected[..., np.newaxis], (1, 1, 3))\n\n        aug = self.augmenter(\n            n_colors=2,\n            from_colorspace=\"RGB\",\n            to_colorspace=\"RGB\",\n            max_size=None)\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_4_channels_integrationtest(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.uint8([\n            [0, 0, 255, 255],\n            [0, 0, 255, 255],\n        ])\n\n        image = np.tile(image[..., np.newaxis], (1, 1, 4))\n        expected = np.tile(expected[..., np.newaxis], (1, 1, 3))\n\n        # alpha channel is expected to not be altered by quantization\n        expected = np.concatenate([expected, image[:, :, 3:4]], axis=-1)\n\n        aug = self.augmenter(\n            n_colors=2,\n            from_colorspace=\"RGB\",\n            to_colorspace=\"RGB\",\n            max_size=None)\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, expected)\n\n    def test_get_parameters(self):\n        aug = self.augmenter(\n            n_colors=(5, 8),\n            from_colorspace=iaa.CSPACE_BGR,\n            to_colorspace=[iaa.CSPACE_HSV, iaa.CSPACE_Lab],\n            max_size=None,\n            interpolation=\"cubic\"\n        )\n        params = aug.get_parameters()\n        assert is_parameter_instance(params[0], iap.DiscreteUniform)\n        assert params[0].a.value == 5\n        assert params[0].b.value == 8\n        assert params[1] == iaa.CSPACE_BGR\n        assert isinstance(params[2], list)\n        assert params[2] == [iaa.CSPACE_HSV,\n                             iaa.CSPACE_Lab]\n        assert params[3] is None\n        assert params[4] == \"cubic\"\n\n    def test_pickleable(self):\n        aug = self.augmenter(n_colors=(2, 16), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(100, 100, 3))\n\n\nclass Test_quantize_colors_kmeans(unittest.TestCase):\n    @mock.patch(\"imgaug.imgaug.warn_deprecated\")\n    def test_warns_deprecated(self, mock_warn):\n        arr = np.arange(1*1*3).astype(np.uint8).reshape((1, 1, 3))\n\n        _ = iaa.quantize_colors_kmeans(arr, 2)\n\n        assert mock_warn.call_count == 1\n\n    @mock.patch(\"imgaug.augmenters.color.quantize_kmeans\")\n    @mock.patch(\"imgaug.imgaug.warn_deprecated\")\n    def test_calls_quantize_kmeans(self, mock_warn, mock_qu):\n        arr = np.arange(1*1*3).astype(np.uint8).reshape((1, 1, 3))\n        mock_qu.return_value = \"foo\"\n\n        result = iaa.quantize_colors_kmeans(arr, 7)\n\n        mock_qu.assert_called_once_with(arr=arr, nb_clusters=7,\n                                        nb_max_iter=10, eps=1.0)\n        assert result == \"foo\"\n        assert mock_warn.call_count == 1\n\n\nclass Test_quantize_kmeans(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @classmethod\n    def _test_images_with_n_channels(cls, nb_channels):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.uint8([\n            [0, 0, 255, 255],\n            [0, 0, 255, 255],\n        ])\n\n        if nb_channels is not None:\n            image = np.tile(image[..., np.newaxis], (1, 1, nb_channels))\n            expected = np.tile(expected[..., np.newaxis], (1, 1, nb_channels))\n\n        observed = iaa.quantize_kmeans(image, 2)\n\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_no_channels(self):\n        self._test_images_with_n_channels(None)\n\n    def test_images_with_1_channel(self):\n        self._test_images_with_n_channels(1)\n\n    def test_images_with_3_channels(self):\n        self._test_images_with_n_channels(3)\n\n    def test_more_colors_than_pixels(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.copy(image)\n\n        observed = iaa.quantize_kmeans(image, 100)\n\n        assert np.array_equal(observed, expected)\n\n    def test_failure_if_n_colors_less_than_2(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n\n        got_exception = False\n        try:\n            _ = iaa.quantize_kmeans(image, 1)\n        except AssertionError as exc:\n            assert \"[2..256]\" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_quantization_is_deterministic(self):\n        rs = iarandom.RNG(1)\n        image = rs.integers(0, 255, (100, 100, 3)).astype(np.uint8)\n\n        # simulate multiple calls, each one of them should produce the\n        # same quantization\n        images_quantized = []\n        for _ in sm.xrange(20):\n            images_quantized.append(iaa.quantize_kmeans(image, 20))\n\n        for image_quantized in images_quantized[1:]:\n            assert np.array_equal(image_quantized, images_quantized[0])\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.quantize_kmeans(image, 2)\n\n                assert np.all(image_aug == 0)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.quantize_kmeans(image, 2)\n\n                assert np.all(image_aug == 0)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n\nclass TestUniformColorQuantization(TestKMeansColorQuantization):\n    def setUp(self):\n        reseed()\n\n    @property\n    def augmenter(self):\n        return iaa.UniformColorQuantization\n\n    @property\n    def quantization_func_name(self):\n        return \"imgaug.augmenters.color.quantize_uniform_\"\n\n    def test___init___defaults(self):\n        aug = self.augmenter()\n        assert is_parameter_instance(aug.n_colors, iap.DiscreteUniform)\n        assert aug.n_colors.a.value == 2\n        assert aug.n_colors.b.value == 16\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n        assert aug.to_colorspace is None\n        assert aug.max_size is None\n        assert aug.interpolation == \"linear\"\n\n    def test___init___custom_parameters(self):\n        aug = self.augmenter(\n            n_colors=(5, 8),\n            from_colorspace=iaa.CSPACE_BGR,\n            to_colorspace=[iaa.CSPACE_HSV, iaa.CSPACE_Lab],\n            max_size=128,\n            interpolation=\"cubic\"\n        )\n        assert is_parameter_instance(aug.n_colors, iap.DiscreteUniform)\n        assert aug.n_colors.a.value == 5\n        assert aug.n_colors.b.value == 8\n        assert aug.from_colorspace == iaa.CSPACE_BGR\n        assert isinstance(aug.to_colorspace, list)\n        assert aug.to_colorspace == [iaa.CSPACE_HSV,\n                                     iaa.CSPACE_Lab]\n        assert aug.max_size == 128\n        assert aug.interpolation == \"cubic\"\n\n    def test_images_with_1_channel_integrationtest(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.uint8([\n            [64, 64, 192, 192],\n            [64, 64, 192, 192],\n        ])\n\n        aug = self.augmenter(\n            n_colors=2,\n            from_colorspace=\"RGB\",\n            to_colorspace=\"RGB\",\n            max_size=None)\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_3_channels_integrationtest(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.uint8([\n            [64, 64, 192, 192],\n            [64, 64, 192, 192],\n        ])\n\n        image = np.tile(image[..., np.newaxis], (1, 1, 3))\n        expected = np.tile(expected[..., np.newaxis], (1, 1, 3))\n\n        aug = self.augmenter(\n            n_colors=2,\n            from_colorspace=\"RGB\",\n            to_colorspace=\"RGB\",\n            max_size=None)\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_4_channels_integrationtest(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.uint8([\n            [64, 64, 192, 192],\n            [64, 64, 192, 192],\n        ])\n\n        image = np.tile(image[..., np.newaxis], (1, 1, 4))\n        expected = np.tile(expected[..., np.newaxis], (1, 1, 3))\n\n        # alpha channel is expected to not be altered by quantization\n        expected = np.concatenate([expected, image[:, :, 3:4]], axis=-1)\n\n        aug = self.augmenter(\n            n_colors=2,\n            from_colorspace=\"RGB\",\n            to_colorspace=\"RGB\",\n            max_size=None)\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, expected)\n\n    def test_from_colorspace(self):\n        def _noop(img):\n            return img\n\n        # Actual to_colorspace doesn't matter here as it is overwritten\n        # via return_value. Important is just to set it to a non-None value\n        # so that a colorspace conversion actually happens.\n        aug = self.augmenter(from_colorspace=\"BGR\",\n                             to_colorspace=\"Lab\")\n        mock_change_colorspace = mock.MagicMock()\n        mock_change_colorspace.return_value = mock_change_colorspace\n        mock_change_colorspace.augment_image.side_effect = _noop\n        mock_change_colorspace._draw_samples.return_value = (None, [\"foo\"])\n\n        fname = \"imgaug.augmenters.color.ChangeColorspace\"\n        with mock.patch(fname, mock_change_colorspace):\n            _ = aug.augment_image(np.zeros((4, 4, 3), dtype=np.uint8))\n\n        # call 0, kwargs, argument 'from_colorspace'\n        assert (\n            mock_change_colorspace.call_args_list[0][1][\"from_colorspace\"]\n            == \"BGR\")\n        # call 1, kwargs, argument 'from_colorspace' (inverse transform)\n        assert (\n            mock_change_colorspace.call_args_list[1][1][\"from_colorspace\"]\n            == \"foo\")\n\n    def test_pickleable(self):\n        aug = self.augmenter(n_colors=(2, 32), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(100, 100, 3))\n\n\n# Not that many tests here as it is basically identical to e.g.\n# UniformColorQuantization\nclass TestUniformColorQuantizationToNBits(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def augmenter(self):\n        return iaa.UniformColorQuantizationToNBits\n\n    @property\n    def quantization_func_name(self):\n        return \"imgaug.augmenters.color.quantize_uniform_to_n_bits\"\n\n    def test___init___defaults(self):\n        aug = self.augmenter()\n        assert is_parameter_instance(aug.counts, iap.DiscreteUniform)\n        assert aug.counts.a.value == 1\n        assert aug.counts.b.value == 8\n        assert aug.from_colorspace == iaa.CSPACE_RGB\n        assert aug.to_colorspace is None\n        assert aug.max_size is None\n        assert aug.interpolation == \"linear\"\n\n    def test___init___custom_parameters(self):\n        aug = self.augmenter(\n            nb_bits=(5, 8),\n            from_colorspace=iaa.CSPACE_BGR,\n            to_colorspace=[iaa.CSPACE_HSV, iaa.CSPACE_Lab],\n            max_size=128,\n            interpolation=\"cubic\"\n        )\n        assert is_parameter_instance(aug.counts, iap.DiscreteUniform)\n        assert aug.counts.a.value == 5\n        assert aug.counts.b.value == 8\n        assert aug.from_colorspace == iaa.CSPACE_BGR\n        assert isinstance(aug.to_colorspace, list)\n        assert aug.to_colorspace == [iaa.CSPACE_HSV,\n                                     iaa.CSPACE_Lab]\n        assert aug.max_size == 128\n        assert aug.interpolation == \"cubic\"\n\n    def test_images_with_1_channel_integrationtest(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.uint8([\n            [0, 0, 128, 128],\n            [0, 0, 128, 128],\n        ])\n\n        aug = self.augmenter(\n            nb_bits=1,\n            from_colorspace=\"RGB\",\n            to_colorspace=\"RGB\",\n            max_size=None)\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_3_channels_integrationtest(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.uint8([\n            [0, 0, 128, 128],\n            [0, 0, 128, 128],\n        ])\n\n        image = np.tile(image[..., np.newaxis], (1, 1, 3))\n        expected = np.tile(expected[..., np.newaxis], (1, 1, 3))\n\n        aug = self.augmenter(\n            nb_bits=1,\n            from_colorspace=\"RGB\",\n            to_colorspace=\"RGB\",\n            max_size=None)\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, expected)\n\n    def test_pickleable(self):\n        aug = iaa.UniformColorQuantizationToNBits(seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(100, 100, 3))\n\n\nclass TestPosterize(TestUniformColorQuantizationToNBits):\n    @property\n    def augmenter(self):\n        return iaa.Posterize\n\n\nclass Test_quantize_colors_uniform(unittest.TestCase):\n    @mock.patch(\"imgaug.imgaug.warn_deprecated\")\n    def test_warns_deprecated(self, mock_warn):\n        arr = np.arange(1*1*3).astype(np.uint8).reshape((1, 1, 3))\n\n        _ = iaa.quantize_colors_uniform(arr, 2)\n\n        assert mock_warn.call_count == 1\n\n    @mock.patch(\"imgaug.augmenters.color.quantize_uniform\")\n    @mock.patch(\"imgaug.imgaug.warn_deprecated\")\n    def test_calls_quantize_uniform(self, mock_warn, mock_qu):\n        arr = np.arange(1*1*3).astype(np.uint8).reshape((1, 1, 3))\n        mock_qu.return_value = \"foo\"\n\n        result = iaa.quantize_colors_uniform(arr, 7)\n\n        mock_qu.assert_called_once_with(arr=arr, nb_bins=7)\n        assert result == \"foo\"\n        assert mock_warn.call_count == 1\n\n\nclass Test_quantize_uniform(unittest.TestCase):\n    @mock.patch(\"imgaug.augmenters.color.quantize_uniform_\")\n    def test_calls_inplace_function(self, mock_qu):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        mock_qu.return_value = \"foo\"\n\n        result = iaa.quantize_uniform(image, 3)\n\n        # image provided to in-place func must be copy with same content\n        assert mock_qu.call_args_list[0][0][0] is not image\n        assert np.array_equal(mock_qu.call_args_list[0][0][0], image)\n\n        assert mock_qu.call_args_list[0][1][\"nb_bins\"] == 3\n        assert result == \"foo\"\n\n\nclass Test_quantize_uniform_(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @classmethod\n    def _test_images_with_n_channels_2_colors(cls, nb_channels):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.uint8([\n            [64, 64, 192, 192],\n            [64, 64, 192, 192],\n        ])\n\n        if nb_channels is not None:\n            image = np.tile(image[..., np.newaxis], (1, 1, nb_channels))\n            expected = np.tile(expected[..., np.newaxis], (1, 1, nb_channels))\n\n        observed = iaa.quantize_uniform_(np.copy(image), 2)\n\n        assert np.array_equal(observed, expected)\n\n    @classmethod\n    def _test_images_with_n_channels_4_colors(cls, nb_channels):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n            [127, 128, 220, 220]\n        ])\n\n        q = 256/4\n        c1 = np.floor(0/q) * q + q/2  # 32\n        c2 = np.floor(64/q) * q + q/2  # 96\n        c3 = np.floor(128/q) * q + q/2  # 160\n        c4 = np.floor(192/q) * q + q/2  # 224\n\n        assert int(c1) == 32\n        assert int(c2) == 96\n        assert int(c3) == 160\n        assert int(c4) == 224\n\n        expected = np.uint8([\n            [c1, c1, c4, c4],\n            [c1, c1, c4, c4],\n            [c2, c3, c4, c4]\n        ])\n\n        if nb_channels is not None:\n            image = np.tile(image[..., np.newaxis], (1, 1, nb_channels))\n            expected = np.tile(expected[..., np.newaxis], (1, 1, nb_channels))\n\n        observed = iaa.quantize_uniform_(np.copy(image), 4)\n\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_no_channels_2_colors(self):\n        self._test_images_with_n_channels_2_colors(None)\n\n    def test_images_with_1_channel_2_colors(self):\n        self._test_images_with_n_channels_2_colors(1)\n\n    def test_images_with_3_channels_2_colors(self):\n        self._test_images_with_n_channels_2_colors(3)\n\n    def test_images_with_no_channels_4_colors(self):\n        self._test_images_with_n_channels_4_colors(None)\n\n    def test_images_with_1_channel_4_colors(self):\n        self._test_images_with_n_channels_4_colors(1)\n\n    def test_images_with_3_channels_4_colors(self):\n        self._test_images_with_n_channels_4_colors(3)\n\n    def test_to_bin_centers_is_false(self):\n        nb_channels = 3\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n            [127, 128, 220, 220]\n        ])\n\n        c1 = 0\n        c2 = 64\n        c3 = 128\n        c4 = 192\n\n        expected = np.uint8([\n            [c1, c1, c4, c4],\n            [c1, c1, c4, c4],\n            [c2, c3, c4, c4]\n        ])\n\n        image = np.tile(image[..., np.newaxis], (1, 1, nb_channels))\n        expected = np.tile(expected[..., np.newaxis], (1, 1, nb_channels))\n\n        observed = iaa.quantize_uniform_(np.copy(image),\n                                         4,\n                                         to_bin_centers=False)\n\n        assert np.array_equal(observed, expected)\n\n    def test_failure_if_n_colors_less_than_2(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n\n        got_exception = False\n        try:\n            _ = iaa.quantize_uniform_(np.copy(image), 1)\n        except AssertionError as exc:\n            assert \"[2..256]\" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_noncontiguous(self):\n        image = np.uint8([\n            [0, 0, 255, 255],\n            [0, 1, 255, 255],\n        ])\n        expected = np.uint8([\n            [192, 192, 64, 64],\n            [192, 192, 64, 64],\n        ])\n\n        image_v = np.fliplr(np.copy(image))\n        assert image_v.flags[\"C_CONTIGUOUS\"] is False\n\n        observed = iaa.quantize_uniform_(image_v, 2)\n\n        assert observed.shape == (2, 4)\n        assert observed.dtype.name == \"uint8\"\n        assert np.array_equal(observed, expected)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.quantize_uniform_(np.copy(image), 2)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.quantize_uniform_(np.copy(image), 2)\n\n                assert np.any(image_aug > 0)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n\nclass Test_quantize_uniform_to_n_bits(unittest.TestCase):\n    @mock.patch(\"imgaug.augmenters.color.quantize_uniform_to_n_bits_\")\n    def test_calls_inplace_function(self, mock_qu):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        mock_qu.return_value = \"foo\"\n\n        result = iaa.quantize_uniform_to_n_bits(image, 3)\n\n        # image provided to in-place func must be copy with same content\n        assert mock_qu.call_args_list[0][0][0] is not image\n        assert np.array_equal(mock_qu.call_args_list[0][0][0], image)\n\n        assert mock_qu.call_args_list[0][1][\"nb_bits\"] == 3\n        assert result == \"foo\"\n\n\n# not much testing necessary here as the function is a wrapper around\n# quantize_uniform()\nclass Test_quantize_uniform_to_n_bits_(unittest.TestCase):\n    @mock.patch(\"imgaug.augmenters.color.quantize_uniform_\")\n    def test_mocked(self, mock_qu):\n        mock_qu.return_value = \"foo\"\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        nb_bits = 3\n\n        result = iaa.quantize_uniform_to_n_bits_(np.copy(image), nb_bits)\n\n        # image provided to in-place func must be copy with same content\n        assert mock_qu.call_args_list[0][0][0] is not image\n        assert np.array_equal(mock_qu.call_args_list[0][0][0], image)\n\n        assert mock_qu.call_args_list[0][1][\"nb_bins\"] == 2**nb_bits\n        assert mock_qu.call_args_list[0][1][\"to_bin_centers\"] is False\n        assert result == \"foo\"\n\n\nclass Test_posterize(unittest.TestCase):\n    @mock.patch(\"imgaug.augmenters.color.quantize_uniform_to_n_bits\")\n    def test_mocked(self, mock_qu):\n        mock_qu.return_value = \"foo\"\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        nb_bits = 3\n\n        result = iaa.posterize(image, nb_bits)\n\n        mock_qu.assert_called_once_with(image, nb_bits)\n        assert result == \"foo\"\n"
  },
  {
    "path": "test/augmenters/test_contrast.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport itertools\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\nimport warnings\n\nimport numpy as np\nimport six.moves as sm\nimport skimage\nimport skimage.data\nimport cv2\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug import dtypes as iadt\nfrom imgaug.augmenters import contrast as contrast_lib\nfrom imgaug.testutils import (ArgCopyingMagicMock, keypoints_equal, reseed,\n                              runtest_pickleable_uint8_img, assertWarns,\n                              is_parameter_instance)\nfrom imgaug.augmentables.batches import _BatchInAugmentation\n\n\nclass TestGammaContrast(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___tuple_to_uniform(self):\n        aug = iaa.GammaContrast((1, 2))\n        assert is_parameter_instance(aug.params1d[0], iap.Uniform)\n        assert is_parameter_instance(aug.params1d[0].a, iap.Deterministic)\n        assert is_parameter_instance(aug.params1d[0].b, iap.Deterministic)\n        assert aug.params1d[0].a.value == 1\n        assert aug.params1d[0].b.value == 2\n\n    def test___init___list_to_choice(self):\n        aug = iaa.GammaContrast([1, 2])\n        assert is_parameter_instance(aug.params1d[0], iap.Choice)\n        assert np.all([val in aug.params1d[0].a for val in [1, 2]])\n\n    def test_images_basic_functionality(self):\n        img = [\n            [1, 2, 3],\n            [4, 5, 6],\n            [7, 8, 9]\n        ]\n        img = np.uint8(img)\n        img3d = np.tile(img[:, :, np.newaxis], (1, 1, 3))\n\n        # check basic functionality with gamma=1 or 2 (deterministic) and\n        # per_channel on/off (makes\n        # no difference due to deterministic gamma)\n        for per_channel in [False, 0, 0.0, True, 1, 1.0]:\n            for gamma in [1, 2]:\n                aug = iaa.GammaContrast(\n                    gamma=iap.Deterministic(gamma),\n                    per_channel=per_channel)\n                img_aug = aug.augment_image(img)\n                img3d_aug = aug.augment_image(img3d)\n                assert img_aug.dtype.name == \"uint8\"\n                assert img3d_aug.dtype.name == \"uint8\"\n                assert np.array_equal(\n                    img_aug,\n                    skimage.exposure.adjust_gamma(img, gamma=gamma))\n                assert np.array_equal(\n                    img3d_aug,\n                    skimage.exposure.adjust_gamma(img3d, gamma=gamma))\n\n    def test_per_channel_is_float(self):\n        # check that per_channel at 50% prob works\n        aug = iaa.GammaContrast((0.5, 2.0), per_channel=0.5)\n        seen = [False, False]\n        img1000d = np.zeros((1, 1, 1000), dtype=np.uint8) + 128\n        for _ in sm.xrange(100):\n            img_aug = aug.augment_image(img1000d)\n            assert img_aug.dtype.name == \"uint8\"\n            nb_values_uq = len(set(img_aug.flatten().tolist()))\n            if nb_values_uq == 1:\n                seen[0] = True\n            else:\n                seen[1] = True\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_keypoints_not_changed(self):\n        aug = iaa.GammaContrast(gamma=2)\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(1, 1)], shape=(3, 3, 3))\n        kpsoi_aug = aug.augment_keypoints([kpsoi])\n        assert keypoints_equal([kpsoi], kpsoi_aug)\n\n    def test_heatmaps_not_changed(self):\n        aug = iaa.GammaContrast(gamma=2)\n        heatmaps_arr = np.zeros((3, 3, 1), dtype=np.float32) + 0.5\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n        heatmaps_aug = aug.augment_heatmaps([heatmaps])[0]\n        assert np.allclose(heatmaps.arr_0to1, heatmaps_aug.arr_0to1)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n                aug = iaa.GammaContrast(0.5)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n                aug = iaa.GammaContrast(0.5)\n\n                image_aug = aug(image=image)\n\n                assert np.any(image_aug != 128)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_other_dtypes_uint_int(self):\n        try:\n            high_res_dt = np.float128\n            dts = [np.uint8, np.uint16, np.uint32, np.uint64,\n                   np.int8, np.int16, np.int32, np.int64]\n        except AttributeError:\n            # cannot reliably check uint64 and int64 on systems that dont\n            # support float128\n            high_res_dt = np.float64\n            dts = [np.uint8, np.uint16, np.uint32,\n                   np.int8, np.int16, np.int32]\n\n        for dtype in dts:\n            dtype = np.dtype(dtype)\n\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            exps = [1, 2, 3]\n            values = [0, 100, int(center_value + 0.1*max_value)]\n            tolerances = [0, 0, 1e-8 * max_value\n                          if dtype\n                          in [np.uint64, np.int64] else 0]\n\n            for exp in exps:\n                aug = iaa.GammaContrast(exp)\n                for value, tolerance in zip(values, tolerances):\n                    with self.subTest(dtype=dtype.name, exp=exp,\n                                      nb_channels=None):\n                        image = np.full((3, 3), value, dtype=dtype)\n                        expected = (\n                            (\n                                (image.astype(high_res_dt) / max_value)\n                                ** exp\n                            ) * max_value\n                        ).astype(dtype)\n                        image_aug = aug.augment_image(image)\n                        value_aug = int(image_aug[0, 0])\n                        value_expected = int(expected[0, 0])\n                        diff = abs(value_aug - value_expected)\n                        assert image_aug.dtype.name == dtype.name\n                        assert len(np.unique(image_aug)) == 1\n                        assert diff <= tolerance\n\n                    # test other channel numbers\n                    for nb_channels in [1, 2, 3, 4, 5, 7, 11]:\n                        with self.subTest(dtype=dtype.name, exp=exp,\n                                          nb_channels=nb_channels):\n                            image = np.full((3, 3), value, dtype=dtype)\n                            image = np.tile(image[..., np.newaxis],\n                                            (1, 1, nb_channels))\n                            for c in sm.xrange(nb_channels):\n                                image[..., c] += c\n                            expected = (\n                                (\n                                    (image.astype(high_res_dt) / max_value)\n                                    ** exp\n                                ) * max_value\n                            ).astype(dtype)\n                            image_aug = aug.augment_image(image)\n                            assert image_aug.shape == (3, 3, nb_channels)\n                            assert image_aug.dtype.name == dtype.name\n                            # can be less than nb_channels when multiple input\n                            # values map to the same output value\n                            # mapping distribution can behave exponential with\n                            # slow start and fast growth at the end\n                            assert len(np.unique(image_aug)) <= nb_channels\n                            for c in sm.xrange(nb_channels):\n                                value_aug = int(image_aug[0, 0, c])\n                                value_expected = int(expected[0, 0, c])\n                                diff = abs(value_aug - value_expected)\n                                assert diff <= tolerance\n\n    def test_other_dtypes_float(self):\n        dts = [np.float16, np.float32, np.float64]\n        for dtype in dts:\n            dtype = np.dtype(dtype)\n\n            def _allclose(a, b):\n                atol = 1e-3 if dtype == np.float16 else 1e-8\n                return np.allclose(a, b, atol=atol, rtol=0)\n\n            exps = [1, 2]\n            isize = np.dtype(dtype).itemsize\n            values = [0, 1.0, 50.0, 100 ** (isize - 1)]\n\n            for exp in exps:\n                aug = iaa.GammaContrast(exp)\n                for value in values:\n                    with self.subTest(dtype=dtype.name, exp=exp,\n                                      nb_channels=None):\n                        image = np.full((3, 3), value, dtype=dtype)\n                        expected = (\n                            image.astype(np.float64)\n                            ** exp\n                        ).astype(dtype)\n                        image_aug = aug.augment_image(image)\n                        assert image_aug.dtype == np.dtype(dtype)\n                        assert _allclose(image_aug, expected)\n\n                    # test other channel numbers\n                    for nb_channels in [1, 2, 3, 4, 5, 7, 11]:\n                        with self.subTest(dtype=dtype.name, exp=exp,\n                                          nb_channels=nb_channels):\n                            image = np.full((3, 3), value, dtype=dtype)\n                            image = np.tile(image[..., np.newaxis],\n                                            (1, 1, nb_channels))\n                            for c in sm.xrange(nb_channels):\n                                image[..., c] += float(c)\n                            expected = (\n                                image.astype(np.float64)\n                                ** exp\n                            ).astype(dtype)\n                            image_aug = aug.augment_image(image)\n                            assert image_aug.shape == (3, 3, nb_channels)\n                            assert image_aug.dtype.name == dtype.name\n                            for c in sm.xrange(nb_channels):\n                                value_aug = image_aug[0, 0, c]\n                                value_expected = expected[0, 0, c]\n                                assert _allclose(value_aug, value_expected)\n\n    def test_pickleable(self):\n        aug = iaa.GammaContrast((0.5, 2.0), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\nclass TestSigmoidContrast(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___tuple_to_uniform(self):\n        # check that tuple to uniform works\n        # note that gain and cutoff are saved in inverted order in\n        # _ContrastFuncWrapper to match the order of skimage's function\n        aug = iaa.SigmoidContrast(gain=(1, 2), cutoff=(0.25, 0.75))\n        assert is_parameter_instance(aug.params1d[0], iap.Uniform)\n        assert is_parameter_instance(aug.params1d[0].a, iap.Deterministic)\n        assert is_parameter_instance(aug.params1d[0].b, iap.Deterministic)\n        assert aug.params1d[0].a.value == 1\n        assert aug.params1d[0].b.value == 2\n        assert is_parameter_instance(aug.params1d[1], iap.Uniform)\n        assert is_parameter_instance(aug.params1d[1].a, iap.Deterministic)\n        assert is_parameter_instance(aug.params1d[1].b, iap.Deterministic)\n        assert np.allclose(aug.params1d[1].a.value, 0.25)\n        assert np.allclose(aug.params1d[1].b.value, 0.75)\n\n    def test___init___list_to_choice(self):\n        # check that list to choice works\n        # note that gain and cutoff are saved in inverted order in\n        # _ContrastFuncWrapper to match the order of skimage's function\n        aug = iaa.SigmoidContrast(gain=[1, 2], cutoff=[0.25, 0.75])\n        assert is_parameter_instance(aug.params1d[0], iap.Choice)\n        assert np.all([val in aug.params1d[0].a for val in [1, 2]])\n        assert is_parameter_instance(aug.params1d[1], iap.Choice)\n        assert np.all([\n            np.allclose(val, val_choice)\n            for val, val_choice\n            in zip([0.25, 0.75], aug.params1d[1].a)])\n\n    def test_images_basic_functionality(self):\n        img = [\n            [1, 2, 3],\n            [4, 5, 6],\n            [7, 8, 9]\n        ]\n        img = np.uint8(img)\n        img3d = np.tile(img[:, :, np.newaxis], (1, 1, 3))\n\n        # check basic functionality with per_chanenl on/off (makes no\n        # difference due to deterministic parameters)\n        for per_channel in [False, 0, 0.0, True, 1, 1.0]:\n            for gain, cutoff in itertools.product([5, 10], [0.25, 0.75]):\n                with self.subTest(gain=gain, cutoff=cutoff,\n                                  per_channel=per_channel):\n                    aug = iaa.SigmoidContrast(\n                        gain=iap.Deterministic(gain),\n                        cutoff=iap.Deterministic(cutoff),\n                        per_channel=per_channel)\n                    img_aug = aug.augment_image(img)\n                    img3d_aug = aug.augment_image(img3d)\n                    assert img_aug.dtype.name == \"uint8\"\n                    assert img3d_aug.dtype.name == \"uint8\"\n                    assert np.array_equal(\n                        img_aug,\n                        skimage.exposure.adjust_sigmoid(\n                            img, gain=gain, cutoff=cutoff))\n                    assert np.array_equal(\n                        img3d_aug,\n                        skimage.exposure.adjust_sigmoid(\n                            img3d, gain=gain, cutoff=cutoff))\n\n    def test_per_channel_is_float(self):\n        # check that per_channel at 50% prob works\n        aug = iaa.SigmoidContrast(gain=(1, 10),\n                                  cutoff=(0.25, 0.75),\n                                  per_channel=0.5)\n        seen = [False, False]\n        img1000d = np.zeros((1, 1, 1000), dtype=np.uint8) + 128\n        for _ in sm.xrange(100):\n            img_aug = aug.augment_image(img1000d)\n            assert img_aug.dtype.name == \"uint8\"\n            nb_values_uq = len(set(img_aug.flatten().tolist()))\n            if nb_values_uq == 1:\n                seen[0] = True\n            else:\n                seen[1] = True\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_keypoints_dont_change(self):\n        aug = iaa.SigmoidContrast(gain=10, cutoff=0.5)\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(1, 1)], shape=(3, 3, 3))\n        kpsoi_aug = aug.augment_keypoints([kpsoi])\n        assert keypoints_equal([kpsoi], kpsoi_aug)\n\n    def test_heatmaps_dont_change(self):\n        aug = iaa.SigmoidContrast(gain=10, cutoff=0.5)\n        heatmaps_arr = np.zeros((3, 3, 1), dtype=np.float32) + 0.5\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n        heatmaps_aug = aug.augment_heatmaps([heatmaps])[0]\n        assert np.allclose(heatmaps.arr_0to1, heatmaps_aug.arr_0to1)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n                aug = iaa.SigmoidContrast(gain=10, cutoff=0.5)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n                aug = iaa.SigmoidContrast(gain=10, cutoff=1.0)\n\n                image_aug = aug(image=image)\n\n                assert np.any(image_aug != 128)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_other_dtypes_uint_int(self):\n        try:\n            high_res_dt = np.float128\n            dtypes = [np.uint8, np.uint16, np.uint32, np.uint64,\n                      np.int8, np.int16, np.int32, np.int64]\n        except AttributeError:\n            # cannot reliably check uint64 and int64 on systems that dont\n            # support float128\n            high_res_dt = np.float64\n            dtypes = [np.uint8, np.uint16, np.uint32,\n                      np.int8, np.int16, np.int32]\n\n        for dtype in dtypes:\n            dtype = np.dtype(dtype)\n\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            gains = [5, 20]\n            cutoffs = [0.25, 0.75]\n            values = [0, 100, int(center_value + 0.1 * max_value)]\n            tmax = 1e-8 * max_value if dtype in [np.uint64, np.int64] else 0\n            tolerances = [tmax, tmax, tmax]\n\n            for gain, cutoff in itertools.product(gains, cutoffs):\n                with self.subTest(dtype=dtype.name, gain=gain, cutoff=cutoff):\n                    aug = iaa.SigmoidContrast(gain=gain, cutoff=cutoff)\n                    for value, tolerance in zip(values, tolerances):\n                        image = np.full((3, 3), value, dtype=dtype)\n                        # TODO this looks like the equation commented out\n                        #      should actually the correct one, but when using\n                        #      it we get a difference between expectation and\n                        #      skimage ground truth\n                        # 1/(1 + exp(gain*(cutoff - I_ij/max)))\n                        expected = (\n                            1\n                            / (\n                                1\n                                + np.exp(\n                                    gain\n                                    * (\n                                        cutoff\n                                        - image.astype(high_res_dt)/max_value\n                                    )\n                                )\n                            )\n                        )\n                        expected = (expected * max_value).astype(dtype)\n                        # expected = (\n                        #   1/(1 + np.exp(gain * (\n                        #       cutoff - (\n                        #           image.astype(high_res_dt)-min_value\n                        #       )/dynamic_range\n                        #   ))))\n                        # expected = (\n                        #   min_value + expected * dynamic_range).astype(dtype)\n                        image_aug = aug.augment_image(image)\n                        value_aug = int(image_aug[0, 0])\n                        value_expected = int(expected[0, 0])\n                        diff = abs(value_aug - value_expected)\n                        assert image_aug.dtype.name == dtype.name\n                        assert len(np.unique(image_aug)) == 1\n                        assert diff <= tolerance\n\n    def test_other_dtypes_float(self):\n        dtypes = [np.float16, np.float32, np.float64]\n        for dtype in dtypes:\n            dtype = np.dtype(dtype)\n\n            def _allclose(a, b):\n                atol = 1e-3 if dtype == np.float16 else 1e-8\n                return np.allclose(a, b, atol=atol, rtol=0)\n\n            gains = [5, 20]\n            cutoffs = [0.25, 0.75]\n            isize = np.dtype(dtype).itemsize\n            values = [0, 1.0, 50.0, 100 ** (isize - 1)]\n\n            for gain, cutoff in itertools.product(gains, cutoffs):\n                with self.subTest(dtype=dtype, gain=gain, cutoff=cutoff):\n                    aug = iaa.SigmoidContrast(gain=gain, cutoff=cutoff)\n                    for value in values:\n                        image = np.full((3, 3), value, dtype=dtype)\n                        expected = (\n                            1\n                            / (\n                                1\n                                + np.exp(\n                                    gain\n                                    * (\n                                        cutoff\n                                        - image.astype(np.float64)\n                                    )\n                                )\n                            )\n                        ).astype(dtype)\n                        image_aug = aug.augment_image(image)\n                        assert image_aug.dtype.name == dtype.name\n                        assert _allclose(image_aug, expected)\n\n    def test_pickleable(self):\n        aug = iaa.SigmoidContrast(gain=(1, 2), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\nclass TestLogContrast(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_images_basic_functionality(self):\n        img = [\n            [1, 2, 3],\n            [4, 5, 6],\n            [7, 8, 9]\n        ]\n        img = np.uint8(img)\n        img3d = np.tile(img[:, :, np.newaxis], (1, 1, 3))\n\n        # check basic functionality with gain=1 or 2 (deterministic) and\n        # per_channel on/off (makes no difference due to deterministic gain)\n        for per_channel in [False, 0, 0.0, True, 1, 1.0]:\n            for gain in [1, 2]:\n                with self.subTest(gain=gain, per_channel=per_channel):\n                    aug = iaa.LogContrast(\n                        gain=iap.Deterministic(gain),\n                        per_channel=per_channel)\n                    img_aug = aug.augment_image(img)\n                    img3d_aug = aug.augment_image(img3d)\n                    assert img_aug.dtype.name == \"uint8\"\n                    assert img3d_aug.dtype.name == \"uint8\"\n                    assert np.array_equal(\n                        img_aug,\n                        skimage.exposure.adjust_log(img, gain=gain))\n                    assert np.array_equal(\n                        img3d_aug,\n                        skimage.exposure.adjust_log(img3d, gain=gain))\n\n    def test___init___tuple_to_uniform(self):\n        aug = iaa.LogContrast((1, 2))\n        assert is_parameter_instance(aug.params1d[0], iap.Uniform)\n        assert is_parameter_instance(aug.params1d[0].a, iap.Deterministic)\n        assert is_parameter_instance(aug.params1d[0].b, iap.Deterministic)\n        assert aug.params1d[0].a.value == 1\n        assert aug.params1d[0].b.value == 2\n\n    def test___init___list_to_choice(self):\n        aug = iaa.LogContrast([1, 2])\n        assert is_parameter_instance(aug.params1d[0], iap.Choice)\n        assert np.all([val in aug.params1d[0].a for val in [1, 2]])\n\n    def test_per_channel_is_float(self):\n        # check that per_channel at 50% prob works\n        aug = iaa.LogContrast((0.5, 2.0), per_channel=0.5)\n        seen = [False, False]\n        img1000d = np.zeros((1, 1, 1000), dtype=np.uint8) + 128\n        for _ in sm.xrange(100):\n            img_aug = aug.augment_image(img1000d)\n            assert img_aug.dtype.name == \"uint8\"\n            nb_values_uq = len(set(img_aug.flatten().tolist()))\n            if nb_values_uq == 1:\n                seen[0] = True\n            else:\n                seen[1] = True\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_keypoints_not_changed(self):\n        aug = iaa.LogContrast(gain=2)\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(1, 1)], shape=(3, 3, 3))\n        kpsoi_aug = aug.augment_keypoints([kpsoi])\n        assert keypoints_equal([kpsoi], kpsoi_aug)\n\n    def test_heatmaps_not_changed(self):\n        aug = iaa.LogContrast(gain=2)\n        heatmap_arr = np.zeros((3, 3, 1), dtype=np.float32) + 0.5\n        heatmaps = ia.HeatmapsOnImage(heatmap_arr, shape=(3, 3, 3))\n        heatmaps_aug = aug.augment_heatmaps([heatmaps])[0]\n        assert np.allclose(heatmaps.arr_0to1, heatmaps_aug.arr_0to1)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n                aug = iaa.LogContrast(gain=2)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n                aug = iaa.LogContrast(gain=2)\n\n                image_aug = aug(image=image)\n\n                assert np.any(image_aug != 128)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_other_dtypes_uint_int(self):\n        # support before 1.17:\n        #   [np.uint8, np.uint16, np.uint32, np.uint64,\n        #    np.int8, np.int16, np.int32, np.int64]\n        # support beginning with 1.17:\n        #   [np.uint8, np.uint16,\n        #    np.int8, np.int16]\n        # uint, int\n        dtypes = [np.uint8, np.uint16, np.int8, np.int16]\n\n        for dtype in dtypes:\n            dtype = np.dtype(dtype)\n\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            gains = [0.5, 0.75, 1.0, 1.1]\n            values = [0, 100, int(center_value + 0.1 * max_value)]\n            tmax = 1e-8 * max_value if dtype in [np.uint64, np.int64] else 0\n            tolerances = [0, tmax, tmax]\n\n            for gain in gains:\n                aug = iaa.LogContrast(gain)\n                for value, tolerance in zip(values, tolerances):\n                    with self.subTest(dtype=dtype.name, gain=gain):\n                        image = np.full((3, 3), value, dtype=dtype)\n                        expected = (\n                            gain\n                            * np.log2(\n                                1 + (image.astype(np.float64)/max_value)\n                            )\n                        )\n                        expected = (expected*max_value).astype(dtype)\n                        image_aug = aug.augment_image(image)\n                        value_aug = int(image_aug[0, 0])\n                        value_expected = int(expected[0, 0])\n                        diff = abs(value_aug - value_expected)\n                        assert image_aug.dtype.name == dtype.name\n                        assert len(np.unique(image_aug)) == 1\n                        assert diff <= tolerance\n\n    def test_other_dtypes_float(self):\n        dtypes = [np.float16, np.float32, np.float64]\n\n        for dtype in dtypes:\n            dtype = np.dtype(dtype)\n\n            def _allclose(a, b):\n                # since numpy 1.17 this needs for some reason at least 1e-5 as\n                # the tolerance, previously 1e-8 worked\n                atol = 1e-2 if dtype == np.float16 else 1e-5\n                return np.allclose(a, b, atol=atol, rtol=0)\n\n            gains = [0.5, 0.75, 1.0, 1.1]\n            isize = np.dtype(dtype).itemsize\n            values = [0, 1.0, 50.0, 100 ** (isize - 1)]\n\n            for gain in gains:\n                aug = iaa.LogContrast(gain)\n                for value in values:\n                    with self.subTest(dtype=dtype.name, gain=gain):\n                        image = np.full((3, 3), value, dtype=dtype)\n                        expected = (\n                            gain\n                            * np.log2(\n                                1 + image.astype(np.float64)\n                            )\n                        )\n                        expected = expected.astype(dtype)\n                        image_aug = aug.augment_image(image)\n                        assert image_aug.dtype.name == dtype\n                        assert _allclose(image_aug, expected)\n\n    def test_pickleable(self):\n        aug = iaa.LogContrast((1, 2), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\nclass TestLinearContrast(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_images_basic_functionality(self):\n        img = [\n            [1, 2, 3],\n            [4, 5, 6],\n            [7, 8, 9]\n        ]\n        img = np.uint8(img)\n        img3d = np.tile(img[:, :, np.newaxis], (1, 1, 3))\n\n        # check basic functionality with alpha=1 or 2 (deterministic) and\n        # per_channel on/off (makes no difference due to deterministic alpha)\n        for per_channel in [False, 0, 0.0, True, 1, 1.0]:\n            for alpha in [1, 2]:\n                with self.subTest(alpha=alpha, per_channel=per_channel):\n                    aug = iaa.LinearContrast(\n                        alpha=iap.Deterministic(alpha),\n                        per_channel=per_channel)\n                    img_aug = aug.augment_image(img)\n                    img3d_aug = aug.augment_image(img3d)\n                    assert img_aug.dtype.name == \"uint8\"\n                    assert img3d_aug.dtype.name == \"uint8\"\n                    assert np.array_equal(\n                        img_aug,\n                        contrast_lib.adjust_contrast_linear(img, alpha=alpha))\n                    assert np.array_equal(\n                        img3d_aug,\n                        contrast_lib.adjust_contrast_linear(img3d, alpha=alpha))\n\n    def test___init___tuple_to_uniform(self):\n        aug = iaa.LinearContrast((1, 2))\n        assert is_parameter_instance(aug.params1d[0], iap.Uniform)\n        assert is_parameter_instance(aug.params1d[0].a, iap.Deterministic)\n        assert is_parameter_instance(aug.params1d[0].b, iap.Deterministic)\n        assert aug.params1d[0].a.value == 1\n        assert aug.params1d[0].b.value == 2\n\n    def test___init___list_to_choice(self):\n        aug = iaa.LinearContrast([1, 2])\n        assert is_parameter_instance(aug.params1d[0], iap.Choice)\n        assert np.all([val in aug.params1d[0].a for val in [1, 2]])\n\n    def test_float_as_per_channel(self):\n        # check that per_channel at 50% prob works\n        aug = iaa.LinearContrast((0.5, 2.0), per_channel=0.5)\n        seen = [False, False]\n        # must not use just value 128 here, otherwise nothing will change as\n        # all values would have distance 0 to 128\n        img1000d = np.zeros((1, 1, 1000), dtype=np.uint8) + 128 + 20\n        for _ in sm.xrange(100):\n            img_aug = aug.augment_image(img1000d)\n            assert img_aug.dtype.name == \"uint8\"\n            nb_values_uq = len(set(img_aug.flatten().tolist()))\n            if nb_values_uq == 1:\n                seen[0] = True\n            else:\n                seen[1] = True\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_keypoints_not_changed(self):\n        aug = iaa.LinearContrast(alpha=2)\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(1, 1)], shape=(3, 3, 3))\n        kpsoi_aug = aug.augment_keypoints([kpsoi])\n        assert keypoints_equal([kpsoi], kpsoi_aug)\n\n    def test_heatmaps_not_changed(self):\n        aug = iaa.LinearContrast(alpha=2)\n        heatmaps_arr = np.zeros((3, 3, 1), dtype=np.float32) + 0.5\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n        heatmaps_aug = aug.augment_heatmaps([heatmaps])[0]\n        assert np.allclose(heatmaps.arr_0to1, heatmaps_aug.arr_0to1)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 129, dtype=np.uint8)\n                aug = iaa.LinearContrast(alpha=2)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 129, dtype=np.uint8)\n                aug = iaa.LinearContrast(alpha=2)\n\n                image_aug = aug(image=image)\n\n                assert np.any(image_aug != 128)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    # test for other dtypes are in Test_adjust_contrast_linear\n\n    def test_pickleable(self):\n        aug = iaa.LinearContrast((0.5, 2.0), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\nclass Test_adjust_contrast_linear(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_other_dtypes(self):\n        dtypes = [np.uint8, np.uint16, np.uint32,\n                  np.int8, np.int16, np.int32,\n                  np.float16, np.float32, np.float64]\n\n        for dtype in dtypes:\n            dtype = np.dtype(dtype)\n\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n            cv = center_value\n            kind = np.dtype(dtype).kind\n            if kind in [\"u\", \"i\"]:\n                cv = int(cv)\n\n            def _compare(a, b):\n                if kind in [\"u\", \"i\"]:\n                    return np.array_equal(a, b)\n                else:\n                    assert kind == \"f\"\n                    atol = 1e-4 if dtype == np.float16 else 1e-8\n                    return np.allclose(a, b, atol=atol, rtol=0)\n\n            img = [\n                [cv-4, cv-3, cv-2],\n                [cv-1, cv+0, cv+1],\n                [cv+2, cv+3, cv+4]\n            ]\n            img = np.array(img, dtype=dtype)\n\n            alphas = [0, 1, 2, 4]\n            if dtype.name not in [\"uint8\", \"int8\", \"float16\"]:\n                alphas.append(100)\n\n            for alpha in alphas:\n                expected = [\n                    [cv-4*alpha, cv-3*alpha, cv-2*alpha],\n                    [cv-1*alpha, cv+0*alpha, cv+1*alpha],\n                    [cv+2*alpha, cv+3*alpha, cv+4*alpha]\n                ]\n\n                expected = np.array(expected, dtype=dtype)\n                observed = contrast_lib.adjust_contrast_linear(\n                    img, alpha=alpha)\n                assert observed.dtype.name == dtype.name\n                assert observed.shape == img.shape\n                assert _compare(observed, expected)\n\n    def test_output_values_exceed_uint8_value_range(self):\n        cv = 127\n        img = [\n            [cv-4, cv-3, cv-2],\n            [cv-1, cv+0, cv+1],\n            [cv+2, cv+3, cv+4]\n        ]\n        img = np.array(img, dtype=np.uint8)\n        observed = contrast_lib.adjust_contrast_linear(img, alpha=255)\n        expected = [\n            [0, 0, 0],\n            [0, cv, 255],\n            [255, 255, 255]\n        ]\n        assert np.array_equal(observed, expected)\n\n    def test_alpha_exceeds_uint8_value_range(self):\n        # overflow in alpha for uint8, should not cause issues\n        cv = 127\n        img = [\n            [cv, cv, cv],\n            [cv, cv, cv],\n            [cv, cv, cv]\n        ]\n        img = np.array(img, dtype=np.uint8)\n        observed = contrast_lib.adjust_contrast_linear(img, alpha=257)\n        expected = [\n            [cv, cv, cv],\n            [cv, cv, cv],\n            [cv, cv, cv]\n        ]\n        assert np.array_equal(observed, expected)\n\n\nclass TestAllChannelsCLAHE(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_init(self):\n        aug = iaa.AllChannelsCLAHE(\n            clip_limit=10,\n            tile_grid_size_px=11,\n            tile_grid_size_px_min=4,\n            per_channel=True)\n        assert is_parameter_instance(aug.clip_limit, iap.Deterministic)\n        assert aug.clip_limit.value == 10\n        assert is_parameter_instance(aug.tile_grid_size_px[0],\n                                     iap.Deterministic)\n        assert aug.tile_grid_size_px[0].value == 11\n        assert aug.tile_grid_size_px[1] is None\n        assert aug.tile_grid_size_px_min == 4\n        assert is_parameter_instance(aug.per_channel, iap.Deterministic)\n        assert np.isclose(aug.per_channel.value, 1.0)\n\n        aug = iaa.AllChannelsCLAHE(\n            clip_limit=(10, 20),\n            tile_grid_size_px=(11, 17),\n            tile_grid_size_px_min=4,\n            per_channel=0.5)\n        assert is_parameter_instance(aug.clip_limit, iap.Uniform)\n        assert aug.clip_limit.a.value == 10\n        assert aug.clip_limit.b.value == 20\n        assert is_parameter_instance(aug.tile_grid_size_px[0],\n                                     iap.DiscreteUniform)\n        assert aug.tile_grid_size_px[0].a.value == 11\n        assert aug.tile_grid_size_px[0].b.value == 17\n        assert aug.tile_grid_size_px[1] is None\n        assert aug.tile_grid_size_px_min == 4\n        assert is_parameter_instance(aug.per_channel, iap.Binomial)\n        assert np.isclose(aug.per_channel.p.value, 0.5)\n\n        aug = iaa.AllChannelsCLAHE(\n            clip_limit=[10, 20, 30],\n            tile_grid_size_px=[11, 17, 21])\n        assert is_parameter_instance(aug.clip_limit, iap.Choice)\n        assert aug.clip_limit.a[0] == 10\n        assert aug.clip_limit.a[1] == 20\n        assert aug.clip_limit.a[2] == 30\n        assert is_parameter_instance(aug.tile_grid_size_px[0], iap.Choice)\n        assert aug.tile_grid_size_px[0].a[0] == 11\n        assert aug.tile_grid_size_px[0].a[1] == 17\n        assert aug.tile_grid_size_px[0].a[2] == 21\n        assert aug.tile_grid_size_px[1] is None\n\n        aug = iaa.AllChannelsCLAHE(tile_grid_size_px=((11, 17), [11, 13, 15]))\n        assert is_parameter_instance(aug.tile_grid_size_px[0], iap.DiscreteUniform)\n        assert aug.tile_grid_size_px[0].a.value == 11\n        assert aug.tile_grid_size_px[0].b.value == 17\n        assert is_parameter_instance(aug.tile_grid_size_px[1], iap.Choice)\n        assert aug.tile_grid_size_px[1].a[0] == 11\n        assert aug.tile_grid_size_px[1].a[1] == 13\n        assert aug.tile_grid_size_px[1].a[2] == 15\n\n    def test_basic_functionality(self):\n        img = [\n            [99, 100, 101],\n            [99, 100, 101],\n            [99, 100, 101]\n        ]\n        img = np.uint8(img)\n        img3d = np.tile(img[:, :, np.newaxis], (1, 1, 3))\n        img3d[..., 1] += 1\n        img3d[..., 2] += 2\n\n        aug = iaa.AllChannelsCLAHE(clip_limit=20, tile_grid_size_px=17)\n\n        mock_clahe = ArgCopyingMagicMock()\n        mock_clahe.apply.return_value = img\n\n        # image with single channel\n        with mock.patch('cv2.createCLAHE') as mock_createCLAHE:\n            mock_createCLAHE.return_value = mock_clahe\n            _ = aug.augment_image(img)\n\n        mock_createCLAHE.assert_called_once_with(\n            clipLimit=20, tileGridSize=(17, 17))\n        assert np.array_equal(mock_clahe.apply.call_args_list[0][0][0], img)\n\n    def test_basic_functionality_3d(self):\n        # image with three channels\n        img = [\n            [99, 100, 101],\n            [99, 100, 101],\n            [99, 100, 101]\n        ]\n        img = np.uint8(img)\n        img3d = np.tile(img[:, :, np.newaxis], (1, 1, 3))\n        img3d[..., 1] += 1\n        img3d[..., 2] += 2\n\n        aug = iaa.AllChannelsCLAHE(clip_limit=20, tile_grid_size_px=17)\n\n        mock_clahe = ArgCopyingMagicMock()\n        mock_clahe.apply.return_value = img\n\n        with mock.patch('cv2.createCLAHE') as mock_createCLAHE:\n            mock_createCLAHE.return_value = mock_clahe\n            _ = aug.augment_image(img3d)\n\n        clist = mock_clahe.apply.call_args_list\n        assert np.array_equal(clist[0][0][0], img3d[..., 0])\n        assert np.array_equal(clist[1][0][0], img3d[..., 1])\n        assert np.array_equal(clist[2][0][0], img3d[..., 2])\n\n    def test_basic_functionality_integrationtest(self):\n        img = np.zeros((3, 7), dtype=np.uint8)\n        img[0, 0] = 90\n        img[0, 1] = 100\n        img[0, 2] = 110\n        for per_channel in [False, 0, 0.0, True, 1, 1.0]:\n            for clip_limit in [4, 6]:\n                for tile_grid_size_px in [3, 5, 7]:\n                    with self.subTest(per_channel=per_channel,\n                                      clip_limit=clip_limit,\n                                      tile_grid_size_px=tile_grid_size_px):\n                        aug = iaa.AllChannelsCLAHE(\n                            clip_limit=clip_limit,\n                            tile_grid_size_px=tile_grid_size_px,\n                            per_channel=per_channel)\n                        img_aug = aug.augment_image(img)\n                        assert int(np.max(img_aug)) - int(np.min(img_aug)) > 2\n\n    def test_tile_grid_size_px_min(self):\n        img = np.zeros((1, 1), dtype=np.uint8)\n        aug = iaa.AllChannelsCLAHE(\n            clip_limit=20,\n            tile_grid_size_px=iap.Deterministic(-1),\n            tile_grid_size_px_min=5)\n        mock_clahe = mock.Mock()\n        mock_clahe.apply.return_value = img\n        mock_createCLAHE = mock.MagicMock(return_value=mock_clahe)\n        with mock.patch('cv2.createCLAHE', mock_createCLAHE):\n            _ = aug.augment_image(img)\n        mock_createCLAHE.assert_called_once_with(\n            clipLimit=20, tileGridSize=(5, 5))\n\n    def test_per_channel_integrationtest(self):\n        # check that per_channel at 50% prob works\n        aug = iaa.AllChannelsCLAHE(\n            clip_limit=(1, 200),\n            tile_grid_size_px=(3, 8),\n            per_channel=0.5)\n        seen = [False, False]\n        img1000d = np.zeros((3, 7, 1000), dtype=np.uint8)\n        img1000d[0, 0, :] = 90\n        img1000d[0, 1, :] = 100\n        img1000d[0, 2, :] = 110\n        for _ in sm.xrange(100):\n            with assertWarns(self, iaa.SuspiciousSingleImageShapeWarning):\n                img_aug = aug.augment_image(img1000d)\n            assert img_aug.dtype.name == \"uint8\"\n\n            maxs = np.max(img_aug, axis=(0, 1))\n            mins = np.min(img_aug, axis=(0, 1))\n            diffs = maxs.astype(np.int32) - mins.astype(np.int32)\n\n            nb_diffs_uq = len(set(diffs.flatten().tolist()))\n            if nb_diffs_uq == 1:\n                seen[0] = True\n            else:\n                seen[1] = True\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_unit_sized_kernels(self):\n        img = np.zeros((1, 1), dtype=np.uint8)\n\n        tile_grid_sizes = [0, 0, 0, 1, 1, 1, 3, 3, 3]\n        tile_grid_min_sizes = [0, 1, 3, 0, 1, 3, 0, 1, 3]\n        nb_calls_expected = [0, 0, 1, 0, 0, 1, 1, 1, 1]\n\n        gen = zip(tile_grid_sizes, tile_grid_min_sizes, nb_calls_expected)\n        for tile_grid_size_px, tile_grid_size_px_min, nb_calls_exp_i in gen:\n            with self.subTest(tile_grid_size_px=tile_grid_size_px,\n                              tile_grid_size_px_min=tile_grid_size_px_min,\n                              nb_calls_expected_i=nb_calls_exp_i):\n                aug = iaa.AllChannelsCLAHE(\n                    clip_limit=20,\n                    tile_grid_size_px=tile_grid_size_px,\n                    tile_grid_size_px_min=tile_grid_size_px_min)\n                mock_clahe = mock.Mock()\n                mock_clahe.apply.return_value = img\n                mock_createCLAHE = mock.MagicMock(return_value=mock_clahe)\n                with mock.patch('cv2.createCLAHE', mock_createCLAHE):\n                    _ = aug.augment_image(img)\n                assert mock_createCLAHE.call_count == nb_calls_exp_i\n\n    def test_other_dtypes(self):\n        aug = iaa.AllChannelsCLAHE(clip_limit=0.01, tile_grid_size_px=3)\n\n        # np.uint32: TypeError: src data type = 6 is not supported\n        # np.uint64: cv2.error: OpenCV(3.4.2) (...)/clahe.cpp:351:\n        #            error: (-215:Assertion failed)\n        #            src.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3))\n        #            || _src.type() == (((2) & ((1 << 3) - 1))\n        #             + (((1)-1) << 3)) in function 'apply'\n        # np.int8: cv2.error: OpenCV(3.4.2) (...)/clahe.cpp:351: error:\n        #          (-215:Assertion failed)\n        #          src.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3))\n        #          || _src.type() == (((2) & ((1 << 3) - 1))\n        #          + (((1)-1) << 3)) in function 'apply'\n        # np.int16: cv2.error: OpenCV(3.4.2) (...)/clahe.cpp:351: error:\n        #           (-215:Assertion failed)\n        #           src.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3))\n        #           || _src.type() == (((2) & ((1 << 3) - 1))\n        #           + (((1)-1) << 3)) in function 'apply'\n        # np.int32: cv2.error: OpenCV(3.4.2) (...)/clahe.cpp:351: error:\n        #           (-215:Assertion failed)\n        #           src.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3))\n        #           || _src.type() == (((2) & ((1 << 3) - 1))\n        #           + (((1)-1) << 3)) in function 'apply'\n        # np.int64: cv2.error: OpenCV(3.4.2) (...)/clahe.cpp:351: error:\n        #           (-215:Assertion failed)\n        #           src.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3))\n        #           || _src.type() == (((2) & ((1 << 3) - 1))\n        #           + (((1)-1) << 3)) in function 'apply'\n        # np.float16: TypeError: src data type = 23 is not supported\n        # np.float32: cv2.error: OpenCV(3.4.2) (...)/clahe.cpp:351: error:\n        #             (-215:Assertion failed)\n        #             src.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3))\n        #             || _src.type() == (((2) & ((1 << 3) - 1))\n        #             + (((1)-1) << 3)) in function 'apply'\n        # np.float64: cv2.error: OpenCV(3.4.2) (...)/clahe.cpp:351: error:\n        #             (-215:Assertion failed)\n        #             src.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3))\n        #             || _src.type() == (((2) & ((1 << 3) - 1))\n        #             + (((1)-1) << 3)) in function 'apply'\n        # np.float128: TypeError: src data type = 13 is not supported\n        for dtype in [np.uint8, np.uint16]:\n            with self.subTest(dtype=np.dtype(dtype).name):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                dynamic_range = max_value - min_value\n\n                img = np.zeros((11, 11, 1), dtype=dtype)\n                img[:, 0, 0] = min_value\n                img[:, 1, 0] = min_value + 30\n                img[:, 2, 0] = min_value + 40\n                img[:, 3, 0] = min_value + 50\n                img[:, 4, 0] = (\n                    int(center_value)\n                    if np.dtype(dtype).kind != \"f\"\n                    else center_value)\n                img[:, 5, 0] = max_value - 50\n                img[:, 6, 0] = max_value - 40\n                img[:, 7, 0] = max_value - 30\n                img[:, 8, 0] = max_value\n                img_aug = aug.augment_image(img)\n\n                assert img_aug.dtype.name == np.dtype(dtype).name\n                assert (\n                    min_value\n                    <= np.min(img_aug)\n                    <= min_value + 0.2 * dynamic_range)\n                assert (\n                    max_value - 0.2 * dynamic_range\n                    <= np.max(img_aug)\n                    <= max_value)\n\n        # TypeError: src data type = 0 is not supported\n        \"\"\"\n        with self.subTest(\"bool\"):\n            dtype = np.dtype(bool)\n            print(dtype)\n\n            img = np.zeros((11, 11, 1), dtype=dtype)\n            img[:, 0, 0] = 0\n            img[:, 1, 0] = 0\n            img[:, 2, 0] = 0\n            img[:, 3, 0] = 0\n            img[:, 4, 0] = 0\n            img[:, 5, 0] = 1\n            img[:, 6, 0] = 1\n            img[:, 7, 0] = 1\n            img[:, 8, 0] = 1\n            img_aug = aug.augment_image(img)\n            print(img[..., 0])\n            print(img_aug[..., 0])\n\n            assert img_aug.dtype.name == np.dtype(dtype).name\n            assert np.min(img_aug) == 0\n            assert np.max(img_aug) == 1\n        \"\"\"\n\n    def test_keypoints_not_changed(self):\n        aug = iaa.AllChannelsCLAHE()\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(1, 1)], shape=(3, 3, 3))\n        kpsoi_aug = aug.augment_keypoints([kpsoi])\n        assert keypoints_equal([kpsoi], kpsoi_aug)\n\n    def test_heatmaps_not_changed(self):\n        aug = iaa.AllChannelsCLAHE()\n        heatmaps_arr = np.zeros((3, 3, 1), dtype=np.float32) + 0.5\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n        heatmaps_aug = aug.augment_heatmaps([heatmaps])[0]\n        assert np.allclose(heatmaps.arr_0to1, heatmaps_aug.arr_0to1)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 129, dtype=np.uint8)\n                aug = iaa.AllChannelsCLAHE()\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 129, dtype=np.uint8)\n                aug = iaa.AllChannelsCLAHE()\n\n                image_aug = aug(image=image)\n\n                assert np.any(image_aug != 128)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_get_parameters(self):\n        aug = iaa.AllChannelsCLAHE(\n            clip_limit=1,\n            tile_grid_size_px=3,\n            tile_grid_size_px_min=2,\n            per_channel=True)\n        params = aug.get_parameters()\n        assert np.all([\n            is_parameter_instance(params[i], iap.Deterministic)\n            for i\n            in [0, 3]])\n        assert params[0].value == 1\n        assert params[1][0].value == 3\n        assert params[1][1] is None\n        assert params[2] == 2\n        assert params[3].value == 1\n\n    def test_pickleable(self):\n        aug = iaa.AllChannelsCLAHE(clip_limit=(30, 50),\n                                   tile_grid_size_px=(4, 12),\n                                   seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(100, 100, 3))\n\n\nclass TestCLAHE(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_init(self):\n        clahe = iaa.CLAHE(\n            clip_limit=1,\n            tile_grid_size_px=3,\n            tile_grid_size_px_min=2,\n            from_colorspace=iaa.CSPACE_BGR,\n            to_colorspace=iaa.CSPACE_HSV)\n\n        assert clahe.all_channel_clahe.clip_limit.value == 1\n        assert clahe.all_channel_clahe.tile_grid_size_px[0].value == 3\n        assert clahe.all_channel_clahe.tile_grid_size_px[1] is None\n        assert clahe.all_channel_clahe.tile_grid_size_px_min == 2\n\n        icba = clahe.intensity_channel_based_applier\n        assert icba.from_colorspace == iaa.CSPACE_BGR\n        assert icba.to_colorspace == iaa.CSPACE_HSV\n\n    @mock.patch(\"imgaug.augmenters.color.change_colorspace_\")\n    def test_single_image_grayscale(self, mock_cs):\n        img = [\n            [0, 1, 2, 3, 4],\n            [5, 6, 7, 8, 9],\n            [10, 11, 12, 13, 14]\n        ]\n        img = np.uint8(img)\n\n        mocked_batch = _BatchInAugmentation(\n            images=[img[..., np.newaxis] + 2])\n\n        def _side_effect(image, _to_colorspace, _from_colorspace):\n            return image + 1\n\n        mock_cs.side_effect = _side_effect\n\n        mock_all_channel_clahe = ArgCopyingMagicMock()\n        mock_all_channel_clahe._augment_batch_.return_value = mocked_batch\n\n        clahe = iaa.CLAHE(\n            clip_limit=1,\n            tile_grid_size_px=3,\n            tile_grid_size_px_min=2,\n            from_colorspace=iaa.CSPACE_RGB,\n            to_colorspace=iaa.CSPACE_Lab)\n        clahe.all_channel_clahe = mock_all_channel_clahe\n\n        img_aug = clahe.augment_image(img)\n        assert np.array_equal(img_aug, img+2)\n\n        assert mock_cs.call_count == 0\n        assert mock_all_channel_clahe._augment_batch_.call_count == 1\n\n    @classmethod\n    def _test_single_image_3d_rgb_to_x(cls, to_colorspace, channel_idx):\n        fname_cs = \"imgaug.augmenters.color.change_colorspace_\"\n        with mock.patch(fname_cs) as mock_cs:\n            img = [\n                [0, 1, 2, 3, 4],\n                [5, 6, 7, 8, 9],\n                [10, 11, 12, 13, 14]\n            ]\n            img = np.uint8(img)\n            img3d = np.tile(img[..., np.newaxis], (1, 1, 3))\n            img3d[..., 1] += 10\n            img3d[..., 2] += 20\n\n            def side_effect_change_colorspace(image, _to_colorspace,\n                                              _from_colorspace):\n                return image + 1\n\n            def side_effect_all_channel_clahe(batch_call, _random_state,\n                                              _parents, _hooks):\n                batch_call = batch_call.deepcopy()\n                batch_call.images = [batch_call.images[0] + 2]\n                return batch_call\n\n            mock_cs.side_effect = side_effect_change_colorspace\n\n            mock_all_channel_clahe = ArgCopyingMagicMock()\n            mock_all_channel_clahe._augment_batch_.side_effect = \\\n                side_effect_all_channel_clahe\n\n            clahe = iaa.CLAHE(\n                clip_limit=1,\n                tile_grid_size_px=3,\n                tile_grid_size_px_min=2,\n                from_colorspace=iaa.CSPACE_RGB,\n                to_colorspace=to_colorspace)\n            clahe.all_channel_clahe = mock_all_channel_clahe\n\n            img3d_aug = clahe.augment_image(np.copy(img3d))\n            expected1 = img3d + 1\n            expected2 = np.copy(expected1)\n            expected2[..., channel_idx] += 2\n            expected3 = np.copy(expected2) + 1\n            assert np.array_equal(img3d_aug, expected3)\n\n            assert mock_cs.call_count == 2\n            assert mock_all_channel_clahe._augment_batch_.call_count == 1\n\n            # indices: call 0, args, arg 0\n            assert np.array_equal(mock_cs.call_args_list[0][0][0], img3d)\n\n            # for some unclear reason, call_args_list here seems to contain the\n            # output instead of the input to side_effect_all_channel_clahe, so\n            # this assert is deactivated for now\n            # cargs = mock_all_channel_clahe.call_args_list\n            # print(\"mock\", cargs[0][0][0][0].shape)\n            # print(\"mock\", cargs[0][0][0][0][..., 0])\n            # print(\"exp \", expected1[..., channel_idx])\n            # assert np.array_equal(\n            #     cargs[0][0][0][0],\n            #     expected1[..., channel_idx:channel_idx+1]\n            # )\n\n            assert np.array_equal(mock_cs.call_args_list[1][0][0], expected2)\n\n    def test_single_image_3d_rgb_to_lab(self):\n        self._test_single_image_3d_rgb_to_x(iaa.CSPACE_Lab, 0)\n\n    def test_single_image_3d_rgb_to_hsv(self):\n        self._test_single_image_3d_rgb_to_x(iaa.CSPACE_HSV, 2)\n\n    def test_single_image_3d_rgb_to_hls(self):\n        self._test_single_image_3d_rgb_to_x(iaa.CSPACE_HLS, 1)\n\n    @mock.patch(\"imgaug.augmenters.color.change_colorspace_\")\n    def test_single_image_4d_rgb_to_lab(self, mock_cs):\n        channel_idx = 0\n\n        img = [\n            [0, 1, 2, 3, 4],\n            [5, 6, 7, 8, 9],\n            [10, 11, 12, 13, 14]\n        ]\n        img = np.uint8(img)\n        img4d = np.tile(img[..., np.newaxis], (1, 1, 4))\n        img4d[..., 1] += 10\n        img4d[..., 2] += 20\n        img4d[..., 3] += 30\n\n        def side_effect_change_colorspace(image, _to_colorspace,\n                                          _from_colorspace):\n            return image + 1\n\n        def side_effect_all_channel_clahe(batch_call, _random_state, _parents,\n                                          _hooks):\n            batch_call = batch_call.deepcopy()\n            batch_call.images = [batch_call.images[0] + 2]\n            return batch_call\n\n        mock_cs.side_effect = side_effect_change_colorspace\n\n        mock_all_channel_clahe = ArgCopyingMagicMock()\n        mock_all_channel_clahe._augment_batch_.side_effect = \\\n            side_effect_all_channel_clahe\n\n        clahe = iaa.CLAHE(\n            clip_limit=1,\n            tile_grid_size_px=3,\n            tile_grid_size_px_min=2,\n            from_colorspace=iaa.CSPACE_RGB,\n            to_colorspace=iaa.CSPACE_Lab)\n        clahe.all_channel_clahe = mock_all_channel_clahe\n\n        img4d_aug = clahe.augment_image(img4d)\n        expected1 = img4d[..., 0:3] + 1\n        expected2 = np.copy(expected1)\n        expected2[..., channel_idx] += 2\n        expected3 = np.copy(expected2) + 1\n        expected4 = np.dstack((expected3, img4d[..., 3:4]))\n        assert np.array_equal(img4d_aug, expected4)\n\n        assert mock_cs.call_count == 2\n        assert mock_all_channel_clahe._augment_batch_.call_count == 1\n\n        # indices: call 0, args, arg 0\n        assert np.array_equal(mock_cs.call_args_list[0][0][0], img4d[..., 0:3])\n\n        # for some unclear reason, call_args_list here seems to contain the\n        # output instead of the input to side_effect_all_channel_clahe, so\n        # this assert is deactivated for now\n        # assert np.array_equal(\n        #     mock_all_channel_clahe.call_args_list[0][0][0][0],\n        #     expected1[..., channel_idx:channel_idx+1]\n        # )\n\n        assert np.array_equal(mock_cs.call_args_list[1][0][0], expected2)\n\n    @mock.patch(\"imgaug.augmenters.color.change_colorspace_\")\n    def test_single_image_5d_rgb_to_lab(self, mock_cs):\n        img = [\n            [0, 1, 2, 3, 4],\n            [5, 6, 7, 8, 9],\n            [10, 11, 12, 13, 14]\n        ]\n        img = np.uint8(img)\n        img5d = np.tile(img[..., np.newaxis], (1, 1, 5))\n        img5d[..., 1] += 10\n        img5d[..., 2] += 20\n        img5d[..., 3] += 30\n        img5d[..., 4] += 40\n\n        def side_effect_change_colorspace(image, _to_colorspace,\n                                          _from_colorspace):\n            return image + 1\n\n        def side_effect_all_channel_clahe(batch_call, _random_state, _parents,\n                                          _hooks):\n            batch_call = batch_call.deepcopy()\n            batch_call.images = [batch_call.images[0] + 2]\n            return batch_call\n\n        mock_cs.side_effect = side_effect_change_colorspace\n\n        mock_all_channel_clahe = ArgCopyingMagicMock()\n        mock_all_channel_clahe._augment_batch_.side_effect = \\\n            side_effect_all_channel_clahe\n\n        clahe = iaa.CLAHE(\n            clip_limit=1,\n            tile_grid_size_px=3,\n            tile_grid_size_px_min=2,\n            from_colorspace=iaa.CSPACE_RGB,\n            to_colorspace=iaa.CSPACE_Lab,\n            name=\"ExampleCLAHE\")\n        clahe.all_channel_clahe = mock_all_channel_clahe\n\n        # note that self.assertWarningRegex does not exist in python 2.7\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            img5d_aug = clahe.augment_image(img5d)\n            assert len(caught_warnings) == 1\n            assert (\n                \"Got image with 5 channels in _IntensityChannelBasedApplier \"\n                \"(parents: ExampleCLAHE)\"\n                in str(caught_warnings[-1].message)\n            )\n\n        assert np.array_equal(img5d_aug, img5d + 2)\n\n        assert mock_cs.call_count == 0\n        assert mock_all_channel_clahe._augment_batch_.call_count == 1\n\n        # indices: call 0, args, arg 0, image 0 in list of images\n        assert np.array_equal(\n            mock_all_channel_clahe\n            ._augment_batch_\n            .call_args_list[0][0][0]\n            .images[0],\n            img5d\n        )\n\n    @classmethod\n    def _test_many_images_rgb_to_lab_list(cls, with_3d_images):\n        fname_cs = \"imgaug.augmenters.color.change_colorspace_\"\n        with mock.patch(fname_cs) as mock_cs:\n            img = [\n                [0, 1, 2, 3, 4],\n                [5, 6, 7, 8, 9],\n                [10, 11, 12, 13, 14]\n            ]\n            img = np.uint8(img)\n\n            n_imgs = 2\n            n_3d_imgs = 3 if with_3d_images else 0\n\n            imgs = []\n            for i in sm.xrange(n_imgs):\n                imgs.append(img + i)\n            for i in sm.xrange(n_3d_imgs):\n                imgs.append(np.tile(img[..., np.newaxis], (1, 1, 3)) + 2 + i)\n\n            def side_effect_change_colorspace(image, _to_colorspace,\n                                              _from_colorspace):\n                return image + 1\n\n            def side_effect_all_channel_clahe(batch_call, _random_state,\n                                              _parents, _hooks):\n                batch_call = batch_call.deepcopy()\n                batch_call.images = [image + 2 for image in batch_call.images]\n                return batch_call\n\n            mock_cs.side_effect = side_effect_change_colorspace\n\n            mock_all_channel_clahe = ArgCopyingMagicMock()\n            mock_all_channel_clahe._augment_batch_.side_effect = \\\n                side_effect_all_channel_clahe\n\n            clahe = iaa.CLAHE(\n                clip_limit=1,\n                tile_grid_size_px=3,\n                tile_grid_size_px_min=2,\n                from_colorspace=iaa.CSPACE_RGB,\n                to_colorspace=iaa.CSPACE_Lab)\n            clahe.all_channel_clahe = mock_all_channel_clahe\n\n            imgs_aug = clahe.augment_images(imgs)\n            assert isinstance(imgs_aug, list)\n\n            assert mock_cs.call_count == (n_3d_imgs*2 if with_3d_images else 0)\n            assert (\n                mock_all_channel_clahe\n                ._augment_batch_\n                .call_count == 1)\n\n            # indices: call 0, args, arg 0\n            assert isinstance(\n                mock_all_channel_clahe\n                ._augment_batch_\n                .call_args_list[0][0][0],\n                _BatchInAugmentation)\n\n            assert (\n                len(mock_all_channel_clahe\n                    ._augment_batch_\n                    .call_args_list[0][0][0]\n                    .images)\n                == 5 if with_3d_images else 2)\n\n            # indices: call 0, args, arg 0, image i in list of images\n            for i in sm.xrange(0, 2):\n                expected = imgs[i][..., np.newaxis]\n                assert np.array_equal(\n                    mock_all_channel_clahe\n                    ._augment_batch_\n                    .call_args_list[0][0][0]\n                    .images[i],\n                    expected\n                )\n\n            if with_3d_images:\n                for i in sm.xrange(2, 5):\n                    expected = imgs[i]\n                    if expected.shape[2] == 4:\n                        expected = expected[..., 0:3]\n                    assert np.array_equal(\n                        mock_cs.call_args_list[i-2][0][0],\n                        expected\n                    )\n\n                    # for some unclear reason, call_args_list here seems to\n                    # contain the output instead of the input to\n                    # side_effect_all_channel_clahe, so this assert is\n                    # deactivated for now\n                    # assert np.array_equal(\n                    #     mock_all_channel_clahe.call_args_list[0][0][0][i],\n                    #     (expected + 1)[..., 0:1]\n                    # )\n\n                    exp = (expected + 1)\n                    exp[..., 0:1] += 2\n                    assert np.array_equal(\n                        mock_cs.call_args_list[3+i-2][0][0],\n                        exp\n                    )\n\n    def test_many_images_rgb_to_lab_list_without_3d_images(self):\n        self._test_many_images_rgb_to_lab_list(with_3d_images=False)\n\n    def test_many_images_rgb_to_lab_list_with_3d_images(self):\n        self._test_many_images_rgb_to_lab_list(with_3d_images=True)\n\n    @classmethod\n    def _test_many_images_rgb_to_lab_array(cls, nb_channels, nb_images):\n        fname_cs = \"imgaug.augmenters.color.change_colorspace_\"\n        with mock.patch(fname_cs) as mock_cs:\n            with_color_conversion = (\n                True if nb_channels is not None and nb_channels in [3, 4]\n                else False)\n\n            img = [\n                [0, 1, 2, 3, 4],\n                [5, 6, 7, 8, 9],\n                [10, 11, 12, 13, 14]\n            ]\n            img = np.uint8(img)\n            if nb_channels is not None:\n                img = np.tile(img[..., np.newaxis], (1, 1, nb_channels))\n\n            imgs = [img] * nb_images\n            imgs = np.uint8(imgs)\n\n            def side_effect_change_colorspace(image, _to_colorspace,\n                                              _from_colorspace):\n                return image + 1\n\n            def side_effect_all_channel_clahe(batch_call, _random_state,\n                                              _parents, _hooks):\n                batch_call = batch_call.deepcopy()\n                batch_call.images = [image + 2 for image in batch_call.images]\n                return batch_call\n\n            mock_cs.side_effect = side_effect_change_colorspace\n\n            mock_all_channel_clahe = ArgCopyingMagicMock()\n            mock_all_channel_clahe._augment_batch_.side_effect = \\\n                side_effect_all_channel_clahe\n\n            clahe = iaa.CLAHE(\n                clip_limit=1,\n                tile_grid_size_px=3,\n                tile_grid_size_px_min=2,\n                from_colorspace=iaa.CSPACE_RGB,\n                to_colorspace=iaa.CSPACE_Lab)\n            clahe.all_channel_clahe = mock_all_channel_clahe\n\n            imgs_aug = clahe.augment_images(imgs)\n            assert ia.is_np_array(imgs_aug)\n\n            assert mock_cs.call_count == (2*nb_images\n                                          if with_color_conversion\n                                          else 0)\n            assert (\n                mock_all_channel_clahe\n                ._augment_batch_\n                .call_count\n                == 1)\n\n            # indices: call 0, args, arg 0\n            assert isinstance(\n                mock_all_channel_clahe\n                ._augment_batch_\n                .call_args_list[0][0][0],\n                _BatchInAugmentation)\n\n            assert (\n                len(\n                    mock_all_channel_clahe\n                    ._augment_batch_\n                    .call_args_list[0][0][0]\n                    .images)\n                == nb_images)\n\n            # indices: call 0, args, arg 0, image i in list of images\n            if not with_color_conversion:\n                for i in sm.xrange(nb_images):\n                    expected = imgs[i]\n                    if expected.ndim == 2:\n                        expected = expected[..., np.newaxis]\n                    # cant have 4 channels and no color conversion for RGB2Lab\n\n                    assert np.array_equal(\n                        mock_all_channel_clahe\n                        ._augment_batch_\n                        .call_args_list[0][0][0]\n                        .images[i],\n                        expected\n                    )\n            else:\n                for i in sm.xrange(nb_images):\n                    expected = imgs[i]\n                    if expected.shape[2] == 4:\n                        expected = expected[..., 0:3]\n                    # cant have color conversion for RGB2Lab and no channel\n                    # axis\n\n                    assert np.array_equal(\n                        mock_cs.call_args_list[i][0][0],\n                        expected\n                    )\n\n                    # for some unclear reason, call_args_list here seems to\n                    # contain the output instead of the input to\n                    # side_effect_all_channel_clahe, so this assert is\n                    # deactivated for now\n                    # assert np.array_equal(\n                    #     mock_all_channel_clahe.call_args_list[0][0][0][i],\n                    #     (expected + 1)[..., 0:1]\n                    # )\n\n                    exp = (expected + 1)\n                    exp[..., 0:1] += 2\n                    assert np.array_equal(\n                        mock_cs.call_args_list[nb_images+i][0][0],\n                        exp\n                    )\n\n    def test_many_images_rgb_to_lab_array(self):\n        gen = itertools.product([None, 1, 3, 4], [1, 2, 4])\n        for nb_channels, nb_images in gen:\n            with self.subTest(nb_channels=nb_channels, nb_images=nb_images):\n                self._test_many_images_rgb_to_lab_array(\n                    nb_channels=nb_channels,\n                    nb_images=nb_images)\n\n    def test_determinism(self):\n        clahe = iaa.CLAHE(clip_limit=(1, 100),\n                          tile_grid_size_px=(3, 60),\n                          tile_grid_size_px_min=2,\n                          from_colorspace=iaa.CSPACE_RGB,\n                          to_colorspace=iaa.CSPACE_Lab)\n\n        for nb_channels in [None, 1, 3, 4]:\n            with self.subTest(nb_channels=nb_channels):\n                img = np.random.randint(0, 255, (128, 128), dtype=np.uint8)\n                if nb_channels is not None:\n                    img = np.tile(img[..., np.newaxis], (1, 1, nb_channels))\n\n                all_same = True\n                for _ in sm.xrange(10):\n                    result1 = clahe.augment_image(img)\n                    result2 = clahe.augment_image(img)\n                    same = np.array_equal(result1, result2)\n                    all_same = all_same and same\n                    if not all_same:\n                        break\n                assert not all_same\n\n                clahe_det = clahe.to_deterministic()\n                all_same = True\n                for _ in sm.xrange(10):\n                    result1 = clahe_det.augment_image(img)\n                    result2 = clahe_det.augment_image(img)\n                    same = np.array_equal(result1, result2)\n                    all_same = all_same and same\n                    if not all_same:\n                        break\n                assert all_same\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0, 3),\n            (0, 1, 3),\n            (1, 0, 3)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 129, dtype=np.uint8)\n                aug = iaa.CLAHE()\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_get_parameters(self):\n        clahe = iaa.CLAHE(\n            clip_limit=1,\n            tile_grid_size_px=3,\n            tile_grid_size_px_min=2,\n            from_colorspace=iaa.CSPACE_BGR,\n            to_colorspace=iaa.CSPACE_HSV)\n        params = clahe.get_parameters()\n        assert params[0].value == 1\n        assert params[1][0].value == 3\n        assert params[1][1] is None\n        assert params[2] == 2\n        assert params[3] == iaa.CSPACE_BGR\n        assert params[4] == iaa.CSPACE_HSV\n\n    def test_pickleable(self):\n        aug = iaa.CLAHE(clip_limit=(30, 50),\n                        tile_grid_size_px=(4, 12),\n                        seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(100, 100, 3))\n\n\nclass TestAllChannelsHistogramEqualization(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_basic_functionality(self):\n        gen = itertools.product([None, 1, 2, 3], [1, 2, 3], [False, True])\n        for nb_channels, nb_images, is_array in gen:\n            with self.subTest(nb_channels=nb_channels, nb_images=nb_images,\n                              is_array=is_array):\n                img = [\n                    [0, 1, 2, 3],\n                    [4, 5, 6, 7],\n                    [8, 9, 10, 11],\n                    [12, 13, 14, 15],\n                    [16, 17, 18, 19]\n                ]\n                img = np.uint8(img)\n                if nb_channels is not None:\n                    img = np.tile(img[..., np.newaxis], (1, 1, nb_channels))\n\n                imgs = [img] * nb_images\n                if is_array:\n                    imgs = np.uint8(imgs)\n\n                def _side_effect(img_call):\n                    return img_call + 1\n\n                mock_equalizeHist = mock.MagicMock(side_effect=_side_effect)\n                with mock.patch('cv2.equalizeHist', mock_equalizeHist):\n                    aug = iaa.AllChannelsHistogramEqualization()\n                    imgs_aug = aug.augment_images(imgs)\n                if is_array:\n                    assert ia.is_np_array(imgs_aug)\n                else:\n                    assert isinstance(imgs_aug, list)\n                assert len(imgs_aug) == nb_images\n                for i in sm.xrange(nb_images):\n                    assert imgs_aug[i].dtype.name == \"uint8\"\n                    assert np.array_equal(imgs_aug[i], imgs[i] + 1)\n\n    def test_basic_functionality_integrationtest(self):\n        nb_channels = 3\n        nb_images = 2\n\n        img = [\n            [0, 1, 2, 3],\n            [4, 5, 6, 7],\n            [8, 9, 10, 11],\n            [12, 13, 14, 15],\n            [16, 17, 18, 19]\n        ]\n        img = np.uint8(img)\n        img = np.tile(img[..., np.newaxis], (1, 1, nb_channels))\n\n        imgs = [img] * nb_images\n        imgs = np.uint8(imgs)\n        imgs[1][3:, ...] = 0\n\n        aug = iaa.AllChannelsHistogramEqualization()\n        imgs_aug = aug.augment_images(imgs)\n        assert imgs_aug.dtype.name == \"uint8\"\n        assert len(imgs_aug) == nb_images\n        for i in sm.xrange(nb_images):\n            assert imgs_aug[i].shape == img.shape\n            assert np.max(imgs_aug[i]) > np.max(img)\n        assert len(np.unique(imgs_aug[0])) > len(np.unique(imgs_aug[1]))\n\n    def test_other_dtypes(self):\n        aug = iaa.AllChannelsHistogramEqualization()\n\n        # np.uint16: cv2.error: OpenCV(3.4.5) (...)/histogram.cpp:3345:\n        #            error: (-215:Assertion failed)\n        #            src.type() == CV_8UC1 in function 'equalizeHist'\n        # np.uint32: TypeError: src data type = 6 is not supported\n        # np.uint64: see np.uint16\n        # np.int8: see np.uint16\n        # np.int16: see np.uint16\n        # np.int32: see np.uint16\n        # np.int64: see np.uint16\n        # np.float16: TypeError: src data type = 23 is not supported\n        # np.float32: see np.uint16\n        # np.float64: see np.uint16\n        # np.float128: TypeError: src data type = 13 is not supported\n        for dtype in [np.uint8]:\n            with self.subTest(dtype=np.dtype(dtype).name):\n                min_value, _center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                dynamic_range = max_value + abs(min_value)\n                if np.dtype(dtype).kind == \"f\":\n                    img = np.zeros((16,), dtype=dtype)\n                    for i in sm.xrange(16):\n                        img[i] = min_value + i * (0.01 * dynamic_range)\n                    img = img.reshape((4, 4))\n                else:\n                    img = np.arange(\n                        min_value, min_value + 16, dtype=dtype).reshape((4, 4))\n                img_aug = aug.augment_image(img)\n                assert img_aug.dtype.name == np.dtype(dtype).name\n                assert img_aug.shape == img.shape\n                assert np.min(img_aug) < min_value + 0.1 * dynamic_range\n                assert np.max(img_aug) > max_value - 0.1 * dynamic_range\n\n    def test_keypoints_not_changed(self):\n        aug = iaa.AllChannelsHistogramEqualization()\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(1, 1)], shape=(3, 3, 3))\n        kpsoi_aug = aug.augment_keypoints([kpsoi])\n        assert keypoints_equal([kpsoi], kpsoi_aug)\n\n    def test_heatmaps_not_changed(self):\n        aug = iaa.AllChannelsHistogramEqualization()\n        heatmaps_arr = np.zeros((3, 3, 1), dtype=np.float32) + 0.5\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n        heatmaps_aug = aug.augment_heatmaps([heatmaps])[0]\n        assert np.allclose(heatmaps.arr_0to1, heatmaps_aug.arr_0to1)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 129, dtype=np.uint8)\n                aug = iaa.AllChannelsHistogramEqualization()\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 129, dtype=np.uint8)\n                aug = iaa.AllChannelsHistogramEqualization()\n\n                image_aug = aug(image=image)\n\n                assert np.any(image_aug != 128)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_get_parameters(self):\n        aug = iaa.AllChannelsHistogramEqualization()\n        params = aug.get_parameters()\n        assert len(params) == 0\n\n    def test_pickleable(self):\n        aug = iaa.AllChannelsHistogramEqualization(seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=2, shape=(100, 100, 3))\n\n\nclass TestHistogramEqualization(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_init(self):\n        aug = iaa.HistogramEqualization(\n            from_colorspace=iaa.CSPACE_BGR,\n            to_colorspace=iaa.CSPACE_HSV)\n        assert isinstance(\n            aug.all_channel_histogram_equalization,\n            iaa.AllChannelsHistogramEqualization)\n\n        icba = aug.intensity_channel_based_applier\n        assert icba.from_colorspace == iaa.CSPACE_BGR\n        assert icba.to_colorspace == iaa.CSPACE_HSV\n\n    def test_basic_functionality_integrationtest(self):\n        for nb_channels in [None, 1, 3, 4, 5]:\n            with self.subTest(nb_channels=nb_channels):\n                img = [\n                    [0, 1, 2, 3, 4],\n                    [5, 6, 7, 8, 9],\n                    [10, 11, 12, 13, 14]\n                ]\n                img = np.uint8(img)\n                if nb_channels is not None:\n                    img = np.tile(img[..., np.newaxis], (1, 1, nb_channels))\n                    if nb_channels >= 3:\n                        img[..., 1] += 10\n                        img[..., 2] += 20\n\n                aug = iaa.HistogramEqualization(\n                    from_colorspace=iaa.CSPACE_BGR,\n                    to_colorspace=iaa.CSPACE_HSV,\n                    name=\"ExampleHistEq\")\n\n                if nb_channels is None or nb_channels != 5:\n                    img_aug = aug.augment_image(img)\n                else:\n                    with warnings.catch_warnings(record=True) as caught_warns:\n                        warnings.simplefilter(\"always\")\n                        img_aug = aug.augment_image(img)\n                        assert len(caught_warns) == 1\n                        assert (\n                            \"Got image with 5 channels in \"\n                            \"_IntensityChannelBasedApplier (parents: \"\n                            \"ExampleHistEq)\"\n                            in str(caught_warns[-1].message)\n                        )\n\n                expected = img\n                if nb_channels is None or nb_channels == 1:\n                    expected = cv2.equalizeHist(expected)\n                    if nb_channels == 1:\n                        expected = expected[..., np.newaxis]\n                elif nb_channels == 5:\n                    for c in sm.xrange(expected.shape[2]):\n                        expected[..., c:c+1] = cv2.equalizeHist(\n                            expected[..., c]\n                        )[..., np.newaxis]\n                else:\n                    if nb_channels == 4:\n                        expected = expected[..., 0:3]\n                    expected = cv2.cvtColor(expected, cv2.COLOR_RGB2HSV)\n                    expected[..., 2] = cv2.equalizeHist(expected[..., 2])\n                    expected = cv2.cvtColor(expected, cv2.COLOR_HSV2RGB)\n                    if nb_channels == 4:\n                        expected = np.concatenate(\n                            (expected, img[..., 3:4]), axis=2)\n\n                assert np.array_equal(img_aug, expected)\n\n    def test_determinism(self):\n        aug = iaa.HistogramEqualization(\n            from_colorspace=iaa.CSPACE_RGB,\n            to_colorspace=iaa.CSPACE_Lab)\n\n        for nb_channels in [None, 1, 3, 4]:\n            with self.subTest(nb_channels=nb_channels):\n                img = np.random.randint(0, 255, (128, 128), dtype=np.uint8)\n                if nb_channels is not None:\n                    img = np.tile(img[..., np.newaxis], (1, 1, nb_channels))\n\n                aug_det = aug.to_deterministic()\n                result1 = aug_det.augment_image(img)\n                result2 = aug_det.augment_image(img)\n                assert np.array_equal(result1, result2)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0, 3),\n            (0, 1, 3),\n            (1, 0, 3)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 129, dtype=np.uint8)\n                aug = iaa.HistogramEqualization()\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_get_parameters(self):\n        aug = iaa.HistogramEqualization(\n            from_colorspace=iaa.CSPACE_BGR,\n            to_colorspace=iaa.CSPACE_HSV)\n        params = aug.get_parameters()\n        assert params[0] == iaa.CSPACE_BGR\n        assert params[1] == iaa.CSPACE_HSV\n\n    def test_pickleable(self):\n        aug = iaa.HistogramEqualization(seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=2, shape=(100, 100, 3))\n"
  },
  {
    "path": "test/augmenters/test_convolutional.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport six.moves as sm\n\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug import dtypes as iadt\nfrom imgaug import random as iarandom\nfrom imgaug.testutils import reseed, runtest_pickleable_uint8_img\n\n\n# TODO add tests for EdgeDetect\n# TODO add tests for DirectedEdgeDetect\n\n\nclass Test_convolve(unittest.TestCase):\n    def test_1x1_identity_matrix_2d_image(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50]\n        ], dtype=np.uint8)\n        matrix = np.float32([\n            [1.0]\n        ])\n\n        image_aug = iaa.convolve(image, matrix)\n\n        assert image_aug is not image\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 3)\n        assert np.array_equal(image_aug, image)\n\n\nclass Test_convolve_(unittest.TestCase):\n    def test_1x1_identity_matrix_2d_image_small_image_sizes(self):\n        for height in np.arange(16):\n            for width in np.arange(16):\n                shapes = [\n                    (height, width),\n                    (height, width, 1),\n                    (height, width, 3)\n                ]\n\n                for shape in shapes:\n                    with self.subTest(shape=shape):\n                        image = np.mod(\n                            np.arange(int(np.prod(shape))).reshape(shape),\n                            255\n                        ).astype(np.uint8)\n                        matrix = np.float32([\n                            [1.0]\n                        ])\n\n                        image_aug = iaa.convolve_(np.copy(image), matrix)\n\n                        assert image_aug.dtype.name == \"uint8\"\n                        assert image_aug.shape == shape\n                        assert np.array_equal(image_aug, image)\n\n    def test_1x1_identity_matrix_2d_image(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50]\n        ], dtype=np.uint8)\n        matrix = np.float32([\n            [1.0]\n        ])\n\n        image_aug = iaa.convolve_(np.copy(image), matrix)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 3)\n        assert np.array_equal(image_aug, image)\n\n    def test_2x2_identity_matrix_2d_image(self):\n        image = np.array([\n            [0, 10, 20, 30],\n            [40, 50, 60, 70]\n        ], dtype=np.uint8)\n        matrix = np.float32([\n            [0.0, 0.0],\n            [0.0, 1.0]\n        ])\n\n        image_aug = iaa.convolve_(np.copy(image), matrix)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 4)\n        assert np.array_equal(image_aug, image)\n\n    def test_3x3_identity_matrix_2d_image(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50]\n        ], dtype=np.uint8)\n        matrix = np.float32([\n            [0.0, 0.0, 0.0],\n            [0.0, 1.0, 0.0],\n            [0.0, 0.0, 0.0]\n        ])\n\n        image_aug = iaa.convolve_(np.copy(image), matrix)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 3)\n        assert np.array_equal(image_aug, image)\n\n    def test_single_matrix_2d_image(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50],\n            [60, 70, 80]\n        ], dtype=np.uint8)\n        matrix = np.float32([\n            [0.0, 1.0, 0.0],\n            [0.0, 1.0, 0.0],\n            [0.0, 0.0, 0.0]\n        ])\n        expected = np.array([\n            [0+30, 10+40, 20+50],\n            [30+0, 40+10, 50+20],\n            [60+30, 70+40, 80+50]\n        ], dtype=np.float32)\n\n        image_aug = iaa.convolve_(np.copy(image), matrix)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (3, 3)\n        assert np.array_equal(image_aug, expected)\n\n    def test_single_matrix_3d_image(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50]\n        ], dtype=np.uint8)\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 2))\n        matrix = np.float32([\n            [0.0, 0.0, 0.0],\n            [0.0, 2.0, 0.0],\n            [0.0, 0.0, 0.0]\n        ])\n\n        image_aug = iaa.convolve_(np.copy(image), matrix)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 3, 2)\n        assert np.array_equal(image_aug, 2*image)\n\n    def test_matrix_is_list_of_arrays(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50]\n        ], dtype=np.uint8)\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 2))\n        matrices = [\n            np.float32([\n                [0.0, 0.0, 0.0],\n                [0.0, 1.0, 0.0],\n                [0.0, 0.0, 0.0]\n            ]),\n            np.float32([\n                [0.0, 0.0, 0.0],\n                [0.0, 2.0, 0.0],\n                [0.0, 0.0, 0.0]\n            ])\n        ]\n\n        image_aug = iaa.convolve_(np.copy(image), matrices)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 3, 2)\n        assert np.array_equal(image_aug[:, :, 0], image[:, :, 0])\n        assert np.array_equal(image_aug[:, :, 1], 2*image[:, :, 1])\n\n    def test_matrix_is_list_containing_none(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50]\n        ], dtype=np.uint8)\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 2))\n        matrices = [\n            None,\n            np.float32([\n                [0.0, 0.0, 0.0],\n                [0.0, 2.0, 0.0],\n                [0.0, 0.0, 0.0]\n            ])\n        ]\n\n        image_aug = iaa.convolve_(np.copy(image), matrices)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 3, 2)\n        assert np.array_equal(image_aug[:, :, 0], image[:, :, 0])\n        assert np.array_equal(image_aug[:, :, 1], 2*image[:, :, 1])\n\n    def test_matrix_is_list_containing_only_none(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50]\n        ], dtype=np.uint8)\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 2))\n        matrices = [\n            None,\n            None\n        ]\n\n        image_aug = iaa.convolve_(np.copy(image), matrices)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 3, 2)\n        assert np.array_equal(image_aug[:, :, 0], image[:, :, 0])\n        assert np.array_equal(image_aug[:, :, 1], image[:, :, 1])\n\n    def test_unusual_channel_numbers(self):\n        for nb_channels in [1, 2, 3, 4, 5, 10, 512, 513]:\n            with self.subTest(nb_channels=nb_channels):\n                image = np.array([\n                    [0, 10, 20],\n                    [30, 40, 50]\n                ], dtype=np.uint8)\n                image = image[:, :, np.newaxis]\n                image = np.tile(image, (1, 1, nb_channels))\n                matrix = np.float32([\n                    [2.0]\n                ])\n\n                image_aug = iaa.convolve_(np.copy(image), matrix)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == (2, 3, nb_channels)\n                assert np.array_equal(image_aug, 2*image)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                matrix = np.float32([\n                    [2.0]\n                ])\n\n                image_aug = iaa.convolve_(np.copy(image), matrix)\n\n                assert image_aug.shape == image.shape\n\n    def test_view_heightwise(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50]\n        ], dtype=np.uint8)\n        image_view = np.copy(image)[:2, :]\n        assert image_view.flags[\"OWNDATA\"] is False\n        matrix = np.float32([\n            [2.0]\n        ])\n\n        image_aug = iaa.convolve_(image_view, matrix)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 3)\n        assert np.array_equal(image_aug, 2*image)\n\n    def test_view_channelwise_1_channel(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50]\n        ], dtype=np.uint8)\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 3))\n        image[:, :, 0] += 0\n        image[:, :, 1] += 1\n        image[:, :, 2] += 2\n        image_view = np.copy(image)[:, :, [False, True, False]]\n        assert image_view.flags[\"OWNDATA\"] is False\n        assert image_view.base.shape == (1, 2, 3)\n        matrix = np.float32([\n            [2.0]\n        ])\n\n        image_aug = iaa.convolve_(image_view, matrix)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 3, 1)\n        assert np.array_equal(image_aug, 2*image[:, :, 1:2])\n\n    def test_view_channelwise_4_channels(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50]\n        ], dtype=np.uint8)\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 6))\n        image[:, :, 0] += 0\n        image[:, :, 1] += 1\n        image[:, :, 2] += 2\n        mask = [False, True, True, True, True, False]\n        image_view = np.copy(image)[:, :, mask]\n        assert image_view.flags[\"OWNDATA\"] is False\n        assert image_view.base.shape == (4, 2, 3)\n        matrix = np.float32([\n            [2.0]\n        ])\n\n        image_aug = iaa.convolve_(image_view, matrix)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 3, 4)\n        assert np.array_equal(image_aug, 2*image[:, :, mask])\n\n    def test_noncontiguous(self):\n        image = np.array([\n            [0, 10, 20],\n            [30, 40, 50]\n        ], dtype=np.uint8)\n        image_nonc = np.array(image, dtype=np.uint8, order=\"F\")\n        assert image_nonc.flags[\"C_CONTIGUOUS\"] is False\n        matrix = np.float32([\n            [2.0]\n        ])\n\n        image_aug = iaa.convolve_(image_nonc, matrix)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 3)\n        assert np.array_equal(image_aug, 2*image)\n\n\n# TODO add test for keypoints once their handling was improved in Convolve\nclass TestConvolve(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def img(self):\n        return np.array([\n            [1, 2, 3],\n            [4, 5, 6],\n            [7, 8, 9]\n        ], dtype=np.uint8)\n\n    def test_matrix_is_none(self):\n        aug = iaa.Convolve(matrix=None)\n        observed = aug.augment_image(self.img)\n        assert np.array_equal(observed, self.img)\n\n    def test_matrix_is_lambda_none(self):\n        def _matrix_generator(_img, _nb_channels, _random_state):\n            return [None]\n\n        aug = iaa.Convolve(matrix=_matrix_generator)\n        observed = aug.augment_image(self.img)\n        assert np.array_equal(observed, self.img)\n\n    def test_matrix_is_1x1_identity(self):\n        # matrix is [[1]]\n        aug = iaa.Convolve(matrix=np.float32([[1]]))\n        observed = aug.augment_image(self.img)\n        assert np.array_equal(observed, self.img)\n\n    def test_matrix_is_lambda_1x1_identity(self):\n        def _matrix_generator(_img, _nb_channels, _random_state):\n            return np.float32([[1]])\n\n        aug = iaa.Convolve(matrix=_matrix_generator)\n        observed = aug.augment_image(self.img)\n        assert np.array_equal(observed, self.img)\n\n    def test_matrix_is_3x3_identity(self):\n        m = np.float32([\n            [0, 0, 0],\n            [0, 1, 0],\n            [0, 0, 0]\n        ])\n        aug = iaa.Convolve(matrix=m)\n        observed = aug.augment_image(self.img)\n        assert np.array_equal(observed, self.img)\n\n    def test_matrix_is_lambda_3x3_identity(self):\n        def _matrix_generator(_img, _nb_channels, _random_state):\n            return np.float32([\n                [0, 0, 0],\n                [0, 1, 0],\n                [0, 0, 0]\n            ])\n        aug = iaa.Convolve(matrix=_matrix_generator)\n        observed = aug.augment_image(self.img)\n        assert np.array_equal(observed, self.img)\n\n    def test_matrix_is_3x3_two_in_center(self):\n        m = np.float32([\n            [0, 0, 0],\n            [0, 2, 0],\n            [0, 0, 0]\n        ])\n        aug = iaa.Convolve(matrix=m)\n        observed = aug.augment_image(self.img)\n        assert np.array_equal(observed, 2*self.img)\n\n    def test_matrix_is_lambda_3x3_two_in_center(self):\n        def _matrix_generator(_img, _nb_channels, _random_state):\n            return np.float32([\n                [0, 0, 0],\n                [0, 2, 0],\n                [0, 0, 0]\n            ])\n\n        aug = iaa.Convolve(matrix=_matrix_generator)\n        observed = aug.augment_image(self.img)\n        assert np.array_equal(observed, 2*self.img)\n\n    def test_matrix_is_3x3_two_in_center_3_channels(self):\n        m = np.float32([\n            [0, 0, 0],\n            [0, 2, 0],\n            [0, 0, 0]\n        ])\n        aug = iaa.Convolve(matrix=m)\n        img3 = np.tile(self.img[..., np.newaxis], (1, 1, 3))  # 3 channels\n        observed = aug.augment_image(img3)\n        assert np.array_equal(observed, 2*img3)\n\n    def test_matrix_is_lambda_3x3_two_in_center_3_channels(self):\n        def _matrix_generator(_img, _nb_channels, _random_state):\n            return np.float32([\n                [0, 0, 0],\n                [0, 2, 0],\n                [0, 0, 0]\n            ])\n\n        aug = iaa.Convolve(matrix=_matrix_generator)\n        img3 = np.tile(self.img[..., np.newaxis], (1, 1, 3))  # 3 channels\n        observed = aug.augment_image(img3)\n        assert np.array_equal(observed, 2*img3)\n\n    def test_matrix_is_3x3_with_multiple_nonzero_values(self):\n        m = np.float32([\n            [0, -1, 0],\n            [0, 10, 0],\n            [0, 0, 0]\n        ])\n        expected = np.uint8([\n            [10*1+(-1)*4, 10*2+(-1)*5, 10*3+(-1)*6],\n            [10*4+(-1)*1, 10*5+(-1)*2, 10*6+(-1)*3],\n            [10*7+(-1)*4, 10*8+(-1)*5, 10*9+(-1)*6]\n        ])\n\n        aug = iaa.Convolve(matrix=m)\n        observed = aug.augment_image(self.img)\n        assert np.array_equal(observed, expected)\n\n    def test_matrix_is_lambda_3x3_with_multiple_nonzero_values(self):\n        def _matrix_generator(_img, _nb_channels, _random_state):\n            return np.float32([\n                [0, -1, 0],\n                [0, 10, 0],\n                [0, 0, 0]\n            ])\n\n        expected = np.uint8([\n            [10*1+(-1)*4, 10*2+(-1)*5, 10*3+(-1)*6],\n            [10*4+(-1)*1, 10*5+(-1)*2, 10*6+(-1)*3],\n            [10*7+(-1)*4, 10*8+(-1)*5, 10*9+(-1)*6]\n        ])\n\n        aug = iaa.Convolve(matrix=_matrix_generator)\n        observed = aug.augment_image(self.img)\n        assert np.array_equal(observed, expected)\n\n    def test_lambda_with_changing_matrices(self):\n        # changing matrices when using callable\n        def _matrix_generator(_img, _nb_channels, random_state):\n            return np.float32([[\n                iarandom.polyfill_integers(random_state, 0, 5)\n            ]])\n\n        expected = []\n        for i in sm.xrange(5):\n            expected.append(self.img * i)\n\n        aug = iaa.Convolve(matrix=_matrix_generator)\n        seen = [False] * 5\n        for _ in sm.xrange(200):\n            observed = aug.augment_image(self.img)\n            found = False\n            for i, expected_i in enumerate(expected):\n                if np.array_equal(observed, expected_i):\n                    seen[i] = True\n                    found = True\n                    break\n            assert found\n            if all(seen):\n                break\n        assert np.all(seen)\n\n    def test_matrix_has_bad_datatype(self):\n        # don't use assertRaisesRegex, because it doesnt exist in 2.7\n        got_exception = False\n        try:\n            _aug = iaa.Convolve(matrix=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Convolve(matrix=np.float32([[1]]))\n\n                image_aug = aug(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_get_parameters(self):\n        matrix = np.int32([[1]])\n        aug = iaa.Convolve(matrix=matrix)\n        params = aug.get_parameters()\n        assert np.array_equal(params[0], matrix)\n        assert params[1] == \"constant\"\n\n    def test_other_dtypes_bool_identity_matrix(self):\n        identity_matrix = np.int64([[1]])\n        aug = iaa.Convolve(matrix=identity_matrix)\n\n        image = np.zeros((3, 3), dtype=bool)\n        image[1, 1] = True\n        image_aug = aug.augment_image(image)\n        assert image.dtype.type == np.bool_\n        assert np.all(image_aug == image)\n\n    def test_other_dtypes_uint_int_identity_matrix(self):\n        identity_matrix = np.int64([[1]])\n        aug = iaa.Convolve(matrix=identity_matrix)\n\n        for dtype in [np.uint8, np.uint16, np.int8, np.int16]:\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = 100\n            image_aug = aug.augment_image(image)\n            assert image.dtype.type == dtype\n            assert np.all(image_aug == image)\n\n    def test_other_dtypes_float_identity_matrix(self):\n        identity_matrix = np.int64([[1]])\n        aug = iaa.Convolve(matrix=identity_matrix)\n\n        for dtype in [np.float16, np.float32, np.float64]:\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = 100.0\n            image_aug = aug.augment_image(image)\n            assert image.dtype.type == dtype\n            assert np.allclose(image_aug, image)\n\n    def test_other_dtypes_bool_non_identity_matrix_with_small_values(self):\n        matrix = np.float64([\n            [0, 0.6, 0],\n            [0, 0.4, 0],\n            [0,   0, 0]\n        ])\n        aug = iaa.Convolve(matrix=matrix)\n\n        image = np.zeros((3, 3), dtype=bool)\n        image[1, 1] = True\n        image[2, 1] = True\n        expected = np.zeros((3, 3), dtype=bool)\n        expected[0, 1] = True\n        expected[2, 1] = True\n        image_aug = aug.augment_image(image)\n        assert image.dtype.type == np.bool_\n        assert np.all(image_aug == expected)\n\n    def test_other_dtypes_uint_int_non_identity_matrix_with_small_values(self):\n        matrix = np.float64([\n            [0, 0.5, 0],\n            [0, 0.5, 0],\n            [0,   0, 0]\n        ])\n        aug = iaa.Convolve(matrix=matrix)\n\n        for dtype in [np.uint8, np.uint16, np.int8, np.int16]:\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = 100\n            image[2, 1] = 100\n            image_aug = aug.augment_image(image)\n\n            expected = np.zeros((3, 3), dtype=dtype)\n            expected[0, 1] = int(np.round(100 * 0.5))\n            expected[1, 1] = int(np.round(100 * 0.5))\n            expected[2, 1] = int(np.round(100 * 0.5 + 100 * 0.5))\n\n            diff = np.abs(\n                image_aug.astype(np.int64)\n                - expected.astype(np.int64))\n            assert image_aug.dtype.type == dtype\n            assert np.max(diff) <= 2\n\n    def test_other_dtypes_float_non_identity_matrix_with_small_values(self):\n        matrix = np.float64([\n            [0, 0.5, 0],\n            [0, 0.5, 0],\n            [0,   0, 0]\n        ])\n        aug = iaa.Convolve(matrix=matrix)\n\n        for dtype in [np.float16, np.float32, np.float64]:\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = 100.0\n            image[2, 1] = 100.0\n            image_aug = aug.augment_image(image)\n\n            expected = np.zeros((3, 3), dtype=dtype)\n            expected[0, 1] = 100 * 0.5\n            expected[1, 1] = 100 * 0.5\n            expected[2, 1] = 100 * 0.5 + 100 * 0.5\n\n            diff = np.abs(\n                image_aug.astype(np.float64) - expected.astype(np.float64)\n            )\n            assert image_aug.dtype.type == dtype\n            assert np.max(diff) < 1.0\n\n    def test_other_dtypes_uint_int_non_identity_matrix_with_large_values(self):\n        matrix = np.float64([\n            [0, 0.5, 0],\n            [0, 0.5, 0],\n            [0,   0, 0]\n        ])\n        aug = iaa.Convolve(matrix=matrix)\n\n        for dtype in [np.uint8, np.uint16, np.int8, np.int16]:\n            _min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            value = int(center_value + 0.4 * max_value)\n\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = value\n            image[2, 1] = value\n            image_aug = aug.augment_image(image)\n\n            expected = np.zeros((3, 3), dtype=dtype)\n            expected[0, 1] = int(np.round(value * 0.5))\n            expected[1, 1] = int(np.round(value * 0.5))\n            expected[2, 1] = int(np.round(value * 0.5 + value * 0.5))\n\n            diff = np.abs(\n                image_aug.astype(np.int64)\n                - expected.astype(np.int64))\n            assert image_aug.dtype.type == dtype\n            assert np.max(diff) <= 2\n\n    def test_other_dtypes_float_non_identity_matrix_with_large_values(self):\n        matrix = np.float64([\n            [0, 0.5, 0],\n            [0, 0.5, 0],\n            [0,   0, 0]\n        ])\n        aug = iaa.Convolve(matrix=matrix)\n\n        for dtype, value in zip([np.float16, np.float32, np.float64],\n                                [5000, 1000*1000, 1000*1000*1000]):\n            image = np.zeros((3, 3), dtype=dtype)\n            image[1, 1] = value\n            image[2, 1] = value\n            image_aug = aug.augment_image(image)\n\n            expected = np.zeros((3, 3), dtype=dtype)\n            expected[0, 1] = value * 0.5\n            expected[1, 1] = value * 0.5\n            expected[2, 1] = value * 0.5 + value * 0.5\n\n            diff = np.abs(\n                image_aug.astype(np.float64) - expected.astype(np.float64))\n            assert image_aug.dtype.type == dtype\n            assert np.max(diff) < 1.0\n\n    def test_failure_on_invalid_dtypes(self):\n        # don't use assertRaisesRegex, because it doesnt exist in 2.7\n        identity_matrix = np.int64([[1]])\n        aug = iaa.Convolve(matrix=identity_matrix)\n        for dt in [np.uint32, np.uint64, np.int32, np.int64]:\n            got_exception = False\n            try:\n                _ = aug.augment_image(np.zeros((1, 1), dtype=dt))\n            except Exception as exc:\n                assert \"forbidden dtype\" in str(exc)\n                got_exception = True\n            assert got_exception\n\n    def test_pickleable__identity_matrix(self):\n        identity_matrix = np.int64([[1]])\n        aug = iaa.Convolve(identity_matrix, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n    def test_pickleable__callback_function(self):\n        aug = iaa.Convolve(_convolve_pickleable_matrix_generator,\n                           seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\ndef _convolve_pickleable_matrix_generator(_img, _nb_channels, random_state):\n    return np.float32([[random_state.integers(0, 5)]])\n\n\nclass TestSharpen(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @classmethod\n    def _compute_sharpened_base_img(cls, lightness, m):\n        img = np.zeros((3, 3), dtype=np.float32)\n        k = 1\n        # note that cv2 uses reflection padding by default\n        img[0, 0] = (\n            (m[1, 1] + lightness)/k * 10\n            + 4 * (m[0, 0]/k) * 10\n            + 4 * (m[2, 2]/k) * 20\n        )\n        img[0, 2] = img[0, 0]\n        img[2, 0] = img[0, 0]\n        img[2, 2] = img[0, 0]\n        img[0, 1] = (\n            (m[1, 1] + lightness)/k * 10\n            + 6 * (m[0, 1]/k) * 10\n            + 2 * (m[2, 2]/k) * 20\n        )\n        img[1, 0] = img[0, 1]\n        img[1, 2] = img[0, 1]\n        img[2, 1] = img[0, 1]\n        img[1, 1] = (\n            (m[1, 1] + lightness)/k * 20\n            + 8 * (m[0, 1]/k) * 10\n        )\n\n        img = np.clip(img, 0, 255).astype(np.uint8)\n\n        return img\n\n    @property\n    def base_img(self):\n        base_img = [[10, 10, 10],\n                    [10, 20, 10],\n                    [10, 10, 10]]\n        base_img = np.uint8(base_img)\n        return base_img\n\n    @property\n    def base_img_sharpened(self):\n        return self._compute_sharpened_base_img(1, self.m)\n\n    @property\n    def m(self):\n        return np.array([[-1, -1, -1],\n                         [-1, 8, -1],\n                         [-1, -1, -1]], dtype=np.float32)\n\n    @property\n    def m_noop(self):\n        return np.array([[0, 0, 0],\n                         [0, 1, 0],\n                         [0, 0, 0]], dtype=np.float32)\n\n    def test_alpha_zero(self):\n        aug = iaa.Sharpen(alpha=0, lightness=1)\n        observed = aug.augment_image(self.base_img)\n        expected = self.base_img\n        assert np.allclose(observed, expected)\n\n    def test_alpha_one(self):\n        aug = iaa.Sharpen(alpha=1.0, lightness=1)\n        observed = aug.augment_image(self.base_img)\n        expected = self.base_img_sharpened\n        assert np.allclose(observed, expected)\n\n    def test_alpha_050(self):\n        aug = iaa.Sharpen(alpha=0.5, lightness=1)\n        observed = aug.augment_image(self.base_img)\n        expected = self._compute_sharpened_base_img(\n            0.5*1, 0.5 * self.m_noop + 0.5 * self.m)\n        assert np.allclose(observed, expected.astype(np.uint8))\n\n    def test_alpha_075(self):\n        aug = iaa.Sharpen(alpha=0.75, lightness=1)\n        observed = aug.augment_image(self.base_img)\n        expected = self._compute_sharpened_base_img(\n            0.75*1, 0.25 * self.m_noop + 0.75 * self.m)\n        assert np.allclose(observed, expected)\n\n    def test_alpha_is_stochastic_parameter(self):\n        aug = iaa.Sharpen(alpha=iap.Choice([0.5, 1.0]), lightness=1)\n        observed = aug.augment_image(self.base_img)\n        expected1 = self._compute_sharpened_base_img(\n            0.5*1, 0.5 * self.m_noop + 0.5 * self.m)\n        expected2 = self._compute_sharpened_base_img(\n            1.0*1, 0.0 * self.m_noop + 1.0 * self.m)\n        assert (\n            np.allclose(observed, expected1)\n            or np.allclose(observed, expected2)\n        )\n\n    def test_failure_if_alpha_has_bad_datatype(self):\n        # don't use assertRaisesRegex, because it doesnt exist in 2.7\n        got_exception = False\n        try:\n            _ = iaa.Sharpen(alpha=\"test\", lightness=1)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_alpha_1_lightness_2(self):\n        aug = iaa.Sharpen(alpha=1.0, lightness=2)\n        observed = aug.augment_image(self.base_img)\n        expected = self._compute_sharpened_base_img(1.0*2, self.m)\n        assert np.allclose(observed, expected)\n\n    def test_alpha_1_lightness_3(self):\n        aug = iaa.Sharpen(alpha=1.0, lightness=3)\n        observed = aug.augment_image(self.base_img)\n        expected = self._compute_sharpened_base_img(1.0*3, self.m)\n        assert np.allclose(observed, expected)\n\n    def test_alpha_1_lightness_is_stochastic_parameter(self):\n        aug = iaa.Sharpen(alpha=1.0, lightness=iap.Choice([1.0, 1.5]))\n        observed = aug.augment_image(self.base_img)\n        expected1 = self._compute_sharpened_base_img(1.0*1.0, self.m)\n        expected2 = self._compute_sharpened_base_img(1.0*1.5, self.m)\n        assert (\n            np.allclose(observed, expected1)\n            or np.allclose(observed, expected2)\n        )\n\n    def test_failure_if_lightness_has_bad_datatype(self):\n        # don't use assertRaisesRegex, because it doesnt exist in 2.7\n        got_exception = False\n        try:\n            _ = iaa.Sharpen(alpha=1.0, lightness=\"test\")\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    # this part doesnt really work so far due to nonlinearities resulting\n    # from clipping to uint8\n    \"\"\"\n    # alpha range\n    aug = iaa.Sharpen(alpha=(0.0, 1.0), lightness=1)\n    base_img = np.copy(base_img)\n    base_img_sharpened_min = _compute_sharpened_base_img(\n        0.0*1, 1.0 * m_noop + 0.0 * m)\n    base_img_sharpened_max = _compute_sharpened_base_img(\n        1.0*1, 0.0 * m_noop + 1.0 * m)\n    #distance_max = np.average(\n        np.abs(\n            base_img_sharpened.astype(np.float32)\n            - base_img.astype(np.float32)\n        )\n    )\n    distance_max = np.average(\n        np.abs(\n            base_img_sharpened_max\n            - base_img_sharpened_min\n        )\n    )\n    nb_iterations = 250\n    distances = []\n    for _ in sm.xrange(nb_iterations):\n        observed = aug.augment_image(base_img)\n        distance = np.average(\n            np.abs(\n                observed.astype(np.float32)\n                - base_img_sharpened_max.astype(np.float32)\n            )\n        ) / distance_max\n        distances.append(distance)\n\n    print(distances)\n    print(min(distances), np.average(distances), max(distances))\n    assert 0 - 1e-4 < min(distances) < 0.1\n    assert 0.4 < np.average(distances) < 0.6\n    assert 0.9 < max(distances) < 1.0 + 1e-4\n\n    nb_bins = 5\n    hist, _ = np.histogram(distances, bins=nb_bins, range=(0.0, 1.0),\n                           density=False)\n    density_expected = 1.0/nb_bins\n    density_tolerance = 0.05\n    for nb_samples in hist:\n        density = nb_samples / nb_iterations\n        assert (\n            density_expected - density_tolerance\n            < density\n            < density_expected + density_tolerance)\n\n    # lightness range\n    aug = iaa.Sharpen(alpha=1.0, lightness=(0.5, 2.0))\n    base_img = np.copy(base_img)\n    base_img_sharpened = _compute_sharpened_base_img(1.0*2.0, m)\n    distance_max = np.average(\n        np.abs(\n            base_img_sharpened.astype(np.int32)\n            - base_img.astype(np.int32)\n        )\n    )\n    nb_iterations = 250\n    distances = []\n    for _ in sm.xrange(nb_iterations):\n        observed = aug.augment_image(base_img)\n        distance = np.average(\n            np.abs(\n                observed.astype(np.int32)\n                - base_img.astype(np.int32)\n            )\n        ) / distance_max\n        distances.append(distance)\n\n    assert 0 - 1e-4 < min(distances) < 0.1\n    assert 0.4 < np.average(distances) < 0.6\n    assert 0.9 < max(distances) < 1.0 + 1e-4\n\n    nb_bins = 5\n    hist, _ = np.histogram(distances, bins=nb_bins, range=(0.0, 1.0),\n                           density=False)\n    density_expected = 1.0/nb_bins\n    density_tolerance = 0.05\n    for nb_samples in hist:\n        density = nb_samples / nb_iterations\n        assert (\n            density_expected - density_tolerance\n            < density\n            < density_expected + density_tolerance)\n    \"\"\"\n\n    def test_pickleable(self):\n        aug = iaa.Sharpen(alpha=(0.0, 1.0), lightness=(1, 3), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\nclass TestEmboss(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @classmethod\n    def _compute_embossed_base_img(cls, img, alpha, strength):\n        img = np.copy(img)\n        base_img_embossed = np.zeros((3, 3), dtype=np.float32)\n\n        m = np.float32([[-1, 0, 0],\n                        [0, 1, 0],\n                        [0, 0, 1]])\n        strength_matrix = strength * np.float32([\n            [-1, -1, 0],\n            [-1, 0, 1],\n            [0, 1, 1]\n        ])\n        ms = m + strength_matrix\n\n        for i in range(base_img_embossed.shape[0]):\n            for j in range(base_img_embossed.shape[1]):\n                for u in range(ms.shape[0]):\n                    for v in range(ms.shape[1]):\n                        weight = ms[u, v]\n                        inputs_i = abs(i + (u - (ms.shape[0]-1)//2))\n                        inputs_j = abs(j + (v - (ms.shape[1]-1)//2))\n                        if inputs_i >= img.shape[0]:\n                            diff = inputs_i - (img.shape[0]-1)\n                            inputs_i = img.shape[0] - 1 - diff\n                        if inputs_j >= img.shape[1]:\n                            diff = inputs_j - (img.shape[1]-1)\n                            inputs_j = img.shape[1] - 1 - diff\n                        inputs = img[inputs_i, inputs_j]\n                        base_img_embossed[i, j] += inputs * weight\n\n        return np.clip(\n            (1-alpha) * img\n            + alpha * base_img_embossed,\n            0,\n            255\n        ).astype(np.uint8)\n\n    @classmethod\n    def _allclose(cls, a, b):\n        return np.max(\n            a.astype(np.float32)\n            - b.astype(np.float32)\n        ) <= 2.1\n\n    @property\n    def base_img(self):\n        return np.array([[10, 10, 10],\n                         [10, 20, 10],\n                         [10, 10, 15]], dtype=np.uint8)\n\n    def test_alpha_0_strength_1(self):\n        aug = iaa.Emboss(alpha=0, strength=1)\n        observed = aug.augment_image(self.base_img)\n        expected = self.base_img\n        assert self._allclose(observed, expected)\n\n    def test_alpha_1_strength_1(self):\n        aug = iaa.Emboss(alpha=1.0, strength=1)\n        observed = aug.augment_image(self.base_img)\n        expected = self._compute_embossed_base_img(\n            self.base_img, alpha=1.0, strength=1)\n        assert self._allclose(observed, expected)\n\n    def test_alpha_050_strength_1(self):\n        aug = iaa.Emboss(alpha=0.5, strength=1)\n        observed = aug.augment_image(self.base_img)\n        expected = self._compute_embossed_base_img(\n            self.base_img, alpha=0.5, strength=1)\n        assert self._allclose(observed, expected.astype(np.uint8))\n\n    def test_alpha_075_strength_1(self):\n        aug = iaa.Emboss(alpha=0.75, strength=1)\n        observed = aug.augment_image(self.base_img)\n        expected = self._compute_embossed_base_img(\n            self.base_img, alpha=0.75, strength=1)\n        assert self._allclose(observed, expected)\n\n    def test_alpha_stochastic_parameter_strength_1(self):\n        aug = iaa.Emboss(alpha=iap.Choice([0.5, 1.0]), strength=1)\n        observed = aug.augment_image(self.base_img)\n        expected1 = self._compute_embossed_base_img(\n            self.base_img, alpha=0.5, strength=1)\n        expected2 = self._compute_embossed_base_img(\n            self.base_img, alpha=1.0, strength=1)\n        assert (\n            self._allclose(observed, expected1)\n            or self._allclose(observed, expected2)\n        )\n\n    def test_failure_on_invalid_datatype_for_alpha(self):\n        # don't use assertRaisesRegex, because it doesnt exist in 2.7\n        got_exception = False\n        try:\n            _ = iaa.Emboss(alpha=\"test\", strength=1)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_alpha_1_strength_2(self):\n        aug = iaa.Emboss(alpha=1.0, strength=2)\n        observed = aug.augment_image(self.base_img)\n        expected = self._compute_embossed_base_img(\n            self.base_img, alpha=1.0, strength=2)\n        assert self._allclose(observed, expected)\n\n    def test_alpha_1_strength_3(self):\n        aug = iaa.Emboss(alpha=1.0, strength=3)\n        observed = aug.augment_image(self.base_img)\n        expected = self._compute_embossed_base_img(\n            self.base_img, alpha=1.0, strength=3)\n        assert self._allclose(observed, expected)\n\n    def test_alpha_1_strength_6(self):\n        aug = iaa.Emboss(alpha=1.0, strength=6)\n        observed = aug.augment_image(self.base_img)\n        expected = self._compute_embossed_base_img(\n            self.base_img, alpha=1.0, strength=6)\n        assert self._allclose(observed, expected)\n\n    def test_alpha_1_strength_stochastic_parameter(self):\n        aug = iaa.Emboss(alpha=1.0, strength=iap.Choice([1.0, 2.5]))\n        observed = aug.augment_image(self.base_img)\n        expected1 = self._compute_embossed_base_img(\n            self.base_img, alpha=1.0, strength=1.0)\n        expected2 = self._compute_embossed_base_img(\n            self.base_img, alpha=1.0, strength=2.5)\n        assert (\n            self._allclose(observed, expected1)\n            or self._allclose(observed, expected2)\n        )\n\n    def test_failure_on_invalid_datatype_for_strength(self):\n        # don't use assertRaisesRegex, because it doesnt exist in 2.7\n        got_exception = False\n        try:\n            _ = iaa.Emboss(alpha=1.0, strength=\"test\")\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_pickleable(self):\n        aug = iaa.Emboss(alpha=(0.0, 1.0), strength=(1, 3), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n"
  },
  {
    "path": "test/augmenters/test_debug.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\nimport os\ntry:\n    import cPickle as pickle\nexcept ImportError:\n    import pickle\n\nimport numpy as np\nimport cv2\nimport imageio\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import random as iarandom\nfrom imgaug.testutils import reseed, TemporaryDirectory\nimport imgaug.augmenters.debug as debuglib\n\n\nclass Test_draw_debug_image(unittest.TestCase):\n    @classmethod\n    def _find_in_image_avg_diff(cls, find_image, in_image):\n        res = cv2.matchTemplate(in_image, find_image, cv2.TM_SQDIFF)\n        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)\n\n        top_left = min_loc\n        bottom_right = (top_left[0] + find_image.shape[1],\n                        top_left[1] + find_image.shape[0])\n        image_found = in_image[top_left[1]:bottom_right[1],\n                               top_left[0]:bottom_right[0],\n                               :]\n        diff = np.abs(image_found.astype(np.float32)\n                      - find_image.astype(np.float32))\n        return np.average(diff)\n\n    @classmethod\n    def _image_contains(cls, find_image, in_image, threshold=2.0):\n        return cls._find_in_image_avg_diff(find_image, in_image) <= threshold\n\n    def test_one_image(self):\n        rng = iarandom.RNG(0)\n        image = rng.integers(0, 256, size=(256, 256, 3), dtype=np.uint8)\n\n        debug_image = iaa.draw_debug_image([image])\n\n        assert self._image_contains(image, debug_image)\n\n    def test_two_images(self):\n        rng = iarandom.RNG(0)\n        images = rng.integers(0, 256, size=(2, 256, 256, 3), dtype=np.uint8)\n\n        debug_image = iaa.draw_debug_image(images)\n\n        assert self._image_contains(images[0, ...], debug_image)\n        assert self._image_contains(images[1, ...], debug_image)\n\n    def test_two_images_of_different_sizes(self):\n        rng = iarandom.RNG(0)\n        image1 = rng.integers(0, 256, size=(256, 256, 3), dtype=np.uint8)\n        image2 = rng.integers(0, 256, size=(512, 256, 3), dtype=np.uint8)\n\n        debug_image = iaa.draw_debug_image([image1, image2])\n\n        assert self._image_contains(image1, debug_image)\n        assert self._image_contains(image2, debug_image)\n\n    def test_two_images_and_heatmaps(self):\n        rng = iarandom.RNG(0)\n        images = rng.integers(0, 256, size=(2, 256, 256, 3), dtype=np.uint8)\n        heatmap = np.zeros((256, 256, 1), dtype=np.float32)\n        heatmap[128-25:128+25, 128-25:128+25] = 1.0\n        heatmap1 = ia.HeatmapsOnImage(np.copy(heatmap), shape=images[0].shape)\n        heatmap2 = ia.HeatmapsOnImage(1.0 - heatmap, shape=images[1].shape)\n        image1_w_overlay = heatmap1.draw_on_image(images[0])[0]\n        image2_w_overlay = heatmap2.draw_on_image(images[1])[0]\n\n        debug_image = iaa.draw_debug_image(images,\n                                           heatmaps=[heatmap1, heatmap2])\n\n        assert self._image_contains(images[0, ...], debug_image)\n        assert self._image_contains(images[1, ...], debug_image)\n        assert self._image_contains(image1_w_overlay, debug_image)\n        assert self._image_contains(image2_w_overlay, debug_image)\n\n    def test_two_images_and_segmaps(self):\n        rng = iarandom.RNG(0)\n        images = rng.integers(0, 256, size=(2, 256, 256, 3), dtype=np.uint8)\n        sm1 = np.zeros((256, 256, 1), dtype=np.int32)\n        sm1[128-25:128+25, 128-25:128+25] = 1\n        sm2 = np.zeros((256, 256, 1), dtype=np.int32)\n        sm2[64-25:64+25, 64-25:64+25] = 2\n        sm2[192-25:192+25, 192-25:192+25] = 3\n        segmap1 = ia.SegmentationMapsOnImage(sm1, shape=images[0].shape)\n        segmap2 = ia.SegmentationMapsOnImage(sm2, shape=images[1].shape)\n        image1_w_overlay = segmap1.draw_on_image(images[0],\n                                                 draw_background=True)[0]\n        image2_w_overlay = segmap2.draw_on_image(images[1],\n                                                 draw_background=True)[0]\n\n        debug_image = iaa.draw_debug_image(images,\n                                           segmentation_maps=[segmap1, segmap2])\n\n        assert self._image_contains(images[0, ...], debug_image)\n        assert self._image_contains(images[1, ...], debug_image)\n        assert self._image_contains(image1_w_overlay, debug_image)\n        assert self._image_contains(image2_w_overlay, debug_image)\n\n    def test_two_images_and_heatmaps__map_size_differs_from_image(self):\n        rng = iarandom.RNG(0)\n        images = rng.integers(0, 256, size=(2, 256, 256, 3), dtype=np.uint8)\n        heatmap = np.zeros((128, 128, 1), dtype=np.float32)\n        heatmap[64-25:64+25, 64-25:64+25] = 1.0\n        heatmap1 = ia.HeatmapsOnImage(np.copy(heatmap), shape=images[0].shape)\n        heatmap2 = ia.HeatmapsOnImage(1.0 - heatmap, shape=images[1].shape)\n        image1_w_overlay = heatmap1.draw_on_image(images[0])[0]\n        image2_w_overlay = heatmap2.draw_on_image(images[1])[0]\n\n        debug_image = iaa.draw_debug_image(images,\n                                           heatmaps=[heatmap1, heatmap2])\n\n        assert self._image_contains(images[0, ...], debug_image)\n        assert self._image_contains(images[1, ...], debug_image)\n        assert self._image_contains(image1_w_overlay, debug_image)\n        assert self._image_contains(image2_w_overlay, debug_image)\n\n    def test_two_images_and_heatmaps__multichannel(self):\n        rng = iarandom.RNG(0)\n        images = rng.integers(0, 256, size=(2, 256, 256, 3), dtype=np.uint8)\n        heatmap = np.zeros((256, 256, 2), dtype=np.float32)\n        heatmap[100-25:100+25, 100-25:100+25, 0] = 1.0\n        heatmap[200-25:200+25, 200-25:200+25, 1] = 1.0\n        heatmap1 = ia.HeatmapsOnImage(np.copy(heatmap), shape=images[0].shape)\n        heatmap2 = ia.HeatmapsOnImage(1.0 - heatmap, shape=images[1].shape)\n        image1_w_overlay_c1, image1_w_overlay_c2 = \\\n            heatmap1.draw_on_image(images[0])\n        image2_w_overlay_c1, image2_w_overlay_c2 = \\\n            heatmap2.draw_on_image(images[1])\n\n        debug_image = iaa.draw_debug_image(images, heatmaps=[heatmap1, heatmap2])\n\n        assert self._image_contains(images[0, ...], debug_image)\n        assert self._image_contains(images[1, ...], debug_image)\n        assert self._image_contains(image1_w_overlay_c1, debug_image)\n        assert self._image_contains(image1_w_overlay_c2, debug_image)\n        assert self._image_contains(image2_w_overlay_c1, debug_image)\n        assert self._image_contains(image2_w_overlay_c2, debug_image)\n\n    def test_two_images_and_keypoints(self):\n        rng = iarandom.RNG(0)\n        images = rng.integers(0, 256, size=(2, 256, 256, 3), dtype=np.uint8)\n        kps = []\n        for x in np.linspace(0, 256, 10):\n            for y in np.linspace(0, 256, 10):\n                kps.append(ia.Keypoint(x=x, y=y))\n        kpsoi1 = ia.KeypointsOnImage(kps, shape=images[0].shape)\n        kpsoi2 = kpsoi1.shift(x=20)\n        image1_w_overlay = kpsoi1.draw_on_image(images[0])\n        image2_w_overlay = kpsoi2.draw_on_image(images[1])\n\n        debug_image = iaa.draw_debug_image(images, keypoints=[kpsoi1, kpsoi2])\n\n        assert self._image_contains(images[0, ...], debug_image)\n        assert self._image_contains(images[1, ...], debug_image)\n        assert self._image_contains(image1_w_overlay, debug_image)\n        assert self._image_contains(image2_w_overlay, debug_image)\n\n    def test_two_images_and_bounding_boxes(self):\n        rng = iarandom.RNG(0)\n        images = rng.integers(0, 256, size=(2, 256, 256, 3), dtype=np.uint8)\n        bbs = []\n        for x in np.linspace(0, 256, 5):\n            for y in np.linspace(0, 256, 5):\n                bbs.append(ia.BoundingBox(x1=x, y1=y, x2=x+20, y2=y+20))\n        bbsoi1 = ia.BoundingBoxesOnImage(bbs, shape=images[0].shape)\n        bbsoi2 = bbsoi1.shift(x=20)\n        image1_w_overlay = bbsoi1.draw_on_image(images[0])\n        image2_w_overlay = bbsoi2.draw_on_image(images[1])\n\n        debug_image = iaa.draw_debug_image(images,\n                                           bounding_boxes=[bbsoi1, bbsoi2])\n\n        assert self._image_contains(images[0, ...], debug_image)\n        assert self._image_contains(images[1, ...], debug_image)\n        assert self._image_contains(image1_w_overlay, debug_image)\n        assert self._image_contains(image2_w_overlay, debug_image)\n\n    def test_two_images_and_polygons(self):\n        rng = iarandom.RNG(0)\n        images = rng.integers(0, 256, size=(2, 32, 32, 3), dtype=np.uint8)\n        polys = []\n        for x in np.linspace(0, 256, 4):\n            for y in np.linspace(0, 256, 4):\n                polys.append(ia.Polygon([(x, y), (x+20, y), (x+20, y+20),\n                                         (x, y+20)]))\n        psoi1 = ia.PolygonsOnImage(polys, shape=images[0].shape)\n        psoi2 = psoi1.shift(x=20)\n        image1_w_overlay = psoi1.draw_on_image(images[0])\n        image2_w_overlay = psoi2.draw_on_image(images[1])\n\n        debug_image = iaa.draw_debug_image(images,\n                                           polygons=[psoi1, psoi2])\n\n        assert self._image_contains(images[0, ...], debug_image)\n        assert self._image_contains(images[1, ...], debug_image)\n        assert self._image_contains(image1_w_overlay, debug_image)\n        assert self._image_contains(image2_w_overlay, debug_image)\n\n    def test_two_images_and_line_strings(self):\n        rng = iarandom.RNG(0)\n        images = rng.integers(0, 256, size=(2, 32, 32, 3), dtype=np.uint8)\n        ls = []\n        for x in np.linspace(0, 256, 4):\n            for y in np.linspace(0, 256, 4):\n                ls.append(ia.LineString([(x, y), (x+20, y), (x+20, y+20),\n                                         (x, y+20)]))\n        lsoi1 = ia.LineStringsOnImage(ls, shape=images[0].shape)\n        lsoi2 = lsoi1.deepcopy()\n        image1_w_overlay = lsoi1.draw_on_image(images[0])\n        image2_w_overlay = lsoi2.draw_on_image(images[1])\n\n        debug_image = iaa.draw_debug_image(images,\n                                           line_strings=[lsoi1, lsoi2])\n\n        assert self._image_contains(images[0, ...], debug_image)\n        assert self._image_contains(images[1, ...], debug_image)\n        assert self._image_contains(image1_w_overlay, debug_image)\n        assert self._image_contains(image2_w_overlay, debug_image)\n\n    def test_one_image_float32(self):\n        rng = iarandom.RNG(0)\n        image = rng.random(size=(256, 256, 3)).astype(np.float32)\n\n        debug_image = iaa.draw_debug_image([image])\n\n        assert self._image_contains((image * 255).astype(np.uint8),\n                                    debug_image)\n\n    def test_one_image_float32_and_heatmap(self):\n        rng = iarandom.RNG(0)\n        image = rng.random(size=(256, 256, 3)).astype(np.float32)\n        heatmap = np.zeros((256, 256, 1), dtype=np.float32)\n        heatmap[128-25:128+25, 128-25:128+25] = 1.0\n        heatmap = ia.HeatmapsOnImage(heatmap, shape=image.shape)\n        image1_w_overlay = heatmap.draw_on_image(\n            (image*255).astype(np.uint8))[0]\n\n        debug_image = iaa.draw_debug_image([image], heatmaps=[heatmap])\n\n        assert self._image_contains((image * 255).astype(np.uint8), debug_image)\n        assert self._image_contains(image1_w_overlay, debug_image)\n\n\nclass SaveDebugImageEveryNBatches(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_mocked(self):\n        class _DummyDestination(debuglib._IImageDestination):\n            def __init__(self):\n                self.received = []\n\n            def receive(self, image):\n                self.received.append(np.copy(image))\n\n        image = iarandom.RNG(0).integers(0, 256, size=(256, 256, 3),\n                                         dtype=np.uint8)\n        destination = _DummyDestination()\n        aug = iaa.SaveDebugImageEveryNBatches(destination, 10)\n\n        for _ in np.arange(20):\n            _ = aug(image=image)\n\n        expected = iaa.draw_debug_image([image])\n        assert len(destination.received) == 2\n        assert np.array_equal(destination.received[0], expected)\n        assert np.array_equal(destination.received[1], expected)\n\n    def test_temp_directory(self):\n        with TemporaryDirectory() as folder_path:\n            image = iarandom.RNG(0).integers(0, 256, size=(256, 256, 3),\n                                             dtype=np.uint8)\n            aug = iaa.SaveDebugImageEveryNBatches(folder_path, 10)\n\n            for _ in np.arange(20):\n                _ = aug(image=image)\n\n            expected = iaa.draw_debug_image([image])\n            path1 = os.path.join(folder_path, \"batch_000000.png\")\n            path2 = os.path.join(folder_path, \"batch_000010.png\")\n            path_latest = os.path.join(folder_path, \"batch_latest.png\")\n            assert len(list(os.listdir(folder_path))) == 3\n            assert os.path.isfile(path1)\n            assert os.path.isfile(path2)\n            assert os.path.isfile(path_latest)\n            assert np.array_equal(imageio.imread(path1), expected)\n            assert np.array_equal(imageio.imread(path2), expected)\n            assert np.array_equal(imageio.imread(path_latest), expected)\n\n    def test_pickleable(self):\n        shape = (16, 16, 3)\n        image = np.mod(np.arange(int(np.prod(shape))), 256).astype(np.uint8)\n        image = image.reshape(shape)\n\n        with TemporaryDirectory() as folder_path:\n            path1 = os.path.join(folder_path, \"batch_000000.png\")\n            path2 = os.path.join(folder_path, \"batch_000010.png\")\n\n            augmenter = iaa.SaveDebugImageEveryNBatches(folder_path, 10)\n            augmenter_pkl = pickle.loads(pickle.dumps(augmenter, protocol=-1))\n\n            # save two images via augmenter without pickling\n            for _ in np.arange(20):\n                _ = augmenter(image=image)\n\n            img11 = imageio.imread(path1)\n            img12 = imageio.imread(path2)\n\n            # reset folder content\n            os.remove(path1)\n            os.remove(path2)\n\n            # save two images via augmenter that was pickled\n            for _ in np.arange(20):\n                _ = augmenter_pkl(image=image)\n\n            img21 = imageio.imread(path1)\n            img22 = imageio.imread(path2)\n\n            # compare the two images of original/pickled augmenters\n            assert np.array_equal(img11, img21)\n            assert np.array_equal(img12, img22)\n"
  },
  {
    "path": "test/augmenters/test_edges.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport itertools\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\ntry:\n    import cPickle as pickle\nexcept ImportError:\n    import pickle\n\nimport numpy as np\nimport cv2\n\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug import random as iarandom\nfrom imgaug.testutils import (reseed, runtest_pickleable_uint8_img,\n                              is_parameter_instance, remove_prefetching)\n\n\nclass TestRandomColorsBinaryImageColorizer(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___default_settings(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer()\n        assert is_parameter_instance(colorizer.color_true, iap.DiscreteUniform)\n        assert is_parameter_instance(colorizer.color_false, iap.DiscreteUniform)\n        assert colorizer.color_true.a.value == 0\n        assert colorizer.color_true.b.value == 255\n        assert colorizer.color_false.a.value == 0\n        assert colorizer.color_false.b.value == 255\n\n    def test___init___deterministic_settinga(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer(color_true=1,\n                                                         color_false=2)\n        assert is_parameter_instance(colorizer.color_true, iap.Deterministic)\n        assert is_parameter_instance(colorizer.color_false, iap.Deterministic)\n        assert colorizer.color_true.value == 1\n        assert colorizer.color_false.value == 2\n\n    def test___init___tuple_and_list(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=(0, 100), color_false=[200, 201, 202])\n        assert is_parameter_instance(colorizer.color_true, iap.DiscreteUniform)\n        assert is_parameter_instance(colorizer.color_false, iap.Choice)\n        assert colorizer.color_true.a.value == 0\n        assert colorizer.color_true.b.value == 100\n        assert colorizer.color_false.a[0] == 200\n        assert colorizer.color_false.a[1] == 201\n        assert colorizer.color_false.a[2] == 202\n\n    def test___init___stochastic_parameters(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=iap.DiscreteUniform(0, 100),\n            color_false=iap.Choice([200, 201, 202]))\n        assert is_parameter_instance(colorizer.color_true, iap.DiscreteUniform)\n        assert is_parameter_instance(colorizer.color_false, iap.Choice)\n        assert colorizer.color_true.a.value == 0\n        assert colorizer.color_true.b.value == 100\n        assert colorizer.color_false.a[0] == 200\n        assert colorizer.color_false.a[1] == 201\n        assert colorizer.color_false.a[2] == 202\n\n    def test__draw_samples(self):\n        class _ListSampler(iap.StochasticParameter):\n            def __init__(self, offset):\n                super(_ListSampler, self).__init__()\n                self.offset = offset\n                self.last_random_state = None\n\n            def _draw_samples(self, size, random_state=None):\n                assert size == (3,)\n                self.last_random_state = random_state\n                return np.uint8([0, 1, 2]) + self.offset\n\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=_ListSampler(0),\n            color_false=_ListSampler(1))\n        random_state = iarandom.RNG(42)\n        color_true, color_false = colorizer._draw_samples(random_state)\n        assert np.array_equal(color_true, [0, 1, 2])\n        assert np.array_equal(color_false, [1, 2, 3])\n        assert colorizer.color_true.last_random_state.equals(random_state)\n        assert colorizer.color_false.last_random_state.equals(random_state)\n\n    def test_colorize__one_channel(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=100,\n            color_false=10)\n        random_state = iarandom.RNG(42)\n\n        # input image has shape (H,W,1)\n        image = np.zeros((5, 5, 1), dtype=np.uint8)\n        image[:, 0:3, :] = 255\n        image_binary = np.zeros((5, 5), dtype=bool)\n        image_binary[:, 0:3] = True\n\n        image_color = colorizer.colorize(\n            image_binary, image, nth_image=0, random_state=random_state)\n\n        assert image_color.ndim == 3\n        assert image_color.shape[-1] == 1\n        assert np.all(image_color[image_binary] == 100)\n        assert np.all(image_color[~image_binary] == 10)\n\n    def test_colorize__three_channels(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=100,\n            color_false=10)\n        random_state = iarandom.RNG(42)\n\n        # input image has shape (H,W,3)\n        image = np.zeros((5, 5, 3), dtype=np.uint8)\n        image[:, 0:3, :] = 255\n        image_binary = np.zeros((5, 5), dtype=bool)\n        image_binary[:, 0:3] = True\n\n        image_color = colorizer.colorize(\n            image_binary, image, nth_image=0, random_state=random_state)\n\n        assert image_color.ndim == 3\n        assert image_color.shape[-1] == 3\n        assert np.all(image_color[image_binary] == 100)\n        assert np.all(image_color[~image_binary] == 10)\n\n    def test_colorize__four_channels(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=100,\n            color_false=10)\n        random_state = iarandom.RNG(42)\n\n        # input image has shape (H,W,4)\n        image = np.zeros((5, 5, 4), dtype=np.uint8)\n        image[:, 0:3, 0:3] = 255\n        image[:, 1:4, 3] = 123  # set some content for alpha channel\n\n        image_binary = np.zeros((5, 5), dtype=bool)\n        image_binary[:, 0:3] = True\n\n        image_color = colorizer.colorize(\n            image_binary, image, nth_image=0, random_state=random_state)\n\n        assert image_color.ndim == 3\n        assert image_color.shape[-1] == 4\n        assert np.all(image_color[image_binary, 0:3] == 100)\n        assert np.all(image_color[~image_binary, 0:3] == 10)\n        # alpha channel must have been kept untouched\n        assert np.all(image_color[:, :, 3:4] == image[:, :, 3:4])\n\n    def test_pickleable(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=(50, 100),\n            color_false=(10, 50))\n        colorizer_pkl = pickle.loads(pickle.dumps(colorizer))\n        random_state = iarandom.RNG(1)\n\n        color_true, color_false = colorizer._draw_samples(\n            random_state.copy())\n        color_true_pkl, color_false_pkl = colorizer_pkl._draw_samples(\n            random_state.copy())\n\n        assert np.array_equal(color_true, color_true_pkl)\n        assert np.array_equal(color_false, color_false_pkl)\n\n\nclass TestCanny(unittest.TestCase):\n    def test___init___default_settings(self):\n        aug = iaa.Canny()\n        assert is_parameter_instance(aug.alpha, iap.Uniform)\n        assert isinstance(aug.hysteresis_thresholds, tuple)\n        assert is_parameter_instance(aug.sobel_kernel_size, iap.DiscreteUniform)\n        assert isinstance(aug.colorizer, iaa.RandomColorsBinaryImageColorizer)\n        assert np.isclose(aug.alpha.a.value, 0.0)\n        assert np.isclose(aug.alpha.b.value, 1.0)\n        assert len(aug.hysteresis_thresholds) == 2\n        assert is_parameter_instance(aug.hysteresis_thresholds[0],\n                                     iap.DiscreteUniform)\n        assert np.isclose(aug.hysteresis_thresholds[0].a.value, 100-40)\n        assert np.isclose(aug.hysteresis_thresholds[0].b.value, 100+40)\n        assert is_parameter_instance(aug.hysteresis_thresholds[1],\n                                     iap.DiscreteUniform)\n        assert np.isclose(aug.hysteresis_thresholds[1].a.value, 200-40)\n        assert np.isclose(aug.hysteresis_thresholds[1].b.value, 200+40)\n        assert aug.sobel_kernel_size.a.value == 3\n        assert aug.sobel_kernel_size.b.value == 7\n        assert is_parameter_instance(aug.colorizer.color_true,\n                                     iap.DiscreteUniform)\n        assert is_parameter_instance(aug.colorizer.color_false,\n                                     iap.DiscreteUniform)\n        assert aug.colorizer.color_true.a.value == 0\n        assert aug.colorizer.color_true.b.value == 255\n        assert aug.colorizer.color_false.a.value == 0\n        assert aug.colorizer.color_false.b.value == 255\n\n    def test___init___custom_settings(self):\n        aug = iaa.Canny(\n            alpha=0.2,\n            hysteresis_thresholds=([0, 1, 2], iap.DiscreteUniform(1, 10)),\n            sobel_kernel_size=[3, 5],\n            colorizer=iaa.RandomColorsBinaryImageColorizer(\n                color_true=10, color_false=20)\n        )\n        assert is_parameter_instance(aug.alpha, iap.Deterministic)\n        assert isinstance(aug.hysteresis_thresholds, tuple)\n        assert is_parameter_instance(aug.sobel_kernel_size, iap.Choice)\n        assert isinstance(aug.colorizer, iaa.RandomColorsBinaryImageColorizer)\n        assert np.isclose(aug.alpha.value, 0.2)\n        assert len(aug.hysteresis_thresholds) == 2\n        assert is_parameter_instance(aug.hysteresis_thresholds[0], iap.Choice)\n        assert aug.hysteresis_thresholds[0].a == [0, 1, 2]\n        assert is_parameter_instance(aug.hysteresis_thresholds[1],\n                                     iap.DiscreteUniform)\n        assert np.isclose(aug.hysteresis_thresholds[1].a.value, 1)\n        assert np.isclose(aug.hysteresis_thresholds[1].b.value, 10)\n        assert is_parameter_instance(aug.sobel_kernel_size, iap.Choice)\n        assert aug.sobel_kernel_size.a == [3, 5]\n        assert is_parameter_instance(aug.colorizer.color_true,\n                                     iap.Deterministic)\n        assert is_parameter_instance(aug.colorizer.color_false,\n                                     iap.Deterministic)\n        assert aug.colorizer.color_true.value == 10\n        assert aug.colorizer.color_false.value == 20\n\n    def test___init___single_value_hysteresis(self):\n        aug = iaa.Canny(\n            alpha=0.2,\n            hysteresis_thresholds=[0, 1, 2],\n            sobel_kernel_size=[3, 5],\n            colorizer=iaa.RandomColorsBinaryImageColorizer(\n                color_true=10, color_false=20)\n        )\n        assert is_parameter_instance(aug.alpha, iap.Deterministic)\n        assert is_parameter_instance(aug.hysteresis_thresholds, iap.Choice)\n        assert is_parameter_instance(aug.sobel_kernel_size, iap.Choice)\n        assert isinstance(aug.colorizer, iaa.RandomColorsBinaryImageColorizer)\n        assert np.isclose(aug.alpha.value, 0.2)\n        assert aug.hysteresis_thresholds.a == [0, 1, 2]\n        assert is_parameter_instance(aug.sobel_kernel_size, iap.Choice)\n        assert aug.sobel_kernel_size.a == [3, 5]\n        assert is_parameter_instance(aug.colorizer.color_true,\n                                     iap.Deterministic)\n        assert is_parameter_instance(aug.colorizer.color_false,\n                                     iap.Deterministic)\n        assert aug.colorizer.color_true.value == 10\n        assert aug.colorizer.color_false.value == 20\n\n    def test__draw_samples__single_value_hysteresis(self):\n        seed = 1\n        nb_images = 1000\n\n        aug = iaa.Canny(\n            alpha=0.2,\n            hysteresis_thresholds=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],\n            sobel_kernel_size=[3, 5, 7],\n            random_state=iarandom.RNG(seed))\n        aug.alpha = remove_prefetching(aug.alpha)\n        aug.hysteresis_thresholds = remove_prefetching(\n            aug.hysteresis_thresholds)\n        aug.sobel_kernel_size = remove_prefetching(aug.sobel_kernel_size)\n\n        example_image = np.zeros((5, 5, 3), dtype=np.uint8)\n        samples = aug._draw_samples([example_image] * nb_images,\n                                    random_state=iarandom.RNG(seed))\n        alpha_samples = samples[0]\n        hthresh_samples = samples[1]\n        sobel_samples = samples[2]\n\n        rss = iarandom.RNG(seed).duplicate(4)\n        alpha_expected = iap.Deterministic(0.2).draw_samples((nb_images,),\n                                                             rss[0])\n        hthresh_expected = iap.Choice(\n            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).draw_samples((nb_images, 2),\n                                                             rss[1])\n        sobel_expected = iap.Choice([3, 5, 7]).draw_samples((nb_images,),\n                                                            rss[2])\n\n        invalid = hthresh_expected[:, 0] > hthresh_expected[:, 1]\n        assert np.any(invalid)\n        hthresh_expected[invalid, :] = hthresh_expected[invalid, :][:, [1, 0]]\n        assert hthresh_expected.shape == (nb_images, 2)\n        assert not np.any(hthresh_expected[:, 0] > hthresh_expected[:, 1])\n\n        assert np.allclose(alpha_samples, alpha_expected)\n        assert np.allclose(hthresh_samples, hthresh_expected)\n        assert np.allclose(sobel_samples, sobel_expected)\n\n    def test__draw_samples__tuple_as_hysteresis(self):\n        seed = 1\n        nb_images = 10\n\n        aug = iaa.Canny(\n            alpha=0.2,\n            hysteresis_thresholds=([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],\n                                   iap.DiscreteUniform(5, 100)),\n            sobel_kernel_size=[3, 5, 7],\n            random_state=iarandom.RNG(seed))\n        aug.alpha = remove_prefetching(aug.alpha)\n        aug.hysteresis_thresholds = (\n            remove_prefetching(aug.hysteresis_thresholds[0]),\n            remove_prefetching(aug.hysteresis_thresholds[1])\n        )\n        aug.sobel_kernel_size = remove_prefetching(aug.sobel_kernel_size)\n\n        example_image = np.zeros((5, 5, 3), dtype=np.uint8)\n        samples = aug._draw_samples([example_image] * nb_images,\n                                    random_state=iarandom.RNG(seed))\n        alpha_samples = samples[0]\n        hthresh_samples = samples[1]\n        sobel_samples = samples[2]\n\n        rss = iarandom.RNG(seed).duplicate(4)\n        alpha_expected = iap.Deterministic(0.2).draw_samples((nb_images,),\n                                                             rss[0])\n        hthresh_expected = [None, None]\n        hthresh_expected[0] = iap.Choice(\n            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).draw_samples((nb_images,),\n                                                             rss[1])\n        # TODO simplify this to rss[2].randint(5, 100+1)\n        #      would currenlty be a bit more ugly, because DiscrUniform\n        #      samples two values for a and b first from rss[2]\n        hthresh_expected[1] = iap.DiscreteUniform(5, 100).draw_samples(\n            (nb_images,), rss[2])\n        hthresh_expected = np.stack(hthresh_expected, axis=-1)\n\n        sobel_expected = iap.Choice([3, 5, 7]).draw_samples((nb_images,),\n                                                            rss[3])\n\n        invalid = hthresh_expected[:, 0] > hthresh_expected[:, 1]\n        hthresh_expected[invalid, :] = hthresh_expected[invalid, :][:, [1, 0]]\n        assert hthresh_expected.shape == (nb_images, 2)\n        assert not np.any(hthresh_expected[:, 0] > hthresh_expected[:, 1])\n\n        assert np.allclose(alpha_samples, alpha_expected)\n        assert np.allclose(hthresh_samples, hthresh_expected)\n        assert np.allclose(sobel_samples, sobel_expected)\n\n    def test_augment_images__alpha_is_zero(self):\n        aug = iaa.Canny(\n            alpha=0.0,\n            hysteresis_thresholds=(0, 10),\n            sobel_kernel_size=[3, 5, 7],\n            random_state=1)\n\n        image = np.arange(5*5*3).astype(np.uint8).reshape((5, 5, 3))\n        image_aug = aug.augment_image(image)\n        assert np.array_equal(image_aug, image)\n\n    def test_augment_images__alpha_is_one(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=254,\n            color_false=1\n        )\n\n        aug = iaa.Canny(\n            alpha=1.0,\n            hysteresis_thresholds=100,\n            sobel_kernel_size=3,\n            colorizer=colorizer,\n            random_state=1)\n\n        image_single_chan = np.uint8([\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 1, 1, 1, 0, 0, 0]\n        ])\n        image = np.tile(image_single_chan[:, :, np.newaxis] * 128, (1, 1, 3))\n\n        # canny image, looks a bit unintuitive, but is what OpenCV returns\n        # can be checked via something like\n        # print(\"canny\\n\", cv2.Canny(image_single_chan*255, threshold1=100,\n        #            threshold2=200,\n        #            apertureSize=3,\n        #            L2gradient=True))\n        image_canny = np.array([\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 1, 0, 0, 1, 0, 0]\n        ], dtype=bool)\n\n        image_aug_expected = np.copy(image)\n        image_aug_expected[image_canny] = 254\n        image_aug_expected[~image_canny] = 1\n\n        image_aug = aug.augment_image(image)\n        assert np.array_equal(image_aug, image_aug_expected)\n\n    def test_augment_images__single_channel(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=254,\n            color_false=1\n        )\n\n        aug = iaa.Canny(\n            alpha=1.0,\n            hysteresis_thresholds=100,\n            sobel_kernel_size=3,\n            colorizer=colorizer,\n            random_state=1)\n\n        image_single_chan = np.uint8([\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 1, 1, 1, 0, 0, 0]\n        ])\n        image = image_single_chan[:, :, np.newaxis] * 128\n\n        # canny image, looks a bit unintuitive, but is what OpenCV returns\n        # can be checked via something like\n        # print(\"canny\\n\", cv2.Canny(image_single_chan*255, threshold1=100,\n        #            threshold2=200,\n        #            apertureSize=3,\n        #            L2gradient=True))\n        image_canny = np.array([\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 1, 0, 0, 1, 0, 0]\n        ], dtype=bool)\n\n        image_aug_expected = np.copy(image)\n        image_aug_expected[image_canny] = int(0.299*254\n                                              + 0.587*254\n                                              + 0.114*254)\n        image_aug_expected[~image_canny] = int(0.299*1 + 0.587*1 + 0.114*1)\n\n        image_aug = aug.augment_image(image)\n        assert np.array_equal(image_aug, image_aug_expected)\n\n    def test_augment_images__four_channels(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=254,\n            color_false=1\n        )\n\n        aug = iaa.Canny(\n            alpha=1.0,\n            hysteresis_thresholds=100,\n            sobel_kernel_size=3,\n            colorizer=colorizer,\n            random_state=1)\n\n        image_single_chan = np.uint8([\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 1, 1, 1, 0, 0, 0]\n        ])\n        image_alpha_channel = np.uint8([\n            [0, 0, 0, 0, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 1, 1, 1, 0, 0],\n            [0, 0, 1, 1, 0, 0, 0],\n            [0, 0, 1, 0, 0, 0, 0]\n        ]) * 255\n        image = np.tile(image_single_chan[:, :, np.newaxis] * 128, (1, 1, 3))\n        image = np.dstack([image, image_alpha_channel[:, :, np.newaxis]])\n        assert image.ndim == 3\n        assert image.shape[-1] == 4\n\n        # canny image, looks a bit unintuitive, but is what OpenCV returns\n        # can be checked via something like\n        # print(\"canny\\n\", cv2.Canny(image_single_chan*255, threshold1=100,\n        #            threshold2=200,\n        #            apertureSize=3,\n        #            L2gradient=True))\n        image_canny = np.array([\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 1, 0, 0, 1, 0, 0]\n        ], dtype=bool)\n\n        image_aug_expected = np.copy(image)\n        image_aug_expected[image_canny, 0:3] = 254\n        image_aug_expected[~image_canny, 0:3] = 1\n\n        image_aug = aug.augment_image(image)\n        assert np.array_equal(image_aug, image_aug_expected)\n\n    def test_augment_images__random_color(self):\n        class _Color(iap.StochasticParameter):\n            def __init__(self, values):\n                super(_Color, self).__init__()\n                self.values = values\n\n            def _draw_samples(self, size, random_state):\n                v = random_state.choice(self.values)\n                return np.full(size, v, dtype=np.uint8)\n\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=_Color([253, 254]),\n            color_false=_Color([1, 2])\n        )\n\n        image_single_chan = np.uint8([\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 0, 0, 1, 0, 0, 0],\n            [0, 1, 1, 1, 0, 0, 0]\n        ])\n        image = np.tile(image_single_chan[:, :, np.newaxis] * 128, (1, 1, 3))\n\n        # canny image, looks a bit unintuitive, but is what OpenCV returns\n        # can be checked via something like\n        # print(\"canny\\n\", cv2.Canny(image_single_chan*255, threshold1=100,\n        #            threshold2=200,\n        #            apertureSize=3,\n        #            L2gradient=True))\n        image_canny = np.array([\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 0, 1, 0, 1, 0, 0],\n            [0, 1, 0, 0, 1, 0, 0]\n        ], dtype=bool)\n\n        seen = {\n            (253, 1): False,\n            (253, 2): False,\n            (254, 1): False,\n            (254, 2): False\n        }\n        for i in range(100):\n            aug = iaa.Canny(\n                alpha=1.0,\n                hysteresis_thresholds=100,\n                sobel_kernel_size=3,\n                colorizer=colorizer,\n                seed=i)\n\n            image_aug = aug.augment_image(image)\n            color_true = np.unique(image_aug[image_canny])\n            color_false = np.unique(image_aug[~image_canny])\n            assert len(color_true) == 1\n            assert len(color_false) == 1\n            color_true = int(color_true[0])\n            color_false = int(color_false[0])\n\n            seen[(int(color_true), int(color_false))] = True\n            assert len(seen.keys()) == 4\n            if all(seen.values()):\n                break\n        assert np.all(seen.values())\n\n    def test_augment_images__random_values(self):\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=255,\n            color_false=0\n        )\n\n        image_single_chan = iarandom.RNG(1).integers(\n            0, 255, size=(100, 100), dtype=\"uint8\")\n        image = np.tile(image_single_chan[:, :, np.newaxis], (1, 1, 3))\n\n        images_canny_uint8 = {}\n        for thresh1, thresh2, ksize in itertools.product([100],\n                                                         [200],\n                                                         [3, 5]):\n            if thresh1 > thresh2:\n                continue\n\n            image_canny = cv2.Canny(\n                image,\n                threshold1=thresh1,\n                threshold2=thresh2,\n                apertureSize=ksize,\n                L2gradient=True)\n            image_canny_uint8 = np.tile(\n                image_canny[:, :, np.newaxis], (1, 1, 3))\n\n            similar = 0\n            for key, image_expected in images_canny_uint8.items():\n                if np.array_equal(image_canny_uint8, image_expected):\n                    similar += 1\n            assert similar == 0\n\n            images_canny_uint8[(thresh1, thresh2, ksize)] = image_canny_uint8\n\n        seen = {key: False for key in images_canny_uint8.keys()}\n\n        for i in range(500):\n            aug = iaa.Canny(\n                alpha=1.0,\n                hysteresis_thresholds=(iap.Deterministic(100),\n                                       iap.Deterministic(200)),\n                sobel_kernel_size=[3, 5],\n                colorizer=colorizer,\n                seed=i)\n\n            image_aug = aug.augment_image(image)\n            match_index = None\n            for key, image_expected in images_canny_uint8.items():\n                if np.array_equal(image_aug, image_expected):\n                    match_index = key\n                    break\n            assert match_index is not None\n            seen[match_index] = True\n\n            assert len(seen.keys()) == len(images_canny_uint8.keys())\n            if all(seen.values()):\n                break\n        assert np.all(seen.values())\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0, 3),\n            (0, 1, 3),\n            (1, 0, 3)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Canny(alpha=1)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_get_parameters(self):\n        alpha = iap.Deterministic(0.2)\n        hysteresis_thresholds = iap.Deterministic(10)\n        sobel_kernel_size = iap.Deterministic(3)\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=10, color_false=20)\n        aug = iaa.Canny(\n            alpha=alpha,\n            hysteresis_thresholds=hysteresis_thresholds,\n            sobel_kernel_size=sobel_kernel_size,\n            colorizer=colorizer\n        )\n        params = aug.get_parameters()\n        assert params[0] is aug.alpha\n        assert params[1] is aug.hysteresis_thresholds\n        assert params[2] is aug.sobel_kernel_size\n        assert params[3] is colorizer\n\n    def test___str___single_value_hysteresis(self):\n        alpha = iap.Deterministic(0.2)\n        hysteresis_thresholds = iap.Deterministic(10)\n        sobel_kernel_size = iap.Deterministic(3)\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=10, color_false=20)\n        aug = iaa.Canny(\n            alpha=alpha,\n            hysteresis_thresholds=hysteresis_thresholds,\n            sobel_kernel_size=sobel_kernel_size,\n            colorizer=colorizer\n        )\n        observed = aug.__str__()\n        expected = (\"Canny(alpha=%s, hysteresis_thresholds=%s, \"\n                    \"sobel_kernel_size=%s, colorizer=%s, name=UnnamedCanny, \"\n                    \"deterministic=False)\") % (\n                        str(aug.alpha),\n                        str(aug.hysteresis_thresholds),\n                        str(aug.sobel_kernel_size),\n                        colorizer)\n        assert observed == expected\n\n    def test___str___tuple_as_hysteresis(self):\n        alpha = iap.Deterministic(0.2)\n        hysteresis_thresholds = (\n            iap.Deterministic(10),\n            iap.Deterministic(11)\n        )\n        sobel_kernel_size = iap.Deterministic(3)\n        colorizer = iaa.RandomColorsBinaryImageColorizer(\n            color_true=10, color_false=20)\n        aug = iaa.Canny(\n            alpha=alpha,\n            hysteresis_thresholds=hysteresis_thresholds,\n            sobel_kernel_size=sobel_kernel_size,\n            colorizer=colorizer\n        )\n        observed = aug.__str__()\n        expected = (\"Canny(alpha=%s, hysteresis_thresholds=(%s, %s), \"\n                    \"sobel_kernel_size=%s, colorizer=%s, name=UnnamedCanny, \"\n                    \"deterministic=False)\") % (\n                        str(aug.alpha),\n                        str(aug.hysteresis_thresholds[0]),\n                        str(aug.hysteresis_thresholds[1]),\n                        str(aug.sobel_kernel_size),\n                        colorizer)\n        assert observed == expected\n\n    def test_pickleable(self):\n        aug = iaa.Canny(seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n"
  },
  {
    "path": "test/augmenters/test_flip.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nfrom abc import ABCMeta, abstractproperty, abstractmethod\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport six\nimport six.moves as sm\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug import dtypes as iadt\nfrom imgaug.testutils import (reseed, assert_cbaois_equal,\n                              runtest_pickleable_uint8_img,\n                              is_parameter_instance)\nfrom imgaug.augmentables.heatmaps import HeatmapsOnImage\nfrom imgaug.augmentables.segmaps import SegmentationMapsOnImage\nimport imgaug.augmenters.flip as fliplib\n\n\nclass TestHorizontalFlip(unittest.TestCase):\n    def test_returns_fliplr(self):\n        aug = iaa.HorizontalFlip(0.5)\n        assert isinstance(aug, iaa.Fliplr)\n        assert np.allclose(aug.p.p.value, 0.5)\n\n\nclass TestVerticalFlip(unittest.TestCase):\n    def test_returns_flipud(self):\n        aug = iaa.VerticalFlip(0.5)\n        assert isinstance(aug, iaa.Flipud)\n        assert np.allclose(aug.p.p.value, 0.5)\n\n\n@six.add_metaclass(ABCMeta)\nclass _TestFliplrAndFlipudBase(object):\n    def setUp(self):\n        reseed()\n\n    @property\n    @abstractproperty\n    def image(self):\n        pass\n\n    @property\n    @abstractproperty\n    def image_flipped(self):\n        pass\n\n    @property\n    def images(self):\n        return np.array([self.image])\n\n    @property\n    def images_flipped(self):\n        return np.array([self.image_flipped])\n\n    @property\n    @abstractproperty\n    def heatmaps(self):\n        pass\n\n    @property\n    @abstractproperty\n    def heatmaps_flipped(self):\n        pass\n\n    @property\n    @abstractproperty\n    def segmaps(self):\n        pass\n\n    @property\n    @abstractproperty\n    def segmaps_flipped(self):\n        pass\n\n    @property\n    @abstractproperty\n    def kpsoi(self):\n        pass\n\n    @property\n    @abstractproperty\n    def kpsoi_flipped(self):\n        pass\n\n    @property\n    @abstractproperty\n    def psoi(self):\n        pass\n\n    @property\n    @abstractproperty\n    def psoi_flipped(self):\n        pass\n\n    @property\n    @abstractproperty\n    def lsoi(self):\n        pass\n\n    @property\n    @abstractproperty\n    def lsoi_flipped(self):\n        pass\n\n    @property\n    @abstractproperty\n    def bbsoi(self):\n        pass\n\n    @property\n    @abstractproperty\n    def bbsoi_flipped(self):\n        pass\n\n    @abstractmethod\n    def create_aug(self, *args, **kwargs):\n        pass\n\n    @abstractmethod\n    def create_arr(self, value, dtype):\n        pass\n\n    @abstractmethod\n    def create_arr_flipped(self, value, dtype):\n        pass\n\n    def test_images_p_is_0(self):\n        aug = self.create_aug(0)\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_images(self.images)\n            expected = self.images\n            assert np.array_equal(observed, expected)\n\n    def test_images_p_is_0__deterministic(self):\n        aug = self.create_aug(0).to_deterministic()\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_images(self.images)\n            expected = self.images\n            assert np.array_equal(observed, expected)\n\n    def test_keypoints_p_is_0(self):\n        self._test_cbaoi_p_is_0(\"augment_keypoints\", self.kpsoi, False)\n\n    def test_keypoints_p_is_0__deterministic(self):\n        self._test_cbaoi_p_is_0(\"augment_keypoints\", self.kpsoi, True)\n\n    def test_polygons_p_is_0(self):\n        self._test_cbaoi_p_is_0(\"augment_polygons\", self.psoi, False)\n\n    def test_polygons_p_is_0__deterministic(self):\n        self._test_cbaoi_p_is_0(\"augment_polygons\", self.psoi, True)\n\n    def test_line_strings_p_is_0(self):\n        self._test_cbaoi_p_is_0(\"augment_line_strings\", self.lsoi, False)\n\n    def test_line_strings_p_is_0__deterministic(self):\n        self._test_cbaoi_p_is_0(\"augment_line_strings\", self.lsoi, True)\n\n    def test_bounding_boxes_p_is_0(self):\n        self._test_cbaoi_p_is_0(\"augment_bounding_boxes\", self.bbsoi, False)\n\n    def test_bounding_boxes_p_is_0__deterministic(self):\n        self._test_cbaoi_p_is_0(\"augment_bounding_boxes\", self.bbsoi, True)\n\n    def _test_cbaoi_p_is_0(self, augf_name, cbaoi, deterministic):\n        aug = self.create_aug(0)\n        if deterministic:\n            aug = aug.to_deterministic()\n\n        for _ in sm.xrange(3):\n            observed = getattr(aug, augf_name)(cbaoi)\n            assert_cbaois_equal(observed, cbaoi)\n\n    def test_heatmaps_p_is_0(self):\n        aug = self.create_aug(0)\n        heatmaps = self.heatmaps\n        observed = aug.augment_heatmaps(heatmaps)\n        assert observed.shape == heatmaps.shape\n        assert np.isclose(observed.min_value, heatmaps.min_value,\n                          rtol=0, atol=1e-6)\n        assert np.isclose(observed.max_value, heatmaps.max_value,\n                          rtol=0, atol=1e-6)\n        assert np.array_equal(observed.get_arr(), heatmaps.get_arr())\n\n    def test_segmaps_p_is_0(self):\n        aug = self.create_aug(0)\n        observed = aug.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == self.segmaps.shape\n        assert np.array_equal(observed.get_arr(), self.segmaps.get_arr())\n\n    def test_images_p_is_1(self):\n        aug = self.create_aug(1.0)\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_images(self.images)\n            expected = self.images_flipped\n            assert np.array_equal(observed, expected)\n\n    def test_images_p_is_1__deterministic(self):\n        aug = self.create_aug(1.0).to_deterministic()\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_images(self.images)\n            expected = self.images_flipped\n            assert np.array_equal(observed, expected)\n\n    def test_keypoints_p_is_1(self):\n        self._test_cbaoi_p_is_1(\n            \"augment_keypoints\", self.kpsoi, self.kpsoi_flipped, False)\n\n    def test_keypoints_p_is_1__deterministic(self):\n        self._test_cbaoi_p_is_1(\n            \"augment_keypoints\", self.kpsoi, self.kpsoi_flipped, True)\n\n    def test_polygons_p_is_1(self):\n        self._test_cbaoi_p_is_1(\n            \"augment_polygons\", self.psoi, self.psoi_flipped, False)\n\n    def test_polygons_p_is_1__deterministic(self):\n        self._test_cbaoi_p_is_1(\n            \"augment_polygons\", self.psoi, self.psoi_flipped, True)\n\n    def test_line_strings_p_is_1(self):\n        self._test_cbaoi_p_is_1(\n            \"augment_line_strings\", self.lsoi, self.lsoi_flipped, False)\n\n    def test_line_strings_p_is_1__deterministic(self):\n        self._test_cbaoi_p_is_1(\n            \"augment_line_strings\", self.lsoi, self.lsoi_flipped, True)\n\n    def test_bounding_boxes_p_is_1(self):\n        self._test_cbaoi_p_is_1(\n            \"augment_bounding_boxes\", self.bbsoi, self.bbsoi_flipped, False)\n\n    def test_bounding_boxes_p_is_1__deterministic(self):\n        self._test_cbaoi_p_is_1(\n            \"augment_bounding_boxes\", self.bbsoi, self.bbsoi_flipped, True)\n\n    def _test_cbaoi_p_is_1(self, augf_name, cbaoi, cbaoi_flipped,\n                           deterministic):\n        aug = self.create_aug(1.0)\n        if deterministic:\n            aug = aug.to_deterministic()\n\n        for _ in sm.xrange(3):\n            observed = getattr(aug, augf_name)(cbaoi)\n            assert_cbaois_equal(observed, cbaoi_flipped)\n\n    def test_heatmaps_p_is_1(self):\n        aug = self.create_aug(1.0)\n        heatmaps = self.heatmaps\n        observed = aug.augment_heatmaps(heatmaps)\n        assert observed.shape == heatmaps.shape\n        assert np.isclose(observed.min_value, heatmaps.min_value,\n                          rtol=0, atol=1e-6)\n        assert np.isclose(observed.max_value, heatmaps.max_value,\n                          rtol=0, atol=1e-6)\n        assert np.array_equal(observed.get_arr(),\n                              self.heatmaps_flipped.get_arr())\n\n    def test_segmaps_p_is_1(self):\n        aug = self.create_aug(1.0)\n        observed = aug.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == self.segmaps.shape\n        assert np.array_equal(observed.get_arr(),\n                              self.segmaps_flipped.get_arr())\n\n    def test_images_p_is_050(self):\n        aug = self.create_aug(0.5)\n\n        nb_iterations = 1000\n        nb_images_flipped = 0\n        for _ in sm.xrange(nb_iterations):\n            observed = aug.augment_images(self.images)\n            if np.array_equal(observed, self.images_flipped):\n                nb_images_flipped += 1\n\n        assert np.isclose(nb_images_flipped/nb_iterations,\n                          0.5, rtol=0, atol=0.1)\n\n    def test_images_p_is_050__deterministic(self):\n        aug = self.create_aug(0.5).to_deterministic()\n\n        nb_iterations = 1000\n        nb_images_flipped_det = 0\n        for _ in sm.xrange(nb_iterations):\n            observed = aug.augment_images(self.images)\n            if np.array_equal(observed, self.images_flipped):\n                nb_images_flipped_det += 1\n\n        assert nb_images_flipped_det in [0, nb_iterations]\n\n    def test_keypoints_p_is_050(self):\n        self._test_cbaoi_p_is_050(\n            \"augment_keypoints\", self.kpsoi, self.kpsoi_flipped)\n\n    def test_keypoints_p_is_050__deterministic(self):\n        self._test_cbaoi_p_is_050__deterministic(\n            \"augment_keypoints\", self.kpsoi, self.kpsoi_flipped)\n\n    def test_polygons_p_is_050(self):\n        self._test_cbaoi_p_is_050(\n            \"augment_polygons\", self.psoi, self.psoi_flipped)\n\n    def test_polygons_p_is_050__deterministic(self):\n        self._test_cbaoi_p_is_050__deterministic(\n            \"augment_polygons\", self.psoi, self.psoi_flipped)\n\n    def test_line_strings_p_is_050(self):\n        self._test_cbaoi_p_is_050(\n            \"augment_line_strings\", self.lsoi, self.lsoi_flipped)\n\n    def test_line_strings_p_is_050__deterministic(self):\n        self._test_cbaoi_p_is_050__deterministic(\n            \"augment_line_strings\", self.lsoi, self.lsoi_flipped)\n\n    def test_bounding_boxes_p_is_050(self):\n        self._test_cbaoi_p_is_050(\n            \"augment_bounding_boxes\", self.bbsoi, self.bbsoi_flipped)\n\n    def test_bounding_boxes_p_is_050__deterministic(self):\n        self._test_cbaoi_p_is_050__deterministic(\n            \"augment_bounding_boxes\", self.bbsoi, self.bbsoi_flipped)\n\n    def _test_cbaoi_p_is_050(self, augf_name, cbaoi, cbaoi_flipped):\n        aug = self.create_aug(0.5)\n\n        nb_iterations = 250\n        nb_cbaoi_flipped = 0\n        for _ in sm.xrange(nb_iterations):\n            observed = getattr(aug, augf_name)(cbaoi)\n            if np.allclose(observed[0].items[0].coords,\n                           cbaoi_flipped[0].items[0].coords):\n                nb_cbaoi_flipped += 1\n\n        assert np.isclose(nb_cbaoi_flipped/nb_iterations,\n                          0.5, rtol=0, atol=0.2)\n\n    def _test_cbaoi_p_is_050__deterministic(self, augf_name, cbaoi,\n                                            cbaoi_flipped):\n        aug = self.create_aug(0.5).to_deterministic()\n\n        nb_iterations = 10\n        nb_cbaoi_flipped = 0\n        for _ in sm.xrange(nb_iterations):\n            observed = getattr(aug, augf_name)(cbaoi)\n            if np.allclose(observed[0].items[0].coords,\n                           cbaoi_flipped[0].items[0].coords):\n                nb_cbaoi_flipped += 1\n\n        assert nb_cbaoi_flipped in [0, nb_iterations]\n\n    def test_list_of_images_p_is_050(self):\n        images_multi = [self.image, self.image]\n        aug = self.create_aug(0.5)\n        nb_iterations = 1000\n        nb_flipped_by_pos = [0] * len(images_multi)\n        for _ in sm.xrange(nb_iterations):\n            observed = aug.augment_images(images_multi)\n            for i in sm.xrange(len(images_multi)):\n                if np.array_equal(observed[i], self.image_flipped):\n                    nb_flipped_by_pos[i] += 1\n\n        assert np.allclose(nb_flipped_by_pos,\n                           500, rtol=0, atol=100)\n\n    def test_list_of_images_p_is_050__deterministic(self):\n        images_multi = [self.image, self.image]\n        aug = self.create_aug(0.5).to_deterministic()\n        nb_iterations = 10\n        nb_flipped_by_pos_det = [0] * len(images_multi)\n        for _ in sm.xrange(nb_iterations):\n            observed = aug.augment_images(images_multi)\n            for i in sm.xrange(len(images_multi)):\n                if np.array_equal(observed[i], self.image_flipped):\n                    nb_flipped_by_pos_det[i] += 1\n\n        for val in nb_flipped_by_pos_det:\n            assert val in [0, nb_iterations]\n\n    def test_images_p_is_stochastic_parameter(self):\n        aug = self.create_aug(p=iap.Choice([0, 1], p=[0.7, 0.3]))\n        seen = [0, 0]\n        for _ in sm.xrange(1000):\n            observed = aug.augment_image(self.image)\n            if np.array_equal(observed, self.image):\n                seen[0] += 1\n            elif np.array_equal(observed, self.image_flipped):\n                seen[1] += 1\n            else:\n                assert False\n        assert np.allclose(seen, [700, 300], rtol=0, atol=75)\n\n    def test_invalid_datatype_for_p_results_in_failure(self):\n        with self.assertRaises(Exception):\n            _ = self.create_aug(p=\"test\")\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = self.create_aug(1.0)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_get_parameters(self):\n        aug = self.create_aug(p=0.5)\n        params = aug.get_parameters()\n        assert is_parameter_instance(params[0], iap.Binomial)\n        assert is_parameter_instance(params[0].p, iap.Deterministic)\n        assert 0.5 - 1e-4 < params[0].p.value < 0.5 + 1e-4\n\n    def test_other_dtypes_bool(self):\n        aug = self.create_aug(1.0)\n\n        image = self.create_arr(True, bool)\n        expected = self.create_arr_flipped(True, bool)\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.type == image.dtype.type\n        assert np.all(image_aug == expected)\n\n    def test_other_dtypes_uint_int(self):\n        aug = self.create_aug(1.0)\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = self.create_arr(value, dtype)\n                expected = self.create_arr_flipped(value, dtype)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.name == dtype\n                assert np.array_equal(image_aug, expected)\n\n    def test_other_dtypes_float(self):\n        aug = self.create_aug(1.0)\n        try:\n            f128 = [np.dtype(\"float128\").name]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n        values = [5000, 1000**2, 1000**3, 1000**4]\n        for dtype, value in zip(dtypes, values):\n            with self.subTest(dtype=dtype):\n                atol = (1e-9 * value\n                        if dtype != \"float16\"\n                        else 1e-3 * value)\n                image = self.create_arr(value, dtype)\n                expected = self.create_arr_flipped(value, dtype)\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.name == dtype\n                assert np.allclose(image_aug, expected, atol=atol)\n\n    def test_pickleable(self):\n        aug = self.create_aug(0.5)\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\nclass TestFliplr(_TestFliplrAndFlipudBase, unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        base_img = np.array([[0, 0, 1],\n                             [0, 0, 1],\n                             [0, 1, 1]], dtype=np.uint8)\n        return base_img[:, :, np.newaxis]\n\n    @property\n    def image_flipped(self):\n        base_img_flipped = np.array([[1, 0, 0],\n                                     [1, 0, 0],\n                                     [1, 1, 0]], dtype=np.uint8)\n        return base_img_flipped[:, :, np.newaxis]\n\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([\n            [0.00, 0.50, 0.75],\n            [0.00, 0.50, 0.75],\n            [0.75, 0.75, 0.75],\n        ])\n        return HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def heatmaps_flipped(self):\n        heatmaps_arr = np.float32([\n            [0.75, 0.50, 0.00],\n            [0.75, 0.50, 0.00],\n            [0.75, 0.75, 0.75],\n        ])\n        return HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def segmaps(self):\n        segmaps_arr = np.int32([\n            [0, 1, 2],\n            [0, 1, 2],\n            [2, 2, 2],\n        ])\n        return SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def segmaps_flipped(self):\n        segmaps_arr = np.int32([\n            [2, 1, 0],\n            [2, 1, 0],\n            [2, 2, 2],\n        ])\n        return SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=0),\n               ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        return [ia.KeypointsOnImage(kps, shape=self.image.shape)]\n\n    @property\n    def kpsoi_flipped(self):\n        kps = [ia.Keypoint(x=3-0, y=0),\n               ia.Keypoint(x=3-1, y=1),\n               ia.Keypoint(x=3-2, y=2)]\n        return [ia.KeypointsOnImage(kps, shape=self.image.shape)]\n\n    @property\n    def psoi(self):\n        polygons = [ia.Polygon([(0, 0), (2, 0), (2, 2)])]\n        return [ia.PolygonsOnImage(polygons, shape=self.image.shape)]\n\n    @property\n    def psoi_flipped(self):\n        polygons = [ia.Polygon([(3-0, 0), (3-2, 0), (3-2, 2)])]\n        return [ia.PolygonsOnImage(polygons, shape=self.image.shape)]\n\n    @property\n    def lsoi(self):\n        ls = [ia.LineString([(0, 0), (2, 0), (2, 2)])]\n        return [ia.LineStringsOnImage(ls, shape=self.image.shape)]\n\n    @property\n    def lsoi_flipped(self):\n        ls = [ia.LineString([(3-0, 0), (3-2, 0), (3-2, 2)])]\n        return [ia.LineStringsOnImage(ls, shape=self.image.shape)]\n\n    @property\n    def bbsoi(self):\n        bbs = [ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)]\n        return [ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)]\n\n    @property\n    def bbsoi_flipped(self):\n        # note that x1 and x2 were inverted (otherwise would be x1>x2)\n        bbs = [ia.BoundingBox(x1=3-2, y1=1, x2=3-0, y2=3)]\n        return [ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)]\n\n    def create_aug(self, *args, **kwargs):\n        return iaa.Fliplr(*args, **kwargs)\n\n    def create_arr(self, value, dtype):\n        arr = np.zeros((3, 3), dtype=dtype)\n        arr[0, 0] = value\n        return arr\n\n    def create_arr_flipped(self, value, dtype):\n        arr = np.zeros((3, 3), dtype=dtype)\n        arr[0, 2] = value\n        return arr\n\n\nclass TestFlipud(_TestFliplrAndFlipudBase, unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        base_img = np.array([[0, 0, 1],\n                             [0, 0, 1],\n                             [0, 1, 1]], dtype=np.uint8)\n        return base_img[:, :, np.newaxis]\n\n    @property\n    def image_flipped(self):\n        base_img_flipped = np.array([[0, 1, 1],\n                                     [0, 0, 1],\n                                     [0, 0, 1]], dtype=np.uint8)\n        return base_img_flipped[:, :, np.newaxis]\n\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([\n            [0.00, 0.50, 0.75],\n            [0.00, 0.50, 0.75],\n            [0.75, 0.75, 0.75],\n        ])\n        return HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def heatmaps_flipped(self):\n        heatmaps_arr = np.float32([\n            [0.75, 0.75, 0.75],\n            [0.00, 0.50, 0.75],\n            [0.00, 0.50, 0.75],\n        ])\n        return HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def segmaps(self):\n        segmaps_arr = np.int32([\n            [0, 1, 2],\n            [0, 1, 2],\n            [2, 2, 2],\n        ])\n        return SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def segmaps_flipped(self):\n        segmaps_arr = np.int32([\n            [2, 2, 2],\n            [0, 1, 2],\n            [0, 1, 2],\n        ])\n        return SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=0),\n               ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        return [ia.KeypointsOnImage(kps, shape=self.image.shape)]\n\n    @property\n    def kpsoi_flipped(self):\n        kps = [ia.Keypoint(x=0, y=3-0),\n               ia.Keypoint(x=1, y=3-1),\n               ia.Keypoint(x=2, y=3-2)]\n        return [ia.KeypointsOnImage(kps, shape=self.image.shape)]\n\n    @property\n    def psoi(self):\n        polygons = [ia.Polygon([(0, 0), (2, 0), (2, 2)])]\n        return [ia.PolygonsOnImage(polygons, shape=self.image.shape)]\n\n    @property\n    def psoi_flipped(self):\n        polygons = [ia.Polygon([(0, 3-0), (2, 3-0), (2, 3-2)])]\n        return [ia.PolygonsOnImage(polygons, shape=self.image.shape)]\n\n    @property\n    def lsoi(self):\n        ls = [ia.LineString([(0, 0), (2, 0), (2, 2)])]\n        return [ia.LineStringsOnImage(ls, shape=self.image.shape)]\n\n    @property\n    def lsoi_flipped(self):\n        ls = [ia.LineString([(0, 3-0), (2, 3-0), (2, 3-2)])]\n        return [ia.LineStringsOnImage(ls, shape=self.image.shape)]\n\n    @property\n    def bbsoi(self):\n        bbs = [ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)]\n        return [ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)]\n\n    @property\n    def bbsoi_flipped(self):\n        # note that y1 and y2 were inverted (otherwise would be y1>y2)\n        bbs = [ia.BoundingBox(x1=0, y1=3-3, x2=2, y2=3-1)]\n        return [ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)]\n\n    def create_aug(self, *args, **kwargs):\n        return iaa.Flipud(*args, **kwargs)\n\n    def create_arr(self, value, dtype):\n        arr = np.zeros((3, 3), dtype=dtype)\n        arr[0, 0] = value\n        return arr\n\n    def create_arr_flipped(self, value, dtype):\n        arr = np.zeros((3, 3), dtype=dtype)\n        arr[2, 0] = value\n        return arr\n\n\nclass Test_fliplr(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @mock.patch(\"imgaug.augmenters.flip._fliplr_sliced\")\n    @mock.patch(\"imgaug.augmenters.flip._fliplr_cv2\")\n    def test__fliplr_cv2_called_mocked(self, mock_cv2, mock_sliced):\n        for dtype in [\"uint8\", \"uint16\", \"int8\", \"int16\"]:\n            mock_cv2.reset_mock()\n            mock_sliced.reset_mock()\n            arr = np.zeros((1, 1), dtype=dtype)\n\n            _ = fliplib.fliplr(arr)\n\n            mock_cv2.assert_called_once_with(arr)\n            assert mock_sliced.call_count == 0\n\n    @mock.patch(\"imgaug.augmenters.flip._fliplr_sliced\")\n    @mock.patch(\"imgaug.augmenters.flip._fliplr_cv2\")\n    def test__fliplr_sliced_called_mocked(self, mock_cv2, mock_sliced):\n        try:\n            f128 = [np.dtype(\"float128\").name]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\n            \"bool\",\n            \"uint32\", \"uint64\",\n            \"int32\", \"int64\",\n            \"float16\", \"float32\", \"float64\"\n        ] + f128\n\n        for dtype in dtypes:\n            mock_cv2.reset_mock()\n            mock_sliced.reset_mock()\n            arr = np.zeros((1, 1), dtype=dtype)\n\n            _ = fliplib.fliplr(arr)\n\n            assert mock_cv2.call_count == 0\n            mock_sliced.assert_called_once_with(arr)\n\n    def test__fliplr_cv2_2d(self):\n        self._test__fliplr_subfunc_n_channels(fliplib._fliplr_cv2, None)\n\n    def test__fliplr_cv2_3d_single_channel(self):\n        self._test__fliplr_subfunc_n_channels(fliplib._fliplr_cv2, 1)\n\n    def test__fliplr_cv2_3d_three_channels(self):\n        self._test__fliplr_subfunc_n_channels(fliplib._fliplr_cv2, 3)\n\n    def test__fliplr_cv2_3d_four_channels(self):\n        self._test__fliplr_subfunc_n_channels(fliplib._fliplr_cv2, 4)\n\n    def test__fliplr_sliced_2d(self):\n        self._test__fliplr_subfunc_n_channels(fliplib._fliplr_sliced, None)\n\n    def test__fliplr_sliced_3d_single_channel(self):\n        self._test__fliplr_subfunc_n_channels(fliplib._fliplr_sliced, 1)\n\n    def test__fliplr_sliced_3d_three_channels(self):\n        self._test__fliplr_subfunc_n_channels(fliplib._fliplr_sliced, 3)\n\n    def test__fliplr_sliced_3d_four_channels(self):\n        self._test__fliplr_subfunc_n_channels(fliplib._fliplr_sliced, 4)\n\n    def test__fliplr_sliced_3d_513_channels(self):\n        self._test__fliplr_subfunc_n_channels(fliplib._fliplr_sliced, 513)\n\n    @classmethod\n    def _test__fliplr_subfunc_n_channels(cls, func, nb_channels):\n        arr = np.uint8([\n            [0, 1, 2, 3],\n            [4, 5, 6, 7],\n            [10, 11, 12, 13]\n        ])\n        if nb_channels is not None:\n            arr = np.tile(arr[..., np.newaxis], (1, 1, nb_channels))\n            for c in sm.xrange(nb_channels):\n                arr[..., c] += c\n\n        arr_flipped = func(arr)\n\n        expected = np.uint8([\n            [3, 2, 1, 0],\n            [7, 6, 5, 4],\n            [13, 12, 11, 10]\n        ])\n        if nb_channels is not None:\n            expected = np.tile(expected[..., np.newaxis], (1, 1, nb_channels))\n            for c in sm.xrange(nb_channels):\n                expected[..., c] += c\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == arr.shape\n        assert np.array_equal(arr_flipped, expected)\n\n    def test_zero_height_arr_cv2(self):\n        arr = np.zeros((0, 4, 1), dtype=np.uint8)\n        arr_flipped = fliplib._fliplr_cv2(arr)\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == (0, 4, 1)\n\n    def test_zero_width_arr_cv2(self):\n        arr = np.zeros((4, 0, 1), dtype=np.uint8)\n        arr_flipped = fliplib._fliplr_cv2(arr)\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == (4, 0, 1)\n\n    def test_zero_channels_arr_cv2(self):\n        arr = np.zeros((4, 1, 0), dtype=np.uint8)\n        arr_flipped = fliplib._fliplr_cv2(arr)\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == (4, 1, 0)\n\n    def test_513_channels_arr_cv2(self):\n        arr = np.zeros((1, 2, 513), dtype=np.uint8)\n        arr[:, 0, :] = 0\n        arr[:, 1, :] = 255\n        arr[0, 0, 0] = 1\n        arr[0, 1, 0] = 254\n        arr[0, 0, 512] = 2\n        arr[0, 1, 512] = 253\n\n        arr_flipped = fliplib._fliplr_cv2(arr)\n\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == (1, 2, 513)\n        assert arr_flipped[0, 1, 0] == 1\n        assert arr_flipped[0, 0, 0] == 254\n        assert arr_flipped[0, 1, 512] == 2\n        assert arr_flipped[0, 0, 512] == 253\n        assert np.all(arr_flipped[0, 0, 1:-2] == 255)\n        assert np.all(arr_flipped[0, 1, 1:-2] == 0)\n\n    def test_zero_height_arr_sliced(self):\n        arr = np.zeros((0, 4, 1), dtype=np.uint8)\n        arr_flipped = fliplib._fliplr_sliced(arr)\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == (0, 4, 1)\n\n    def test_zero_width_arr_sliced(self):\n        arr = np.zeros((4, 0, 1), dtype=np.uint8)\n        arr_flipped = fliplib._fliplr_sliced(arr)\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == (4, 0, 1)\n\n    def test_zero_channels_arr_sliced(self):\n        arr = np.zeros((4, 1, 0), dtype=np.uint8)\n        arr_flipped = fliplib._fliplr_sliced(arr)\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == (4, 1, 0)\n\n    def test_513_channels_arr_sliced(self):\n        arr = np.zeros((1, 2, 513), dtype=np.uint8)\n        arr[:, 0, :] = 0\n        arr[:, 1, :] = 255\n        arr[0, 0, 0] = 1\n        arr[0, 1, 0] = 254\n        arr[0, 0, 512] = 2\n        arr[0, 1, 512] = 253\n\n        arr_flipped = fliplib._fliplr_sliced(arr)\n\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == (1, 2, 513)\n        assert arr_flipped[0, 1, 0] == 1\n        assert arr_flipped[0, 0, 0] == 254\n        assert arr_flipped[0, 1, 512] == 2\n        assert arr_flipped[0, 0, 512] == 253\n        assert np.all(arr_flipped[0, 0, 1:-2] == 255)\n        assert np.all(arr_flipped[0, 1, 1:-2] == 0)\n\n    def test_bool_faithful(self):\n        arr = np.array([[False, False, True]], dtype=bool)\n        arr_flipped = fliplib.fliplr(arr)\n        expected = np.array([[True, False, False]], dtype=bool)\n        assert arr_flipped.dtype.name == \"bool\"\n        assert arr_flipped.shape == (1, 3)\n        assert np.array_equal(arr_flipped, expected)\n\n    def test_uint_int_faithful(self):\n        dts = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n               \"int8\", \"int16\", \"int32\", \"int64\"]\n        for dt in dts:\n            with self.subTest(dtype=dt):\n                dt = np.dtype(dt)\n                minv, center, maxv = iadt.get_value_range_of_dtype(dt)\n                center = int(center)\n                arr = np.array([[minv, center, maxv]], dtype=dt)\n\n                arr_flipped = fliplib.fliplr(arr)\n\n                expected = np.array([[maxv, center, minv]], dtype=dt)\n                assert arr_flipped.dtype.name == dt.name\n                assert arr_flipped.shape == (1, 3)\n                assert np.array_equal(arr_flipped, expected)\n\n    def test_float_faithful_to_min_max(self):\n        try:\n            f128 = [np.dtype(\"float128\").name]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n\n        for dt in dtypes:\n            with self.subTest(dtype=dt):\n                dt = np.dtype(dt)\n                minv, center, maxv = iadt.get_value_range_of_dtype(dt)\n                center = int(center)\n                atol = 1e-4 if dt.name == \"float16\" else 1e-8\n                arr = np.array([[minv, center, maxv]], dtype=dt)\n\n                arr_flipped = fliplib.fliplr(arr)\n\n                expected = np.array([[maxv, center, minv]], dtype=dt)\n                assert arr_flipped.dtype.name == dt.name\n                assert arr_flipped.shape == (1, 3)\n                assert np.allclose(arr_flipped, expected, rtol=0, atol=atol)\n\n    def test_float_faithful_to_large_values(self):\n        try:\n            f128 = [np.dtype(\"float128\").name]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dts = [\"float16\", \"float32\", \"float64\"] + f128\n        values = [\n            [0.01, 0.1, 1.0, 10.0**1, 10.0**2],  # float16\n            [0.01, 0.1, 1.0, 10.0**1, 10.0**2, 10.0**4, 10.0**6],  # float32\n            [0.01, 0.1, 1.0, 10.0**1, 10.0**2, 10.0**6, 10.0**10],  # float64\n            [0.01, 0.1, 1.0, 10.0**1, 10.0**2, 10.0**7, 10.0**11],  # float128\n        ]\n        for dt, values_i in zip(dts, values):\n            for value in values_i:\n                with self.subTest(dtype=dt, value=value):\n                    dt = np.dtype(dt)\n                    minv, center, maxv = -value, 0.0, value\n                    atol = 1e-4 if dt.name == \"float16\" else 1e-8\n                    arr = np.array([[minv, center, maxv]], dtype=dt)\n\n                    arr_flipped = fliplib.fliplr(arr)\n\n                    expected = np.array([[maxv, center, minv]], dtype=dt)\n                    assert arr_flipped.dtype.name == dt.name\n                    assert arr_flipped.shape == (1, 3)\n                    assert np.allclose(arr_flipped, expected, rtol=0, atol=atol)\n\n\nclass Test_flipud(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test__flipud_2d(self):\n        self._test__flipud_subfunc_n_channels(fliplib.flipud, None)\n\n    def test__flipud_3d_single_channel(self):\n        self._test__flipud_subfunc_n_channels(fliplib.flipud, 1)\n\n    def test__flipud_3d_three_channels(self):\n        self._test__flipud_subfunc_n_channels(fliplib.flipud, 3)\n\n    def test__flipud_3d_four_channels(self):\n        self._test__flipud_subfunc_n_channels(fliplib.flipud, 4)\n\n    @classmethod\n    def _test__flipud_subfunc_n_channels(cls, func, nb_channels):\n        arr = np.uint8([\n            [0, 1, 2, 3],\n            [4, 5, 6, 7],\n            [10, 11, 12, 13]\n        ])\n        if nb_channels is not None:\n            arr = np.tile(arr[..., np.newaxis], (1, 1, nb_channels))\n            for c in sm.xrange(nb_channels):\n                arr[..., c] += c\n\n        arr_flipped = func(arr)\n\n        expected = np.uint8([\n            [10, 11, 12, 13],\n            [4, 5, 6, 7],\n            [0, 1, 2, 3]\n        ])\n        if nb_channels is not None:\n            expected = np.tile(expected[..., np.newaxis], (1, 1, nb_channels))\n            for c in sm.xrange(nb_channels):\n                expected[..., c] += c\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == arr.shape\n        assert np.array_equal(arr_flipped, expected)\n\n    def test_zero_width_arr(self):\n        arr = np.zeros((4, 0, 1), dtype=np.uint8)\n        arr_flipped = fliplib.flipud(arr)\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == (4, 0, 1)\n\n    def test_zero_height_arr(self):\n        arr = np.zeros((0, 4, 1), dtype=np.uint8)\n        arr_flipped = fliplib.flipud(arr)\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == (0, 4, 1)\n\n    def test_zero_channels_arr(self):\n        arr = np.zeros((4, 1, 0), dtype=np.uint8)\n        arr_flipped = fliplib.flipud(arr)\n        assert arr_flipped.dtype.name == \"uint8\"\n        assert arr_flipped.shape == (4, 1, 0)\n\n    def test_bool_faithful(self):\n        arr = np.array([[False], [False], [True]], dtype=bool)\n        arr_flipped = fliplib.flipud(arr)\n        expected = np.array([[True], [False], [False]], dtype=bool)\n        assert arr_flipped.dtype.name == \"bool\"\n        assert arr_flipped.shape == (3, 1)\n        assert np.array_equal(arr_flipped, expected)\n\n    def test_uint_int_faithful(self):\n        dts = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n               \"int8\", \"int16\", \"int32\", \"int64\"]\n        for dt in dts:\n            with self.subTest(dtype=dt):\n                dt = np.dtype(dt)\n                minv, center, maxv = iadt.get_value_range_of_dtype(dt)\n                center = int(center)\n                arr = np.array([[minv], [center], [maxv]], dtype=dt)\n\n                arr_flipped = fliplib.flipud(arr)\n\n                expected = np.array([[maxv], [center], [minv]], dtype=dt)\n                assert arr_flipped.dtype.name == dt.name\n                assert arr_flipped.shape == (3, 1)\n                assert np.array_equal(arr_flipped, expected)\n\n    def test_float_faithful_to_min_max(self):\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n        dts = [\"float16\", \"float32\", \"float64\"] + f128\n        for dt in dts:\n            with self.subTest(dtype=dt):\n                dt = np.dtype(dt)\n                minv, center, maxv = iadt.get_value_range_of_dtype(dt)\n                center = int(center)\n                atol = 1e-4 if dt.name == \"float16\" else 1e-8\n                arr = np.array([[minv], [center], [maxv]], dtype=dt)\n\n                arr_flipped = fliplib.flipud(arr)\n\n                expected = np.array([[maxv], [center], [minv]], dtype=dt)\n                assert arr_flipped.dtype.name == dt.name\n                assert arr_flipped.shape == (3, 1)\n                assert np.allclose(arr_flipped, expected, rtol=0, atol=atol)\n\n    def test_float_faithful_to_large_values(self):\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n        dts = [\"float16\", \"float32\", \"float64\"] + f128\n        values = [\n            [0.01, 0.1, 1.0, 10.0**1, 10.0**2],  # float16\n            [0.01, 0.1, 1.0, 10.0**1, 10.0**2, 10.0**4, 10.0**6],  # float32\n            [0.01, 0.1, 1.0, 10.0**1, 10.0**2, 10.0**6, 10.0**10],  # float64\n            [0.01, 0.1, 1.0, 10.0**1, 10.0**2, 10.0**7, 10.0**11],  # float128\n        ]\n        for dt, values_i in zip(dts, values):\n            for value in values_i:\n                with self.subTest(dtype=dt, value=value):\n                    dt = np.dtype(dt)\n                    minv, center, maxv = -value, 0.0, value\n                    atol = 1e-4 if dt.name == \"float16\" else 1e-8\n                    arr = np.array([[minv], [center], [maxv]], dtype=dt)\n\n                    arr_flipped = fliplib.flipud(arr)\n\n                    expected = np.array([[maxv], [center], [minv]], dtype=dt)\n                    assert arr_flipped.dtype.name == dt.name\n                    assert arr_flipped.shape == (3, 1)\n                    assert np.allclose(arr_flipped, expected, rtol=0, atol=atol)\n"
  },
  {
    "path": "test/augmenters/test_geometric.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport itertools\nimport warnings\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport six.moves as sm\nimport skimage.morphology\nimport cv2\n\nimport imgaug as ia\nfrom imgaug import random as iarandom\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug import dtypes as iadt\nfrom imgaug.testutils import (\n    array_equal_lists, keypoints_equal, reseed, assert_cbaois_equal,\n    runtest_pickleable_uint8_img, assertWarns, is_parameter_instance)\nfrom imgaug.augmentables.heatmaps import HeatmapsOnImage\nfrom imgaug.augmentables.segmaps import SegmentationMapsOnImage\nimport imgaug.augmenters.geometric as geometriclib\n\n\ndef _assert_same_min_max(observed, actual):\n    assert np.isclose(observed.min_value, actual.min_value, rtol=0, atol=1e-6)\n    assert np.isclose(observed.max_value, actual.max_value, rtol=0, atol=1e-6)\n\n\ndef _assert_same_shape(observed, actual):\n    assert observed.shape == actual.shape\n\n# TODO add more tests for Affine .mode\n# TODO add more tests for Affine shear\n\n\nclass TestAffine(unittest.TestCase):\n    def test_get_parameters(self):\n        aug = iaa.Affine(scale=1, translate_px=2, rotate=3, shear=4,\n                         order=1, cval=0, mode=\"constant\", backend=\"cv2\",\n                         fit_output=True)\n\n        params = aug.get_parameters()\n\n        assert is_parameter_instance(params[0], iap.Deterministic)  # scale\n        assert isinstance(params[1], tuple)  # translate\n        assert is_parameter_instance(params[2], iap.Deterministic)  # rotate\n        assert is_parameter_instance(params[3], iap.Deterministic)  # shear\n        assert params[0].value == 1  # scale\n        assert params[1][0].value == 2  # translate\n        assert params[2].value == 3  # rotate\n        assert params[3].value == 4  # shear\n        assert params[4].value == 1  # order\n        assert params[5].value == 0  # cval\n        assert params[6].value == \"constant\"  # mode\n        assert params[7] == \"cv2\"  # backend\n        assert params[8] is True  # fit_output\n\n\nclass TestAffine___init__(unittest.TestCase):\n    def test___init___scale_is_stochastic_parameter(self):\n        aug = iaa.Affine(scale=iap.Uniform(0.7, 0.9))\n\n        assert is_parameter_instance(aug.scale, iap.Uniform)\n        assert is_parameter_instance(aug.scale.a, iap.Deterministic)\n        assert is_parameter_instance(aug.scale.b, iap.Deterministic)\n        assert 0.7 - 1e-8 < aug.scale.a.value < 0.7 + 1e-8\n        assert 0.9 - 1e-8 < aug.scale.b.value < 0.9 + 1e-8\n\n    def test___init___translate_percent_is_stochastic_parameter(self):\n        aug = iaa.Affine(translate_percent=iap.Uniform(0.7, 0.9))\n\n        assert isinstance(aug.translate, tuple)\n        assert is_parameter_instance(aug.translate[0], iap.Uniform)\n        assert is_parameter_instance(aug.translate[0].a, iap.Deterministic)\n        assert is_parameter_instance(aug.translate[0].b, iap.Deterministic)\n        assert 0.7 - 1e-8 < aug.translate[0].a.value < 0.7 + 1e-8\n        assert 0.9 - 1e-8 < aug.translate[0].b.value < 0.9 + 1e-8\n        assert aug.translate[1] is None\n        assert aug.translate[2] == \"percent\"\n\n    def test___init___translate_px_is_stochastic_parameter(self):\n        aug = iaa.Affine(translate_px=iap.DiscreteUniform(1, 10))\n\n        assert isinstance(aug.translate, tuple)\n        assert is_parameter_instance(aug.translate[0], iap.DiscreteUniform)\n        assert is_parameter_instance(aug.translate[0].a, iap.Deterministic)\n        assert is_parameter_instance(aug.translate[0].b, iap.Deterministic)\n        assert aug.translate[0].a.value == 1\n        assert aug.translate[0].b.value == 10\n        assert aug.translate[1] is None\n        assert aug.translate[2] == \"px\"\n\n    def test___init___rotate_is_stochastic_parameter(self):\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=iap.Uniform(10, 20),\n                         shear=0)\n\n        assert is_parameter_instance(aug.rotate, iap.Uniform)\n        assert is_parameter_instance(aug.rotate.a, iap.Deterministic)\n        assert aug.rotate.a.value == 10\n        assert is_parameter_instance(aug.rotate.b, iap.Deterministic)\n        assert aug.rotate.b.value == 20\n\n    def test___init___shear_is_stochastic_parameter(self):\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=0,\n                         shear=iap.Uniform(10, 20))\n\n        assert is_parameter_instance(aug.shear, iap.Uniform)\n        assert is_parameter_instance(aug.shear.a, iap.Deterministic)\n        assert aug.shear.a.value == 10\n        assert is_parameter_instance(aug.shear.b, iap.Deterministic)\n        assert aug.shear.b.value == 20\n\n    def test___init___cval_is_all(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=ia.ALL)\n\n        assert is_parameter_instance(aug.cval, iap.Uniform)\n        assert is_parameter_instance(aug.cval.a, iap.Deterministic)\n        assert is_parameter_instance(aug.cval.b, iap.Deterministic)\n        assert aug.cval.a.value == 0\n        assert aug.cval.b.value == 255\n\n    def test___init___cval_is_stochastic_parameter(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=iap.DiscreteUniform(1, 5))\n\n        assert is_parameter_instance(aug.cval, iap.DiscreteUniform)\n        assert is_parameter_instance(aug.cval.a, iap.Deterministic)\n        assert is_parameter_instance(aug.cval.b, iap.Deterministic)\n        assert aug.cval.a.value == 1\n        assert aug.cval.b.value == 5\n\n    def test___init___mode_is_all(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=0, mode=ia.ALL)\n        assert is_parameter_instance(aug.mode, iap.Choice)\n\n    def test___init___mode_is_string(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=0, mode=\"edge\")\n        assert is_parameter_instance(aug.mode, iap.Deterministic)\n        assert aug.mode.value == \"edge\"\n\n    def test___init___mode_is_list(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=0, mode=[\"constant\", \"edge\"])\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert (\n            len(aug.mode.a) == 2\n            and \"constant\" in aug.mode.a\n            and \"edge\" in aug.mode.a)\n\n    def test___init___mode_is_stochastic_parameter(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=0, mode=iap.Choice([\"constant\", \"edge\"]))\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert (\n            len(aug.mode.a) == 2\n            and \"constant\" in aug.mode.a\n            and \"edge\" in aug.mode.a)\n\n    def test___init___fit_output_is_true(self):\n        aug = iaa.Affine(fit_output=True)\n        assert aug.fit_output is True\n\n    # ------------\n    # exceptions for bad inputs\n    # ------------\n    def test___init___bad_datatype_for_scale_fails(self):\n        with self.assertRaises(Exception):\n            _ = iaa.Affine(scale=False)\n\n    def test___init___bad_datatype_for_translate_px_fails(self):\n        with self.assertRaises(Exception):\n            _ = iaa.Affine(translate_px=False)\n\n    def test___init___bad_datatype_for_translate_percent_fails(self):\n        with self.assertRaises(Exception):\n            _ = iaa.Affine(translate_percent=False)\n\n    def test___init___bad_datatype_for_rotate_fails(self):\n        with self.assertRaises(Exception):\n            _ = iaa.Affine(scale=1.0, translate_px=0, rotate=False, shear=0,\n                           cval=0)\n\n    def test___init___bad_datatype_for_shear_fails(self):\n        with self.assertRaises(Exception):\n            _ = iaa.Affine(scale=1.0, translate_px=0, rotate=0, shear=False,\n                           cval=0)\n\n    def test___init___bad_datatype_for_cval_fails(self):\n        with self.assertRaises(Exception):\n            _ = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                           cval=None)\n\n    def test___init___bad_datatype_for_mode_fails(self):\n        with self.assertRaises(Exception):\n            _ = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                           cval=0, mode=False)\n\n    def test___init___bad_datatype_for_order_fails(self):\n        # bad order datatype in case of backend=cv2\n        with self.assertRaises(Exception):\n            _ = iaa.Affine(backend=\"cv2\", order=\"test\")\n\n    def test___init___nonexistent_order_for_cv2_fails(self):\n        # non-existent order in case of backend=cv2\n        with self.assertRaises(AssertionError):\n            _ = iaa.Affine(backend=\"cv2\", order=-1)\n\n\n# TODO add test with multiple images\nclass TestAffine_noop(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def base_img(self):\n        base_img = np.array([[0, 0, 0],\n                             [0, 255, 0],\n                             [0, 0, 0]], dtype=np.uint8)\n        return base_img[:, :, np.newaxis]\n\n    @property\n    def images(self):\n        return np.array([self.base_img])\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        return [ia.KeypointsOnImage(kps, shape=self.base_img.shape)]\n\n    @property\n    def psoi(self):\n        polygons = [ia.Polygon([(0, 0), (2, 0), (2, 2)])]\n        return [ia.PolygonsOnImage(polygons, shape=self.base_img.shape)]\n\n    @property\n    def lsoi(self):\n        ls = [ia.LineString([(0, 0), (2, 0), (2, 2)])]\n        return [ia.LineStringsOnImage(ls, shape=self.base_img.shape)]\n\n    @property\n    def bbsoi(self):\n        bbs = [ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)]\n        return [ia.BoundingBoxesOnImage(bbs, shape=self.base_img.shape)]\n\n    def test_image_noop(self):\n        # no translation/scale/rotate/shear, shouldnt change nothing\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=0, shear=0)\n\n        observed = aug.augment_images(self.images)\n\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_image_noop__deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.images)\n\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_image_noop__list(self):\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=0, shear=0)\n\n        observed = aug.augment_images([self.base_img])\n\n        expected = [self.base_img]\n        assert array_equal_lists(observed, expected)\n\n    def test_image_noop__list_and_deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.base_img])\n\n        expected = [self.base_img]\n        assert array_equal_lists(observed, expected)\n\n    def test_keypoints_noop(self):\n        self._test_cba_noop(\"augment_keypoints\", self.kpsoi, False)\n\n    def test_keypoints_noop__deterministic(self):\n        self._test_cba_noop(\"augment_keypoints\", self.kpsoi, True)\n\n    def test_polygons_noop(self):\n        self._test_cba_noop(\"augment_polygons\", self.psoi, False)\n\n    def test_polygons_noop__deterministic(self):\n        self._test_cba_noop(\"augment_polygons\", self.psoi, True)\n\n    def test_line_strings_noop(self):\n        self._test_cba_noop(\"augment_line_strings\", self.lsoi, False)\n\n    def test_line_strings_noop__deterministic(self):\n        self._test_cba_noop(\"augment_line_strings\", self.lsoi, True)\n\n    def test_bounding_boxes_noop(self):\n        self._test_cba_noop(\"augment_bounding_boxes\", self.bbsoi, False)\n\n    def test_bounding_boxes_noop__deterministic(self):\n        self._test_cba_noop(\"augment_bounding_boxes\", self.bbsoi, True)\n\n    @classmethod\n    def _test_cba_noop(cls, augf_name, cbaoi, deterministic):\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=0, shear=0)\n        if deterministic:\n            aug = aug.to_deterministic()\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        expected = cbaoi\n        assert_cbaois_equal(observed, expected)\n\n\n# TODO add test with multiple images\nclass TestAffine_scale(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    # ---------------------\n    # scale: zoom in\n    # ---------------------\n\n    @property\n    def base_img(self):\n        base_img = np.array([[0, 0, 0],\n                             [0, 255, 0],\n                             [0, 0, 0]], dtype=np.uint8)\n        return base_img[:, :, np.newaxis]\n\n    @property\n    def images(self):\n        return np.array([self.base_img])\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        return [ia.KeypointsOnImage(kps, shape=self.base_img.shape)]\n\n    def kpsoi_scaled(self, scale_y, scale_x):\n        coords = np.array([\n            [0, 0],\n            [1, 1],\n            [2, 2]\n        ], dtype=np.float32)\n        coords_scaled = self._scale_coordinates(coords, scale_y, scale_x)\n        return [ia.KeypointsOnImage.from_xy_array(\n                    coords_scaled,\n                    shape=self.base_img.shape)]\n\n    @property\n    def psoi(self):\n        polys = [ia.Polygon([(0, 0), (0, 2), (2, 2)])]\n        return [ia.PolygonsOnImage(polys, shape=self.base_img.shape)]\n\n    def psoi_scaled(self, scale_y, scale_x):\n        coords = np.array([\n            [0, 0],\n            [0, 2],\n            [2, 2]\n        ], dtype=np.float32)\n        coords_scaled = self._scale_coordinates(coords, scale_y, scale_x)\n        return [ia.PolygonsOnImage(\n                    [ia.Polygon(coords_scaled)],\n                    shape=self.base_img.shape)]\n\n    @property\n    def lsoi(self):\n        ls = [ia.LineString([(0, 0), (0, 2), (2, 2)])]\n        return [ia.LineStringsOnImage(ls, shape=self.base_img.shape)]\n\n    def lsoi_scaled(self, scale_y, scale_x):\n        coords = np.array([\n            [0, 0],\n            [0, 2],\n            [2, 2]\n        ], dtype=np.float32)\n        coords_scaled = self._scale_coordinates(coords, scale_y, scale_x)\n        return [ia.LineStringsOnImage(\n                    [ia.LineString(coords_scaled)],\n                    shape=self.base_img.shape)]\n\n    @property\n    def bbsoi(self):\n        bbs = [ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)]\n        return [ia.BoundingBoxesOnImage(bbs, shape=self.base_img.shape)]\n\n    def bbsoi_scaled(self, scale_y, scale_x):\n        coords = np.array([\n            [0, 1],\n            [2, 3]\n        ], dtype=np.float32)\n        coords_scaled = self._scale_coordinates(coords, scale_y, scale_x)\n        return [ia.BoundingBoxesOnImage.from_xyxy_array(\n                    coords_scaled.reshape((1, 4)),\n                    shape=self.base_img.shape)]\n\n    def _scale_coordinates(self, coords, scale_y, scale_x):\n        height, width = self.base_img.shape[0:2]\n        coords_scaled = []\n        for x, y in coords:\n            # the additional +0.5 and -0.5 here makes up for the shift factor\n            # used in the affine matrix generation\n            offset = 0.0\n            x_centered = x - width/2 + offset\n            y_centered = y - height/2 + offset\n            x_new = x_centered * scale_x + width/2 - offset\n            y_new = y_centered * scale_y + height/2 - offset\n            coords_scaled.append((x_new, y_new))\n        return np.float32(coords_scaled)\n\n    @property\n    def scale_zoom_in_outer_pixels(self):\n        base_img = self.base_img\n        outer_pixels = ([], [])\n        for i in sm.xrange(base_img.shape[0]):\n            for j in sm.xrange(base_img.shape[1]):\n                if i != j:\n                    outer_pixels[0].append(i)\n                    outer_pixels[1].append(j)\n        return outer_pixels\n\n    def test_image_scale_zoom_in(self):\n        aug = iaa.Affine(scale=1.75, translate_px=0, rotate=0, shear=0)\n\n        observed = aug.augment_images(self.images)\n\n        outer_pixels = self.scale_zoom_in_outer_pixels\n        assert observed[0][1, 1] > 250\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 20).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 150).all()\n\n    def test_image_scale_zoom_in__deterministic(self):\n        aug = iaa.Affine(scale=1.75, translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.images)\n\n        outer_pixels = self.scale_zoom_in_outer_pixels\n        assert observed[0][1, 1] > 250\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 20).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 150).all()\n\n    def test_image_scale_zoom_in__list(self):\n        aug = iaa.Affine(scale=1.75, translate_px=0, rotate=0, shear=0)\n\n        observed = aug.augment_images([self.base_img])\n\n        outer_pixels = self.scale_zoom_in_outer_pixels\n        assert observed[0][1, 1] > 250\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 20).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 150).all()\n\n    def test_image_scale_zoom_in__list_and_deterministic(self):\n        aug = iaa.Affine(scale=1.75, translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.base_img])\n\n        outer_pixels = self.scale_zoom_in_outer_pixels\n        assert observed[0][1, 1] > 250\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 20).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 150).all()\n\n    def test_keypoints_scale_zoom_in(self):\n        self._test_cba_scale(\n            \"augment_keypoints\", 1.75,\n            self.kpsoi, self.kpsoi_scaled(1.75, 1.75), False)\n\n    def test_keypoints_scale_zoom_in__deterministic(self):\n        self._test_cba_scale(\n            \"augment_keypoints\", 1.75,\n            self.kpsoi, self.kpsoi_scaled(1.75, 1.75), True)\n\n    def test_polygons_scale_zoom_in(self):\n        self._test_cba_scale(\n            \"augment_polygons\", 1.75,\n            self.psoi, self.psoi_scaled(1.75, 1.75), False)\n\n    def test_polygons_scale_zoom_in__deterministic(self):\n        self._test_cba_scale(\n            \"augment_polygons\", 1.75,\n            self.psoi, self.psoi_scaled(1.75, 1.75), True)\n\n    def test_line_strings_scale_zoom_in(self):\n        self._test_cba_scale(\n            \"augment_line_strings\", 1.75,\n            self.lsoi, self.lsoi_scaled(1.75, 1.75), False)\n\n    def test_line_strings_scale_zoom_in__deterministic(self):\n        self._test_cba_scale(\n            \"augment_line_strings\", 1.75,\n            self.lsoi, self.lsoi_scaled(1.75, 1.75), True)\n\n    def test_bounding_boxes_scale_zoom_in(self):\n        self._test_cba_scale(\n            \"augment_bounding_boxes\", 1.75,\n            self.bbsoi, self.bbsoi_scaled(1.75, 1.75), False)\n\n    def test_bounding_boxes_scale_zoom_in__deterministic(self):\n        self._test_cba_scale(\n            \"augment_bounding_boxes\", 1.75,\n            self.bbsoi, self.bbsoi_scaled(1.75, 1.75), True)\n\n    @classmethod\n    def _test_cba_scale(cls, augf_name, scale, cbaoi, cbaoi_scaled,\n                        deterministic):\n        aug = iaa.Affine(scale=scale, translate_px=0, rotate=0, shear=0)\n        if deterministic:\n            aug = aug.to_deterministic()\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(observed, cbaoi_scaled)\n\n    # ---------------------\n    # scale: zoom in only on x axis\n    # ---------------------\n    def test_image_scale_zoom_in_only_x_axis(self):\n        aug = iaa.Affine(scale={\"x\": 1.75, \"y\": 1.0},\n                         translate_px=0, rotate=0, shear=0)\n\n        observed = aug.augment_images(self.images)\n\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[1, 1], [0, 2]] > 20).all()\n        assert (observed[0][[1, 1], [0, 2]] < 150).all()\n        assert (observed[0][0, :] < 5).all()\n        assert (observed[0][2, :] < 5).all()\n\n    def test_image_scale_zoom_in_only_x_axis__deterministic(self):\n        aug = iaa.Affine(scale={\"x\": 1.75, \"y\": 1.0},\n                         translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.images)\n\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[1, 1], [0, 2]] > 20).all()\n        assert (observed[0][[1, 1], [0, 2]] < 150).all()\n        assert (observed[0][0, :] < 5).all()\n        assert (observed[0][2, :] < 5).all()\n\n    def test_image_scale_zoom_in_only_x_axis__list(self):\n        aug = iaa.Affine(scale={\"x\": 1.75, \"y\": 1.0},\n                         translate_px=0, rotate=0, shear=0)\n\n        observed = aug.augment_images([self.base_img])\n\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[1, 1], [0, 2]] > 20).all()\n        assert (observed[0][[1, 1], [0, 2]] < 150).all()\n        assert (observed[0][0, :] < 5).all()\n        assert (observed[0][2, :] < 5).all()\n\n    def test_image_scale_zoom_in_only_x_axis__deterministic_and_list(self):\n        aug = iaa.Affine(scale={\"x\": 1.75, \"y\": 1.0},\n                         translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.base_img])\n\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[1, 1], [0, 2]] > 20).all()\n        assert (observed[0][[1, 1], [0, 2]] < 150).all()\n        assert (observed[0][0, :] < 5).all()\n        assert (observed[0][2, :] < 5).all()\n\n    def test_keypoints_scale_zoom_in_only_x_axis(self):\n        self._test_cba_scale(\n            \"augment_keypoints\", {\"y\": 1.0, \"x\": 1.75}, self.kpsoi,\n            self.kpsoi_scaled(1.0, 1.75), False)\n\n    def test_keypoints_scale_zoom_in_only_x_axis__deterministic(self):\n        self._test_cba_scale(\n            \"augment_keypoints\", {\"y\": 1.0, \"x\": 1.75}, self.kpsoi,\n            self.kpsoi_scaled(1.0, 1.75), True)\n\n    def test_polygons_scale_zoom_in_only_x_axis(self):\n        self._test_cba_scale(\n            \"augment_polygons\", {\"y\": 1.0, \"x\": 1.75}, self.psoi,\n            self.psoi_scaled(1.0, 1.75), False)\n\n    def test_polygons_scale_zoom_in_only_x_axis__deterministic(self):\n        self._test_cba_scale(\n            \"augment_polygons\", {\"y\": 1.0, \"x\": 1.75}, self.psoi,\n            self.psoi_scaled(1.0, 1.75), True)\n\n    def test_line_strings_scale_zoom_in_only_x_axis(self):\n        self._test_cba_scale(\n            \"augment_line_strings\", {\"y\": 1.0, \"x\": 1.75}, self.lsoi,\n            self.lsoi_scaled(1.0, 1.75), False)\n\n    def test_line_strings_scale_zoom_in_only_x_axis__deterministic(self):\n        self._test_cba_scale(\n            \"augment_line_strings\", {\"y\": 1.0, \"x\": 1.75}, self.lsoi,\n            self.lsoi_scaled(1.0, 1.75), True)\n\n    def test_bounding_boxes_scale_zoom_in_only_x_axis(self):\n        self._test_cba_scale(\n            \"augment_bounding_boxes\", {\"y\": 1.0, \"x\": 1.75}, self.bbsoi,\n            self.bbsoi_scaled(1.0, 1.75), False)\n\n    def test_bounding_boxes_scale_zoom_in_only_x_axis__deterministic(self):\n        self._test_cba_scale(\n            \"augment_bounding_boxes\", {\"y\": 1.0, \"x\": 1.75}, self.bbsoi,\n            self.bbsoi_scaled(1.0, 1.75), True)\n\n    # ---------------------\n    # scale: zoom in only on y axis\n    # ---------------------\n    def test_image_scale_zoom_in_only_y_axis(self):\n        aug = iaa.Affine(scale={\"x\": 1.0, \"y\": 1.75},\n                         translate_px=0, rotate=0, shear=0)\n\n        observed = aug.augment_images(self.images)\n\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[0, 2], [1, 1]] > 20).all()\n        assert (observed[0][[0, 2], [1, 1]] < 150).all()\n        assert (observed[0][:, 0] < 5).all()\n        assert (observed[0][:, 2] < 5).all()\n\n    def test_image_scale_zoom_in_only_y_axis__deterministic(self):\n        aug = iaa.Affine(scale={\"x\": 1.0, \"y\": 1.75},\n                         translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.images)\n\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[0, 2], [1, 1]] > 20).all()\n        assert (observed[0][[0, 2], [1, 1]] < 150).all()\n        assert (observed[0][:, 0] < 5).all()\n        assert (observed[0][:, 2] < 5).all()\n\n    def test_image_scale_zoom_in_only_y_axis__list(self):\n        aug = iaa.Affine(scale={\"x\": 1.0, \"y\": 1.75},\n                         translate_px=0, rotate=0, shear=0)\n\n        observed = aug.augment_images([self.base_img])\n\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[0, 2], [1, 1]] > 20).all()\n        assert (observed[0][[0, 2], [1, 1]] < 150).all()\n        assert (observed[0][:, 0] < 5).all()\n        assert (observed[0][:, 2] < 5).all()\n\n    def test_image_scale_zoom_in_only_y_axis__deterministic_and_list(self):\n        aug = iaa.Affine(scale={\"x\": 1.0, \"y\": 1.75},\n                         translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.base_img])\n\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[0, 2], [1, 1]] > 20).all()\n        assert (observed[0][[0, 2], [1, 1]] < 150).all()\n        assert (observed[0][:, 0] < 5).all()\n        assert (observed[0][:, 2] < 5).all()\n\n    def test_keypoints_scale_zoom_in_only_y_axis(self):\n        self._test_cba_scale(\n            \"augment_keypoints\", {\"y\": 1.75, \"x\": 1.0}, self.kpsoi,\n            self.kpsoi_scaled(1.75, 1.0), False)\n\n    def test_keypoints_scale_zoom_in_only_y_axis__deterministic(self):\n        self._test_cba_scale(\n            \"augment_keypoints\", {\"y\": 1.75, \"x\": 1.0}, self.kpsoi,\n            self.kpsoi_scaled(1.75, 1.0), True)\n\n    def test_polygons_scale_zoom_in_only_y_axis(self):\n        self._test_cba_scale(\n            \"augment_polygons\", {\"y\": 1.75, \"x\": 1.0}, self.psoi,\n            self.psoi_scaled(1.75, 1.0), False)\n\n    def test_polygons_scale_zoom_in_only_y_axis__deterministic(self):\n        self._test_cba_scale(\n            \"augment_polygons\", {\"y\": 1.75, \"x\": 1.0}, self.psoi,\n            self.psoi_scaled(1.75, 1.0), True)\n\n    def test_line_strings_scale_zoom_in_only_y_axis(self):\n        self._test_cba_scale(\n            \"augment_polygons\", {\"y\": 1.75, \"x\": 1.0}, self.psoi,\n            self.psoi_scaled(1.75, 1.0), False)\n\n    def test_line_strings_scale_zoom_in_only_y_axis__deterministic(self):\n        self._test_cba_scale(\n            \"augment_line_strings\", {\"y\": 1.75, \"x\": 1.0}, self.lsoi,\n            self.lsoi_scaled(1.75, 1.0), True)\n\n    def test_bounding_boxes_scale_zoom_in_only_y_axis(self):\n        self._test_cba_scale(\n            \"augment_bounding_boxes\", {\"y\": 1.75, \"x\": 1.0}, self.bbsoi,\n            self.bbsoi_scaled(1.75, 1.0), False)\n\n    def test_bounding_boxes_scale_zoom_in_only_y_axis__deterministic(self):\n        self._test_cba_scale(\n            \"augment_bounding_boxes\", {\"y\": 1.75, \"x\": 1.0}, self.bbsoi,\n            self.bbsoi_scaled(1.75, 1.0), True)\n\n    # ---------------------\n    # scale: zoom out\n    # ---------------------\n    # these tests use a 4x4 area of all 255, which is zoomed out to a 4x4 area\n    # in which the center 2x2 area is 255\n    # zoom in should probably be adapted to this style\n    # no separate tests here for x/y axis, should work fine if zoom in works\n    # with that\n\n    @property\n    def scale_zoom_out_base_img(self):\n        return np.ones((4, 4, 1), dtype=np.uint8) * 255\n\n    @property\n    def scale_zoom_out_images(self):\n        return np.array([self.scale_zoom_out_base_img])\n\n    @property\n    def scale_zoom_out_outer_pixels(self):\n        outer_pixels = ([], [])\n        for y in sm.xrange(4):\n            xs = sm.xrange(4) if y in [0, 3] else [0, 3]\n            for x in xs:\n                outer_pixels[0].append(y)\n                outer_pixels[1].append(x)\n        return outer_pixels\n\n    @property\n    def scale_zoom_out_inner_pixels(self):\n        return [1, 1, 2, 2], [1, 2, 1, 2]\n\n    @property\n    def scale_zoom_out_kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=3, y=0),\n               ia.Keypoint(x=0, y=3), ia.Keypoint(x=3, y=3)]\n        return [ia.KeypointsOnImage(kps,\n                                    shape=self.scale_zoom_out_base_img.shape)]\n\n    @property\n    def scale_zoom_out_kpsoi_aug(self):\n        kps_aug = [ia.Keypoint(x=0.765, y=0.765),\n                   ia.Keypoint(x=2.235, y=0.765),\n                   ia.Keypoint(x=0.765, y=2.235),\n                   ia.Keypoint(x=2.235, y=2.235)]\n        return [ia.KeypointsOnImage(kps_aug,\n                                    shape=self.scale_zoom_out_base_img.shape)]\n\n    def test_image_scale_zoom_out(self):\n        aug = iaa.Affine(scale=0.49, translate_px=0, rotate=0, shear=0)\n\n        observed = aug.augment_images(self.scale_zoom_out_images)\n\n        outer_pixels = self.scale_zoom_out_outer_pixels\n        inner_pixels = self.scale_zoom_out_inner_pixels\n        assert (observed[0][outer_pixels] < 25).all()\n        assert (observed[0][inner_pixels] > 200).all()\n\n    def test_image_scale_zoom_out__deterministic(self):\n        aug = iaa.Affine(scale=0.49, translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.scale_zoom_out_images)\n\n        outer_pixels = self.scale_zoom_out_outer_pixels\n        inner_pixels = self.scale_zoom_out_inner_pixels\n        assert (observed[0][outer_pixels] < 25).all()\n        assert (observed[0][inner_pixels] > 200).all()\n\n    def test_image_scale_zoom_out__list(self):\n        aug = iaa.Affine(scale=0.49, translate_px=0, rotate=0, shear=0)\n\n        observed = aug.augment_images([self.scale_zoom_out_base_img])\n\n        outer_pixels = self.scale_zoom_out_outer_pixels\n        inner_pixels = self.scale_zoom_out_inner_pixels\n        assert (observed[0][outer_pixels] < 25).all()\n        assert (observed[0][inner_pixels] > 200).all()\n\n    def test_image_scale_zoom_out__list_and_deterministic(self):\n        aug = iaa.Affine(scale=0.49, translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.scale_zoom_out_base_img])\n\n        outer_pixels = self.scale_zoom_out_outer_pixels\n        inner_pixels = self.scale_zoom_out_inner_pixels\n        assert (observed[0][outer_pixels] < 25).all()\n        assert (observed[0][inner_pixels] > 200).all()\n\n    def test_keypoints_scale_zoom_out(self):\n        self._test_cba_scale(\n            \"augment_keypoints\", 0.49, self.kpsoi,\n            self.kpsoi_scaled(0.49, 0.49), False)\n\n    def test_keypoints_scale_zoom_out__deterministic(self):\n        self._test_cba_scale(\n            \"augment_keypoints\", 0.49, self.kpsoi,\n            self.kpsoi_scaled(0.49, 0.49), True)\n\n    def test_polygons_scale_zoom_out(self):\n        self._test_cba_scale(\n            \"augment_polygons\", 0.49, self.psoi,\n            self.psoi_scaled(0.49, 0.49), False)\n\n    def test_polygons_scale_zoom_out__deterministic(self):\n        self._test_cba_scale(\n            \"augment_polygons\", 0.49, self.psoi,\n            self.psoi_scaled(0.49, 0.49), True)\n\n    def test_line_strings_scale_zoom_out(self):\n        self._test_cba_scale(\n            \"augment_line_strings\", 0.49, self.lsoi,\n            self.lsoi_scaled(0.49, 0.49), False)\n\n    def test_line_strings_scale_zoom_out__deterministic(self):\n        self._test_cba_scale(\n            \"augment_line_strings\", 0.49, self.lsoi,\n            self.lsoi_scaled(0.49, 0.49), True)\n\n    def test_bounding_boxes_scale_zoom_out(self):\n        self._test_cba_scale(\n            \"augment_bounding_boxes\", 0.49, self.bbsoi,\n            self.bbsoi_scaled(0.49, 0.49), False)\n\n    def test_bounding_boxes_scale_zoom_out__deterministic(self):\n        self._test_cba_scale(\n            \"augment_bounding_boxes\", 0.49, self.bbsoi,\n            self.bbsoi_scaled(0.49, 0.49), True)\n\n    # ---------------------\n    # scale: x and y axis are both tuples\n    # ---------------------\n    def test_image_x_and_y_axis_are_tuples(self):\n        aug = iaa.Affine(scale={\"x\": (0.5, 1.5), \"y\": (0.5, 1.5)},\n                         translate_px=0, rotate=0, shear=0)\n\n        image = np.array([[0, 0, 0, 0, 0],\n                          [0, 1, 1, 1, 0],\n                          [0, 1, 2, 1, 0],\n                          [0, 1, 1, 1, 0],\n                          [0, 0, 0, 0, 0]], dtype=np.uint8) * 100\n        image = image[:, :, np.newaxis]\n        images = np.array([image])\n\n        last_aug = None\n        nb_changed_aug = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                last_aug = observed_aug\n        assert nb_changed_aug >= int(nb_iterations * 0.8)\n\n    def test_image_x_and_y_axis_are_tuples__deterministic(self):\n        aug = iaa.Affine(scale={\"x\": (0.5, 1.5), \"y\": (0.5, 1.5)},\n                         translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        image = np.array([[0, 0, 0, 0, 0],\n                          [0, 1, 1, 1, 0],\n                          [0, 1, 2, 1, 0],\n                          [0, 1, 1, 1, 0],\n                          [0, 0, 0, 0, 0]], dtype=np.uint8) * 100\n        image = image[:, :, np.newaxis]\n        images = np.array([image])\n\n        last_aug_det = None\n        nb_changed_aug_det = 0\n        nb_iterations = 10\n        for i in sm.xrange(nb_iterations):\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug_det = observed_aug_det\n        assert nb_changed_aug_det == 0\n\n    # ------------\n    # alignment\n    # TODO add alignment tests for: BBs, Polys, LS\n    # ------------\n    def test_keypoint_alignment(self):\n        image = np.zeros((100, 100), dtype=np.uint8)\n        image[40-1:40+2, 40-1:40+2] = 255\n        image[40-1:40+2, 60-1:60+2] = 255\n\n        kps = [ia.Keypoint(x=40, y=40), ia.Keypoint(x=60, y=40)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=image.shape)\n\n        images = [image, image, image]\n        kpsois = [kpsoi.deepcopy(),\n                  ia.KeypointsOnImage([], shape=image.shape),\n                  kpsoi.deepcopy()]\n\n        aug = iaa.Affine(scale=[0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5,\n                                1.6, 1.7],\n                         order=0)\n\n        for iter in sm.xrange(40):\n            images_aug, kpsois_aug = aug(images=images, keypoints=kpsois)\n\n            assert kpsois_aug[1].empty\n\n            for i in [0, 2]:\n                image_aug = images_aug[i]\n                kpsoi_aug = kpsois_aug[i]\n\n                for kp in kpsoi_aug.keypoints:\n                    value = image_aug[int(kp.y), int(kp.x)]\n                    assert value > 200\n\n    # ------------\n    # make sure that polygons stay valid upon extreme scaling\n    # ------------\n    def test_polygons_stay_valid_when_using_extreme_scalings(self):\n        scales = [1e-4, 1e-2, 1e2, 1e4]\n        backends = [\"auto\", \"cv2\", \"skimage\"]\n        orders = [0, 1, 3]\n\n        gen = itertools.product(scales, backends, orders)\n        for scale, backend, order in gen:\n            with self.subTest(scale=scale, backend=backend, order=order):\n                aug = iaa.Affine(scale=scale, order=order)\n                psoi = ia.PolygonsOnImage([\n                    ia.Polygon([(0, 0), (10, 0), (5, 5)])],\n                    shape=(10, 10))\n\n                psoi_aug = aug.augment_polygons(psoi)\n\n                poly = psoi_aug.polygons[0]\n                ext = poly.exterior\n                assert poly.is_valid\n                assert ext[0][0] < ext[2][0] < ext[1][0]\n                assert ext[0][1] < ext[2][1]\n                assert np.allclose(ext[0][1], ext[1][1])\n\n\nclass TestAffine_translate(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        return np.uint8([\n            [0, 0, 0],\n            [0, 1, 0],\n            [0, 0, 0]\n        ])[:, :, np.newaxis]\n\n    @property\n    def image_1px_right(self):\n        return np.uint8([\n            [0, 0, 0],\n            [0, 0, 1],\n            [0, 0, 0]\n        ])[:, :, np.newaxis]\n\n    @property\n    def image_1px_bottom(self):\n        return np.uint8([\n            [0, 0, 0],\n            [0, 0, 0],\n            [0, 1, 0]\n        ])[:, :, np.newaxis]\n\n    @property\n    def images(self):\n        return np.array([self.image])\n\n    @property\n    def images_1px_right(self):\n        return np.array([self.image_1px_right])\n\n    @property\n    def images_1px_bottom(self):\n        return np.array([self.image_1px_bottom])\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=1, y=1)]\n        return [ia.KeypointsOnImage(kps, shape=self.image.shape)]\n\n    @property\n    def kpsoi_1px_right(self):\n        kps = [ia.Keypoint(x=2, y=1)]\n        return [ia.KeypointsOnImage(kps, shape=self.image.shape)]\n\n    @property\n    def kpsoi_1px_bottom(self):\n        kps = [ia.Keypoint(x=1, y=2)]\n        return [ia.KeypointsOnImage(kps, shape=self.image.shape)]\n\n    @property\n    def psoi(self):\n        polys = [ia.Polygon([(0, 0), (2, 0), (2, 2)])]\n        return [ia.PolygonsOnImage(polys, shape=self.image.shape)]\n\n    @property\n    def psoi_1px_right(self):\n        polys = [ia.Polygon([(0+1, 0), (2+1, 0), (2+1, 2)])]\n        return [ia.PolygonsOnImage(polys, shape=self.image.shape)]\n\n    @property\n    def psoi_1px_bottom(self):\n        polys = [ia.Polygon([(0, 0+1), (2, 0+1), (2, 2+1)])]\n        return [ia.PolygonsOnImage(polys, shape=self.image.shape)]\n\n    @property\n    def lsoi(self):\n        ls = [ia.LineString([(0, 0), (2, 0), (2, 2)])]\n        return [ia.LineStringsOnImage(ls, shape=self.image.shape)]\n\n    @property\n    def lsoi_1px_right(self):\n        ls = [ia.LineString([(0+1, 0), (2+1, 0), (2+1, 2)])]\n        return [ia.LineStringsOnImage(ls, shape=self.image.shape)]\n\n    @property\n    def lsoi_1px_bottom(self):\n        ls = [ia.LineString([(0, 0+1), (2, 0+1), (2, 2+1)])]\n        return [ia.LineStringsOnImage(ls, shape=self.image.shape)]\n\n    @property\n    def bbsoi(self):\n        bbs = [ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)]\n        return [ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)]\n\n    @property\n    def bbsoi_1px_right(self):\n        bbs = [ia.BoundingBox(x1=0+1, y1=1, x2=2+1, y2=3)]\n        return [ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)]\n\n    @property\n    def bbsoi_1px_bottom(self):\n        bbs = [ia.BoundingBox(x1=0, y1=1+1, x2=2, y2=3+1)]\n        return [ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)]\n\n    # ---------------------\n    # translate: move one pixel to the right\n    # ---------------------\n    def test_image_translate_1px_right(self):\n        # move one pixel to the right\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 1, \"y\": 0}, rotate=0,\n                         shear=0)\n\n        observed = aug.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_right)\n\n    def test_image_translate_1px_right__deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 1, \"y\": 0}, rotate=0,\n                         shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_right)\n\n    def test_image_translate_1px_right__list(self):\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 1, \"y\": 0}, rotate=0,\n                         shear=0)\n\n        observed = aug.augment_images([self.image])\n\n        assert array_equal_lists(observed, [self.image_1px_right])\n\n    def test_image_translate_1px_right__list_and_deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 1, \"y\": 0}, rotate=0,\n                         shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.image])\n\n        assert array_equal_lists(observed, [self.image_1px_right])\n\n    def test_keypoints_translate_1px_right(self):\n        self._test_cba_translate_px(\n            \"augment_keypoints\", {\"x\": 1, \"y\": 0},\n            self.kpsoi, self.kpsoi_1px_right, False)\n\n    def test_keypoints_translate_1px_right__deterministic(self):\n        self._test_cba_translate_px(\n            \"augment_keypoints\", {\"x\": 1, \"y\": 0},\n            self.kpsoi, self.kpsoi_1px_right, True)\n\n    def test_polygons_translate_1px_right(self):\n        self._test_cba_translate_px(\n            \"augment_polygons\", {\"x\": 1, \"y\": 0},\n            self.psoi, self.psoi_1px_right, False)\n\n    def test_polygons_translate_1px_right__deterministic(self):\n        self._test_cba_translate_px(\n            \"augment_polygons\", {\"x\": 1, \"y\": 0},\n            self.psoi, self.psoi_1px_right, True)\n\n    def test_line_strings_translate_1px_right(self):\n        self._test_cba_translate_px(\n            \"augment_line_strings\", {\"x\": 1, \"y\": 0},\n            self.lsoi, self.lsoi_1px_right, False)\n\n    def test_line_strings_translate_1px_right__deterministic(self):\n        self._test_cba_translate_px(\n            \"augment_line_strings\", {\"x\": 1, \"y\": 0},\n            self.lsoi, self.lsoi_1px_right, True)\n\n    def test_bounding_boxes_translate_1px_right(self):\n        self._test_cba_translate_px(\n            \"augment_bounding_boxes\", {\"x\": 1, \"y\": 0},\n            self.bbsoi, self.bbsoi_1px_right, False)\n\n    def test_bounding_boxes_translate_1px_right__deterministic(self):\n        self._test_cba_translate_px(\n            \"augment_bounding_boxes\", {\"x\": 1, \"y\": 0},\n            self.bbsoi, self.bbsoi_1px_right, True)\n\n    @classmethod\n    def _test_cba_translate_px(cls, augf_name, px, cbaoi, cbaoi_translated,\n                               deterministic):\n        aug = iaa.Affine(scale=1.0, translate_px=px, rotate=0, shear=0)\n        if deterministic:\n            aug = aug.to_deterministic()\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(observed, cbaoi_translated)\n\n    def test_image_translate_1px_right_skimage(self):\n        # move one pixel to the right\n        # with backend = skimage\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 1, \"y\": 0}, rotate=0,\n                         shear=0, backend=\"skimage\")\n\n        observed = aug.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_right)\n\n    def test_image_translate_1px_right_skimage_order_all(self):\n        # move one pixel to the right\n        # with backend = skimage, order=ALL\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 1, \"y\": 0}, rotate=0,\n                         shear=0, backend=\"skimage\", order=ia.ALL)\n\n        observed = aug.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_right)\n\n    def test_image_translate_1px_right_skimage_order_is_list(self):\n        # move one pixel to the right\n        # with backend = skimage, order=list\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 1, \"y\": 0}, rotate=0,\n                         shear=0, backend=\"skimage\", order=[0, 1, 3])\n\n        observed = aug.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_right)\n\n    def test_image_translate_1px_right_cv2_order_is_list(self):\n        # move one pixel to the right\n        # with backend = cv2, order=list\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 1, \"y\": 0}, rotate=0,\n                         shear=0, backend=\"cv2\", order=[0, 1, 3])\n\n        observed = aug.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_right)\n\n    def test_image_translate_1px_right_cv2_order_is_stoch_param(self):\n        # move one pixel to the right\n        # with backend = cv2, order=StochasticParameter\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 1, \"y\": 0}, rotate=0,\n                         shear=0, backend=\"cv2\", order=iap.Choice([0, 1, 3]))\n\n        observed = aug.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_right)\n\n    # ---------------------\n    # translate: move one pixel to the bottom\n    # ---------------------\n    def test_image_translate_1px_bottom(self):\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 0, \"y\": 1}, rotate=0,\n                         shear=0)\n\n        observed = aug.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_bottom)\n\n    def test_image_translate_1px_bottom__deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 0, \"y\": 1}, rotate=0,\n                         shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_bottom)\n\n    def test_image_translate_1px_bottom__list(self):\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 0, \"y\": 1}, rotate=0,\n                         shear=0)\n\n        observed = aug.augment_images([self.image])\n\n        assert array_equal_lists(observed, [self.image_1px_bottom])\n\n    def test_image_translate_1px_bottom__list_and_deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": 0, \"y\": 1}, rotate=0,\n                         shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.image])\n\n        assert array_equal_lists(observed, [self.image_1px_bottom])\n\n    def test_keypoints_translate_1px_bottom(self):\n        self._test_cba_translate_px(\n            \"augment_keypoints\", {\"x\": 0, \"y\": 1},\n            self.kpsoi, self.kpsoi_1px_bottom, False)\n\n    def test_keypoints_translate_1px_bottom__deterministic(self):\n        self._test_cba_translate_px(\n            \"augment_keypoints\", {\"x\": 0, \"y\": 1},\n            self.kpsoi, self.kpsoi_1px_bottom, True)\n\n    def test_polygons_translate_1px_bottom(self):\n        self._test_cba_translate_px(\n            \"augment_polygons\", {\"x\": 0, \"y\": 1},\n            self.psoi, self.psoi_1px_bottom, False)\n\n    def test_polygons_translate_1px_bottom__deterministic(self):\n        self._test_cba_translate_px(\n            \"augment_polygons\", {\"x\": 0, \"y\": 1},\n            self.psoi, self.psoi_1px_bottom, True)\n\n    def test_line_strings_translate_1px_bottom(self):\n        self._test_cba_translate_px(\n            \"augment_line_strings\", {\"x\": 0, \"y\": 1},\n            self.lsoi, self.lsoi_1px_bottom, False)\n\n    def test_line_strings_translate_1px_bottom__deterministic(self):\n        self._test_cba_translate_px(\n            \"augment_line_strings\", {\"x\": 0, \"y\": 1},\n            self.lsoi, self.lsoi_1px_bottom, True)\n\n    def test_bounding_boxes_translate_1px_bottom(self):\n        self._test_cba_translate_px(\n            \"augment_bounding_boxes\", {\"x\": 0, \"y\": 1},\n            self.bbsoi, self.bbsoi_1px_bottom, False)\n\n    def test_bounding_boxes_translate_1px_bottom__deterministic(self):\n        self._test_cba_translate_px(\n            \"augment_bounding_boxes\", {\"x\": 0, \"y\": 1},\n            self.bbsoi, self.bbsoi_1px_bottom, True)\n\n    # ---------------------\n    # translate: fraction of the image size (towards the right)\n    # ---------------------\n    def test_image_translate_33percent_right(self):\n        aug = iaa.Affine(scale=1.0, translate_percent={\"x\": 0.3333, \"y\": 0},\n                         rotate=0, shear=0)\n\n        observed = aug.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_right)\n\n    def test_image_translate_33percent_right__deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_percent={\"x\": 0.3333, \"y\": 0},\n                         rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_right)\n\n    def test_image_translate_33percent_right__list(self):\n        aug = iaa.Affine(scale=1.0, translate_percent={\"x\": 0.3333, \"y\": 0},\n                         rotate=0, shear=0)\n\n        observed = aug.augment_images([self.image])\n\n        assert array_equal_lists(observed, [self.image_1px_right])\n\n    def test_image_translate_33percent_right__list_and_deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_percent={\"x\": 0.3333, \"y\": 0},\n                         rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.image])\n\n        assert array_equal_lists(observed, [self.image_1px_right])\n\n    def test_keypoints_translate_33percent_right(self):\n        self._test_cba_translate_percent(\n            \"augment_keypoints\", {\"x\": 0.3333, \"y\": 0},\n            self.kpsoi, self.kpsoi_1px_right, False)\n\n    def test_keypoints_translate_33percent_right__deterministic(self):\n        self._test_cba_translate_percent(\n            \"augment_keypoints\", {\"x\": 0.3333, \"y\": 0},\n            self.kpsoi, self.kpsoi_1px_right, True)\n\n    def test_polygons_translate_33percent_right(self):\n        self._test_cba_translate_percent(\n            \"augment_polygons\", {\"x\": 0.3333, \"y\": 0},\n            self.psoi, self.psoi_1px_right, False)\n\n    def test_polygons_translate_33percent_right__deterministic(self):\n        self._test_cba_translate_percent(\n            \"augment_polygons\", {\"x\": 0.3333, \"y\": 0},\n            self.psoi, self.psoi_1px_right, True)\n\n    def test_line_strings_translate_33percent_right(self):\n        self._test_cba_translate_percent(\n            \"augment_line_strings\", {\"x\": 0.3333, \"y\": 0},\n            self.lsoi, self.lsoi_1px_right, False)\n\n    def test_line_strings_translate_33percent_right__deterministic(self):\n        self._test_cba_translate_percent(\n            \"augment_line_strings\", {\"x\": 0.3333, \"y\": 0},\n            self.lsoi, self.lsoi_1px_right, True)\n\n    def test_bounding_boxes_translate_33percent_right(self):\n        self._test_cba_translate_percent(\n            \"augment_bounding_boxes\", {\"x\": 0.3333, \"y\": 0},\n            self.bbsoi, self.bbsoi_1px_right, False)\n\n    def test_bounding_boxes_translate_33percent_right__deterministic(self):\n        self._test_cba_translate_percent(\n            \"augment_bounding_boxes\", {\"x\": 0.3333, \"y\": 0},\n            self.bbsoi, self.bbsoi_1px_right, True)\n\n    def test_keypoints_with_continuous_param_results_in_absolute_shift(self):\n        # This test ensures that t ~ uniform(a, b) results in a translation\n        # by t pixels and not t%\n        # see issue #505\n        # use iap.Uniform() here to ensure that is really a float value that\n        # is sampled and not accidentally DisceteUniform\n        aug = iaa.Affine(translate_px=iap.Uniform(10, 20))\n        kps = [ia.Keypoint(x=10, y=10)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(1000, 1000))\n\n        for _ in np.arange(5):\n            kpsoi_aug = aug.augment_keypoints(kpsoi)\n\n            kp_aug = kpsoi_aug.keypoints[0]\n            assert 10+10 <= kp_aug.x <= 10+20\n            assert 10+10 <= kp_aug.y <= 10+20\n\n    @classmethod\n    def _test_cba_translate_percent(cls, augf_name, percent, cbaoi,\n                                    cbaoi_translated, deterministic):\n        aug = iaa.Affine(scale=1.0, translate_percent=percent, rotate=0,\n                         shear=0)\n        if deterministic:\n            aug = aug.to_deterministic()\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(observed, cbaoi_translated, max_distance=1e-3)\n\n    # ---------------------\n    # translate: fraction of the image size (towards the bottom)\n    # ---------------------\n    def test_image_translate_33percent_bottom(self):\n        # move 33% (one pixel) to the bottom\n        aug = iaa.Affine(scale=1.0, translate_percent={\"x\": 0, \"y\": 0.3333},\n                         rotate=0, shear=0)\n\n        observed = aug.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_bottom)\n\n    def test_image_translate_33percent_bottom__deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_percent={\"x\": 0, \"y\": 0.3333},\n                         rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_1px_bottom)\n\n    def test_image_translate_33percent_bottom__list(self):\n        aug = iaa.Affine(scale=1.0, translate_percent={\"x\": 0, \"y\": 0.3333},\n                         rotate=0, shear=0)\n\n        observed = aug.augment_images([self.image])\n\n        assert array_equal_lists(observed, [self.image_1px_bottom])\n\n    def test_image_translate_33percent_bottom__list_and_deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_percent={\"x\": 0, \"y\": 0.3333},\n                         rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.image])\n\n        assert array_equal_lists(observed, [self.image_1px_bottom])\n\n    def test_keypoints_translate_33percent_bottom(self):\n        self._test_cba_translate_percent(\n            \"augment_keypoints\", {\"x\": 0, \"y\": 0.3333},\n            self.kpsoi, self.kpsoi_1px_bottom, False)\n\n    def test_keypoints_translate_33percent_bottom__deterministic(self):\n        self._test_cba_translate_percent(\n            \"augment_keypoints\", {\"x\": 0, \"y\": 0.3333},\n            self.kpsoi, self.kpsoi_1px_bottom, True)\n\n    def test_polygons_translate_33percent_bottom(self):\n        self._test_cba_translate_percent(\n            \"augment_polygons\", {\"x\": 0, \"y\": 0.3333},\n            self.psoi, self.psoi_1px_bottom, False)\n\n    def test_polygons_translate_33percent_bottom__deterministic(self):\n        self._test_cba_translate_percent(\n            \"augment_polygons\", {\"x\": 0, \"y\": 0.3333},\n            self.psoi, self.psoi_1px_bottom, True)\n\n    def test_line_strings_translate_33percent_bottom(self):\n        self._test_cba_translate_percent(\n            \"augment_line_strings\", {\"x\": 0, \"y\": 0.3333},\n            self.lsoi, self.lsoi_1px_bottom, False)\n\n    def test_line_strings_translate_33percent_bottom__deterministic(self):\n        self._test_cba_translate_percent(\n            \"augment_line_strings\", {\"x\": 0, \"y\": 0.3333},\n            self.lsoi, self.lsoi_1px_bottom, True)\n\n    def test_bounding_boxes_translate_33percent_bottom(self):\n        self._test_cba_translate_percent(\n            \"augment_bounding_boxes\", {\"x\": 0, \"y\": 0.3333},\n            self.bbsoi, self.bbsoi_1px_bottom, False)\n\n    def test_bounding_boxes_translate_33percent_bottom__deterministic(self):\n        self._test_cba_translate_percent(\n            \"augment_bounding_boxes\", {\"x\": 0, \"y\": 0.3333},\n            self.bbsoi, self.bbsoi_1px_bottom, True)\n\n    # ---------------------\n    # translate: axiswise uniform distributions\n    # ---------------------\n    def test_image_translate_by_axiswise_uniform_distributions(self):\n        # 0-1px to left/right and 0-1px to top/bottom\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": (-1, 1), \"y\": (-1, 1)},\n                         rotate=0, shear=0)\n        last_aug = None\n        nb_changed_aug = 0\n        nb_iterations = 1000\n        centers_aug = self.image.astype(np.int32) * 0\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(self.images)\n            if i == 0:\n                last_aug = observed_aug\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                last_aug = observed_aug\n\n            assert len(observed_aug[0].nonzero()[0]) == 1\n            centers_aug += (observed_aug[0] > 0)\n\n        assert nb_changed_aug >= int(nb_iterations * 0.7)\n        assert (centers_aug > int(nb_iterations * (1/9 * 0.6))).all()\n        assert (centers_aug < int(nb_iterations * (1/9 * 1.4))).all()\n\n    def test_image_translate_by_axiswise_uniform_distributions__det(self):\n        # 0-1px to left/right and 0-1px to top/bottom\n        aug = iaa.Affine(scale=1.0, translate_px={\"x\": (-1, 1), \"y\": (-1, 1)},\n                         rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n        last_aug_det = None\n        nb_changed_aug_det = 0\n        nb_iterations = 10\n        centers_aug_det = self.image.astype(np.int32) * 0\n        for i in sm.xrange(nb_iterations):\n            observed_aug_det = aug_det.augment_images(self.images)\n            if i == 0:\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug_det = observed_aug_det\n\n            assert len(observed_aug_det[0].nonzero()[0]) == 1\n            centers_aug_det += (observed_aug_det[0] > 0)\n\n        assert nb_changed_aug_det == 0\n\n    # ---------------------\n    # translate heatmaps\n    # ---------------------\n    @property\n    def heatmaps(self):\n        return ia.HeatmapsOnImage(\n            np.float32([\n                [0.0, 0.5, 0.75],\n                [0.0, 0.5, 0.75],\n                [0.75, 0.75, 0.75],\n            ]),\n            shape=(3, 3, 3)\n        )\n\n    @property\n    def heatmaps_1px_right(self):\n        return ia.HeatmapsOnImage(\n            np.float32([\n                [0.0, 0.0, 0.5],\n                [0.0, 0.0, 0.5],\n                [0.0, 0.75, 0.75],\n            ]),\n            shape=(3, 3, 3)\n        )\n\n    def test_heatmaps_translate_1px_right(self):\n        aug = iaa.Affine(translate_px={\"x\": 1})\n\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n\n        _assert_same_shape(observed, self.heatmaps)\n        _assert_same_min_max(observed, self.heatmaps)\n        assert np.array_equal(observed.get_arr(),\n                              self.heatmaps_1px_right.get_arr())\n\n    def test_heatmaps_translate_1px_right_should_ignore_cval(self):\n        # should still use mode=constant cval=0 even when other settings chosen\n        aug = iaa.Affine(translate_px={\"x\": 1}, cval=255)\n\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n\n        _assert_same_shape(observed, self.heatmaps)\n        _assert_same_min_max(observed, self.heatmaps)\n        assert np.array_equal(observed.get_arr(),\n                              self.heatmaps_1px_right.get_arr())\n\n    def test_heatmaps_translate_1px_right_should_ignore_mode(self):\n        aug = iaa.Affine(translate_px={\"x\": 1}, mode=\"edge\", cval=255)\n\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n\n        _assert_same_shape(observed, self.heatmaps)\n        _assert_same_min_max(observed, self.heatmaps)\n        assert np.array_equal(observed.get_arr(),\n                              self.heatmaps_1px_right.get_arr())\n\n    # ---------------------\n    # translate segmaps\n    # ---------------------\n    @property\n    def segmaps(self):\n        return SegmentationMapsOnImage(\n            np.int32([\n                [0, 1, 2],\n                [0, 1, 2],\n                [2, 2, 2],\n            ]),\n            shape=(3, 3, 3)\n        )\n\n    @property\n    def segmaps_1px_right(self):\n        return SegmentationMapsOnImage(\n            np.int32([\n                [0, 0, 1],\n                [0, 0, 1],\n                [0, 2, 2],\n            ]),\n            shape=(3, 3, 3)\n        )\n\n    def test_segmaps_translate_1px_right(self):\n        aug = iaa.Affine(translate_px={\"x\": 1})\n\n        observed = aug.augment_segmentation_maps([self.segmaps])[0]\n\n        _assert_same_shape(observed, self.segmaps)\n        assert np.array_equal(observed.get_arr(),\n                              self.segmaps_1px_right.get_arr())\n\n    def test_segmaps_translate_1px_right_should_ignore_cval(self):\n        # should still use mode=constant cval=0 even when other settings chosen\n        aug = iaa.Affine(translate_px={\"x\": 1}, cval=255)\n\n        observed = aug.augment_segmentation_maps([self.segmaps])[0]\n\n        _assert_same_shape(observed, self.segmaps)\n        assert np.array_equal(observed.get_arr(),\n                              self.segmaps_1px_right.get_arr())\n\n    def test_segmaps_translate_1px_right_should_ignore_mode(self):\n        aug = iaa.Affine(translate_px={\"x\": 1}, mode=\"edge\", cval=255)\n\n        observed = aug.augment_segmentation_maps([self.segmaps])[0]\n\n        _assert_same_shape(observed, self.segmaps)\n        assert np.array_equal(observed.get_arr(),\n                              self.segmaps_1px_right.get_arr())\n\n\nclass TestAffine_rotate(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        return np.uint8([\n            [0, 0, 0],\n            [255, 255, 255],\n            [0, 0, 0]\n        ])[:, :, np.newaxis]\n\n    @property\n    def image_rot90(self):\n        return np.uint8([\n            [0, 255, 0],\n            [0, 255, 0],\n            [0, 255, 0]\n        ])[:, :, np.newaxis]\n\n    @property\n    def images(self):\n        return np.array([self.image])\n\n    @property\n    def images_rot90(self):\n        return np.array([self.image_rot90])\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=1), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=1)]\n        return [ia.KeypointsOnImage(kps, shape=self.image.shape)]\n\n    @property\n    def kpsoi_rot90(self):\n        kps = [ia.Keypoint(x=3-1, y=0), ia.Keypoint(x=3-1, y=1),\n               ia.Keypoint(x=3-1, y=2)]\n        return [ia.KeypointsOnImage(kps, shape=self.image_rot90.shape)]\n\n    @property\n    def psoi(self):\n        polys = [ia.Polygon([(0, 0), (3, 0), (3, 3)])]\n        return [ia.PolygonsOnImage(polys, shape=self.image.shape)]\n\n    @property\n    def psoi_rot90(self):\n        polys = [ia.Polygon([(3-0, 0), (3-0, 3), (3-3, 3)])]\n        return [ia.PolygonsOnImage(polys, shape=self.image_rot90.shape)]\n\n    @property\n    def lsoi(self):\n        ls = [ia.LineString([(0, 0), (3, 0), (3, 3)])]\n        return [ia.LineStringsOnImage(ls, shape=self.image.shape)]\n\n    @property\n    def lsoi_rot90(self):\n        ls = [ia.LineString([(3-0, 0), (3-0, 3), (3-3, 3)])]\n        return [ia.LineStringsOnImage(ls, shape=self.image_rot90.shape)]\n\n    @property\n    def bbsoi(self):\n        bbs = [ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)]\n        return [ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)]\n\n    @property\n    def bbsoi_rot90(self):\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=2, y2=2)]\n        return [ia.BoundingBoxesOnImage(bbs, shape=self.image_rot90.shape)]\n\n    def test_image_rot90(self):\n        # rotate by 90 degrees\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=90, shear=0)\n\n        observed = aug.augment_images(self.images)\n\n        observed[observed >= 100] = 255\n        observed[observed < 100] = 0\n        assert np.array_equal(observed, self.images_rot90)\n\n    def test_image_rot90__deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=90, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.images)\n\n        observed[observed >= 100] = 255\n        observed[observed < 100] = 0\n        assert np.array_equal(observed, self.images_rot90)\n\n    def test_image_rot90__list(self):\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=90, shear=0)\n\n        observed = aug.augment_images([self.image])\n\n        observed[0][observed[0] >= 100] = 255\n        observed[0][observed[0] < 100] = 0\n        assert array_equal_lists(observed, [self.image_rot90])\n\n    def test_image_rot90__list_and_deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=90, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.image])\n\n        observed[0][observed[0] >= 100] = 255\n        observed[0][observed[0] < 100] = 0\n        assert array_equal_lists(observed, [self.image_rot90])\n\n    def test_keypoints_rot90(self):\n        self._test_cba_rotate(\n            \"augment_keypoints\", 90, self.kpsoi, self.kpsoi_rot90, False)\n\n    def test_keypoints_rot90__deterministic(self):\n        self._test_cba_rotate(\n            \"augment_keypoints\", 90, self.kpsoi, self.kpsoi_rot90, True)\n\n    def test_polygons_rot90(self):\n        self._test_cba_rotate(\n            \"augment_polygons\", 90, self.psoi, self.psoi_rot90, False)\n\n    def test_polygons_rot90__deterministic(self):\n        self._test_cba_rotate(\n            \"augment_polygons\", 90, self.psoi, self.psoi_rot90, True)\n\n    def test_line_strings_rot90(self):\n        self._test_cba_rotate(\n            \"augment_line_strings\", 90, self.lsoi, self.lsoi_rot90, False)\n\n    def test_line_strings_rot90__deterministic(self):\n        self._test_cba_rotate(\n            \"augment_line_strings\", 90, self.lsoi, self.lsoi_rot90, True)\n\n    def test_bounding_boxes_rot90(self):\n        self._test_cba_rotate(\n            \"augment_bounding_boxes\", 90, self.bbsoi, self.bbsoi_rot90, False)\n\n    def test_bounding_boxes_rot90__deterministic(self):\n        self._test_cba_rotate(\n            \"augment_bounding_boxes\", 90, self.bbsoi, self.bbsoi_rot90, True)\n\n    @classmethod\n    def _test_cba_rotate(cls, augf_name, rotate, cbaoi,\n                         cbaoi_rotated, deterministic):\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=rotate,\n                         shear=0)\n        if deterministic:\n            aug = aug.to_deterministic()\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(observed, cbaoi_rotated)\n\n    def test_image_rotate_is_tuple_0_to_364_deg(self):\n        # random rotation 0-364 degrees\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=(0, 364), shear=0)\n        last_aug = None\n        nb_changed_aug = 0\n        nb_iterations = 1000\n        pixels_sums_aug = self.image.astype(np.int32) * 0\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(self.images)\n            if i == 0:\n                last_aug = observed_aug\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                last_aug = observed_aug\n\n            pixels_sums_aug += (observed_aug[0] > 100)\n\n        assert nb_changed_aug >= int(nb_iterations * 0.9)\n        # center pixel, should always be white when rotating line around center\n        assert pixels_sums_aug[1, 1] > (nb_iterations * 0.98)\n        assert pixels_sums_aug[1, 1] < (nb_iterations * 1.02)\n\n        # outer pixels, should sometimes be white\n        # the values here had to be set quite tolerant, the middle pixels at\n        # top/left/bottom/right get more activation than expected\n        outer_pixels = ([0, 0, 0, 1, 1, 2, 2, 2],\n                        [0, 1, 2, 0, 2, 0, 1, 2])\n        assert (\n            pixels_sums_aug[outer_pixels] > int(nb_iterations * (2/8 * 0.4))\n        ).all()\n        assert (\n            pixels_sums_aug[outer_pixels] < int(nb_iterations * (2/8 * 2.0))\n        ).all()\n\n    def test_image_rotate_is_tuple_0_to_364_deg__deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_px=0, rotate=(0, 364), shear=0)\n        aug_det = aug.to_deterministic()\n        last_aug_det = None\n        nb_changed_aug_det = 0\n        nb_iterations = 10\n        pixels_sums_aug_det = self.image.astype(np.int32) * 0\n        for i in sm.xrange(nb_iterations):\n            observed_aug_det = aug_det.augment_images(self.images)\n            if i == 0:\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug_det = observed_aug_det\n\n            pixels_sums_aug_det += (observed_aug_det[0] > 100)\n\n        assert nb_changed_aug_det == 0\n        # center pixel, should always be white when rotating line around center\n        assert pixels_sums_aug_det[1, 1] > (nb_iterations * 0.98)\n        assert pixels_sums_aug_det[1, 1] < (nb_iterations * 1.02)\n\n    def test_alignment_between_images_and_heatmaps_for_fixed_rot(self):\n        # measure alignment between images and heatmaps when rotating\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            aug = iaa.Affine(rotate=45, backend=backend)\n            image = np.zeros((7, 6), dtype=np.uint8)\n            image[:, 2:3+1] = 255\n            hm = ia.HeatmapsOnImage(image.astype(np.float32)/255, shape=(7, 6))\n\n            img_aug = aug.augment_image(image)\n            hm_aug = aug.augment_heatmaps([hm])[0]\n\n            img_aug_mask = img_aug > 255*0.1\n            hm_aug_mask = hm_aug.arr_0to1 > 0.1\n            same = np.sum(img_aug_mask == hm_aug_mask[:, :, 0])\n            assert hm_aug.shape == (7, 6)\n            assert hm_aug.arr_0to1.shape == (7, 6, 1)\n            assert (same / img_aug_mask.size) >= 0.95\n\n    def test_alignment_between_images_and_smaller_heatmaps_for_fixed_rot(self):\n        # measure alignment between images and heatmaps when rotating\n        # here with smaller heatmaps\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(rotate=45, backend=backend)\n\n                image = np.zeros((56, 48), dtype=np.uint8)\n                image[:, 16:24+1] = 255\n                hm = ia.HeatmapsOnImage(\n                    ia.imresize_single_image(\n                        image, (28, 24), interpolation=\"cubic\"\n                    ).astype(np.float32)/255,\n                    shape=(56, 48)\n                )\n\n                img_aug = aug.augment_image(image)\n                hm_aug = aug.augment_heatmaps([hm])[0]\n\n                img_aug_mask = img_aug > 255*0.1\n                hm_aug_mask = ia.imresize_single_image(\n                    hm_aug.arr_0to1, img_aug.shape[0:2], interpolation=\"cubic\"\n                ) > 0.1\n                same = np.sum(img_aug_mask == hm_aug_mask[:, :, 0])\n                assert hm_aug.shape == (56, 48)\n                assert hm_aug.arr_0to1.shape == (28, 24, 1)\n                assert (same / img_aug_mask.size) >= 0.9\n\n    def test_bounding_boxes_have_expected_shape_after_augmentation(self):\n        image = np.zeros((100, 100), dtype=np.uint8)\n        image[20:80, 20:80] = 255\n        bb = ia.BoundingBox(x1=20, y1=20, x2=80, y2=80)\n        bbsoi = ia.BoundingBoxesOnImage([bb], shape=image.shape)\n        for rotate in [10, 20, 40, 80, 120]:\n            with self.subTest(rotate=rotate):\n                aug = iaa.Affine(rotate=rotate, order=0)\n\n                image_aug, bbsoi_aug = aug(image=image, bounding_boxes=bbsoi)\n\n                xx = np.nonzero(np.max(image_aug > 100, axis=0))[0]\n                yy = np.nonzero(np.max(image_aug > 100, axis=1))[0]\n                bb_exp_x1 = xx[0]\n                bb_exp_x2 = xx[-1]\n                bb_exp_y1 = yy[0]\n                bb_exp_y2 = yy[-1]\n                bb_expected = ia.BoundingBox(x1=bb_exp_x1, y1=bb_exp_y1,\n                                             x2=bb_exp_x2, y2=bb_exp_y2)\n                assert bbsoi_aug.bounding_boxes[0].iou(bb_expected) > 0.95\n\n\nclass TestAffine_cval(unittest.TestCase):\n    @property\n    def image(self):\n        return np.ones((3, 3, 1), dtype=np.uint8) * 255\n\n    @property\n    def images(self):\n        return np.array([self.image])\n\n    def test_image_fixed_cval(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=128)\n\n        observed = aug.augment_images(self.images)\n\n        assert (observed[0] > 128 - 30).all()\n        assert (observed[0] < 128 + 30).all()\n\n    def test_image_fixed_cval__deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=128)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.images)\n\n        assert (observed[0] > 128 - 30).all()\n        assert (observed[0] < 128 + 30).all()\n\n    def test_image_fixed_cval__list(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=128)\n\n        observed = aug.augment_images([self.image])\n\n        assert (observed[0] > 128 - 30).all()\n        assert (observed[0] < 128 + 30).all()\n\n    def test_image_fixed_cval__list_and_deterministic(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=128)\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.image])\n\n        assert (observed[0] > 128 - 30).all()\n        assert (observed[0] < 128 + 30).all()\n\n    def test_image_cval_is_tuple(self):\n        # random cvals\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=(0, 255))\n        last_aug = None\n        nb_changed_aug = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(self.images)\n\n            if i == 0:\n                last_aug = observed_aug\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                last_aug = observed_aug\n\n        assert nb_changed_aug >= int(nb_iterations * 0.9)\n\n    def test_image_cval_is_tuple__deterministic(self):\n        # random cvals\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=(0, 255))\n        aug_det = aug.to_deterministic()\n        last_aug_det = None\n        nb_changed_aug_det = 0\n        nb_iterations = 10\n        for i in sm.xrange(nb_iterations):\n            observed_aug_det = aug_det.augment_images(self.images)\n\n            if i == 0:\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug_det = observed_aug_det\n\n        assert nb_changed_aug_det == 0\n\n    def test_float_cval_on_float_image(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=0.25)\n        image = np.full((10, 10, 3), 0.75, dtype=np.float32)\n        image_aug = aug(image=image)\n        assert np.allclose(image_aug, 0.25)\n\n    def test_float_cval_on_int_image(self):\n        aug = iaa.Affine(scale=1.0, translate_px=100, rotate=0, shear=0,\n                         cval=2.75)\n        image = np.full((10, 10, 3), 10, dtype=np.uint8)\n        image_aug = aug(image=image)\n        assert np.allclose(image_aug, 2)  # cval is casted to int, no rounding\n\n\nclass TestAffine_fit_output(unittest.TestCase):\n    @property\n    def image(self):\n        return np.ones((3, 3, 1), dtype=np.uint8) * 255\n\n    @property\n    def images(self):\n        return np.array([self.image])\n\n    @property\n    def heatmaps(self):\n        return ia.HeatmapsOnImage(\n            np.float32([\n                [0.0, 0.5, 0.75],\n                [0.0, 0.5, 0.75],\n                [0.75, 0.75, 0.75],\n            ]),\n            shape=(3, 3, 3)\n        )\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=1), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=1)]\n        return [ia.KeypointsOnImage(kps, shape=self.image.shape)]\n\n    def test_image_translate(self):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(translate_px=100, fit_output=True,\n                                 backend=backend)\n\n                observed = aug.augment_images(self.images)\n\n                expected = self.images\n                assert np.array_equal(observed, expected)\n\n    def test_keypoints_translate(self):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(translate_px=100, fit_output=True,\n                                 backend=backend)\n\n                observed = aug.augment_keypoints(self.kpsoi)\n\n                expected = self.kpsoi\n                assert keypoints_equal(observed, expected)\n\n    def test_heatmaps_translate(self):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(translate_px=100, fit_output=True,\n                                 backend=backend)\n\n                observed = aug.augment_heatmaps([self.heatmaps])[0]\n\n                expected = self.heatmaps\n                assert np.allclose(observed.arr_0to1, expected.arr_0to1)\n\n    def test_image_rot45(self):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(rotate=45, fit_output=True,\n                                 backend=backend)\n                img = np.zeros((10, 10), dtype=np.uint8)\n                img[0:2, 0:2] = 255\n                img[-2:, 0:2] = 255\n                img[0:2, -2:] = 255\n                img[-2:, -2:] = 255\n\n                img_aug = aug.augment_image(img)\n\n                _labels, nb_labels = skimage.morphology.label(\n                    img_aug > 240, return_num=True, connectivity=2)\n                assert nb_labels == 4\n\n    def test_heatmaps_rot45(self):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(rotate=45, fit_output=True,\n                                 backend=backend)\n                img = np.zeros((10, 10), dtype=np.uint8)\n                img[0:2, 0:2] = 255\n                img[-2:, 0:2] = 255\n                img[0:2, -2:] = 255\n                img[-2:, -2:] = 255\n                hm = ia.HeatmapsOnImage(img.astype(np.float32)/255,\n                                        shape=(10, 10))\n\n                hm_aug = aug.augment_heatmaps([hm])[0]\n\n                _labels, nb_labels = skimage.morphology.label(\n                    hm_aug.arr_0to1 > 240/255, return_num=True, connectivity=2)\n                assert nb_labels == 4\n\n    def test_heatmaps_rot45__heatmaps_smaller_than_image(self):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(rotate=45, fit_output=True,\n                                 backend=backend)\n                img = np.zeros((80, 80), dtype=np.uint8)\n                img[0:5, 0:5] = 255\n                img[-5:, 0:5] = 255\n                img[0:5, -5:] = 255\n                img[-5:, -5:] = 255\n                hm = HeatmapsOnImage(\n                    ia.imresize_single_image(\n                        img, (40, 40), interpolation=\"cubic\"\n                    ).astype(np.float32)/255,\n                    shape=(80, 80)\n                )\n\n                hm_aug = aug.augment_heatmaps([hm])[0]\n\n                # these asserts are deactivated because the image size can\n                # change under fit_output=True\n                # assert hm_aug.shape == (80, 80)\n                # assert hm_aug.arr_0to1.shape == (40, 40, 1)\n                _labels, nb_labels = skimage.morphology.label(\n                    hm_aug.arr_0to1 > 200/255, return_num=True, connectivity=2)\n                assert nb_labels == 4\n\n    def test_image_heatmap_alignment_random_rots(self):\n        nb_iterations = 50\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                for _ in sm.xrange(nb_iterations):\n                    aug = iaa.Affine(rotate=(0, 364), fit_output=True,\n                                     backend=backend)\n                    img = np.zeros((80, 80), dtype=np.uint8)\n                    img[0:5, 0:5] = 255\n                    img[-5:, 0:5] = 255\n                    img[0:5, -5:] = 255\n                    img[-5:, -5:] = 255\n                    hm = HeatmapsOnImage(\n                        img.astype(np.float32)/255,\n                        shape=(80, 80)\n                    )\n\n                    img_aug = aug.augment_image(img)\n                    hm_aug = aug.augment_heatmaps([hm])[0]\n\n                    img_aug_mask = img_aug > 255*0.1\n                    hm_aug_mask = ia.imresize_single_image(\n                        hm_aug.arr_0to1, img_aug.shape[0:2],\n                        interpolation=\"cubic\"\n                    ) > 0.1\n                    same = np.sum(img_aug_mask == hm_aug_mask[:, :, 0])\n                    assert (same / img_aug_mask.size) >= 0.95\n\n    def test_image_heatmap_alignment_random_rots__hms_smaller_than_img(self):\n        nb_iterations = 50\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                for _ in sm.xrange(nb_iterations):\n                    aug = iaa.Affine(rotate=(0, 364), fit_output=True,\n                                     backend=backend)\n                    img = np.zeros((80, 80), dtype=np.uint8)\n                    img[0:5, 0:5] = 255\n                    img[-5:, 0:5] = 255\n                    img[0:5, -5:] = 255\n                    img[-5:, -5:] = 255\n                    hm = HeatmapsOnImage(\n                        ia.imresize_single_image(\n                            img, (40, 40), interpolation=\"cubic\"\n                        ).astype(np.float32)/255,\n                        shape=(80, 80)\n                    )\n\n                    img_aug = aug.augment_image(img)\n                    hm_aug = aug.augment_heatmaps([hm])[0]\n\n                    img_aug_mask = img_aug > 255*0.1\n                    hm_aug_mask = ia.imresize_single_image(\n                        hm_aug.arr_0to1, img_aug.shape[0:2],\n                        interpolation=\"cubic\"\n                    ) > 0.1\n                    same = np.sum(img_aug_mask == hm_aug_mask[:, :, 0])\n                    assert (same / img_aug_mask.size) >= 0.95\n\n    def test_segmaps_rot45(self):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(rotate=45, fit_output=True,\n                                 backend=backend)\n                img = np.zeros((80, 80), dtype=np.uint8)\n                img[0:5, 0:5] = 255\n                img[-5:, 0:5] = 255\n                img[0:5, -5:] = 255\n                img[-5:, -5:] = 255\n                segmap = SegmentationMapsOnImage(\n                    (img > 100).astype(np.int32),\n                    shape=(80, 80)\n                )\n\n                segmap_aug = aug.augment_segmentation_maps([segmap])[0]\n\n                # these asserts are deactivated because the image size can\n                # change under fit_output=True\n                # assert segmap_aug.shape == (80, 80)\n                # assert segmap_aug.arr_0to1.shape == (40, 40, 1)\n                _labels, nb_labels = skimage.morphology.label(\n                    segmap_aug.arr > 0, return_num=True, connectivity=2)\n                assert nb_labels == 4\n\n    def test_segmaps_rot45__segmaps_smaller_than_img(self):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(rotate=45, fit_output=True,\n                                 backend=backend)\n                img = np.zeros((80, 80), dtype=np.uint8)\n                img[0:5, 0:5] = 255\n                img[-5:, 0:5] = 255\n                img[0:5, -5:] = 255\n                img[-5:, -5:] = 255\n                segmap = SegmentationMapsOnImage(\n                    (\n                        ia.imresize_single_image(\n                            img, (40, 40), interpolation=\"cubic\"\n                        ) > 100\n                     ).astype(np.int32),\n                    shape=(80, 80)\n                )\n\n                segmap_aug = aug.augment_segmentation_maps([segmap])[0]\n\n                # these asserts are deactivated because the image size can\n                # change under fit_output=True\n                # assert segmap_aug.shape == (80, 80)\n                # assert segmap_aug.arr_0to1.shape == (40, 40, 1)\n                _labels, nb_labels = skimage.morphology.label(\n                    segmap_aug.arr > 0, return_num=True, connectivity=2)\n                assert nb_labels == 4\n\n    def test_image_segmap_alignment_random_rots(self):\n        nb_iterations = 50\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                for _ in sm.xrange(nb_iterations):\n                    aug = iaa.Affine(rotate=(0, 364), fit_output=True,\n                                     backend=backend)\n                    img = np.zeros((80, 80), dtype=np.uint8)\n                    img[0:5, 0:5] = 255\n                    img[-5:, 0:5] = 255\n                    img[0:5, -5:] = 255\n                    img[-5:, -5:] = 255\n                    segmap = SegmentationMapsOnImage(\n                        (img > 100).astype(np.int32),\n                        shape=(80, 80)\n                    )\n\n                    img_aug = aug.augment_image(img)\n                    segmap_aug = aug.augment_segmentation_maps([segmap])[0]\n\n                    img_aug_mask = img_aug > 100\n                    segmap_aug_mask = ia.imresize_single_image(\n                        segmap_aug.arr,\n                        img_aug.shape[0:2],\n                        interpolation=\"nearest\"\n                    ) > 0\n                    same = np.sum(img_aug_mask == segmap_aug_mask[:, :, 0])\n                    assert (same / img_aug_mask.size) >= 0.95\n\n    def test_image_segmap_alignment_random_rots__sms_smaller_than_img(self):\n        nb_iterations = 50\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                for _ in sm.xrange(nb_iterations):\n                    aug = iaa.Affine(rotate=(0, 364), fit_output=True,\n                                     backend=backend)\n                    img = np.zeros((80, 80), dtype=np.uint8)\n                    img[0:5, 0:5] = 255\n                    img[-5:, 0:5] = 255\n                    img[0:5, -5:] = 255\n                    img[-5:, -5:] = 255\n                    segmap = SegmentationMapsOnImage(\n                        (\n                            ia.imresize_single_image(\n                                img, (40, 40), interpolation=\"cubic\"\n                            ) > 100\n                         ).astype(np.int32),\n                        shape=(80, 80)\n                    )\n\n                    img_aug = aug.augment_image(img)\n                    segmap_aug = aug.augment_segmentation_maps([segmap])[0]\n\n                    img_aug_mask = img_aug > 100\n                    segmap_aug_mask = ia.imresize_single_image(\n                        segmap_aug.arr,\n                        img_aug.shape[0:2],\n                        interpolation=\"nearest\"\n                    ) > 0\n                    same = np.sum(img_aug_mask == segmap_aug_mask[:, :, 0])\n                    assert (same / img_aug_mask.size) >= 0.95\n\n    def test_keypoints_rot90_without_fit_output(self):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(rotate=90, backend=backend)\n                kps = ia.KeypointsOnImage([ia.Keypoint(10, 10)],\n                                          shape=(100, 200, 3))\n                kps_aug = aug.augment_keypoints(kps)\n                assert kps_aug.shape == (100, 200, 3)\n                assert not np.allclose(\n                    [kps_aug.keypoints[0].x, kps_aug.keypoints[0].y],\n                    [kps.keypoints[0].x, kps.keypoints[0].y],\n                    atol=1e-2, rtol=0)\n\n    def test_keypoints_rot90(self):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(rotate=90, fit_output=True, backend=backend)\n                kps = ia.KeypointsOnImage([ia.Keypoint(10, 10)],\n                                          shape=(100, 200, 3))\n\n                kps_aug = aug.augment_keypoints(kps)\n\n                assert kps_aug.shape == (200, 100, 3)\n                assert not np.allclose(\n                    [kps_aug.keypoints[0].x, kps_aug.keypoints[0].y],\n                    [kps.keypoints[0].x, kps.keypoints[0].y],\n                    atol=1e-2, rtol=0)\n\n    def test_empty_keypoints_rot90(self):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(rotate=90, fit_output=True, backend=backend)\n                kps = ia.KeypointsOnImage([], shape=(100, 200, 3))\n\n                kps_aug = aug.augment_keypoints(kps)\n\n                assert kps_aug.shape == (200, 100, 3)\n                assert len(kps_aug.keypoints) == 0\n\n    def _test_cbaoi_rot90_without_fit_output(self, cbaoi, augf_name):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                # verify that shape in PolygonsOnImages changes\n                aug = iaa.Affine(rotate=90, backend=backend)\n\n                cbaoi_aug = getattr(aug, augf_name)([cbaoi, cbaoi])\n\n                assert len(cbaoi_aug) == 2\n                for cbaoi_aug_i in cbaoi_aug:\n                    if isinstance(cbaoi, (ia.PolygonsOnImage,\n                                          ia.LineStringsOnImage)):\n                        assert cbaoi_aug_i.shape == cbaoi.shape\n                        assert not cbaoi_aug_i.items[0].coords_almost_equals(\n                            cbaoi.items[0].coords, max_distance=1e-2)\n                    else:\n                        assert_cbaois_equal(cbaoi_aug_i, cbaoi)\n\n    def test_polygons_rot90_without_fit_output(self):\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(10, 10), (20, 10), (20, 20)])\n        ], shape=(100, 200, 3))\n\n        self._test_cbaoi_rot90_without_fit_output(psoi, \"augment_polygons\")\n\n    def test_line_strings_rot90_without_fit_output(self):\n        lsoi = ia.LineStringsOnImage([\n            ia.LineString([(10, 10), (20, 10), (20, 20), (10, 10)])\n        ], shape=(100, 200, 3))\n\n        self._test_cbaoi_rot90_without_fit_output(lsoi, \"augment_line_strings\")\n\n    def _test_cbaoi_rot90(self, cbaoi, expected, augf_name):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(rotate=90, fit_output=True, backend=backend)\n\n                cbaoi_aug = getattr(aug, augf_name)([cbaoi, cbaoi])\n\n                assert len(cbaoi_aug) == 2\n                for cbaoi_aug_i in cbaoi_aug:\n                    assert_cbaois_equal(cbaoi_aug_i, expected)\n\n    def test_polygons_rot90(self):\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(10, 10), (20, 10), (20, 20)])\n        ], shape=(100, 200, 3))\n        expected = ia.PolygonsOnImage([\n            ia.Polygon([(100-10-1, 10), (100-10-1, 20), (100-20-1, 20)])\n        ], shape=(200, 100, 3))\n        self._test_cbaoi_rot90(psoi, expected, \"augment_polygons\")\n\n    def test_line_strings_rot90(self):\n        lsoi = ia.LineStringsOnImage([\n            ia.LineString([(10, 10), (20, 10), (20, 20), (10, 10)])\n        ], shape=(100, 200, 3))\n        expected = ia.LineStringsOnImage([\n            ia.LineString([(100-10-1, 10), (100-10-1, 20), (100-20-1, 20),\n                           (100-10-1, 10)])\n        ], shape=(200, 100, 3))\n        self._test_cbaoi_rot90(lsoi, expected, \"augment_line_strings\")\n\n    def test_bounding_boxes_rot90(self):\n        lsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=10, y1=10, x2=20, y2=20)\n        ], shape=(100, 200, 3))\n        expected = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=100-20-1, y1=10, x2=100-10-1, y2=20)\n        ], shape=(200, 100, 3))\n        self._test_cbaoi_rot90(lsoi, expected, \"augment_bounding_boxes\")\n\n    def _test_empty_cbaoi_rot90(self, cbaoi, expected, augf_name):\n        for backend in [\"auto\", \"cv2\", \"skimage\"]:\n            with self.subTest(backend=backend):\n                aug = iaa.Affine(rotate=90, fit_output=True, backend=backend)\n\n                cbaoi_aug = getattr(aug, augf_name)(cbaoi)\n\n                assert_cbaois_equal(cbaoi_aug, expected)\n\n    def test_empty_polygons_rot90(self):\n        psoi = ia.PolygonsOnImage([], shape=(100, 200, 3))\n        expected = ia.PolygonsOnImage([], shape=(200, 100, 3))\n        self._test_empty_cbaoi_rot90(psoi, expected, \"augment_polygons\")\n\n    def test_empty_line_strings_rot90(self):\n        lsoi = ia.LineStringsOnImage([], shape=(100, 200, 3))\n        expected = ia.LineStringsOnImage([], shape=(200, 100, 3))\n        self._test_empty_cbaoi_rot90(lsoi, expected, \"augment_line_strings\")\n\n    def test_empty_bounding_boxes_rot90(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(100, 200, 3))\n        expected = ia.BoundingBoxesOnImage([], shape=(200, 100, 3))\n        self._test_empty_cbaoi_rot90(bbsoi, expected, \"augment_bounding_boxes\")\n\n\n# TODO merge these into TestAffine_rotate since they are rotations?\n#      or extend to contain other affine params too?\nclass TestAffine_alignment(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_image_segmap_alignment_with_translate_px(self):\n        image = np.zeros((80, 100, 3), dtype=np.uint8)\n        image[40-10:40+10, 50-10:50+10, :] = 255\n        hm = np.zeros((40, 50, 1), dtype=np.float32)\n        hm[20-5:20+5, 25-5:25+5, 0] = 1.0\n        hm = ia.HeatmapsOnImage(hm, shape=image.shape)\n\n        # note that if x is an odd value (e.g. 1), the projection is a bit\n        # less accurate as x=1 projected to a half-sized segmap is x=0.5,\n        # leading to interpolation effects\n        xvals = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, [0, 10, 20]]\n\n        for xvals_i in xvals:\n            with self.subTest(x=xvals_i):\n                aug = iaa.Affine(translate_px={\"x\": xvals_i})\n                iterations = 2 if ia.is_single_number(xvals_i) else 20\n\n                for _ in np.arange(iterations):\n                    image_aug, hm_aug = aug(image=image, heatmaps=hm)\n\n                    hm_aug_arr_rs = ia.imresize_single_image(\n                        hm_aug.get_arr(), (80, 100), interpolation=\"nearest\")\n                    overlap_true = np.sum(\n                        np.logical_and(\n                            (image_aug[..., 0] > 220),\n                            (hm_aug_arr_rs[..., 0] > 0.9)\n                        )\n                    )\n                    p_same_on_zero_cells = np.average(\n                        (image_aug[..., 0] > 220)\n                        == (hm_aug_arr_rs[..., 0] > 0.9))\n                    assert overlap_true > 19*19\n                    assert p_same_on_zero_cells > 0.98\n\n    def test_image_segmap_alignment_with_translate_percent(self):\n        image = np.zeros((80, 100, 3), dtype=np.uint8)\n        image[40-10:40+10, 50-10:50+10, :] = 255\n        hm = np.zeros((40, 50, 1), dtype=np.float32)\n        hm[20-5:20+5, 25-5:25+5, 0] = 1.0\n        hm = ia.HeatmapsOnImage(hm, shape=image.shape)\n\n        # note that if x is an odd value (e.g. 1), the projection is a bit\n        # less accurate as x=1 projected to a half-sized segmap is x=0.5,\n        # leading to interpolation effects\n        width = image.shape[1]\n        xvals = [0/width, 2/width, 4/width, 6/width, 8/width, 10/width,\n                 12/width, 14/width, 16/width, 18/width, 20/width,\n                 [0/width, 10/width, 20/width]]\n\n        for xvals_i in xvals:\n            with self.subTest(x=xvals_i):\n                aug = iaa.Affine(translate_percent={\"x\": xvals_i})\n                iterations = 2 if ia.is_single_number(xvals_i) else 20\n\n                for _ in np.arange(iterations):\n                    image_aug, hm_aug = aug(image=image, heatmaps=hm)\n\n                    hm_aug_arr_rs = ia.imresize_single_image(\n                        hm_aug.get_arr(), (80, 100), interpolation=\"nearest\")\n                    overlap_true = np.sum(\n                        np.logical_and(\n                            (image_aug[..., 0] > 220),\n                            (hm_aug_arr_rs[..., 0] > 0.9)\n                        )\n                    )\n                    p_same_on_zero_cells = np.average(\n                        (image_aug[..., 0] > 220)\n                        == (hm_aug_arr_rs[..., 0] > 0.9))\n                    assert overlap_true > 19*19\n                    assert p_same_on_zero_cells > 0.98\n\n    def test_image_keypoint_alignment(self):\n        aug = iaa.Affine(rotate=[0, 180], order=0)\n        img = np.zeros((10, 10), dtype=np.uint8)\n        img[0:5, 5] = 255\n        img[2, 4:6] = 255\n        img_rot = [np.copy(img), np.copy(np.flipud(np.fliplr(img)))]\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=5, y=2)], shape=img.shape)\n        kpsoi_rot = [(5, 2), (5, 10-2)]\n        img_aug_indices = []\n        kpsois_aug_indices = []\n        for _ in sm.xrange(40):\n            aug_det = aug.to_deterministic()\n            imgs_aug = aug_det.augment_images([img, img])\n            kpsois_aug = aug_det.augment_keypoints([kpsoi, kpsoi])\n\n            assert kpsois_aug[0].shape == img.shape\n            assert kpsois_aug[1].shape == img.shape\n\n            for img_aug in imgs_aug:\n                if np.array_equal(img_aug, img_rot[0]):\n                    img_aug_indices.append(0)\n                elif np.array_equal(img_aug, img_rot[1]):\n                    img_aug_indices.append(1)\n                else:\n                    assert False\n            for kpsoi_aug in kpsois_aug:\n                similar_to_rot_0 = np.allclose(\n                    [kpsoi_aug.keypoints[0].x, kpsoi_aug.keypoints[0].y],\n                    kpsoi_rot[0])\n                similar_to_rot_180 = np.allclose(\n                    [kpsoi_aug.keypoints[0].x, kpsoi_aug.keypoints[0].y],\n                    kpsoi_rot[1])\n                if similar_to_rot_0:\n                    kpsois_aug_indices.append(0)\n                elif similar_to_rot_180:\n                    kpsois_aug_indices.append(1)\n                else:\n                    assert False\n        assert np.array_equal(img_aug_indices, kpsois_aug_indices)\n        assert len(set(img_aug_indices)) == 2\n        assert len(set(kpsois_aug_indices)) == 2\n\n    @classmethod\n    def _test_image_cbaoi_alignment(cls, cbaoi, cbaoi_rot, augf_name):\n        aug = iaa.Affine(rotate=[0, 180], order=0)\n        img = np.zeros((10, 10), dtype=np.uint8)\n        img[0:5, 5] = 255\n        img[2, 4:6] = 255\n        img_rot = [np.copy(img), np.copy(np.flipud(np.fliplr(img)))]\n\n        img_aug_indices = []\n        cbaois_aug_indices = []\n        for _ in sm.xrange(40):\n            aug_det = aug.to_deterministic()\n            imgs_aug = aug_det.augment_images([img, img])\n            cbaois_aug = getattr(aug_det, augf_name)([cbaoi, cbaoi])\n\n            assert cbaois_aug[0].shape == img.shape\n            assert cbaois_aug[1].shape == img.shape\n            if hasattr(cbaois_aug[0].items[0], \"is_valid\"):\n                assert cbaois_aug[0].items[0].is_valid\n                assert cbaois_aug[1].items[0].is_valid\n\n            for img_aug in imgs_aug:\n                if np.array_equal(img_aug, img_rot[0]):\n                    img_aug_indices.append(0)\n                elif np.array_equal(img_aug, img_rot[1]):\n                    img_aug_indices.append(1)\n                else:\n                    assert False\n            for cbaoi_aug in cbaois_aug:\n                if cbaoi_aug.items[0].coords_almost_equals(cbaoi_rot[0]):\n                    cbaois_aug_indices.append(0)\n                elif cbaoi_aug.items[0].coords_almost_equals(cbaoi_rot[1]):\n                    cbaois_aug_indices.append(1)\n                else:\n                    assert False\n        assert np.array_equal(img_aug_indices, cbaois_aug_indices)\n        assert len(set(img_aug_indices)) == 2\n        assert len(set(cbaois_aug_indices)) == 2\n\n    def test_image_polygon_alignment(self):\n        psoi = ia.PolygonsOnImage([ia.Polygon([(1, 1), (9, 1), (5, 5)])],\n                                  shape=(10, 10))\n        psoi_rot = [\n            psoi.polygons[0].deepcopy(),\n            ia.Polygon([(10-1, 10-1), (10-9, 10-1), (10-5, 10-5)])\n        ]\n        self._test_image_cbaoi_alignment(psoi, psoi_rot,\n                                         \"augment_polygons\")\n\n    def test_image_line_string_alignment(self):\n        lsoi = ia.LineStringsOnImage([ia.LineString([(1, 1), (9, 1), (5, 5)])],\n                                     shape=(10, 10))\n        lsoi_rot = [\n            lsoi.items[0].deepcopy(),\n            ia.LineString([(10-1, 10-1), (10-9, 10-1), (10-5, 10-5)])\n        ]\n        self._test_image_cbaoi_alignment(lsoi, lsoi_rot,\n                                         \"augment_line_strings\")\n\n    def test_image_bounding_box_alignment(self):\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=1, y1=1, x2=9, y2=5)], shape=(10, 10))\n        bbsoi_rot = [\n            bbsoi.items[0].deepcopy(),\n            ia.BoundingBox(x1=10-9, y1=10-5, x2=10-1, y2=10-1)]\n        self._test_image_cbaoi_alignment(bbsoi, bbsoi_rot,\n                                         \"augment_bounding_boxes\")\n\n\nclass TestAffine_other_dtypes(unittest.TestCase):\n    @property\n    def translate_mask(self):\n        mask = np.zeros((3, 3), dtype=bool)\n        mask[1, 2] = True\n        return mask\n\n    @property\n    def image(self):\n        image = np.zeros((17, 17), dtype=bool)\n        image[2:15, 5:13] = True\n        return image\n\n    @property\n    def rot_mask_inner(self):\n        img_flipped = iaa.Fliplr(1.0)(image=self.image)\n        return img_flipped == 1\n\n    @property\n    def rot_mask_outer(self):\n        img_flipped = iaa.Fliplr(1.0)(image=self.image)\n        return img_flipped == 0\n\n    @property\n    def rot_thresh_inner(self):\n        return 0.9\n\n    @property\n    def rot_thresh_outer(self):\n        return 0.9\n\n    def rot_thresh_inner_float(self, order):\n        return 0.85 if order == 1 else 0.7\n\n    def rot_thresh_outer_float(self, order):\n        return 0.85 if order == 1 else 0.4\n\n    def test_translate_skimage_order_0_bool(self):\n        aug = iaa.Affine(translate_px={\"x\": 1}, order=0, mode=\"constant\",\n                         backend=\"skimage\")\n        image = np.zeros((3, 3), dtype=bool)\n        image[1, 1] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.all(image_aug[~self.translate_mask] == 0)\n        assert np.all(image_aug[self.translate_mask] == 1)\n\n    def test_translate_skimage_order_0_uint_int(self):\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"int8\", \"int16\", \"int32\"]\n        for dtype in dtypes:\n            aug = iaa.Affine(translate_px={\"x\": 1}, order=0, mode=\"constant\",\n                             backend=\"skimage\")\n\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            if np.dtype(dtype).kind == \"i\":\n                values = [1, 5, 10, 100, int(0.1 * max_value),\n                          int(0.2 * max_value), int(0.5 * max_value),\n                          max_value - 100, max_value]\n                values = values + [(-1) * value for value in values]\n            else:\n                values = [1, 5, 10, 100, int(center_value),\n                          int(0.1 * max_value), int(0.2 * max_value),\n                          int(0.5 * max_value), max_value - 100, max_value]\n\n            for value in values:\n                image = np.zeros((3, 3), dtype=dtype)\n                image[1, 1] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.all(image_aug[~self.translate_mask] == 0)\n                assert np.all(image_aug[self.translate_mask] == value)\n\n    def test_translate_skimage_order_0_float(self):\n        # float\n        dtypes = [\"float16\", \"float32\", \"float64\"]\n        for dtype in dtypes:\n            aug = iaa.Affine(translate_px={\"x\": 1}, order=0, mode=\"constant\",\n                             backend=\"skimage\")\n\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            def _isclose(a, b):\n                atol = 1e-4 if dtype == \"float16\" else 1e-8\n                return np.isclose(a, b, atol=atol, rtol=0)\n\n            isize = np.dtype(dtype).itemsize\n            values = [\n                0.01,\n                1.0,\n                10.0,\n                100.0,\n                500 ** (isize - 1),\n                float(np.float64(1000 ** (isize - 1)))\n            ]\n            values = values + [(-1) * value for value in values]\n            values = values + [min_value, max_value]\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[1, 1] = value\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert np.all(_isclose(image_aug[~self.translate_mask], 0))\n                    assert np.all(_isclose(image_aug[self.translate_mask],\n                                           value))\n\n    def test_rotate_skimage_order_not_0_bool(self):\n        # skimage, order!=0 and rotate=180\n        for order in [1, 3, 4, 5]:\n            aug = iaa.Affine(rotate=180, order=order, mode=\"constant\",\n                             backend=\"skimage\")\n            aug_flip = iaa.Sequential([iaa.Flipud(1.0), iaa.Fliplr(1.0)])\n\n            image = np.zeros((17, 17), dtype=bool)\n            image[2:15, 5:13] = True\n\n            image_aug = aug.augment_image(image)\n            image_exp = aug_flip.augment_image(image)\n\n            assert image_aug.dtype.name == image.dtype.name\n            assert (\n                np.sum(image_aug == image_exp)/image.size\n            ) > self.rot_thresh_inner\n\n    def test_rotate_skimage_order_not_0_uint_int(self):\n        def _compute_matching(image_aug, image_exp, mask):\n            return np.sum(\n                np.isclose(image_aug[mask], image_exp[mask], rtol=0,\n                           atol=1.001)\n            ) / np.sum(mask)\n\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"int8\", \"int16\", \"int32\"]\n        for dtype in dtypes:\n            for order in [1, 3, 4, 5]:\n                aug = iaa.Affine(rotate=180, order=order, mode=\"constant\",\n                                 backend=\"skimage\")\n                aug_flip = iaa.Sequential([iaa.Flipud(1.0), iaa.Fliplr(1.0)])\n\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                if np.dtype(dtype).kind == \"i\":\n                    values = [1, 5, 10, 100, int(0.1 * max_value),\n                              int(0.2 * max_value), int(0.5 * max_value),\n                              max_value - 100, max_value]\n                    values = values + [(-1) * value for value in values]\n                else:\n                    values = [1, 5, 10, 100, int(center_value),\n                              int(0.1 * max_value), int(0.2 * max_value),\n                              int(0.5 * max_value), max_value - 100, max_value]\n\n                for value in values:\n                    with self.subTest(dtype=dtype, order=order, value=value):\n                        image = np.zeros((17, 17), dtype=dtype)\n                        image[2:15, 5:13] = value\n\n                        image_aug = aug.augment_image(image)\n                        image_exp = aug_flip.augment_image(image)\n\n                        assert image_aug.dtype.name == dtype\n                        assert _compute_matching(\n                            image_aug, image_exp, self.rot_mask_inner\n                        ) > self.rot_thresh_inner\n                        assert _compute_matching(\n                            image_aug, image_exp, self.rot_mask_outer\n                        ) > self.rot_thresh_outer\n\n    def test_rotate_skimage_order_not_0_float(self):\n        def _compute_matching(image_aug, image_exp, mask):\n            return np.sum(\n                _isclose(image_aug[mask], image_exp[mask])\n            ) / np.sum(mask)\n\n        for order in [1, 3, 4, 5]:\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n            if order == 5:\n                # float64 caused too many interpolation inaccuracies for\n                # order=5, not wrong but harder to test\n                dtypes = [\"float16\", \"float32\"]\n            for dtype in dtypes:\n                aug = iaa.Affine(rotate=180, order=order, mode=\"constant\",\n                                 backend=\"skimage\")\n                aug_flip = iaa.Sequential([iaa.Flipud(1.0), iaa.Fliplr(1.0)])\n\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                def _isclose(a, b):\n                    atol = 1e-4 if dtype == \"float16\" else 1e-8\n                    if order not in [0, 1]:\n                        atol = 1e-2\n                    return np.isclose(a, b, atol=atol, rtol=0)\n\n                isize = np.dtype(dtype).itemsize\n                values = [0.01, 1.0, 10.0, 100.0, 500 ** (isize - 1),\n                          1000 ** (isize - 1)]\n                values = values + [(-1) * value for value in values]\n                if order not in [3, 4]:  # results in NaNs otherwise\n                    values = values + [min_value, max_value]\n                for value in values:\n                    with self.subTest(order=order, dtype=dtype, value=value):\n                        image = np.zeros((17, 17), dtype=dtype)\n                        image[2:15, 5:13] = value\n\n                        image_aug = aug.augment_image(image)\n                        image_exp = aug_flip.augment_image(image)\n\n                        assert image_aug.dtype.name == dtype\n                        assert _compute_matching(\n                            image_aug, image_exp, self.rot_mask_inner\n                        ) > self.rot_thresh_inner_float(order)\n                        assert _compute_matching(\n                            image_aug, image_exp, self.rot_mask_outer\n                        ) > self.rot_thresh_outer_float(order)\n\n    def test_translate_cv2_order_0_bool(self):\n        aug = iaa.Affine(translate_px={\"x\": 1}, order=0, mode=\"constant\",\n                         backend=\"cv2\")\n\n        image = np.zeros((3, 3), dtype=bool)\n        image[1, 1] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.all(image_aug[~self.translate_mask] == 0)\n        assert np.all(image_aug[self.translate_mask] == 1)\n\n    def test_translate_cv2_order_0_uint_int(self):\n        aug = iaa.Affine(translate_px={\"x\": 1}, order=0, mode=\"constant\",\n                         backend=\"cv2\")\n\n        dtypes = [\"uint8\", \"uint16\", \"int8\", \"int16\", \"int32\"]\n        for dtype in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            if np.dtype(dtype).kind == \"i\":\n                values = [1, 5, 10, 100, int(0.1 * max_value),\n                          int(0.2 * max_value), int(0.5 * max_value),\n                          max_value - 100, max_value]\n                values = values + [(-1) * value for value in values]\n            else:\n                values = [1, 5, 10, 100, int(center_value),\n                          int(0.1 * max_value), int(0.2 * max_value),\n                          int(0.5 * max_value), max_value - 100, max_value]\n\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[1, 1] = value\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert np.all(image_aug[~self.translate_mask] == 0)\n                    assert np.all(image_aug[self.translate_mask] == value)\n\n    def test_translate_cv2_order_0_float(self):\n        aug = iaa.Affine(translate_px={\"x\": 1}, order=0, mode=\"constant\",\n                         backend=\"cv2\")\n\n        dtypes = [\"float16\", \"float32\", \"float64\"]\n        for dtype in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            def _isclose(a, b):\n                atol = 1e-4 if dtype == \"float16\" else 1e-8\n                return np.isclose(a, b, atol=atol, rtol=0)\n\n            isize = np.dtype(dtype).itemsize\n            values = [\n                0.01,\n                1.0,\n                10.0,\n                100.0,\n                500 ** (isize - 1),\n                float(np.float64(1000 ** (isize - 1)))\n            ]\n            values = values + [(-1) * value for value in values]\n            values = values + [min_value, max_value]\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[1, 1] = value\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert np.all(_isclose(image_aug[~self.translate_mask], 0))\n                    assert np.all(_isclose(image_aug[self.translate_mask],\n                                           value))\n\n    def test_rotate_cv2_order_1_and_3_bool(self):\n        # cv2, order=1 and rotate=180\n        for order in [1, 3]:\n            aug = iaa.Affine(rotate=180, order=order, mode=\"constant\",\n                             backend=\"cv2\")\n            aug_flip = iaa.Sequential([iaa.Flipud(1.0), iaa.Fliplr(1.0)])\n\n            image = np.zeros((17, 17), dtype=bool)\n            image[2:15, 5:13] = True\n\n            image_aug = aug.augment_image(image)\n            image_exp = aug_flip.augment_image(image)\n\n            assert image_aug.dtype.name == image.dtype.name\n            assert (np.sum(image_aug == image_exp) / image.size) > 0.9\n\n    def test_rotate_cv2_order_1_and_3_uint_int(self):\n        # cv2, order=1 and rotate=180\n        for order in [1, 3]:\n            aug = iaa.Affine(rotate=180, order=order, mode=\"constant\",\n                             backend=\"cv2\")\n            aug_flip = iaa.Sequential([iaa.Flipud(1.0), iaa.Fliplr(1.0)])\n\n            dtypes = [\"uint8\", \"uint16\", \"int8\", \"int16\"]\n            for dtype in dtypes:\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                if np.dtype(dtype).kind == \"i\":\n                    values = [1, 5, 10, 100, int(0.1 * max_value),\n                              int(0.2 * max_value), int(0.5 * max_value),\n                              max_value - 100, max_value]\n                    values = values + [(-1) * value for value in values]\n                else:\n                    values = [1, 5, 10, 100, int(center_value),\n                              int(0.1 * max_value), int(0.2 * max_value),\n                              int(0.5 * max_value), max_value - 100, max_value]\n\n                for value in values:\n                    with self.subTest(order=order, dtype=dtype, value=value):\n                        image = np.zeros((17, 17), dtype=dtype)\n                        image[2:15, 5:13] = value\n\n                        image_aug = aug.augment_image(image)\n                        image_exp = aug_flip.augment_image(image)\n\n                        assert image_aug.dtype.name == dtype\n                        assert (\n                            np.sum(image_aug == image_exp) / image.size\n                        ) > 0.9\n\n    def test_rotate_cv2_order_1_and_3_float(self):\n        # cv2, order=1 and rotate=180\n        for order in [1, 3]:\n            aug = iaa.Affine(rotate=180, order=order, mode=\"constant\",\n                             backend=\"cv2\")\n            aug_flip = iaa.Sequential([iaa.Flipud(1.0), iaa.Fliplr(1.0)])\n\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n            for dtype in dtypes:\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                def _isclose(a, b):\n                    atol = 1e-4 if dtype == \"float16\" else 1e-8\n                    return np.isclose(a, b, atol=atol, rtol=0)\n\n                isize = np.dtype(dtype).itemsize\n                values = [0.01, 1.0, 10.0, 100.0, 500 ** (isize - 1),\n                          1000 ** (isize - 1)]\n                values = values + [(-1) * value for value in values]\n                values = values + [min_value, max_value]\n                for value in values:\n                    with self.subTest(order=order, dtype=dtype, value=value):\n                        image = np.zeros((17, 17), dtype=dtype)\n                        image[2:15, 5:13] = value\n\n                        image_aug = aug.augment_image(image)\n                        image_exp = aug_flip.augment_image(image)\n\n                        assert image_aug.dtype.name == dtype\n                        assert (\n                            np.sum(_isclose(image_aug, image_exp)) / image.size\n                        ) > 0.9\n\n\nclass TestAffine_other(unittest.TestCase):\n    def test_unusual_channel_numbers(self):\n        with assertWarns(self, iaa.SuspiciousSingleImageShapeWarning):\n            nb_channels_lst = [4, 5, 512, 513]\n            orders = [0, 1, 3]\n            backends = [\"auto\", \"skimage\", \"cv2\"]\n            gen = itertools.product(nb_channels_lst, orders, backends)\n            for nb_channels, order, backend in gen:\n                with self.subTest(nb_channels=nb_channels, order=order,\n                                  backend=backend):\n                    aug = iaa.Affine(translate_px={\"x\": -1}, mode=\"constant\",\n                                     cval=255, order=order, backend=backend)\n\n                    image = np.full((3, 3, nb_channels), 128, dtype=np.uint8)\n                    heatmap_arr = np.full((3, 3, nb_channels), 0.5,\n                                          dtype=np.float32)\n                    heatmap = ia.HeatmapsOnImage(heatmap_arr, shape=image.shape)\n\n                    image_aug, heatmap_aug = aug(image=image, heatmaps=heatmap)\n                    hm_aug_arr = heatmap_aug.arr_0to1\n\n                    assert image_aug.shape == (3, 3, nb_channels)\n                    assert heatmap_aug.arr_0to1.shape == (3, 3, nb_channels)\n                    assert heatmap_aug.shape == image.shape\n                    assert np.allclose(image_aug[:, 0:2, :], 128, rtol=0,\n                                       atol=2)\n                    assert np.allclose(image_aug[:, 2:3, 0:3], 255, rtol=0,\n                                       atol=2)\n                    assert np.allclose(image_aug[:, 2:3, 3:], 255, rtol=0,\n                                       atol=2)\n                    assert np.allclose(hm_aug_arr[:, 0:2, :], 0.5, rtol=0,\n                                       atol=0.025)\n                    assert np.allclose(hm_aug_arr[:, 2:3, :], 0.0, rtol=0,\n                                       atol=0.025)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for fit_output in [False, True]:\n            for shape in shapes:\n                with self.subTest(shape=shape, fit_output=fit_output):\n                    image = np.zeros(shape, dtype=np.uint8)\n                    aug = iaa.Affine(rotate=45, fit_output=fit_output)\n\n                    image_aug = aug(image=image)\n\n                    assert image_aug.dtype.name == \"uint8\"\n                    assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.Affine(scale=(0.9, 1.1), translate_px=(-4, 4),\n                         rotate=(-10, 10), shear=(-10, 10), order=[0, 1])\n        runtest_pickleable_uint8_img(aug, iterations=20)\n\n\nclass TestScaleX(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.ScaleX(1.5)\n        assert isinstance(aug, iaa.Affine)\n        assert np.isclose(aug.scale[0].value, 1.5)\n        assert aug.order.value == 1\n        assert aug.cval.value == 0\n        assert aug.mode.value == \"constant\"\n        assert aug.fit_output is False\n\n    def test_integrationtest(self):\n        image = np.zeros((10, 10), dtype=np.uint8)\n        image[5, 5] = 255\n        aug = iaa.ScaleX(4.0, order=0)\n\n        image_aug = aug(image=image)\n\n        xx = np.nonzero(np.max(image_aug, axis=0) > 200)[0]\n        yy = np.nonzero(np.max(image_aug, axis=1) > 200)[0]\n        x1, x2 = xx[0], xx[-1]\n        y1, y2 = yy[0], yy[-1]\n        # not >=3, because if e.g. index 1 is spread to 0 to 3 after scaling,\n        # it covers four cells (0, 1, 2, 3), but 3-0 is 3\n        assert x2 - x1 >= 3\n        assert y2 - y1 < 1\n\n\nclass TestScaleY(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.ScaleY(1.5)\n        assert isinstance(aug, iaa.Affine)\n        assert np.isclose(aug.scale[1].value, 1.5)\n        assert aug.order.value == 1\n        assert aug.cval.value == 0\n        assert aug.mode.value == \"constant\"\n        assert aug.fit_output is False\n\n    def test_integrationtest(self):\n        image = np.zeros((10, 10), dtype=np.uint8)\n        image[5, 5] = 255\n        aug = iaa.ScaleY(4.0, order=0)\n\n        image_aug = aug(image=image)\n\n        xx = np.nonzero(np.max(image_aug, axis=0) > 200)[0]\n        yy = np.nonzero(np.max(image_aug, axis=1) > 200)[0]\n        x1, x2 = xx[0], xx[-1]\n        y1, y2 = yy[0], yy[-1]\n        # not >=3, because if e.g. index 1 is spread to 0 to 3 after scaling,\n        # it covers four cells (0, 1, 2, 3), but 3-0 is 3\n        assert y2 - y1 >= 3\n        assert x2 - x1 < 1\n\n\nclass TestTranslateX(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___translate_percent(self):\n        aug = iaa.TranslateX(percent=0.5)\n        assert isinstance(aug, iaa.Affine)\n        assert np.isclose(aug.translate[0].value, 0.5)\n        assert aug.order.value == 1\n        assert aug.cval.value == 0\n        assert aug.mode.value == \"constant\"\n        assert aug.fit_output is False\n\n    def test___init___translate_px(self):\n        aug = iaa.TranslateX(px=2)\n        assert isinstance(aug, iaa.Affine)\n        assert np.isclose(aug.translate[0].value, 2)\n        assert aug.order.value == 1\n        assert aug.cval.value == 0\n        assert aug.mode.value == \"constant\"\n        assert aug.fit_output is False\n\n    def test___init___both_none(self):\n        aug = iaa.TranslateX()\n        assert np.isclose(aug.translate[0].a.value, -0.25)\n        assert np.isclose(aug.translate[0].b.value, 0.25)\n\n    def test_integrationtest_translate_percent(self):\n        image = np.full((50, 50), 255, dtype=np.uint8)\n        aug = iaa.TranslateX(percent=0.5, order=1, cval=0)\n\n        image_aug = aug(image=image)\n\n        expected = np.copy(image)\n        expected[:, 0:25] = 0\n        overlap = np.average(np.isclose(image_aug, expected, atol=1.01))\n        assert overlap > (1.0 - (1/50) - 1e-4)\n\n    def test_integrationtest_translate_px(self):\n        image = np.full((50, 50), 255, dtype=np.uint8)\n        aug = iaa.TranslateX(px=25, order=1, cval=0)\n\n        image_aug = aug(image=image)\n\n        expected = np.copy(image)\n        expected[:, 0:25] = 0\n        overlap = np.average(np.isclose(image_aug, expected, atol=1.01))\n        assert overlap > (1.0 - (1/50) - 1e-4)\n\n\nclass TestTranslateY(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___translate_percent(self):\n        aug = iaa.TranslateY(percent=0.5)\n        assert isinstance(aug, iaa.Affine)\n        assert np.isclose(aug.translate[1].value, 0.5)\n        assert aug.order.value == 1\n        assert aug.cval.value == 0\n        assert aug.mode.value == \"constant\"\n        assert aug.fit_output is False\n\n    def test___init___translate_px(self):\n        aug = iaa.TranslateY(px=2)\n        assert isinstance(aug, iaa.Affine)\n        assert np.isclose(aug.translate[1].value, 2)\n        assert aug.order.value == 1\n        assert aug.cval.value == 0\n        assert aug.mode.value == \"constant\"\n        assert aug.fit_output is False\n\n    def test___init___both_none(self):\n        aug = iaa.TranslateY()\n        assert np.isclose(aug.translate[1].a.value, -0.25)\n        assert np.isclose(aug.translate[1].b.value, 0.25)\n\n    def test_integrationtest_translate_percent(self):\n        image = np.full((50, 50), 255, dtype=np.uint8)\n        aug = iaa.TranslateY(percent=0.5, order=1, cval=0)\n\n        image_aug = aug(image=image)\n\n        expected = np.copy(image)\n        expected[0:25, :] = 0\n        overlap = np.average(np.isclose(image_aug, expected, atol=1.01))\n        assert overlap > (1.0 - (1/50) - 1e-4)\n\n    def test_integrationtest_translate_px(self):\n        image = np.full((50, 50), 255, dtype=np.uint8)\n        aug = iaa.TranslateY(px=25, order=1, cval=0)\n\n        image_aug = aug(image=image)\n\n        expected = np.copy(image)\n        expected[0:25, :] = 0\n        overlap = np.average(np.isclose(image_aug, expected, atol=1.01))\n        assert overlap > (1.0 - (1/50) - 1e-4)\n\n\nclass TestRotate(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___(self):\n        aug = iaa.Rotate(rotate=45)\n        assert isinstance(aug, iaa.Affine)\n        assert np.isclose(aug.rotate.value, 45)\n        assert aug.order.value == 1\n        assert aug.cval.value == 0\n        assert aug.mode.value == \"constant\"\n        assert aug.fit_output is False\n\n    def test_integrationtest(self):\n        image = np.zeros((40, 20), dtype=np.uint8)\n        image[:, 10:10+1] = 255\n        aug = iaa.Rotate(90, order=0)\n\n        image_aug = aug(image=image)\n\n        assert image_aug.shape == (40, 20)\n        assert np.isclose(np.sum(image_aug[20-1:20+2, :]), 255*20, atol=1)\n\n\nclass TestShearX(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.ShearX(40)\n        assert isinstance(aug, iaa.Affine)\n        assert aug.shear[0].value == 40\n        assert aug.order.value == 1\n        assert aug.cval.value == 0\n        assert aug.mode.value == \"constant\"\n        assert aug.fit_output is False\n\n    def test_integrationtest(self):\n        def _find_coords(arr):\n            xx = np.nonzero(np.max(arr, axis=0) > 200)[0]\n            yy = np.nonzero(np.max(arr, axis=1) > 200)[0]\n            x1 = xx[0]\n            x2 = xx[-1]\n            y1 = yy[0]\n            y2 = yy[-1]\n            return x1+(x2-x1)/2, y1+(y2-y1)/2\n\n        image = np.zeros((50, 50, 4), dtype=np.uint8)\n        image[10:10+1, 20:20+1, 0] = 255\n        image[10:10+1, 30:30+1, 1] = 255\n        image[40:40+1, 30:30+1, 2] = 255\n        image[40:40+1, 20:20+1, 3] = 255\n        aug = iaa.ShearX(30, order=0)\n\n        image_aug = aug(image=image)\n\n        x1, y1 = _find_coords(image_aug[..., 0])\n        x2, y2 = _find_coords(image_aug[..., 1])\n        x3, y3 = _find_coords(image_aug[..., 2])\n        x4, y4 = _find_coords(image_aug[..., 3])\n        assert x1 > 20\n        assert np.isclose(y1, 10.0)\n        assert np.isclose(y2, 10.0)\n        assert x3 < 30\n        assert np.isclose(y3, 40.0)\n        assert np.isclose(y4, 40.0)\n        assert not np.isclose(x1, x4)\n        assert not np.isclose(x2, x3)\n\n\nclass TestShearY(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.ShearY(40)\n        assert isinstance(aug, iaa.Affine)\n        assert aug.shear[1].value == 40\n        assert aug.order.value == 1\n        assert aug.cval.value == 0\n        assert aug.mode.value == \"constant\"\n        assert aug.fit_output is False\n\n    def test_integrationtest(self):\n        def _find_coords(arr):\n            xx = np.nonzero(np.max(arr, axis=0) > 200)[0]\n            yy = np.nonzero(np.max(arr, axis=1) > 200)[0]\n            x1 = xx[0]\n            x2 = xx[-1]\n            y1 = yy[0]\n            y2 = yy[-1]\n            return x1+(x2-x1)/2, y1+(y2-y1)/2\n\n        image = np.zeros((50, 50, 4), dtype=np.uint8)\n        image[20:20+1, 10:10+1, 0] = 255\n        image[20:20+1, 40:40+1, 1] = 255\n        image[30:30+1, 40:40+1, 2] = 255\n        image[30:30+1, 10:10+1, 3] = 255\n        aug = iaa.ShearY(30, order=0)\n\n        image_aug = aug(image=image)\n\n        x1, y1 = _find_coords(image_aug[..., 0])\n        x2, y2 = _find_coords(image_aug[..., 1])\n        x3, y3 = _find_coords(image_aug[..., 2])\n        x4, y4 = _find_coords(image_aug[..., 3])\n        assert y1 < 20\n        assert np.isclose(x1, 10.0)\n        assert np.isclose(x4, 10.0)\n        assert y2 > 20\n        assert np.isclose(x2, 40.0)\n        assert np.isclose(x3, 40.0)\n        assert not np.isclose(y1, y2)\n        assert not np.isclose(y3, y4)\n\n\n# TODO migrate to unittest and split up tests or remove AffineCv2\ndef test_AffineCv2():\n    reseed()\n\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n        _ = iaa.AffineCv2()\n\n    assert \"is deprecated\" in str(caught_warnings[0].message)\n\n    with warnings.catch_warnings():\n        warnings.simplefilter(\"ignore\", category=ia.DeprecationWarning)\n\n        base_img = np.array([[0, 0, 0],\n                             [0, 255, 0],\n                             [0, 0, 0]], dtype=np.uint8)\n        base_img = base_img[:, :, np.newaxis]\n\n        images = np.array([base_img])\n        images_list = [base_img]\n        outer_pixels = ([], [])\n        for i in sm.xrange(base_img.shape[0]):\n            for j in sm.xrange(base_img.shape[1]):\n                if i != j:\n                    outer_pixels[0].append(i)\n                    outer_pixels[1].append(j)\n\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        keypoints = [ia.KeypointsOnImage(kps, shape=base_img.shape)]\n\n        # no translation/scale/rotate/shear, shouldnt change nothing\n        aug = iaa.AffineCv2(scale=1.0, translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n\n        observed = aug_det.augment_images(images)\n        expected = images\n        assert np.array_equal(observed, expected)\n\n        observed = aug.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n        observed = aug_det.augment_images(images_list)\n        expected = images_list\n        assert array_equal_lists(observed, expected)\n\n        observed = aug.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        expected = keypoints\n        assert keypoints_equal(observed, expected)\n\n        # ---------------------\n        # scale\n        # ---------------------\n        # zoom in\n        aug = iaa.AffineCv2(scale=1.75, translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 20).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 150).all()\n\n        observed = aug_det.augment_images(images)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 20).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 150).all()\n\n        observed = aug.augment_images(images_list)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 20).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 150).all()\n\n        observed = aug_det.augment_images(images_list)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] > 20).all()\n        assert (observed[0][outer_pixels[0], outer_pixels[1]] < 150).all()\n\n        observed = aug.augment_keypoints(keypoints)\n        assert observed[0].keypoints[0].x < 0\n        assert observed[0].keypoints[0].y < 0\n        assert observed[0].keypoints[1].x == 1\n        assert observed[0].keypoints[1].y == 1\n        assert observed[0].keypoints[2].x > 2\n        assert observed[0].keypoints[2].y > 2\n\n        observed = aug_det.augment_keypoints(keypoints)\n        assert observed[0].keypoints[0].x < 0\n        assert observed[0].keypoints[0].y < 0\n        assert observed[0].keypoints[1].x == 1\n        assert observed[0].keypoints[1].y == 1\n        assert observed[0].keypoints[2].x > 2\n        assert observed[0].keypoints[2].y > 2\n\n        # zoom in only on x axis\n        aug = iaa.AffineCv2(scale={\"x\": 1.75, \"y\": 1.0}, translate_px=0,\n                            rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[1, 1], [0, 2]] > 20).all()\n        assert (observed[0][[1, 1], [0, 2]] < 150).all()\n        assert (observed[0][0, :] < 5).all()\n        assert (observed[0][2, :] < 5).all()\n\n        observed = aug_det.augment_images(images)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[1, 1], [0, 2]] > 20).all()\n        assert (observed[0][[1, 1], [0, 2]] < 150).all()\n        assert (observed[0][0, :] < 5).all()\n        assert (observed[0][2, :] < 5).all()\n\n        observed = aug.augment_images(images_list)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[1, 1], [0, 2]] > 20).all()\n        assert (observed[0][[1, 1], [0, 2]] < 150).all()\n        assert (observed[0][0, :] < 5).all()\n        assert (observed[0][2, :] < 5).all()\n\n        observed = aug_det.augment_images(images_list)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[1, 1], [0, 2]] > 20).all()\n        assert (observed[0][[1, 1], [0, 2]] < 150).all()\n        assert (observed[0][0, :] < 5).all()\n        assert (observed[0][2, :] < 5).all()\n\n        observed = aug.augment_keypoints(keypoints)\n        assert observed[0].keypoints[0].x < 0\n        assert observed[0].keypoints[0].y == 0\n        assert observed[0].keypoints[1].x == 1\n        assert observed[0].keypoints[1].y == 1\n        assert observed[0].keypoints[2].x > 2\n        assert observed[0].keypoints[2].y == 2\n\n        observed = aug_det.augment_keypoints(keypoints)\n        assert observed[0].keypoints[0].x < 0\n        assert observed[0].keypoints[0].y == 0\n        assert observed[0].keypoints[1].x == 1\n        assert observed[0].keypoints[1].y == 1\n        assert observed[0].keypoints[2].x > 2\n        assert observed[0].keypoints[2].y == 2\n\n        # zoom in only on y axis\n        aug = iaa.AffineCv2(scale={\"x\": 1.0, \"y\": 1.75}, translate_px=0,\n                            rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        observed = aug.augment_images(images)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[0, 2], [1, 1]] > 20).all()\n        assert (observed[0][[0, 2], [1, 1]] < 150).all()\n        assert (observed[0][:, 0] < 5).all()\n        assert (observed[0][:, 2] < 5).all()\n\n        observed = aug_det.augment_images(images)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[0, 2], [1, 1]] > 20).all()\n        assert (observed[0][[0, 2], [1, 1]] < 150).all()\n        assert (observed[0][:, 0] < 5).all()\n        assert (observed[0][:, 2] < 5).all()\n\n        observed = aug.augment_images(images_list)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[0, 2], [1, 1]] > 20).all()\n        assert (observed[0][[0, 2], [1, 1]] < 150).all()\n        assert (observed[0][:, 0] < 5).all()\n        assert (observed[0][:, 2] < 5).all()\n\n        observed = aug_det.augment_images(images_list)\n        assert observed[0][1, 1] > 250\n        assert (observed[0][[0, 2], [1, 1]] > 20).all()\n        assert (observed[0][[0, 2], [1, 1]] < 150).all()\n        assert (observed[0][:, 0] < 5).all()\n        assert (observed[0][:, 2] < 5).all()\n\n        observed = aug.augment_keypoints(keypoints)\n        assert observed[0].keypoints[0].x == 0\n        assert observed[0].keypoints[0].y < 0\n        assert observed[0].keypoints[1].x == 1\n        assert observed[0].keypoints[1].y == 1\n        assert observed[0].keypoints[2].x == 2\n        assert observed[0].keypoints[2].y > 2\n\n        observed = aug_det.augment_keypoints(keypoints)\n        assert observed[0].keypoints[0].x == 0\n        assert observed[0].keypoints[0].y < 0\n        assert observed[0].keypoints[1].x == 1\n        assert observed[0].keypoints[1].y == 1\n        assert observed[0].keypoints[2].x == 2\n        assert observed[0].keypoints[2].y > 2\n\n        # zoom out\n        # this one uses a 4x4 area of all 255, which is zoomed out to a 4x4\n        # area in which the center 2x2 area is 255\n        # zoom in should probably be adapted to this style\n        # no separate tests here for x/y axis, should work fine if zoom in\n        # works with that\n        aug = iaa.AffineCv2(scale=0.49, translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        image = np.ones((4, 4, 1), dtype=np.uint8) * 255\n        images = np.array([image])\n        images_list = [image]\n        outer_pixels = ([], [])\n        for y in sm.xrange(4):\n            xs = sm.xrange(4) if y in [0, 3] else [0, 3]\n            for x in xs:\n                outer_pixels[0].append(y)\n                outer_pixels[1].append(x)\n        inner_pixels = ([1, 1, 2, 2], [1, 2, 1, 2])\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=3, y=0),\n               ia.Keypoint(x=0, y=3), ia.Keypoint(x=3, y=3)]\n        keypoints = [ia.KeypointsOnImage(kps, shape=image.shape)]\n        kps_aug = [ia.Keypoint(x=0.765, y=0.765),\n                   ia.Keypoint(x=2.235, y=0.765),\n                   ia.Keypoint(x=0.765, y=2.235),\n                   ia.Keypoint(x=2.235, y=2.235)]\n        keypoints_aug = [ia.KeypointsOnImage(kps_aug, shape=image.shape)]\n\n        observed = aug.augment_images(images)\n        assert (observed[0][outer_pixels] < 25).all()\n        assert (observed[0][inner_pixels] > 200).all()\n\n        observed = aug_det.augment_images(images)\n        assert (observed[0][outer_pixels] < 25).all()\n        assert (observed[0][inner_pixels] > 200).all()\n\n        observed = aug.augment_images(images_list)\n        assert (observed[0][outer_pixels] < 25).all()\n        assert (observed[0][inner_pixels] > 200).all()\n\n        observed = aug_det.augment_images(images_list)\n        assert (observed[0][outer_pixels] < 25).all()\n        assert (observed[0][inner_pixels] > 200).all()\n\n        observed = aug.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        # varying scales\n        aug = iaa.AffineCv2(scale={\"x\": (0.5, 1.5), \"y\": (0.5, 1.5)},\n                            translate_px=0, rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        image = np.array([[0, 0, 0, 0, 0],\n                          [0, 1, 1, 1, 0],\n                          [0, 1, 2, 1, 0],\n                          [0, 1, 1, 1, 0],\n                          [0, 0, 0, 0, 0]], dtype=np.uint8) * 100\n        image = image[:, :, np.newaxis]\n        images = np.array([image])\n\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n        assert nb_changed_aug >= int(nb_iterations * 0.8)\n        assert nb_changed_aug_det == 0\n\n        aug = iaa.AffineCv2(scale=iap.Uniform(0.7, 0.9))\n        assert is_parameter_instance(aug.scale, iap.Uniform)\n        assert is_parameter_instance(aug.scale.a, iap.Deterministic)\n        assert is_parameter_instance(aug.scale.b, iap.Deterministic)\n        assert 0.7 - 1e-8 < aug.scale.a.value < 0.7 + 1e-8\n        assert 0.9 - 1e-8 < aug.scale.b.value < 0.9 + 1e-8\n\n        # ---------------------\n        # translate\n        # ---------------------\n        # move one pixel to the right\n        aug = iaa.AffineCv2(scale=1.0, translate_px={\"x\": 1, \"y\": 0},\n                            rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        image = np.zeros((3, 3, 1), dtype=np.uint8)\n        image_aug = np.copy(image)\n        image[1, 1] = 255\n        image_aug[1, 2] = 255\n        images = np.array([image])\n        images_aug = np.array([image_aug])\n        images_list = [image]\n        images_aug_list = [image_aug]\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)],\n                                         shape=base_img.shape)]\n        keypoints_aug = [ia.KeypointsOnImage([ia.Keypoint(x=2, y=1)],\n                                             shape=base_img.shape)]\n\n        observed = aug.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        observed = aug_det.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        observed = aug.augment_images(images_list)\n        assert array_equal_lists(observed, images_aug_list)\n\n        observed = aug_det.augment_images(images_list)\n        assert array_equal_lists(observed, images_aug_list)\n\n        observed = aug.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        # move one pixel to the right\n        aug = iaa.AffineCv2(scale=1.0, translate_px={\"x\": 1, \"y\": 0},\n                            rotate=0, shear=0)\n        observed = aug.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        # move one pixel to the right\n        aug = iaa.AffineCv2(scale=1.0, translate_px={\"x\": 1, \"y\": 0},\n                            rotate=0, shear=0)\n        observed = aug.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        # move one pixel to the right\n        # with order=ALL\n        aug = iaa.AffineCv2(scale=1.0, translate_px={\"x\": 1, \"y\": 0},\n                            rotate=0, shear=0, order=ia.ALL)\n        observed = aug.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        # move one pixel to the right\n        # with order=list\n        aug = iaa.AffineCv2(scale=1.0, translate_px={\"x\": 1, \"y\": 0},\n                            rotate=0, shear=0, order=[0, 1, 2])\n        observed = aug.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        # move one pixel to the right\n        # with order=StochasticParameter\n        aug = iaa.AffineCv2(scale=1.0, translate_px={\"x\": 1, \"y\": 0},\n                            rotate=0, shear=0, order=iap.Choice([0, 1, 2]))\n        observed = aug.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        # move one pixel to the bottom\n        aug = iaa.AffineCv2(scale=1.0, translate_px={\"x\": 0, \"y\": 1},\n                            rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        image = np.zeros((3, 3, 1), dtype=np.uint8)\n        image_aug = np.copy(image)\n        image[1, 1] = 255\n        image_aug[2, 1] = 255\n        images = np.array([image])\n        images_aug = np.array([image_aug])\n        images_list = [image]\n        images_aug_list = [image_aug]\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)],\n                                         shape=base_img.shape)]\n        keypoints_aug = [ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)],\n                                             shape=base_img.shape)]\n\n        observed = aug.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        observed = aug_det.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        observed = aug.augment_images(images_list)\n        assert array_equal_lists(observed, images_aug_list)\n\n        observed = aug_det.augment_images(images_list)\n        assert array_equal_lists(observed, images_aug_list)\n\n        observed = aug.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        # move 33% (one pixel) to the right\n        aug = iaa.AffineCv2(scale=1.0, translate_percent={\"x\": 0.3333, \"y\": 0},\n                            rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        image = np.zeros((3, 3, 1), dtype=np.uint8)\n        image_aug = np.copy(image)\n        image[1, 1] = 255\n        image_aug[1, 2] = 255\n        images = np.array([image])\n        images_aug = np.array([image_aug])\n        images_list = [image]\n        images_aug_list = [image_aug]\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)],\n                                         shape=base_img.shape)]\n        keypoints_aug = [ia.KeypointsOnImage([ia.Keypoint(x=2, y=1)],\n                                             shape=base_img.shape)]\n\n        observed = aug.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        observed = aug_det.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        observed = aug.augment_images(images_list)\n        assert array_equal_lists(observed, images_aug_list)\n\n        observed = aug_det.augment_images(images_list)\n        assert array_equal_lists(observed, images_aug_list)\n\n        observed = aug.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        # move 33% (one pixel) to the bottom\n        aug = iaa.AffineCv2(scale=1.0, translate_percent={\"x\": 0, \"y\": 0.3333},\n                            rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n\n        image = np.zeros((3, 3, 1), dtype=np.uint8)\n        image_aug = np.copy(image)\n        image[1, 1] = 255\n        image_aug[2, 1] = 255\n        images = np.array([image])\n        images_aug = np.array([image_aug])\n        images_list = [image]\n        images_aug_list = [image_aug]\n        keypoints = [ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)],\n                                         shape=base_img.shape)]\n        keypoints_aug = [ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)],\n                                             shape=base_img.shape)]\n\n        observed = aug.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        observed = aug_det.augment_images(images)\n        assert np.array_equal(observed, images_aug)\n\n        observed = aug.augment_images(images_list)\n        assert array_equal_lists(observed, images_aug_list)\n\n        observed = aug_det.augment_images(images_list)\n        assert array_equal_lists(observed, images_aug_list)\n\n        observed = aug.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        # 0-1px to left/right and 0-1px to top/bottom\n        aug = iaa.AffineCv2(scale=1.0,\n                            translate_px={\"x\": (-1, 1), \"y\": (-1, 1)},\n                            rotate=0, shear=0)\n        aug_det = aug.to_deterministic()\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        centers_aug = np.copy(image).astype(np.int32) * 0\n        centers_aug_det = np.copy(image).astype(np.int32) * 0\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n\n            assert len(observed_aug[0].nonzero()[0]) == 1\n            assert len(observed_aug_det[0].nonzero()[0]) == 1\n            centers_aug += (observed_aug[0] > 0)\n            centers_aug_det += (observed_aug_det[0] > 0)\n\n        assert nb_changed_aug >= int(nb_iterations * 0.7)\n        assert nb_changed_aug_det == 0\n        assert (centers_aug > int(nb_iterations * (1/9 * 0.6))).all()\n        assert (centers_aug < int(nb_iterations * (1/9 * 1.4))).all()\n\n        aug = iaa.AffineCv2(translate_percent=iap.Uniform(0.7, 0.9))\n        assert is_parameter_instance(aug.translate, iap.Uniform)\n        assert is_parameter_instance(aug.translate.a, iap.Deterministic)\n        assert is_parameter_instance(aug.translate.b, iap.Deterministic)\n        assert 0.7 - 1e-8 < aug.translate.a.value < 0.7 + 1e-8\n        assert 0.9 - 1e-8 < aug.translate.b.value < 0.9 + 1e-8\n\n        aug = iaa.AffineCv2(translate_px=iap.DiscreteUniform(1, 10))\n        assert is_parameter_instance(aug.translate, iap.DiscreteUniform)\n        assert is_parameter_instance(aug.translate.a, iap.Deterministic)\n        assert is_parameter_instance(aug.translate.b, iap.Deterministic)\n        assert aug.translate.a.value == 1\n        assert aug.translate.b.value == 10\n\n        # ---------------------\n        # translate heatmaps\n        # ---------------------\n        heatmaps = HeatmapsOnImage(\n            np.float32([\n                [0.0, 0.5, 0.75],\n                [0.0, 0.5, 0.75],\n                [0.75, 0.75, 0.75],\n            ]),\n            shape=(3, 3, 3)\n        )\n        arr_expected_1px_right = np.float32([\n            [0.0, 0.0, 0.5],\n            [0.0, 0.0, 0.5],\n            [0.0, 0.75, 0.75],\n        ])\n        aug = iaa.AffineCv2(translate_px={\"x\": 1})\n        observed = aug.augment_heatmaps([heatmaps])[0]\n        assert observed.shape == heatmaps.shape\n        assert np.isclose(observed.min_value, heatmaps.min_value,\n                          rtol=0, atol=1e-6)\n        assert np.isclose(observed.max_value, heatmaps.max_value,\n                          rtol=0, atol=1e-6)\n        assert np.array_equal(observed.get_arr(), arr_expected_1px_right)\n\n        # should still use mode=constant cval=0 even when other settings chosen\n        aug = iaa.AffineCv2(translate_px={\"x\": 1}, cval=255)\n        observed = aug.augment_heatmaps([heatmaps])[0]\n        assert observed.shape == heatmaps.shape\n        assert np.isclose(observed.min_value, heatmaps.min_value,\n                          rtol=0, atol=1e-6)\n        assert np.isclose(observed.max_value, heatmaps.max_value,\n                          rtol=0, atol=1e-6)\n        assert np.array_equal(observed.get_arr(), arr_expected_1px_right)\n\n        aug = iaa.AffineCv2(translate_px={\"x\": 1}, mode=\"replicate\", cval=255)\n        observed = aug.augment_heatmaps([heatmaps])[0]\n        assert observed.shape == heatmaps.shape\n        assert np.isclose(observed.min_value, heatmaps.min_value,\n                          rtol=0, atol=1e-6)\n        assert np.isclose(observed.max_value, heatmaps.max_value,\n                          rtol=0, atol=1e-6)\n        assert np.array_equal(observed.get_arr(), arr_expected_1px_right)\n\n        # ---------------------\n        # translate segmaps\n        # ---------------------\n        segmaps = SegmentationMapsOnImage(\n            np.int32([\n                [0, 1, 2],\n                [0, 1, 2],\n                [2, 2, 2],\n            ]),\n            shape=(3, 3, 3)\n        )\n        arr_expected_1px_right = np.int32([\n            [0, 0, 1],\n            [0, 0, 1],\n            [0, 2, 2],\n        ])\n        aug = iaa.AffineCv2(translate_px={\"x\": 1})\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n        assert observed.shape == segmaps.shape\n        assert np.array_equal(observed.get_arr(), arr_expected_1px_right)\n\n        # should still use mode=constant cval=0 even when other settings chosen\n        aug = iaa.AffineCv2(translate_px={\"x\": 1}, cval=255)\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n        assert observed.shape == segmaps.shape\n        assert np.array_equal(observed.get_arr(), arr_expected_1px_right)\n\n        aug = iaa.AffineCv2(translate_px={\"x\": 1}, mode=\"replicate\", cval=255)\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n        assert observed.shape == segmaps.shape\n        assert np.array_equal(observed.get_arr(), arr_expected_1px_right)\n\n        # ---------------------\n        # rotate\n        # ---------------------\n        # rotate by 45 degrees\n        aug = iaa.AffineCv2(scale=1.0, translate_px=0, rotate=90, shear=0)\n        aug_det = aug.to_deterministic()\n\n        image = np.zeros((3, 3, 1), dtype=np.uint8)\n        image_aug = np.copy(image)\n        image[1, :] = 255\n        image_aug[0, 1] = 255\n        image_aug[1, 1] = 255\n        image_aug[2, 1] = 255\n        images = np.array([image])\n        images_aug = np.array([image_aug])\n        images_list = [image]\n        images_aug_list = [image_aug]\n        kps = [ia.Keypoint(x=0, y=1), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=1)]\n        keypoints = [ia.KeypointsOnImage(kps, shape=base_img.shape)]\n        kps_aug = [ia.Keypoint(x=1, y=0), ia.Keypoint(x=1, y=1),\n                   ia.Keypoint(x=1, y=2)]\n        keypoints_aug = [ia.KeypointsOnImage(kps_aug, shape=base_img.shape)]\n\n        observed = aug.augment_images(images)\n        observed[observed >= 100] = 255\n        observed[observed < 100] = 0\n        assert np.array_equal(observed, images_aug)\n\n        observed = aug_det.augment_images(images)\n        observed[observed >= 100] = 255\n        observed[observed < 100] = 0\n        assert np.array_equal(observed, images_aug)\n\n        observed = aug.augment_images(images_list)\n        observed[0][observed[0] >= 100] = 255\n        observed[0][observed[0] < 100] = 0\n        assert array_equal_lists(observed, images_aug_list)\n\n        observed = aug_det.augment_images(images_list)\n        observed[0][observed[0] >= 100] = 255\n        observed[0][observed[0] < 100] = 0\n        assert array_equal_lists(observed, images_aug_list)\n\n        observed = aug.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        observed = aug_det.augment_keypoints(keypoints)\n        assert keypoints_equal(observed, keypoints_aug)\n\n        # rotate by StochasticParameter\n        aug = iaa.AffineCv2(scale=1.0, translate_px=0,\n                            rotate=iap.Uniform(10, 20), shear=0)\n        assert is_parameter_instance(aug.rotate, iap.Uniform)\n        assert is_parameter_instance(aug.rotate.a, iap.Deterministic)\n        assert aug.rotate.a.value == 10\n        assert is_parameter_instance(aug.rotate.b, iap.Deterministic)\n        assert aug.rotate.b.value == 20\n\n        # random rotation 0-364 degrees\n        aug = iaa.AffineCv2(scale=1.0, translate_px=0, rotate=(0, 364),\n                            shear=0)\n        aug_det = aug.to_deterministic()\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        pixels_sums_aug = np.copy(image).astype(np.int32) * 0\n        pixels_sums_aug_det = np.copy(image).astype(np.int32) * 0\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n\n            pixels_sums_aug += (observed_aug[0] > 100)\n            pixels_sums_aug_det += (observed_aug_det[0] > 100)\n\n        assert nb_changed_aug >= int(nb_iterations * 0.9)\n        assert nb_changed_aug_det == 0\n        # center pixel, should always be white when rotating line around center\n        assert pixels_sums_aug[1, 1] > (nb_iterations * 0.98)\n        assert pixels_sums_aug[1, 1] < (nb_iterations * 1.02)\n\n        # outer pixels, should sometimes be white\n        # the values here had to be set quite tolerant, the middle pixels at\n        # top/left/bottom/right get more activation than expected\n        outer_pixels = ([0, 0, 0, 1, 1, 2, 2, 2], [0, 1, 2, 0, 2, 0, 1, 2])\n        assert (\n            pixels_sums_aug[outer_pixels] > int(nb_iterations * (2/8 * 0.4))\n        ).all()\n        assert (\n            pixels_sums_aug[outer_pixels] < int(nb_iterations * (2/8 * 2.0))\n        ).all()\n\n        # ---------------------\n        # shear\n        # ---------------------\n        # TODO\n\n        # shear by StochasticParameter\n        aug = iaa.AffineCv2(scale=1.0, translate_px=0, rotate=0,\n                            shear=iap.Uniform(10, 20))\n        assert is_parameter_instance(aug.shear, iap.Uniform)\n        assert is_parameter_instance(aug.shear.a, iap.Deterministic)\n        assert aug.shear.a.value == 10\n        assert is_parameter_instance(aug.shear.b, iap.Deterministic)\n        assert aug.shear.b.value == 20\n\n        # ---------------------\n        # cval\n        # ---------------------\n        aug = iaa.AffineCv2(scale=1.0, translate_px=100, rotate=0, shear=0,\n                            cval=128)\n        aug_det = aug.to_deterministic()\n\n        image = np.ones((3, 3, 1), dtype=np.uint8) * 255\n        image_aug = np.copy(image)\n        images = np.array([image])\n        images_list = [image]\n\n        observed = aug.augment_images(images)\n        assert (observed[0] > 128 - 30).all()\n        assert (observed[0] < 128 + 30).all()\n\n        observed = aug_det.augment_images(images)\n        assert (observed[0] > 128 - 30).all()\n        assert (observed[0] < 128 + 30).all()\n\n        observed = aug.augment_images(images_list)\n        assert (observed[0] > 128 - 30).all()\n        assert (observed[0] < 128 + 30).all()\n\n        observed = aug_det.augment_images(images_list)\n        assert (observed[0] > 128 - 30).all()\n        assert (observed[0] < 128 + 30).all()\n\n        # random cvals\n        aug = iaa.AffineCv2(scale=1.0, translate_px=100, rotate=0, shear=0,\n                            cval=(0, 255))\n        aug_det = aug.to_deterministic()\n        last_aug = None\n        last_aug_det = None\n        nb_changed_aug = 0\n        nb_changed_aug_det = 0\n        nb_iterations = 1000\n        averages = []\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(images)\n            observed_aug_det = aug_det.augment_images(images)\n            if i == 0:\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug = observed_aug\n                last_aug_det = observed_aug_det\n\n            averages.append(int(np.average(observed_aug)))\n\n        assert nb_changed_aug >= int(nb_iterations * 0.9)\n        assert nb_changed_aug_det == 0\n        # center pixel, should always be white when rotating line around center\n        assert pixels_sums_aug[1, 1] > (nb_iterations * 0.98)\n        assert pixels_sums_aug[1, 1] < (nb_iterations * 1.02)\n        assert len(set(averages)) > 200\n\n        aug = iaa.AffineCv2(scale=1.0, translate_px=100, rotate=0, shear=0,\n                            cval=ia.ALL)\n        assert is_parameter_instance(aug.cval, iap.DiscreteUniform)\n        assert is_parameter_instance(aug.cval.a, iap.Deterministic)\n        assert is_parameter_instance(aug.cval.b, iap.Deterministic)\n        assert aug.cval.a.value == 0\n        assert aug.cval.b.value == 255\n\n        aug = iaa.AffineCv2(scale=1.0, translate_px=100, rotate=0, shear=0,\n                            cval=iap.DiscreteUniform(1, 5))\n        assert is_parameter_instance(aug.cval, iap.DiscreteUniform)\n        assert is_parameter_instance(aug.cval.a, iap.Deterministic)\n        assert is_parameter_instance(aug.cval.b, iap.Deterministic)\n        assert aug.cval.a.value == 1\n        assert aug.cval.b.value == 5\n\n        # ------------\n        # mode\n        # ------------\n        aug = iaa.AffineCv2(scale=1.0, translate_px=100, rotate=0, shear=0,\n                            cval=0, mode=ia.ALL)\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        aug = iaa.AffineCv2(scale=1.0, translate_px=100, rotate=0, shear=0,\n                            cval=0, mode=\"replicate\")\n        assert is_parameter_instance(aug.mode, iap.Deterministic)\n        assert aug.mode.value == \"replicate\"\n        aug = iaa.AffineCv2(scale=1.0, translate_px=100, rotate=0, shear=0,\n                            cval=0, mode=[\"replicate\", \"reflect\"])\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert (\n            len(aug.mode.a) == 2\n            and \"replicate\" in aug.mode.a\n            and \"reflect\" in aug.mode.a)\n        aug = iaa.AffineCv2(scale=1.0, translate_px=100, rotate=0, shear=0,\n                            cval=0,\n                            mode=iap.Choice([\"replicate\", \"reflect\"]))\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert (\n            len(aug.mode.a) == 2\n            and \"replicate\" in aug.mode.a\n            and \"reflect\" in aug.mode.a)\n\n        # ------------\n        # exceptions for bad inputs\n        # ------------\n        # scale\n        got_exception = False\n        try:\n            _ = iaa.AffineCv2(scale=False)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        # translate_px\n        got_exception = False\n        try:\n            _ = iaa.AffineCv2(translate_px=False)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        # translate_percent\n        got_exception = False\n        try:\n            _ = iaa.AffineCv2(translate_percent=False)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        # rotate\n        got_exception = False\n        try:\n            _ = iaa.AffineCv2(scale=1.0, translate_px=0, rotate=False,\n                              shear=0, cval=0)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        # shear\n        got_exception = False\n        try:\n            _ = iaa.AffineCv2(scale=1.0, translate_px=0, rotate=0,\n                              shear=False, cval=0)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        # cval\n        got_exception = False\n        try:\n            _ = iaa.AffineCv2(scale=1.0, translate_px=100, rotate=0,\n                              shear=0, cval=None)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        # mode\n        got_exception = False\n        try:\n            _ = iaa.AffineCv2(scale=1.0, translate_px=100, rotate=0,\n                              shear=0, cval=0, mode=False)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        # non-existent order\n        got_exception = False\n        try:\n            _ = iaa.AffineCv2(order=-1)\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        # bad order datatype\n        got_exception = False\n        try:\n            _ = iaa.AffineCv2(order=\"test\")\n        except Exception:\n            got_exception = True\n        assert got_exception\n\n        # ----------\n        # get_parameters\n        # ----------\n        aug = iaa.AffineCv2(scale=1, translate_px=2, rotate=3, shear=4,\n                            order=1, cval=0, mode=\"constant\")\n        params = aug.get_parameters()\n        assert is_parameter_instance(params[0], iap.Deterministic)  # scale\n        assert is_parameter_instance(params[1], iap.Deterministic)  # translate\n        assert is_parameter_instance(params[2], iap.Deterministic)  # rotate\n        assert is_parameter_instance(params[3], iap.Deterministic)  # shear\n        assert params[0].value == 1  # scale\n        assert params[1].value == 2  # translate\n        assert params[2].value == 3  # rotate\n        assert params[3].value == 4  # shear\n        assert params[4].value == 1  # order\n        assert params[5].value == 0  # cval\n        assert params[6].value == \"constant\"  # mode\n\n\nclass TestPiecewiseAffine(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        img = np.zeros((60, 80), dtype=np.uint8)\n        img[:, 9:11+1] = 255\n        img[:, 69:71+1] = 255\n        return img\n\n    @property\n    def mask(self):\n        return self.image > 0\n\n    @property\n    def heatmaps(self):\n        return HeatmapsOnImage((self.image / 255.0).astype(np.float32),\n                               shape=(60, 80, 3))\n\n    @property\n    def segmaps(self):\n        return SegmentationMapsOnImage(self.mask.astype(np.int32),\n                                       shape=(60, 80, 3))\n\n    # -----\n    # __init__\n    # -----\n    def test___init___scale_is_list(self):\n        # scale as list\n        aug = iaa.PiecewiseAffine(scale=[0.01, 0.10], nb_rows=12, nb_cols=4)\n        assert is_parameter_instance(aug.scale, iap.Choice)\n        assert 0.01 - 1e-8 < aug.scale.a[0] < 0.01 + 1e-8\n        assert 0.10 - 1e-8 < aug.scale.a[1] < 0.10 + 1e-8\n\n    def test___init___scale_is_tuple(self):\n        # scale as tuple\n        aug = iaa.PiecewiseAffine(scale=(0.01, 0.10), nb_rows=12, nb_cols=4)\n        assert is_parameter_instance(aug.jitter.scale, iap.Uniform)\n        assert is_parameter_instance(aug.jitter.scale.a, iap.Deterministic)\n        assert is_parameter_instance(aug.jitter.scale.b, iap.Deterministic)\n        assert 0.01 - 1e-8 < aug.jitter.scale.a.value < 0.01 + 1e-8\n        assert 0.10 - 1e-8 < aug.jitter.scale.b.value < 0.10 + 1e-8\n\n    def test___init___scale_is_stochastic_parameter(self):\n        # scale as StochasticParameter\n        aug = iaa.PiecewiseAffine(scale=iap.Uniform(0.01, 0.10), nb_rows=12,\n                                  nb_cols=4)\n        assert is_parameter_instance(aug.jitter.scale, iap.Uniform)\n        assert is_parameter_instance(aug.jitter.scale.a, iap.Deterministic)\n        assert is_parameter_instance(aug.jitter.scale.b, iap.Deterministic)\n        assert 0.01 - 1e-8 < aug.jitter.scale.a.value < 0.01 + 1e-8\n        assert 0.10 - 1e-8 < aug.jitter.scale.b.value < 0.10 + 1e-8\n\n    def test___init___bad_datatype_for_scale_leads_to_failure(self):\n        # bad datatype for scale\n        got_exception = False\n        try:\n            _ = iaa.PiecewiseAffine(scale=False, nb_rows=12, nb_cols=4)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test___init___nb_rows_is_list(self):\n        # rows as list\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=[4, 20], nb_cols=4)\n        assert is_parameter_instance(aug.nb_rows, iap.Choice)\n        assert aug.nb_rows.a[0] == 4\n        assert aug.nb_rows.a[1] == 20\n\n    def test___init___nb_rows_is_tuple(self):\n        # rows as tuple\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=(4, 20), nb_cols=4)\n        assert is_parameter_instance(aug.nb_rows, iap.DiscreteUniform)\n        assert is_parameter_instance(aug.nb_rows.a, iap.Deterministic)\n        assert is_parameter_instance(aug.nb_rows.b, iap.Deterministic)\n        assert aug.nb_rows.a.value == 4\n        assert aug.nb_rows.b.value == 20\n\n    def test___init___nb_rows_is_stochastic_parameter(self):\n        # rows as StochasticParameter\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=iap.DiscreteUniform(4, 20),\n                                  nb_cols=4)\n        assert is_parameter_instance(aug.nb_rows, iap.DiscreteUniform)\n        assert is_parameter_instance(aug.nb_rows.a, iap.Deterministic)\n        assert is_parameter_instance(aug.nb_rows.b, iap.Deterministic)\n        assert aug.nb_rows.a.value == 4\n        assert aug.nb_rows.b.value == 20\n\n    def test___init___bad_datatype_for_nb_rows_leads_to_failure(self):\n        # bad datatype for rows\n        got_exception = False\n        try:\n            _ = iaa.PiecewiseAffine(scale=0.05, nb_rows=False, nb_cols=4)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test___init___nb_cols_is_list(self):\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=4, nb_cols=[4, 20])\n        assert is_parameter_instance(aug.nb_cols, iap.Choice)\n        assert aug.nb_cols.a[0] == 4\n        assert aug.nb_cols.a[1] == 20\n\n    def test___init___nb_cols_is_tuple(self):\n        # cols as tuple\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=4, nb_cols=(4, 20))\n        assert is_parameter_instance(aug.nb_cols, iap.DiscreteUniform)\n        assert is_parameter_instance(aug.nb_cols.a, iap.Deterministic)\n        assert is_parameter_instance(aug.nb_cols.b, iap.Deterministic)\n        assert aug.nb_cols.a.value == 4\n        assert aug.nb_cols.b.value == 20\n\n    def test___init___nb_cols_is_stochastic_parameter(self):\n        # cols as StochasticParameter\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=4,\n                                  nb_cols=iap.DiscreteUniform(4, 20))\n        assert is_parameter_instance(aug.nb_cols, iap.DiscreteUniform)\n        assert is_parameter_instance(aug.nb_cols.a, iap.Deterministic)\n        assert is_parameter_instance(aug.nb_cols.b, iap.Deterministic)\n        assert aug.nb_cols.a.value == 4\n        assert aug.nb_cols.b.value == 20\n\n    def test___init___bad_datatype_for_nb_cols_leads_to_failure(self):\n        # bad datatype for cols\n        got_exception = False\n        try:\n            _aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=4, nb_cols=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test___init___order_is_int(self):\n        # single int for order\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8, order=0)\n        assert is_parameter_instance(aug.order, iap.Deterministic)\n        assert aug.order.value == 0\n\n    def test___init___order_is_list(self):\n        # list for order\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8,\n                                  order=[0, 1, 3])\n        assert is_parameter_instance(aug.order, iap.Choice)\n        assert all([v in aug.order.a for v in [0, 1, 3]])\n\n    def test___init___order_is_stochastic_parameter(self):\n        # StochasticParameter for order\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8,\n                                  order=iap.Choice([0, 1, 3]))\n        assert is_parameter_instance(aug.order, iap.Choice)\n        assert all([v in aug.order.a for v in [0, 1, 3]])\n\n    def test___init___order_is_all(self):\n        # ALL for order\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8,\n                                  order=ia.ALL)\n        assert is_parameter_instance(aug.order, iap.Choice)\n        assert all([v in aug.order.a for v in [0, 1, 3, 4, 5]])\n\n    def test___init___bad_datatype_for_order_leads_to_failure(self):\n        # bad datatype for order\n        got_exception = False\n        try:\n            _ = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8,\n                                    order=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test___init___cval_is_list(self):\n        # cval as list\n        aug = iaa.PiecewiseAffine(scale=0.7, nb_rows=5, nb_cols=5,\n                                  mode=\"constant\", cval=[0, 10])\n        assert is_parameter_instance(aug.cval, iap.Choice)\n        assert aug.cval.a[0] == 0\n        assert aug.cval.a[1] == 10\n\n    def test___init___cval_is_tuple(self):\n        # cval as tuple\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8,\n                                  mode=\"constant\", cval=(0, 10))\n        assert is_parameter_instance(aug.cval, iap.Uniform)\n        assert is_parameter_instance(aug.cval.a, iap.Deterministic)\n        assert is_parameter_instance(aug.cval.b, iap.Deterministic)\n        assert aug.cval.a.value == 0\n        assert aug.cval.b.value == 10\n\n    def test___init___cval_is_stochastic_parameter(self):\n        # cval as StochasticParameter\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8,\n                                  mode=\"constant\",\n                                  cval=iap.DiscreteUniform(0, 10))\n        assert is_parameter_instance(aug.cval, iap.DiscreteUniform)\n        assert is_parameter_instance(aug.cval.a, iap.Deterministic)\n        assert is_parameter_instance(aug.cval.b, iap.Deterministic)\n        assert aug.cval.a.value == 0\n        assert aug.cval.b.value == 10\n\n    def test___init___cval_is_all(self):\n        # ALL as cval\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8,\n                                  mode=\"constant\", cval=ia.ALL)\n        assert is_parameter_instance(aug.cval, iap.Uniform)\n        assert is_parameter_instance(aug.cval.a, iap.Deterministic)\n        assert is_parameter_instance(aug.cval.b, iap.Deterministic)\n        assert aug.cval.a.value == 0\n        assert aug.cval.b.value == 255\n\n    def test___init___bad_datatype_for_cval_leads_to_failure(self):\n        # bas datatype for cval\n        got_exception = False\n        try:\n            _ = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8, cval=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test___init___mode_is_string(self):\n        # single string for mode\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8,\n                                  mode=\"nearest\")\n        assert is_parameter_instance(aug.mode, iap.Deterministic)\n        assert aug.mode.value == \"nearest\"\n\n    def test___init___mode_is_list(self):\n        # list for mode\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8,\n                                  mode=[\"nearest\", \"edge\", \"symmetric\"])\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert all([\n            v in aug.mode.a for v in [\"nearest\", \"edge\", \"symmetric\"]\n        ])\n\n    def test___init___mode_is_stochastic_parameter(self):\n        # StochasticParameter for mode\n        aug = iaa.PiecewiseAffine(\n            scale=0.1, nb_rows=8, nb_cols=8,\n            mode=iap.Choice([\"nearest\", \"edge\", \"symmetric\"]))\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert all([\n            v in aug.mode.a for v in [\"nearest\", \"edge\", \"symmetric\"]\n        ])\n\n    def test___init___mode_is_all(self):\n        # ALL for mode\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8, mode=ia.ALL)\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert all([\n            v in aug.mode.a\n            for v\n            in [\"constant\", \"edge\", \"symmetric\", \"reflect\", \"wrap\"]\n        ])\n\n    def test___init___bad_datatype_for_mode_leads_to_failure(self):\n        # bad datatype for mode\n        got_exception = False\n        try:\n            _ = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=8,\n                                    mode=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    # -----\n    # scale\n    # -----\n    def test_scale_is_small_image(self):\n        # basic test\n        aug = iaa.PiecewiseAffine(scale=0.01, nb_rows=12, nb_cols=4)\n\n        observed = aug.augment_image(self.image)\n\n        assert (\n            100.0\n            < np.average(observed[self.mask])\n            < np.average(self.image[self.mask])\n        )\n        assert (\n            100.0-75.0\n            > np.average(observed[~self.mask])\n            > np.average(self.image[~self.mask])\n        )\n\n    def test_scale_is_small_image_absolute_scale(self):\n        aug = iaa.PiecewiseAffine(scale=1, nb_rows=12, nb_cols=4,\n                                  absolute_scale=True)\n\n        observed = aug.augment_image(self.image)\n\n        assert (\n            100.0\n            < np.average(observed[self.mask])\n            < np.average(self.image[self.mask])\n        )\n        assert (\n            100.0-75.0\n            > np.average(observed[~self.mask])\n            > np.average(self.image[~self.mask])\n        )\n\n    def test_scale_is_small_heatmaps(self):\n        # basic test, heatmaps\n        aug = iaa.PiecewiseAffine(scale=0.01, nb_rows=12, nb_cols=4)\n\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n\n        observed_arr = observed.get_arr()\n        assert observed.shape == self.heatmaps.shape\n        _assert_same_min_max(observed, self.heatmaps)\n        assert (\n            100.0/255.0\n            < np.average(observed_arr[self.mask])\n            < np.average(self.heatmaps.get_arr()[self.mask]))\n        assert (\n            (100.0-75.0)/255.0\n            > np.average(observed_arr[~self.mask])\n            > np.average(self.heatmaps.get_arr()[~self.mask]))\n\n    def test_scale_is_small_segmaps(self):\n        # basic test, segmaps\n        aug = iaa.PiecewiseAffine(scale=0.001, nb_rows=12, nb_cols=4)\n\n        observed = aug.augment_segmentation_maps([self.segmaps])[0]\n\n        observed_arr = observed.get_arr()\n        # left column starts at 9-11 and right one at 69-71\n        # result is 9-11 (curvy, i.e. like 50% filled) and 70-71 (straight,\n        # i.e. 100% filled). Reason for that is unclear, maybe a scikit-image\n        # problem.\n        observed_arr_left_col = observed_arr[:, 9:11+1]\n        observed_arr_right_col = observed_arr[:, 69:71+1]\n        assert observed.shape == self.segmaps.shape\n        assert np.average(observed_arr_left_col == 1) > 0.5\n        assert np.average(observed_arr_right_col == 1) > 0.5\n        assert np.average(observed_arr[~self.mask] == 0) > 0.9\n\n    def test_scale_is_zero_image(self):\n        # scale 0\n        aug = iaa.PiecewiseAffine(scale=0, nb_rows=12, nb_cols=4)\n\n        observed = aug.augment_image(self.image)\n\n        assert np.array_equal(observed, self.image)\n\n    def test_scale_is_zero_image_absolute_scale(self):\n        aug = iaa.PiecewiseAffine(scale=0, nb_rows=12, nb_cols=4,\n                                  absolute_scale=True)\n\n        observed = aug.augment_image(self.image)\n\n        assert np.array_equal(observed, self.image)\n\n    def test_scale_is_zero_heatmaps(self):\n        # scale 0, heatmaps\n        aug = iaa.PiecewiseAffine(scale=0, nb_rows=12, nb_cols=4)\n\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n\n        observed_arr = observed.get_arr()\n        assert observed.shape == self.heatmaps.shape\n        _assert_same_min_max(observed, self.heatmaps)\n        assert np.array_equal(observed_arr, self.heatmaps.get_arr())\n\n    def test_scale_is_zero_segmaps(self):\n        # scale 0, segmaps\n        aug = iaa.PiecewiseAffine(scale=0, nb_rows=12, nb_cols=4)\n\n        observed = aug.augment_segmentation_maps([self.segmaps])[0]\n\n        observed_arr = observed.get_arr()\n        assert observed.shape == self.segmaps.shape\n        assert np.array_equal(observed_arr, self.segmaps.get_arr())\n\n    def test_scale_is_zero_keypoints(self):\n        # scale 0, keypoints\n        aug = iaa.PiecewiseAffine(scale=0, nb_rows=12, nb_cols=4)\n        kps = [ia.Keypoint(x=5, y=3), ia.Keypoint(x=3, y=8)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(14, 14, 3))\n\n        kpsoi_aug = aug.augment_keypoints([kpsoi])[0]\n\n        assert_cbaois_equal(kpsoi_aug, kpsoi)\n\n    @classmethod\n    def _test_scale_is_zero_cbaoi(cls, cbaoi, augf_name):\n        aug = iaa.PiecewiseAffine(scale=0, nb_rows=10, nb_cols=10)\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(observed, cbaoi)\n\n    def test_scale_is_zero_polygons(self):\n        exterior = [(10, 10),\n                    (70, 10), (70, 20), (70, 30), (70, 40),\n                    (70, 50), (70, 60), (70, 70), (70, 80),\n                    (70, 90),\n                    (10, 90),\n                    (10, 80), (10, 70), (10, 60), (10, 50),\n                    (10, 40), (10, 30), (10, 20), (10, 10)]\n        poly = ia.Polygon(exterior)\n        psoi = ia.PolygonsOnImage([poly, poly.shift(x=1, y=1)],\n                                  shape=(100, 80))\n\n        self._test_scale_is_zero_cbaoi(psoi, \"augment_polygons\")\n\n    def test_scale_is_zero_line_strings(self):\n        coords = [(10, 10),\n                  (70, 10), (70, 20), (70, 30), (70, 40),\n                  (70, 50), (70, 60), (70, 70), (70, 80),\n                  (70, 90),\n                  (10, 90),\n                  (10, 80), (10, 70), (10, 60), (10, 50),\n                  (10, 40), (10, 30), (10, 20), (10, 10)]\n        ls = ia.LineString(coords)\n        lsoi = ia.LineStringsOnImage([ls, ls.shift(x=1, y=1)],\n                                     shape=(100, 80))\n\n        self._test_scale_is_zero_cbaoi(lsoi, \"augment_line_strings\")\n\n    def test_scale_is_zero_bounding_boxes(self):\n        bb = ia.BoundingBox(x1=10, y1=10, x2=70, y2=20)\n        bbsoi = ia.BoundingBoxesOnImage([bb, bb.shift(x=1, y=1)],\n                                        shape=(100, 80))\n\n        self._test_scale_is_zero_cbaoi(bbsoi, \"augment_bounding_boxes\")\n\n    def test_scale_stronger_values_should_increase_changes_images(self):\n        # stronger scale should lead to stronger changes\n        aug1 = iaa.PiecewiseAffine(scale=0.01, nb_rows=12, nb_cols=4)\n        aug2 = iaa.PiecewiseAffine(scale=0.10, nb_rows=12, nb_cols=4)\n\n        observed1 = aug1.augment_image(self.image)\n        observed2 = aug2.augment_image(self.image)\n\n        assert (\n            np.average(observed1[~self.mask])\n            < np.average(observed2[~self.mask])\n        )\n\n    def test_scale_stronger_values_should_increase_changes_images_abs(self):\n        aug1 = iaa.PiecewiseAffine(scale=1, nb_rows=12, nb_cols=4,\n                                   absolute_scale=True)\n        aug2 = iaa.PiecewiseAffine(scale=10, nb_rows=12, nb_cols=4,\n                                   absolute_scale=True)\n\n        observed1 = aug1.augment_image(self.image)\n        observed2 = aug2.augment_image(self.image)\n\n        assert (\n            np.average(observed1[~self.mask])\n            < np.average(observed2[~self.mask])\n        )\n\n    def test_scale_stronger_values_should_increase_changes_heatmaps(self):\n        # stronger scale should lead to stronger changes, heatmaps\n        aug1 = iaa.PiecewiseAffine(scale=0.01, nb_rows=12, nb_cols=4)\n        aug2 = iaa.PiecewiseAffine(scale=0.10, nb_rows=12, nb_cols=4)\n        \n        observed1 = aug1.augment_heatmaps([self.heatmaps])[0]\n        observed2 = aug2.augment_heatmaps([self.heatmaps])[0]\n        \n        observed1_arr = observed1.get_arr()\n        observed2_arr = observed2.get_arr()\n        assert observed1.shape == self.heatmaps.shape\n        assert observed2.shape == self.heatmaps.shape\n        _assert_same_min_max(observed1, self.heatmaps)\n        _assert_same_min_max(observed2, self.heatmaps)\n        assert (\n            np.average(observed1_arr[~self.mask])\n            < np.average(observed2_arr[~self.mask])\n        )\n\n    def test_scale_stronger_values_should_increase_changes_heatmaps_abs(self):\n        aug1 = iaa.PiecewiseAffine(scale=1, nb_rows=12, nb_cols=4,\n                                   absolute_scale=True)\n        aug2 = iaa.PiecewiseAffine(scale=10, nb_rows=12, nb_cols=4,\n                                   absolute_scale=True)\n\n        observed1 = aug1.augment_heatmaps([self.heatmaps])[0]\n        observed2 = aug2.augment_heatmaps([self.heatmaps])[0]\n\n        observed1_arr = observed1.get_arr()\n        observed2_arr = observed2.get_arr()\n        assert observed1.shape == self.heatmaps.shape\n        assert observed2.shape == self.heatmaps.shape\n        _assert_same_min_max(observed1, self.heatmaps)\n        _assert_same_min_max(observed2, self.heatmaps)\n        assert (\n            np.average(observed1_arr[~self.mask])\n            < np.average(observed2_arr[~self.mask])\n        )\n\n    def test_scale_stronger_values_should_increase_changes_segmaps(self):\n        # stronger scale should lead to stronger changes, segmaps\n        aug1 = iaa.PiecewiseAffine(scale=0.01, nb_rows=12, nb_cols=4)\n        aug2 = iaa.PiecewiseAffine(scale=0.10, nb_rows=12, nb_cols=4)\n\n        observed1 = aug1.augment_segmentation_maps([self.segmaps])[0]\n        observed2 = aug2.augment_segmentation_maps([self.segmaps])[0]\n\n        observed1_arr = observed1.get_arr()\n        observed2_arr = observed2.get_arr()\n        assert observed1.shape == self.segmaps.shape\n        assert observed2.shape == self.segmaps.shape\n        assert (\n            np.average(observed1_arr[~self.mask] == 0)\n            > np.average(observed2_arr[~self.mask] == 0)\n        )\n\n    def test_scale_alignment_between_images_and_heatmaps(self):\n        # strong scale, measure alignment between images and heatmaps\n        aug = iaa.PiecewiseAffine(scale=0.10, nb_rows=12, nb_cols=4)\n        aug_det = aug.to_deterministic()\n\n        img_aug = aug_det.augment_image(self.image)\n        hm_aug = aug_det.augment_heatmaps([self.heatmaps])[0]\n\n        img_aug_mask = img_aug > 255*0.1\n        hm_aug_mask = hm_aug.arr_0to1 > 0.1\n        same = np.sum(img_aug_mask == hm_aug_mask[:, :, 0])\n        assert hm_aug.shape == (60, 80, 3)\n        _assert_same_min_max(hm_aug, self.heatmaps)\n        assert (same / img_aug_mask.size) >= 0.98\n\n    def test_scale_alignment_between_images_and_segmaps(self):\n        # strong scale, measure alignment between images and segmaps\n        aug = iaa.PiecewiseAffine(scale=0.10, nb_rows=12, nb_cols=4)\n        aug_det = aug.to_deterministic()\n\n        img_aug = aug_det.augment_image(self.image)\n        segmap_aug = aug_det.augment_segmentation_maps([self.segmaps])[0]\n\n        img_aug_mask = (img_aug > 255*0.1)\n        segmap_aug_mask = (segmap_aug.arr == 1)\n        same = np.sum(img_aug_mask == segmap_aug_mask[:, :, 0])\n        assert segmap_aug.shape == (60, 80, 3)\n        assert (same / img_aug_mask.size) >= 0.9\n\n    def test_scale_alignment_between_images_and_smaller_heatmaps(self):\n        # strong scale, measure alignment between images and heatmaps\n        # heatmaps here smaller than image\n        aug = iaa.PiecewiseAffine(scale=0.10, nb_rows=12, nb_cols=4)\n        aug_det = aug.to_deterministic()\n\n        heatmaps_small = ia.HeatmapsOnImage(\n            (\n                ia.imresize_single_image(\n                    self.image, (30, 40+10), interpolation=\"cubic\"\n                ) / 255.0\n            ).astype(np.float32),\n            shape=(60, 80, 3)\n        )\n\n        img_aug = aug_det.augment_image(self.image)\n        hm_aug = aug_det.augment_heatmaps([heatmaps_small])[0]\n\n        img_aug_mask = img_aug > 255*0.1\n        hm_aug_mask = ia.imresize_single_image(\n            hm_aug.arr_0to1, (60, 80), interpolation=\"cubic\"\n        ) > 0.1\n        same = np.sum(img_aug_mask == hm_aug_mask[:, :, 0])\n        assert hm_aug.shape == (60, 80, 3)\n        assert hm_aug.arr_0to1.shape == (30, 40+10, 1)\n        assert (same / img_aug_mask.size) >= 0.9  # seems to be 0.948 actually\n\n    def test_scale_alignment_between_images_and_smaller_heatmaps_abs(self):\n        # image is 60x80, so a scale of 8 is about 0.1*max(60,80)\n        aug = iaa.PiecewiseAffine(scale=8, nb_rows=12, nb_cols=4,\n                                  absolute_scale=True)\n        aug_det = aug.to_deterministic()\n\n        heatmaps_small = ia.HeatmapsOnImage(\n            (\n                ia.imresize_single_image(\n                    self.image, (30, 40+10), interpolation=\"cubic\"\n                ) / 255.0\n            ).astype(np.float32),\n            shape=(60, 80, 3)\n        )\n\n        img_aug = aug_det.augment_image(self.image)\n        hm_aug = aug_det.augment_heatmaps([heatmaps_small])[0]\n\n        img_aug_mask = img_aug > 255*0.1\n        hm_aug_mask = ia.imresize_single_image(\n            hm_aug.arr_0to1, (60, 80), interpolation=\"cubic\"\n        ) > 0.1\n        same = np.sum(img_aug_mask == hm_aug_mask[:, :, 0])\n        assert hm_aug.shape == (60, 80, 3)\n        assert hm_aug.arr_0to1.shape == (30, 40+10, 1)\n        assert (same / img_aug_mask.size) >= 0.9  # seems to be 0.930 actually\n\n    def test_scale_alignment_between_images_and_smaller_segmaps(self):\n        # strong scale, measure alignment between images and segmaps\n        # segmaps here smaller than image\n        aug = iaa.PiecewiseAffine(scale=0.10, nb_rows=12, nb_cols=4)\n        aug_det = aug.to_deterministic()\n        segmaps_small = SegmentationMapsOnImage(\n            (\n                ia.imresize_single_image(\n                    self.image, (30, 40+10), interpolation=\"cubic\"\n                ) > 100\n            ).astype(np.int32),\n            shape=(60, 80, 3)\n        )\n\n        img_aug = aug_det.augment_image(self.image)\n        segmaps_aug = aug_det.augment_segmentation_maps([segmaps_small])[0]\n\n        img_aug_mask = img_aug > 255*0.1\n        segmaps_aug_mask = (\n            ia.imresize_single_image(\n                segmaps_aug.arr, (60, 80),\n                interpolation=\"nearest\"\n            ) == 1\n        )\n        same = np.sum(img_aug_mask == segmaps_aug_mask[:, :, 0])\n        assert segmaps_aug.shape == (60, 80, 3)\n        assert segmaps_aug.arr.shape == (30, 40+10, 1)\n        assert (same / img_aug_mask.size) >= 0.9\n\n    def test_scale_alignment_between_images_and_keypoints(self):\n        # strong scale, measure alignment between images and keypoints\n        # fairly large scale here, as otherwise keypoints can end up\n        # outside of the image plane\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=12, nb_cols=4)\n        aug_det = aug.to_deterministic()\n        kps = [ia.Keypoint(x=160, y=110), ia.Keypoint(x=140, y=90)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(200, 300, 3))\n        img_kps = np.zeros((200, 300, 3), dtype=np.uint8)\n        img_kps = kpsoi.draw_on_image(img_kps, color=[255, 255, 255])\n\n        img_kps_aug = aug_det.augment_image(img_kps)\n        kpsoi_aug = aug_det.augment_keypoints([kpsoi])[0]\n\n        assert kpsoi_aug.shape == (200, 300, 3)\n        bb1 = ia.BoundingBox(\n            x1=kpsoi_aug.keypoints[0].x-1, y1=kpsoi_aug.keypoints[0].y-1,\n            x2=kpsoi_aug.keypoints[0].x+1, y2=kpsoi_aug.keypoints[0].y+1)\n        bb2 = ia.BoundingBox(\n            x1=kpsoi_aug.keypoints[1].x-1, y1=kpsoi_aug.keypoints[1].y-1,\n            x2=kpsoi_aug.keypoints[1].x+1, y2=kpsoi_aug.keypoints[1].y+1)\n        patch1 = bb1.extract_from_image(img_kps_aug)\n        patch2 = bb2.extract_from_image(img_kps_aug)\n        assert np.max(patch1) > 150\n        assert np.max(patch2) > 150\n        assert np.average(img_kps_aug) < 40\n\n    # this test was apparently added later on (?) without noticing that\n    # a similar test already existed\n    def test_scale_alignment_between_images_and_keypoints2(self):\n        img = np.zeros((100, 80), dtype=np.uint8)\n        img[:, 9:11+1] = 255\n        img[:, 69:71+1] = 255\n        kps = [ia.Keypoint(x=10, y=20), ia.Keypoint(x=10, y=40),\n               ia.Keypoint(x=70, y=20), ia.Keypoint(x=70, y=40)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=img.shape)\n\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=10, nb_cols=10)\n        aug_det = aug.to_deterministic()\n\n        observed_img = aug_det.augment_image(img)\n        observed_kpsoi = aug_det.augment_keypoints([kpsoi])\n\n        assert not keypoints_equal([kpsoi], observed_kpsoi)\n        for kp in observed_kpsoi[0].keypoints:\n            assert observed_img[int(kp.y), int(kp.x)] > 0\n\n    @classmethod\n    def _test_scale_alignment_between_images_and_poly_or_line_strings(\n            cls, cba_class, cbaoi_class, augf_name):\n        img = np.zeros((100, 80), dtype=np.uint8)\n        img[:, 10-5:10+5] = 255\n        img[:, 70-5:70+5] = 255\n        coords = [(10, 10),\n                  (70, 10), (70, 20), (70, 30), (70, 40),\n                  (70, 50), (70, 60), (70, 70), (70, 80),\n                  (70, 90),\n                  (10, 90),\n                  (10, 80), (10, 70), (10, 60), (10, 50),\n                  (10, 40), (10, 30), (10, 20), (10, 10)]\n        cba = cba_class(coords)\n        cbaoi = cbaoi_class([cba, cba.shift(x=1, y=1)],\n                            shape=img.shape)\n\n        aug = iaa.PiecewiseAffine(scale=0.03, nb_rows=10, nb_cols=10)\n        aug_det = aug.to_deterministic()\n\n        observed_imgs = aug_det.augment_images([img, img])\n        observed_cbaois = getattr(aug_det, augf_name)([cbaoi, cbaoi])\n\n        for observed_img, observed_cbaoi in zip(observed_imgs, observed_cbaois):\n            assert observed_cbaoi.shape == img.shape\n            for cba_aug in observed_cbaoi.items:\n                if hasattr(cba_aug, \"is_valid\"):\n                    assert cba_aug.is_valid\n                for point_aug in cba_aug.coords:\n                    x = int(np.round(point_aug[0]))\n                    y = int(np.round(point_aug[1]))\n                    assert observed_img[y, x] > 0\n\n    def test_scale_alignment_between_images_and_polygons(self):\n        self._test_scale_alignment_between_images_and_poly_or_line_strings(\n            ia.Polygon, ia.PolygonsOnImage, \"augment_polygons\")\n\n    def test_scale_alignment_between_images_and_line_strings(self):\n        self._test_scale_alignment_between_images_and_poly_or_line_strings(\n            ia.LineString, ia.LineStringsOnImage, \"augment_line_strings\")\n\n    def test_scale_alignment_between_images_and_bounding_boxes(self):\n        img = np.zeros((100, 80), dtype=np.uint8)\n        s = 0\n        img[10-s:10+s+1, 20-s:20+s+1] = 255\n        img[60-s:60+s+1, 70-s:70+s+1] = 255\n        bb = ia.BoundingBox(y1=10, x1=20, y2=60, x2=70)\n        bbsoi = ia.BoundingBoxesOnImage([bb], shape=img.shape)\n\n        aug = iaa.PiecewiseAffine(scale=0.03, nb_rows=10, nb_cols=10)\n\n        observed_imgs, observed_bbsois = aug(\n            images=[img], bounding_boxes=[bbsoi])\n\n        for observed_img, observed_bbsoi in zip(observed_imgs, observed_bbsois):\n            assert observed_bbsoi.shape == img.shape\n\n            observed_img_x = np.max(observed_img, axis=0)\n            observed_img_y = np.max(observed_img, axis=1)\n\n            nonz_x = np.nonzero(observed_img_x)[0]\n            nonz_y = np.nonzero(observed_img_y)[0]\n\n            img_x1 = min(nonz_x)\n            img_x2 = max(nonz_x)\n            img_y1 = min(nonz_y)\n            img_y2 = max(nonz_y)\n            expected = ia.BoundingBox(x1=img_x1, y1=img_y1,\n                                      x2=img_x2, y2=img_y2)\n\n            for bb_aug in observed_bbsoi.bounding_boxes:\n                # we don't expect perfect IoU here, because the actual\n                # underlying KP aug used distance maps\n                # most IoUs seem to end up in the range 0.9-0.95\n                assert bb_aug.iou(expected) > 0.8\n\n    def test_scale_is_list(self):\n        aug1 = iaa.PiecewiseAffine(scale=0.01, nb_rows=12, nb_cols=4)\n        aug2 = iaa.PiecewiseAffine(scale=0.10, nb_rows=12, nb_cols=4)\n        aug = iaa.PiecewiseAffine(scale=[0.01, 0.10], nb_rows=12, nb_cols=4)\n\n        avg1 = np.average([\n            np.average(\n                aug1.augment_image(self.image)\n                * (~self.mask).astype(np.float32)\n            )\n            for _ in sm.xrange(3)\n        ])\n        avg2 = np.average([\n            np.average(\n                aug2.augment_image(self.image)\n                * (~self.mask).astype(np.float32)\n            )\n            for _ in sm.xrange(3)\n        ])\n        seen = [0, 0]\n        for _ in sm.xrange(15):\n            observed = aug.augment_image(self.image)\n\n            avg = np.average(observed * (~self.mask).astype(np.float32))\n            diff1 = abs(avg - avg1)\n            diff2 = abs(avg - avg2)\n            if diff1 < diff2:\n                seen[0] += 1\n            else:\n                seen[1] += 1\n        assert seen[0] > 0\n        assert seen[1] > 0\n\n    # -----\n    # rows and cols\n    # -----\n    @classmethod\n    def _compute_observed_std_ygrad_in_mask(cls, observed, mask):\n        grad_vert = (\n                observed[1:, :].astype(np.float32)\n                - observed[:-1, :].astype(np.float32)\n            )\n        grad_vert = grad_vert * (~mask[1:, :]).astype(np.float32)\n        return np.std(grad_vert)\n\n    def _compute_std_ygrad_in_mask(self, aug, image, mask, nb_iterations):\n        stds = []\n        for _ in sm.xrange(nb_iterations):\n            observed = aug.augment_image(image)\n\n            stds.append(\n                self._compute_observed_std_ygrad_in_mask(observed, mask)\n            )\n        return np.average(stds)\n\n    def test_nb_rows_affects_images(self):\n        # verify effects of rows\n        aug1 = iaa.PiecewiseAffine(scale=0.05, nb_rows=4, nb_cols=4)\n        aug2 = iaa.PiecewiseAffine(scale=0.05, nb_rows=30, nb_cols=4)\n\n        std1 = self._compute_std_ygrad_in_mask(aug1, self.image, self.mask, 3)\n        std2 = self._compute_std_ygrad_in_mask(aug2, self.image, self.mask, 3)\n\n        assert std1 < std2\n\n    def test_nb_rows_is_list_affects_images(self):\n        # rows as list\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=[4, 20], nb_cols=4)\n        aug1 = iaa.PiecewiseAffine(scale=0.05, nb_rows=4, nb_cols=4)\n        aug2 = iaa.PiecewiseAffine(scale=0.05, nb_rows=30, nb_cols=4)\n\n        std1 = self._compute_std_ygrad_in_mask(aug1, self.image, self.mask, 3)\n        std2 = self._compute_std_ygrad_in_mask(aug2, self.image, self.mask, 3)\n\n        seen = [0, 0]\n        for _ in sm.xrange(20):\n            observed = aug.augment_image(self.image)\n\n            std = self._compute_observed_std_ygrad_in_mask(observed, self.mask)\n            diff1 = abs(std - std1)\n            diff2 = abs(std - std2)\n            if diff1 < diff2:\n                seen[0] += 1\n            else:\n                seen[1] += 1\n        assert seen[0] > 0\n        assert seen[1] > 0\n\n    def test_nb_cols_affects_images(self):\n        # verify effects of cols\n        image = self.image.T\n        mask = self.mask.T\n\n        aug1 = iaa.PiecewiseAffine(scale=0.05, nb_rows=4, nb_cols=4)\n        aug2 = iaa.PiecewiseAffine(scale=0.05, nb_rows=20, nb_cols=4)\n\n        std1 = self._compute_std_ygrad_in_mask(aug1, image, mask, 3)\n        std2 = self._compute_std_ygrad_in_mask(aug2, image, mask, 3)\n\n        assert std1 < std2\n\n    def test_nb_cols_is_list_affects_images(self):\n        # cols as list\n        image = self.image.T\n        mask = self.mask.T\n\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=4, nb_cols=[4, 20])\n        aug1 = iaa.PiecewiseAffine(scale=0.05, nb_rows=4, nb_cols=4)\n        aug2 = iaa.PiecewiseAffine(scale=0.05, nb_rows=4, nb_cols=30)\n\n        std1 = self._compute_std_ygrad_in_mask(aug1, image, mask, 3)\n        std2 = self._compute_std_ygrad_in_mask(aug2, image, mask, 3)\n\n        seen = [0, 0]\n        for _ in sm.xrange(20):\n            observed = aug.augment_image(image)\n\n            std = self._compute_observed_std_ygrad_in_mask(observed, mask)\n            diff1 = abs(std - std1)\n            diff2 = abs(std - std2)\n            if diff1 < diff2:\n                seen[0] += 1\n            else:\n                seen[1] += 1\n        assert seen[0] > 0\n        assert seen[1] > 0\n\n    # -----\n    # order\n    # -----\n    # TODO\n\n    # -----\n    # cval\n    # -----\n    def test_cval_is_zero(self):\n        # since scikit-image 0.16.2 and scipy 1.4.0(!), this test requires\n        # several iterations to find one image that required filling with cval\n        found = False\n        for _ in np.arange(50):\n            img = np.zeros((16, 16, 3), dtype=np.uint8) + 255\n            aug = iaa.PiecewiseAffine(scale=0.7, nb_rows=10, nb_cols=10,\n                                      mode=\"constant\", cval=0)\n            observed = aug.augment_image(img)\n            if np.sum([observed[:, :] == [0, 0, 0]]) > 0:\n                found = True\n                break\n        assert found\n\n    def test_cval_should_be_ignored_by_heatmaps(self):\n        # cval as deterministic, heatmaps should always use cval=0\n        heatmaps = HeatmapsOnImage(\n            np.zeros((50, 50, 1), dtype=np.float32), shape=(50, 50, 3))\n        aug = iaa.PiecewiseAffine(scale=0.7, nb_rows=10, nb_cols=10,\n                                  mode=\"constant\", cval=255)\n        observed = aug.augment_heatmaps([heatmaps])[0]\n        assert np.sum([observed.get_arr()[:, :] >= 0.01]) == 0\n\n    def test_cval_should_be_ignored_by_segmaps(self):\n        # cval as deterministic, segmaps should always use cval=0\n        segmaps = SegmentationMapsOnImage(\n            np.zeros((50, 50, 1), dtype=np.int32), shape=(50, 50, 3))\n        aug = iaa.PiecewiseAffine(scale=0.7, nb_rows=10, nb_cols=10,\n                                  mode=\"constant\", cval=255)\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n        assert np.sum([observed.get_arr()[:, :] > 0]) == 0\n\n    def test_cval_is_list(self):\n        # cval as list\n        img = np.zeros((20, 20), dtype=np.uint8) + 255\n        aug = iaa.PiecewiseAffine(scale=0.7, nb_rows=5, nb_cols=5,\n                                  mode=\"constant\", cval=[0, 10])\n\n        seen = [0, 0, 0]\n        for _ in sm.xrange(30):\n            observed = aug.augment_image(img)\n            nb_0 = np.sum([observed[:, :] == 0])\n            nb_10 = np.sum([observed[:, :] == 10])\n            if nb_0 > 0:\n                seen[0] += 1\n            elif nb_10 > 0:\n                seen[1] += 1\n            else:\n                seen[2] += 1\n        assert seen[0] > 5\n        assert seen[1] > 5\n        assert seen[2] <= 4\n\n    # -----\n    # mode\n    # -----\n    # TODO\n\n    # ---------\n    # remaining keypoints tests\n    # ---------\n    def test_keypoints_outside_of_image(self):\n        # keypoints outside of image\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=10, nb_cols=10)\n        kps = [ia.Keypoint(x=-10, y=-20)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(10, 10, 3))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        assert_cbaois_equal(observed, kpsoi)\n\n    def test_keypoints_empty(self):\n        # empty keypoints\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=10, nb_cols=10)\n        kpsoi = ia.KeypointsOnImage([], shape=(10, 10, 3))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        assert_cbaois_equal(observed, kpsoi)\n\n    # ---------\n    # remaining polygons tests\n    # ---------\n    def test_polygons_outside_of_image(self):\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=10, nb_cols=10)\n        exterior = [(-10, -10), (110, -10), (110, 90), (-10, 90)]\n        poly = ia.Polygon(exterior)\n        psoi = ia.PolygonsOnImage([poly], shape=(10, 10, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        assert_cbaois_equal(observed, psoi)\n\n    def test_empty_polygons(self):\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=10, nb_cols=10)\n        psoi = ia.PolygonsOnImage([], shape=(10, 10, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        assert_cbaois_equal(observed, psoi)\n\n    # ---------\n    # remaining line string tests\n    # ---------\n    def test_line_strings_outside_of_image(self):\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=10, nb_cols=10)\n        coords = [(-10, -10), (110, -10), (110, 90), (-10, 90)]\n        ls = ia.LineString(coords)\n        lsoi = ia.LineStringsOnImage([ls], shape=(10, 10, 3))\n\n        observed = aug.augment_line_strings(lsoi)\n\n        assert_cbaois_equal(observed, lsoi)\n\n    def test_empty_line_strings(self):\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=10, nb_cols=10)\n        lsoi = ia.LineStringsOnImage([], shape=(10, 10, 3))\n\n        observed = aug.augment_line_strings(lsoi)\n\n        assert_cbaois_equal(observed, lsoi)\n\n    # ---------\n    # remaining bounding box tests\n    # ---------\n    def test_bounding_boxes_outside_of_image(self):\n        aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=10, nb_cols=10)\n        bbs = ia.BoundingBox(x1=-10, y1=-10, x2=15, y2=15)\n        bbsoi = ia.BoundingBoxesOnImage([bbs], shape=(10, 10, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        assert_cbaois_equal(observed, bbsoi)\n\n    def test_empty_bounding_boxes(self):\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=10, nb_cols=10)\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(10, 10, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        assert_cbaois_equal(observed, bbsoi)\n\n    # ---------\n    # zero-sized axes\n    # ---------\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.PiecewiseAffine(scale=0.05, nb_rows=2, nb_cols=2)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_zero_sized_axes_absolute_scale(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.PiecewiseAffine(scale=5, nb_rows=2, nb_cols=2,\n                                          absolute_scale=True)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    # ---------\n    # other methods\n    # ---------\n    def test_get_parameters(self):\n        aug = iaa.PiecewiseAffine(scale=0.1, nb_rows=8, nb_cols=10, order=1,\n                                  cval=2, mode=\"constant\",\n                                  absolute_scale=False)\n        params = aug.get_parameters()\n        assert params[0] is aug.jitter.scale\n        assert params[1] is aug.nb_rows\n        assert params[2] is aug.nb_cols\n        assert params[3] is aug.order\n        assert params[4] is aug.cval\n        assert params[5] is aug.mode\n        assert params[6] is False\n        assert 0.1 - 1e-8 < params[0].value < 0.1 + 1e-8\n        assert params[1].value == 8\n        assert params[2].value == 10\n        assert params[3].value == 1\n        assert params[4].value == 2\n        assert params[5].value == \"constant\"\n\n    # ---------\n    # other dtypes\n    # ---------\n    @property\n    def other_dtypes_mask(self):\n        mask = np.zeros((21, 21), dtype=bool)\n        mask[:, 7:13] = True\n        return mask\n\n    def test_other_dtypes_bool(self):\n        aug = iaa.PiecewiseAffine(scale=0.2, nb_rows=8, nb_cols=4, order=0,\n                                  mode=\"constant\")\n\n        image = np.zeros((21, 21), dtype=bool)\n        image[self.other_dtypes_mask] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.name == image.dtype.name\n        assert not np.all(image_aug == 1)\n        assert np.any(image_aug[~self.other_dtypes_mask] == 1)\n\n    def test_other_dtypes_uint_int(self):\n        aug = iaa.PiecewiseAffine(scale=0.2, nb_rows=8, nb_cols=4, order=0,\n                                  mode=\"constant\")\n\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"int8\", \"int16\", \"int32\"]\n        for dtype in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            if np.dtype(dtype).kind == \"i\":\n                values = [1, 5, 10, 100, int(0.1 * max_value),\n                          int(0.2 * max_value), int(0.5 * max_value),\n                          max_value-100, max_value]\n                values = values + [(-1)*value for value in values]\n            else:\n                values = [1, 5, 10, 100, int(center_value),\n                          int(0.1 * max_value), int(0.2 * max_value),\n                          int(0.5 * max_value), max_value-100, max_value]\n\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((21, 21), dtype=dtype)\n                    image[:, 7:13] = value\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert not np.all(image_aug == value)\n                    assert np.any(image_aug[~self.other_dtypes_mask] == value)\n\n    def test_other_dtypes_float(self):\n        aug = iaa.PiecewiseAffine(scale=0.2, nb_rows=8, nb_cols=4, order=0,\n                                  mode=\"constant\")\n\n        dtypes = [\"float16\", \"float32\", \"float64\"]\n        for dtype in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            def _isclose(a, b):\n                atol = 1e-4 if dtype == \"float16\" else 1e-8\n                return np.isclose(a, b, atol=atol, rtol=0)\n\n            isize = np.dtype(dtype).itemsize\n            values = [\n                0.01,\n                1.0,\n                10.0,\n                100.0,\n                500 ** (isize - 1),\n                float(np.float64(1000 ** (isize - 1)))\n            ]\n            values = values + [(-1) * value for value in values]\n            values = values + [min_value, max_value]\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((21, 21), dtype=dtype)\n                    image[:, 7:13] = value\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert not np.all(_isclose(image_aug, value))\n                    assert np.any(_isclose(image_aug[~self.other_dtypes_mask],\n                                           value))\n\n    def test_pickleable(self):\n        aug = iaa.PiecewiseAffine(scale=0.2, nb_rows=4, nb_cols=4, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3, shape=(25, 25, 1))\n\n\nclass TestPerspectiveTransform(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        img = np.zeros((30, 30), dtype=np.uint8)\n        img[10:20, 10:20] = 255\n        return img\n\n    @property\n    def heatmaps(self):\n        return HeatmapsOnImage((self.image / 255.0).astype(np.float32),\n                               shape=self.image.shape)\n\n    @property\n    def segmaps(self):\n        return SegmentationMapsOnImage((self.image > 0).astype(np.int32),\n                                       shape=self.image.shape)\n\n    # --------\n    # __init__\n    # --------\n    def test___init___scale_is_tuple(self):\n        # tuple for scale\n        aug = iaa.PerspectiveTransform(scale=(0.1, 0.2))\n        assert is_parameter_instance(aug.jitter.scale, iap.Uniform)\n        assert is_parameter_instance(aug.jitter.scale.a, iap.Deterministic)\n        assert is_parameter_instance(aug.jitter.scale.b, iap.Deterministic)\n        assert 0.1 - 1e-8 < aug.jitter.scale.a.value < 0.1 + 1e-8\n        assert 0.2 - 1e-8 < aug.jitter.scale.b.value < 0.2 + 1e-8\n\n    def test___init___scale_is_list(self):\n        # list for scale\n        aug = iaa.PerspectiveTransform(scale=[0.1, 0.2, 0.3])\n        assert is_parameter_instance(aug.jitter.scale, iap.Choice)\n        assert len(aug.jitter.scale.a) == 3\n        assert 0.1 - 1e-8 < aug.jitter.scale.a[0] < 0.1 + 1e-8\n        assert 0.2 - 1e-8 < aug.jitter.scale.a[1] < 0.2 + 1e-8\n        assert 0.3 - 1e-8 < aug.jitter.scale.a[2] < 0.3 + 1e-8\n\n    def test___init___scale_is_stochastic_parameter(self):\n        # StochasticParameter for scale\n        aug = iaa.PerspectiveTransform(scale=iap.Choice([0.1, 0.2, 0.3]))\n        assert is_parameter_instance(aug.jitter.scale, iap.Choice)\n        assert len(aug.jitter.scale.a) == 3\n        assert 0.1 - 1e-8 < aug.jitter.scale.a[0] < 0.1 + 1e-8\n        assert 0.2 - 1e-8 < aug.jitter.scale.a[1] < 0.2 + 1e-8\n        assert 0.3 - 1e-8 < aug.jitter.scale.a[2] < 0.3 + 1e-8\n\n    def test___init___bad_datatype_for_scale_leads_to_failure(self):\n        # bad datatype for scale\n        got_exception = False\n        try:\n            _ = iaa.PerspectiveTransform(scale=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test___init___mode_is_all(self):\n        aug = iaa.PerspectiveTransform(cval=0, mode=ia.ALL)\n        assert is_parameter_instance(aug.mode, iap.Choice)\n\n    def test___init___mode_is_string(self):\n        aug = iaa.PerspectiveTransform(cval=0, mode=\"replicate\")\n        assert is_parameter_instance(aug.mode, iap.Deterministic)\n        assert aug.mode.value == \"replicate\"\n\n    def test___init___mode_is_list(self):\n        aug = iaa.PerspectiveTransform(cval=0, mode=[\"replicate\", \"constant\"])\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert (\n            len(aug.mode.a) == 2\n            and \"replicate\" in aug.mode.a\n            and \"constant\" in aug.mode.a)\n\n    def test___init___mode_is_stochastic_parameter(self):\n        aug = iaa.PerspectiveTransform(\n            cval=0, mode=iap.Choice([\"replicate\", \"constant\"]))\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert (\n            len(aug.mode.a) == 2\n            and \"replicate\" in aug.mode.a\n            and \"constant\" in aug.mode.a)\n\n    # --------\n    # image, heatmaps, segmaps\n    # --------\n    def test_image_without_keep_size(self):\n        # without keep_size\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n\n        observed = aug.augment_image(self.image)\n\n        y1 = int(30*0.2)\n        y2 = int(30*0.8)\n        x1 = int(30*0.2)\n        x2 = int(30*0.8)\n\n        expected = self.image[y1:y2, x1:x2]\n        assert all([\n            abs(s1-s2) <= 1 for s1, s2 in zip(observed.shape, expected.shape)\n        ])\n        if observed.shape != expected.shape:\n            observed = ia.imresize_single_image(\n                observed, expected.shape[0:2], interpolation=\"cubic\")\n        # differences seem to mainly appear around the border of the inner\n        # rectangle, possibly due to interpolation\n        assert np.average(\n            np.abs(observed.astype(np.int32) - expected.astype(np.int32))\n        ) < 30.0\n\n    def test_image_heatmaps_alignment_without_keep_size(self):\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n        hm = HeatmapsOnImage(\n            self.image.astype(np.float32)/255.0,\n            shape=(30, 30)\n        )\n\n        observed = aug.augment_image(self.image)\n        hm_aug = aug.augment_heatmaps([hm])[0]\n\n        y1 = int(30*0.2)\n        y2 = int(30*0.8)\n        x1 = int(30*0.2)\n        x2 = int(30*0.8)\n\n        expected = (y2 - y1, x2 - x1)\n        assert all([\n            abs(s1-s2) <= 1\n            for s1, s2\n            in zip(hm_aug.shape, expected)\n        ])\n        assert all([\n            abs(s1-s2) <= 1\n            for s1, s2\n            in zip(hm_aug.arr_0to1.shape, expected + (1,))\n        ])\n        img_aug_mask = observed > 255*0.1\n        hm_aug_mask = hm_aug.arr_0to1 > 0.1\n        same = np.sum(img_aug_mask == hm_aug_mask[:, :, 0])\n        assert (same / img_aug_mask.size) >= 0.99\n\n    def test_image_segmaps_alignment_without_keep_size(self):\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n        segmaps = SegmentationMapsOnImage(\n            (self.image > 100).astype(np.int32),\n            shape=(30, 30)\n        )\n\n        observed = aug.augment_image(self.image)\n        segmaps_aug = aug.augment_segmentation_maps([segmaps])[0]\n\n        y1 = int(30*0.2)\n        y2 = int(30*0.8)\n        x1 = int(30*0.2)\n        x2 = int(30*0.8)\n\n        expected = (y2 - y1, x2 - x1)\n        assert all([\n            abs(s1-s2) <= 1\n            for s1, s2\n            in zip(segmaps_aug.shape, expected)\n        ])\n        assert all([\n            abs(s1-s2) <= 1\n            for s1, s2\n            in zip(segmaps_aug.arr.shape, expected + (1,))\n        ])\n        img_aug_mask = observed > 255*0.5\n        segmaps_aug_mask = segmaps_aug.arr > 0\n        same = np.sum(img_aug_mask == segmaps_aug_mask[:, :, 0])\n        assert (same / img_aug_mask.size) >= 0.99\n\n    def test_consecutive_calls_produce_different_results(self):\n        # PerspectiveTransform works with random_state.copy(), so we\n        # test explicitly that it doesn't always use the same samples\n        aug = iaa.PerspectiveTransform((0.0, 0.2))\n        image = np.mod(np.arange(16*16), 255).astype(np.uint8).reshape((16, 16))\n        nb_same = 0\n        last_image = aug(image=image)\n        for _ in np.arange(100):\n            image_aug = aug(image=image)\n            nb_same += int(np.array_equal(image_aug, last_image))\n        assert nb_same <= 1\n\n    def test_heatmaps_smaller_than_image_without_keep_size(self):\n        # without keep_size, different heatmap size\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n\n        height, width = 300, 200\n        height_small, width_small = 150, 100\n\n        y1 = int(height*0.2)\n        y2 = int(height*0.8)\n        x1 = int(width*0.2)\n        x2 = int(width*0.8)\n        y1_small = int(height_small*0.2)\n        y2_small = int(height_small*0.8)\n        x1_small = int(width_small*0.2)\n        x2_small = int(width_small*0.8)\n\n        img_small = ia.imresize_single_image(\n            self.image,\n            (height_small, width_small),\n            interpolation=\"cubic\")\n        hm = ia.HeatmapsOnImage(\n            img_small.astype(np.float32)/255.0,\n            shape=(height, width))\n\n        img_aug = aug.augment_image(self.image)\n        hm_aug = aug.augment_heatmaps([hm])[0]\n\n        expected = (y2 - y1, x2 - x1)\n        expected_small = (y2_small - y1_small, x2_small - x1_small, 1)\n        assert all([\n            abs(s1-s2) <= 1\n            for s1, s2\n            in zip(hm_aug.shape, expected)\n        ])\n        assert all([\n            abs(s1-s2) <= 1\n            for s1, s2\n            in zip(hm_aug.arr_0to1.shape, expected_small)\n        ])\n        img_aug_mask = img_aug > 255*0.1\n        hm_aug_mask = ia.imresize_single_image(\n            hm_aug.arr_0to1, img_aug.shape[0:2], interpolation=\"linear\"\n        ) > 0.1\n        same = np.sum(img_aug_mask == hm_aug_mask[:, :, 0])\n        assert (same / img_aug_mask.size) >= 0.96\n\n    def test_segmaps_smaller_than_image_without_keep_size(self):\n        # without keep_size, different segmap size\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n\n        y1 = int(30*0.2)\n        y2 = int(30*0.8)\n        x1 = int(30*0.2)\n        x2 = int(30*0.8)\n        x1_small = int(25*0.2)\n        x2_small = int(25*0.8)\n        y1_small = int(20*0.2)\n        y2_small = int(20*0.8)\n\n        img_small = ia.imresize_single_image(\n            self.image,\n            (20, 25),\n            interpolation=\"cubic\")\n        seg = SegmentationMapsOnImage(\n            (img_small > 100).astype(np.int32),\n            shape=(30, 30))\n\n        img_aug = aug.augment_image(self.image)\n        seg_aug = aug.augment_segmentation_maps([seg])[0]\n\n        expected = (y2 - y1, x2 - x1)\n        expected_small = (y2_small - y1_small, x2_small - x1_small, 1)\n        assert all([\n            abs(s1-s2) <= 1\n            for s1, s2\n            in zip(seg_aug.shape, expected)\n        ])\n        assert all([\n            abs(s1-s2) <= 1\n            for s1, s2\n            in zip(seg_aug.arr.shape, expected_small)\n        ])\n        img_aug_mask = img_aug > 255*0.5\n        seg_aug_mask = ia.imresize_single_image(\n            seg_aug.arr, img_aug.shape[0:2], interpolation=\"nearest\") > 0\n        same = np.sum(img_aug_mask == seg_aug_mask[:, :, 0])\n        assert (same / img_aug_mask.size) >= 0.92\n\n    def test_image_with_keep_size(self):\n        # with keep_size\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=True)\n        aug.jitter = iap.Deterministic(0.2)\n\n        observed = aug.augment_image(self.image)\n\n        expected = self.image[int(30*0.2):int(30*0.8),\n                              int(30*0.2):int(30*0.8)]\n        expected = ia.imresize_single_image(\n            expected,\n            self.image.shape[0:2],\n            interpolation=\"cubic\")\n        assert observed.shape == self.image.shape\n        # differences seem to mainly appear around the border of the inner\n        # rectangle, possibly due to interpolation\n        assert np.average(\n            np.abs(observed.astype(np.int32) - expected.astype(np.int32))\n        ) < 30.0\n\n    def test_heatmaps_with_keep_size(self):\n        # with keep_size, heatmaps\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=True)\n        aug.jitter = iap.Deterministic(0.2)\n\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n\n        heatmaps_arr = self.heatmaps.get_arr()\n        expected = heatmaps_arr[int(30*0.2):int(30*0.8),\n                                int(30*0.2):int(30*0.8)]\n        expected = ia.imresize_single_image(\n            (expected*255).astype(np.uint8),\n            self.image.shape[0:2],\n            interpolation=\"cubic\")\n        expected = (expected / 255.0).astype(np.float32)\n        assert observed.shape == self.heatmaps.shape\n        _assert_same_min_max(observed, self.heatmaps)\n        # differences seem to mainly appear around the border of the inner\n        # rectangle, possibly due to interpolation\n        assert np.average(np.abs(observed.get_arr() - expected)) < 30.0\n\n    def test_segmaps_with_keep_size(self):\n        # with keep_size, segmaps\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=True)\n        aug.jitter = iap.Deterministic(0.2)\n\n        observed = aug.augment_segmentation_maps([self.segmaps])[0]\n\n        segmaps_arr = self.segmaps.get_arr()\n        expected = segmaps_arr[int(30*0.2):int(30*0.8),\n                               int(30*0.2):int(30*0.8)]\n        expected = ia.imresize_single_image(\n            (expected*255).astype(np.uint8),\n            self.image.shape[0:2],\n            interpolation=\"cubic\")\n        expected = (expected > 255*0.5).astype(np.int32)\n        assert observed.shape == self.segmaps.shape\n        assert np.average(observed.get_arr() != expected) < 0.05\n\n    def test_image_rgb_with_keep_size(self):\n        # with keep_size, RGB images\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=True)\n        aug.jitter = iap.Deterministic(0.2)\n        imgs = np.tile(self.image[np.newaxis, :, :, np.newaxis], (2, 1, 1, 3))\n\n        observed = aug.augment_images(imgs)\n\n        for img_idx in sm.xrange(2):\n            for c in sm.xrange(3):\n                observed_i = observed[img_idx, :, :, c]\n                expected = imgs[img_idx,\n                                int(30*0.2):int(30*0.8),\n                                int(30*0.2):int(30*0.8),\n                                c]\n                expected = ia.imresize_single_image(\n                    expected, imgs.shape[1:3], interpolation=\"cubic\")\n                assert observed_i.shape == imgs.shape[1:3]\n                # differences seem to mainly appear around the border of the\n                # inner rectangle, possibly due to interpolation\n                assert np.average(\n                    np.abs(\n                        observed_i.astype(np.int32) - expected.astype(np.int32)\n                    )\n                ) < 30.0\n\n    # --------\n    # keypoints\n    # --------\n    def test_keypoints_without_keep_size(self):\n        # keypoint augmentation without keep_size\n        # TODO deviations of around 0.4-0.7 in this and the next test (between\n        #      expected and observed coordinates) -- why?\n        kps = [ia.Keypoint(x=10, y=10), ia.Keypoint(x=14, y=11)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=self.image.shape)\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n\n        observed = aug.augment_keypoints([kpsoi])\n\n        kps_expected = [\n            ia.Keypoint(x=10-0.2*30, y=10-0.2*30),\n            ia.Keypoint(x=14-0.2*30, y=11-0.2*30)\n        ]\n        gen = zip(observed[0].keypoints, kps_expected)\n        # TODO deviations of around 0.5 here from expected values, why?\n        for kp_observed, kp_expected in gen:\n            assert kp_observed.coords_almost_equals(\n                kp_expected, max_distance=1.5)\n\n    def test_keypoints_with_keep_size(self):\n        # keypoint augmentation with keep_size\n        kps = [ia.Keypoint(x=10, y=10), ia.Keypoint(x=14, y=11)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=self.image.shape)\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=True)\n        aug.jitter = iap.Deterministic(0.2)\n\n        observed = aug.augment_keypoints([kpsoi])\n\n        kps_expected = [\n            ia.Keypoint(x=((10-0.2*30)/(30*0.6))*30,\n                        y=((10-0.2*30)/(30*0.6))*30),\n            ia.Keypoint(x=((14-0.2*30)/(30*0.6))*30,\n                        y=((11-0.2*30)/(30*0.6))*30)\n        ]\n        gen = zip(observed[0].keypoints, kps_expected)\n        # TODO deviations of around 0.5 here from expected values, why?\n        for kp_observed, kp_expected in gen:\n            assert kp_observed.coords_almost_equals(\n                kp_expected, max_distance=1.5)\n\n    def test_image_keypoint_alignment(self):\n        img = np.zeros((100, 100), dtype=np.uint8)\n        img[25-3:25+3, 25-3:25+3] = 255\n        img[50-3:50+3, 25-3:25+3] = 255\n        img[75-3:75+3, 25-3:25+3] = 255\n        img[25-3:25+3, 75-3:75+3] = 255\n        img[50-3:50+3, 75-3:75+3] = 255\n        img[75-3:75+3, 75-3:75+3] = 255\n        img[50-3:75+3, 50-3:75+3] = 255\n        kps = [\n            ia.Keypoint(y=25, x=25), ia.Keypoint(y=50, x=25),\n            ia.Keypoint(y=75, x=25), ia.Keypoint(y=25, x=75),\n            ia.Keypoint(y=50, x=75), ia.Keypoint(y=75, x=75),\n            ia.Keypoint(y=50, x=50)\n        ]\n        kpsoi = ia.KeypointsOnImage(kps, shape=img.shape)\n        aug = iaa.PerspectiveTransform(scale=(0.05, 0.15), keep_size=True)\n\n        for _ in sm.xrange(10):\n            aug_det = aug.to_deterministic()\n            imgs_aug = aug_det.augment_images([img, img])\n            kpsois_aug = aug_det.augment_keypoints([kpsoi, kpsoi])\n\n            for img_aug, kpsoi_aug in zip(imgs_aug, kpsois_aug):\n                assert kpsoi_aug.shape == img.shape\n                for kp_aug in kpsoi_aug.keypoints:\n                    x, y = int(np.round(kp_aug.x)), int(np.round(kp_aug.y))\n                    if 0 <= x < img.shape[1] and 0 <= y < img.shape[0]:\n                        assert img_aug[y, x] > 10\n\n    def test_empty_keypoints(self):\n        # test empty keypoints\n        kpsoi = ia.KeypointsOnImage([], shape=(20, 10, 3))\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=True)\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        assert_cbaois_equal(observed, kpsoi)\n\n    # --------\n    # abstract test methods for polygons and line strings\n    # --------\n    @classmethod\n    def _test_cbaois_without_keep_size(cls, cba_class, cbaoi_class, augf_name):\n        points = np.float32([\n            [10, 10],\n            [25, 10],\n            [25, 25],\n            [10, 25]\n        ])\n        cbaoi = cbaoi_class([cba_class(points)], shape=(30, 30, 3))\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert observed.shape == (30 - 12, 30 - 12, 3)\n        assert len(observed.items) == 1\n        if hasattr(observed.items[0], \"is_valid\"):\n            assert observed.items[0].is_valid\n\n        points_expected = np.copy(points)\n        points_expected[:, 0] -= 0.2 * 30\n        points_expected[:, 1] -= 0.2 * 30\n        # TODO deviations of around 0.5 here from expected values, why?\n        assert observed.items[0].coords_almost_equals(\n            points_expected, max_distance=1.5)\n\n    @classmethod\n    def _test_cbaois_with_keep_size(cls, cba_class, cbaoi_class, augf_name):\n        # polygon augmentation with keep_size\n        points = np.float32([\n            [10, 10],\n            [25, 10],\n            [25, 25],\n            [10, 25]\n        ])\n        cbaoi = cbaoi_class([cba_class(points)], shape=(30, 30, 3))\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=True)\n        aug.jitter = iap.Deterministic(0.2)\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert observed.shape == (30, 30, 3)\n        assert len(observed.items) == 1\n        if hasattr(observed.items[0], \"is_valid\"):\n            assert observed.items[0].is_valid\n\n        points_expected = np.copy(points)\n        points_expected[:, 0] = (\n            (points_expected[:, 0] - 0.2 * 30) / (30 * 0.6)\n        ) * 30\n        points_expected[:, 1] = (\n            (points_expected[:, 1] - 0.2 * 30) / (30 * 0.6)\n        ) * 30\n        # TODO deviations of around 0.5 here from expected values, why?\n        assert observed.items[0].coords_almost_equals(\n            points_expected, max_distance=2.5)\n\n    @classmethod\n    def _test_image_cba_alignment(cls, cba_class, cbaoi_class, augf_name):\n        img = np.zeros((100, 100), dtype=np.uint8)\n        img[25-3:25+3, 25-3:25+3] = 255\n        img[50-3:50+3, 25-3:25+3] = 255\n        img[75-3:75+3, 25-3:25+3] = 255\n        img[25-3:25+3, 75-3:75+3] = 255\n        img[50-3:50+3, 75-3:75+3] = 255\n        img[75-3:75+3, 75-3:75+3] = 255\n        points = [\n            [25, 25],\n            [75, 25],\n            [75, 50],\n            [75, 75],\n            [25, 75],\n            [25, 50]\n        ]\n\n        cbaoi = cbaoi_class([cba_class(points)], shape=img.shape)\n        aug = iaa.PerspectiveTransform(scale=0.1, keep_size=True)\n        for _ in sm.xrange(10):\n            aug_det = aug.to_deterministic()\n            imgs_aug = aug_det.augment_images([img] * 4)\n            cbaois_aug = getattr(aug_det, augf_name)([cbaoi] * 4)\n\n            for img_aug, cbaoi_aug in zip(imgs_aug, cbaois_aug):\n                assert cbaoi_aug.shape == img.shape\n                for cba_aug in cbaoi_aug.items:\n                    if hasattr(cba_aug, \"is_valid\"):\n                        assert cba_aug.is_valid\n                    for x, y in cba_aug.coords:\n                        if 0 <= x < img.shape[1] and 0 <= y < img.shape[0]:\n                            bb = ia.BoundingBox(x1=x-2, x2=x+2, y1=y-2, y2=y+2)\n                            img_ex = bb.extract_from_image(img_aug)\n                            assert np.any(img_ex > 10)\n\n    @classmethod\n    def _test_empty_cba(cls, cbaoi, augf_name):\n        # test empty polygons\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=True)\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(observed, cbaoi)\n\n    # --------\n    # polygons\n    # --------\n    def test_polygons_without_keep_size(self):\n        self._test_cbaois_without_keep_size(ia.Polygon, ia.PolygonsOnImage,\n                                            \"augment_polygons\")\n\n    def test_polygons_with_keep_size(self):\n        self._test_cbaois_with_keep_size(ia.Polygon, ia.PolygonsOnImage,\n                                         \"augment_polygons\")\n\n    def test_image_polygon_alignment(self):\n        self._test_image_cba_alignment(ia.Polygon, ia.PolygonsOnImage,\n                                       \"augment_polygons\")\n\n    def test_empty_polygons(self):\n        psoi = ia.PolygonsOnImage([], shape=(20, 10, 3))\n        self._test_empty_cba(psoi, \"augment_polygons\")\n\n    def test_polygons_under_extreme_scale_values(self):\n        # test extreme scales\n        # TODO when setting .min_height and .min_width in PerspectiveTransform\n        #      to 1x1, at least one of the output polygons was invalid and had\n        #      only 3 instead of the expected 4 points - why?\n        for scale in [0.1, 0.2, 0.3, 0.4]:\n            with self.subTest(scale=scale):\n                exterior = np.float32([\n                    [10, 10],\n                    [25, 10],\n                    [25, 25],\n                    [10, 25]\n                ])\n                psoi = ia.PolygonsOnImage([ia.Polygon(exterior)],\n                                          shape=(30, 30, 3))\n                aug = iaa.PerspectiveTransform(scale=scale, keep_size=True)\n                aug.jitter = iap.Deterministic(scale)\n\n                observed = aug.augment_polygons(psoi)\n\n                assert observed.shape == (30, 30, 3)\n                assert len(observed.polygons) == 1\n                assert observed.polygons[0].is_valid\n\n                # FIXME this part is currently deactivated due to too large\n                #       deviations from expectations. As the alignment check\n                #       works, this is probably some error on the test side\n                \"\"\"\n                exterior_expected = np.copy(exterior)\n                exterior_expected[:, 0] = (\n                    (exterior_expected[:, 0] - scale * 30) / (30*(1-2*scale))\n                ) * 30\n                exterior_expected[:, 1] = (\n                    (exterior_expected[:, 1] - scale * 30) / (30*(1-2*scale))\n                ) * 30\n                poly0 = observed.polygons[0]\n                # TODO deviations of around 0.5 here from expected values, why?\n                assert poly0.exterior_almost_equals(\n                    exterior_expected, max_distance=2.0)\n                \"\"\"\n\n    # --------\n    # line strings\n    # --------\n    def test_line_strings_without_keep_size(self):\n        self._test_cbaois_without_keep_size(ia.LineString, ia.LineStringsOnImage,\n                                            \"augment_line_strings\")\n\n    def test_line_strings_with_keep_size(self):\n        self._test_cbaois_with_keep_size(ia.LineString, ia.LineStringsOnImage,\n                                         \"augment_line_strings\")\n\n    def test_image_line_string_alignment(self):\n        self._test_image_cba_alignment(ia.LineString, ia.LineStringsOnImage,\n                                       \"augment_line_strings\")\n\n    def test_empty_line_strings(self):\n        lsoi = ia.LineStringsOnImage([], shape=(20, 10, 3))\n        self._test_empty_cba(lsoi, \"augment_line_strings\")\n\n    # --------\n    # bounding boxes\n    # --------\n    def test_bounding_boxes_without_keep_size(self):\n        # BB augmentation without keep_size\n        # TODO deviations of around 0.4-0.7 in this and the next test (between\n        #      expected and observed coordinates) -- why?\n        bbs = [ia.BoundingBox(x1=0, y1=10, x2=20, y2=20)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n\n        observed = aug.augment_bounding_boxes([bbsoi])\n\n        bbs_expected = [\n            ia.BoundingBox(x1=0-0.2*30, y1=10-0.2*30,\n                           x2=20-0.2*30, y2=20-0.2*30)\n        ]\n        gen = zip(observed[0].bounding_boxes, bbs_expected)\n        # TODO deviations of around 0.5 here from expected values, why?\n        for bb_observed, bb_expected in gen:\n            assert bb_observed.coords_almost_equals(\n                bb_expected, max_distance=1.5)\n\n    def test_bounding_boxes_with_keep_size(self):\n        # BB augmentation with keep_size\n        bbs = [ia.BoundingBox(x1=0, y1=10, x2=20, y2=20)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=True)\n        aug.jitter = iap.Deterministic(0.2)\n\n        observed = aug.augment_bounding_boxes([bbsoi])\n\n        bbs_expected = [\n            ia.BoundingBox(\n                x1=((0-0.2*30)/(30*0.6))*30,\n                y1=((10-0.2*30)/(30*0.6))*30,\n                x2=((20-0.2*30)/(30*0.6))*30,\n                y2=((20-0.2*30)/(30*0.6))*30\n            )\n        ]\n        gen = zip(observed[0].bounding_boxes, bbs_expected)\n        # TODO deviations of around 0.5 here from expected values, why?\n        for bb_observed, bb_expected in gen:\n            assert bb_observed.coords_almost_equals(\n                bb_expected, max_distance=1.5)\n\n    def test_image_bounding_box_alignment(self):\n        img = np.zeros((100, 100), dtype=np.uint8)\n        img[35:35+1, 35:65+1] = 255\n        img[65:65+1, 35:65+1] = 255\n        img[35:65+1, 35:35+1] = 255\n        img[35:65+1, 65:65+1] = 255\n        bbs = [\n            ia.BoundingBox(y1=35.5, x1=35.5, y2=65.5, x2=65.5),\n        ]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=img.shape)\n        aug = iaa.PerspectiveTransform(scale=(0.05, 0.2), keep_size=True)\n\n        for _ in sm.xrange(30):\n            imgs_aug, bbsois_aug = aug(\n                images=[img, img, img, img],\n                bounding_boxes=[bbsoi, bbsoi, bbsoi, bbsoi])\n\n            nb_skipped = 0\n            for img_aug, bbsoi_aug in zip(imgs_aug, bbsois_aug):\n                assert bbsoi_aug.shape == img_aug.shape\n                for bb_aug in bbsoi_aug.bounding_boxes:\n                    if bb_aug.is_fully_within_image(img_aug):\n                        # top, bottom, left, right\n                        x1 = bb_aug.x1_int\n                        x2 = bb_aug.x2_int\n                        y1 = bb_aug.y1_int\n                        y2 = bb_aug.y2_int\n                        top_row = img_aug[y1-1:y1+1, x1-1:x2+1]\n                        btm_row = img_aug[y2-1:y2+1, x1-1:x2+1]\n                        lft_row = img_aug[y1-1:y2+1, x1-1:x1+1]\n                        rgt_row = img_aug[y1-1:y2+1, x2-1:x2+1]\n                        assert np.max(top_row) > 10\n                        assert np.max(btm_row) > 10\n                        assert np.max(lft_row) > 10\n                        assert np.max(rgt_row) > 10\n                    else:\n                        nb_skipped += 1\n            assert nb_skipped <= 3\n\n    def test_bounding_boxes_cover_extreme_points(self):\n        # Test that for BBs, the augmented BB x coord is really the minimum\n        # of the BB corner x-coords after augmentation and e.g. not just always\n        # the augmented top-left corner's coordinate.\n        h = w = 200  # height, width\n        s = 5  # block size\n        j_r = 0.1  # relative amount of jitter\n        j = int(h * j_r)  # absolute amount of jitter\n\n        # Note that PerspectiveTransform currently places four points on the\n        # image and back-projects to the image size (roughly).\n        # That's why e.g. TopWiderThanBottom has coordinates that seem like\n        # the top is thinner than the bottom (after projecting back to the\n        # image rectangle, the top becomes wider).\n        class _JitterTopWiderThanBottom(object):\n            def draw_samples(self, size, random_state):\n                return np.float32([\n                    [\n                        [j_r, 0.0],  # top-left\n                        [j_r, 0.0],  # top-right\n                        [0.0, 0.0],  # bottom-right\n                        [0.0, 0.0],  # bottom-left\n                    ]\n                ])\n\n        class _JitterTopThinnerThanBottom(object):\n            def draw_samples(self, size, random_state):\n                return np.float32([\n                    [\n                        [0.0, 0.0],  # top-left\n                        [0.0, 0.0],  # top-right\n                        [j_r, 0.0],  # bottom-right\n                        [j_r, 0.0],  # bottom-left\n                    ]\n                ])\n\n        class _JitterLeftWiderThanRight(object):\n            def draw_samples(self, size, random_state):\n                return np.float32([\n                    [\n                        [0.0, j_r],  # top-left\n                        [0.0, 0.0],  # top-right\n                        [0.0, 0.0],  # bottom-right\n                        [0.0, j_r],  # bottom-left\n                    ]\n                ])\n\n        class _JitterLeftThinnerThanRight(object):\n            def draw_samples(self, size, random_state):\n                return np.float32([\n                    [\n                        [0.0, 0.0],  # top-left\n                        [0.0, j_r],  # top-right\n                        [0.0, j_r],  # bottom-right\n                        [0.0, 0.0],  # bottom-left\n                    ]\n                ])\n\n        jitters = [\n            _JitterTopWiderThanBottom(),\n            _JitterTopThinnerThanBottom(),\n            _JitterLeftWiderThanRight(),\n            _JitterLeftThinnerThanRight(),\n        ]\n\n        # expected coordinates after applying the above jitter\n        # coordinates here are given as\n        #   (ystart, yend), (xstart, xend)\n        coords = [\n            # top wider than bottom\n            [\n                [(0+j, s+j+1), (0, s+1)],  # top left\n                [(0+j, s+j+1), (w-s, w+1)],  # top right\n                [(h-s-j, h-j+1), (w-s-j, w-j+1)],  # bottom right\n                [(h-s-j, h-j+1), (0+j, s+j+1)]  # bottom left\n            ],\n            # top thinner than bottom\n            [\n                [(0+j, s+j+1), (0+j, s+j+1)],\n                [(0+j, s+j+1), (w-s-j, w-j+1)],\n                [(h-s-j, h-j+1), (w-s, w+1)],\n                [(h-s-j, h-j+1), (0, s+1)]\n            ],\n            # left wider than right\n            [\n                [(0, s+1), (0+j, s+j+1)],\n                [(0+j, s+j+1), (w-s-j, w-j+1)],\n                [(h-s-j, h-j+1), (w-s-j, w-j+1)],\n                [(h-s, h+1), (0+j, s+j+1)]\n            ],\n            # left thinner than right\n            [\n                [(0+j, s+j+1), (0+j, s+j+1)],\n                [(0, s+1), (w-s-j, w-j+1)],\n                [(h-s, h+1), (w-s-j, w-j+1)],\n                [(h-s-j, h-j+1), (0+j, s+j+1)]\n            ],\n        ]\n\n        image = np.zeros((h-1, w-1, 4), dtype=np.uint8)\n        image = iaa.pad(image, top=1, right=1, bottom=1, left=1, cval=50)\n        image[0+j:s+j+1, 0+j:s+j+1, 0] = 255\n        image[0+j:s+j+1, w-s-j:w-j+1, 1] = 255\n        image[h-s-j:h-j+1, w-s-j:w-j+1, 2] = 255\n        image[h-s-j:h-j+1, 0+j:s+j+1, 3] = 255\n\n        bb = ia.BoundingBox(x1=0.0+j,\n                            y1=0.0+j,\n                            x2=w-j,\n                            y2=h-j)\n        bbsoi = ia.BoundingBoxesOnImage([bb], shape=image.shape)\n\n        i = 0\n        for jitter, coords_i in zip(jitters, coords):\n            with self.subTest(jitter=jitter.__class__.__name__):\n                aug = iaa.PerspectiveTransform(scale=0.2, keep_size=True)\n                aug.jitter = jitter\n\n                image_aug, bbsoi_aug = aug(image=image, bounding_boxes=bbsoi)\n                assert image_aug.shape == image.shape\n\n                (tl_y1, tl_y2), (tl_x1, tl_x2) = coords_i[0]\n                (tr_y1, tr_y2), (tr_x1, tr_x2) = coords_i[1]\n                (br_y1, br_y2), (br_x1, br_x2) = coords_i[2]\n                (bl_y1, bl_y2), (bl_x1, bl_x2) = coords_i[3]\n\n                # We have to be rather tolerant here (>100 instead of e.g.\n                # >200), because the transformation seems to be not that\n                # accurate and the blobs may be a few pixels off the expected\n                # coorindates.\n                assert np.max(image_aug[tl_y1:tl_y2, tl_x1:tl_x2, 0]) > 100\n                assert np.max(image_aug[tr_y1:tr_y2, tr_x1:tr_x2, 1]) > 100\n                assert np.max(image_aug[br_y1:br_y2, br_x1:br_x2, 2]) > 100\n                assert np.max(image_aug[bl_y1:bl_y2, bl_x1:bl_x2, 3]) > 100\n\n                # We have rather strong tolerances of 7.5 here, partially\n                # because the blobs are wide and the true coordinates are in\n                # the center of the blobs; partially, because of above\n                # mentioned inaccuracy of PerspectiveTransform.\n                bb_aug = bbsoi_aug.bounding_boxes[0]\n                exp_x1 = min([tl_x1, tr_x1, br_x1, bl_x1])\n                exp_x2 = max([tl_x2, tr_x2, br_x2, bl_x2])\n                exp_y1 = min([tl_y1, tr_y1, br_y1, bl_y1])\n                exp_y2 = max([tl_y2, tr_y2, br_y2, bl_y2])\n                assert np.isclose(bb_aug.x1, exp_x1, atol=7.5)\n                assert np.isclose(bb_aug.y1, exp_y1, atol=7.5)\n                assert np.isclose(bb_aug.x2, exp_x2, atol=7.5)\n                assert np.isclose(bb_aug.y2, exp_y2, atol=7.5)\n\n    def test_empty_bounding_boxes(self):\n        # test empty bounding boxes\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(20, 10, 3))\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=True)\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        assert_cbaois_equal(observed, bbsoi)\n\n    # ------------\n    # mode\n    # ------------\n    def test_draw_samples_with_mode_being_int(self):\n        aug = iaa.PerspectiveTransform(scale=0.001, mode=cv2.BORDER_REPLICATE)\n\n        samples = aug._draw_samples([(10, 10, 3)], iarandom.RNG(0))\n\n        assert samples.modes.shape == (1,)\n        assert samples.modes[0] == cv2.BORDER_REPLICATE\n\n    def test_draw_samples_with_mode_being_string(self):\n        aug = iaa.PerspectiveTransform(scale=0.001, mode=\"replicate\")\n\n        samples = aug._draw_samples([(10, 10, 3)], iarandom.RNG(0))\n\n        assert samples.modes.shape == (1,)\n        assert samples.modes[0] == cv2.BORDER_REPLICATE\n\n    def test_mode_replicate_copies_values(self):\n        aug = iaa.PerspectiveTransform(\n            scale=0.001, mode=\"replicate\", cval=0, seed=31)\n        img = np.ones((256, 256, 3), dtype=np.uint8) * 255\n\n        img_aug = aug.augment_image(img)\n\n        assert (img_aug == 255).all()\n\n    def test_mode_constant_uses_cval(self):\n        aug255 = iaa.PerspectiveTransform(\n            scale=0.001, mode=\"constant\", cval=255, seed=31)\n        aug0 = iaa.PerspectiveTransform(\n            scale=0.001, mode=\"constant\", cval=0, seed=31)\n        img = np.ones((256, 256, 3), dtype=np.uint8) * 255\n\n        img_aug255 = aug255.augment_image(img)\n        img_aug0 = aug0.augment_image(img)\n\n        assert (img_aug255 == 255).all()\n        # TODO This was originally \"assert not (...)\", but since\n        #      PerspectiveTransform has become more precise, there are no\n        #      filled pixels anymore at the edges. That is because PerspT\n        #      currently only zooms in and not out. Filled pixels at the sides\n        #      were previously due to a bug.\n        assert (img_aug0 == 255).all()\n\n    # ---------\n    # fit_output\n    # ---------\n    def test_fit_output_with_fixed_jitter(self):\n        aug = iaa.PerspectiveTransform(scale=0.2, fit_output=True,\n                                       keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n\n        image = np.zeros((40, 40, 3), dtype=np.uint8)\n        image[0:3, 0:3, 0] = 255\n        image[0:3, 40-3:, 1] = 255\n        image[40-3:, 40-3:, 2] = 255\n\n        image_aug = aug(image=image)\n\n        h, w = image_aug.shape[0:2]\n        y0 = np.argmax(image_aug[:, 0, 0])\n        x0 = np.argmax(image_aug[0, :, 0])\n        y1 = np.argmax(image_aug[:, w-1, 1])\n        x1 = np.argmax(image_aug[0, :, 1])\n        y2 = np.argmax(image_aug[:, w-1, 2])\n        x2 = np.argmax(image_aug[h-1, :, 2])\n\n        # different shape\n        assert image_aug.shape == image.shape\n\n        # corners roughly still at top-left, top-right, bottom-right\n        assert 0 <= y0 <= 3\n        assert 0 <= x0 <= 3\n        assert 0 <= y1 <= 3\n        assert image_aug.shape[1]-3 <= x1 <= image_aug.shape[1]\n        assert image_aug.shape[1]-3 <= y2 <= image_aug.shape[1]\n        assert image_aug.shape[1]-3 <= x2 <= image_aug.shape[1]\n\n        # no corner pixels now in the center\n        assert np.max(image_aug[8:h-8, 8:w-8, :]) == 0\n\n    def test_fit_output_with_random_jitter(self):\n        aug = iaa.PerspectiveTransform(scale=0.1, fit_output=True,\n                                       keep_size=False)\n\n        image = np.zeros((50, 50, 4), dtype=np.uint8)\n        image[0:5, 0:5, 0] = 255\n        image[0:5, 50-5:, 1] = 255\n        image[50-5:, 50-5:, 2] = 255\n        image[50-5:, 0:5, 3] = 255\n\n        for _ in sm.xrange(10):\n            image_aug = aug(image=image)\n\n            h, w = image_aug.shape[0:2]\n            arr_nochan = np.max(image_aug, axis=2)\n            y_idx = np.where(np.max(arr_nochan, axis=1))[0]\n            x_idx = np.where(np.max(arr_nochan, axis=0))[0]\n            y_min = np.min(y_idx)\n            y_max = np.max(y_idx)\n            x_min = np.min(x_idx)\n            x_max = np.max(x_idx)\n\n            tol = 0\n            assert 0 <= y_min <= 5+tol\n            assert 0 <= x_min <= 5+tol\n            assert h-5-tol <= y_max <= h-1\n            assert w-5-tol <= x_max <= w-1\n\n    def test_fit_output_with_random_jitter__segmentation_maps(self):\n        aug = iaa.PerspectiveTransform(scale=0.1, fit_output=True,\n                                       keep_size=False)\n\n        arr = np.zeros((50, 50, 4), dtype=np.uint8)\n        arr[0:5, 0:5, 0] = 1\n        arr[0:5, 50-5:, 1] = 1\n        arr[50-5:, 50-5:, 2] = 1\n        arr[50-5:, 0:5, 3] = 1\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(50, 50, 3))\n\n        image = np.zeros((49, 49, 3), dtype=np.uint8)\n        image = iaa.pad(image, top=1, right=1, bottom=1, left=1, cval=128)\n\n        for _ in sm.xrange(10):\n            image_aug, segmap_aug = aug(image=image, segmentation_maps=segmap)\n\n            h, w = segmap_aug.arr.shape[0:2]\n            arr_nochan = np.max(segmap_aug.arr, axis=2)\n            y_idx = np.where(np.max(arr_nochan, axis=1))[0]\n            x_idx = np.where(np.max(arr_nochan, axis=0))[0]\n            y_min = np.min(y_idx)\n            y_max = np.max(y_idx)\n            x_min = np.min(x_idx)\n            x_max = np.max(x_idx)\n\n            tol = 0\n            assert 0 <= y_min <= 5+tol\n            assert 0 <= x_min <= 5+tol\n            assert h-5-tol <= y_max <= h-1\n            assert w-5-tol <= x_max <= w-1\n\n    def test_fit_output_with_fixed_jitter__keypoints(self):\n        aug = iaa.PerspectiveTransform(scale=0.1, fit_output=True,\n                                       keep_size=False)\n\n        kpsoi = ia.KeypointsOnImage.from_xy_array([\n            (0, 0),\n            (50, 0),\n            (50, 50),\n            (0, 50)\n        ], shape=(50, 50, 3))\n\n        for i in sm.xrange(10):\n            kpsoi_aug = aug(keypoints=kpsoi)\n\n            h, w = kpsoi_aug.shape[0:2]\n            y0, x0 = kpsoi_aug.keypoints[0].y, kpsoi_aug.keypoints[0].x\n            y1, x1 = kpsoi_aug.keypoints[1].y, kpsoi_aug.keypoints[1].x\n            y2, x2 = kpsoi_aug.keypoints[2].y, kpsoi_aug.keypoints[2].x\n            y3, x3 = kpsoi_aug.keypoints[3].y, kpsoi_aug.keypoints[3].x\n\n            y_min = min([y0, y1, y2, y3])\n            y_max = max([y0, y1, y2, y3])\n            x_min = min([x0, x1, x2, x3])\n            x_max = max([x0, x1, x2, x3])\n            tol = 0.5\n            assert 0-tol <= y_min <= tol, \"Got y_min=%.4f at %d\" % (y_min, i)\n            assert 0-tol <= x_min <= tol, \"Got x_min=%.4f at %d\" % (x_min, i)\n            assert h-tol <= y_max <= h+tol, (\n                \"Got y_max=%.4f for h=%.2f at %d\" % (y_max, h, i))\n            assert w-tol <= x_max <= w+tol, (\n                \"Got x_max=%.4f for w=%.2f at %d\" % (x_max, w, i))\n\n    # ---------\n    # unusual channel numbers\n    # ---------\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.PerspectiveTransform(scale=0.01)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 0)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    # ---------\n    # zero-sized axes\n    # ---------\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            for keep_size in [False, True]:\n                with self.subTest(shape=shape, keep_size=keep_size):\n                    for _ in sm.xrange(3):\n                        image = np.zeros(shape, dtype=np.uint8)\n                        aug = iaa.PerspectiveTransform(scale=0.01)\n\n                        image_aug = aug(image=image)\n\n                        assert image_aug.dtype.name == \"uint8\"\n                        assert image_aug.shape == shape\n\n    # --------\n    # get_parameters\n    # --------\n    def test_get_parameters(self):\n        aug = iaa.PerspectiveTransform(scale=0.1, keep_size=False)\n        params = aug.get_parameters()\n        assert is_parameter_instance(params[0], iap.Normal)\n        assert is_parameter_instance(params[0].scale, iap.Deterministic)\n        assert 0.1 - 1e-8 < params[0].scale.value < 0.1 + 1e-8\n        assert params[1] is False\n        assert params[2].value == 0\n        assert params[3].value == \"constant\"\n        assert params[4] is False\n\n    # --------\n    # other dtypes\n    # --------\n    def test_other_dtypes_bool(self):\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n\n        y1 = int(30 * 0.2)\n        y2 = int(30 * 0.8)\n        x1 = int(30 * 0.2)\n        x2 = int(30 * 0.8)\n\n        image = np.zeros((30, 30), dtype=bool)\n        image[12:18, :] = True\n        image[:, 12:18] = True\n        expected = image[y1:y2, x1:x2]\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.name == image.dtype.name\n        assert image_aug.shape == expected.shape\n        assert (np.sum(image_aug == expected) / expected.size) > 0.9\n\n    def test_other_dtypes_uint_int(self):\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n\n        y1 = int(30 * 0.2)\n        y2 = int(30 * 0.8)\n        x1 = int(30 * 0.2)\n        x2 = int(30 * 0.8)\n\n        dtypes = [\"uint8\", \"uint16\", \"int8\", \"int16\"]\n        for dtype in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            if np.dtype(dtype).kind == \"i\":\n                values = [0, 1, 5, 10, 100, int(0.1 * max_value),\n                          int(0.2 * max_value), int(0.5 * max_value),\n                          max_value-100, max_value]\n                values = values + [(-1)*value for value in values]\n            else:\n                values = [0, 1, 5, 10, 100, int(center_value),\n                          int(0.1 * max_value), int(0.2 * max_value),\n                          int(0.5 * max_value), max_value-100, max_value]\n\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((30, 30), dtype=dtype)\n                    image[12:18, :] = value\n                    image[:, 12:18] = value\n                    expected = image[y1:y2, x1:x2]\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert image_aug.shape == expected.shape\n                    # rather high tolerance of 0.7 here because of\n                    # interpolation\n                    assert (\n                        np.sum(image_aug == expected) / expected.size\n                    ) > 0.7\n\n    def test_other_dtypes_float(self):\n        aug = iaa.PerspectiveTransform(scale=0.2, keep_size=False)\n        aug.jitter = iap.Deterministic(0.2)\n\n        y1 = int(30 * 0.2)\n        y2 = int(30 * 0.8)\n        x1 = int(30 * 0.2)\n        x2 = int(30 * 0.8)\n\n        dtypes = [\"float16\", \"float32\", \"float64\"]\n        for dtype in dtypes:\n            def _isclose(a, b):\n                atol = 1e-4 if dtype == \"float16\" else 1e-8\n                return np.isclose(a, b, atol=atol, rtol=0)\n\n            isize = np.dtype(dtype).itemsize\n            values = [0.01, 1.0, 10.0, 100.0, 500 ** (isize - 1),\n                      1000 ** (isize - 1)]\n            values = values + [(-1) * value for value in values]\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((30, 30), dtype=dtype)\n                    image[12:18, :] = value\n                    image[:, 12:18] = value\n                    expected = image[y1:y2, x1:x2]\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert image_aug.shape == expected.shape\n                    # rather high tolerance of 0.7 here because of\n                    # interpolation\n                    assert (\n                        np.sum(_isclose(image_aug, expected)) / expected.size\n                    ) > 0.7\n\n    def test_pickleable(self):\n        aug = iaa.PerspectiveTransform(0.2, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=4, shape=(25, 25, 1))\n\n\nclass _elastic_trans_temp_thresholds(object):\n    def __init__(self, alpha, sigma):\n        self.alpha = alpha\n        self.sigma = sigma\n        self.old_alpha = None\n        self.old_sigma = None\n\n    def __enter__(self):\n        self.old_alpha = iaa.ElasticTransformation.KEYPOINT_AUG_ALPHA_THRESH\n        self.old_sigma = iaa.ElasticTransformation.KEYPOINT_AUG_SIGMA_THRESH\n        iaa.ElasticTransformation.KEYPOINT_AUG_ALPHA_THRESH = self.alpha\n        iaa.ElasticTransformation.KEYPOINT_AUG_SIGMA_THRESH = self.sigma\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        iaa.ElasticTransformation.KEYPOINT_AUG_ALPHA_THRESH = self.old_alpha\n        iaa.ElasticTransformation.KEYPOINT_AUG_SIGMA_THRESH = self.old_sigma\n\n\n# TODO add tests for order\n# TODO improve tests for cval\n# TODO add tests for mode\nclass TestElasticTransformation(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        img = np.zeros((50, 50), dtype=np.uint8) + 255\n        img = np.pad(img, ((100, 100), (100, 100)), mode=\"constant\",\n                     constant_values=0)\n        return img\n\n    @property\n    def mask(self):\n        img = self.image\n        mask = img > 0\n        return mask\n\n    @property\n    def heatmaps(self):\n        img = self.image\n        return HeatmapsOnImage(img.astype(np.float32) / 255.0,\n                               shape=img.shape)\n\n    @property\n    def segmaps(self):\n        img = self.image\n        return SegmentationMapsOnImage((img > 0).astype(np.int32),\n                                       shape=img.shape)\n\n    # -----------\n    # __init__\n    # -----------\n    def test___init___bad_datatype_for_alpha_leads_to_failure(self):\n        # test alpha having bad datatype\n        got_exception = False\n        try:\n            _ = iaa.ElasticTransformation(alpha=False, sigma=0.25)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test___init___alpha_is_tuple(self):\n        # test alpha being tuple\n        aug = iaa.ElasticTransformation(alpha=(1.0, 2.0), sigma=0.25)\n        assert is_parameter_instance(aug.alpha, iap.Uniform)\n        assert is_parameter_instance(aug.alpha.a, iap.Deterministic)\n        assert is_parameter_instance(aug.alpha.b, iap.Deterministic)\n        assert 1.0 - 1e-8 < aug.alpha.a.value < 1.0 + 1e-8\n        assert 2.0 - 1e-8 < aug.alpha.b.value < 2.0 + 1e-8\n\n    def test___init___sigma_is_tuple(self):\n        # test sigma being tuple\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=(1.0, 2.0))\n        assert is_parameter_instance(aug.sigma, iap.Uniform)\n        assert is_parameter_instance(aug.sigma.a, iap.Deterministic)\n        assert is_parameter_instance(aug.sigma.b, iap.Deterministic)\n        assert 1.0 - 1e-8 < aug.sigma.a.value < 1.0 + 1e-8\n        assert 2.0 - 1e-8 < aug.sigma.b.value < 2.0 + 1e-8\n\n    def test___init___bad_datatype_for_sigma_leads_to_failure(self):\n        # test sigma having bad datatype\n        got_exception = False\n        try:\n            _ = iaa.ElasticTransformation(alpha=0.25, sigma=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test___init___order_is_all(self):\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=1.0, order=ia.ALL)\n        assert is_parameter_instance(aug.order, iap.Choice)\n        assert all([order in aug.order.a for order in [0, 1, 2, 3, 4, 5]])\n\n    def test___init___order_is_int(self):\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=1.0, order=1)\n        assert is_parameter_instance(aug.order, iap.Deterministic)\n        assert aug.order.value == 1\n\n    def test___init___order_is_list(self):\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=1.0, order=[0, 1, 2])\n        assert is_parameter_instance(aug.order, iap.Choice)\n        assert all([order in aug.order.a for order in [0, 1, 2]])\n\n    def test___init___order_is_stochastic_parameter(self):\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=1.0,\n                                        order=iap.Choice([0, 1, 2, 3]))\n        assert is_parameter_instance(aug.order, iap.Choice)\n        assert all([order in aug.order.a for order in [0, 1, 2, 3]])\n\n    def test___init___bad_datatype_for_order_leads_to_failure(self):\n        got_exception = False\n        try:\n            _ = iaa.ElasticTransformation(alpha=0.25, sigma=1.0, order=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test___init___cval_is_all(self):\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=1.0, cval=ia.ALL)\n        assert is_parameter_instance(aug.cval, iap.Uniform)\n        assert is_parameter_instance(aug.cval.a, iap.Deterministic)\n        assert is_parameter_instance(aug.cval.b, iap.Deterministic)\n        assert aug.cval.a.value == 0\n        assert aug.cval.b.value == 255\n\n    def test___init___cval_is_int(self):\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=1.0, cval=128)\n        assert is_parameter_instance(aug.cval, iap.Deterministic)\n        assert aug.cval.value == 128\n\n    def test___init___cval_is_list(self):\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=1.0,\n                                        cval=[16, 32, 64])\n        assert is_parameter_instance(aug.cval, iap.Choice)\n        assert all([cval in aug.cval.a for cval in [16, 32, 64]])\n\n    def test___init___cval_is_stochastic_parameter(self):\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=1.0,\n                                        cval=iap.Choice([16, 32, 64]))\n        assert is_parameter_instance(aug.cval, iap.Choice)\n        assert all([cval in aug.cval.a for cval in [16, 32, 64]])\n\n    def test___init___cval_is_tuple(self):\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=1.0, cval=(128, 255))\n        assert is_parameter_instance(aug.cval, iap.Uniform)\n        assert is_parameter_instance(aug.cval.a, iap.Deterministic)\n        assert is_parameter_instance(aug.cval.b, iap.Deterministic)\n        assert aug.cval.a.value == 128\n        assert aug.cval.b.value == 255\n\n    def test___init___bad_datatype_for_cval_leads_to_failure(self):\n        got_exception = False\n        try:\n            _ = iaa.ElasticTransformation(alpha=0.25, sigma=1.0, cval=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test___init___mode_is_all(self):\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=1.0, mode=ia.ALL)\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert all([\n            mode in aug.mode.a\n            for mode\n            in [\"constant\", \"nearest\", \"reflect\", \"wrap\"]])\n\n    def test___init___mode_is_string(self):\n        aug = iaa.ElasticTransformation(alpha=0.25, sigma=1.0, mode=\"nearest\")\n        assert is_parameter_instance(aug.mode, iap.Deterministic)\n        assert aug.mode.value == \"nearest\"\n\n    def test___init___mode_is_list(self):\n        aug = iaa.ElasticTransformation(\n            alpha=0.25, sigma=1.0, mode=[\"constant\", \"nearest\"])\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert all([mode in aug.mode.a for mode in [\"constant\", \"nearest\"]])\n\n    def test___init___mode_is_stochastic_parameter(self):\n        aug = iaa.ElasticTransformation(\n            alpha=0.25, sigma=1.0, mode=iap.Choice([\"constant\", \"nearest\"]))\n        assert is_parameter_instance(aug.mode, iap.Choice)\n        assert all([mode in aug.mode.a for mode in [\"constant\", \"nearest\"]])\n\n    def test___init___bad_datatype_for_mode_leads_to_failure(self):\n        got_exception = False\n        try:\n            _ = iaa.ElasticTransformation(alpha=0.25, sigma=1.0, mode=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    # -----------\n    # alpha, sigma\n    # -----------\n    def test_images(self):\n        # test basic funtionality\n        aug = iaa.ElasticTransformation(alpha=5, sigma=0.25)\n\n        observed = aug.augment_image(self.image)\n\n        mask = self.mask\n        # assume that some white/255 pixels have been moved away from the\n        # center and replaced by black/0 pixels\n        assert np.sum(observed[mask]) < np.sum(self.image[mask])\n        # assume that some black/0 pixels have been moved away from the outer\n        # area and replaced by white/255 pixels\n        assert np.sum(observed[~mask]) > np.sum(self.image[~mask])\n\n    def test_images_nonsquare(self):\n        # test basic funtionality with non-square images\n        aug = iaa.ElasticTransformation(alpha=2.0, sigma=0.25, order=3)\n        img_nonsquare = np.zeros((50, 100), dtype=np.uint8) + 255\n        img_nonsquare = np.pad(img_nonsquare, ((100, 100), (100, 100)),\n                               mode=\"constant\", constant_values=0)\n        mask_nonsquare = (img_nonsquare > 0)\n\n        observed = aug.augment_image(img_nonsquare)\n\n        assert (\n            np.sum(observed[mask_nonsquare])\n            < np.sum(img_nonsquare[mask_nonsquare]))\n        assert (\n            np.sum(observed[~mask_nonsquare])\n            > np.sum(img_nonsquare[~mask_nonsquare]))\n\n    def test_images_unusual_channel_numbers(self):\n        # test unusual channels numbers\n        aug = iaa.ElasticTransformation(alpha=5, sigma=0.5)\n        for nb_channels in [1, 2, 4, 5, 7, 10, 11]:\n            img_c = np.tile(self.image[..., np.newaxis], (1, 1, nb_channels))\n            assert img_c.shape == (250, 250, nb_channels)\n\n            observed = aug.augment_image(img_c)\n\n            assert observed.shape == (250, 250, nb_channels)\n            for c in sm.xrange(1, nb_channels):\n                assert np.array_equal(observed[..., c], observed[..., 0])\n\n    def test_heatmaps(self):\n        # test basic funtionality, heatmaps\n        aug = iaa.ElasticTransformation(alpha=0.5, sigma=0.25)\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n\n        mask = self.mask\n        assert observed.shape == self.heatmaps.shape\n        _assert_same_min_max(observed, self.heatmaps)\n        assert (\n            np.sum(observed.get_arr()[mask])\n            < np.sum(self.heatmaps.get_arr()[mask]))\n        assert (\n            np.sum(observed.get_arr()[~mask])\n            > np.sum(self.heatmaps.get_arr()[~mask]))\n\n    def test_segmaps(self):\n        # test basic funtionality, segmaps\n        # alpha=1.5 instead of 0.5 as above here, because otherwise nothing\n        # is moved\n        aug = iaa.ElasticTransformation(alpha=1.5, sigma=0.25)\n\n        observed = aug.augment_segmentation_maps([self.segmaps])[0]\n\n        mask = self.mask\n        assert observed.shape == self.segmaps.shape\n        assert (\n            np.sum(observed.get_arr()[mask])\n            < np.sum(self.segmaps.get_arr()[mask]))\n        assert (\n            np.sum(observed.get_arr()[~mask])\n            > np.sum(self.segmaps.get_arr()[~mask]))\n\n    def test_images_weak_vs_strong_alpha(self):\n        # test effects of increased alpha strength\n        aug1 = iaa.ElasticTransformation(alpha=0.1, sigma=0.25)\n        aug2 = iaa.ElasticTransformation(alpha=5.0, sigma=0.25)\n\n        observed1 = aug1.augment_image(self.image)\n        observed2 = aug2.augment_image(self.image)\n\n        mask = self.mask\n        # assume that the inner area has become more black-ish when using high\n        # alphas (more white pixels were moved out of the inner area)\n        assert np.sum(observed1[mask]) > np.sum(observed2[mask])\n        # assume that the outer area has become more white-ish when using high\n        # alphas (more black pixels were moved into the inner area)\n        assert np.sum(observed1[~mask]) < np.sum(observed2[~mask])\n\n    def test_heatmaps_weak_vs_strong_alpha(self):\n        # test effects of increased alpha strength, heatmaps\n        aug1 = iaa.ElasticTransformation(alpha=0.1, sigma=0.25)\n        aug2 = iaa.ElasticTransformation(alpha=5.0, sigma=0.25)\n\n        observed1 = aug1.augment_heatmaps([self.heatmaps])[0]\n        observed2 = aug2.augment_heatmaps([self.heatmaps])[0]\n\n        mask = self.mask\n        assert observed1.shape == self.heatmaps.shape\n        assert observed2.shape == self.heatmaps.shape\n        _assert_same_min_max(observed1, self.heatmaps)\n        _assert_same_min_max(observed2, self.heatmaps)\n        assert (\n            np.sum(observed1.get_arr()[mask])\n            > np.sum(observed2.get_arr()[mask]))\n        assert (\n            np.sum(observed1.get_arr()[~mask])\n            < np.sum(observed2.get_arr()[~mask]))\n\n    def test_segmaps_weak_vs_strong_alpha(self):\n        # test effects of increased alpha strength, segmaps\n        aug1 = iaa.ElasticTransformation(alpha=0.1, sigma=0.25)\n        aug2 = iaa.ElasticTransformation(alpha=5.0, sigma=0.25)\n\n        observed1 = aug1.augment_segmentation_maps([self.segmaps])[0]\n        observed2 = aug2.augment_segmentation_maps([self.segmaps])[0]\n\n        mask = self.mask\n        assert observed1.shape == self.segmaps.shape\n        assert observed2.shape == self.segmaps.shape\n        assert (\n            np.sum(observed1.get_arr()[mask])\n            > np.sum(observed2.get_arr()[mask]))\n        assert (\n            np.sum(observed1.get_arr()[~mask])\n            < np.sum(observed2.get_arr()[~mask]))\n\n    def test_images_low_vs_high_sigma(self):\n        # test effects of increased sigmas\n        aug1 = iaa.ElasticTransformation(alpha=3.0, sigma=0.1)\n        aug2 = iaa.ElasticTransformation(alpha=3.0, sigma=3.0)\n\n        observed1 = aug1.augment_image(self.image)\n        observed2 = aug2.augment_image(self.image)\n\n        observed1_std_hori = np.std(\n            observed1.astype(np.float32)[:, 1:]\n            - observed1.astype(np.float32)[:, :-1])\n        observed2_std_hori = np.std(\n            observed2.astype(np.float32)[:, 1:]\n            - observed2.astype(np.float32)[:, :-1])\n        observed1_std_vert = np.std(\n            observed1.astype(np.float32)[1:, :]\n            - observed1.astype(np.float32)[:-1, :])\n        observed2_std_vert = np.std(\n            observed2.astype(np.float32)[1:, :]\n            - observed2.astype(np.float32)[:-1, :])\n        observed1_std = (observed1_std_hori + observed1_std_vert) / 2\n        observed2_std = (observed2_std_hori + observed2_std_vert) / 2\n        assert observed1_std > observed2_std\n\n    def test_images_alpha_is_stochastic_parameter(self):\n        # test alpha being iap.Choice\n        aug = iaa.ElasticTransformation(alpha=iap.Choice([0.001, 5.0]),\n                                        sigma=0.25)\n        seen = [0, 0]\n        for _ in sm.xrange(100):\n            observed = aug.augment_image(self.image)\n            diff = np.average(\n                np.abs(\n                    self.image.astype(np.float32)\n                    - observed.astype(np.float32)\n                )\n            )\n            if diff < 1.0:\n                seen[0] += 1\n            else:\n                seen[1] += 1\n        assert seen[0] > 10\n        assert seen[1] > 10\n\n    def test_sigma_is_stochastic_parameter(self):\n        # test sigma being iap.Choice\n        for order in [0, 1, 3]:\n            with self.subTest(order=order):\n                aug = iaa.ElasticTransformation(alpha=50.0,\n                                                sigma=iap.Choice([0.001, 5.0]),\n                                                order=order)\n                seen = [0, 0]\n                for _ in sm.xrange(100):\n                    observed = aug.augment_image(self.image)\n\n                    observed_std_hori = np.std(\n                        observed.astype(np.float32)[:, 1:]\n                        - observed.astype(np.float32)[:, :-1])\n                    observed_std_vert = np.std(\n                        observed.astype(np.float32)[1:, :]\n                        - observed.astype(np.float32)[:-1, :])\n                    observed_std = (observed_std_hori + observed_std_vert) / 2\n\n                    if observed_std > 25.0:\n                        seen[0] += 1\n                    else:\n                        seen[1] += 1\n                assert seen[0] > 10\n                assert seen[1] > 10\n\n    # -----------\n    # cval\n    # -----------\n    def test_images_cval_is_int_and_order_is_0(self):\n        aug = iaa.ElasticTransformation(alpha=30.0, sigma=3.0, mode=\"constant\",\n                                        cval=255, order=0)\n        img = np.zeros((100, 100), dtype=np.uint8)\n\n        observed = aug.augment_image(img)\n\n        assert np.sum(observed == 255) > 0\n        assert np.sum(np.logical_and(0 < observed, observed < 255)) == 0\n\n    def test_images_cval_is_int_and_order_is_0_weak_alpha(self):\n        aug = iaa.ElasticTransformation(alpha=3.0, sigma=3.0, mode=\"constant\",\n                                        cval=0, order=0)\n        img = np.zeros((100, 100), dtype=np.uint8)\n\n        observed = aug.augment_image(img)\n\n        assert np.sum(observed == 255) == 0\n\n    def test_images_cval_is_int_and_order_is_2(self):\n        aug = iaa.ElasticTransformation(alpha=3.0, sigma=3.0, mode=\"constant\",\n                                        cval=255, order=2)\n        img = np.zeros((100, 100), dtype=np.uint8)\n\n        observed = aug.augment_image(img)\n\n        assert np.sum(np.logical_and(0 < observed, observed < 255)) > 0\n\n    def test_images_cval_is_int_image_hw3(self):\n        aug = iaa.ElasticTransformation(alpha=5.0, sigma=3.0, mode=\"constant\",\n                                        cval=255, order=0)\n        img = np.zeros((100, 100, 3), dtype=np.uint8)\n\n        observed = aug.augment_image(img)\n\n        count_255 = np.sum(observed == 255, axis=2)\n        mask_not_all_channels_same_intensity = np.logical_and(\n            count_255 > 0, count_255 < 3)\n        mask_all_channels_same_intensity = (count_255 == 3)\n        assert not np.any(mask_not_all_channels_same_intensity)\n        assert np.any(mask_all_channels_same_intensity)\n\n    def test_heatmaps_ignore_cval(self):\n        # cval with heatmaps\n        heatmaps = HeatmapsOnImage(\n            np.zeros((32, 32, 1), dtype=np.float32), shape=(32, 32, 3))\n        aug = iaa.ElasticTransformation(alpha=3.0, sigma=3.0,\n                                        mode=\"constant\", cval=255)\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        assert observed.shape == heatmaps.shape\n        _assert_same_min_max(observed, heatmaps)\n        assert np.sum(observed.get_arr() > 0.01) == 0\n\n    def test_segmaps_ignore_cval(self):\n        # cval with segmaps\n        segmaps = SegmentationMapsOnImage(\n            np.zeros((32, 32, 1), dtype=np.int32), shape=(32, 32, 3))\n        aug = iaa.ElasticTransformation(alpha=3.0, sigma=3.0, mode=\"constant\",\n                                        cval=255)\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        assert observed.shape == segmaps.shape\n        assert np.sum(observed.get_arr() > 0) == 0\n\n    # -----------\n    # keypoints\n    # -----------\n    def test_keypoints_no_movement_if_alpha_below_threshold(self):\n        # for small alpha, should not move if below threshold\n        with _elastic_trans_temp_thresholds(alpha=1.0, sigma=0.0):\n            kps = [\n                ia.Keypoint(x=1, y=1), ia.Keypoint(x=15, y=25),\n                ia.Keypoint(x=5, y=5), ia.Keypoint(x=7, y=4),\n                ia.Keypoint(x=48, y=5), ia.Keypoint(x=21, y=37),\n                ia.Keypoint(x=32, y=39), ia.Keypoint(x=6, y=8),\n                ia.Keypoint(x=12, y=21), ia.Keypoint(x=3, y=45),\n                ia.Keypoint(x=45, y=3), ia.Keypoint(x=7, y=48)]\n            kpsoi = ia.KeypointsOnImage(kps, shape=(50, 50))\n            aug = iaa.ElasticTransformation(alpha=0.001, sigma=1.0)\n    \n            observed = aug.augment_keypoints([kpsoi])[0]\n    \n            d = kpsoi.to_xy_array() - observed.to_xy_array()\n            d[:, 0] = d[:, 0] ** 2\n            d[:, 1] = d[:, 1] ** 2\n            d = np.sum(d, axis=1)\n            d = np.average(d, axis=0)\n            assert d < 1e-8\n\n    def test_keypoints_no_movement_if_sigma_below_threshold(self):\n        # for small sigma, should not move if below threshold\n        with _elastic_trans_temp_thresholds(alpha=0.0, sigma=1.0):\n            kps = [\n                ia.Keypoint(x=1, y=1), ia.Keypoint(x=15, y=25),\n                ia.Keypoint(x=5, y=5), ia.Keypoint(x=7, y=4),\n                ia.Keypoint(x=48, y=5), ia.Keypoint(x=21, y=37),\n                ia.Keypoint(x=32, y=39), ia.Keypoint(x=6, y=8),\n                ia.Keypoint(x=12, y=21), ia.Keypoint(x=3, y=45),\n                ia.Keypoint(x=45, y=3), ia.Keypoint(x=7, y=48)]\n            kpsoi = ia.KeypointsOnImage(kps, shape=(50, 50))\n            aug = iaa.ElasticTransformation(alpha=1.0, sigma=0.001)\n\n            observed = aug.augment_keypoints([kpsoi])[0]\n\n            d = kpsoi.to_xy_array() - observed.to_xy_array()\n            d[:, 0] = d[:, 0] ** 2\n            d[:, 1] = d[:, 1] ** 2\n            d = np.sum(d, axis=1)\n            d = np.average(d, axis=0)\n            assert d < 1e-8\n\n    def test_keypoints_small_movement_for_weak_alpha_if_threshold_zero(self):\n        # for small alpha (at sigma 1.0), should barely move\n        # if thresholds set to zero\n        with _elastic_trans_temp_thresholds(alpha=0.0, sigma=0.0):\n            kps = [\n                ia.Keypoint(x=1, y=1), ia.Keypoint(x=15, y=25),\n                ia.Keypoint(x=5, y=5), ia.Keypoint(x=7, y=4),\n                ia.Keypoint(x=48, y=5), ia.Keypoint(x=21, y=37),\n                ia.Keypoint(x=32, y=39), ia.Keypoint(x=6, y=8),\n                ia.Keypoint(x=12, y=21), ia.Keypoint(x=3, y=45),\n                ia.Keypoint(x=45, y=3), ia.Keypoint(x=7, y=48)]\n            kpsoi = ia.KeypointsOnImage(kps, shape=(50, 50))\n            aug = iaa.ElasticTransformation(alpha=0.001, sigma=1.0)\n\n            observed = aug.augment_keypoints([kpsoi])[0]\n\n            d = kpsoi.to_xy_array() - observed.to_xy_array()\n            d[:, 0] = d[:, 0] ** 2\n            d[:, 1] = d[:, 1] ** 2\n            d = np.sum(d, axis=1)\n            d = np.average(d, axis=0)\n            assert d < 0.5\n\n    def test_image_keypoint_alignment(self):\n        # test alignment between between images and keypoints\n        image = np.zeros((120, 70), dtype=np.uint8)\n        s = 3\n        image[:, 35-s:35+s+1] = 255\n        kps = [ia.Keypoint(x=35, y=20),\n               ia.Keypoint(x=35, y=40),\n               ia.Keypoint(x=35, y=60),\n               ia.Keypoint(x=35, y=80),\n               ia.Keypoint(x=35, y=100)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=image.shape)\n        aug = iaa.ElasticTransformation(alpha=70, sigma=5)\n        aug_det = aug.to_deterministic()\n\n        images_aug = aug_det.augment_images([image, image])\n        kpsois_aug = aug_det.augment_keypoints([kpsoi, kpsoi])\n\n        count_bad = 0\n        for image_aug, kpsoi_aug in zip(images_aug, kpsois_aug):\n            assert kpsoi_aug.shape == (120, 70)\n            assert len(kpsoi_aug.keypoints) == 5\n            for kp_aug in kpsoi_aug.keypoints:\n                x, y = int(np.round(kp_aug.x)), int(np.round(kp_aug.y))\n                bb = ia.BoundingBox(x1=x-2, x2=x+2+1, y1=y-2, y2=y+2+1)\n                img_ex = bb.extract_from_image(image_aug)\n                if np.any(img_ex > 10):\n                    pass  # close to expected location\n                else:\n                    count_bad += 1\n        assert count_bad <= 1\n\n    def test_empty_keypoints(self):\n        aug = iaa.ElasticTransformation(alpha=10, sigma=10)\n        kpsoi = ia.KeypointsOnImage([], shape=(10, 10, 3))\n\n        kpsoi_aug = aug.augment_keypoints(kpsoi)\n\n        assert len(kpsoi_aug.keypoints) == 0\n        assert kpsoi_aug.shape == (10, 10, 3)\n\n    # -----------\n    # abstract methods for polygons and line strings\n    # -----------\n    @classmethod\n    def _test_cbaois_no_movement_if_alpha_below_threshold(\n            cls, cba_class, cbaoi_class, augf_name):\n        # for small alpha, should not move if below threshold\n        with _elastic_trans_temp_thresholds(alpha=1.0, sigma=0.0):\n            cba = cba_class([(10, 15), (40, 15), (40, 35), (10, 35)])\n            cbaoi = cbaoi_class([cba], shape=(50, 50))\n            aug = iaa.ElasticTransformation(alpha=0.001, sigma=1.0)\n\n            observed = getattr(aug, augf_name)(cbaoi)\n\n            assert observed.shape == (50, 50)\n            assert len(observed.items) == 1\n            assert observed.items[0].coords_almost_equals(cba)\n            if hasattr(observed.items[0], \"is_valid\"):\n                assert observed.items[0].is_valid\n\n    @classmethod\n    def _test_cbaois_no_movement_if_sigma_below_threshold(\n            cls, cba_class, cbaoi_class, augf_name):\n        # for small sigma, should not move if below threshold\n        with _elastic_trans_temp_thresholds(alpha=0.0, sigma=1.0):\n            cba = cba_class([(10, 15), (40, 15), (40, 35), (10, 35)])\n            cbaoi = cbaoi_class([cba], shape=(50, 50))\n            aug = iaa.ElasticTransformation(alpha=1.0, sigma=0.001)\n\n            observed = getattr(aug, augf_name)(cbaoi)\n\n            assert observed.shape == (50, 50)\n            assert len(observed.items) == 1\n            assert observed.items[0].coords_almost_equals(cba)\n            if hasattr(observed.items[0], \"is_valid\"):\n                assert observed.items[0].is_valid\n\n    @classmethod\n    def _test_cbaois_small_movement_for_weak_alpha_if_threshold_zero(\n            cls, cba_class, cbaoi_class, augf_name):\n        # for small alpha (at sigma 1.0), should barely move\n        # if thresholds set to zero\n        with _elastic_trans_temp_thresholds(alpha=0.0, sigma=0.0):\n            cba = cba_class([(10, 15), (40, 15), (40, 35), (10, 35)])\n            cbaoi = cbaoi_class([cba], shape=(50, 50))\n            aug = iaa.ElasticTransformation(alpha=0.001, sigma=1.0)\n\n            observed = getattr(aug, augf_name)(cbaoi)\n\n            assert observed.shape == (50, 50)\n            assert len(observed.items) == 1\n            assert observed.items[0].coords_almost_equals(\n                cba, max_distance=0.5)\n            if hasattr(observed.items[0], \"is_valid\"):\n                assert observed.items[0].is_valid\n\n    @classmethod\n    def _test_image_cbaoi_alignment(cls, cba_class, cbaoi_class, augf_name):\n        # test alignment between between images and polygons\n        height_step_size = 50\n        width_step_size = 30\n        height_steps = 2  # don't set >2, otherwise polygon will be broken\n        width_steps = 10\n        height = (2+height_steps) * height_step_size\n        width = (2+width_steps) * width_step_size\n        s = 3\n\n        image = np.zeros((height, width), dtype=np.uint8)\n\n        points = []\n        for w in sm.xrange(0, 2+width_steps):\n            if w not in [0, width_steps+2-1]:\n                x = width_step_size * w\n                y = height_step_size\n                points.append((x, y))\n                image[y-s:y+s+1, x-s:x+s+1] = 255\n        for w in sm.xrange(2+width_steps-1, 0, -1):\n            if w not in [0, width_steps+2-1]:\n                x = width_step_size * w\n                y = height_step_size*2\n                points.append((x, y))\n                image[y-s:y+s+1, x-s:x+s+1] = 255\n\n        cba = cba_class(points)\n        cbaoi = cbaoi_class([cba], shape=image.shape)\n        aug = iaa.ElasticTransformation(alpha=100, sigma=7)\n        aug_det = aug.to_deterministic()\n\n        images_aug = aug_det.augment_images([image, image])\n        cbaois_aug = getattr(aug_det, augf_name)([cbaoi, cbaoi])\n\n        count_bad = 0\n        for image_aug, cbaoi_aug in zip(images_aug, cbaois_aug):\n            assert cbaoi_aug.shape == image.shape\n            assert len(cbaoi_aug.items) == 1\n            for cba_aug in cbaoi_aug.items:\n                if hasattr(cba_aug, \"is_valid\"):\n                    assert cba_aug.is_valid\n                for point_aug in cba_aug.coords:\n                    x, y = point_aug[0], point_aug[1]\n                    bb = ia.BoundingBox(x1=x-2, x2=x+2, y1=y-2, y2=y+2)\n                    img_ex = bb.extract_from_image(image_aug)\n                    if np.any(img_ex > 10):\n                        pass  # close to expected location\n                    else:\n                        count_bad += 1\n        assert count_bad <= 3\n\n    @classmethod\n    def _test_empty_cbaois(cls, cbaoi, augf_name):\n        aug = iaa.ElasticTransformation(alpha=10, sigma=10)\n\n        cbaoi_aug = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(cbaoi_aug, cbaoi)\n\n    # -----------\n    # polygons\n    # -----------\n    def test_polygons_no_movement_if_alpha_below_threshold(self):\n        self._test_cbaois_no_movement_if_alpha_below_threshold(\n            ia.Polygon, ia.PolygonsOnImage, \"augment_polygons\")\n\n    def test_polygons_no_movement_if_sigma_below_threshold(self):\n        self._test_cbaois_no_movement_if_sigma_below_threshold(\n            ia.Polygon, ia.PolygonsOnImage, \"augment_polygons\")\n\n    def test_polygons_small_movement_for_weak_alpha_if_threshold_zero(self):\n        self._test_cbaois_small_movement_for_weak_alpha_if_threshold_zero(\n            ia.Polygon, ia.PolygonsOnImage, \"augment_polygons\")\n\n    def test_image_polygon_alignment(self):\n        self._test_image_cbaoi_alignment(\n            ia.Polygon, ia.PolygonsOnImage, \"augment_polygons\")\n\n    def test_empty_polygons(self):\n        cbaoi = ia.PolygonsOnImage([], shape=(10, 10, 3))\n        self._test_empty_cbaois(cbaoi, \"augment_polygons\")\n\n    # -----------\n    # line strings\n    # -----------\n    def test_line_strings_no_movement_if_alpha_below_threshold(self):\n        self._test_cbaois_no_movement_if_alpha_below_threshold(\n            ia.LineString, ia.LineStringsOnImage, \"augment_line_strings\")\n\n    def test_line_strings_no_movement_if_sigma_below_threshold(self):\n        self._test_cbaois_no_movement_if_sigma_below_threshold(\n            ia.LineString, ia.LineStringsOnImage, \"augment_line_strings\")\n\n    def test_line_strings_small_movement_for_weak_alpha_if_threshold_zero(self):\n        self._test_cbaois_small_movement_for_weak_alpha_if_threshold_zero(\n            ia.LineString, ia.LineStringsOnImage, \"augment_line_strings\")\n\n    def test_image_line_string_alignment(self):\n        self._test_image_cbaoi_alignment(\n            ia.LineString, ia.LineStringsOnImage, \"augment_line_strings\")\n\n    def test_empty_line_strings(self):\n        cbaoi = ia.LineStringsOnImage([], shape=(10, 10, 3))\n        self._test_empty_cbaois(cbaoi, \"augment_line_strings\")\n\n    # -----------\n    # bounding boxes\n    # -----------\n    def test_bounding_boxes_no_movement_if_alpha_below_threshold(self):\n        # for small alpha, should not move if below threshold\n        with _elastic_trans_temp_thresholds(alpha=1.0, sigma=0.0):\n            bbs = [\n                ia.BoundingBox(x1=10, y1=12, x2=20, y2=22),\n                ia.BoundingBox(x1=20, y1=32, x2=40, y2=42)\n            ]\n            bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(50, 50))\n            aug = iaa.ElasticTransformation(alpha=0.001, sigma=1.0)\n\n            observed = aug.augment_bounding_boxes([bbsoi])[0]\n\n            d = bbsoi.to_xyxy_array() - observed.to_xyxy_array()\n            d = d.reshape((2*2, 2))\n            d[:, 0] = d[:, 0] ** 2\n            d[:, 1] = d[:, 1] ** 2\n            d = np.sum(d, axis=1)\n            d = np.average(d, axis=0)\n            assert d < 1e-8\n\n    def test_bounding_boxes_no_movement_if_sigma_below_threshold(self):\n        # for small sigma, should not move if below threshold\n        with _elastic_trans_temp_thresholds(alpha=0.0, sigma=1.0):\n            bbs = [\n                ia.BoundingBox(x1=10, y1=12, x2=20, y2=22),\n                ia.BoundingBox(x1=20, y1=32, x2=40, y2=42)\n            ]\n            bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(50, 50))\n            aug = iaa.ElasticTransformation(alpha=1.0, sigma=0.001)\n\n            observed = aug.augment_bounding_boxes([bbsoi])[0]\n\n            d = bbsoi.to_xyxy_array() - observed.to_xyxy_array()\n            d = d.reshape((2*2, 2))\n            d[:, 0] = d[:, 0] ** 2\n            d[:, 1] = d[:, 1] ** 2\n            d = np.sum(d, axis=1)\n            d = np.average(d, axis=0)\n            assert d < 1e-8\n\n    def test_bounding_boxes_small_movement_for_weak_alpha_if_threshold_zero(\n            self):\n        # for small alpha (at sigma 1.0), should barely move\n        # if thresholds set to zero\n        with _elastic_trans_temp_thresholds(alpha=0.0, sigma=0.0):\n            bbs = [\n                ia.BoundingBox(x1=10, y1=12, x2=20, y2=22),\n                ia.BoundingBox(x1=20, y1=32, x2=40, y2=42)\n            ]\n            bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(50, 50))\n            aug = iaa.ElasticTransformation(alpha=0.001, sigma=1.0)\n\n            observed = aug.augment_bounding_boxes([bbsoi])[0]\n\n            d = bbsoi.to_xyxy_array() - observed.to_xyxy_array()\n            d = d.reshape((2*2, 2))\n            d[:, 0] = d[:, 0] ** 2\n            d[:, 1] = d[:, 1] ** 2\n            d = np.sum(d, axis=1)\n            d = np.average(d, axis=0)\n            assert d < 0.5\n\n    def test_image_bounding_box_alignment(self):\n        # test alignment between between images and bounding boxes\n        image = np.zeros((100, 100), dtype=np.uint8)\n        image[35:35+1, 35:65+1] = 255\n        image[65:65+1, 35:65+1] = 255\n        image[35:65+1, 35:35+1] = 255\n        image[35:65+1, 65:65+1] = 255\n        bbs = [\n            ia.BoundingBox(x1=35.5, y1=35.5, x2=65.5, y2=65.5)\n        ]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=image.shape)\n        aug = iaa.ElasticTransformation(alpha=70, sigma=5)\n\n        images_aug, bbsois_aug = aug(images=[image, image],\n                                     bounding_boxes=[bbsoi, bbsoi])\n\n        count_bad = 0\n        for image_aug, bbsoi_aug in zip(images_aug, bbsois_aug):\n            assert bbsoi_aug.shape == (100, 100)\n            assert len(bbsoi_aug.bounding_boxes) == 1\n            for bb_aug in bbsoi_aug.bounding_boxes:\n                if bb_aug.is_fully_within_image(image_aug):\n                    # top, bottom, left, right\n                    x1 = bb_aug.x1_int\n                    x2 = bb_aug.x2_int\n                    y1 = bb_aug.y1_int\n                    y2 = bb_aug.y2_int\n                    top_row = image_aug[y1-2:y1+2, x1-2:x2+2]\n                    btm_row = image_aug[y2-2:y2+2, x1-2:x2+2]\n                    lft_row = image_aug[y1-2:y2+2, x1-2:x1+2]\n                    rgt_row = image_aug[y1-2:y2+2, x2-2:x2+2]\n                    assert np.max(top_row) > 10\n                    assert np.max(btm_row) > 10\n                    assert np.max(lft_row) > 10\n                    assert np.max(rgt_row) > 10\n                else:\n                    count_bad += 1\n        assert count_bad <= 1\n\n    def test_empty_bounding_boxes(self):\n        aug = iaa.ElasticTransformation(alpha=10, sigma=10)\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(10, 10, 3))\n\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n\n        assert len(bbsoi_aug.bounding_boxes) == 0\n        assert bbsoi_aug.shape == (10, 10, 3)\n\n    # -----------\n    # heatmaps alignment\n    # -----------\n    def test_image_heatmaps_alignment(self):\n        # test alignment between images and heatmaps\n        for order in [0, 1, 3]:\n            with self.subTest(order=order):\n                img = np.zeros((80, 80), dtype=np.uint8)\n                img[:, 30:50] = 255\n                img[30:50, :] = 255\n                hm = HeatmapsOnImage(img.astype(np.float32)/255.0, shape=(80, 80))\n                aug = iaa.ElasticTransformation(\n                    alpha=60.0,\n                    sigma=4.0,\n                    mode=\"constant\",\n                    cval=0,\n                    order=order\n                )\n                aug_det = aug.to_deterministic()\n\n                img_aug = aug_det.augment_image(img)\n                hm_aug = aug_det.augment_heatmaps([hm])[0]\n\n                img_aug_mask = img_aug > 255*0.1\n                hm_aug_mask = hm_aug.arr_0to1 > 0.1\n                same = np.sum(img_aug_mask == hm_aug_mask[:, :, 0])\n                assert hm_aug.shape == (80, 80)\n                assert hm_aug.arr_0to1.shape == (80, 80, 1)\n                assert (same / img_aug_mask.size) >= 0.97\n\n    def test_image_heatmaps_alignment_if_heatmaps_smaller_than_image(self):\n        # test alignment between images and heatmaps\n        # here with heatmaps that are smaller than the image\n        for order in [0, 1, 3]:\n            with self.subTest(order=order):\n                img = np.zeros((80, 80), dtype=np.uint8)\n                img[:, 30:50] = 255\n                img[30:50, :] = 255\n                img_small = ia.imresize_single_image(\n                    img, (40, 40), interpolation=\"nearest\")\n                hm = HeatmapsOnImage(\n                    img_small.astype(np.float32)/255.0,\n                    shape=(80, 80))\n                aug = iaa.ElasticTransformation(\n                    alpha=60.0, sigma=4.0, mode=\"constant\", cval=0)\n                aug_det = aug.to_deterministic()\n\n                img_aug = aug_det.augment_image(img)\n                hm_aug = aug_det.augment_heatmaps([hm])[0]\n\n                img_aug_mask = img_aug > 255*0.1\n                hm_aug_mask = ia.imresize_single_image(\n                    hm_aug.arr_0to1, (80, 80), interpolation=\"nearest\"\n                ) > 0.1\n                same = np.sum(img_aug_mask == hm_aug_mask[:, :, 0])\n                assert hm_aug.shape == (80, 80)\n                assert hm_aug.arr_0to1.shape == (40, 40, 1)\n                # TODO this is a fairly low threshold, why is that the case?\n                assert (same / img_aug_mask.size) >= 0.9\n\n    # -----------\n    # segmaps alignment\n    # -----------\n    def test_image_segmaps_alignment(self):\n        # test alignment between images and segmaps\n        img = np.zeros((80, 80), dtype=np.uint8)\n        img[:, 30:50] = 255\n        img[30:50, :] = 255\n        segmaps = SegmentationMapsOnImage(\n            (img > 0).astype(np.int32),\n            shape=(80, 80))\n        aug = iaa.ElasticTransformation(\n            alpha=60.0, sigma=4.0, mode=\"constant\", cval=0, order=0)\n        aug_det = aug.to_deterministic()\n\n        img_aug = aug_det.augment_image(img)\n        segmaps_aug = aug_det.augment_segmentation_maps([segmaps])[0]\n\n        img_aug_mask = img_aug > 255*0.1\n        segmaps_aug_mask = segmaps_aug.arr > 0\n        same = np.sum(img_aug_mask == segmaps_aug_mask[:, :, 0])\n        assert segmaps_aug.shape == (80, 80)\n        assert segmaps_aug.arr.shape == (80, 80, 1)\n        assert (same / img_aug_mask.size) >= 0.99\n\n    def test_image_segmaps_alignment_if_heatmaps_smaller_than_image(self):\n        # test alignment between images and segmaps\n        # here with segmaps that are smaller than the image\n        img = np.zeros((80, 80), dtype=np.uint8)\n        img[:, 30:50] = 255\n        img[30:50, :] = 255\n        img_small = ia.imresize_single_image(\n            img, (40, 40), interpolation=\"nearest\")\n        segmaps = SegmentationMapsOnImage(\n            (img_small > 0).astype(np.int32), shape=(80, 80))\n        aug = iaa.ElasticTransformation(\n            alpha=60.0, sigma=4.0, mode=\"constant\", cval=0, order=0)\n        aug_det = aug.to_deterministic()\n\n        img_aug = aug_det.augment_image(img)\n        segmaps_aug = aug_det.augment_segmentation_maps([segmaps])[0]\n\n        img_aug_mask = img_aug > 255*0.1\n        segmaps_aug_mask = ia.imresize_single_image(\n            segmaps_aug.arr, (80, 80), interpolation=\"nearest\") > 0\n        same = np.sum(img_aug_mask == segmaps_aug_mask[:, :, 0])\n        assert segmaps_aug.shape == (80, 80)\n        assert segmaps_aug.arr.shape == (40, 40, 1)\n        assert (same / img_aug_mask.size) >= 0.93\n\n    # ---------\n    # unusual channel numbers\n    # ---------\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.ElasticTransformation(alpha=2.0, sigma=2.0)\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 0)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    # ---------\n    # zero-sized axes\n    # ---------\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            for keep_size in [False, True]:\n                with self.subTest(shape=shape, keep_size=keep_size):\n                    for _ in sm.xrange(3):\n                        image = np.zeros(shape, dtype=np.uint8)\n                        aug = iaa.ElasticTransformation(alpha=2.0, sigma=2.0)\n\n                        image_aug = aug(image=image)\n\n                        assert image_aug.dtype.name == \"uint8\"\n                        assert image_aug.shape == shape\n\n    # -----------\n    # get_parameters\n    # -----------\n    def test_get_parameters(self):\n        aug = iaa.ElasticTransformation(\n            alpha=0.25, sigma=1.0, order=2, cval=10, mode=\"constant\")\n        params = aug.get_parameters()\n        assert params[0] is aug.alpha\n        assert params[1] is aug.sigma\n        assert params[2] is aug.order\n        assert params[3] is aug.cval\n        assert params[4] is aug.mode\n        assert 0.25 - 1e-8 < params[0].value < 0.25 + 1e-8\n        assert 1.0 - 1e-8 < params[1].value < 1.0 + 1e-8\n        assert params[2].value == 2\n        assert params[3].value == 10\n        assert params[4].value == \"constant\"\n\n    # -----------\n    # other dtypes\n    # -----------\n    def test_other_dtypes_bool(self):\n        aug = iaa.ElasticTransformation(sigma=0.5, alpha=5, order=0)\n        mask = np.zeros((21, 21), dtype=bool)\n        mask[7:13, 7:13] = True\n\n        image = np.zeros((21, 21), dtype=bool)\n        image[mask] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.name == image.dtype.name\n        assert not np.all(image_aug == 1)\n        assert np.any(image_aug[~mask] == 1)\n\n    def test_other_dtypes_uint_int(self):\n        aug = iaa.ElasticTransformation(sigma=0.5, alpha=5, order=0)\n        mask = np.zeros((21, 21), dtype=bool)\n        mask[7:13, 7:13] = True\n\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"int8\", \"int16\", \"int32\"]\n        for dtype in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            image = np.zeros((21, 21), dtype=dtype)\n            image[7:13, 7:13] = max_value\n\n            image_aug = aug.augment_image(image)\n\n            assert image_aug.dtype.name == dtype\n            assert not np.all(image_aug == max_value)\n            assert np.any(image_aug[~mask] == max_value)\n\n    def test_other_dtypes_float(self):\n        aug = iaa.ElasticTransformation(sigma=0.5, alpha=5, order=0)\n        mask = np.zeros((21, 21), dtype=bool)\n        mask[7:13, 7:13] = True\n\n        for dtype in [\"float16\", \"float32\", \"float64\"]:\n            def _isclose(a, b):\n                atol = 1e-4 if dtype == \"float16\" else 1e-8\n                return np.isclose(a, b, atol=atol, rtol=0)\n\n            isize = np.dtype(dtype).itemsize\n            values = [\n                0.01,\n                1.0,\n                10.0,\n                100.0,\n                500 ** (isize - 1),\n                float(np.float64(1000 ** (isize - 1)))\n            ]\n            values = values + [(-1) * value for value in values]\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((21, 21), dtype=dtype)\n                    image[7:13, 7:13] = value\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert not np.all(_isclose(image_aug, value))\n                    assert np.any(_isclose(image_aug[~mask], value))\n\n    def test_other_dtypes_bool_all_orders(self):\n        mask = np.zeros((50, 50), dtype=bool)\n        mask[10:40, 20:30] = True\n        mask[20:30, 10:40] = True\n\n        for order in [0, 1, 2, 3, 4, 5]:\n            aug = iaa.ElasticTransformation(sigma=1.0, alpha=50, order=order)\n\n            image = np.zeros((50, 50), dtype=bool)\n            image[mask] = True\n\n            image_aug = aug.augment_image(image)\n\n            assert image_aug.dtype.name == image.dtype.name\n            assert not np.all(image_aug == 1)\n            assert np.any(image_aug[~mask] == 1)\n\n    def test_other_dtypes_uint_int_all_orders(self):\n        mask = np.zeros((50, 50), dtype=bool)\n        mask[10:40, 20:30] = True\n        mask[20:30, 10:40] = True\n\n        for order in [0, 1, 2, 3, 4, 5]:\n            aug = iaa.ElasticTransformation(sigma=1.0, alpha=50, order=order)\n\n            dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                      \"int8\", \"int16\", \"int32\", \"int64\"]\n            if order == 0:\n                dtypes = [\"uint8\", \"uint16\", \"uint32\",\n                          \"int8\", \"int16\", \"int32\"]\n            for dtype in dtypes:\n                with self.subTest(dtype=dtype):\n                    min_value, center_value, max_value = \\\n                        iadt.get_value_range_of_dtype(dtype)\n                    dynamic_range = max_value - min_value\n\n                    image = np.zeros((50, 50), dtype=dtype)\n                    image[mask] = max_value\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.name == dtype\n                    if order == 0:\n                        assert not np.all(image_aug == max_value)\n                        assert np.any(image_aug[~mask] == max_value)\n                    else:\n                        atol = 0.1 * dynamic_range\n                        assert not np.all(\n                            np.isclose(image_aug,\n                                       max_value,\n                                       rtol=0, atol=atol)\n                        )\n                        assert np.any(\n                            np.isclose(image_aug[~mask],\n                                       max_value,\n                                       rtol=0, atol=atol))\n\n    def test_other_dtypes_float_all_orders(self):\n        mask = np.zeros((50, 50), dtype=bool)\n        mask[10:40, 20:30] = True\n        mask[20:30, 10:40] = True\n\n        for order in [0, 1, 2, 3, 4, 5]:\n            aug = iaa.ElasticTransformation(sigma=1.0, alpha=50, order=order)\n\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n            for dtype in dtypes:\n                with self.subTest(dtype=dtype):\n                    min_value, center_value, max_value = \\\n                        iadt.get_value_range_of_dtype(dtype)\n\n                    def _isclose(a, b):\n                        atol = 1e-4 if dtype == \"float16\" else 1e-8\n                        return np.isclose(a, b, atol=atol, rtol=0)\n\n                    value = (\n                        0.1 * max_value\n                        if dtype != \"float64\"\n                        else 0.0001 * max_value)\n                    image = np.zeros((50, 50), dtype=dtype)\n                    image[mask] = value\n                    image_aug = aug.augment_image(image)\n                    if order == 0:\n                        assert image_aug.dtype.name == dtype\n                        assert not np.all(\n                            _isclose(image_aug, value)\n                        )\n                        assert np.any(\n                            _isclose(image_aug[~mask], value)\n                        )\n                    else:\n                        atol = (\n                            10\n                            if dtype == \"float16\"\n                            else 0.00001 * max_value)\n                        assert not np.all(\n                            np.isclose(\n                                image_aug,\n                                value,\n                                rtol=0, atol=atol\n                            ))\n                        assert np.any(\n                            np.isclose(\n                                image_aug[~mask],\n                                value,\n                                rtol=0, atol=atol\n                            ))\n\n    def test_pickleable(self):\n        aug = iaa.ElasticTransformation(alpha=(0.2, 1.5), sigma=(1.0, 10.0),\n                                        seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=4, shape=(25, 25, 1))\n\n\nclass _TwoValueParam(iap.StochasticParameter):\n    def __init__(self, v1, v2):\n        super(_TwoValueParam, self).__init__()\n        self.v1 = v1\n        self.v2 = v2\n\n    def _draw_samples(self, size, random_state):\n        arr = np.full(size, self.v1, dtype=np.int32)\n        arr[1::2] = self.v2\n        return arr\n\n\nclass TestRot90(unittest.TestCase):\n    @property\n    def kp_offset(self):\n        # set this to -1 when using integer-based KP rotation instead of\n        # subpixel/float-based rotation\n        return 0\n\n    @property\n    def image(self):\n        return np.arange(4*4*3).reshape((4, 4, 3)).astype(np.uint8)\n\n    @property\n    def heatmaps(self):\n        return HeatmapsOnImage(self.image[..., 0:1].astype(np.float32) / 255,\n                               shape=(4, 4, 3))\n\n    @property\n    def heatmaps_smaller(self):\n        return HeatmapsOnImage(\n            np.float32([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]), shape=(4, 8, 3))\n\n    @property\n    def segmaps(self):\n        return SegmentationMapsOnImage(\n            self.image[..., 0:1].astype(np.int32), shape=(4, 4, 3))\n\n    @property\n    def segmaps_smaller(self):\n        return SegmentationMapsOnImage(\n            np.int32([[0, 1, 2], [3, 4, 5]]), shape=(4, 8, 3))\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=2, y=3)]\n        return ia.KeypointsOnImage(kps, shape=(4, 8, 3))\n\n    @property\n    def psoi(self):\n        return ia.PolygonsOnImage(\n            [ia.Polygon([(1, 1), (3, 1), (3, 3), (1, 3)])],\n            shape=(4, 8, 3)\n        )\n\n    @property\n    def lsoi(self):\n        return ia.LineStringsOnImage(\n            [ia.LineString([(1, 1), (3, 1), (3, 3), (1, 3)])],\n            shape=(4, 8, 3)\n        )\n\n    @property\n    def bbsoi(self):\n        return ia.BoundingBoxesOnImage(\n            [ia.BoundingBox(x1=1, y1=1, x2=3, y2=3)],\n            shape=(4, 8, 3)\n        )\n\n    @property\n    def kpsoi_k1(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        expected_k1_kps = [(4-2+kp_offset, 1),\n                           (4-3+kp_offset, 2)]\n        kps = [ia.Keypoint(x, y) for x, y in expected_k1_kps]\n        return ia.KeypointsOnImage(kps, shape=(8, 4, 3))\n\n    @property\n    def kpsoi_k2(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        expected_k1_kps = self.kpsoi_k1.to_xy_array()\n        expected_k2_kps = [\n            (8-expected_k1_kps[0][1]+kp_offset, expected_k1_kps[0][0]),\n            (8-expected_k1_kps[1][1]+kp_offset, expected_k1_kps[1][0])]\n        kps = [ia.Keypoint(x, y) for x, y in expected_k2_kps]\n        return ia.KeypointsOnImage(kps, shape=(4, 8, 3))\n\n    @property\n    def kpsoi_k3(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        expected_k2_kps = self.kpsoi_k2.to_xy_array()\n        expected_k3_kps = [\n            (4-expected_k2_kps[0][1]+kp_offset, expected_k2_kps[0][0]),\n            (4-expected_k2_kps[1][1]+kp_offset, expected_k2_kps[1][0])]\n        kps = [ia.Keypoint(x, y) for x, y in expected_k3_kps]\n        return ia.KeypointsOnImage(kps, shape=(8, 4, 3))\n\n    @property\n    def psoi_k1(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        expected_k1_polys = [(4-1+kp_offset, 1),\n                             (4-1+kp_offset, 3),\n                             (4-3+kp_offset, 3),\n                             (4-3+kp_offset, 1)]\n        return ia.PolygonsOnImage([ia.Polygon(expected_k1_polys)],\n                                  shape=(8, 4, 3))\n\n    @property\n    def psoi_k2(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        expected_k1_polys = self.psoi_k1.polygons[0].exterior\n        expected_k2_polys = [\n            (8-expected_k1_polys[0][1]+kp_offset, expected_k1_polys[0][0]),\n            (8-expected_k1_polys[1][1]+kp_offset, expected_k1_polys[1][0]),\n            (8-expected_k1_polys[2][1]+kp_offset, expected_k1_polys[2][0]),\n            (8-expected_k1_polys[3][1]+kp_offset, expected_k1_polys[3][0])]\n        return ia.PolygonsOnImage([ia.Polygon(expected_k2_polys)],\n                                  shape=(4, 8, 3))\n\n    @property\n    def psoi_k3(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        expected_k2_polys = self.psoi_k2.polygons[0].exterior\n        expected_k3_polys = [\n            (4-expected_k2_polys[0][1]+kp_offset, expected_k2_polys[0][0]),\n            (4-expected_k2_polys[1][1]+kp_offset, expected_k2_polys[1][0]),\n            (4-expected_k2_polys[2][1]+kp_offset, expected_k2_polys[2][0]),\n            (4-expected_k2_polys[3][1]+kp_offset, expected_k2_polys[3][0])]\n        return ia.PolygonsOnImage([ia.Polygon(expected_k3_polys)],\n                                  shape=(8, 4, 3))\n\n    @property\n    def lsoi_k1(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        expected_k1_ls = [(4-1+kp_offset, 1),\n                          (4-1+kp_offset, 3),\n                          (4-3+kp_offset, 3),\n                          (4-3+kp_offset, 1)]\n        return ia.LineStringsOnImage([ia.LineString(expected_k1_ls)],\n                                     shape=(8, 4, 3))\n\n    @property\n    def lsoi_k2(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        expected_k1_ls = self.psoi_k1.items[0].coords\n        expected_k2_ls = [\n            (8-expected_k1_ls[0][1]+kp_offset, expected_k1_ls[0][0]),\n            (8-expected_k1_ls[1][1]+kp_offset, expected_k1_ls[1][0]),\n            (8-expected_k1_ls[2][1]+kp_offset, expected_k1_ls[2][0]),\n            (8-expected_k1_ls[3][1]+kp_offset, expected_k1_ls[3][0])]\n        return ia.LineStringsOnImage([ia.LineString(expected_k2_ls)],\n                                     shape=(4, 8, 3))\n\n    @property\n    def lsoi_k3(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        expected_k2_ls = self.lsoi_k2.items[0].coords\n        expected_k3_ls = [\n            (4-expected_k2_ls[0][1]+kp_offset, expected_k2_ls[0][0]),\n            (4-expected_k2_ls[1][1]+kp_offset, expected_k2_ls[1][0]),\n            (4-expected_k2_ls[2][1]+kp_offset, expected_k2_ls[2][0]),\n            (4-expected_k2_ls[3][1]+kp_offset, expected_k2_ls[3][0])]\n        return ia.LineStringsOnImage([ia.LineString(expected_k3_ls)],\n                                     shape=(8, 4, 3))\n\n    @property\n    def bbsoi_k1(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        expected_k1_coords = [\n            (4-1+kp_offset, 1),\n            (4-3+kp_offset, 3)]\n        return ia.BoundingBoxesOnImage([\n            ia.BoundingBox(\n                x1=min(expected_k1_coords[0][0], expected_k1_coords[1][0]),\n                y1=min(expected_k1_coords[0][1], expected_k1_coords[1][1]),\n                x2=max(expected_k1_coords[1][0], expected_k1_coords[0][0]),\n                y2=max(expected_k1_coords[1][1], expected_k1_coords[0][1])\n            )], shape=(8, 4, 3))\n\n    @property\n    def bbsoi_k2(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        coords = self.bbsoi_k1.bounding_boxes[0].coords\n        expected_k2_coords = [\n            (8-coords[0][1]+kp_offset, coords[0][0]),\n            (8-coords[1][1]+kp_offset, coords[1][0])]\n        return ia.BoundingBoxesOnImage([\n            ia.BoundingBox(\n                x1=min(expected_k2_coords[0][0], expected_k2_coords[1][0]),\n                y1=min(expected_k2_coords[0][1], expected_k2_coords[1][1]),\n                x2=max(expected_k2_coords[1][0], expected_k2_coords[0][0]),\n                y2=max(expected_k2_coords[1][1], expected_k2_coords[0][1])\n            )],\n            shape=(4, 8, 3))\n\n    @property\n    def bbsoi_k3(self):\n        # without keep size\n        kp_offset = self.kp_offset\n        coords = self.bbsoi_k2.bounding_boxes[0].coords\n        expected_k3_coords = [\n            (4-coords[0][1]+kp_offset, coords[0][0]),\n            (4-coords[1][1]+kp_offset, coords[1][0])]\n        return ia.BoundingBoxesOnImage([\n            ia.BoundingBox(\n                x1=min(expected_k3_coords[0][0], expected_k3_coords[1][0]),\n                y1=min(expected_k3_coords[0][1], expected_k3_coords[1][1]),\n                x2=max(expected_k3_coords[1][0], expected_k3_coords[0][0]),\n                y2=max(expected_k3_coords[1][1], expected_k3_coords[0][1])\n            )],\n            shape=(8, 4, 3))\n\n    def test___init___k_is_list(self):\n        aug = iaa.Rot90([1, 3])\n        assert is_parameter_instance(aug.k, iap.Choice)\n        assert len(aug.k.a) == 2\n        assert aug.k.a[0] == 1\n        assert aug.k.a[1] == 3\n\n    def test___init___k_is_all(self):\n        aug = iaa.Rot90(ia.ALL)\n        assert is_parameter_instance(aug.k, iap.Choice)\n        assert len(aug.k.a) == 4\n        assert aug.k.a == [0, 1, 2, 3]\n\n    def test_images_k_is_0_and_4(self):\n        for k in [0, 4]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                img_aug = aug.augment_image(self.image)\n\n                assert img_aug.dtype.name == \"uint8\"\n                assert np.array_equal(img_aug, self.image)\n\n    def test_heatmaps_k_is_0_and_4(self):\n        for k in [0, 4]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                hms_aug = aug.augment_heatmaps([self.heatmaps])[0]\n\n                assert (hms_aug.arr_0to1.dtype.name\n                        == self.heatmaps.arr_0to1.dtype.name)\n                assert np.allclose(hms_aug.arr_0to1, self.heatmaps.arr_0to1)\n                assert hms_aug.shape == self.heatmaps.shape\n\n    def test_segmaps_k_is_0_and_4(self):\n        for k in [0, 4]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                segmaps_aug = aug.augment_segmentation_maps(\n                    [self.segmaps]\n                )[0]\n\n                assert (\n                    segmaps_aug.arr.dtype.name\n                    == self.segmaps.arr.dtype.name)\n                assert np.allclose(segmaps_aug.arr, self.segmaps.arr)\n                assert segmaps_aug.shape == self.segmaps.shape\n\n    def test_keypoints_k_is_0_and_4(self):\n        for k in [0, 4]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                kpsoi_aug = aug.augment_keypoints([self.kpsoi])[0]\n\n                assert_cbaois_equal(kpsoi_aug, self.kpsoi)\n\n    def test_polygons_k_is_0_and_4(self):\n        for k in [0, 4]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                psoi_aug = aug.augment_polygons(self.psoi)\n\n                assert_cbaois_equal(psoi_aug, self.psoi)\n\n    def test_line_strings_k_is_0_and_4(self):\n        for k in [0, 4]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                lsoi_aug = aug.augment_line_strings(self.lsoi)\n\n                assert_cbaois_equal(lsoi_aug, self.lsoi)\n\n    def test_bounding_boxes_k_is_0_and_4(self):\n        for k in [0, 4]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                bbsoi_aug = aug.augment_bounding_boxes(self.bbsoi)\n\n                assert_cbaois_equal(bbsoi_aug, self.bbsoi)\n\n    def test_images_k_is_1_and_5(self):\n        for k in [1, 5]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                img_aug = aug.augment_image(self.image)\n\n                assert img_aug.dtype.name == \"uint8\"\n                assert np.array_equal(img_aug,\n                                      np.rot90(self.image, 1, axes=(1, 0)))\n\n    def test_heatmaps_k_is_1_and_5(self):\n        for k in [1, 5]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                hms_aug = aug.augment_heatmaps([self.heatmaps])[0]\n\n                assert (hms_aug.arr_0to1.dtype.name\n                        == self.heatmaps.arr_0to1.dtype.name)\n                assert np.allclose(\n                    hms_aug.arr_0to1,\n                    np.rot90(self.heatmaps.arr_0to1, 1, axes=(1, 0)))\n                assert hms_aug.shape == (4, 4, 3)\n\n    def test_heatmaps_smaller_than_image_k_is_1_and_5(self):\n        for k in [1, 5]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                hms_smaller_aug = aug.augment_heatmaps(\n                    [self.heatmaps_smaller]\n                )[0]\n\n                assert (\n                    hms_smaller_aug.arr_0to1.dtype.name\n                    == self.heatmaps_smaller.arr_0to1.dtype.name)\n                assert np.allclose(\n                    hms_smaller_aug.arr_0to1,\n                    np.rot90(self.heatmaps_smaller.arr_0to1, 1, axes=(1, 0)))\n                assert hms_smaller_aug.shape == (8, 4, 3)\n\n    def test_segmaps_k_is_1_and_5(self):\n        for k in [1, 5]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                segmaps_aug = aug.augment_segmentation_maps(\n                    [self.segmaps]\n                )[0]\n\n                assert (\n                    segmaps_aug.arr.dtype.name\n                    == self.segmaps.arr.dtype.name)\n                assert np.allclose(\n                    segmaps_aug.arr,\n                    np.rot90(self.segmaps.arr, 1, axes=(1, 0)))\n                assert segmaps_aug.shape == (4, 4, 3)\n\n    def test_segmaps_smaller_than_image_k_is_1_and_5(self):\n        for k in [1, 5]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                segmaps_smaller_aug = aug.augment_segmentation_maps(\n                    self.segmaps_smaller)\n\n                assert (\n                    segmaps_smaller_aug.arr.dtype.name\n                    == self.segmaps_smaller.arr.dtype.name)\n                assert np.allclose(\n                    segmaps_smaller_aug.arr,\n                    np.rot90(self.segmaps_smaller.arr, 1, axes=(1, 0)))\n                assert segmaps_smaller_aug.shape == (8, 4, 3)\n\n    def test_keypoints_k_is_1_and_5(self):\n        for k in [1, 5]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                kpsoi_aug = aug.augment_keypoints([self.kpsoi])[0]\n\n                assert_cbaois_equal(kpsoi_aug, self.kpsoi_k1)\n\n    def test_polygons_k_is_1_and_5(self):\n        for k in [1, 5]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                psoi_aug = aug.augment_polygons(self.psoi)\n\n                assert_cbaois_equal(psoi_aug, self.psoi_k1)\n\n    def test_line_strings_k_is_1_and_5(self):\n        for k in [1, 5]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                lsoi_aug = aug.augment_line_strings(self.lsoi)\n\n                assert_cbaois_equal(lsoi_aug, self.lsoi_k1)\n\n    def test_bounding_boxes_k_is_1_and_5(self):\n        for k in [1, 5]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                bbsoi_aug = aug.augment_bounding_boxes(self.bbsoi)\n\n                assert_cbaois_equal(bbsoi_aug, self.bbsoi_k1)\n\n    def test_images_k_is_2(self):\n        aug = iaa.Rot90(2, keep_size=False)\n        img = self.image\n\n        img_aug = aug.augment_image(img)\n\n        assert img_aug.dtype.name == \"uint8\"\n        assert np.array_equal(img_aug, np.rot90(img, 2, axes=(1, 0)))\n\n    def test_heatmaps_k_is_2(self):\n        aug = iaa.Rot90(2, keep_size=False)\n        hms = self.heatmaps\n\n        hms_aug = aug.augment_heatmaps([hms])[0]\n\n        assert hms_aug.arr_0to1.dtype.name == hms.arr_0to1.dtype.name\n        assert np.allclose(\n            hms_aug.arr_0to1,\n            np.rot90(hms.arr_0to1, 2, axes=(1, 0)))\n        assert hms_aug.shape == (4, 4, 3)\n\n    def test_heatmaps_smaller_than_image_k_is_2(self):\n        aug = iaa.Rot90(2, keep_size=False)\n        hms_smaller = self.heatmaps_smaller\n\n        hms_smaller_aug = aug.augment_heatmaps([hms_smaller])[0]\n\n        assert (hms_smaller_aug.arr_0to1.dtype.name\n                == hms_smaller.arr_0to1.dtype.name)\n        assert np.allclose(\n            hms_smaller_aug.arr_0to1,\n            np.rot90(hms_smaller.arr_0to1, 2, axes=(1, 0)))\n        assert hms_smaller_aug.shape == (4, 8, 3)\n\n    def test_segmaps_k_is_2(self):\n        aug = iaa.Rot90(2, keep_size=False)\n        segmaps = self.segmaps\n\n        segmaps_aug = aug.augment_segmentation_maps([segmaps])[0]\n\n        assert segmaps_aug.arr.dtype.name == segmaps.arr.dtype.name\n        assert np.allclose(\n            segmaps_aug.arr,\n            np.rot90(segmaps.arr, 2, axes=(1, 0)))\n        assert segmaps_aug.shape == (4, 4, 3)\n\n    def test_segmaps_smaller_than_image_k_is_2(self):\n        aug = iaa.Rot90(2, keep_size=False)\n        segmaps_smaller = self.segmaps_smaller\n\n        segmaps_smaller_aug = aug.augment_segmentation_maps(segmaps_smaller)\n\n        assert (segmaps_smaller_aug.arr.dtype.name\n                == segmaps_smaller.arr.dtype.name)\n        assert np.allclose(\n            segmaps_smaller_aug.arr,\n            np.rot90(segmaps_smaller.arr, 2, axes=(1, 0)))\n        assert segmaps_smaller_aug.shape == (4, 8, 3)\n\n    def test_keypoints_k_is_2(self):\n        aug = iaa.Rot90(2, keep_size=False)\n\n        kpsoi_aug = aug.augment_keypoints([self.kpsoi])[0]\n\n        assert_cbaois_equal(kpsoi_aug, self.kpsoi_k2)\n\n    def test_polygons_k_is_2(self):\n        aug = iaa.Rot90(2, keep_size=False)\n\n        psoi_aug = aug.augment_polygons(self.psoi)\n\n        assert_cbaois_equal(psoi_aug, self.psoi_k2)\n\n    def test_line_strings_k_is_2(self):\n        aug = iaa.Rot90(2, keep_size=False)\n\n        lsoi_aug = aug.augment_line_strings(self.lsoi)\n\n        assert_cbaois_equal(lsoi_aug, self.lsoi_k2)\n\n    def test_bounding_boxes_k_is_2(self):\n        aug = iaa.Rot90(2, keep_size=False)\n\n        bbsoi_aug = aug.augment_bounding_boxes(self.bbsoi)\n\n        assert_cbaois_equal(bbsoi_aug, self.bbsoi_k2)\n\n    def test_images_k_is_3_and_minus1(self):\n        img = self.image\n        for k in [3, -1]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                img_aug = aug.augment_image(img)\n\n                assert img_aug.dtype.name == \"uint8\"\n                assert np.array_equal(img_aug, np.rot90(img, 3, axes=(1, 0)))\n\n    def test_heatmaps_k_is_3_and_minus1(self):\n        hms = self.heatmaps\n        for k in [3, -1]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                hms_aug = aug.augment_heatmaps([hms])[0]\n\n                assert (hms_aug.arr_0to1.dtype.name\n                        == hms.arr_0to1.dtype.name)\n                assert np.allclose(\n                    hms_aug.arr_0to1,\n                    np.rot90(hms.arr_0to1, 3, axes=(1, 0)))\n                assert hms_aug.shape == (4, 4, 3)\n\n    def test_heatmaps_smaller_than_image_k_is_3_and_minus1(self):\n        hms_smaller = self.heatmaps_smaller\n        for k in [3, -1]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                hms_smaller_aug = aug.augment_heatmaps([hms_smaller])[0]\n\n                assert (hms_smaller_aug.arr_0to1.dtype.name\n                        == hms_smaller.arr_0to1.dtype.name)\n                assert np.allclose(\n                    hms_smaller_aug.arr_0to1,\n                    np.rot90(hms_smaller.arr_0to1, 3, axes=(1, 0)))\n                assert hms_smaller_aug.shape == (8, 4, 3)\n\n    def test_segmaps_k_is_3_and_minus1(self):\n        segmaps = self.segmaps\n        for k in [3, -1]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                segmaps_aug = aug.augment_segmentation_maps([segmaps])[0]\n\n                assert (segmaps_aug.arr.dtype.name\n                        == segmaps.arr.dtype.name)\n                assert np.allclose(\n                    segmaps_aug.arr,\n                    np.rot90(segmaps.arr, 3, axes=(1, 0)))\n                assert segmaps_aug.shape == (4, 4, 3)\n\n    def test_segmaps_smaller_than_image_k_is_3_and_minus1(self):\n        segmaps_smaller = self.segmaps_smaller\n        for k in [3, -1]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                segmaps_smaller_aug = aug.augment_segmentation_maps(\n                    segmaps_smaller)\n\n                assert (segmaps_smaller_aug.arr.dtype.name\n                        == segmaps_smaller.arr.dtype.name)\n                assert np.allclose(\n                    segmaps_smaller_aug.arr,\n                    np.rot90(segmaps_smaller.arr, 3, axes=(1, 0)))\n                assert segmaps_smaller_aug.shape == (8, 4, 3)\n\n    def test_keypoints_k_is_3_and_minus1(self):\n        for k in [3, -1]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                kpsoi_aug = aug.augment_keypoints([self.kpsoi])[0]\n\n                assert_cbaois_equal(kpsoi_aug, self.kpsoi_k3)\n\n    def test_polygons_k_is_3_and_minus1(self):\n        for k in [3, -1]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                psoi_aug = aug.augment_polygons(self.psoi)\n\n                assert_cbaois_equal(psoi_aug, self.psoi_k3)\n\n    def test_line_strings_k_is_3_and_minus1(self):\n        for k in [3, -1]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                lsoi_aug = aug.augment_line_strings(self.lsoi)\n\n                assert_cbaois_equal(lsoi_aug, self.lsoi_k3)\n\n    def test_bounding_boxes_k_is_3_and_minus1(self):\n        for k in [3, -1]:\n            with self.subTest(k=k):\n                aug = iaa.Rot90(k, keep_size=False)\n\n                bbsoi_aug = aug.augment_bounding_boxes(self.bbsoi)\n\n                assert_cbaois_equal(bbsoi_aug, self.bbsoi_k3)\n\n    def test_images_k_is_1_verify_without_using_numpy_rot90(self):\n        # verify once without np.rot90\n        aug = iaa.Rot90(k=1, keep_size=False)\n        image = np.uint8([[1, 0, 0],\n                          [0, 2, 0]])\n\n        img_aug = aug.augment_image(image)\n\n        expected = np.uint8([[0, 1], [2, 0], [0, 0]])\n        assert np.array_equal(img_aug, expected)\n\n    def test_images_k_is_1_keep_size_is_true(self):\n        # keep_size=True, k=1\n        aug = iaa.Rot90(1, keep_size=True)\n        img_nonsquare = np.arange(5*4*3).reshape((5, 4, 3)).astype(np.uint8)\n\n        img_aug = aug.augment_image(img_nonsquare)\n\n        assert img_aug.dtype.name == \"uint8\"\n        assert np.array_equal(\n            img_aug,\n            ia.imresize_single_image(\n                np.rot90(img_nonsquare, 1, axes=(1, 0)),\n                (5, 4)\n            )\n        )\n\n    def test_heatmaps_k_is_1_keep_size_is_true(self):\n        aug = iaa.Rot90(1, keep_size=True)\n        hms = self.heatmaps\n\n        hms_aug = aug.augment_heatmaps([hms])[0]\n\n        assert hms_aug.arr_0to1.dtype.name == hms.arr_0to1.dtype.name\n        assert np.allclose(\n            hms_aug.arr_0to1,\n            np.rot90(hms.arr_0to1, 1, axes=(1, 0)))\n        assert hms_aug.shape == (4, 4, 3)\n\n    def test_heatmaps_smaller_than_image_k_is_1_keep_size_is_true(self):\n        aug = iaa.Rot90(1, keep_size=True)\n        hms_smaller = self.heatmaps_smaller\n\n        hms_smaller_aug = aug.augment_heatmaps([hms_smaller])[0]\n\n        hms_smaller_rot = np.rot90(hms_smaller.arr_0to1, 1, axes=(1, 0))\n        hms_smaller_rot = np.clip(\n            ia.imresize_single_image(\n                hms_smaller_rot, (2, 3), interpolation=\"cubic\"\n            ),\n            0.0, 1.0)\n        assert (hms_smaller_aug.arr_0to1.dtype.name\n                == hms_smaller.arr_0to1.dtype.name)\n        assert np.allclose(hms_smaller_aug.arr_0to1, hms_smaller_rot)\n        assert hms_smaller_aug.shape == (4, 8, 3)\n\n    def test_segmaps_k_is_1_keep_size_is_true(self):\n        aug = iaa.Rot90(1, keep_size=True)\n        segmaps = self.segmaps\n\n        segmaps_aug = aug.augment_segmentation_maps([segmaps])[0]\n\n        assert (segmaps_aug.arr.dtype.name\n                == segmaps.arr.dtype.name)\n        assert np.allclose(segmaps_aug.arr,\n                           np.rot90(segmaps.arr, 1, axes=(1, 0)))\n        assert segmaps_aug.shape == (4, 4, 3)\n\n    def test_segmaps_smaller_than_image_k_is_1_keep_size_is_true(self):\n        aug = iaa.Rot90(1, keep_size=True)\n        segmaps_smaller = self.segmaps_smaller\n\n        segmaps_smaller_aug = aug.augment_segmentation_maps(segmaps_smaller)\n\n        segmaps_smaller_rot = np.rot90(segmaps_smaller.arr, 1, axes=(1, 0))\n        segmaps_smaller_rot = ia.imresize_single_image(\n            segmaps_smaller_rot, (2, 3), interpolation=\"nearest\")\n        assert (segmaps_smaller_aug.arr.dtype.name\n                == segmaps_smaller.arr.dtype.name)\n        assert np.allclose(segmaps_smaller_aug.arr, segmaps_smaller_rot)\n        assert segmaps_smaller_aug.shape == (4, 8, 3)\n\n    def test_keypoints_k_is_1_keep_size_is_true(self):\n        aug = iaa.Rot90(1, keep_size=True)\n        kp_offset = self.kp_offset\n        kpsoi = self.kpsoi\n\n        kpsoi_aug = aug.augment_keypoints([kpsoi])[0]\n\n        expected = [(4-2+kp_offset, 1), (4-3+kp_offset, 2)]\n        expected = [(8*x/4, 4*y/8) for x, y in expected]\n        assert kpsoi_aug.shape == (4, 8, 3)\n        for kp_aug, kp in zip(kpsoi_aug.keypoints, expected):\n            assert np.allclose([kp_aug.x, kp_aug.y], [kp[0], kp[1]])\n\n    def test_polygons_k_is_1_keep_size_is_true(self):\n        aug = iaa.Rot90(1, keep_size=True)\n        psoi = self.psoi\n        kp_offset = self.kp_offset\n\n        psoi_aug = aug.augment_polygons(psoi)\n\n        expected = [(4-1+kp_offset, 1), (4-1+kp_offset, 3),\n                    (4-3+kp_offset, 3), (4-3+kp_offset, 1)]\n        expected = [(8*x/4, 4*y/8) for x, y in expected]\n        assert psoi_aug.shape == (4, 8, 3)\n        assert len(psoi_aug.polygons) == 1\n        assert psoi_aug.polygons[0].is_valid\n        assert psoi_aug.polygons[0].exterior_almost_equals(expected)\n\n    def test_line_strings_k_is_1_keep_size_is_true(self):\n        aug = iaa.Rot90(1, keep_size=True)\n        lsoi = self.lsoi\n        kp_offset = self.kp_offset\n\n        lsoi_aug = aug.augment_line_strings(lsoi)\n\n        expected = [(4-1+kp_offset, 1), (4-1+kp_offset, 3),\n                    (4-3+kp_offset, 3), (4-3+kp_offset, 1)]\n        expected = [(8*x/4, 4*y/8) for x, y in expected]\n        assert lsoi_aug.shape == (4, 8, 3)\n        assert len(lsoi_aug.items) == 1\n        assert lsoi_aug.items[0].coords_almost_equals(expected)\n\n    def test_bounding_boxes_k_is_1_keep_size_is_true(self):\n        aug = iaa.Rot90(1, keep_size=True)\n        bbsoi = self.bbsoi\n        kp_offset = self.kp_offset\n\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n\n        expected = [(4-1+kp_offset, 1),\n                    (4-3+kp_offset, 3)]\n        expected = [(8*x/4, 4*y/8) for x, y in expected]\n        expected = np.float32([\n            [min(expected[0][0], expected[1][0]),\n             min(expected[0][1], expected[1][1])],\n            [max(expected[0][0], expected[1][0]),\n             max(expected[0][1], expected[1][1])]\n        ])\n        assert bbsoi_aug.shape == (4, 8, 3)\n        assert len(bbsoi_aug.bounding_boxes) == 1\n        assert bbsoi_aug.bounding_boxes[0].coords_almost_equals(expected)\n\n    def test_images_k_is_list(self):\n        aug = iaa.Rot90(_TwoValueParam(1, 2), keep_size=False)\n        img = self.image\n\n        imgs_aug = aug.augment_images([img] * 4)\n\n        assert np.array_equal(imgs_aug[0], np.rot90(img, 1, axes=(1, 0)))\n        assert np.array_equal(imgs_aug[1], np.rot90(img, 2, axes=(1, 0)))\n        assert np.array_equal(imgs_aug[2], np.rot90(img, 1, axes=(1, 0)))\n        assert np.array_equal(imgs_aug[3], np.rot90(img, 2, axes=(1, 0)))\n\n    def test_heatmaps_smaller_than_image_k_is_list(self):\n        def _rot_hm(hm, k):\n            return np.rot90(hm.arr_0to1, k, axes=(1, 0))\n\n        aug = iaa.Rot90(_TwoValueParam(1, 2), keep_size=False)\n        hms_smaller = self.heatmaps_smaller\n\n        hms_aug = aug.augment_heatmaps([hms_smaller] * 4)\n\n        assert hms_aug[0].shape == (8, 4, 3)\n        assert hms_aug[1].shape == (4, 8, 3)\n        assert hms_aug[2].shape == (8, 4, 3)\n        assert hms_aug[3].shape == (4, 8, 3)\n        assert np.allclose(hms_aug[0].arr_0to1, _rot_hm(hms_smaller, 1))\n        assert np.allclose(hms_aug[1].arr_0to1, _rot_hm(hms_smaller, 2))\n        assert np.allclose(hms_aug[2].arr_0to1, _rot_hm(hms_smaller, 1))\n        assert np.allclose(hms_aug[3].arr_0to1, _rot_hm(hms_smaller, 2))\n\n    def test_segmaps_smaller_than_image_k_is_list(self):\n        def _rot_sm(segmap, k):\n            return np.rot90(segmap.arr, k, axes=(1, 0))\n\n        aug = iaa.Rot90(_TwoValueParam(1, 2), keep_size=False)\n        segmaps_smaller = self.segmaps_smaller\n\n        segmaps_aug = aug.augment_segmentation_maps([segmaps_smaller] * 4)\n\n        assert segmaps_aug[0].shape == (8, 4, 3)\n        assert segmaps_aug[1].shape == (4, 8, 3)\n        assert segmaps_aug[2].shape == (8, 4, 3)\n        assert segmaps_aug[3].shape == (4, 8, 3)\n        assert np.allclose(segmaps_aug[0].arr, _rot_sm(segmaps_smaller, 1))\n        assert np.allclose(segmaps_aug[1].arr, _rot_sm(segmaps_smaller, 2))\n        assert np.allclose(segmaps_aug[2].arr, _rot_sm(segmaps_smaller, 1))\n        assert np.allclose(segmaps_aug[3].arr, _rot_sm(segmaps_smaller, 2))\n\n    def test_keypoints_k_is_list(self):\n        aug = iaa.Rot90(_TwoValueParam(1, 2), keep_size=False)\n        kpsoi = self.kpsoi\n\n        kpsoi_aug = aug.augment_keypoints([kpsoi] * 4)\n\n        assert_cbaois_equal(kpsoi_aug[0], self.kpsoi_k1)\n        assert_cbaois_equal(kpsoi_aug[1], self.kpsoi_k2)\n        assert_cbaois_equal(kpsoi_aug[2], self.kpsoi_k1)\n        assert_cbaois_equal(kpsoi_aug[3], self.kpsoi_k2)\n\n    def test_polygons_k_is_list(self):\n        aug = iaa.Rot90(_TwoValueParam(1, 2), keep_size=False)\n        psoi = self.psoi\n\n        psoi_aug = aug.augment_polygons([psoi] * 4)\n\n        assert_cbaois_equal(psoi_aug[0], self.psoi_k1)\n        assert_cbaois_equal(psoi_aug[1], self.psoi_k2)\n        assert_cbaois_equal(psoi_aug[2], self.psoi_k1)\n        assert_cbaois_equal(psoi_aug[3], self.psoi_k2)\n\n    def test_line_strings_k_is_list(self):\n        aug = iaa.Rot90(_TwoValueParam(1, 2), keep_size=False)\n        lsoi = self.lsoi\n\n        lsoi_aug = aug.augment_line_strings([lsoi] * 4)\n\n        assert_cbaois_equal(lsoi_aug[0], self.lsoi_k1)\n        assert_cbaois_equal(lsoi_aug[1], self.lsoi_k2)\n        assert_cbaois_equal(lsoi_aug[2], self.lsoi_k1)\n        assert_cbaois_equal(lsoi_aug[3], self.lsoi_k2)\n\n    def test_bounding_boxes_k_is_list(self):\n        aug = iaa.Rot90(_TwoValueParam(1, 2), keep_size=False)\n        bbsoi = self.bbsoi\n\n        bbsoi_aug = aug.augment_bounding_boxes([bbsoi] * 4)\n\n        assert_cbaois_equal(bbsoi_aug[0], self.bbsoi_k1)\n        assert_cbaois_equal(bbsoi_aug[1], self.bbsoi_k2)\n        assert_cbaois_equal(bbsoi_aug[2], self.bbsoi_k1)\n        assert_cbaois_equal(bbsoi_aug[3], self.bbsoi_k2)\n\n    def test_empty_keypoints(self):\n        aug = iaa.Rot90(k=1, keep_size=False)\n        kpsoi = ia.KeypointsOnImage([], shape=(4, 8, 3))\n\n        kpsoi_aug = aug.augment_keypoints(kpsoi)\n\n        expected = self.kpsoi_k1\n        expected.keypoints = []\n        assert_cbaois_equal(kpsoi_aug, expected)\n\n    def test_empty_polygons(self):\n        aug = iaa.Rot90(k=1, keep_size=False)\n        psoi = ia.PolygonsOnImage([], shape=(4, 8, 3))\n\n        psoi_aug = aug.augment_polygons(psoi)\n\n        expected = self.psoi_k1\n        expected.polygons = []\n        assert_cbaois_equal(psoi_aug, expected)\n\n    def test_empty_line_strings(self):\n        aug = iaa.Rot90(k=1, keep_size=False)\n        lsoi = ia.LineStringsOnImage([], shape=(4, 8, 3))\n\n        lsoi_aug = aug.augment_line_strings(lsoi)\n\n        expected = self.lsoi_k1\n        expected.line_strings = []\n        assert_cbaois_equal(lsoi_aug, expected)\n\n    def test_empty_bounding_boxes(self):\n        aug = iaa.Rot90(k=1, keep_size=False)\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(4, 8, 3))\n\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n\n        expected = self.bbsoi_k1\n        expected.bounding_boxes = []\n        assert_cbaois_equal(bbsoi_aug, expected)\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Rot90(k=1)\n\n                image_aug = aug(image=image)\n\n                shape_expected = tuple([shape[1], shape[0]] + list(shape[2:]))\n                assert np.all(image_aug == 0)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape_expected\n\n    def test_zero_sized_axes_k_0_or_2(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            for keep_size in [False, True]:\n                with self.subTest(shape=shape, keep_size=keep_size):\n                    for _ in sm.xrange(10):\n                        image = np.zeros(shape, dtype=np.uint8)\n                        aug = iaa.Rot90([0, 2], keep_size=keep_size)\n\n                        image_aug = aug(image=image)\n\n                        assert image_aug.shape == shape\n\n    def test_zero_sized_axes_k_1_or_3_no_keep_size(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                for _ in sm.xrange(10):\n                    image = np.zeros(shape, dtype=np.uint8)\n                    aug = iaa.Rot90([1, 3], keep_size=False)\n\n                    image_aug = aug(image=image)\n\n                    shape_expected = tuple([shape[1], shape[0]]\n                                           + list(shape[2:]))\n                    assert image_aug.shape == shape_expected\n\n    def test_zero_sized_axes_k_1_or_3_keep_size(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                for _ in sm.xrange(10):\n                    image = np.zeros(shape, dtype=np.uint8)\n                    aug = iaa.Rot90([1, 3], keep_size=True)\n\n                    image_aug = aug(image=image)\n\n                    assert image_aug.shape == image.shape\n\n    def test_get_parameters(self):\n        aug = iaa.Rot90([1, 3], keep_size=False)\n        assert aug.get_parameters()[0] == aug.k\n        assert aug.get_parameters()[1] is False\n\n    def test_other_dtypes_bool(self):\n        aug = iaa.Rot90(2)\n\n        image = np.zeros((3, 3), dtype=bool)\n        image[0, 0] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.all(image_aug[0, 0] == 0)\n        assert np.all(image_aug[2, 2] == 1)\n\n    def test_other_dtypes_uint_int(self):\n        aug = iaa.Rot90(2)\n\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int16\", \"int32\", \"int64\"]\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = max_value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.all(image_aug[0, 0] == 0)\n                assert np.all(image_aug[2, 2] == max_value)\n\n    def test_other_dtypes_float(self):\n        aug = iaa.Rot90(2)\n\n        try:\n            high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n        except AttributeError:\n            high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n\n        for dtype in dtypes:\n            def _allclose(a, b):\n                atol = 1e-4 if dtype == \"float16\" else 1e-8\n                return np.allclose(a, b, atol=atol, rtol=0)\n\n            isize = np.dtype(dtype).itemsize\n            values = [\n                0,\n                1.0,\n                10.0,\n                100.0,\n                high_res_dt(500 ** (isize-1)),\n                high_res_dt(1000 ** (isize-1))\n            ]\n            values = values + [(-1) * value for value in values]\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[0, 0] = value\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert _allclose(image_aug[0, 0], 0)\n                    assert _allclose(image_aug[2, 2], high_res_dt(value))\n\n    def test_pickleable(self):\n        aug = iaa.Rot90([0, 1, 2, 3], seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5)\n\n\nclass TestWithPolarWarping(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___single_augmenter_as_child(self):\n        aug = iaa.WithPolarWarping(iaa.Noop())\n        assert isinstance(aug.children, iaa.Sequential)\n        assert isinstance(aug.children[0], iaa.Noop)\n\n    def test___init___list_of_augmenters_as_child(self):\n        aug = iaa.WithPolarWarping([iaa.Noop(), iaa.Noop()])\n        assert isinstance(aug.children, iaa.Sequential)\n        assert isinstance(aug.children[0], iaa.Noop)\n        assert isinstance(aug.children[1], iaa.Noop)\n\n    def test_images_no_change(self):\n        image = np.mod(np.arange(10*20*3), 255).astype(np.uint8)\n        image = image.reshape((10, 20, 3))\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        image_aug = aug(image=image)\n\n        avg_dist = np.average(\n            np.abs(\n                image_aug.astype(np.int32)[2:-2, 2:-2]\n                - image.astype(np.int32)[2:-2, 2:-2]\n            )\n        )\n        assert image_aug.shape == (10, 20, 3)\n        assert avg_dist < 7.0\n\n    def test_heatmaps_no_change(self):\n        hm = np.linspace(0, 1.0, 10*20, dtype=np.float32).reshape((10, 20, 1))\n        hm = ia.HeatmapsOnImage(hm, shape=(10, 20, 3))\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        hm_aug = aug(heatmaps=hm)\n\n        avg_dist = np.average(\n            np.abs(\n                hm_aug.get_arr()[2:-2, 2:-2]\n                - hm.get_arr()[2:-2, 2:-2]\n            )\n        )\n        assert hm_aug.shape == (10, 20, 3)\n        assert avg_dist < 0.0125\n\n    def test_segmentation_maps_no_change(self):\n        sm = np.zeros((10, 20, 1), dtype=np.int32)\n        sm[1, 0:5] = 1\n        sm[3:3, 3:3] = 2\n        sm[7:9, :] = 3\n        sm = ia.SegmentationMapsOnImage(sm, shape=(10, 20, 3))\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        sm_aug = aug(segmentation_maps=sm)\n\n        p_same = np.average(\n            sm_aug.get_arr()[2:-2, 2:-2]\n            == sm.get_arr()[2:-2, 2:-2]\n        )\n        assert sm_aug.shape == (10, 20, 3)\n        assert p_same > 0.95\n\n    def test_keypoints_no_change(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=5, y=5),\n               ia.Keypoint(x=5, y=9)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(10, 20, 3))\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        kpsoi_aug = aug(keypoints=kpsoi)\n\n        assert kpsoi_aug.shape == (10, 20, 3)\n        assert np.allclose(kpsoi_aug.to_xy_array(), kpsoi.to_xy_array(),\n                           atol=0.01)\n\n    def test_bounding_boxes_no_change(self):\n        bbs = [\n            ia.BoundingBox(x1=1, y1=2, x2=3, y2=4, label=\"foo\"),\n            ia.BoundingBox(x1=3, y1=5, x2=7, y2=10),\n        ]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(10, 20, 3))\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        bbsoi_aug = aug(bounding_boxes=bbsoi)\n\n        assert bbsoi_aug.items[0].label == \"foo\"\n        assert bbsoi_aug.items[1].label is None\n        assert bbsoi_aug.shape == (10, 20, 3)\n        assert np.allclose(bbsoi_aug.to_xy_array(), bbsoi.to_xy_array(),\n                           atol=0.01)\n\n    def test_polygons_no_change(self):\n        ps = [\n            ia.Polygon([(0, 2), (4, 2), (4, 4)], label=\"foo\"),\n            ia.Polygon([(0, 0), (5, 0), (5, 5), (0, 5)])\n        ]\n        psoi = ia.PolygonsOnImage(ps, shape=(10, 20, 3))\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        psoi_aug = aug(polygons=psoi)\n\n        assert psoi_aug.items[0].label == \"foo\"\n        assert psoi_aug.items[1].label is None\n        assert psoi_aug.shape == (10, 20, 3)\n        assert np.allclose(psoi_aug.to_xy_array(), psoi.to_xy_array(),\n                           atol=0.01)\n\n    def test_line_strings_no_change(self):\n        ls = [\n            ia.LineString([(0, 2), (4, 2), (4, 4)]),\n            ia.LineString([(0, 0), (5, 0), (5, 5), (0, 5)])\n        ]\n        lsoi = ia.LineStringsOnImage(ls, shape=(10, 20, 3))\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        lsoi_aug = aug(line_strings=lsoi)\n\n        assert lsoi_aug.shape == (10, 20, 3)\n        assert np.allclose(lsoi_aug.to_xy_array(), lsoi.to_xy_array(),\n                           atol=0.01)\n\n    def test_bounding_boxes_and_polygons_provided_no_change(self):\n        bbs = [\n            ia.BoundingBox(x1=1, y1=2, x2=3, y2=4, label=\"foo\"),\n            ia.BoundingBox(x1=3, y1=5, x2=7, y2=10),\n        ]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(10, 20, 3))\n        ps = [\n            ia.Polygon([(0, 2), (4, 2), (4, 4)], label=\"foo\"),\n            ia.Polygon([(0, 0), (5, 0), (5, 5), (0, 5)])\n        ]\n        psoi = ia.PolygonsOnImage(ps, shape=(10, 20, 3))\n\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        aug = aug.to_deterministic()\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n        psoi_aug = aug.augment_polygons(psoi)\n\n        assert bbsoi_aug.items[0].label == \"foo\"\n        assert bbsoi_aug.items[1].label is None\n        assert bbsoi_aug.shape == (10, 20, 3)\n        assert np.allclose(bbsoi_aug.to_xy_array(), bbsoi.to_xy_array(),\n                           atol=0.01)\n\n        assert psoi_aug.items[0].label == \"foo\"\n        assert psoi_aug.items[1].label is None\n        assert psoi_aug.shape == (10, 20, 3)\n        assert np.allclose(psoi_aug.to_xy_array(), psoi.to_xy_array(),\n                           atol=0.01)\n\n    def test_images_translation_x(self):\n        image = np.zeros((50, 70, 3), dtype=np.uint8)\n        image[20-1:20+1, 30-1:30+1, 0] = 255\n        image[30-1:30+1, 40-1:40+1, 1] = 255\n        aug = iaa.WithPolarWarping(iaa.Affine(translate_px={\"x\": 15}))\n\n        image_aug = aug(image=image)\n\n        x1 = np.argmax(np.max(image_aug[..., 0], axis=0))\n        y1 = np.argmax(np.max(image_aug[..., 0], axis=1))\n        x2 = np.argmax(np.max(image_aug[..., 1], axis=0))\n        y2 = np.argmax(np.max(image_aug[..., 1], axis=1))\n\n        # translation on x axis in polar representation should move all points\n        # a bit away from the center\n        min_diff = 4\n        assert image_aug.shape == (50, 70, 3)\n        assert x1 < 30 - min_diff\n        assert y1 < 20 - min_diff\n        assert x2 > 40 + min_diff\n        assert y2 > 30 + min_diff\n\n    def test_heatmaps_translation_x(self):\n        hm = np.zeros((50, 70, 2), dtype=np.float32)\n        hm[20-1:20+1, 30-1:30+1, 0] = 1.0\n        hm[30-1:30+1, 40-1:40+1, 1] = 1.0\n        hm = ia.HeatmapsOnImage(hm, shape=(50, 70, 3))\n        aug = iaa.WithPolarWarping(iaa.Affine(translate_px={\"x\": 15}))\n\n        hm_aug = aug(heatmaps=hm)\n\n        hm_aug_arr = hm_aug.get_arr()\n        x1 = np.argmax(np.max(hm_aug_arr[..., 0], axis=0))\n        y1 = np.argmax(np.max(hm_aug_arr[..., 0], axis=1))\n        x2 = np.argmax(np.max(hm_aug_arr[..., 1], axis=0))\n        y2 = np.argmax(np.max(hm_aug_arr[..., 1], axis=1))\n\n        # translation on x axis in polar representation should move all points\n        # a bit away from the center\n        min_diff = 4\n        assert hm_aug_arr.shape == (50, 70, 2)\n        assert hm_aug.shape == (50, 70, 3)\n        assert x1 < 30 - min_diff\n        assert y1 < 20 - min_diff\n        assert x2 > 40 + min_diff\n        assert y2 > 30 + min_diff\n\n    def test_segmentation_maps_translation_x(self):\n        sm = np.zeros((50, 70, 2), dtype=np.int32)\n        sm[20-1:20+1, 30-1:30+1, 0] = 1\n        sm[30-1:30+1, 40-1:40+1, 1] = 2\n        sm = ia.SegmentationMapsOnImage(sm, shape=(50, 70, 3))\n        aug = iaa.WithPolarWarping(iaa.Affine(translate_px={\"x\": 15}))\n\n        sm_aug = aug(segmentation_maps=sm)\n\n        sm_aug_arr = sm_aug.get_arr()\n        x1 = np.argmax(np.max(sm_aug_arr[..., 0], axis=0))\n        y1 = np.argmax(np.max(sm_aug_arr[..., 0], axis=1))\n        x2 = np.argmax(np.max(sm_aug_arr[..., 1], axis=0))\n        y2 = np.argmax(np.max(sm_aug_arr[..., 1], axis=1))\n\n        # translation on x axis in polar representation should move all points\n        # a bit away from the center\n        min_diff = 4\n        assert sm_aug_arr.shape == (50, 70, 2)\n        assert sm_aug.shape == (50, 70, 3)\n        assert x1 < 30 - min_diff\n        assert y1 < 20 - min_diff\n        assert x2 > 40 + min_diff\n        assert y2 > 30 + min_diff\n\n    def test_keypoints_translation_x(self):\n        cbas = [ia.Keypoint(y=20, x=30), ia.Keypoint(y=30, x=40)]\n        cbaoi = ia.KeypointsOnImage(cbas, shape=(50, 70, 3))\n        aug = iaa.WithPolarWarping(iaa.Affine(translate_px={\"x\": 15}))\n\n        cbaoi_aug = aug(keypoints=cbaoi)\n\n        x1 = cbaoi_aug.items[0].x\n        y1 = cbaoi_aug.items[0].y\n        x2 = cbaoi_aug.items[1].x\n        y2 = cbaoi_aug.items[1].y\n\n        # translation on x axis in polar representation should move all points\n        # a bit away from the center\n        min_diff = 4\n        assert cbaoi_aug.shape == (50, 70, 3)\n        assert x1 < 30 - min_diff\n        assert y1 < 20 - min_diff\n        assert x2 > 40 + min_diff\n        assert y2 > 30 + min_diff\n\n    def test_bounding_boxes_translation_x(self):\n        cbas = [ia.BoundingBox(y1=20, x1=30, y2=20+2, x2=30+2),\n                ia.BoundingBox(y1=30, x1=40, y2=30+2, x2=40+2)]\n        cbaoi = ia.BoundingBoxesOnImage(cbas, shape=(50, 70, 3))\n        aug = iaa.WithPolarWarping(iaa.Affine(translate_px={\"x\": 15}))\n\n        cbaoi_aug = aug(bounding_boxes=cbaoi)\n\n        x1 = cbaoi_aug.items[0].x1\n        y1 = cbaoi_aug.items[0].y1\n        x2 = cbaoi_aug.items[1].x2\n        y2 = cbaoi_aug.items[1].y2\n\n        # translation on x axis in polar representation should move all points\n        # a bit away from the center\n        min_diff = 4\n        assert cbaoi_aug.shape == (50, 70, 3)\n        assert x1 < 30 - min_diff\n        assert y1 < 20 - min_diff\n        assert x2 > 40 + min_diff\n        assert y2 > 30 + min_diff\n\n    def test_polygons_translation_x(self):\n        cbas = [ia.Polygon([(30, 20), (30+2, 20), (30+2, 20+2)]),\n                ia.Polygon([(40, 30), (40+2, 30), (40+2, 30+2)])]\n        cbaoi = ia.PolygonsOnImage(cbas, shape=(50, 70, 3))\n        aug = iaa.WithPolarWarping(iaa.Affine(translate_px={\"x\": 15}))\n\n        cbaoi_aug = aug(polygons=cbaoi)\n\n        x1 = cbaoi_aug.items[0].coords[0][0]\n        y1 = cbaoi_aug.items[0].coords[0][1]\n        x2 = cbaoi_aug.items[1].coords[2][0]\n        y2 = cbaoi_aug.items[1].coords[2][1]\n\n        # translation on x axis in polar representation should move all points\n        # a bit away from the center\n        min_diff = 4\n        assert cbaoi_aug.shape == (50, 70, 3)\n        assert x1 < 30 - min_diff\n        assert y1 < 20 - min_diff\n        assert x2 > 40 + min_diff\n        assert y2 > 30 + min_diff\n\n    def test_line_strings_translation_x(self):\n        cbas = [ia.LineString([(30, 20), (30+2, 20), (30+2, 20+2)]),\n                ia.LineString([(40, 30), (40+2, 30), (40+2, 30+2)])]\n        cbaoi = ia.LineStringsOnImage(cbas, shape=(50, 70, 3))\n        aug = iaa.WithPolarWarping(iaa.Affine(translate_px={\"x\": 15}))\n\n        cbaoi_aug = aug(line_strings=cbaoi)\n\n        x1 = cbaoi_aug.items[0].coords[0][0]\n        y1 = cbaoi_aug.items[0].coords[0][1]\n        x2 = cbaoi_aug.items[1].coords[2][0]\n        y2 = cbaoi_aug.items[1].coords[2][1]\n\n        # translation on x axis in polar representation should move all points\n        # a bit away from the center\n        min_diff = 4\n        assert cbaoi_aug.shape == (50, 70, 3)\n        assert x1 < 30 - min_diff\n        assert y1 < 20 - min_diff\n        assert x2 > 40 + min_diff\n        assert y2 > 30 + min_diff\n\n    def test_image_heatmap_alignment(self):\n        image = np.zeros((80, 100, 3), dtype=np.uint8)\n        image[40-10:40+10, 50-10:50+10, :] = 255\n        hm = np.zeros((40, 50, 1), dtype=np.float32)\n        hm[20-5:20+5, 25-5:25+5, :] = 1.0\n        hm = ia.HeatmapsOnImage(hm, shape=image.shape)\n        aug = iaa.WithPolarWarping(iaa.Affine(translate_px={\"x\": 10}))\n\n        image_aug, hm_aug = aug(image=image, heatmaps=hm)\n\n        hm_aug_arr = hm_aug.get_arr()\n        hm_aug_arr_rs = ia.imresize_single_image(hm_aug_arr, (80, 100),\n                                                 interpolation=\"nearest\")\n        overlap = np.average(\n            (image_aug[..., 0] > 200)\n            == (hm_aug_arr_rs[..., 0] > 0.9)\n        )\n        assert image_aug.shape == (80, 100, 3)\n        assert hm_aug.shape == (80, 100, 3)\n        assert hm_aug_arr.shape == (40, 50, 1)\n        assert overlap > 0.96\n\n    def test_image_segmentation_map_alignment(self):\n        image = np.zeros((80, 100, 3), dtype=np.uint8)\n        image[40-10:40+10, 50-10:50+10, :] = 255\n        sm = np.zeros((40, 50, 1), dtype=np.int32)\n        sm[20-5:20+5, 25-5:25+5, :] = 1\n        sm = ia.SegmentationMapsOnImage(sm, shape=image.shape)\n        aug = iaa.WithPolarWarping(iaa.Affine(translate_px={\"x\": 10}))\n\n        image_aug, sm_aug = aug(image=image, segmentation_maps=sm)\n\n        sm_aug_arr = sm_aug.get_arr()\n        sm_aug_arr_rs = ia.imresize_single_image(sm_aug_arr, (80, 100),\n                                                 interpolation=\"nearest\")\n        overlap = np.average(\n            (image_aug[..., 0] > 200)\n            == (sm_aug_arr_rs[..., 0] == 1)\n        )\n        assert image_aug.shape == (80, 100, 3)\n        assert sm_aug.shape == (80, 100, 3)\n        assert sm_aug_arr.shape == (40, 50, 1)\n        assert overlap > 0.96\n\n    def test_image_keypoint_alignment(self):\n        image = np.zeros((80, 100, 3), dtype=np.uint8)\n        image[40-10:40-10+3, 50-10:50-10+3, :] = 255\n        image[40+10:40+10+3, 50+10:50+10+3, :] = 255\n\n        kps = [ia.Keypoint(y=40-10+1.5, x=50-10+1.5),\n               ia.Keypoint(y=40+10+1.5, x=50+10+1.5)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=image.shape)\n        aug = iaa.WithPolarWarping(iaa.Affine(translate_px={\"x\": 10}))\n\n        image_aug, kpsoi_aug = aug(image=image, keypoints=kpsoi)\n\n        kp1 = kpsoi_aug.items[0]\n        kp2 = kpsoi_aug.items[1]\n        kp1_intensity = image_aug[int(kp1.y), int(kp1.x), 0]\n        kp2_intensity = image_aug[int(kp2.y), int(kp2.x), 0]\n        assert image_aug.shape == (80, 100, 3)\n        assert kpsoi_aug.shape == (80, 100, 3)\n        assert kp1_intensity > 200\n        assert kp2_intensity > 200\n\n    def test_image_is_noncontiguous(self):\n        image = np.mod(np.arange(10*20*3), 255).astype(np.uint8)\n        image = image.reshape((10, 20, 3))\n        image_cp = np.fliplr(np.copy(image))\n        image = np.fliplr(image)\n        assert image.flags[\"C_CONTIGUOUS\"] is False\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        image_aug = aug(image=image)\n\n        avg_dist = np.average(\n            np.abs(\n                image_aug.astype(np.int32)[2:-2, 2:-2]\n                - image_cp.astype(np.int32)[2:-2, 2:-2]\n            )\n        )\n        assert image_aug.shape == (10, 20, 3)\n        assert avg_dist < 7.0\n\n    def test_image_is_view(self):\n        image = np.mod(np.arange(10*20*3), 255).astype(np.uint8)\n        image = image.reshape((10, 20, 3))\n        image_cp = np.copy(image)[2:, 2:, :]\n        image = image[2:, 2:, :]\n        assert image.flags[\"OWNDATA\"] is False\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        image_aug = aug(image=image)\n\n        avg_dist = np.average(\n            np.abs(\n                image_aug.astype(np.int32)[2:-2, 2:-2]\n                - image_cp.astype(np.int32)[2:-2, 2:-2]\n            )\n        )\n        assert image_aug.shape == (8, 18, 3)\n        assert avg_dist < 7.0\n\n    def test_propagation_hooks(self):\n        image = np.mod(np.arange(30*30), 255).astype(np.uint8)\n        image = image.reshape((30, 30))\n        aug = iaa.WithPolarWarping(iaa.Add(50))\n\n        def _propagator(images, augmenter, parents, default):\n            return False if augmenter is aug else default\n\n        hooks = ia.HooksImages(propagator=_propagator)\n\n        observed1 = aug.augment_image(image)\n        observed2 = aug.augment_image(image, hooks=hooks)\n\n        image_plus50 = np.clip(image.astype(np.int32)+50, 0, 255)\n        diff1 = np.abs(observed1[2:-2].astype(np.int32)\n                       - image_plus50[2:-2].astype(np.int32))\n        diff2 = np.abs(observed2[2:-2].astype(np.int32)\n                       - image_plus50[2:-2].astype(np.int32))\n        overlap_1_add = np.average(diff1 <= 1)\n        overlap_2_add = np.average(diff2 <= 2)\n        assert overlap_1_add >= 0.9\n        assert overlap_2_add < 0.01\n\n    def test_unusual_channel_numbers(self):\n        with assertWarns(self, iaa.SuspiciousSingleImageShapeWarning):\n            shapes = [\n                (5, 5, 4),\n                (5, 5, 5),\n                (5, 5, 512),\n                (5, 5, 513)\n            ]\n\n            for shape in shapes:\n                with self.subTest(shape=shape):\n                    image = np.zeros(shape, dtype=np.uint8)\n                    aug = iaa.WithPolarWarping(iaa.Noop())\n\n                    image_aug = aug(image=image)\n\n                    shape_expected = tuple([shape[1], shape[0]] + list(shape[2:]))\n                    assert np.all(image_aug == 0)\n                    assert image_aug.dtype.name == \"uint8\"\n                    assert image_aug.shape == shape_expected\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)],\n                                            shape=image.shape)\n                sm_arr = np.zeros((3, 3), dtype=np.int32)\n                sm_arr[1, 1] = 1\n                sm = ia.SegmentationMapsOnImage(sm_arr, shape=image.shape)\n                aug = iaa.WithPolarWarping(iaa.Noop())\n\n                aug_det = aug.to_deterministic()\n                image_aug = aug_det(image=image)\n                kpsoi_aug = aug_det(keypoints=kpsoi)\n                sm_aug = aug_det(segmentation_maps=sm)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n                assert np.allclose(kpsoi_aug.to_xy_array(),\n                                   kpsoi.to_xy_array())\n                assert kpsoi_aug.shape == shape\n                assert np.array_equal(sm_aug.get_arr(), sm_arr)\n                assert sm_aug.shape == shape\n\n    def test_other_dtypes_bool(self):\n        aug = iaa.WithPolarWarping(iaa.Noop())\n        arr = np.zeros((20, 20), dtype=bool)\n        arr[10-3:10+3, 10-3:10+3] = True\n\n        arr_aug = aug(image=arr)\n\n        overlap = np.average(arr_aug == arr)\n        assert arr_aug.shape == (20, 20)\n        assert arr_aug.dtype.name == \"bool\"\n        assert overlap > 0.95\n\n    def test_other_dtypes_uint_int(self):\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        dtypes = [\"uint8\", \"uint16\",\n                  \"int8\", \"int16\", \"int32\",]\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                center_value = int(center_value)\n\n                image = np.zeros((30, 10), dtype=dtype)\n                image[0:10, :] = min_value\n                image[10:20, :] = center_value\n                image[20:30, :] = max_value\n                image = iaa.pad(image, top=2, right=2, bottom=2, left=2,\n                                cval=0)\n\n                image_aug = aug.augment_image(image)\n                image_aug = image_aug[2:-2, 2:-2]\n\n                overlap_min = np.average(image_aug[0:10] == min_value)\n                overlap_cv = np.average(image_aug[10:20] == center_value)\n                overlap_max = np.average(image_aug[20:30] == max_value)\n                assert image_aug.dtype.name == dtype\n                assert overlap_min > 0.9\n                assert overlap_cv > 0.9\n                assert overlap_max > 0.9\n\n    def test_other_dtypes_float(self):\n        def _avg_close(arr_aug, expected_val):\n            atol = 1e-8\n            return np.average(np.isclose(arr_aug, expected_val,\n                                         rtol=0, atol=atol))\n\n        aug = iaa.WithPolarWarping(iaa.Noop())\n\n        dtypes = [\"float16\", \"float32\", \"float64\"]\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                center_value = center_value\n\n                image = np.zeros((70, 10), dtype=dtype)\n                image[0:10, :] = min_value\n                image[10:20, :] = center_value\n                image[20:30, :] = max_value\n                image[30:40, :] = -1.0\n                image[40:50, :] = 1.0\n                image[50:60, :] = -100.0\n                image[60:70, :] = 100.0\n                image = iaa.pad(image, top=2, right=2, bottom=2, left=2,\n                                cval=0)\n\n                image_aug = aug.augment_image(image)\n                image_aug = image_aug[2:-2, 2:-2]\n\n                overlap1 = _avg_close(image_aug[0:10], min_value)\n                overlap2 = _avg_close(image_aug[10:20], center_value)\n                overlap3 = _avg_close(image_aug[20:30], max_value)\n                overlap4 = _avg_close(image_aug[30:40], -1.0)\n                overlap5 = _avg_close(image_aug[40:50], 1.0)\n                overlap6 = _avg_close(image_aug[50:60], -100.0)\n                overlap7 = _avg_close(image_aug[60:70], 100.0)\n                assert image_aug.dtype.name == dtype\n                assert overlap1 > 0.9\n                assert overlap2 > 0.9\n                assert overlap3 > 0.9\n                assert overlap4 > 0.9\n                assert overlap5 > 0.9\n                assert overlap6 > 0.9\n                assert overlap7 > 0.9\n\n    def test_get_parameters(self):\n        aug = iaa.WithPolarWarping(iaa.Noop())\n        params = aug.get_parameters()\n        assert len(params) == 0\n\n    def test_get_children_lists(self):\n        children = iaa.Sequential([iaa.Noop()])\n        aug = iaa.WithPolarWarping(children)\n        assert aug.get_children_lists() == [children]\n\n    def test_to_deterministic(self):\n        child = iaa.Identity()\n        aug = iaa.WithPolarWarping([child])\n\n        aug_det = aug.to_deterministic()\n\n        assert aug_det.deterministic\n        assert aug_det.random_state is not aug.random_state\n        assert aug_det.children.deterministic\n        assert aug_det.children[0].deterministic\n\n    def test___repr___and___str__(self):\n        children = iaa.Sequential([iaa.Noop()])\n        aug = iaa.WithPolarWarping(children, name=\"WithPolarWarpingTest\")\n        expected = (\n            \"WithPolarWarping(\"\n            \"name=WithPolarWarpingTest, \"\n            \"children=%s, \"\n            \"deterministic=False\"\n            \")\" % (str(children),))\n\n        assert aug.__repr__() == expected\n        assert aug.__str__() == expected\n\n    def test_pickleable(self):\n        aug = iaa.WithPolarWarping(\n            iaa.Affine(translate_px=(0, 10), seed=1),\n            seed=2)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(25, 25, 1))\n\n\nclass Test_apply_jigsaw(unittest.TestCase):\n    def test_no_movement(self):\n        dtypes = [\n            \"bool\",\n            \"uint8\", \"uint16\", \"uint32\", \"uint64\",\n            \"int8\", \"int16\", \"int32\", \"int64\",\n            \"float16\", \"float32\", \"float64\"\n        ]\n\n        try:\n            dtypes.append(np.dtype(\"float128\"))\n        except TypeError:\n            pass  # float128 not known on system\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                arr = np.arange(20*20*1).reshape((20, 20, 1))\n                if dtype == \"bool\":\n                    mask = np.logical_or(\n                        arr % 4 == 0,\n                        arr % 7 == 0)\n                    arr[mask] = 1\n                    arr[~mask] = 0\n                arr = arr.astype(dtype)\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                arr[0, 0] = min_value\n                arr[0, 1] = max_value\n\n                destinations = np.arange(5*5).reshape((5, 5))\n\n                observed = iaa.apply_jigsaw(arr, destinations)\n\n                if arr.dtype.kind != \"f\":\n                    assert np.array_equal(observed, arr)\n                else:\n                    atol = 1e-4 if dtype == \"float16\" else 1e-8\n                    assert np.allclose(observed, arr, rtol=0, atol=atol)\n\n    def test_no_movement_zero_sized_axes(self):\n        sizes = [\n            (0, 1),\n            (1, 0),\n            (0, 0)\n        ]\n\n        dtype = \"uint8\"\n        for size in sizes:\n            with self.subTest(size=size):\n                arr = np.zeros(size, dtype=dtype)\n                destinations = np.arange(1*1).reshape((1, 1))\n\n                observed = iaa.apply_jigsaw(arr, destinations)\n\n                assert np.array_equal(observed, arr)\n\n    def _test_two_cells_moved__n_channels(self, nb_channels):\n        dtypes = [\n            \"bool\",\n            \"uint8\", \"uint16\", \"uint32\", \"uint64\",\n            \"int8\", \"int16\", \"int32\", \"int64\",\n            \"float16\", \"float32\", \"float64\"\n        ]\n\n        try:\n            dtypes.append(np.dtype(\"float128\").name)\n        except TypeError:\n            pass  # float128 not known by user system\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                c = 1 if nb_channels is None else nb_channels\n                arr = np.arange(20*20*c)\n                if dtype == \"bool\":\n                    mask = np.logical_or(\n                        arr % 4 == 0,\n                        arr % 7 == 0)\n                    arr[mask] = 1\n                    arr[~mask] = 0\n                if nb_channels is not None:\n                    arr = arr.reshape((20, 20, c))\n                else:\n                    arr = arr.reshape((20, 20))\n                arr = arr.astype(dtype)\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                arr[0, 0] = min_value\n                arr[0, 1] = max_value\n\n                destinations = np.arange(5*5).reshape((5, 5))\n                destinations[0, 0] = 4  # cell 0 will be filled with 4\n                destinations[0, 4] = 0  # cell 4 will be filled with 0\n                destinations[0, 1] = 6  # cell 1 will be filled with 6\n                destinations[1, 1] = 1  # cell 6 will be filled with 1\n\n                observed = iaa.apply_jigsaw(arr, destinations)\n\n                cell_0_obs = observed[0:4, 0:4]\n                cell_0_exp = arr[0:4, 16:20]\n                cell_4_obs = observed[0:4, 16:20]\n                cell_4_exp = arr[0:4, 0:4]\n                cell_1_obs = observed[0:4, 4:8]\n                cell_1_exp = arr[4:8, 4:8]\n                cell_6_obs = observed[4:8, 4:8]\n                cell_6_exp = arr[0:4, 4:8]\n                cell_2_obs = observed[0:4, 8:12]\n                cell_2_exp = arr[0:4, 8:12]\n                if arr.dtype.kind != \"f\":\n                    assert np.array_equal(cell_0_obs, cell_0_exp)\n                    assert np.array_equal(cell_4_obs, cell_4_exp)\n                    assert np.array_equal(cell_1_obs, cell_1_exp)\n                    assert np.array_equal(cell_6_obs, cell_6_exp)\n                    assert np.array_equal(cell_2_obs, cell_2_exp)\n                else:\n                    atol = 1e-4 if dtype == \"float16\" else 1e-8\n                    kwargs = {\"rtol\": 0, \"atol\": atol}\n                    assert np.allclose(cell_0_obs, cell_0_exp, **kwargs)\n                    assert np.allclose(cell_4_obs, cell_4_exp, **kwargs)\n                    assert np.allclose(cell_1_obs, cell_1_exp, **kwargs)\n                    assert np.allclose(cell_6_obs, cell_6_exp, **kwargs)\n                    assert np.allclose(cell_2_obs, cell_2_exp, **kwargs)\n\n                assert observed.shape == arr.shape\n                assert observed.dtype.name == dtype\n\n    def test_two_cells_moved__no_channels(self):\n        self._test_two_cells_moved__n_channels(None)\n\n    def test_two_cells_moved__1_channel(self):\n        self._test_two_cells_moved__n_channels(1)\n\n    def test_two_cells_moved__3_channels(self):\n        self._test_two_cells_moved__n_channels(3)\n\n\nclass Test_apply_jigsaw_to_coords(unittest.TestCase):\n    def test_no_movement(self):\n        arr = np.float32([\n            (0.0, 0.0),\n            (5.0, 5.0),\n            (25.0, 50.5),\n            (10.01, 21.0)\n        ])\n        destinations = np.arange(10*10).reshape((10, 10))\n\n        observed = iaa.apply_jigsaw_to_coords(arr, destinations, (50, 100))\n\n        assert np.allclose(observed, arr)\n\n    def test_with_movement(self):\n        arr = np.float32([\n            (0.0, 0.0),  # in cell (0,0) = idx 0\n            (5.0, 5.0),  # in cell (0,0) = idx 0\n            (25.0, 50.5),  # in cell (5,2) = idx 52\n            (10.01, 21.0)  # in cell (2,1) = idx 12\n        ])\n        destinations = np.arange(10*10).reshape((10, 10))\n        destinations[0, 0] = 1\n        destinations[0, 1] = 0\n        destinations[5, 2] = 7\n        destinations[0, 7] = 52\n\n        observed = iaa.apply_jigsaw_to_coords(arr, destinations, (100, 100))\n\n        expected = np.float32([\n            (10.0, 0.0),\n            (15.0, 5.0),\n            (75.0, 0.5),\n            (10.01, 21.0)\n        ])\n        assert np.allclose(observed, expected)\n\n    def test_with_movement_non_square_image(self):\n        arr = np.float32([\n            (0.5, 0.6),  # in cell (0,0) = idx 0\n            (180.7, 90.8),  # in cell (9,9) = idx 99\n        ])\n        destinations = np.arange(10*10).reshape((10, 10))\n        destinations[0, 0] = 99\n        destinations[9, 9] = 0\n\n        observed = iaa.apply_jigsaw_to_coords(arr, destinations, (100, 200))\n\n        expected = np.float32([\n            (180+0.5, 90+0.6),\n            (0+0.7, 0+0.8)\n        ])\n        assert np.allclose(observed, expected)\n\n    def test_empty_coords(self):\n        arr = np.zeros((0, 2), dtype=np.float32)\n        destinations = np.arange(10*10).reshape((10, 10))\n\n        observed = iaa.apply_jigsaw_to_coords(arr, destinations, (100, 100))\n\n        assert np.allclose(observed, arr)\n\n\nclass Test_generate_jigsaw_destinations(unittest.TestCase):\n    def test_max_steps_0(self):\n        rng = iarandom.RNG(0)\n        max_steps = 0\n        rows = 10\n        cols = 20\n\n        observed = iaa.generate_jigsaw_destinations(rows, cols, max_steps, rng,\n                                                    connectivity=8)\n\n        assert np.array_equal(\n            observed,\n            np.arange(rows*cols).reshape((rows, cols)))\n\n    def test_max_steps_1(self):\n        rng = iarandom.RNG(0)\n        max_steps = 1\n        rows = 10\n        cols = 20\n\n        observed = iaa.generate_jigsaw_destinations(rows, cols, max_steps, rng,\n                                                    connectivity=8)\n\n        yy = (observed // cols).reshape((rows, cols))\n        xx = np.mod(observed, cols).reshape((rows, cols))\n        yy_expected = np.tile(np.arange(rows).reshape((rows, 1)), (1, cols))\n        xx_expected = np.tile(np.arange(cols).reshape((1, cols)), (rows, 1))\n\n        yy_diff = yy_expected - yy\n        xx_diff = xx_expected - xx\n        dist = np.sqrt(yy_diff ** 2 + xx_diff ** 2)\n\n        assert np.min(dist) <= 0.01\n        assert np.any(dist >= np.sqrt(2) - 1e-4)\n        assert np.max(dist) <= np.sqrt(2) + 1e-4\n\n    def test_max_steps_1_connectivity_4(self):\n        rng = iarandom.RNG(0)\n        max_steps = 1\n        rows = 10\n        cols = 20\n\n        observed = iaa.generate_jigsaw_destinations(rows, cols, max_steps, rng,\n                                                    connectivity=4)\n\n        yy = (observed // cols).reshape((rows, cols))\n        xx = np.mod(observed, cols).reshape((rows, cols))\n        yy_expected = np.tile(np.arange(rows).reshape((rows, 1)), (1, cols))\n        xx_expected = np.tile(np.arange(cols).reshape((1, cols)), (rows, 1))\n\n        yy_diff = yy_expected - yy\n        xx_diff = xx_expected - xx\n        dist = np.sqrt(yy_diff ** 2 + xx_diff ** 2)\n\n        assert np.min(dist) <= 0.01\n        assert np.any(dist >= 0.99)\n        assert np.max(dist) <= 1.01\n\n\nclass TestJigsaw(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___defaults(self):\n        aug = iaa.Jigsaw(nb_rows=1, nb_cols=2)\n        assert aug.nb_rows.value == 1\n        assert aug.nb_cols.value == 2\n        assert aug.max_steps.value == 1\n        assert aug.allow_pad is True\n\n    def test___init___custom(self):\n        aug = iaa.Jigsaw(nb_rows=1, nb_cols=2, max_steps=3, allow_pad=False)\n        assert aug.nb_rows.value == 1\n        assert aug.nb_cols.value == 2\n        assert aug.max_steps.value == 3\n        assert aug.allow_pad is False\n\n    def test__draw_samples(self):\n        aug = iaa.Jigsaw(nb_rows=(1, 5), nb_cols=(1, 6), max_steps=(1, 3))\n        batch = mock.Mock()\n        batch.nb_rows = 100\n\n        samples = aug._draw_samples(batch, iarandom.RNG(0))\n\n        assert len(np.unique(samples.nb_rows)) > 1\n        assert len(np.unique(samples.nb_cols)) > 1\n        assert len(np.unique(samples.max_steps)) > 1\n        assert np.all(samples.nb_rows >= 1)\n        assert np.all(samples.nb_rows <= 5)\n        assert np.all(samples.nb_cols >= 1)\n        assert np.all(samples.nb_cols <= 6)\n        assert np.all(samples.max_steps >= 1)\n        assert np.all(samples.max_steps <= 3)\n\n        all_same = True\n        first = samples.destinations[0]\n        for dest in samples.destinations:\n            this_same = (dest.shape == first.shape\n                         and np.array_equal(dest, first))\n            all_same = all_same and this_same\n        assert not all_same\n\n    def test_images_without_shifts(self):\n        aug = iaa.Jigsaw(nb_rows=2, nb_cols=2, max_steps=0)\n        image = np.mod(np.arange(20*20*3), 255).astype(np.uint8)\n        image = image.reshape((20, 20, 3))\n\n        image_aug = aug(image=image)\n\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (20, 20, 3)\n        assert np.array_equal(image_aug, image)\n\n    def test_heatmaps_without_shifts(self):\n        aug = iaa.Jigsaw(nb_rows=2, nb_cols=2, max_steps=0)\n        arr = np.linspace(0, 1.0, 20*20*1).astype(np.float32)\n        arr = arr.reshape((20, 20, 1))\n        heatmap = ia.HeatmapsOnImage(arr, shape=(20, 20, 3))\n\n        heatmap_aug = aug(heatmaps=heatmap)\n\n        assert heatmap_aug.shape == (20, 20, 3)\n        assert np.allclose(heatmap_aug.arr_0to1, heatmap.arr_0to1)\n\n    def test_segmaps_without_shifts(self):\n        aug = iaa.Jigsaw(nb_rows=2, nb_cols=2, max_steps=0)\n        arr = np.zeros((20, 20, 1), dtype=np.int32)\n        arr[0:10, :] = 1\n        arr[10:20, 10:20] = 2\n        arr = arr.reshape((20, 20, 1))\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(20, 20, 3))\n\n        segmap_aug = aug(segmentation_maps=segmap)\n\n        assert segmap_aug.shape == (20, 20, 3)\n        assert np.array_equal(segmap_aug.arr, segmap.arr)\n\n    def test_keypoints_without_shifts(self):\n        aug = iaa.Jigsaw(nb_rows=2, nb_cols=2, max_steps=0)\n        kpsoi = ia.KeypointsOnImage.from_xy_array([\n            (0, 0),\n            (5.5, 3.5),\n            (12.1, 23.5)\n        ], shape=(20, 20, 3))\n\n        kpsoi_aug = aug(keypoints=kpsoi)\n\n        assert kpsoi_aug.shape == (20, 20, 3)\n        assert np.allclose(kpsoi_aug.to_xy_array(), kpsoi.to_xy_array())\n\n    def test_images_with_shifts(self):\n        # these rows/cols/max_steps parameters are mostly ignored due to the\n        # mocked _draw_samples method below\n        aug = iaa.Jigsaw(nb_rows=2, nb_cols=2, max_steps=1)\n        image = np.mod(np.arange(19*19*3), 255).astype(np.uint8)\n        image = image.reshape((19, 19, 3))\n        destinations = np.array([\n            [3, 1],\n            [2, 0]\n        ], dtype=np.int32)\n\n        old_func = aug._draw_samples\n\n        def _mocked_draw_samples(batch, random_state):\n            samples = old_func(batch, random_state)\n            return geometriclib._JigsawSamples(\n                nb_rows=samples.nb_rows,\n                nb_cols=samples.nb_cols,\n                max_steps=samples.max_steps,\n                destinations=[destinations])\n\n        aug._draw_samples = _mocked_draw_samples\n\n        image_aug = aug(image=image)\n\n        expected = iaa.pad(image, bottom=1, right=1, cval=0)\n        expected = iaa.apply_jigsaw(expected, destinations)\n        assert np.array_equal(image_aug, expected)\n\n    def test_heatmaps_with_shifts(self):\n        # these rows/cols/max_steps parameters are mostly ignored due to the\n        # mocked _draw_samples method below\n        aug = iaa.Jigsaw(nb_rows=2, nb_cols=2, max_steps=1)\n        arr = np.linspace(0, 1.0, 18*18*1).astype(np.float32)\n        arr = arr.reshape((18, 18, 1))\n        heatmap = ia.HeatmapsOnImage(arr, shape=(19, 19, 3))\n        destinations = np.array([\n            [3, 1],\n            [2, 0]\n        ], dtype=np.int32)\n\n        old_func = aug._draw_samples\n\n        def _mocked_draw_samples(batch, random_state):\n            samples = old_func(batch, random_state)\n            return geometriclib._JigsawSamples(\n                nb_rows=samples.nb_rows,\n                nb_cols=samples.nb_cols,\n                max_steps=samples.max_steps,\n                destinations=[destinations])\n\n        aug._draw_samples = _mocked_draw_samples\n\n        heatmap_aug = aug(heatmaps=heatmap)\n\n        expected = ia.imresize_single_image(arr, (19, 19),\n                                            interpolation=\"cubic\")\n        expected = np.clip(expected, 0, 1.0)\n        expected = iaa.pad(expected, bottom=1, right=1, cval=0.0)\n        expected = iaa.apply_jigsaw(expected, destinations)\n        expected = ia.imresize_single_image(expected, (18, 18),\n                                            interpolation=\"cubic\")\n        expected = np.clip(expected, 0, 1.0)\n        assert np.allclose(heatmap_aug.arr_0to1, expected)\n\n    def test_segmaps_with_shifts(self):\n        # these rows/cols/max_steps parameters are mostly ignored due to the\n        # mocked _draw_samples method below\n        aug = iaa.Jigsaw(nb_rows=2, nb_cols=2, max_steps=1)\n        arr = np.zeros((18, 18, 1), dtype=np.int32)\n        arr[0:10, :] = 1\n        arr[10:18, 10:18] = 2\n        arr = arr.reshape((18, 18, 1))\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(19, 19, 3))\n        destinations = np.array([\n            [3, 1],\n            [2, 0]\n        ], dtype=np.int32)\n\n        old_func = aug._draw_samples\n\n        def _mocked_draw_samples(batch, random_state):\n            samples = old_func(batch, random_state)\n            return geometriclib._JigsawSamples(\n                nb_rows=samples.nb_rows,\n                nb_cols=samples.nb_cols,\n                max_steps=samples.max_steps,\n                destinations=[destinations])\n\n        aug._draw_samples = _mocked_draw_samples\n\n        segmap_aug = aug(segmentation_maps=segmap)\n\n        expected = ia.imresize_single_image(arr, (19, 19),\n                                            interpolation=\"nearest\")\n        expected = iaa.pad(expected, bottom=1, right=1, cval=0)\n        expected = iaa.apply_jigsaw(expected, destinations)\n        expected = ia.imresize_single_image(expected, (18, 18),\n                                            interpolation=\"nearest\")\n        assert np.array_equal(segmap_aug.arr, expected)\n\n    def test_keypoints_with_shifts(self):\n        # these rows/cols/max_steps parameters are mostly ignored due to the\n        # mocked _draw_samples method below\n        aug = iaa.Jigsaw(nb_rows=5, nb_cols=5, max_steps=1)\n        kpsoi = ia.KeypointsOnImage.from_xy_array([\n            (0, 0),\n            (5.5, 3.5),\n            (4.0, 12.5),\n            (11.1, 11.2),\n            (12.1, 23.5)\n        ], shape=(18, 18, 3))\n        destinations = np.array([\n            [3, 1],\n            [2, 0]\n        ], dtype=np.int32)\n\n        old_func = aug._draw_samples\n\n        def _mocked_draw_samples(batch, random_state):\n            samples = old_func(batch, random_state)\n            return geometriclib._JigsawSamples(\n                nb_rows=samples.nb_rows,\n                nb_cols=samples.nb_cols,\n                max_steps=samples.max_steps,\n                destinations=[destinations])\n\n        aug._draw_samples = _mocked_draw_samples\n\n        kpsoi_aug = aug(keypoints=kpsoi)\n\n        expected = kpsoi.deepcopy()\n        expected.shape = (20, 20, 3)\n        # (0.0, 0.0) to cell at bottom-right, 1px pad at top and left\n        expected.keypoints[0].x = 10.0 + (0.0 - 0.0) + 1.0\n        expected.keypoints[0].y = 10.0 + (0.0 - 0.0) + 1.0\n        # (5.5, 3.5) to cell at bottom-right, 1px pad at top and left\n        expected.keypoints[1].x = 10.0 + (5.5 - 0.0) + 1.0\n        expected.keypoints[1].y = 10.0 + (3.5 - 0.0) + 1.0\n        # (4.0, 12.5) not moved to other cell, but 1px pad at top and left\n        expected.keypoints[2].x = 4.0 + 1.0\n        expected.keypoints[2].y = 12.5 + 1.0\n        # (11.0, 11.0) to cell at top-left, 1px pad at top and left\n        expected.keypoints[3].x = 0.0 + (11.1 - 10.0) + 1.0\n        expected.keypoints[3].y = 0.0 + (11.2 - 10.0) + 1.0\n        # (12.1, 23.5) not moved to other cell, but 1px pad at top and left\n        expected.keypoints[4].x = 12.1 + 1.0\n        expected.keypoints[4].y = 23.5 + 1.0\n        expected.shape = (20, 20, 3)\n        assert kpsoi_aug.shape == (20, 20, 3)\n        assert np.allclose(kpsoi_aug.to_xy_array(), expected.to_xy_array())\n\n    def test_images_and_heatmaps_aligned(self):\n        nb_changed = 0\n        rs = iarandom.RNG(0)\n        for _ in np.arange(10):\n            aug = iaa.Jigsaw(nb_rows=(2, 5), nb_cols=(2, 5), max_steps=(0, 3))\n            image_small = rs.integers(0, 10, size=(10, 15)).astype(np.float32)\n            image_small = image_small / 10.0\n            image = ia.imresize_single_image(image_small, (20, 30),\n                                             interpolation=\"cubic\")\n            image = np.clip(image, 0, 1.0)\n            hm = ia.HeatmapsOnImage(image_small, shape=(20, 30))\n\n            images_aug, hms_aug = aug(images=[image, image, image],\n                                      heatmaps=[hm, hm, hm])\n\n            for image_aug, hm_aug in zip(images_aug, hms_aug):\n                # TODO added squeeze here because get_arr() falsely returns\n                #      (H,W,1) for 2D inputs\n                arr = np.squeeze(hm_aug.get_arr())\n                image_aug_rs = ia.imresize_single_image(\n                    image_aug.astype(np.float32),\n                    arr.shape[0:2],\n                    interpolation=\"cubic\")\n                image_aug_rs = np.clip(image_aug_rs, 0, 1.0)\n                overlap = np.average(np.isclose(image_aug_rs, arr))\n\n                assert overlap > 0.99\n                if not np.array_equal(arr, hm.get_arr()):\n                    nb_changed += 1\n        assert nb_changed > 5\n\n    def test_images_and_segmaps_aligned(self):\n        nb_changed = 0\n        rs = iarandom.RNG(0)\n        for _ in np.arange(10):\n            aug = iaa.Jigsaw(nb_rows=(2, 5), nb_cols=(2, 5), max_steps=(0, 3))\n            image_small = rs.integers(0, 10, size=(10, 15))\n            image = ia.imresize_single_image(image_small, (20, 30),\n                                             interpolation=\"nearest\")\n            image = image.astype(np.uint8)\n            segm = ia.SegmentationMapsOnImage(image_small, shape=(20, 30))\n\n            images_aug, sms_aug = aug(images=[image, image, image],\n                                      segmentation_maps=[segm, segm, segm])\n\n            for image_aug, sm_aug in zip(images_aug, sms_aug):\n                arr = sm_aug.get_arr()\n                image_aug_rs = ia.imresize_single_image(\n                    image_aug, arr.shape[0:2], interpolation=\"nearest\")\n                overlap = np.average(image_aug_rs == arr)\n\n                assert overlap > 0.99\n                if not np.array_equal(arr, segm.arr):\n                    nb_changed += 1\n        assert nb_changed > 5\n\n    def test_images_and_keypoints_aligned(self):\n        for i in np.arange(20):\n            aug = iaa.Jigsaw(nb_rows=(1, 3), nb_cols=(1, 3), max_steps=(2, 5),\n                             seed=i)\n            # make sure that these coords are not exactly at a grid cell\n            # border with any possibly sampled height/width in grid cells\n            y = 17.5\n            x = 25.5\n            kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=x, y=y)],\n                                        shape=(20, 30))\n            image = np.zeros((20, 30), dtype=np.uint8)\n            image[int(y), int(x)] = 255\n\n            images_aug, kpsois_aug = aug(images=[image, image, image],\n                                         keypoints=[kpsoi, kpsoi, kpsoi])\n\n            for image_aug, kpsoi_aug in zip(images_aug, kpsois_aug):\n                x_aug = kpsoi_aug.keypoints[0].x\n                y_aug = kpsoi_aug.keypoints[0].y\n                idx = np.argmax(image_aug)\n                y_aug_img, x_aug_img = np.unravel_index(idx,\n                                                        image_aug.shape)\n                dist = np.sqrt((x_aug - x_aug_img)**2 + (y_aug - y_aug_img)**2)\n                # best possible distance is about 0.7 as KP coords are in cell\n                # center and sampled coords are at cell top left\n                assert dist < 0.8\n\n    def test_no_error_for_1x1_grids(self):\n        aug = iaa.Jigsaw(nb_rows=1, nb_cols=1, max_steps=2)\n        image = np.mod(np.arange(19*19*3), 255).astype(np.uint8)\n        image = image.reshape((19, 19, 3))\n        kpsoi = ia.KeypointsOnImage.from_xy_array([\n            (0, 0),\n            (5.5, 3.5),\n            (4.0, 12.5),\n            (11.1, 11.2),\n            (12.1, 23.5)\n        ], shape=(19, 19, 3))\n\n        image_aug, kpsoi_aug = aug(image=image, keypoints=kpsoi)\n\n        assert np.array_equal(image_aug, image)\n        assert np.allclose(kpsoi_aug.to_xy_array(), kpsoi.to_xy_array())\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                for _ in sm.xrange(3):\n                    image = np.zeros(shape, dtype=np.uint8)\n                    aug = iaa.Jigsaw(nb_rows=2, nb_cols=2, max_steps=2)\n\n                    image_aug = aug(image=image)\n\n                    # (2, 2, [C]) here, because rows/cols are padded to be\n                    # multiple of nb_rows and nb_cols\n                    shape_exp = tuple([2, 2] + list(shape[2:]))\n                    assert image_aug.dtype.name == \"uint8\"\n                    assert np.array_equal(image_aug,\n                                          np.zeros(shape_exp, dtype=np.uint8))\n\n    def test_get_parameters(self):\n        aug = iaa.Jigsaw(nb_rows=1, nb_cols=2)\n        params = aug.get_parameters()\n        assert params[0] is aug.nb_rows\n        assert params[1] is aug.nb_cols\n        assert params[2] is aug.max_steps\n        assert params[3] is True\n\n    def test_pickleable(self):\n        aug = iaa.Jigsaw(nb_rows=(1, 4), nb_cols=(1, 4), max_steps=(1, 3))\n        runtest_pickleable_uint8_img(aug, iterations=20, shape=(32, 32, 3))\n"
  },
  {
    "path": "test/augmenters/test_imgcorruptlike.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\nimport functools\n\nimport numpy as np\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import random as iarandom\nfrom imgaug import parameters as iap\nfrom imgaug.testutils import runtest_pickleable_uint8_img\n\n# imagecorruptions cannot be installed in <=3.4 due to their\n# scikit-image requirement\nSUPPORTS_LIBRARY = (sys.version_info[0] == 3 and sys.version_info[1] >= 5)\n\nif SUPPORTS_LIBRARY:\n    import imagecorruptions\n    from imagecorruptions import corrupt\n\n\nclass Test_get_imgcorrupt_subset(unittest.TestCase):\n    @unittest.skipUnless(SUPPORTS_LIBRARY,\n                         \"imagecorruptions can only be tested for python 3.5+\")\n    def test_by_comparison_with_imagecorruptions(self):\n        subset_names = [\"common\", \"validation\", \"all\"]\n        for subset in subset_names:\n            with self.subTest(subset=subset):\n                func_names, funcs = iaa.imgcorruptlike.get_corruption_names(\n                    subset)\n                func_names_exp = imagecorruptions.get_corruption_names(subset)\n\n                assert func_names == func_names_exp\n                for func_name, func in zip(func_names, funcs):\n                    assert getattr(\n                        iaa.imgcorruptlike, \"apply_%s\" % (func_name,)\n                    ) is func\n\n    @unittest.skipUnless(SUPPORTS_LIBRARY,\n                         \"imagecorruptions can only be tested for python 3.5+\")\n    def test_subset_functions(self):\n        subset_names = [\"common\", \"validation\", \"all\"]\n        for subset in subset_names:\n            func_names, funcs = iaa.imgcorruptlike.get_corruption_names(subset)\n            image = np.mod(\n                np.arange(32*32*3), 256\n            ).reshape((32, 32, 3)).astype(np.uint8)\n\n            for func_name, func in zip(func_names, funcs):\n                with self.subTest(subset=subset, name=func_name):\n                    # don't verify here whether e.g. only seed 2 produces\n                    # different results from seed 1, because some methods\n                    # are only dependent on the severity\n                    image_aug1 = func(image, severity=5, seed=1)\n                    image_aug2 = func(image, severity=5, seed=1)\n                    image_aug3 = func(image, severity=1, seed=2)\n                    assert not np.array_equal(image, image_aug1)\n                    assert not np.array_equal(image, image_aug2)\n                    assert not np.array_equal(image_aug2, image_aug3)\n                    assert np.array_equal(image_aug1, image_aug2)\n\n\nclass _CompareFuncWithImageCorruptions(unittest.TestCase):\n    def _test_by_comparison_with_imagecorruptions(\n            self,\n            fname,\n            shapes=((64, 64), (64, 64, 1), (64, 64, 3)),\n            dtypes=(\"uint8\",),\n            severities=(1, 2, 3, 4, 5),\n            seeds=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)):\n        for shape in shapes:\n            for dtype in dtypes:\n                for severity in severities:\n                    for seed in seeds:\n                        with self.subTest(shape=shape, severity=severity,\n                                          seed=seed):\n                            image_imgaug = self.create_image_imgaug(\n                                shape, dtype, 1000 + seed)\n                            image_imgcor = np.copy(image_imgaug)\n\n                            self._run_single_comparison_test(\n                                fname, image_imgaug, image_imgcor, severity,\n                                seed)\n\n    @classmethod\n    def create_image_imgaug(cls, shape, dtype, seed, tile=None):\n        rng = iarandom.RNG(1000 + seed)\n\n        if dtype.startswith(\"uint\"):\n            image = rng.integers(0, 256, size=shape, dtype=dtype)\n        else:\n            assert dtype.startswith(\"float\")\n            image = rng.uniform(0.0, 1.0, size=shape)\n            image = image.astype(dtype)\n\n        if tile is not None:\n            image = np.tile(image, tile)\n\n        return image\n\n    @classmethod\n    def _run_single_comparison_test(cls, fname, image_imgaug, image_imgcor,\n                                    severity, seed):\n        image_imgaug_sum = np.sum(image_imgaug)\n        image_imgcor_sum = np.sum(image_imgcor)\n\n        image_aug, image_aug_exp = cls._generate_augmented_images(\n            fname, image_imgaug, image_imgcor, severity, seed)\n\n        # assert that the original image is unchanged,\n        # i.e. it was not augmented in-place\n        assert np.isclose(np.sum(image_imgcor), image_imgcor_sum, rtol=0,\n                          atol=1e-4)\n        assert np.isclose(np.sum(image_imgaug), image_imgaug_sum, rtol=0,\n                          atol=1e-4)\n\n        # assert that the functions returned numpy arrays and not PIL images\n        assert ia.is_np_array(image_aug_exp)\n        assert ia.is_np_array(image_aug)\n\n        assert image_aug.shape == image_imgaug.shape\n        assert image_aug.dtype.name == image_aug_exp.dtype.name\n\n        atol = 1e-4  # set this to 0.5+1e-4 if output is converted to uint8\n        assert np.allclose(image_aug, image_aug_exp, rtol=0, atol=atol)\n\n    @classmethod\n    def _generate_augmented_images(cls, fname, image_imgaug, image_imgcor,\n                                   severity, seed):\n        func_imgaug = getattr(\n            iaa.imgcorruptlike,\n            \"apply_%s\" % (fname,))\n        func_imagecor = functools.partial(corrupt, corruption_name=fname)\n\n        with iarandom.temporary_numpy_seed(seed):\n            image_aug_exp = func_imagecor(image_imgcor, severity=severity)\n            if not ia.is_np_array(image_aug_exp):\n                image_aug_exp = np.asarray(image_aug_exp)\n            if image_imgcor.ndim == 2:\n                image_aug_exp = image_aug_exp[:, :, 0]\n            elif image_imgcor.shape[-1] == 1:\n                image_aug_exp = image_aug_exp[:, :, 0:1]\n\n        image_aug = func_imgaug(image_imgaug, severity=severity,\n                                seed=seed)\n\n        return image_aug, image_aug_exp\n\n\n@unittest.skipUnless(SUPPORTS_LIBRARY,\n                     \"imagecorruptions can only be tested for python 3.5+\")\nclass Test_apply_functions(_CompareFuncWithImageCorruptions):\n    def test_apply_gaussian_noise(self):\n        self._test_by_comparison_with_imagecorruptions(\"gaussian_noise\")\n\n    def test_apply_shot_noise(self):\n        self._test_by_comparison_with_imagecorruptions(\"shot_noise\")\n\n    def test_apply_impulse_noise(self):\n        self._test_by_comparison_with_imagecorruptions(\"impulse_noise\")\n\n    def test_apply_speckle_noise(self):\n        self._test_by_comparison_with_imagecorruptions(\"speckle_noise\")\n\n    def test_apply_gaussian_blur(self):\n        self._test_by_comparison_with_imagecorruptions(\"gaussian_blur\")\n\n    def test_apply_glass_blur(self):\n        # glass_blur() is extremely slow, so we run only a reduced set\n        # of tests here\n        self._test_by_comparison_with_imagecorruptions(\n            \"glass_blur\",\n            shapes=[(32, 32), (32, 32, 1), (32, 32, 3)],\n            severities=[1, 4],\n            seeds=[1, 2, 3])\n\n    def test_apply_defocus_blur(self):\n        self._test_by_comparison_with_imagecorruptions(\n            \"defocus_blur\")\n\n    def test_apply_motion_blur(self):\n        self._test_by_comparison_with_imagecorruptions(\n            \"motion_blur\")\n\n    def test_apply_zoom_blur(self):\n        self._test_by_comparison_with_imagecorruptions(\n            \"zoom_blur\")\n\n    def test_apply_fog(self):\n        self._test_by_comparison_with_imagecorruptions(\n            \"fog\")\n\n    def test_apply_frost(self):\n        self._test_by_comparison_with_imagecorruptions(\n            \"frost\",\n            severities=[1, 5],\n            seeds=[1, 5, 10])\n\n    def test_apply_snow(self):\n        self._test_by_comparison_with_imagecorruptions(\n            \"snow\")\n\n    def test_apply_spatter(self):\n        self._test_by_comparison_with_imagecorruptions(\n            \"spatter\")\n\n    def test_apply_contrast(self):\n        self._test_by_comparison_with_imagecorruptions(\"contrast\")\n\n    def test_apply_brightness(self):\n        self._test_by_comparison_with_imagecorruptions(\"brightness\")\n\n    def test_apply_saturate(self):\n        self._test_by_comparison_with_imagecorruptions(\n            \"saturate\")\n\n    def test_apply_jpeg_compression(self):\n        self._test_by_comparison_with_imagecorruptions(\n            \"jpeg_compression\")\n\n    def test_apply_pixelate(self):\n        self._test_by_comparison_with_imagecorruptions(\n            \"pixelate\")\n\n    def test_apply_elastic_transform(self):\n        self._test_by_comparison_with_imagecorruptions(\n            \"elastic_transform\")\n\n\n@unittest.skipUnless(SUPPORTS_LIBRARY,\n                     \"imagecorruptions can only be tested for python 3.5+\")\nclass TestAugmenters(unittest.TestCase):\n    @classmethod\n    def _test_augmenter(cls, augmenter_name, func_expected,\n                        dependent_on_seed):\n        # this test verifies:\n        # - called function seems to be the expected function\n        # - images produced by augmenter match images produced by function\n        # - a different seed (and sometimes severity) will lead to a\n        #   different image\n        # - augmenter can be pickled\n        severity = 5\n        aug_cls = getattr(iaa.imgcorruptlike, augmenter_name)\n        image = np.mod(\n            np.arange(32*32*3), 256\n        ).reshape((32, 32, 3)).astype(np.uint8)\n\n        with iap.no_prefetching():\n            rng = iarandom.RNG(1)\n            # Replay sampling of severities.\n            # Even for deterministic values this is required as currently\n            # there is an advance() at the end of each draw_samples().\n            _ = iap.Deterministic(1).draw_samples((1,), rng)\n\n            # As for the functions above, we can't just change the seed value\n            # to get different augmentations as many functions are dependend\n            # only on the severity. So we change only for some functions only\n            # the seed and for the others severity+seed.\n            image_aug1 = aug_cls(severity=severity, seed=1)(image=image)\n            image_aug2 = aug_cls(severity=severity, seed=1)(image=image)\n            if dependent_on_seed:\n                image_aug3 = aug_cls(severity=severity, seed=2)(\n                    image=image)\n            else:\n                image_aug3 = aug_cls(severity=severity-1, seed=2)(\n                    image=image)\n            image_aug_exp = func_expected(\n                image,\n                severity=severity,\n                seed=rng.generate_seed_())\n\n        assert aug_cls(severity=severity).func is func_expected\n        assert np.array_equal(image_aug1, image_aug_exp)\n        assert np.array_equal(image_aug2, image_aug_exp)\n        assert not np.array_equal(image_aug3, image_aug2)\n\n        # pickling test\n        aug = aug_cls(severity=(1, 5))\n        runtest_pickleable_uint8_img(aug, shape=(32, 32, 3))\n\n    def test_gaussian_noise(self):\n        self._test_augmenter(\"GaussianNoise\",\n                             iaa.imgcorruptlike.apply_gaussian_noise,\n                             True)\n\n    def test_shot_noise(self):\n        self._test_augmenter(\"ShotNoise\",\n                             iaa.imgcorruptlike.apply_shot_noise,\n                             True)\n\n    def test_impulse_noise(self):\n        self._test_augmenter(\"ImpulseNoise\",\n                             iaa.imgcorruptlike.apply_impulse_noise,\n                             True)\n\n    def test_speckle_noise(self):\n        self._test_augmenter(\"SpeckleNoise\",\n                             iaa.imgcorruptlike.apply_speckle_noise,\n                             True)\n\n    def test_gaussian_blur(self):\n        self._test_augmenter(\"GaussianBlur\",\n                             iaa.imgcorruptlike.apply_gaussian_blur,\n                             False)\n\n    def test_glass_blur(self):\n        self._test_augmenter(\"GlassBlur\",\n                             iaa.imgcorruptlike.apply_glass_blur,\n                             False)\n\n    def test_defocus_blur(self):\n        self._test_augmenter(\"DefocusBlur\",\n                             iaa.imgcorruptlike.apply_defocus_blur,\n                             False)\n\n    def test_motion_blur(self):\n        self._test_augmenter(\"MotionBlur\",\n                             iaa.imgcorruptlike.apply_motion_blur,\n                             False)\n\n    def test_zoom_blur(self):\n        self._test_augmenter(\"ZoomBlur\",\n                             iaa.imgcorruptlike.apply_zoom_blur,\n                             False)\n\n    def test_fog(self):\n        self._test_augmenter(\"Fog\",\n                             iaa.imgcorruptlike.apply_fog,\n                             True)\n\n    def test_frost(self):\n        self._test_augmenter(\"Frost\",\n                             iaa.imgcorruptlike.apply_frost,\n                             False)\n\n    def test_snow(self):\n        self._test_augmenter(\"Snow\",\n                             iaa.imgcorruptlike.apply_snow,\n                             True)\n\n    def test_spatter(self):\n        self._test_augmenter(\"Spatter\",\n                             iaa.imgcorruptlike.apply_spatter,\n                             True)\n\n    def test_contrast(self):\n        self._test_augmenter(\"Contrast\",\n                             iaa.imgcorruptlike.apply_contrast,\n                             False)\n\n    def test_brightness(self):\n        self._test_augmenter(\"Brightness\",\n                             iaa.imgcorruptlike.apply_brightness,\n                             False)\n\n    def test_saturate(self):\n        self._test_augmenter(\"Saturate\",\n                             iaa.imgcorruptlike.apply_saturate,\n                             False)\n\n    def test_jpeg_compression(self):\n        self._test_augmenter(\"JpegCompression\",\n                             iaa.imgcorruptlike.apply_jpeg_compression,\n                             False)\n\n    def test_pixelate(self):\n        self._test_augmenter(\"Pixelate\",\n                             iaa.imgcorruptlike.apply_pixelate,\n                             False)\n\n    def test_elastic_transform(self):\n        self._test_augmenter(\"ElasticTransform\",\n                             iaa.imgcorruptlike.apply_elastic_transform,\n                             True)\n"
  },
  {
    "path": "test/augmenters/test_meta.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport os\nimport warnings\nimport sys\nimport itertools\nimport copy\nfrom abc import ABCMeta, abstractmethod\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\ntry:\n    import cPickle as pickle\nexcept ImportError:\n    import pickle\n\nimport numpy as np\nimport six\nimport six.moves as sm\nimport cv2\nimport PIL.Image\nimport imageio\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug import dtypes as iadt\nfrom imgaug import random as iarandom\nfrom imgaug.testutils import (create_random_images, create_random_keypoints,\n                              array_equal_lists, keypoints_equal, reseed,\n                              assert_cbaois_equal,\n                              runtest_pickleable_uint8_img,\n                              TemporaryDirectory, is_parameter_instance)\nfrom imgaug.augmentables.heatmaps import HeatmapsOnImage\nfrom imgaug.augmentables.segmaps import SegmentationMapsOnImage\nfrom imgaug.augmentables.lines import LineString, LineStringsOnImage\nfrom imgaug.augmentables.polys import _ConcavePolygonRecoverer\nfrom imgaug.augmentables.batches import _BatchInAugmentation\n\n\nIS_PY36_OR_HIGHER = (sys.version_info[0] == 3 and sys.version_info[1] >= 6)\n\n\nclass _InplaceDummyAugmenterImgsArray(iaa.meta.Augmenter):\n    def __init__(self, addval):\n        super(_InplaceDummyAugmenterImgsArray, self).__init__()\n        self.addval = addval\n\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        batch.images += self.addval\n        return batch\n\n    def get_parameters(self):\n        return []\n\n\nclass _InplaceDummyAugmenterImgsList(iaa.meta.Augmenter):\n    def __init__(self, addval):\n        super(_InplaceDummyAugmenterImgsList, self).__init__()\n        self.addval = addval\n\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        assert len(batch.images) > 0\n        for i in range(len(batch.images)):\n            batch.images[i] += self.addval\n        return batch\n\n    def get_parameters(self):\n        return []\n\n\nclass _InplaceDummyAugmenterSegMaps(iaa.meta.Augmenter):\n    def __init__(self, addval):\n        super(_InplaceDummyAugmenterSegMaps, self).__init__()\n        self.addval = addval\n\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        assert len(batch.segmentation_maps) > 0\n        for i in range(len(batch.segmentation_maps)):\n            batch.segmentation_maps[i].arr += self.addval\n        return batch\n\n    def get_parameters(self):\n        return []\n\n\nclass _InplaceDummyAugmenterKeypoints(iaa.meta.Augmenter):\n    def __init__(self, x, y):\n        super(_InplaceDummyAugmenterKeypoints, self).__init__()\n        self.x = x\n        self.y = y\n\n    def _augment_batch_(self, batch, random_state, parents, hooks):\n        assert len(batch.keypoints) > 0\n        for i in range(len(batch.keypoints)):\n            kpsoi = batch.keypoints[i]\n            for j in range(len(kpsoi)):\n                batch.keypoints[i].keypoints[j].x += self.x\n                batch.keypoints[i].keypoints[j].y += self.y\n        return batch\n\n    def get_parameters(self):\n        return []\n\n\nclass TestIdentity(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_images(self):\n        aug = iaa.Identity()\n        images = create_random_images((16, 70, 50, 3))\n\n        observed = aug.augment_images(images)\n\n        expected = images\n        assert np.array_equal(observed, expected)\n\n    def test_images_deterministic(self):\n        aug_det = iaa.Identity().to_deterministic()\n        images = create_random_images((16, 70, 50, 3))\n\n        observed = aug_det.augment_images(images)\n\n        expected = images\n        assert np.array_equal(observed, expected)\n\n    def test_heatmaps(self):\n        aug = iaa.Identity()\n        heatmaps_arr = np.linspace(0.0, 1.0, 2*2, dtype=\"float32\")\\\n            .reshape((2, 2, 1))\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(2, 2, 3))\n\n        observed = aug.augment_heatmaps(heatmaps)\n\n        assert np.allclose(observed.arr_0to1, heatmaps.arr_0to1)\n\n    def test_heatmaps_deterministic(self):\n        aug_det = iaa.Identity().to_deterministic()\n        heatmaps_arr = np.linspace(0.0, 1.0, 2*2, dtype=\"float32\")\\\n            .reshape((2, 2, 1))\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(2, 2, 3))\n\n        observed = aug_det.augment_heatmaps(heatmaps)\n\n        assert np.allclose(observed.arr_0to1, heatmaps.arr_0to1)\n\n    def test_segmentation_maps(self):\n        aug = iaa.Identity()\n        segmaps_arr = np.arange(2*2).reshape((2, 2, 1)).astype(np.int32)\n        segmaps = SegmentationMapsOnImage(segmaps_arr, shape=(2, 2, 3))\n\n        observed = aug.augment_segmentation_maps(segmaps)\n\n        assert np.array_equal(observed.arr, segmaps.arr)\n\n    def test_segmentation_maps_deterministic(self):\n        aug_det = iaa.Identity().to_deterministic()\n        segmaps_arr = np.arange(2*2).reshape((2, 2, 1)).astype(np.int32)\n        segmaps = SegmentationMapsOnImage(segmaps_arr, shape=(2, 2, 3))\n\n        observed = aug_det.augment_segmentation_maps(segmaps)\n\n        assert np.array_equal(observed.arr, segmaps.arr)\n\n    def test_keypoints(self):\n        aug = iaa.Identity()\n        keypoints = create_random_keypoints((16, 70, 50, 3), 4)\n\n        observed = aug.augment_keypoints(keypoints)\n\n        assert_cbaois_equal(observed, keypoints)\n\n    def test_keypoints_deterministic(self):\n        aug_det = iaa.Identity().to_deterministic()\n        keypoints = create_random_keypoints((16, 70, 50, 3), 4)\n\n        observed = aug_det.augment_keypoints(keypoints)\n\n        assert_cbaois_equal(observed, keypoints)\n\n    def test_polygons(self):\n        aug = iaa.Identity()\n        polygon = ia.Polygon([(10, 10), (30, 10), (30, 50), (10, 50)])\n        psoi = ia.PolygonsOnImage([polygon], shape=(100, 75, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        assert_cbaois_equal(observed, psoi)\n\n    def test_polygons_deterministic(self):\n        aug_det = iaa.Identity().to_deterministic()\n        polygon = ia.Polygon([(10, 10), (30, 10), (30, 50), (10, 50)])\n        psoi = ia.PolygonsOnImage([polygon], shape=(100, 75, 3))\n\n        observed = aug_det.augment_polygons(psoi)\n\n        assert_cbaois_equal(observed, psoi)\n\n    def test_line_strings(self):\n        aug = iaa.Identity()\n        ls = LineString([(10, 10), (30, 10), (30, 50), (10, 50)])\n        lsoi = LineStringsOnImage([ls], shape=(100, 75, 3))\n\n        observed = aug.augment_line_strings(lsoi)\n\n        assert_cbaois_equal(observed, lsoi)\n\n    def test_line_strings_deterministic(self):\n        aug_det = iaa.Identity().to_deterministic()\n        ls = LineString([(10, 10), (30, 10), (30, 50), (10, 50)])\n        lsoi = LineStringsOnImage([ls], shape=(100, 75, 3))\n\n        observed = aug_det.augment_line_strings(lsoi)\n\n        assert_cbaois_equal(observed, lsoi)\n\n    def test_bounding_boxes(self):\n        aug = iaa.Identity()\n        bbs = ia.BoundingBox(x1=10, y1=10, x2=30, y2=50)\n        bbsoi = ia.BoundingBoxesOnImage([bbs], shape=(100, 75, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        assert_cbaois_equal(observed, bbsoi)\n\n    def test_bounding_boxes_deterministic(self):\n        aug_det = iaa.Identity().to_deterministic()\n        bbs = ia.BoundingBox(x1=10, y1=10, x2=30, y2=50)\n        bbsoi = ia.BoundingBoxesOnImage([bbs], shape=(100, 75, 3))\n\n        observed = aug_det.augment_bounding_boxes(bbsoi)\n\n        assert_cbaois_equal(observed, bbsoi)\n\n    def test_keypoints_empty(self):\n        aug = iaa.Identity()\n        kpsoi = ia.KeypointsOnImage([], shape=(4, 5, 3))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        assert_cbaois_equal(observed, kpsoi)\n\n    def test_polygons_empty(self):\n        aug = iaa.Identity()\n        psoi = ia.PolygonsOnImage([], shape=(4, 5, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        assert_cbaois_equal(observed, psoi)\n\n    def test_line_strings_empty(self):\n        aug = iaa.Identity()\n        lsoi = ia.LineStringsOnImage([], shape=(4, 5, 3))\n\n        observed = aug.augment_line_strings(lsoi)\n\n        assert_cbaois_equal(observed, lsoi)\n\n    def test_bounding_boxes_empty(self):\n        aug = iaa.Identity()\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(4, 5, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        assert_cbaois_equal(observed, bbsoi)\n\n    def test_get_parameters(self):\n        assert iaa.Identity().get_parameters() == []\n\n    def test_other_dtypes_bool(self):\n        aug = iaa.Identity()\n        image = np.zeros((3, 3), dtype=bool)\n        image[0, 0] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.type == image.dtype.type\n        assert np.all(image_aug == image)\n\n    def test_other_dtypes_uint_int(self):\n        aug = iaa.Identity()\n\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.array_equal(image_aug, image)\n\n    def test_other_dtypes_float(self):\n        aug = iaa.Identity()\n\n        try:\n            f128 = [np.dtype(\"float128\").name]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n\n        for dtype, value in zip(dtypes, values):\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.all(image_aug == image)\n\n    def test_pickleable(self):\n        aug = iaa.Noop()\n        runtest_pickleable_uint8_img(aug, iterations=2)\n\n\nclass TestNoop(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.Noop()\n        assert isinstance(aug, iaa.Identity)\n\n    def test_images(self):\n        image = np.mod(np.arange(10*10*3), 255)\n        image = image.astype(np.uint8).reshape((10, 10, 3))\n\n        image_aug = iaa.Noop()(image=image)\n\n        assert np.array_equal(image, image_aug)\n\n\n# TODO add tests for line strings\nclass TestLambda(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def base_img(self):\n        base_img = np.array([[0, 0, 1],\n                             [0, 0, 1],\n                             [0, 1, 1]], dtype=np.uint8)\n        base_img = base_img[:, :, np.newaxis]\n        return base_img\n\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([[0.0, 0.0, 1.0],\n                                   [0.0, 0.0, 1.0],\n                                   [0.0, 1.0, 1.0]])\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n        return heatmaps\n\n    @property\n    def heatmaps_aug(self):\n        heatmaps_arr_aug = np.float32([[0.5, 0.0, 1.0],\n                                       [0.0, 0.0, 1.0],\n                                       [0.0, 1.0, 1.0]])\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr_aug, shape=(3, 3, 3))\n        return heatmaps\n\n    @property\n    def segmentation_maps(self):\n        segmaps_arr = np.int32([[0, 0, 1],\n                                [0, 0, 1],\n                                [0, 1, 1]])\n        segmaps = SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n        return segmaps\n\n    @property\n    def segmentation_maps_aug(self):\n        segmaps_arr_aug = np.int32([[1, 1, 2],\n                                    [1, 1, 2],\n                                    [1, 2, 2]])\n        segmaps = SegmentationMapsOnImage(segmaps_arr_aug, shape=(3, 3, 3))\n        return segmaps\n\n    @property\n    def keypoints(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        kpsoi = [ia.KeypointsOnImage(kps, shape=(3, 3, 3))]\n        return kpsoi\n\n    @property\n    def keypoints_aug(self):\n        expected_kps = [ia.Keypoint(x=1, y=0), ia.Keypoint(x=2, y=1),\n                        ia.Keypoint(x=0, y=2)]\n        expected = [ia.KeypointsOnImage(expected_kps, shape=(3, 3, 3))]\n        return expected\n\n    @property\n    def polygons(self):\n        poly = ia.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])\n        psois = [ia.PolygonsOnImage([poly], shape=(3, 3, 3))]\n        return psois\n\n    @property\n    def polygons_aug(self):\n        expected_poly = ia.Polygon([(1, 2), (3, 2), (3, 4), (1, 4)])\n        expected_psoi = [ia.PolygonsOnImage([expected_poly], shape=(3, 3, 3))]\n        return expected_psoi\n\n    @property\n    def lsoi(self):\n        ls = ia.LineString([(0, 0), (2, 0), (2, 2), (0, 2)])\n        lsois = [ia.LineStringsOnImage([ls], shape=(3, 3, 3))]\n        return lsois\n\n    @property\n    def lsoi_aug(self):\n        ls = ia.LineString([(1, 2), (3, 2), (3, 4), (1, 4)])\n        lsois = [ia.LineStringsOnImage([ls], shape=(3, 3, 3))]\n        return lsois\n\n    @property\n    def bbsoi(self):\n        bb = ia.BoundingBox(x1=0, y1=1, x2=3, y2=4)\n        bbsois = [ia.BoundingBoxesOnImage([bb], shape=(3, 3, 3))]\n        return bbsois\n\n    @property\n    def bbsoi_aug(self):\n        bb = ia.BoundingBox(x1=0+1, y1=1+2, x2=3+1, y2=4+2)\n        bbsois = [ia.BoundingBoxesOnImage([bb], shape=(3, 3, 3))]\n        return bbsois\n\n    @classmethod\n    def func_images(cls, images, random_state, parents, hooks):\n        if isinstance(images, list):\n            images = [image + 1 for image in images]\n        else:\n            images = images + 1\n        return images\n\n    @classmethod\n    def func_heatmaps(cls, heatmaps, random_state, parents, hooks):\n        heatmaps[0].arr_0to1[0, 0] += 0.5\n        return heatmaps\n\n    @classmethod\n    def func_segmaps(cls, segmaps, random_state, parents, hooks):\n        segmaps[0].arr += 1\n        return segmaps\n\n    @classmethod\n    def func_keypoints(cls, keypoints_on_images, random_state, parents, hooks):\n        for keypoints_on_image in keypoints_on_images:\n            for kp in keypoints_on_image.keypoints:\n                kp.x = (kp.x + 1) % 3\n        return keypoints_on_images\n\n    @classmethod\n    def func_polygons(cls, polygons_on_images, random_state, parents, hooks):\n        if len(polygons_on_images[0].polygons) == 0:\n            return [ia.PolygonsOnImage([], shape=polygons_on_images[0].shape)]\n        new_exterior = np.copy(polygons_on_images[0].polygons[0].exterior)\n        new_exterior[:, 0] += 1\n        new_exterior[:, 1] += 2\n        return [\n            ia.PolygonsOnImage([ia.Polygon(new_exterior)],\n                               shape=polygons_on_images[0].shape)\n        ]\n\n    @classmethod\n    def func_line_strings(cls, line_strings_on_images, random_state, parents,\n                          hooks):\n        if line_strings_on_images[0].empty:\n            return [ia.LineStringsOnImage(\n                [], shape=line_strings_on_images[0].shape)]\n        new_coords = np.copy(line_strings_on_images[0].items[0].coords)\n        new_coords[:, 0] += 1\n        new_coords[:, 1] += 2\n        return [\n            ia.LineStringsOnImage(\n                [ia.LineString(new_coords)],\n                shape=line_strings_on_images[0].shape)\n        ]\n\n    @classmethod\n    def func_bbs(cls, bounding_boxes_on_images, random_state, parents, hooks):\n        if bounding_boxes_on_images[0].empty:\n            return [\n                ia.BoundingBoxesOnImage(\n                    [], shape=bounding_boxes_on_images[0].shape)\n            ]\n        new_coords = np.copy(bounding_boxes_on_images[0].items[0].coords)\n        new_coords[:, 0] += 1\n        new_coords[:, 1] += 2\n        return [\n            ia.BoundingBoxesOnImage(\n                [ia.BoundingBox(x1=new_coords[0][0], y1=new_coords[0][1],\n                                x2=new_coords[1][0], y2=new_coords[1][1])],\n                shape=bounding_boxes_on_images[0].shape)\n        ]\n\n    def test_images(self):\n        image = self.base_img\n        expected = image + 1\n        aug = iaa.Lambda(func_images=self.func_images)\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_image(image)\n\n            assert np.array_equal(observed, expected)\n\n    def test_images_deterministic(self):\n        image = self.base_img\n        expected = image + 1\n        aug_det = iaa.Lambda(func_images=self.func_images).to_deterministic()\n\n        for _ in sm.xrange(3):\n            observed = aug_det.augment_image(image)\n\n            assert np.array_equal(observed, expected)\n\n    def test_images_list(self):\n        image = self.base_img\n        expected = [image + 1]\n        aug = iaa.Lambda(func_images=self.func_images)\n\n        observed = aug.augment_images([image])\n\n        assert array_equal_lists(observed, expected)\n\n    def test_images_list_deterministic(self):\n        image = self.base_img\n        expected = [image + 1]\n        aug_det = iaa.Lambda(func_images=self.func_images).to_deterministic()\n\n        observed = aug_det.augment_images([image])\n\n        assert array_equal_lists(observed, expected)\n\n    def test_heatmaps(self):\n        heatmaps = self.heatmaps\n        heatmaps_arr_aug = self.heatmaps_aug.get_arr()\n        aug = iaa.Lambda(func_heatmaps=self.func_heatmaps)\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_heatmaps(heatmaps)\n\n            assert observed.shape == (3, 3, 3)\n            assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n            assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n            assert np.allclose(observed.get_arr(), heatmaps_arr_aug)\n\n    def test_heatmaps_deterministic(self):\n        heatmaps = self.heatmaps\n        heatmaps_arr_aug = self.heatmaps_aug.get_arr()\n        aug_det = iaa.Lambda(func_heatmaps=self.func_heatmaps)\\\n            .to_deterministic()\n\n        for _ in sm.xrange(3):\n            observed = aug_det.augment_heatmaps(heatmaps)\n\n            assert observed.shape == (3, 3, 3)\n            assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n            assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n            assert np.allclose(observed.get_arr(), heatmaps_arr_aug)\n\n    def test_segmentation_maps(self):\n        segmaps = self.segmentation_maps\n        segmaps_arr_aug = self.segmentation_maps_aug.get_arr()\n        aug = iaa.Lambda(func_segmentation_maps=self.func_segmaps)\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_segmentation_maps(segmaps)\n\n            assert observed.shape == (3, 3, 3)\n            assert np.array_equal(observed.get_arr(), segmaps_arr_aug)\n\n    def test_segmentation_maps_deterministic(self):\n        segmaps = self.segmentation_maps\n        segmaps_arr_aug = self.segmentation_maps_aug.get_arr()\n        aug_det = iaa.Lambda(func_segmentation_maps=self.func_segmaps)\\\n            .to_deterministic()\n\n        for _ in sm.xrange(3):\n            observed = aug_det.augment_segmentation_maps(segmaps)\n\n            assert observed.shape == (3, 3, 3)\n            assert np.array_equal(observed.get_arr(), segmaps_arr_aug)\n\n    def test_keypoints(self):\n        kpsoi = self.keypoints\n        aug = iaa.Lambda(func_keypoints=self.func_keypoints)\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_keypoints(kpsoi)\n\n            expected = self.keypoints_aug\n            assert_cbaois_equal(observed, expected)\n\n    def test_keypoints_deterministic(self):\n        kpsoi = self.keypoints\n        aug = iaa.Lambda(func_keypoints=self.func_keypoints)\n        aug = aug.to_deterministic()\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_keypoints(kpsoi)\n\n            expected = self.keypoints_aug\n            assert_cbaois_equal(observed, expected)\n\n    def test_polygons(self):\n        psois = self.polygons\n        aug = iaa.Lambda(func_polygons=self.func_polygons)\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_polygons(psois)\n\n            expected_psoi = self.polygons_aug\n            assert_cbaois_equal(observed, expected_psoi)\n\n    def test_polygons_deterministic(self):\n        psois = self.polygons\n\n        aug = iaa.Lambda(func_polygons=self.func_polygons)\n        aug = aug.to_deterministic()\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_polygons(psois)\n\n            expected_psoi = self.polygons_aug\n            assert_cbaois_equal(observed, expected_psoi)\n\n    def test_line_strings(self):\n        lsois = self.lsoi\n        aug = iaa.Lambda(func_line_strings=self.func_line_strings)\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_line_strings(lsois)\n\n            expected_lsoi = self.lsoi_aug\n            assert_cbaois_equal(observed, expected_lsoi)\n\n    def test_line_strings_deterministic(self):\n        lsois = self.lsoi\n\n        aug = iaa.Lambda(func_line_strings=self.func_line_strings)\n        aug = aug.to_deterministic()\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_line_strings(lsois)\n\n            expected_lsoi = self.lsoi_aug\n            assert_cbaois_equal(observed, expected_lsoi)\n\n    def test_bounding_boxes(self):\n        bbsoi = self.bbsoi\n        aug = iaa.Lambda(func_bounding_boxes=self.func_bbs)\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_bounding_boxes(bbsoi)\n\n            expected = self.bbsoi_aug\n            assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes_deterministic(self):\n        bbsoi = self.bbsoi\n        aug = iaa.Lambda(func_bounding_boxes=self.func_bbs)\n        aug = aug.to_deterministic()\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_bounding_boxes(bbsoi)\n\n            expected = self.bbsoi_aug\n            assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes_x1_x2_coords_can_get_flipped(self):\n        # Verify that if any augmented BB ends up with x1 > x2 that the\n        # x-coordinates will be flipped to ensure that x1 is always below x2\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)\n        ], shape=(10, 10, 3))\n\n        def _func_bbs(bounding_boxes_on_images, random_state, parents, hooks):\n            bounding_boxes_on_images[0].bounding_boxes[0].x1 += 10\n            return bounding_boxes_on_images\n\n        aug = iaa.Lambda(func_bounding_boxes=_func_bbs)\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_bounding_boxes(bbsoi)\n\n            assert np.allclose(\n                observed.bounding_boxes[0].coords,\n                [(2, 1), (0+10, 3)]\n            )\n\n    def test_bounding_boxes_y1_y2_coords_can_get_flipped(self):\n        # Verify that if any augmented BB ends up with y1 > y2 that the\n        # x-coordinates will be flipped to ensure that y1 is always below y2\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)\n        ], shape=(10, 10, 3))\n\n        def _func_bbs(bounding_boxes_on_images, random_state, parents, hooks):\n            bounding_boxes_on_images[0].bounding_boxes[0].y1 += 10\n            return bounding_boxes_on_images\n\n        aug = iaa.Lambda(func_bounding_boxes=_func_bbs)\n\n        for _ in sm.xrange(3):\n            observed = aug.augment_bounding_boxes(bbsoi)\n\n            assert np.allclose(\n                observed.bounding_boxes[0].coords,\n                [(0, 3), (2, 1+10)]\n            )\n\n    def test_keypoints_empty(self):\n        kpsoi = ia.KeypointsOnImage([], shape=(1, 2, 3))\n        aug = iaa.Lambda(func_keypoints=self.func_keypoints)\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        assert_cbaois_equal(observed, kpsoi)\n\n    def test_polygons_empty(self):\n        psoi = ia.PolygonsOnImage([], shape=(1, 2, 3))\n        aug = iaa.Lambda(func_polygons=self.func_polygons)\n\n        observed = aug.augment_polygons(psoi)\n\n        assert_cbaois_equal(observed, psoi)\n\n    def test_line_strings_empty(self):\n        lsoi = ia.LineStringsOnImage([], shape=(1, 2, 3))\n        aug = iaa.Lambda(func_line_strings=self.func_line_strings)\n\n        observed = aug.augment_line_strings(lsoi)\n\n        assert_cbaois_equal(observed, lsoi)\n\n    def test_bounding_boxes_empty(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(1, 2, 3))\n        aug = iaa.Lambda(func_bounding_boxes=self.func_bbs)\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        assert_cbaois_equal(observed, bbsoi)\n\n    # TODO add tests when funcs are not set in Lambda\n\n    def test_other_dtypes_bool(self):\n        def func_images(images, random_state, parents, hooks):\n            aug = iaa.Flipud(1.0)  # flipud is know to work with all dtypes\n            return aug.augment_images(images)\n\n        aug = iaa.Lambda(func_images=func_images)\n\n        image = np.zeros((3, 3), dtype=bool)\n        image[0, 0] = True\n        expected = np.zeros((3, 3), dtype=bool)\n        expected[2, 0] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.name == \"bool\"\n        assert np.all(image_aug == expected)\n\n    def test_other_dtypes_uint_int(self):\n        def func_images(images, random_state, parents, hooks):\n            aug = iaa.Flipud(1.0)  # flipud is know to work with all dtypes\n            return aug.augment_images(images)\n\n        aug = iaa.Lambda(func_images=func_images)\n\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n                expected = np.zeros((3, 3), dtype=dtype)\n                expected[2, 0] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.array_equal(image_aug, expected)\n\n    def test_other_dtypes_float(self):\n        def func_images(images, random_state, parents, hooks):\n            aug = iaa.Flipud(1.0)  # flipud is know to work with all dtypes\n            return aug.augment_images(images)\n\n        aug = iaa.Lambda(func_images=func_images)\n\n        try:\n            f128 = [np.dtype(\"float128\").name]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n\n        for dtype, value in zip(dtypes, values):\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n                expected = np.zeros((3, 3), dtype=dtype)\n                expected[2, 0] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.all(image_aug == expected)\n\n    def test_pickleable(self):\n        aug = iaa.Lambda(\n            func_images=_lambda_pickleable_callback_images,\n            seed=1)\n        runtest_pickleable_uint8_img(aug)\n\n\ndef _lambda_pickleable_callback_images(images, random_state, parents, hooks):\n    aug = iaa.Flipud(0.5, seed=random_state)\n    return aug.augment_images(images)\n\n\nclass TestAssertLambda(unittest.TestCase):\n    DTYPES_UINT = [\"uint8\", \"uint16\", \"uint32\", \"uint64\"]\n    DTYPES_INT = [\"int8\", \"int32\", \"int64\"]\n    DTYPES_FLOAT = (\n        [\"float16\", \"float32\", \"float64\"]\n        + (\n            [\"float128\"] if hasattr(np, \"float128\") else []\n        )\n    )\n\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        base_img = np.array([[0, 0, 1],\n                             [0, 0, 1],\n                             [0, 1, 1]], dtype=np.uint8)\n        return np.atleast_3d(base_img)\n\n    @property\n    def images(self):\n        return np.array([self.image])\n\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([[0.0, 0.0, 1.0],\n                                   [0.0, 0.0, 1.0],\n                                   [0.0, 1.0, 1.0]])\n        return ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def segmaps(self):\n        segmaps_arr = np.int32([[0, 0, 1],\n                                [0, 0, 1],\n                                [0, 1, 1]])\n        return SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        return ia.KeypointsOnImage(kps, shape=self.image.shape)\n\n    @property\n    def psoi(self):\n        polygons = [ia.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]\n        return ia.PolygonsOnImage(polygons, shape=self.image.shape)\n\n    @property\n    def lsoi(self):\n        lss = [ia.LineString([(0, 0), (2, 0), (2, 2), (0, 2)])]\n        return ia.LineStringsOnImage(lss, shape=self.image.shape)\n\n    @property\n    def bbsoi(self):\n        bb = ia.BoundingBox(x1=0, y1=0, x2=2, y2=2)\n        return ia.BoundingBoxesOnImage([bb], shape=self.image.shape)\n\n    @property\n    def aug_succeeds(self):\n        def _func_images_succeeds(images, random_state, parents, hooks):\n            return images[0][0, 0] == 0 and images[0][2, 2] == 1\n\n        def _func_heatmaps_succeeds(heatmaps, random_state, parents, hooks):\n            return heatmaps[0].arr_0to1[0, 0] < 0 + 1e-6\n\n        def _func_segmaps_succeeds(segmaps, random_state, parents, hooks):\n            return segmaps[0].arr[0, 0] == 0\n\n        def _func_keypoints_succeeds(keypoints_on_images, random_state, parents,\n                                    hooks):\n            return (\n                keypoints_on_images[0].keypoints[0].x == 0\n                and keypoints_on_images[0].keypoints[2].x == 2\n            )\n\n        def _func_bounding_boxes_succeeds(bounding_boxes_on_images,\n                                          random_state, parents, hooks):\n            return (bounding_boxes_on_images[0].items[0].x1 == 0\n                    and bounding_boxes_on_images[0].items[0].x2 == 2)\n\n        def _func_polygons_succeeds(polygons_on_images, random_state, parents,\n                                   hooks):\n            return (polygons_on_images[0].polygons[0].exterior[0][0] == 0\n                    and polygons_on_images[0].polygons[0].exterior[2][1] == 2)\n\n        def _func_line_strings_succeeds(line_strings_on_image, random_state,\n                                        parents, hooks):\n            return (line_strings_on_image[0].items[0].coords[0][0] == 0\n                    and line_strings_on_image[0].items[0].coords[2][1] == 2)\n\n        return iaa.AssertLambda(\n            func_images=_func_images_succeeds,\n            func_heatmaps=_func_heatmaps_succeeds,\n            func_segmentation_maps=_func_segmaps_succeeds,\n            func_keypoints=_func_keypoints_succeeds,\n            func_bounding_boxes=_func_bounding_boxes_succeeds,\n            func_polygons=_func_polygons_succeeds,\n            func_line_strings=_func_line_strings_succeeds)\n\n    @property\n    def aug_fails(self):\n        def _func_images_fails(images, random_state, parents, hooks):\n            return images[0][0, 0] == 1\n\n        def _func_heatmaps_fails(heatmaps, random_state, parents, hooks):\n            return heatmaps[0].arr_0to1[0, 0] > 0 + 1e-6\n\n        def _func_segmaps_fails(segmaps, random_state, parents, hooks):\n            return segmaps[0].arr[0, 0] == 1\n\n        def _func_keypoints_fails(keypoints_on_images, random_state, parents,\n                                  hooks):\n            return keypoints_on_images[0].keypoints[0].x == 2\n\n        def _func_bounding_boxes_fails(bounding_boxes_on_images, random_state,\n                                       parents, hooks):\n            return bounding_boxes_on_images[0].items[0].x1 == 2\n\n        def _func_polygons_fails(polygons_on_images, random_state, parents,\n                                 hooks):\n            return polygons_on_images[0].polygons[0].exterior[0][0] == 2\n\n        def _func_line_strings_fails(line_strings_on_images, random_state,\n                                     parents, hooks):\n            return line_strings_on_images[0].items[0].coords[0][0] == 2\n\n        return iaa.AssertLambda(\n            func_images=_func_images_fails,\n            func_heatmaps=_func_heatmaps_fails,\n            func_segmentation_maps=_func_segmaps_fails,\n            func_keypoints=_func_keypoints_fails,\n            func_bounding_boxes=_func_bounding_boxes_fails,\n            func_polygons=_func_polygons_fails,\n            func_line_strings=_func_line_strings_fails)\n\n    def test_images_as_array_with_assert_that_succeeds(self):\n        observed = self.aug_succeeds.augment_images(self.images)\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_images_as_array_with_assert_that_fails(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_images(self.images)\n\n    def test_images_as_array_with_assert_that_succeeds__deterministic(self):\n        aug_succeeds_det = self.aug_succeeds.to_deterministic()\n        observed = aug_succeeds_det.augment_images(self.images)\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_images_as_array_with_assert_that_fails__deterministic(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_images(self.images)\n\n    def test_images_as_list_with_assert_that_succeeds(self):\n        observed = self.aug_succeeds.augment_images([self.images[0]])\n        expected = [self.images[0]]\n        assert array_equal_lists(observed, expected)\n\n    def test_images_as_list_with_assert_that_fails(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_images([self.images[0]])\n\n    def test_images_as_list_with_assert_that_succeeds__deterministic(self):\n        aug_succeeds_det = self.aug_succeeds.to_deterministic()\n        observed = aug_succeeds_det.augment_images([self.images[0]])\n        expected = [self.images[0]]\n        assert array_equal_lists(observed, expected)\n\n    def test_images_as_list_with_assert_that_fails__deterministic(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_images([self.images[0]])\n\n    def test_heatmaps_with_assert_that_succeeds(self):\n        observed = self.aug_succeeds.augment_heatmaps(self.heatmaps)\n        assert observed.shape == (3, 3, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_heatmaps_with_assert_that_fails(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_heatmaps(self.heatmaps)\n\n    def test_heatmaps_with_assert_that_succeeds__deterministic(self):\n        aug_succeeds_det = self.aug_succeeds.to_deterministic()\n        observed = aug_succeeds_det.augment_heatmaps(self.heatmaps)\n        assert observed.shape == (3, 3, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_heatmaps_with_assert_that_fails__deterministic(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_heatmaps(self.heatmaps)\n\n    def test_segmaps_with_assert_that_succeeds(self):\n        observed = self.aug_succeeds.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == (3, 3, 3)\n        assert np.array_equal(observed.get_arr(), self.segmaps.get_arr())\n\n    def test_segmaps_with_assert_that_fails(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_segmentation_maps(self.segmaps)\n\n    def test_segmaps_with_assert_that_succeeds__deterministic(self):\n        aug_succeeds_det = self.aug_succeeds.to_deterministic()\n        observed = aug_succeeds_det.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == (3, 3, 3)\n        assert np.array_equal(observed.get_arr(), self.segmaps.get_arr())\n\n    def test_segmaps_with_assert_that_fails__deterministic(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_segmentation_maps(self.segmaps)\n\n    def test_keypoints_with_assert_that_succeeds(self):\n        observed = self.aug_succeeds.augment_keypoints(self.kpsoi)\n        assert_cbaois_equal(observed, self.kpsoi)\n\n    def test_keypoints_with_assert_that_fails(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_keypoints(self.kpsoi)\n\n    def test_keypoints_with_assert_that_succeeds__deterministic(self):\n        aug_succeeds_det = self.aug_succeeds.to_deterministic()\n        observed = aug_succeeds_det.augment_keypoints(self.kpsoi)\n        assert_cbaois_equal(observed, self.kpsoi)\n\n    def test_keypoints_with_assert_that_fails__deterministic(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_keypoints(self.kpsoi)\n\n    def test_polygons_with_assert_that_succeeds(self):\n        observed = self.aug_succeeds.augment_polygons(self.psoi)\n        assert_cbaois_equal(observed, self.psoi)\n\n    def test_polygons_with_assert_that_fails(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_polygons(self.psoi)\n\n    def test_polygons_with_assert_that_succeeds__deterministic(self):\n        aug_succeeds_det = self.aug_succeeds.to_deterministic()\n        observed = aug_succeeds_det.augment_polygons(self.psoi)\n        assert_cbaois_equal(observed, self.psoi)\n\n    def test_polygons_with_assert_that_fails__deterministic(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_polygons(self.psoi)\n\n    def test_line_strings_with_assert_that_succeeds(self):\n        observed = self.aug_succeeds.augment_line_strings(self.lsoi)\n        assert_cbaois_equal(observed, self.lsoi)\n\n    def test_line_strings_with_assert_that_fails(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_line_strings(self.lsoi)\n\n    def test_line_strings_with_assert_that_succeeds__deterministic(self):\n        aug_succeeds_det = self.aug_succeeds.to_deterministic()\n        observed = aug_succeeds_det.augment_line_strings(self.lsoi)\n        assert_cbaois_equal(observed, self.lsoi)\n\n    def test_line_strings_with_assert_that_fails__deterministic(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_line_strings(self.lsoi)\n\n    def test_bounding_boxes_with_assert_that_succeeds(self):\n        observed = self.aug_succeeds.augment_bounding_boxes(self.bbsoi)\n        assert_cbaois_equal(observed, self.bbsoi)\n\n    def test_bounding_boxes_with_assert_that_fails(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_bounding_boxes(self.bbsoi)\n\n    def test_bounding_boxes_with_assert_that_succeeds__deterministic(self):\n        aug_succeeds_det = self.aug_succeeds.to_deterministic()\n        observed = aug_succeeds_det.augment_bounding_boxes(self.bbsoi)\n        assert_cbaois_equal(observed, self.bbsoi)\n\n    def test_bounding_boxes_with_assert_that_fails__deterministic(self):\n        with self.assertRaises(AssertionError):\n            _ = self.aug_fails.augment_bounding_boxes(self.bbsoi)\n\n    def test_other_dtypes_bool__with_assert_that_succeeds(self):\n        def func_images_succeeds(images, random_state, parents, hooks):\n            return np.allclose(images[0][0, 0], 1, rtol=0, atol=1e-6)\n\n        aug = iaa.AssertLambda(func_images=func_images_succeeds)\n\n        image = np.zeros((3, 3), dtype=bool)\n        image[0, 0] = True\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.all(image_aug == image)\n\n    def test_other_dtypes_uint_int__with_assert_that_succeeds(self):\n        def func_images_succeeds(images, random_state, parents, hooks):\n            return np.allclose(images[0][0, 0], 1, rtol=0, atol=1e-6)\n\n        aug = iaa.AssertLambda(func_images=func_images_succeeds)\n\n        dtypes = self.DTYPES_UINT + self.DTYPES_INT\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = 1\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.name == dtype\n                assert np.array_equal(image_aug, image)\n\n    def test_other_dtypes_float__with_assert_that_succeeds(self):\n        def func_images_succeeds(images, random_state, parents, hooks):\n            return np.allclose(images[0][0, 0], 1, rtol=0, atol=1e-6)\n\n        aug = iaa.AssertLambda(func_images=func_images_succeeds)\n\n        dtypes = self.DTYPES_FLOAT\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = 1\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.name == dtype\n                assert np.all(image_aug == image)\n\n    def test_other_dtypes_bool__with_assert_that_fails(self):\n        def func_images_fails(images, random_state, parents, hooks):\n            return np.allclose(images[0][0, 1], 1, rtol=0, atol=1e-6)\n\n        aug = iaa.AssertLambda(func_images=func_images_fails)\n\n        image = np.zeros((3, 3), dtype=bool)\n        image[0, 0] = True\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_image(image)\n\n    def test_other_dtypes_uint_int__with_assert_that_fails(self):\n        def func_images_fails(images, random_state, parents, hooks):\n            return np.allclose(images[0][0, 1], 1, rtol=0, atol=1e-6)\n\n        aug = iaa.AssertLambda(func_images=func_images_fails)\n\n        dtypes = self.DTYPES_UINT + self.DTYPES_INT\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = 1\n                with self.assertRaises(AssertionError):\n                    _ = aug.augment_image(image)\n\n    def test_other_dtypes_float__with_assert_that_fails(self):\n        def func_images_fails(images, random_state, parents, hooks):\n            return np.allclose(images[0][0, 1], 1, rtol=0, atol=1e-6)\n\n        aug = iaa.AssertLambda(func_images=func_images_fails)\n\n        dtypes = self.DTYPES_FLOAT\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = 1\n                with self.assertRaises(AssertionError):\n                    _ = aug.augment_image(image)\n\n    def test_pickleable(self):\n        aug = iaa.AssertLambda(\n            func_images=_assertlambda_pickleable_callback_images,\n            seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=2)\n\n\n# in py3+, this could be a classmethod of TestAssertLambda,\n# but in py2.7 such classmethods are not pickle-able and would cause an error\ndef _assertlambda_pickleable_callback_images(images, random_state,\n                                             parents, hooks):\n    return np.any(images[0] > 0)\n\n\nclass TestAssertShape(unittest.TestCase):\n    DTYPES_UINT = [\"uint8\", \"uint16\", \"uint32\", \"uint64\"]\n    DTYPES_INT = [\"int8\", \"int32\", \"int64\"]\n    DTYPES_FLOAT = (\n        [\"float16\", \"float32\", \"float64\"]\n        + (\n            [\"float128\"] if hasattr(np, \"float128\") else []\n        )\n    )\n\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        base_img = np.array([[0, 0, 1, 0],\n                             [0, 0, 1, 0],\n                             [0, 1, 1, 0]], dtype=np.uint8)\n        return np.atleast_3d(base_img)\n\n    @property\n    def images(self):\n        return np.array([self.image])\n\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([[0.0, 0.0, 1.0, 0.0],\n                                   [0.0, 0.0, 1.0, 0.0],\n                                   [0.0, 1.0, 1.0, 0.0]])\n        return ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 4, 3))\n\n    @property\n    def segmaps(self):\n        segmaps_arr = np.int32([[0, 0, 1, 0],\n                                [0, 0, 1, 0],\n                                [0, 1, 1, 0]])\n        return SegmentationMapsOnImage(segmaps_arr, shape=(3, 4, 3))\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        return ia.KeypointsOnImage(kps, shape=self.image.shape)\n\n    @property\n    def psoi(self):\n        polygons = [ia.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]\n        return ia.PolygonsOnImage(polygons, shape=self.image.shape)\n\n    @property\n    def lsoi(self):\n        lss = [ia.LineString([(0, 0), (2, 0), (2, 2), (0, 2)])]\n        return ia.LineStringsOnImage(lss, shape=self.image.shape)\n\n    @property\n    def bbsoi(self):\n        bb = ia.BoundingBox(x1=0, y1=0, x2=2, y2=2)\n        return ia.BoundingBoxesOnImage([bb], shape=self.image.shape)\n\n    @property\n    def image_h4(self):\n        base_img_h4 = np.array([[0, 0, 1, 0],\n                                [0, 0, 1, 0],\n                                [0, 1, 1, 0],\n                                [1, 0, 1, 0]], dtype=np.uint8)\n        return np.atleast_3d(base_img_h4)\n\n    @property\n    def images_h4(self):\n        return np.array([self.image_h4])\n\n    @property\n    def heatmaps_h4(self):\n        heatmaps_arr_h4 = np.float32([[0.0, 0.0, 1.0, 0.0],\n                                      [0.0, 0.0, 1.0, 0.0],\n                                      [0.0, 1.0, 1.0, 0.0],\n                                      [1.0, 0.0, 1.0, 0.0]])\n        return ia.HeatmapsOnImage(heatmaps_arr_h4, shape=(4, 4, 3))\n\n    @property\n    def segmaps_h4(self):\n        segmaps_arr_h4 = np.int32([[0, 0, 1, 0],\n                                   [0, 0, 1, 0],\n                                   [0, 1, 1, 0],\n                                   [1, 0, 1, 0]])\n        return SegmentationMapsOnImage(segmaps_arr_h4, shape=(4, 4, 3))\n\n    @property\n    def kpsoi_h4(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        return ia.KeypointsOnImage(kps, shape=self.image_h4.shape)\n\n    @property\n    def psoi_h4(self):\n        polygons = [ia.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]\n        return ia.PolygonsOnImage(polygons, shape=self.image_h4.shape)\n\n    @property\n    def lsoi_h4(self):\n        lss = [ia.LineString([(0, 0), (2, 0), (2, 2), (0, 2)])]\n        return ia.LineStringsOnImage(lss, shape=self.image_h4.shape)\n\n    @property\n    def bbsoi_h4(self):\n        bb = ia.BoundingBox(x1=0, y1=0, x2=2, y2=2)\n        return ia.BoundingBoxesOnImage([bb], shape=self.image_h4.shape)\n\n    @property\n    def aug_exact_shape(self):\n        return iaa.AssertShape((1, 3, 4, 1))\n\n    @property\n    def aug_none_in_shape(self):\n        return iaa.AssertShape((None, 3, 4, 1))\n\n    @property\n    def aug_list_in_shape(self):\n        return iaa.AssertShape((1, [1, 3, 5], 4, 1))\n\n    @property\n    def aug_tuple_in_shape(self):\n        return iaa.AssertShape((1, (1, 4), 4, 1))\n\n    def test_images_with_exact_shape__succeeds(self):\n        aug = self.aug_exact_shape\n        observed = aug.augment_images(self.images)\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_exact_shape__succeeds__deterministic(self):\n        aug_det = self.aug_exact_shape.to_deterministic()\n        observed = aug_det.augment_images(self.images)\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_exact_shape__succeeds__list(self):\n        aug = self.aug_exact_shape\n        observed = aug.augment_images([self.images[0]])\n        expected = [self.images[0]]\n        assert array_equal_lists(observed, expected)\n\n    def test_images_with_exact_shape__succeeds__deterministic__list(self):\n        aug_det = self.aug_exact_shape.to_deterministic()\n        observed = aug_det.augment_images([self.images[0]])\n        expected = [self.images[0]]\n        assert array_equal_lists(observed, expected)\n\n    def test_heatmaps_with_exact_shape__succeeds(self):\n        aug = self.aug_exact_shape\n        observed = aug.augment_heatmaps(self.heatmaps)\n        assert observed.shape == (3, 4, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_heatmaps_with_exact_shape__succeeds__deterministic(self):\n        aug_det = self.aug_exact_shape.to_deterministic()\n        observed = aug_det.augment_heatmaps(self.heatmaps)\n        assert observed.shape == (3, 4, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_segmaps_with_exact_shape__succeeds(self):\n        aug = self.aug_exact_shape\n        observed = aug.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == (3, 4, 3)\n        assert np.array_equal(observed.get_arr(), self.segmaps.get_arr())\n\n    def test_segmaps_with_exact_shape__succeeds__deterministic(self):\n        aug_det = self.aug_exact_shape.to_deterministic()\n        observed = aug_det.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == (3, 4, 3)\n        assert np.array_equal(observed.get_arr(), self.segmaps.get_arr())\n\n    def test_keypoints_with_exact_shape__succeeds(self):\n        aug = self.aug_exact_shape\n        observed = aug.augment_keypoints(self.kpsoi)\n        assert_cbaois_equal(observed, self.kpsoi)\n\n    def test_keypoints_with_exact_shape__succeeds__deterministic(self):\n        aug_det = self.aug_exact_shape.to_deterministic()\n        observed = aug_det.augment_keypoints(self.kpsoi)\n        assert_cbaois_equal(observed, self.kpsoi)\n\n    def test_polygons_with_exact_shape__succeeds(self):\n        aug = self.aug_exact_shape\n        observed = aug.augment_polygons(self.psoi)\n        assert_cbaois_equal(observed, self.psoi)\n\n    def test_polygons_with_exact_shape__succeeds__deterministic(self):\n        aug_det = self.aug_exact_shape.to_deterministic()\n        observed = aug_det.augment_polygons(self.psoi)\n        assert_cbaois_equal(observed, self.psoi)\n\n    def test_line_strings_with_exact_shape__succeeds(self):\n        aug = self.aug_exact_shape\n        observed = aug.augment_line_strings(self.lsoi)\n        assert_cbaois_equal(observed, self.lsoi)\n\n    def test_line_strings_with_exact_shape__succeeds__deterministic(self):\n        aug_det = self.aug_exact_shape.to_deterministic()\n        observed = aug_det.augment_line_strings(self.lsoi)\n        assert_cbaois_equal(observed, self.lsoi)\n\n    def test_bounding_boxes_with_exact_shape__succeeds(self):\n        aug = self.aug_exact_shape\n        observed = aug.augment_bounding_boxes(self.bbsoi)\n        assert_cbaois_equal(observed, self.bbsoi)\n\n    def test_bounding_boxes_with_exact_shape__succeeds__deterministic(self):\n        aug_det = self.aug_exact_shape.to_deterministic()\n        observed = aug_det.augment_bounding_boxes(self.bbsoi)\n        assert_cbaois_equal(observed, self.bbsoi)\n\n    def test_images_with_exact_shape__fails(self):\n        aug = self.aug_exact_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_images(self.images_h4)\n\n    def test_heatmaps_with_exact_shape__fails(self):\n        aug = self.aug_exact_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_heatmaps(self.heatmaps_h4)\n\n    def test_keypoints_with_exact_shape__fails(self):\n        aug = self.aug_exact_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_keypoints(self.kpsoi_h4)\n\n    def test_polygons_with_exact_shape__fails(self):\n        aug = self.aug_exact_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_polygons(self.psoi_h4)\n\n    def test_line_strings_with_exact_shape__fails(self):\n        aug = self.aug_exact_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_line_strings(self.lsoi_h4)\n\n    def test_bounding_boxes_with_exact_shape__fails(self):\n        aug = self.aug_exact_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_bounding_boxes(self.bbsoi_h4)\n\n    def test_images_with_none_in_shape__succeeds(self):\n        aug = self.aug_none_in_shape\n        observed = aug.augment_images(self.images)\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_none_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_none_in_shape.to_deterministic()\n        observed = aug_det.augment_images(self.images)\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_heatmaps_with_none_in_shape__succeeds(self):\n        aug = self.aug_none_in_shape\n        observed = aug.augment_heatmaps(self.heatmaps)\n        assert observed.shape == (3, 4, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_heatmaps_with_none_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_none_in_shape.to_deterministic()\n        observed = aug_det.augment_heatmaps(self.heatmaps)\n        assert observed.shape == (3, 4, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_segmaps_with_none_in_shape__succeeds(self):\n        aug = self.aug_none_in_shape\n        observed = aug.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == (3, 4, 3)\n        assert np.array_equal(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_segmaps_with_none_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_none_in_shape.to_deterministic()\n        observed = aug_det.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == (3, 4, 3)\n        assert np.array_equal(observed.get_arr(), self.segmaps.get_arr())\n\n    def test_keypoints_with_none_in_shape__succeeds(self):\n        aug = self.aug_none_in_shape\n        observed = aug.augment_keypoints(self.kpsoi)\n        assert_cbaois_equal(observed, self.kpsoi)\n\n    def test_keypoints_with_none_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_none_in_shape.to_deterministic()\n        observed = aug_det.augment_keypoints(self.kpsoi)\n        assert_cbaois_equal(observed, self.kpsoi)\n\n    def test_polygons_with_none_in_shape__succeeds(self):\n        aug = self.aug_none_in_shape\n        observed = aug.augment_polygons(self.psoi)\n        assert_cbaois_equal(observed, self.psoi)\n\n    def test_polygons_with_none_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_none_in_shape.to_deterministic()\n        observed = aug_det.augment_polygons(self.psoi)\n        assert_cbaois_equal(observed, self.psoi)\n\n    def test_line_strings_with_none_in_shape__succeeds(self):\n        aug = self.aug_none_in_shape\n        observed = aug.augment_line_strings(self.lsoi)\n        assert_cbaois_equal(observed, self.lsoi)\n\n    def test_line_strings_with_none_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_none_in_shape.to_deterministic()\n        observed = aug_det.augment_line_strings(self.lsoi)\n        assert_cbaois_equal(observed, self.lsoi)\n\n    def test_bounding_boxes_with_none_in_shape__succeeds(self):\n        aug = self.aug_none_in_shape\n        observed = aug.augment_bounding_boxes(self.bbsoi)\n        assert_cbaois_equal(observed, self.bbsoi)\n\n    def test_bounding_boxes_with_none_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_none_in_shape.to_deterministic()\n        observed = aug_det.augment_bounding_boxes(self.bbsoi)\n        assert_cbaois_equal(observed, self.bbsoi)\n\n    def test_images_with_none_in_shape__fails(self):\n        aug = self.aug_none_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_images(self.images_h4)\n\n    def test_heatmaps_with_none_in_shape__fails(self):\n        aug = self.aug_none_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_heatmaps(self.heatmaps_h4)\n\n    def test_keypoints_with_none_in_shape__fails(self):\n        aug = self.aug_none_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_keypoints(self.kpsoi_h4)\n\n    def test_polygons_with_none_in_shape__fails(self):\n        aug = self.aug_none_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_polygons(self.psoi_h4)\n\n    def test_line_strings_with_none_in_shape__fails(self):\n        aug = self.aug_none_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_line_strings(self.lsoi_h4)\n\n    def test_bounding_boxes_with_none_in_shape__fails(self):\n        aug = self.aug_none_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_bounding_boxes(self.bbsoi_h4)\n\n    def test_images_with_list_in_shape__succeeds(self):\n        aug = self.aug_list_in_shape\n        observed = aug.augment_images(self.images)\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_list_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_list_in_shape.to_deterministic()\n        observed = aug_det.augment_images(self.images)\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_heatmaps_with_list_in_shape__succeeds(self):\n        aug = self.aug_list_in_shape\n        observed = aug.augment_heatmaps(self.heatmaps)\n        assert observed.shape == (3, 4, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_heatmaps_with_list_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_list_in_shape.to_deterministic()\n        observed = aug_det.augment_heatmaps(self.heatmaps)\n        assert observed.shape == (3, 4, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_segmaps_with_list_in_shape__succeeds(self):\n        aug = self.aug_list_in_shape\n        observed = aug.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == (3, 4, 3)\n        assert np.array_equal(observed.get_arr(), self.segmaps.get_arr())\n\n    def test_segmaps_with_list_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_list_in_shape.to_deterministic()\n        observed = aug_det.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == (3, 4, 3)\n        assert np.array_equal(observed.get_arr(), self.segmaps.get_arr())\n\n    def test_keypoints_with_list_in_shape__succeeds(self):\n        aug = self.aug_list_in_shape\n        observed = aug.augment_keypoints(self.kpsoi)\n        assert_cbaois_equal(observed, self.kpsoi)\n\n    def test_keypoints_with_list_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_list_in_shape.to_deterministic()\n        observed = aug_det.augment_keypoints(self.kpsoi)\n        assert_cbaois_equal(observed, self.kpsoi)\n\n    def test_polygons_with_list_in_shape__succeeds(self):\n        aug = self.aug_list_in_shape\n        observed = aug.augment_polygons(self.psoi)\n        assert_cbaois_equal(observed, self.psoi)\n\n    def test_polygons_with_list_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_list_in_shape.to_deterministic()\n        observed = aug_det.augment_polygons(self.psoi)\n        assert_cbaois_equal(observed, self.psoi)\n\n    def test_line_strings_with_list_in_shape__succeeds(self):\n        aug = self.aug_list_in_shape\n        observed = aug.augment_line_strings(self.lsoi)\n        assert_cbaois_equal(observed, self.lsoi)\n\n    def test_line_strings_with_list_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_list_in_shape.to_deterministic()\n        observed = aug_det.augment_line_strings(self.lsoi)\n        assert_cbaois_equal(observed, self.lsoi)\n\n    def test_bounding_boxes_with_list_in_shape__succeeds(self):\n        aug = self.aug_list_in_shape\n        observed = aug.augment_bounding_boxes(self.bbsoi)\n        assert_cbaois_equal(observed, self.bbsoi)\n\n    def test_bounding_boxes_with_list_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_list_in_shape.to_deterministic()\n        observed = aug_det.augment_bounding_boxes(self.bbsoi)\n        assert_cbaois_equal(observed, self.bbsoi)\n\n    def test_images_with_list_in_shape__fails(self):\n        aug = self.aug_list_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_images(self.images_h4)\n\n    def test_heatmaps_with_list_in_shape__fails(self):\n        aug = self.aug_list_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_heatmaps(self.heatmaps_h4)\n\n    def test_segmaps_with_list_in_shape__fails(self):\n        aug = self.aug_list_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_segmentation_maps(self.segmaps_h4)\n\n    def test_keypoints_with_list_in_shape__fails(self):\n        aug = self.aug_list_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_keypoints(self.kpsoi_h4)\n\n    def test_polygons_with_list_in_shape__fails(self):\n        aug = self.aug_list_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_polygons(self.psoi_h4)\n\n    def test_line_strings_with_list_in_shape__fails(self):\n        aug = self.aug_list_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_line_strings(self.lsoi_h4)\n\n    def test_bounding_boxes_with_list_in_shape__fails(self):\n        aug = self.aug_list_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_bounding_boxes(self.bbsoi_h4)\n\n    def test_images_with_tuple_in_shape__succeeds(self):\n        aug = self.aug_tuple_in_shape\n        observed = aug.augment_images(self.images)\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_images_with_tuple_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_tuple_in_shape.to_deterministic()\n        observed = aug_det.augment_images(self.images)\n        expected = self.images\n        assert np.array_equal(observed, expected)\n\n    def test_heatmaps_with_tuple_in_shape__succeeds(self):\n        aug = self.aug_tuple_in_shape\n        observed = aug.augment_heatmaps(self.heatmaps)\n        assert observed.shape == (3, 4, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_heatmaps_with_tuple_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_tuple_in_shape.to_deterministic()\n        observed = aug_det.augment_heatmaps(self.heatmaps)\n        assert observed.shape == (3, 4, 3)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_segmaps_with_tuple_in_shape__succeeds(self):\n        aug = self.aug_tuple_in_shape\n        observed = aug.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == (3, 4, 3)\n        assert np.array_equal(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_segmaps_with_tuple_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_tuple_in_shape.to_deterministic()\n        observed = aug_det.augment_segmentation_maps(self.segmaps)\n        assert observed.shape == (3, 4, 3)\n        assert np.array_equal(observed.get_arr(), self.heatmaps.get_arr())\n\n    def test_keypoints_with_tuple_in_shape__succeeds(self):\n        aug = self.aug_tuple_in_shape\n        observed = aug.augment_keypoints(self.kpsoi)\n        assert_cbaois_equal(observed, self.kpsoi)\n\n    def test_keypoints_with_tuple_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_tuple_in_shape.to_deterministic()\n        observed = aug_det.augment_keypoints(self.kpsoi)\n        assert_cbaois_equal(observed, self.kpsoi)\n\n    def test_polygons_with_tuple_in_shape__succeeds(self):\n        aug = self.aug_tuple_in_shape\n        observed = aug.augment_polygons(self.psoi)\n        assert_cbaois_equal(observed, self.psoi)\n\n    def test_polygons_with_tuple_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_tuple_in_shape.to_deterministic()\n        observed = aug_det.augment_polygons(self.psoi)\n        assert_cbaois_equal(observed, self.psoi)\n\n    def test_line_strings_with_tuple_in_shape__succeeds(self):\n        aug = self.aug_tuple_in_shape\n        observed = aug.augment_line_strings(self.lsoi)\n        assert_cbaois_equal(observed, self.lsoi)\n\n    def test_line_strings_with_tuple_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_tuple_in_shape.to_deterministic()\n        observed = aug_det.augment_line_strings(self.lsoi)\n        assert_cbaois_equal(observed, self.lsoi)\n\n    def test_bounding_boxes_with_tuple_in_shape__succeeds(self):\n        aug = self.aug_tuple_in_shape\n        observed = aug.augment_bounding_boxes(self.bbsoi)\n        assert_cbaois_equal(observed, self.bbsoi)\n\n    def test_bounding_boxes_with_tuple_in_shape__succeeds__deterministic(self):\n        aug_det = self.aug_tuple_in_shape.to_deterministic()\n        observed = aug_det.augment_bounding_boxes(self.bbsoi)\n        assert_cbaois_equal(observed, self.bbsoi)\n\n    def test_images_with_tuple_in_shape__fails(self):\n        aug = self.aug_tuple_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_images(self.images_h4)\n\n    def test_heatmaps_with_tuple_in_shape__fails(self):\n        aug = self.aug_tuple_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_heatmaps(self.heatmaps_h4)\n\n    def test_segmaps_with_tuple_in_shape__fails(self):\n        aug = self.aug_tuple_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_segmentation_maps(self.segmaps_h4)\n\n    def test_keypoints_with_tuple_in_shape__fails(self):\n        aug = self.aug_tuple_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_keypoints(self.kpsoi_h4)\n\n    def test_polygons_with_tuple_in_shape__fails(self):\n        aug = self.aug_tuple_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_polygons(self.psoi_h4)\n\n    def test_line_strings_with_tuple_in_shape__fails(self):\n        aug = self.aug_tuple_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_line_strings(self.lsoi_h4)\n\n    def test_bounding_boxes_with_tuple_in_shape__fails(self):\n        aug = self.aug_tuple_in_shape\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_bounding_boxes(self.bbsoi_h4)\n\n    def test_fails_if_shape_contains_invalid_datatype(self):\n        got_exception = False\n        try:\n            aug = iaa.AssertShape((1, False, 4, 1))\n            _ = aug.augment_images(np.zeros((1, 2, 2, 1), dtype=np.uint8))\n        except Exception as exc:\n            assert \"Invalid datatype \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_other_dtypes_bool__succeeds(self):\n        aug = iaa.AssertShape((None, 3, 3, 1))\n        image = np.zeros((3, 3, 1), dtype=bool)\n        image[0, 0, 0] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.type == image.dtype.type\n        assert np.all(image_aug == image)\n\n    def test_other_dtypes_uint_int__succeeds(self):\n        aug = iaa.AssertShape((None, 3, 3, 1))\n        for dtype in self.DTYPES_UINT + self.DTYPES_INT:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3, 1), dtype=dtype)\n                image[0, 0, 0] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.array_equal(image_aug, image)\n\n    def test_other_dtypes_float__succeeds(self):\n        aug = iaa.AssertShape((None, 3, 3, 1))\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n\n        for dtype, value in zip(self.DTYPES_FLOAT, values):\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3, 1), dtype=dtype)\n                image[0, 0, 0] = 1\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.name == dtype\n                assert np.all(image_aug == image)\n\n    def test_other_dtypes_bool__fails(self):\n        aug = iaa.AssertShape((None, 3, 4, 1))\n        image = np.zeros((3, 3, 1), dtype=bool)\n        image[0, 0, 0] = True\n\n        with self.assertRaises(AssertionError):\n            _ = aug.augment_image(image)\n\n    def test_other_dtypes_uint_int__fails(self):\n        aug = iaa.AssertShape((None, 3, 4, 1))\n\n        for dtype in self.DTYPES_UINT + self.DTYPES_INT:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n            value = max_value\n            image = np.zeros((3, 3, 1), dtype=dtype)\n            image[0, 0, 0] = value\n            with self.assertRaises(AssertionError):\n                _ = aug.augment_image(image)\n\n    def test_other_dtypes_float__fails(self):\n        aug = iaa.AssertShape((None, 3, 4, 1))\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n\n        for dtype, value in zip(self.DTYPES_FLOAT, values):\n            image = np.zeros((3, 3, 1), dtype=dtype)\n            image[0, 0, 0] = value\n            with self.assertRaises(AssertionError):\n                _ = aug.augment_image(image)\n\n    def test_pickleable(self):\n        aug = iaa.AssertShape(\n            shape=(None, 15, 15, None), check_images=True,\n            seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=2, shape=(15, 15, 1))\n\n\ndef test_clip_augmented_image_():\n    warnings.resetwarnings()\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        image = np.zeros((1, 3), dtype=np.uint8)\n        image[0, 0] = 10\n        image[0, 1] = 20\n        image[0, 2] = 30\n        image_clipped = iaa.clip_augmented_image_(image,\n                                                  min_value=15, max_value=25)\n        assert image_clipped[0, 0] == 15\n        assert image_clipped[0, 1] == 20\n        assert image_clipped[0, 2] == 25\n\n    assert len(caught_warnings) >= 1\n    assert \"deprecated\" in str(caught_warnings[-1].message)\n\n\ndef test_clip_augmented_image():\n    warnings.resetwarnings()\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        image = np.zeros((1, 3), dtype=np.uint8)\n        image[0, 0] = 10\n        image[0, 1] = 20\n        image[0, 2] = 30\n        image_clipped = iaa.clip_augmented_image(image,\n                                                 min_value=15, max_value=25)\n        assert image_clipped[0, 0] == 15\n        assert image_clipped[0, 1] == 20\n        assert image_clipped[0, 2] == 25\n\n    assert len(caught_warnings) >= 1\n    assert \"deprecated\" in str(caught_warnings[-1].message)\n\n\ndef test_clip_augmented_images_():\n    warnings.resetwarnings()\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        images = np.zeros((2, 1, 3), dtype=np.uint8)\n        images[:, 0, 0] = 10\n        images[:, 0, 1] = 20\n        images[:, 0, 2] = 30\n        imgs_clipped = iaa.clip_augmented_images_(images,\n                                                  min_value=15, max_value=25)\n        assert np.all(imgs_clipped[:, 0, 0] == 15)\n        assert np.all(imgs_clipped[:, 0, 1] == 20)\n        assert np.all(imgs_clipped[:, 0, 2] == 25)\n\n        images = [np.zeros((1, 3), dtype=np.uint8) for _ in sm.xrange(2)]\n        for i in sm.xrange(len(images)):\n            images[i][0, 0] = 10\n            images[i][0, 1] = 20\n            images[i][0, 2] = 30\n        imgs_clipped = iaa.clip_augmented_images_(images,\n                                                  min_value=15, max_value=25)\n        assert isinstance(imgs_clipped, list)\n        assert np.all([imgs_clipped[i][0, 0] == 15\n                       for i in sm.xrange(len(images))])\n        assert np.all([imgs_clipped[i][0, 1] == 20\n                       for i in sm.xrange(len(images))])\n        assert np.all([imgs_clipped[i][0, 2] == 25\n                       for i in sm.xrange(len(images))])\n\n    assert len(caught_warnings) >= 1\n    assert \"deprecated\" in str(caught_warnings[-1].message)\n\n\ndef test_clip_augmented_images():\n    warnings.resetwarnings()\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        images = np.zeros((2, 1, 3), dtype=np.uint8)\n        images[:, 0, 0] = 10\n        images[:, 0, 1] = 20\n        images[:, 0, 2] = 30\n        imgs_clipped = iaa.clip_augmented_images(images,\n                                                 min_value=15, max_value=25)\n        assert np.all(imgs_clipped[:, 0, 0] == 15)\n        assert np.all(imgs_clipped[:, 0, 1] == 20)\n        assert np.all(imgs_clipped[:, 0, 2] == 25)\n\n        images = [np.zeros((1, 3), dtype=np.uint8) for _ in sm.xrange(2)]\n        for i in sm.xrange(len(images)):\n            images[i][0, 0] = 10\n            images[i][0, 1] = 20\n            images[i][0, 2] = 30\n        imgs_clipped = iaa.clip_augmented_images(images,\n                                                 min_value=15, max_value=25)\n        assert isinstance(imgs_clipped, list)\n        assert np.all([imgs_clipped[i][0, 0] == 15\n                       for i in sm.xrange(len(images))])\n        assert np.all([imgs_clipped[i][0, 1] == 20\n                       for i in sm.xrange(len(images))])\n        assert np.all([imgs_clipped[i][0, 2] ==\n                       25 for i in sm.xrange(len(images))])\n\n    assert len(caught_warnings) >= 1\n    assert \"deprecated\" in str(caught_warnings[-1].message)\n\n\ndef test_reduce_to_nonempty():\n    kpsois = [\n        ia.KeypointsOnImage([ia.Keypoint(x=0, y=1)], shape=(4, 4, 3)),\n        ia.KeypointsOnImage([ia.Keypoint(x=0, y=1), ia.Keypoint(x=1, y=0)],\n                            shape=(4, 4, 3)),\n        ia.KeypointsOnImage([], shape=(4, 4, 3)),\n        ia.KeypointsOnImage([ia.Keypoint(x=2, y=2)], shape=(4, 4, 3)),\n        ia.KeypointsOnImage([], shape=(4, 4, 3))\n    ]\n\n    kpsois_reduced, ids = iaa.reduce_to_nonempty(kpsois)\n    assert kpsois_reduced == [kpsois[0], kpsois[1], kpsois[3]]\n    assert ids == [0, 1, 3]\n\n    kpsois = [\n        ia.KeypointsOnImage([], shape=(4, 4, 3)),\n        ia.KeypointsOnImage([], shape=(4, 4, 3))\n    ]\n\n    kpsois_reduced, ids = iaa.reduce_to_nonempty(kpsois)\n    assert kpsois_reduced == []\n    assert ids == []\n\n    kpsois = [\n        ia.KeypointsOnImage([ia.Keypoint(x=0, y=1)], shape=(4, 4, 3))\n    ]\n\n    kpsois_reduced, ids = iaa.reduce_to_nonempty(kpsois)\n    assert kpsois_reduced == [kpsois[0]]\n    assert ids == [0]\n\n    kpsois = []\n\n    kpsois_reduced, ids = iaa.reduce_to_nonempty(kpsois)\n    assert kpsois_reduced == []\n    assert ids == []\n\n\ndef test_invert_reduce_to_nonempty():\n    kpsois = [\n        ia.KeypointsOnImage([ia.Keypoint(x=0, y=1)], shape=(4, 4, 3)),\n        ia.KeypointsOnImage([ia.Keypoint(x=0, y=1),\n                             ia.Keypoint(x=1, y=0)], shape=(4, 4, 3)),\n        ia.KeypointsOnImage([ia.Keypoint(x=2, y=2)], shape=(4, 4, 3)),\n    ]\n\n    kpsois_recovered = iaa.invert_reduce_to_nonempty(\n        kpsois, [0, 1, 2], [\"foo1\", \"foo2\", \"foo3\"])\n    assert kpsois_recovered == [\"foo1\", \"foo2\", \"foo3\"]\n\n    kpsois_recovered = iaa.invert_reduce_to_nonempty(kpsois, [1], [\"foo1\"])\n    assert np.all([\n        isinstance(kpsoi, ia.KeypointsOnImage)\n        for kpsoi\n        in kpsois])  # assert original list not changed\n    assert kpsois_recovered == [kpsois[0], \"foo1\", kpsois[2]]\n\n    kpsois_recovered = iaa.invert_reduce_to_nonempty(kpsois, [], [])\n    assert kpsois_recovered == [kpsois[0], kpsois[1], kpsois[2]]\n\n    kpsois_recovered = iaa.invert_reduce_to_nonempty([], [], [])\n    assert kpsois_recovered == []\n\n\nclass _DummyAugmenter(iaa.Augmenter):\n    def _augment_images(self, images, random_state, parents, hooks):\n        return images\n\n    def get_parameters(self):\n        return []\n\n\nclass _DummyAugmenterBBs(iaa.Augmenter):\n    def _augment_images(self, images, random_state, parents, hooks):\n        return images\n\n    def _augment_bounding_boxes(self, bounding_boxes_on_images, random_state,\n                                parents, hooks):\n        return [bbsoi.shift(x=1)\n                for bbsoi\n                in bounding_boxes_on_images]\n\n    def get_parameters(self):\n        return []\n\n\n# TODO remove _augment_heatmaps() and _augment_keypoints() here once they are\n#      no longer abstract methods but default to noop\nclass _DummyAugmenterCallsParent(iaa.Augmenter):\n    def _augment_images(self, images, random_state, parents, hooks):\n        return super(_DummyAugmenterCallsParent, self)\\\n            ._augment_images(images, random_state, parents, hooks)\n\n    def get_parameters(self):\n        return super(_DummyAugmenterCallsParent, self)\\\n            .get_parameters()\n\n\ndef _same_rs(rs1, rs2):\n    return rs1.equals(rs2)\n\n\n# TODO the test in here do not check everything, but instead only the cases\n#      that were not yet indirectly tested via other tests\nclass TestAugmenter(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___global_rng(self):\n        aug = _DummyAugmenter()\n        assert not aug.deterministic\n        assert aug.random_state.is_global_rng()\n\n    def test___init___deterministic(self):\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            aug = _DummyAugmenter(deterministic=True)\n            assert aug.deterministic\n            assert not aug.random_state.is_global_rng()\n\n            assert len(caught_warnings) == 1\n            assert (\n                \"is deprecated\"\n                in str(caught_warnings[-1].message))\n\n    # old name for parameter `seed`\n    def test___init___random_state_is_rng(self):\n        rs = iarandom.RNG(123)\n        aug = _DummyAugmenter(seed=rs)\n        assert aug.random_state.generator is rs.generator\n\n    # old name for parameter `seed`\n    def test___init___random_state_is_seed(self):\n        aug = _DummyAugmenter(seed=123)\n        assert aug.random_state.equals(iarandom.RNG(123))\n\n    def test___init___seed_is_random_state(self):\n        rs = iarandom.RNG(123)\n        aug = _DummyAugmenter(seed=rs)\n        assert aug.random_state.generator is rs.generator\n\n    def test___init___seed_is_seed(self):\n        aug = _DummyAugmenter(seed=123)\n        assert aug.random_state.equals(iarandom.RNG(123))\n\n    def test_augment_images_called_probably_with_single_image(self):\n        aug = _DummyAugmenter()\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            _ = aug.augment_images(np.zeros((16, 32, 3), dtype=np.uint8))\n\n        assert len(caught_warnings) == 1\n        assert (\n            \"indicates that you provided a single image with shape (H, W, C)\"\n            in str(caught_warnings[-1].message)\n        )\n\n    def test_augment_images_array_in_list_out(self):\n        self._test_augment_images_array_in_list_out_varying_channels(\n            [3] * 20)\n\n    def test_augment_images_array_in_list_out_single_channel(self):\n        self._test_augment_images_array_in_list_out_varying_channels(\n            [1] * 20)\n\n    def test_augment_images_array_in_list_out_no_channels(self):\n        self._test_augment_images_array_in_list_out_varying_channels(\n            [None] * 20)\n\n    def test_augment_images_array_in_list_out_varying_channels(self):\n        self._test_augment_images_array_in_list_out_varying_channels(\n            [\"random\"] * 20)\n\n    @classmethod\n    def _test_augment_images_array_in_list_out_varying_channels(cls,\n                                                                nb_channels):\n        assert len(nb_channels) == 20\n\n        aug = iaa.Crop(((1, 8), (1, 8), (1, 8), (1, 8)), keep_size=False)\n        seen = [0, 0]\n\n        for nb_channels_i in nb_channels:\n            if nb_channels_i == \"random\":\n                channels = np.random.choice([None, 1, 3, 4, 9], size=(16,))\n\n            elif nb_channels_i is None:\n                channels = np.random.choice([None], size=(16,))\n            else:\n                channels = np.random.choice([nb_channels_i], size=(16,))\n\n            images = [np.zeros((64, 64), dtype=np.uint8)\n                      if c is None\n                      else np.zeros((64, 64, c), dtype=np.uint8)\n                      for c in channels]\n\n            if nb_channels_i != \"random\":\n                images = np.array(images)\n\n            observed = aug.augment_images(images)\n\n            if ia.is_np_array(observed):\n                seen[0] += 1\n            else:\n                seen[1] += 1\n\n            for image, c in zip(observed, channels):\n                if c is None:\n                    assert image.ndim == 2\n                else:\n                    assert image.ndim == 3\n                    assert image.shape[2] == c\n                assert 48 <= image.shape[0] <= 62\n                assert 48 <= image.shape[1] <= 62\n\n        assert seen[0] <= 3\n        assert seen[1] >= 17\n\n    def test_augment_images_with_2d_inputs(self):\n        base_img1 = np.array([[0, 0, 1, 1],\n                              [0, 0, 1, 1],\n                              [0, 1, 1, 1]], dtype=np.uint8)\n        base_img2 = np.array([[0, 0, 1, 1],\n                              [0, 1, 1, 1],\n                              [0, 1, 0, 0]], dtype=np.uint8)\n\n        base_img1_flipped = np.array([[1, 1, 0, 0],\n                                      [1, 1, 0, 0],\n                                      [1, 1, 1, 0]], dtype=np.uint8)\n        base_img2_flipped = np.array([[1, 1, 0, 0],\n                                      [1, 1, 1, 0],\n                                      [0, 0, 1, 0]], dtype=np.uint8)\n\n        images = np.array([base_img1, base_img2])\n        images_flipped = np.array([base_img1_flipped, base_img2_flipped])\n        images_list = [base_img1, base_img2]\n        images_flipped_list = [base_img1_flipped, base_img2_flipped]\n        images_list2d3d = [base_img1, base_img2[:, :, np.newaxis]]\n        images_flipped_list2d3d = [\n            base_img1_flipped,\n            base_img2_flipped[:, :, np.newaxis]]\n\n        aug = iaa.Fliplr(1.0)\n        noaug = iaa.Fliplr(0.0)\n\n        # one numpy array as input\n        observed = aug.augment_images(images)\n        assert np.array_equal(observed, images_flipped)\n\n        observed = noaug.augment_images(images)\n        assert np.array_equal(observed, images)\n\n        # list of 2d images\n        observed = aug.augment_images(images_list)\n        assert array_equal_lists(observed, images_flipped_list)\n\n        observed = noaug.augment_images(images_list)\n        assert array_equal_lists(observed, images_list)\n\n        # list of images, one 2d and one 3d\n        observed = aug.augment_images(images_list2d3d)\n        assert array_equal_lists(observed, images_flipped_list2d3d)\n\n        observed = noaug.augment_images(images_list2d3d)\n        assert array_equal_lists(observed, images_list2d3d)\n\n    def test_augment_keypoints_single_instance(self):\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(10, 10)], shape=(32, 32, 3))\n        aug = iaa.Affine(translate_px={\"x\": 1})\n\n        kpsoi_aug = aug.augment_keypoints(kpsoi)\n\n        assert len(kpsoi_aug.keypoints) == 1\n        assert kpsoi_aug.keypoints[0].x == 11\n\n    def test_augment_keypoints_single_instance_rot90(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=2, y=5),\n               ia.Keypoint(x=3, y=3)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(5, 10, 3))\n        aug = iaa.Rot90(1, keep_size=False)\n\n        kpsoi_aug = aug.augment_keypoints(kpsoi)\n\n        # set offset to -1 if Rot90 uses int-based coordinate transformation\n        kp_offset = 0\n        assert np.allclose(kpsoi_aug.keypoints[0].x, 5 - 2 + kp_offset)\n        assert np.allclose(kpsoi_aug.keypoints[0].y, 1)\n        assert np.allclose(kpsoi_aug.keypoints[1].x, 5 - 5 + kp_offset)\n        assert np.allclose(kpsoi_aug.keypoints[1].y, 2)\n        assert np.allclose(kpsoi_aug.keypoints[2].x, 5 - 3 + kp_offset)\n        assert np.allclose(kpsoi_aug.keypoints[2].y, 3)\n\n    def test_augment_keypoints_many_instances_rot90(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=2, y=5),\n               ia.Keypoint(x=3, y=3)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(5, 10, 3))\n        aug = iaa.Rot90(1, keep_size=False)\n\n        kpsoi_aug = aug.augment_keypoints([kpsoi, kpsoi, kpsoi])\n\n        # set offset to -1 if Rot90 uses int-based coordinate transformation\n        kp_offset = 0\n        for i in range(3):\n            assert np.allclose(kpsoi_aug[i].keypoints[0].x, 5 - 2 + kp_offset)\n            assert np.allclose(kpsoi_aug[i].keypoints[0].y, 1)\n            assert np.allclose(kpsoi_aug[i].keypoints[1].x, 5 - 5 + kp_offset)\n            assert np.allclose(kpsoi_aug[i].keypoints[1].y, 2)\n            assert np.allclose(kpsoi_aug[i].keypoints[2].x, 5 - 3 + kp_offset)\n            assert np.allclose(kpsoi_aug[i].keypoints[2].y, 3)\n\n    def test_augment_keypoints_empty_instance(self):\n        # test empty KeypointsOnImage objects\n        kpsoi = ia.KeypointsOnImage([], shape=(32, 32, 3))\n        aug = iaa.Affine(translate_px={\"x\": 1})\n\n        kpsoi_aug = aug.augment_keypoints([kpsoi])\n\n        assert len(kpsoi_aug) == 1\n        assert len(kpsoi_aug[0].keypoints) == 0\n\n    def test_augment_keypoints_mixed_filled_and_empty_instances(self):\n        kpsoi1 = ia.KeypointsOnImage([], shape=(32, 32, 3))\n        kpsoi2 = ia.KeypointsOnImage([ia.Keypoint(10, 10)], shape=(32, 32, 3))\n        aug = iaa.Affine(translate_px={\"x\": 1})\n\n        kpsoi_aug = aug.augment_keypoints([kpsoi1, kpsoi2])\n\n        assert len(kpsoi_aug) == 2\n        assert len(kpsoi_aug[0].keypoints) == 0\n        assert len(kpsoi_aug[1].keypoints) == 1\n        assert kpsoi_aug[1].keypoints[0].x == 11\n\n    def test_augment_keypoints_aligned_despite_empty_instance(self):\n        # Test if augmenting lists of KeypointsOnImage is still aligned with\n        # image augmentation when one KeypointsOnImage instance is empty\n        # (no keypoints)\n        kpsoi_lst = [\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=0)], shape=(1, 10)),\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=0)], shape=(1, 10)),\n            ia.KeypointsOnImage([ia.Keypoint(x=1, y=0)], shape=(1, 10)),\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=0)], shape=(1, 10)),\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=0)], shape=(1, 10)),\n            ia.KeypointsOnImage([], shape=(1, 8)),\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=0)], shape=(1, 10)),\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=0)], shape=(1, 10)),\n            ia.KeypointsOnImage([ia.Keypoint(x=1, y=0)], shape=(1, 10)),\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=0)], shape=(1, 10)),\n            ia.KeypointsOnImage([ia.Keypoint(x=0, y=0)], shape=(1, 10))\n        ]\n        image = np.zeros((1, 10), dtype=np.uint8)\n        image[0, 0] = 255\n        images = np.tile(image[np.newaxis, :, :], (len(kpsoi_lst), 1, 1))\n\n        aug = iaa.Affine(translate_px={\"x\": (0, 8)}, order=0, mode=\"constant\",\n                         cval=0)\n\n        for i in sm.xrange(10):\n            for is_list in [False, True]:\n                with self.subTest(i=i, is_list=is_list):\n                    aug_det = aug.to_deterministic()\n                    if is_list:\n                        images_aug = aug_det.augment_images(list(images))\n                    else:\n                        images_aug = aug_det.augment_images(images)\n                    kpsoi_lst_aug = aug_det.augment_keypoints(kpsoi_lst)\n\n                    if is_list:\n                        images_aug = np.array(images_aug, dtype=np.uint8)\n                    translations_imgs = np.argmax(images_aug[:, 0, :], axis=1)\n                    translations_kps = [\n                        kpsoi.keypoints[0].x\n                        if len(kpsoi.keypoints) > 0\n                        else None\n                        for kpsoi\n                        in kpsoi_lst_aug]\n\n                    assert len([kpresult\n                                for kpresult\n                                in translations_kps\n                                if kpresult is None]) == 1\n                    assert translations_kps[5] is None\n                    translations_imgs = np.concatenate(\n                        [translations_imgs[0:5], translations_imgs[6:]])\n                    translations_kps = np.array(\n                        translations_kps[0:5] + translations_kps[6:],\n                        dtype=translations_imgs.dtype)\n                    translations_kps[2] -= 1\n                    translations_kps[8-1] -= 1\n                    assert np.array_equal(translations_imgs, translations_kps)\n\n    def test_augment_keypoints_aligned_despite_nongeometric_image_ops(self):\n        # Verify for keypoints that adding augmentations that only\n        # affect images doesn't lead to misalignments between image\n        # and keypoint transformations\n        augs = iaa.Sequential([\n            iaa.Fliplr(0.5),\n            iaa.AdditiveGaussianNoise(scale=(0.01, 0.1)),\n            iaa.Affine(translate_px={\"x\": (-10, 10), \"y\": (-10, 10)},\n                       order=0, mode=\"constant\", cval=0),\n            iaa.AddElementwise((0, 1)),\n            iaa.Flipud(0.5)\n        ], random_order=True)\n\n        kps = [ia.Keypoint(x=15.5, y=12.5), ia.Keypoint(x=23.5, y=20.5),\n               ia.Keypoint(x=61.5, y=36.5), ia.Keypoint(x=47.5, y=32.5)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(50, 80, 4))\n        image = kpsoi.to_keypoint_image(size=1)\n        images = np.tile(image[np.newaxis, ...], (20, 1, 1, 1))\n\n        for _ in sm.xrange(50):\n            images_aug, kpsois_aug = augs(images=images,\n                                          keypoints=[kpsoi]*len(images))\n\n            for image_aug, kpsoi_aug in zip(images_aug, kpsois_aug):\n                kpsoi_recovered = ia.KeypointsOnImage.from_keypoint_image(\n                    image_aug, nb_channels=4, threshold=100\n                )\n\n                for kp, kp_image in zip(kpsoi_aug.keypoints,\n                                        kpsoi_recovered.keypoints):\n                    distance = np.sqrt((kp.x - kp_image.x)**2\n                                       + (kp.y - kp_image.y)**2)\n                    assert distance <= 1\n\n    def test_augment_bounding_boxes(self):\n        aug = _DummyAugmenterBBs()\n        bb = ia.BoundingBox(x1=1, y1=4, x2=2, y2=5)\n        bbs = [bb]\n        bbsois = [ia.BoundingBoxesOnImage(bbs, shape=(10, 10, 3))]\n        bbsois_aug = aug.augment_bounding_boxes(bbsois)\n\n        bb_aug = bbsois_aug[0].bounding_boxes[0]\n\n        assert bb_aug.x1 == 1+1\n        assert bb_aug.y1 == 4\n        assert bb_aug.x2 == 2+1\n        assert bb_aug.y2 == 5\n\n    def test_augment_bounding_boxes_empty_bboi(self):\n        aug = _DummyAugmenterBBs()\n        bbsois = [ia.BoundingBoxesOnImage([], shape=(10, 10, 3))]\n\n        bbsois_aug = aug.augment_bounding_boxes(bbsois)\n\n        assert len(bbsois_aug) == 1\n        assert bbsois_aug[0].bounding_boxes == []\n\n    def test_augment_bounding_boxes_empty_list(self):\n        aug = _DummyAugmenterBBs()\n\n        bbsois_aug = aug.augment_bounding_boxes([])\n\n        assert bbsois_aug == []\n\n    def test_augment_bounding_boxes_single_instance(self):\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=1, x2=3, y1=4, y2=5),\n            ia.BoundingBox(x1=2.5, x2=3, y1=0, y2=2)\n        ], shape=(5, 10, 3))\n        aug = iaa.Identity()\n\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n\n        for bb_aug, bb in zip(bbsoi_aug.bounding_boxes, bbsoi.bounding_boxes):\n            assert np.allclose(bb_aug.x1, bb.x1)\n            assert np.allclose(bb_aug.x2, bb.x2)\n            assert np.allclose(bb_aug.y1, bb.y1)\n            assert np.allclose(bb_aug.y2, bb.y2)\n\n    def test_augment_bounding_boxes_single_instance_rot90(self):\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=1, x2=3, y1=4, y2=5),\n            ia.BoundingBox(x1=2.5, x2=3, y1=0, y2=2)\n        ], shape=(5, 10, 3))\n        aug = iaa.Rot90(1, keep_size=False)\n\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n\n        # set offset to -1 if Rot90 uses int-based coordinate transformation\n        kp_offset = 0\n        # Note here that the new coordinates are minima/maxima of the BB, so\n        # not as straight forward to compute the new coords as for keypoint\n        # augmentation\n        bb0 = bbsoi_aug.bounding_boxes[0]\n        bb1 = bbsoi_aug.bounding_boxes[1]\n        assert np.allclose(bb0.x1, 5 - 5 + kp_offset)\n        assert np.allclose(bb0.x2, 5 - 4 + kp_offset)\n        assert np.allclose(bb0.y1, 1)\n        assert np.allclose(bb0.y2, 3)\n        assert np.allclose(bb1.x1, 5 - 2 + kp_offset)\n        assert np.allclose(bb1.x2, 5 - 0 + kp_offset)\n        assert np.allclose(bb1.y1, 2.5)\n        assert np.allclose(bb1.y2, 3)\n\n    def test_augment_bounding_box_list_of_many_instances(self):\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=1, x2=3, y1=4, y2=5),\n            ia.BoundingBox(x1=2.5, x2=3, y1=0, y2=2)\n        ], shape=(5, 10, 3))\n        aug = iaa.Rot90(1, keep_size=False)\n\n        bbsoi_aug = aug.augment_bounding_boxes([bbsoi, bbsoi, bbsoi])\n\n        # set offset to -1 if Rot90 uses int-based coordinate transformation\n        kp_offset = 0\n        for i in range(3):\n            bb0 = bbsoi_aug[i].bounding_boxes[0]\n            bb1 = bbsoi_aug[i].bounding_boxes[1]\n            assert np.allclose(bb0.x1, 5 - 5 + kp_offset)\n            assert np.allclose(bb0.x2, 5 - 4 + kp_offset)\n            assert np.allclose(bb0.y1, 1)\n            assert np.allclose(bb0.y2, 3)\n            assert np.allclose(bb1.x1, 5 - 2 + kp_offset)\n            assert np.allclose(bb1.x2, 5 - 0 + kp_offset)\n            assert np.allclose(bb1.y1, 2.5)\n            assert np.allclose(bb1.y2, 3)\n\n    def test_augment_heatmaps_noop_single_heatmap(self):\n        heatmap_arr = np.linspace(0.0, 1.0, num=4*4).reshape((4, 4, 1))\n        heatmap = ia.HeatmapsOnImage(heatmap_arr.astype(np.float32),\n                                     shape=(4, 4, 3))\n\n        aug = iaa.Identity()\n        heatmap_aug = aug.augment_heatmaps(heatmap)\n        assert np.allclose(heatmap_aug.arr_0to1, heatmap.arr_0to1)\n\n    def test_augment_heatmaps_rot90_single_heatmap(self):\n        heatmap_arr = np.linspace(0.0, 1.0, num=4*4).reshape((4, 4, 1))\n        heatmap = ia.HeatmapsOnImage(heatmap_arr.astype(np.float32),\n                                     shape=(4, 4, 3))\n        aug = iaa.Rot90(1, keep_size=False)\n\n        heatmap_aug = aug.augment_heatmaps(heatmap)\n\n        assert np.allclose(heatmap_aug.arr_0to1, np.rot90(heatmap.arr_0to1, -1))\n\n    def test_augment_heatmaps_rot90_list_of_many_heatmaps(self):\n        heatmap_arr = np.linspace(0.0, 1.0, num=4*4).reshape((4, 4, 1))\n        heatmap = ia.HeatmapsOnImage(heatmap_arr.astype(np.float32),\n                                     shape=(4, 4, 3))\n        aug = iaa.Rot90(1, keep_size=False)\n\n        heatmaps_aug = aug.augment_heatmaps([heatmap] * 3)\n\n        for hm in heatmaps_aug:\n            assert np.allclose(hm.arr_0to1, np.rot90(heatmap.arr_0to1, -1))\n\n    def test_legacy_fallback_to_kp_aug_for_cbaois(self):\n        class _LegacyAugmenter(iaa.Augmenter):\n            def _augment_keypoints(self, keypoints_on_images, random_state,\n                                   parents, hooks):\n                return [kpsoi.shift(x=1) for kpsoi in keypoints_on_images]\n\n            def get_parameters(self):\n                return []\n\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)\n        ], shape=(4, 5, 3))\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        ], shape=(4, 5, 3))\n        lsoi = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (1, 0), (1, 1)])\n        ], shape=(4, 5, 3))\n\n        aug = _LegacyAugmenter()\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n        psoi_aug = aug.augment_polygons(psoi)\n        lsoi_aug = aug.augment_line_strings(lsoi)\n\n        assert bbsoi_aug[0].coords_almost_equals(bbsoi[0].shift(x=1))\n        assert psoi_aug[0].coords_almost_equals(psoi[0].shift(x=1))\n        assert lsoi_aug[0].coords_almost_equals(lsoi[0].shift(x=1))\n\n    def test_localize_random_state(self):\n        aug = _DummyAugmenter()\n\n        aug_localized = aug.localize_random_state()\n\n        assert aug_localized is not aug\n        assert aug.random_state.is_global_rng()\n        assert not aug_localized.random_state.is_global_rng()\n\n    def test_seed_(self):\n        aug1 = _DummyAugmenter()\n        aug2 = _DummyAugmenter().to_deterministic()\n        aug0 = iaa.Sequential([aug1, aug2])\n\n        aug0_copy = aug0.deepcopy()\n        assert _same_rs(aug0.random_state, aug0_copy.random_state)\n        assert _same_rs(aug0[0].random_state, aug0_copy[0].random_state)\n        assert _same_rs(aug0[1].random_state, aug0_copy[1].random_state)\n\n        aug0_copy.seed_()\n\n        assert not _same_rs(aug0.random_state, aug0_copy.random_state)\n        assert not _same_rs(aug0[0].random_state, aug0_copy[0].random_state)\n        assert _same_rs(aug0[1].random_state, aug0_copy[1].random_state)\n\n    def test_seed__deterministic_too(self):\n        aug1 = _DummyAugmenter()\n        aug2 = _DummyAugmenter().to_deterministic()\n        aug0 = iaa.Sequential([aug1, aug2])\n\n        aug0_copy = aug0.deepcopy()\n        assert _same_rs(aug0.random_state, aug0_copy.random_state)\n        assert _same_rs(aug0[0].random_state, aug0_copy[0].random_state)\n        assert _same_rs(aug0[1].random_state, aug0_copy[1].random_state)\n\n        aug0_copy.seed_(deterministic_too=True)\n\n        assert not _same_rs(aug0.random_state, aug0_copy.random_state)\n        assert not _same_rs(aug0[0].random_state, aug0_copy[0].random_state)\n        assert not _same_rs(aug0[1].random_state, aug0_copy[1].random_state)\n\n    def test_seed__with_integer(self):\n        aug1 = _DummyAugmenter()\n        aug2 = _DummyAugmenter().to_deterministic()\n        aug0 = iaa.Sequential([aug1, aug2])\n\n        aug0_copy = aug0.deepcopy()\n        assert _same_rs(aug0.random_state, aug0_copy.random_state)\n        assert _same_rs(aug0[0].random_state, aug0_copy[0].random_state)\n        assert _same_rs(aug0[1].random_state, aug0_copy[1].random_state)\n\n        aug0_copy.seed_(123)\n\n        assert not _same_rs(aug0.random_state, aug0_copy.random_state)\n        assert not _same_rs(aug0[0].random_state, aug0_copy[0].random_state)\n        assert _same_rs(aug0_copy.random_state, iarandom.RNG(123))\n        expected = iarandom.RNG(123).derive_rng_()\n        assert _same_rs(aug0_copy[0].random_state, expected)\n\n    def test_seed__with_rng(self):\n        aug1 = _DummyAugmenter()\n        aug2 = _DummyAugmenter().to_deterministic()\n        aug0 = iaa.Sequential([aug1, aug2])\n\n        aug0_copy = aug0.deepcopy()\n        assert _same_rs(aug0.random_state, aug0_copy.random_state)\n        assert _same_rs(aug0[0].random_state, aug0_copy[0].random_state)\n        assert _same_rs(aug0[1].random_state, aug0_copy[1].random_state)\n\n        aug0_copy.seed_(iarandom.RNG(123))\n\n        assert not _same_rs(aug0.random_state, aug0_copy.random_state)\n        assert not _same_rs(aug0[0].random_state, aug0_copy[0].random_state)\n        assert _same_rs(aug0[1].random_state, aug0_copy[1].random_state)\n        assert _same_rs(aug0_copy.random_state,\n                        iarandom.RNG(123))\n        expected = iarandom.RNG(123).derive_rng_()\n        assert _same_rs(aug0_copy[0].random_state, expected)\n\n    def test_get_parameters(self):\n        # test for \"raise NotImplementedError\"\n        aug = _DummyAugmenterCallsParent()\n        with self.assertRaises(NotImplementedError):\n            aug.get_parameters()\n\n    def test_get_all_children_flat(self):\n        aug1 = _DummyAugmenter()\n        aug21 = _DummyAugmenter()\n        aug2 = iaa.Sequential([aug21])\n        aug0 = iaa.Sequential([aug1, aug2])\n\n        children = aug0.get_all_children(flat=True)\n\n        assert isinstance(children, list)\n        assert children[0] == aug1\n        assert children[1] == aug2\n        assert children[2] == aug21\n\n    def test_get_all_children_not_flat(self):\n        aug1 = _DummyAugmenter()\n        aug21 = _DummyAugmenter()\n        aug2 = iaa.Sequential([aug21])\n        aug0 = iaa.Sequential([aug1, aug2])\n\n        children = aug0.get_all_children(flat=False)\n\n        assert isinstance(children, list)\n        assert children[0] == aug1\n        assert children[1] == aug2\n        assert isinstance(children[2], list)\n        assert children[2][0] == aug21\n\n    def test___repr___and___str__(self):\n        class DummyAugmenterRepr(iaa.Augmenter):\n            def _augment_images(self, images, random_state, parents, hooks):\n                return images\n\n            def _augment_heatmaps(self, heatmaps, random_state, parents, hooks):\n                return heatmaps\n\n            def _augment_keypoints(self, keypoints_on_images, random_state,\n                                   parents, hooks):\n                return keypoints_on_images\n\n            def get_parameters(self):\n                return [\"A\", \"B\", \"C\"]\n\n        aug1 = DummyAugmenterRepr(name=\"Example\")\n        aug2 = DummyAugmenterRepr(name=\"Example\").to_deterministic()\n\n        expected1 = (\n            \"DummyAugmenterRepr(\"\n            \"name=Example, parameters=[A, B, C], deterministic=False\"\n            \")\")\n        expected2 = (\n            \"DummyAugmenterRepr(\"\n            \"name=Example, parameters=[A, B, C], deterministic=True\"\n            \")\")\n\n        assert aug1.__repr__() == aug1.__str__() == expected1\n        assert aug2.__repr__() == aug2.__str__() == expected2\n\n\n# -----------\n# lambda functions used in Test TestAugmenter_augment_batches\n# in test method test_augment_batches_with_many_different_augmenters().\n# They are here instead of in the test method, because otherwise there were\n# issues with spawn mode not being able to pickle functions,\n# see issue #414.\n\ndef _augment_batches__lambda_func_images(\n        images, random_state, parents, hooks):\n    return images\n\n\ndef _augment_batches__lambda_func_keypoints(\n        keypoints_on_images, random_state, parents, hooks):\n    return keypoints_on_images\n\n\ndef _augment_batches__assertlambda_func_images(\n        images, random_state, parents, hooks):\n    return True\n\n\ndef _augment_batches__assertlambda_func_keypoints(\n        keypoints_on_images, random_state, parents, hooks):\n    return True\n# -----------\n\n\nclass TestAugmenter_augment_batches(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_augment_batches_list_of_empty_list_deprecated(self):\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            aug = _DummyAugmenter()\n\n            batches_aug = list(aug.augment_batches([[]]))\n\n            assert isinstance(batches_aug, list)\n            assert len(batches_aug) == 1\n            assert isinstance(batches_aug[0], list)\n\n        assert len(caught_warnings) == 1\n        assert \"deprecated\" in str(caught_warnings[-1].message)\n\n    def test_augment_batches_list_of_arrays_deprecated(self):\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            aug = _DummyAugmenter()\n            image_batches = [np.zeros((1, 2, 2, 3), dtype=np.uint8)]\n\n            batches_aug = list(aug.augment_batches(image_batches))\n\n            assert isinstance(batches_aug, list)\n            assert len(batches_aug) == 1\n            assert array_equal_lists(batches_aug, image_batches)\n\n        assert len(caught_warnings) == 1\n        assert \"deprecated\" in str(caught_warnings[-1].message)\n\n    def test_augment_batches_list_of_list_of_arrays_deprecated(self):\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            aug = _DummyAugmenter()\n            image_batches = [[np.zeros((2, 2, 3), dtype=np.uint8),\n                              np.zeros((2, 3, 3))]]\n\n            batches_aug = list(aug.augment_batches(image_batches))\n\n            assert isinstance(batches_aug, list)\n            assert len(batches_aug) == 1\n            assert array_equal_lists(batches_aug[0], image_batches[0])\n\n        assert len(caught_warnings) == 1\n        assert \"deprecated\" in str(caught_warnings[-1].message)\n\n    def test_augment_batches_invalid_datatype(self):\n        aug = _DummyAugmenter()\n        with self.assertRaises(Exception):\n            _ = list(aug.augment_batches(None))\n\n    def test_augment_batches_list_of_invalid_datatype(self):\n        aug = _DummyAugmenter()\n        got_exception = False\n        try:\n            _ = list(aug.augment_batches([None]))\n        except Exception as exc:\n            got_exception = True\n            assert \"Unknown datatype of batch\" in str(exc)\n        assert got_exception\n\n    def test_augment_batches_list_of_list_of_invalid_datatype(self):\n        aug = _DummyAugmenter()\n        got_exception = False\n        try:\n            _ = list(aug.augment_batches([[None]]))\n        except Exception as exc:\n            got_exception = True\n            assert \"Unknown datatype in batch[0]\" in str(exc)\n        assert got_exception\n\n    def test_augment_batches_batch_with_list_of_images(self):\n        image = np.array([[0, 0, 1, 1, 1],\n                          [0, 0, 1, 1, 1],\n                          [0, 1, 1, 1, 1]], dtype=np.uint8)\n        image_flipped = np.fliplr(image)\n        keypoint = ia.Keypoint(x=2, y=1)\n        keypoints = [ia.KeypointsOnImage([keypoint], shape=image.shape + (1,))]\n        kp_flipped = ia.Keypoint(\n            x=image.shape[1]-keypoint.x,\n            y=keypoint.y\n        )\n\n        # basic functionality test (images as list)\n        for bg in [True, False]:\n            seq = iaa.Fliplr(1.0)\n            batches = [ia.Batch(images=[np.copy(image)], keypoints=keypoints)]\n            batches_aug = list(seq.augment_batches(batches, background=bg))\n            baug0 = batches_aug[0]\n            assert np.array_equal(baug0.images_aug[0], image_flipped)\n            assert baug0.keypoints_aug[0].keypoints[0].x == kp_flipped.x\n            assert baug0.keypoints_aug[0].keypoints[0].y == kp_flipped.y\n            assert np.array_equal(baug0.images_unaug[0], image)\n            assert baug0.keypoints_unaug[0].keypoints[0].x == keypoint.x\n            assert baug0.keypoints_unaug[0].keypoints[0].y == keypoint.y\n\n    def test_augment_batches_batch_with_array_of_images(self):\n        image = np.array([[0, 0, 1, 1, 1],\n                          [0, 0, 1, 1, 1],\n                          [0, 1, 1, 1, 1]], dtype=np.uint8)\n        image_flipped = np.fliplr(image)\n        keypoint = ia.Keypoint(x=2, y=1)\n        keypoints = [ia.KeypointsOnImage([keypoint], shape=image.shape + (1,))]\n        kp_flipped = ia.Keypoint(\n            x=image.shape[1]-keypoint.x,\n            y=keypoint.y\n        )\n\n        # basic functionality test (images as array)\n        for bg in [True, False]:\n            seq = iaa.Fliplr(1.0)\n            batches = [ia.Batch(images=np.uint8([np.copy(image)]),\n                                keypoints=keypoints)]\n            batches_aug = list(seq.augment_batches(batches, background=bg))\n            baug0 = batches_aug[0]\n            assert np.array_equal(baug0.images_aug, np.uint8([image_flipped]))\n            assert baug0.keypoints_aug[0].keypoints[0].x == kp_flipped.x\n            assert baug0.keypoints_aug[0].keypoints[0].y == kp_flipped.y\n            assert np.array_equal(baug0.images_unaug, np.uint8([image]))\n            assert baug0.keypoints_unaug[0].keypoints[0].x == keypoint.x\n            assert baug0.keypoints_unaug[0].keypoints[0].y == keypoint.y\n\n    def test_augment_batches_background(self):\n        image = np.array([[0, 0, 1, 1, 1],\n                          [0, 0, 1, 1, 1],\n                          [0, 1, 1, 1, 1]], dtype=np.uint8)\n        image_flipped = np.fliplr(image)\n        kps = ia.Keypoint(x=2, y=1)\n        kpsoi = ia.KeypointsOnImage([kps], shape=image.shape + (1,))\n        kp_flipped = ia.Keypoint(\n            x=image.shape[1]-kps.x,\n            y=kps.y\n        )\n\n        seq = iaa.Fliplr(0.5)\n\n        for bg, as_array in itertools.product([False, True], [False, True]):\n            # with images as list\n            nb_flipped_images = 0\n            nb_flipped_keypoints = 0\n            nb_iterations = 1000\n            images = (\n                np.uint8([np.copy(image)])\n                if as_array\n                else [np.copy(image)])\n            batches = [\n                ia.Batch(images=images,\n                         keypoints=[kpsoi.deepcopy()])\n                for _ in sm.xrange(nb_iterations)\n            ]\n\n            batches_aug = list(seq.augment_batches(batches, background=bg))\n\n            for batch_aug in batches_aug:\n                image_aug = batch_aug.images_aug[0]\n                keypoint_aug = batch_aug.keypoints_aug[0].keypoints[0]\n\n                img_matches_unflipped = np.array_equal(image_aug, image)\n                img_matches_flipped = np.array_equal(image_aug, image_flipped)\n                assert img_matches_unflipped or img_matches_flipped\n                if img_matches_flipped:\n                    nb_flipped_images += 1\n\n                kp_matches_unflipped = (\n                    np.isclose(keypoint_aug.x, kps.x)\n                    and np.isclose(keypoint_aug.y, kps.y))\n                kp_matches_flipped = (\n                    np.isclose(keypoint_aug.x, kp_flipped.x)\n                    and np.isclose(keypoint_aug.y, kp_flipped.y))\n                assert kp_matches_flipped or kp_matches_unflipped\n                if kp_matches_flipped:\n                    nb_flipped_keypoints += 1\n            assert 0.4*nb_iterations <= nb_flipped_images <= 0.6*nb_iterations\n            assert nb_flipped_images == nb_flipped_keypoints\n\n    def test_augment_batches_with_many_different_augmenters(self):\n        image = np.array([[0, 0, 1, 1, 1],\n                          [0, 0, 1, 1, 1],\n                          [0, 1, 1, 1, 1]], dtype=np.uint8)\n        keypoint = ia.Keypoint(x=2, y=1)\n        keypoints = [ia.KeypointsOnImage([keypoint], shape=image.shape + (1,))]\n\n        augs = [\n            iaa.Sequential([iaa.Fliplr(1.0), iaa.Flipud(1.0)]),\n            iaa.SomeOf(1, [iaa.Fliplr(1.0), iaa.Flipud(1.0)]),\n            iaa.OneOf([iaa.Fliplr(1.0), iaa.Flipud(1.0)]),\n            iaa.Sometimes(1.0, iaa.Fliplr(1)),\n            iaa.WithColorspace(\"HSV\", children=iaa.Add((-50, 50))),\n            iaa.WithChannels([0], iaa.Add((-50, 50))),\n            iaa.Identity(name=\"Identity-nochange\"),\n            iaa.Lambda(\n                func_images=_augment_batches__lambda_func_images,\n                func_keypoints=_augment_batches__lambda_func_keypoints,\n                name=\"Lambda-nochange\"\n            ),\n            iaa.AssertLambda(\n                func_images=_augment_batches__assertlambda_func_images,\n                func_keypoints=_augment_batches__assertlambda_func_keypoints,\n                name=\"AssertLambda-nochange\"\n            ),\n            iaa.AssertShape(\n                (None, 64, 64, 3),\n                check_keypoints=False,\n                name=\"AssertShape-nochange\"\n            ),\n            iaa.Resize((0.5, 0.9)),\n            iaa.CropAndPad(px=(-50, 50)),\n            iaa.Pad(px=(1, 50)),\n            iaa.Crop(px=(1, 50)),\n            iaa.Fliplr(1.0),\n            iaa.Flipud(1.0),\n            iaa.Superpixels(p_replace=(0.25, 1.0), n_segments=(16, 128)),\n            iaa.ChangeColorspace(to_colorspace=\"GRAY\"),\n            iaa.Grayscale(alpha=(0.1, 1.0)),\n            iaa.GaussianBlur(1.0),\n            iaa.AverageBlur(5),\n            iaa.MedianBlur(5),\n            iaa.Convolve(np.array([[0, 1, 0],\n                                   [1, -4, 1],\n                                   [0, 1, 0]])),\n            iaa.Sharpen(alpha=(0.1, 1.0), lightness=(0.8, 1.2)),\n            iaa.Emboss(alpha=(0.1, 1.0), strength=(0.8, 1.2)),\n            iaa.EdgeDetect(alpha=(0.1, 1.0)),\n            iaa.DirectedEdgeDetect(alpha=(0.1, 1.0), direction=(0.0, 1.0)),\n            iaa.Add((-50, 50)),\n            iaa.AddElementwise((-50, 50)),\n            iaa.AdditiveGaussianNoise(scale=(0.1, 1.0)),\n            iaa.Multiply((0.6, 1.4)),\n            iaa.MultiplyElementwise((0.6, 1.4)),\n            iaa.Dropout((0.3, 0.5)),\n            iaa.CoarseDropout((0.3, 0.5), size_percent=(0.05, 0.2)),\n            iaa.Invert(0.5),\n            iaa.Affine(\n                scale=(0.7, 1.3),\n                translate_percent=(-0.1, 0.1),\n                rotate=(-20, 20),\n                shear=(-20, 20),\n                order=ia.ALL,\n                mode=ia.ALL,\n                cval=(0, 255)),\n            iaa.PiecewiseAffine(scale=(0.1, 0.3)),\n            iaa.ElasticTransformation(alpha=2.0)\n        ]\n\n        nb_iterations = 100\n        image = ia.data.quokka(size=(64, 64))\n        batches = [ia.Batch(images=[np.copy(image)],\n                            keypoints=[keypoints[0].deepcopy()])\n                   for _ in sm.xrange(nb_iterations)]\n        for aug in augs:\n            nb_changed = 0\n            batches_aug = list(aug.augment_batches(batches, background=True))\n            for batch_aug in batches_aug:\n                image_aug = batch_aug.images_aug[0]\n                if (image.shape != image_aug.shape\n                        or not np.array_equal(image, image_aug)):\n                    nb_changed += 1\n                    if nb_changed > 10:\n                        break\n            if \"-nochange\" not in aug.name:\n                assert nb_changed > 0\n            else:\n                assert nb_changed == 0\n\n\nclass TestAugmenter_augment_batch(unittest.TestCase):\n    def test_deprecation(self):\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            aug = _InplaceDummyAugmenterImgsArray(1)\n\n            batch = ia.UnnormalizedBatch(\n                images=np.zeros((1, 1, 1, 3), dtype=np.uint8))\n            _batch_aug = aug.augment_batch(batch)\n\n            assert len(caught_warnings) == 1\n            assert \"is deprecated\" in str(caught_warnings[0].message)\n\n    def test_augments_correctly_images(self):\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            image = np.arange(10*20).astype(np.uint8).reshape((10, 20, 1))\n            image = np.tile(image, (1, 1, 3))\n            image[:, :, 0] += 0\n            image[:, :, 1] += 1\n            image[:, :, 2] += 2\n            images = image[np.newaxis, :, :, :]\n            image_cp = np.copy(image)\n\n            aug = _InplaceDummyAugmenterImgsArray(1)\n\n            batch = ia.UnnormalizedBatch(images=images)\n            batch_aug = aug.augment_batch(batch)\n\n            image_unaug = batch_aug.images_unaug[0, :, :, :]\n            image_aug = batch_aug.images_aug[0, :, :, :]\n\n            assert batch_aug is batch\n            assert batch_aug.images_aug is not batch.images_unaug\n            assert batch_aug.images_aug is not batch_aug.images_unaug\n\n            assert np.array_equal(image, image_cp)\n            assert np.array_equal(image_unaug, image_cp)\n            assert np.array_equal(image_aug, image_cp + 1)\n\n\nclass TestAugmenter_augment_batch_(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_verify_inplace_aug__imgs__unnormalized_batch(self):\n        image = np.arange(10*20).astype(np.uint8).reshape((10, 20, 1))\n        image = np.tile(image, (1, 1, 3))\n        image[:, :, 0] += 0\n        image[:, :, 1] += 1\n        image[:, :, 2] += 2\n        images = image[np.newaxis, :, :, :]\n        image_cp = np.copy(image)\n\n        aug = _InplaceDummyAugmenterImgsArray(1)\n\n        batch = ia.UnnormalizedBatch(images=images)\n        batch_aug = aug.augment_batch_(batch)\n        image_unaug = batch_aug.images_unaug[0, :, :, :]\n        image_aug = batch_aug.images_aug[0, :, :, :]\n\n        assert batch_aug is batch\n        assert batch_aug.images_aug is not batch.images_unaug\n        assert batch_aug.images_aug is not batch_aug.images_unaug\n\n        assert np.array_equal(image, image_cp)\n        assert np.array_equal(image_unaug, image_cp)\n        assert np.array_equal(image_aug, image_cp + 1)\n\n    def test_verify_inplace_aug__imgs__normalized_batch(self):\n        image = np.arange(10*20).astype(np.uint8).reshape((10, 20, 1))\n        image = np.tile(image, (1, 1, 3))\n        image[:, :, 0] += 0\n        image[:, :, 1] += 1\n        image[:, :, 2] += 2\n        images = image[np.newaxis, :, :, :]\n        image_cp = np.copy(image)\n\n        aug = _InplaceDummyAugmenterImgsArray(1)\n\n        batch = ia.Batch(images=images)\n        batch_aug = aug.augment_batch_(batch)\n        image_unaug = batch_aug.images_unaug[0, :, :, :]\n        image_aug = batch_aug.images_aug[0, :, :, :]\n\n        assert batch_aug is batch\n        assert batch_aug.images_aug is not batch.images_unaug\n        assert batch_aug.images_aug is not batch_aug.images_unaug\n\n        assert np.array_equal(image, image_cp)\n        assert np.array_equal(image_unaug, image_cp)\n        assert np.array_equal(image_aug, image_cp + 1)\n\n    def test_verify_inplace_aug__imgs__batchinaug(self):\n        image = np.arange(10*20).astype(np.uint8).reshape((10, 20, 1))\n        image = np.tile(image, (1, 1, 3))\n        image[:, :, 0] += 0\n        image[:, :, 1] += 1\n        image[:, :, 2] += 2\n        images = image[np.newaxis, :, :, :]\n        image_cp = np.copy(image)\n\n        aug = _InplaceDummyAugmenterImgsArray(1)\n\n        batch = _BatchInAugmentation(images=images)\n        batch_aug = aug.augment_batch_(batch)\n        image_aug = batch_aug.images[0, :, :, :]\n\n        assert batch_aug is batch\n        assert batch_aug.images is batch.images\n\n        assert not np.array_equal(image, image_cp)\n        assert np.array_equal(image_aug, image_cp + 1)\n\n    def test_verify_inplace_aug__segmaps__normalized_batch(self):\n        segmap_arr = np.zeros((10, 20, 3), dtype=np.int32)\n        segmap_arr[3:6, 3:9] = 1\n        segmap = ia.SegmentationMapsOnImage(segmap_arr, shape=(10, 20, 3))\n        segmap_cp = ia.SegmentationMapsOnImage(np.copy(segmap_arr),\n                                               shape=(10, 20, 3))\n\n        aug = _InplaceDummyAugmenterSegMaps(1)\n\n        batch = ia.Batch(segmentation_maps=[segmap])\n        batch_aug = aug.augment_batch_(batch)\n        segmap_unaug = batch_aug.segmentation_maps_unaug[0]\n        segmap_aug = batch_aug.segmentation_maps_aug[0]\n\n        assert batch_aug is batch\n        assert (batch_aug.segmentation_maps_aug\n                is not batch.segmentation_maps_unaug)\n        assert (batch_aug.segmentation_maps_aug\n                is not batch_aug.segmentation_maps_unaug)\n\n        assert np.array_equal(segmap.get_arr(), segmap_cp.get_arr())\n        assert np.array_equal(segmap_unaug.get_arr(), segmap_cp.get_arr())\n        assert np.array_equal(segmap_aug.get_arr(), segmap_cp.get_arr() + 1)\n\n    def test_verify_inplace_aug__keypoints_normalized_batch(self):\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)],\n                                    shape=(10, 20, 3))\n        kpsoi_cp = ia.KeypointsOnImage([ia.Keypoint(x=1, y=2)],\n                                       shape=(10, 20, 3))\n\n        aug = _InplaceDummyAugmenterKeypoints(x=1, y=3)\n\n        batch = ia.Batch(keypoints=[kpsoi])\n        batch_aug = aug.augment_batch_(batch)\n        kpsoi_unaug = batch_aug.keypoints_unaug[0]\n        kpsoi_aug = batch_aug.keypoints_aug[0]\n\n        assert batch_aug is batch\n        assert (batch_aug.keypoints_aug\n                is not batch.keypoints_unaug)\n        assert (batch_aug.keypoints_aug\n                is not batch_aug.keypoints_unaug)\n\n        assert np.allclose(kpsoi.to_xy_array(), kpsoi_cp.to_xy_array())\n        assert np.allclose(kpsoi_unaug.to_xy_array(), kpsoi_cp.to_xy_array())\n        assert np.allclose(kpsoi_aug.to_xy_array()[:, 0],\n                           kpsoi_cp.to_xy_array()[:, 0] + 1)\n        assert np.allclose(kpsoi_aug.to_xy_array()[:, 1],\n                           kpsoi_cp.to_xy_array()[:, 1] + 3)\n\n    def test_call_changes_global_rng_state(self):\n        state_before = copy.deepcopy(iarandom.get_global_rng().state)\n        aug = iaa.Rot90(k=(0, 3))\n        image = np.arange(4*4).astype(np.uint8).reshape((4, 4))\n        batch = ia.UnnormalizedBatch(images=[image])\n\n        _batch_aug = aug.augment_batch_(batch)\n\n        state_after = iarandom.get_global_rng().state\n        assert repr(state_before) != repr(state_after)\n\n    def test_multiple_calls_produce_not_the_same_results(self):\n        aug = iaa.Rot90(k=(0, 3))\n        image = np.arange(4*4).astype(np.uint8).reshape((4, 4))\n        nb_images = 1000\n        batch1 = ia.UnnormalizedBatch(images=[image] * nb_images)\n        batch2 = ia.UnnormalizedBatch(images=[image] * nb_images)\n        batch3 = ia.UnnormalizedBatch(images=[image] * nb_images)\n\n        batch_aug1 = aug.augment_batch_(batch1)\n        batch_aug2 = aug.augment_batch_(batch2)\n        batch_aug3 = aug.augment_batch_(batch3)\n\n        assert batch_aug1 is not batch_aug2\n        assert batch_aug1 is not batch_aug2\n        assert batch_aug2 is not batch_aug3\n\n        nb_equal = [0, 0, 0]\n        for image_aug1, image_aug2, image_aug3 in zip(batch_aug1.images_aug,\n                                                      batch_aug2.images_aug,\n                                                      batch_aug3.images_aug):\n            nb_equal[0] += int(np.array_equal(image_aug1, image_aug2))\n            nb_equal[1] += int(np.array_equal(image_aug1, image_aug3))\n            nb_equal[2] += int(np.array_equal(image_aug2, image_aug3))\n\n        assert nb_equal[0] < (0.25 + 0.1) * nb_images\n        assert nb_equal[1] < (0.25 + 0.1) * nb_images\n        assert nb_equal[2] < (0.25 + 0.1) * nb_images\n\n    def test_calls_affect_other_augmenters_with_global_rng(self):\n        # with calling aug1\n        iarandom.seed(1)\n        aug1 = iaa.Rot90(k=(0, 3))\n        aug2 = iaa.Add((0, 255))\n        image = np.arange(4*4).astype(np.uint8).reshape((4, 4))\n        nb_images = 50\n        batch1 = ia.UnnormalizedBatch(images=[image] * 1)\n        batch2 = ia.UnnormalizedBatch(images=[image] * nb_images)\n\n        batch_aug11 = aug1.augment_batch_(batch1)\n        batch_aug12 = aug2.augment_batch_(batch2)\n\n        # with calling aug1, repetition (to see that seed() works)\n        iarandom.seed(1)\n        aug1 = iaa.Rot90(k=(0, 3))\n        aug2 = iaa.Add((0, 255))\n        image = np.arange(4*4).astype(np.uint8).reshape((4, 4))\n        nb_images = 50\n        batch1 = ia.UnnormalizedBatch(images=[image] * 1)\n        batch2 = ia.UnnormalizedBatch(images=[image] * nb_images)\n\n        batch_aug21 = aug1.augment_batch_(batch1)\n        batch_aug22 = aug2.augment_batch_(batch2)\n\n        # without calling aug1\n        iarandom.seed(1)\n        aug2 = iaa.Add((0, 255))\n        image = np.arange(4*4).astype(np.uint8).reshape((4, 4))\n        nb_images = 50\n        batch2 = ia.UnnormalizedBatch(images=[image] * nb_images)\n\n        batch_aug32 = aug2.augment_batch_(batch2)\n\n        # comparison\n        assert np.array_equal(\n            np.array(batch_aug12.images_aug, dtype=np.uint8),\n            np.array(batch_aug22.images_aug, dtype=np.uint8)\n        )\n        assert not np.array_equal(\n            np.array(batch_aug12.images_aug, dtype=np.uint8),\n            np.array(batch_aug32.images_aug, dtype=np.uint8)\n        )\n\n\nclass TestAugmenter_augment_segmentation_maps(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_augment_segmentation_maps_single_instance(self):\n        arr = np.int32([\n            [0, 1, 1],\n            [0, 1, 1],\n            [0, 1, 1]\n        ])\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n        aug = iaa.Identity()\n\n        segmap_aug = aug.augment_segmentation_maps(segmap)\n\n        assert np.array_equal(segmap_aug.arr, segmap.arr)\n\n    def test_augment_segmentation_maps_list_of_single_instance(self):\n        arr = np.int32([\n            [0, 1, 1],\n            [0, 1, 1],\n            [0, 1, 1]\n        ])\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n        aug = iaa.Identity()\n\n        segmap_aug = aug.augment_segmentation_maps([segmap])[0]\n\n        assert np.array_equal(segmap_aug.arr, segmap.arr)\n\n    def test_augment_segmentation_maps_affine(self):\n        arr = np.int32([\n            [0, 1, 1],\n            [0, 1, 1],\n            [0, 1, 1]\n        ])\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n        aug = iaa.Affine(translate_px={\"x\": 1})\n\n        segmap_aug = aug.augment_segmentation_maps(segmap)\n\n        expected = np.int32([\n            [0, 0, 1],\n            [0, 0, 1],\n            [0, 0, 1]\n        ])\n        expected = expected[:, :, np.newaxis]\n        assert np.array_equal(segmap_aug.arr, expected)\n\n    def test_augment_segmentation_maps_pad(self):\n        arr = np.int32([\n            [0, 1, 1],\n            [0, 1, 1],\n            [0, 1, 1]\n        ])\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n        aug = iaa.Pad(px=(1, 0, 0, 0), keep_size=False)\n\n        segmap_aug = aug.augment_segmentation_maps(segmap)\n\n        expected = np.int32([\n            [0, 0, 0],\n            [0, 1, 1],\n            [0, 1, 1],\n            [0, 1, 1]\n        ])\n        expected = expected[:, :, np.newaxis]\n        assert np.array_equal(segmap_aug.arr, expected)\n\n    def test_augment_segmentation_maps_pad_some_classes_not_provided(self):\n        # only classes 0 and 3\n        arr = np.int32([\n            [0, 3, 3],\n            [0, 3, 3],\n            [0, 3, 3]\n        ])\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n        aug = iaa.Pad(px=(1, 0, 0, 0), keep_size=False)\n\n        segmap_aug = aug.augment_segmentation_maps(segmap)\n\n        expected = np.int32([\n            [0, 0, 0],\n            [0, 3, 3],\n            [0, 3, 3],\n            [0, 3, 3]\n        ])\n        expected = expected[:, :, np.newaxis]\n        assert np.array_equal(segmap_aug.arr, expected)\n\n    def test_augment_segmentation_maps_pad_only_background_class(self):\n        # only class 0\n        arr = np.int32([\n            [0, 0, 0],\n            [0, 0, 0],\n            [0, 0, 0]\n        ])\n        segmap = ia.SegmentationMapsOnImage(arr, shape=(3, 3))\n        aug = iaa.Pad(px=(1, 0, 0, 0), keep_size=False)\n\n        segmap_aug = aug.augment_segmentation_maps(segmap)\n\n        expected = np.int32([\n            [0, 0, 0],\n            [0, 0, 0],\n            [0, 0, 0],\n            [0, 0, 0]\n        ])\n        expected = expected[:, :, np.newaxis]\n        assert np.array_equal(segmap_aug.arr, expected)\n\n    def test_augment_segmentation_maps_multichannel_rot90(self):\n        segmap = ia.SegmentationMapsOnImage(\n            np.arange(0, 4*4).reshape((4, 4, 1)).astype(np.int32),\n            shape=(4, 4, 3)\n        )\n        aug = iaa.Rot90(1, keep_size=False)\n\n        segmaps_aug = aug.augment_segmentation_maps([segmap, segmap, segmap])\n\n        for i in range(3):\n            assert np.allclose(segmaps_aug[i].arr, np.rot90(segmap.arr, -1))\n\n\nclass TestAugmenter_draw_grid(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_draw_grid_list_of_3d_arrays(self):\n        # list, shape (3, 3, 3)\n        aug = _DummyAugmenter()\n        image = np.zeros((3, 3, 3), dtype=np.uint8)\n        image[0, 0, :] = 10\n        image[0, 1, :] = 50\n        image[1, 1, :] = 255\n\n        grid = aug.draw_grid([image], rows=2, cols=2)\n\n        grid_expected = np.vstack([\n            np.hstack([image, image]),\n            np.hstack([image, image])\n        ])\n        assert np.array_equal(grid, grid_expected)\n\n    def test_draw_grid_list_of_2d_arrays(self):\n        # list, shape (3, 3)\n        aug = _DummyAugmenter()\n        image = np.zeros((3, 3, 3), dtype=np.uint8)\n        image[0, 0, :] = 10\n        image[0, 1, :] = 50\n        image[1, 1, :] = 255\n\n        grid = aug.draw_grid([image[..., 0]], rows=2, cols=2)\n\n        grid_expected = np.vstack([\n            np.hstack([image[..., 0:1], image[..., 0:1]]),\n            np.hstack([image[..., 0:1], image[..., 0:1]])\n        ])\n        grid_expected = np.tile(grid_expected, (1, 1, 3))\n        assert np.array_equal(grid, grid_expected)\n\n    def test_draw_grid_list_of_1d_arrays_fails(self):\n        # list, shape (2,)\n        aug = _DummyAugmenter()\n\n        with self.assertRaises(Exception):\n            _ = aug.draw_grid([np.zeros((2,), dtype=np.uint8)], rows=2, cols=2)\n\n    def test_draw_grid_4d_array(self):\n        # array, shape (1, 3, 3, 3)\n        aug = _DummyAugmenter()\n        image = np.zeros((3, 3, 3), dtype=np.uint8)\n        image[0, 0, :] = 10\n        image[0, 1, :] = 50\n        image[1, 1, :] = 255\n\n        grid = aug.draw_grid(np.uint8([image]), rows=2, cols=2)\n\n        grid_expected = np.vstack([\n            np.hstack([image, image]),\n            np.hstack([image, image])\n        ])\n        assert np.array_equal(grid, grid_expected)\n\n    def test_draw_grid_3d_array(self):\n        # array, shape (3, 3, 3)\n        aug = _DummyAugmenter()\n        image = np.zeros((3, 3, 3), dtype=np.uint8)\n        image[0, 0, :] = 10\n        image[0, 1, :] = 50\n        image[1, 1, :] = 255\n\n        grid = aug.draw_grid(image, rows=2, cols=2)\n\n        grid_expected = np.vstack([\n            np.hstack([image, image]),\n            np.hstack([image, image])\n        ])\n        assert np.array_equal(grid, grid_expected)\n\n    def test_draw_grid_2d_array(self):\n        # array, shape (3, 3)\n        aug = _DummyAugmenter()\n        image = np.zeros((3, 3, 3), dtype=np.uint8)\n        image[0, 0, :] = 10\n        image[0, 1, :] = 50\n        image[1, 1, :] = 255\n\n        grid = aug.draw_grid(image[..., 0], rows=2, cols=2)\n\n        grid_expected = np.vstack([\n            np.hstack([image[..., 0:1], image[..., 0:1]]),\n            np.hstack([image[..., 0:1], image[..., 0:1]])\n        ])\n        grid_expected = np.tile(grid_expected, (1, 1, 3))\n        assert np.array_equal(grid, grid_expected)\n\n    def test_draw_grid_1d_array(self):\n        # array, shape (2,)\n        aug = _DummyAugmenter()\n\n        with self.assertRaises(Exception):\n            _ = aug.draw_grid(np.zeros((2,), dtype=np.uint8), rows=2, cols=2)\n\n\n@six.add_metaclass(ABCMeta)\nclass _TestAugmenter_augment_cbaois(object):\n    \"\"\"Class that is used to test augment_polygons() and augment_line_strings().\n\n    Originally this was only used for polygons and then made more flexible.\n    This is why some descriptions are still geared towards polygons.\n\n    Abbreviations:\n        cba = coordinate based augmentable, e.g. Polygon\n        cbaoi = coordinate based augmentable on image, e.g. PolygonsOnImage\n\n    \"\"\"\n\n    def setUp(self):\n        reseed()\n\n    @abstractmethod\n    def _augfunc(self, augmenter, *args, **kwargs):\n        \"\"\"Return augmenter.augment_*(...).\"\"\"\n\n    @property\n    @abstractmethod\n    def _ObjClass(self):\n        \"\"\"Return Polygon, LineString or similar class.\"\"\"\n\n    @property\n    @abstractmethod\n    def _ObjOnImageClass(self):\n        \"\"\"Return PolygonsOnImage, LineStringsOnImage or similar class.\"\"\"\n\n    def _Obj(self, *args, **kwargs):\n        return self._ObjClass(*args, **kwargs)\n\n    def _ObjOnImage(self, *args, **kwargs):\n        return self._ObjOnImageClass(*args, **kwargs)\n\n    def _compare_coords_of_cba(self, observed, expected, atol=1e-4, rtol=0):\n        return np.allclose(observed, expected, atol=atol, rtol=rtol)\n\n    def test_single_empty_instance(self):\n        # single instance of PolygonsOnImage with 0 polygons\n        aug = iaa.Rot90(1, keep_size=False)\n        cbaoi = self._ObjOnImage([], shape=(10, 11, 3))\n\n        cbaoi_aug = self._augfunc(aug, cbaoi)\n\n        assert isinstance(cbaoi_aug, self._ObjOnImageClass)\n        assert cbaoi_aug.empty\n        assert cbaoi_aug.shape == (11, 10, 3)\n\n    def test_list_of_single_empty_instance(self):\n        # list of PolygonsOnImage with 0 polygons\n        aug = iaa.Rot90(1, keep_size=False)\n        cbaoi = self._ObjOnImage([], shape=(10, 11, 3))\n\n        cbaois_aug = self._augfunc(aug, [cbaoi])\n\n        assert isinstance(cbaois_aug, list)\n        assert isinstance(cbaois_aug[0], self._ObjOnImageClass)\n        assert cbaois_aug[0].empty\n        assert cbaois_aug[0].shape == (11, 10, 3)\n\n    def test_two_cbaois_each_two_cbas(self):\n        # 2 PolygonsOnImage, each 2 polygons\n        aug = iaa.Rot90(1, keep_size=False)\n        cbaois = [\n            self._ObjOnImage(\n                [self._Obj([(0, 0), (5, 0), (5, 5)]),\n                 self._Obj([(1, 1), (6, 1), (6, 6)])],\n                shape=(10, 10, 3)),\n            self._ObjOnImage(\n                [self._Obj([(2, 2), (7, 2), (7, 7)]),\n                 self._Obj([(3, 3), (8, 3), (8, 8)])],\n                shape=(10, 10, 3)),\n        ]\n\n        cbaois_aug = self._augfunc(aug, cbaois)\n\n        assert isinstance(cbaois_aug, list)\n        assert isinstance(cbaois_aug[0], self._ObjOnImageClass)\n        assert isinstance(cbaois_aug[0], self._ObjOnImageClass)\n        assert len(cbaois_aug[0].items) == 2\n        assert len(cbaois_aug[1].items) == 2\n        kp_offset = 0\n        assert self._compare_coords_of_cba(\n            cbaois_aug[0].items[0].coords,\n            [(10-0+kp_offset, 0), (10-0+kp_offset, 5), (10-5+kp_offset, 5)],\n            atol=1e-4, rtol=0\n        )\n        assert self._compare_coords_of_cba(\n            cbaois_aug[0].items[1].coords,\n            [(10-1+kp_offset, 1), (10-1+kp_offset, 6), (10-6+kp_offset, 6)],\n            atol=1e-4, rtol=0\n        )\n        assert self._compare_coords_of_cba(\n            cbaois_aug[1].items[0].coords,\n            [(10-2+kp_offset, 2), (10-2+kp_offset, 7), (10-7+kp_offset, 7)],\n            atol=1e-4, rtol=0\n        )\n        assert self._compare_coords_of_cba(\n            cbaois_aug[1].items[1].coords,\n            [(10-3+kp_offset, 3), (10-3+kp_offset, 8), (10-8+kp_offset, 8)],\n            atol=1e-4, rtol=0\n        )\n        assert cbaois_aug[0].shape == (10, 10, 3)\n        assert cbaois_aug[1].shape == (10, 10, 3)\n\n    def test_randomness_between_and_within_batches(self):\n        # test whether there is randomness within each batch and between\n        # batches\n        aug = iaa.Rot90((0, 3), keep_size=False)\n        cba = self._Obj([(0, 0), (5, 0), (5, 5)])\n        cbaoi = self._ObjOnImage(\n            [cba.deepcopy() for _ in sm.xrange(1)],\n            shape=(10, 11, 3)\n        )\n        cbaois = [cbaoi.deepcopy() for _ in sm.xrange(100)]\n\n        cbaois_aug1 = self._augfunc(aug, cbaois)\n        cbaois_aug2 = self._augfunc(aug, cbaois)\n\n        # --> different between runs\n        cbas1 = [cba\n                 for cbaoi in cbaois_aug1\n                 for cba in cbaoi.items]\n        cbas2 = [cba\n                 for cbaoi in cbaois_aug2\n                 for cba in cbaoi.items]\n        assert len(cbas1) == len(cbas2)\n        same = []\n        for cba1, cba2 in zip(cbas1, cbas2):\n            points1 = np.float32(cba1.coords)\n            points2 = np.float32(cba2.coords)\n            same.append(self._compare_coords_of_cba(points1, points2,\n                                                    atol=1e-2, rtol=0))\n        assert not np.all(same)\n\n        # --> different between PolygonOnImages\n        same = []\n        points1 = np.float32([cba.coords\n                              for cba\n                              in cbaois_aug1[0].items])\n        for cba in cbaois_aug1[1:]:\n            points2 = np.float32([cba.coords\n                                  for cba\n                                  in cba.items])\n            same.append(self._compare_coords_of_cba(points1, points2,\n                                                    atol=1e-2, rtol=0))\n        assert not np.all(same)\n\n        # --> different between polygons\n        points1 = set()\n        for cba in cbaois_aug1[0].items:\n            for point in cba.coords:\n                points1.add(tuple(\n                    [int(point[0]*10), int(point[1]*10)]\n                ))\n        assert len(points1) > 1\n\n    def test_determinism(self):\n        aug = iaa.Rot90((0, 3), keep_size=False)\n        aug_det = aug.to_deterministic()\n        cba = self._Obj([(0, 0), (5, 0), (5, 5)])\n        cbaoi = self._ObjOnImage(\n            [cba.deepcopy() for _ in sm.xrange(1)],\n            shape=(10, 11, 3)\n        )\n        cbaois = [cbaoi.deepcopy() for _ in sm.xrange(100)]\n\n        cbaois_aug1 = self._augfunc(aug_det, cbaois)\n        cbaois_aug2 = self._augfunc(aug_det, cbaois)\n\n        # --> different between PolygonsOnImages\n        same = []\n        points1 = np.float32([cba.coords\n                              for cba\n                              in cbaois_aug1[0].items])\n        for cbaoi in cbaois_aug1[1:]:\n            points2 = np.float32([cba.coords\n                                  for cba\n                                  in cbaoi.items])\n            same.append(self._compare_coords_of_cba(points1, points2,\n                                                    atol=1e-2, rtol=0))\n        assert not np.all(same)\n\n        # --> similar between augmentation runs\n        cbas1 = [cba\n                 for cbaoi in cbaois_aug1\n                 for cba in cbaoi.items]\n        cbas2 = [cba\n                 for cbaoi in cbaois_aug2\n                 for cba in cbaoi.items]\n        assert len(cbas1) == len(cbas2)\n        for cba1, cba2 in zip(cbas1, cbas2):\n            points1 = np.float32(cba1.coords)\n            points2 = np.float32(cba2.coords)\n            assert self._compare_coords_of_cba(points1, points2,\n                                               atol=1e-2, rtol=0)\n\n    def test_aligned_with_images(self):\n        aug = iaa.Rot90((0, 3), keep_size=False)\n        aug_det = aug.to_deterministic()\n        image = np.zeros((10, 20), dtype=np.uint8)\n        image[5, :] = 255\n        image[2:5, 10] = 255\n        image_rots = [iaa.Rot90(k, keep_size=False).augment_image(image)\n                      for k in [0, 1, 2, 3]]\n        cba = self._Obj([(0, 0), (10, 0), (10, 20)])\n        kp_offs = 0  # offset\n        cbas_rots = [\n            [(0, 0), (10, 0), (10, 20)],\n            [(10-0+kp_offs, 0), (10-0+kp_offs, 10), (10-20+kp_offs, 10)],\n            [(20-0+kp_offs, 10), (20-10+kp_offs, 10), (20-10+kp_offs, -10)],\n            [(10-10+kp_offs, 20), (10-10+kp_offs, 10), (10-(-10)+kp_offs, 10)]\n        ]\n        cbaois = [self._ObjOnImage([cba], shape=image.shape)\n                  for _ in sm.xrange(50)]\n\n        images_aug = aug_det.augment_images([image] * 50)\n        cbaois_aug = self._augfunc(aug_det, cbaois)\n\n        seen = set()\n        for image_aug, cbaoi_aug in zip(images_aug, cbaois_aug):\n            found_image = False\n            for img_rot_idx, img_rot in enumerate(image_rots):\n                if (image_aug.shape == img_rot.shape\n                        and np.allclose(image_aug, img_rot)):\n                    found_image = True\n                    break\n\n            found_cba = False\n            for poly_rot_idx, cba_rot in enumerate(cbas_rots):\n                coords_observed = cbaoi_aug.items[0].coords\n                if self._compare_coords_of_cba(coords_observed, cba_rot):\n                    found_cba = True\n                    break\n\n            assert found_image\n            assert found_cba\n            assert img_rot_idx == poly_rot_idx\n            seen.add((img_rot_idx, poly_rot_idx))\n        assert 2 <= len(seen) <= 4  # assert not always the same rot\n\n    def test_aligned_with_images_despite_empty_instances(self):\n        # Test if augmenting lists of e.g. PolygonsOnImage is still aligned\n        # with image augmentation when one e.g. PolygonsOnImage instance is\n        # empty (e.g. contains no polygons)\n        cba = self._Obj([(0, 0), (5, 0), (5, 5), (0, 5)])\n        cbaoi_lst = [\n            self._ObjOnImage([cba.deepcopy()], shape=(10, 20)),\n            self._ObjOnImage([cba.deepcopy()], shape=(10, 20)),\n            self._ObjOnImage([cba.shift(x=1)], shape=(10, 20)),\n            self._ObjOnImage([cba.deepcopy()], shape=(10, 20)),\n            self._ObjOnImage([cba.deepcopy()], shape=(10, 20)),\n            self._ObjOnImage([], shape=(1, 8)),\n            self._ObjOnImage([cba.deepcopy()], shape=(10, 20)),\n            self._ObjOnImage([cba.deepcopy()], shape=(10, 20)),\n            self._ObjOnImage([cba.shift(x=1)], shape=(10, 20)),\n            self._ObjOnImage([cba.deepcopy()], shape=(10, 20)),\n            self._ObjOnImage([cba.deepcopy()], shape=(10, 20))\n        ]\n        image = np.zeros((10, 20), dtype=np.uint8)\n        image[0, 0] = 255\n        image[0, 5] = 255\n        image[5, 5] = 255\n        image[5, 0] = 255\n        images = np.tile(image[np.newaxis, :, :], (len(cbaoi_lst), 1, 1))\n\n        aug = iaa.Affine(translate_px={\"x\": (0, 8)}, order=0, mode=\"constant\",\n                         cval=0)\n\n        for _ in sm.xrange(10):\n            for is_list in [False, True]:\n                aug_det = aug.to_deterministic()\n                inputs = images\n                if is_list:\n                    inputs = list(inputs)\n\n                images_aug = aug_det.augment_images(inputs)\n                cbaoi_aug_lst = self._augfunc(aug_det, cbaoi_lst)\n\n                if is_list:\n                    images_aug = np.array(images_aug, dtype=np.uint8)\n                translations_imgs = np.argmax(images_aug[:, 0, :], axis=1)\n\n                translations_points = [\n                    (cbaoi.items[0].coords[0][0] if not cbaoi.empty else None)\n                    for cbaoi\n                    in cbaoi_aug_lst]\n\n                assert len([\n                    pointresult for\n                    pointresult\n                    in translations_points\n                    if pointresult is None\n                ]) == 1\n                assert translations_points[5] is None\n                translations_imgs = np.concatenate(\n                    [translations_imgs[0:5], translations_imgs[6:]])\n                translations_points = np.array(\n                    translations_points[0:5] + translations_points[6:],\n                    dtype=translations_imgs.dtype)\n                translations_points[2] -= 1\n                translations_points[8-1] -= 1\n                assert np.array_equal(translations_imgs, translations_points)\n\n\n# This is the same as _ConcavePolygonRecoverer, but we make sure that we\n# always sample random values. This is to advance the state of random_state\n# and ensure that this breaks not alignment.\nclass _DummyRecoverer(_ConcavePolygonRecoverer):\n    def recover_from(self, new_exterior, old_polygon, random_state=0):\n        # sample lots of values to ensure that the RNG is advanced\n        _ = random_state.integers(0, 2**30, 100)\n        return super(_DummyRecoverer, self).recover_from(\n            new_exterior, old_polygon, random_state=random_state)\n\n\nclass _DummyAugmenterWithRecoverer(iaa.Augmenter):\n    def __init__(self, use_recoverer=True):\n        super(_DummyAugmenterWithRecoverer, self).__init__()\n        self.random_samples_images = []\n        self.random_samples_kps = []\n\n        if use_recoverer:\n            self.recoverer = _DummyRecoverer()\n        else:\n            self.recoverer = None\n\n    def _augment_images(self, images, random_state, parents, hooks):\n        sample = random_state.integers(0, 2**30)\n        self.random_samples_images.append(sample)\n        return images\n\n    def _augment_polygons(self, polygons_on_images, random_state, parents,\n                          hooks):\n        return self._augment_polygons_as_keypoints(\n            polygons_on_images, random_state, parents, hooks,\n            recoverer=self.recoverer)\n\n    def _augment_keypoints(self, keypoints_on_images, random_state, parents,\n                           hooks):\n        sample = random_state.integers(0, 2**30)\n        self.random_samples_kps.append(sample)\n\n        assert len(keypoints_on_images) in [1, 2]\n        assert len(keypoints_on_images[0].keypoints) == 7\n\n        result = []\n\n        for _ in keypoints_on_images:\n            # every second call of _augment_polygons()...\n            if len(self.random_samples_kps) % 2 == 1:\n                # not concave\n                kpsoi = ia.KeypointsOnImage([\n                    ia.Keypoint(x=0, y=0),\n                    ia.Keypoint(x=10, y=0),\n                    ia.Keypoint(x=10, y=4),\n                    ia.Keypoint(x=-1, y=5),\n                    ia.Keypoint(x=10, y=6),\n                    ia.Keypoint(x=10, y=10),\n                    ia.Keypoint(x=0, y=10)\n                ], shape=(10, 10, 3))\n            else:\n                # concave\n                kpsoi = ia.KeypointsOnImage([\n                    ia.Keypoint(x=0, y=0),\n                    ia.Keypoint(x=10, y=0),\n                    ia.Keypoint(x=10, y=4),\n                    ia.Keypoint(x=10, y=5),\n                    ia.Keypoint(x=10, y=6),\n                    ia.Keypoint(x=10, y=10),\n                    ia.Keypoint(x=0, y=10)\n                ], shape=(10, 10, 3))\n            result.append(kpsoi)\n        return result\n\n    def get_parameters(self):\n        return []\n\n\nclass TestAugmenter_augment_polygons(_TestAugmenter_augment_cbaois,\n                                     unittest.TestCase):\n    def _augfunc(self, augmenter, *args, **kwargs):\n        return augmenter.augment_polygons(*args, **kwargs)\n\n    @property\n    def _ObjClass(self):\n        return ia.Polygon\n\n    @property\n    def _ObjOnImageClass(self):\n        return ia.PolygonsOnImage\n\n    def _coords(self, obj):\n        return obj.exterior\n    \n    def _entities(self, obj_on_image):\n        return obj_on_image.polygons\n\n    def test_polygon_recoverer(self):\n        # This is mostly a dummy polygon. The augmenter always returns the\n        # same non-concave polygon.\n        poly = ia.Polygon([(0, 0), (10, 0),\n                           (10, 4), (10, 5), (10, 6),\n                           (10, 10), (0, 10)])\n        psoi = ia.PolygonsOnImage([poly], shape=(10, 10, 3))\n        aug = _DummyAugmenterWithRecoverer()\n\n        psoi_aug = aug.augment_polygons(psoi)\n        poly_aug = psoi_aug.polygons[0]\n\n        bb = ia.BoundingBox(x1=0, y1=0, x2=10, y2=10)\n        bb_aug = ia.BoundingBox(\n            x1=np.min(poly_aug.exterior[:, 0]),\n            y1=np.min(poly_aug.exterior[:, 1]),\n            x2=np.max(poly_aug.exterior[:, 0]),\n            y2=np.max(poly_aug.exterior[:, 1])\n        )\n        assert bb.iou(bb_aug) > 0.9\n        assert psoi_aug.polygons[0].is_valid\n\n    def test_polygon_aligned_without_recoverer(self):\n        # This is mostly a dummy polygon. The augmenter always returns the\n        # same non-concave polygon.\n        poly = ia.Polygon([(0, 0), (10, 0),\n                           (10, 4), (10, 5), (10, 6),\n                           (10, 10), (0, 10)])\n        psoi = ia.PolygonsOnImage([poly], shape=(10, 10, 3))\n        image = np.zeros((10, 10, 3))\n        aug = _DummyAugmenterWithRecoverer(use_recoverer=False)\n\n        images_aug1, psois_aug1 = aug(images=[image, image],\n                                      polygons=[psoi, psoi])\n\n        images_aug2, psois_aug2 = aug(images=[image, image],\n                                      polygons=[psoi, psoi])\n\n        images_aug3, psois_aug3 = aug(images=[image, image],\n                                      polygons=[psoi, psoi])\n\n        images_aug4, psois_aug4 = aug(images=[image, image],\n                                      polygons=[psoi, psoi])\n\n        assert not psois_aug1[0].polygons[0].is_valid\n        assert not psois_aug1[1].polygons[0].is_valid\n        assert psois_aug2[0].polygons[0].is_valid\n        assert psois_aug2[1].polygons[0].is_valid\n        assert not psois_aug3[0].polygons[0].is_valid\n        assert not psois_aug3[1].polygons[0].is_valid\n        assert psois_aug4[0].polygons[0].is_valid\n        assert psois_aug4[1].polygons[0].is_valid\n\n        assert aug.random_samples_images == aug.random_samples_kps\n\n    def test_polygon_aligned_with_recoverer(self):\n        # This is mostly a dummy polygon. The augmenter always returns the\n        # same non-concave polygon.\n        poly = ia.Polygon([(0, 0), (10, 0),\n                           (10, 4), (10, 5), (10, 6),\n                           (10, 10), (0, 10)])\n        psoi = ia.PolygonsOnImage([poly], shape=(10, 10, 3))\n        image = np.zeros((10, 10, 3))\n        aug = _DummyAugmenterWithRecoverer(use_recoverer=True)\n\n        images_aug1, psois_aug1 = aug(images=[image, image],\n                                      polygons=[psoi, psoi])\n\n        images_aug2, psois_aug2 = aug(images=[image, image],\n                                      polygons=[psoi, psoi])\n\n        images_aug3, psois_aug3 = aug(images=[image, image],\n                                      polygons=[psoi, psoi])\n\n        images_aug4, psois_aug4 = aug(images=[image, image],\n                                      polygons=[psoi, psoi])\n\n        assert psois_aug1[0].polygons[0].is_valid\n        assert psois_aug1[1].polygons[0].is_valid\n        assert psois_aug2[0].polygons[0].is_valid\n        assert psois_aug2[1].polygons[0].is_valid\n        assert psois_aug3[0].polygons[0].is_valid\n        assert psois_aug3[1].polygons[0].is_valid\n        assert psois_aug4[0].polygons[0].is_valid\n        assert psois_aug4[1].polygons[0].is_valid\n\n        assert aug.random_samples_images == aug.random_samples_kps\n\n\nclass TestAugmenter_augment_line_strings(_TestAugmenter_augment_cbaois,\n                                         unittest.TestCase):\n    def _augfunc(self, augmenter, *args, **kwargs):\n        return augmenter.augment_line_strings(*args, **kwargs)\n\n    @property\n    def _ObjClass(self):\n        return ia.LineString\n\n    @property\n    def _ObjOnImageClass(self):\n        return ia.LineStringsOnImage\n\n\nclass TestAugmenter_augment_bounding_boxes(_TestAugmenter_augment_cbaois,\n                                           unittest.TestCase):\n    def _augfunc(self, augmenter, *args, **kwargs):\n        return augmenter.augment_bounding_boxes(*args, **kwargs)\n\n    @property\n    def _ObjClass(self):\n        return ia.BoundingBox\n\n    @property\n    def _ObjOnImageClass(self):\n        return ia.BoundingBoxesOnImage\n\n    def _Obj(self, *args, **kwargs):\n        assert len(args) == 1\n        coords = np.float32(args[0]).reshape((-1, 2))\n        x1 = np.min(coords[:, 0])\n        y1 = np.min(coords[:, 1])\n        x2 = np.max(coords[:, 0])\n        y2 = np.max(coords[:, 1])\n        return self._ObjClass(x1=x1, y1=y1, x2=x2, y2=y2, **kwargs)\n\n    def _compare_coords_of_cba(self, observed, expected, atol=1e-4, rtol=0):\n        observed = np.float32(observed).reshape((-1, 2))\n        expected = np.float32(expected).reshape((-1, 2))\n        assert observed.shape[0] == 2\n        assert expected.shape[1] == 2\n\n        obs_x1 = np.min(observed[:, 0])\n        obs_y1 = np.min(observed[:, 1])\n        obs_x2 = np.max(observed[:, 0])\n        obs_y2 = np.max(observed[:, 1])\n\n        exp_x1 = np.min(expected[:, 0])\n        exp_y1 = np.min(expected[:, 1])\n        exp_x2 = np.max(expected[:, 0])\n        exp_y2 = np.max(expected[:, 1])\n\n        return np.allclose(\n            [obs_x1, obs_y1, obs_x2, obs_y2],\n            [exp_x1, exp_y1, exp_x2, exp_y2],\n            atol=atol, rtol=rtol)\n\n\n# the method is mostly tested indirectly, so very few tests here\nclass TestAugmenter_augment_bounding_boxes_by_keypoints(unittest.TestCase):\n    def test_x_min_max(self):\n        # ensure that min() and max() are applied to augmented x-coordinates\n        # when they are converted back to BBs\n\n        class _ShiftingXCoordAugmenter(iaa.Augmenter):\n            def _augment_images(self, images, random_state, parents, hooks):\n                return images\n\n            def _augment_bounding_boxes(self, bounding_boxes_on_images,\n                                        random_state, parents, hooks):\n                return self._augment_bounding_boxes_as_keypoints(\n                    bounding_boxes_on_images, random_state, parents, hooks)\n\n            def _augment_keypoints(self, keypoints_on_images, random_state,\n                                   parents, hooks):\n                keypoints_on_images[0].keypoints[0].x += 10\n                keypoints_on_images[0].keypoints[1].x -= 10\n                return keypoints_on_images\n\n            def get_parameters(self):\n                return []\n\n        aug = _ShiftingXCoordAugmenter()\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)], shape=(10, 10, 3))\n        observed = aug(bounding_boxes=bbsoi)\n        assert np.allclose(\n            observed.bounding_boxes[0].coords,\n            [(2-10, 1), (0+10, 3)]\n        )\n\n    def test_y_min_max(self):\n        # ensure that min() and max() are applied to augmented y-coordinates\n        # when they are converted back to BBs\n\n        class _ShiftingYCoordAugmenter(iaa.Augmenter):\n            def _augment_images(self, images, random_state, parents, hooks):\n                return images\n\n            def _augment_bounding_boxes(self, bounding_boxes_on_images,\n                                        random_state, parents, hooks):\n                return self._augment_bounding_boxes_as_keypoints(\n                    bounding_boxes_on_images, random_state, parents, hooks)\n\n            def _augment_keypoints(self, keypoints_on_images, random_state,\n                                   parents, hooks):\n                keypoints_on_images[0].keypoints[0].y += 10\n                keypoints_on_images[0].keypoints[1].y -= 10\n                return keypoints_on_images\n\n            def get_parameters(self):\n                return []\n\n        aug = _ShiftingYCoordAugmenter()\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)], shape=(10, 10, 3))\n        observed = aug(bounding_boxes=bbsoi)\n        assert np.allclose(\n            observed.bounding_boxes[0].coords,\n            [(0, 1-10), (2, 1+10)]\n        )\n\n    def test_x1_x2_can_get_flipped(self):\n        # ensure that augmented x-coordinates where x1>x2 are flipped\n        # before creating BBs from them\n\n        class _FlippingX1X2Augmenter(iaa.Augmenter):\n            def _augment_images(self, images, random_state, parents, hooks):\n                return images\n\n            def _augment_bounding_boxes(self, bounding_boxes_on_images,\n                                        random_state, parents, hooks):\n                return self._augment_bounding_boxes_as_keypoints(\n                    bounding_boxes_on_images, random_state, parents, hooks)\n\n            def _augment_keypoints(self, keypoints_on_images, random_state,\n                                   parents, hooks):\n                keypoints_on_images[0].keypoints[0].x += 10  # top left\n                keypoints_on_images[0].keypoints[3].x += 10  # bottom left\n                return keypoints_on_images\n\n            def get_parameters(self):\n                return []\n\n        aug = _FlippingX1X2Augmenter()\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)], shape=(10, 10, 3))\n        observed = aug(bounding_boxes=bbsoi)\n        assert np.allclose(\n            observed.bounding_boxes[0].coords,\n            [(2, 1), (0+10, 3)]\n        )\n\n    def test_y1_y2_can_get_flipped(self):\n        # ensure that augmented y-coordinates where y1>y2 are flipped\n        # before creating BBs from them\n\n        class _FlippingY1Y2Augmenter(iaa.Augmenter):\n            def _augment_images(self, images, random_state, parents, hooks):\n                return images\n\n            def _augment_bounding_boxes(self, bounding_boxes_on_images,\n                                        random_state, parents, hooks):\n                return self._augment_bounding_boxes_as_keypoints(\n                    bounding_boxes_on_images, random_state, parents, hooks)\n\n            def _augment_keypoints(self, keypoints_on_images, random_state,\n                                   parents, hooks):\n                keypoints_on_images[0].keypoints[0].y += 10  # top left\n                keypoints_on_images[0].keypoints[1].y += 10  # top right\n                return keypoints_on_images\n\n            def get_parameters(self):\n                return []\n\n        aug = _FlippingY1Y2Augmenter()\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)], shape=(10, 10, 3))\n        observed = aug(bounding_boxes=bbsoi)\n        assert np.allclose(\n            observed.bounding_boxes[0].coords,\n            [(0, 3), (2, 1+10)]\n        )\n\n\nclass TestAugmenter_augment(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_image(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n\n        image_aug = aug.augment(image=image)\n\n        assert image_aug.shape == image.shape\n        assert np.array_equal(image_aug, image)\n\n    def test_images_list(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n\n        images_aug = aug.augment(images=[image])\n\n        assert images_aug[0].shape == image.shape\n        assert np.array_equal(images_aug[0], image)\n\n    def test_images_and_heatmaps(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        heatmaps = ia.data.quokka_heatmap((128, 128), extract=\"square\")\n\n        images_aug, heatmaps_aug = aug.augment(images=[image],\n                                               heatmaps=[heatmaps])\n\n        assert np.array_equal(images_aug[0], image)\n        assert np.allclose(heatmaps_aug[0].arr_0to1, heatmaps.arr_0to1)\n\n    def test_images_and_segmentation_maps(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n\n        images_aug, segmaps_aug = aug.augment(images=[image],\n                                              segmentation_maps=[segmaps])\n        assert np.array_equal(images_aug[0], image)\n        assert np.allclose(segmaps_aug[0].arr, segmaps.arr)\n\n    def test_images_and_keypoints(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n\n        images_aug, keypoints_aug = aug.augment(images=[image],\n                                                keypoints=[keypoints])\n\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(keypoints_aug[0], keypoints)\n\n    def test_images_and_polygons(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n\n        images_aug, polygons_aug = aug.augment(images=[image],\n                                               polygons=[polygons])\n\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(polygons_aug[0], polygons)\n\n    def test_images_and_line_strings(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        psoi = ia.data.quokka_polygons((128, 128), extract=\"square\")\n        lsoi = ia.LineStringsOnImage([\n            psoi.polygons[0].to_line_string(closed=False)\n        ], shape=psoi.shape)\n\n        images_aug, lsoi_aug = aug.augment(images=[image],\n                                           line_strings=[lsoi])\n\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(lsoi_aug[0], lsoi)\n\n    def test_images_and_bounding_boxes(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        bbs = ia.data.quokka_bounding_boxes((128, 128), extract=\"square\")\n\n        images_aug, bbs_aug = aug.augment(images=[image], bounding_boxes=[bbs])\n\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(bbs_aug[0], bbs)\n\n    def test_image_return_batch(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n\n        batch = aug.augment(image=image, return_batch=True)\n\n        image_aug = batch.images_aug[0]\n        assert np.array_equal(image, image_aug)\n\n    def test_images_and_heatmaps_return_batch(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        heatmaps = ia.data.quokka_heatmap((128, 128), extract=\"square\")\n\n        batch = aug.augment(images=[image], heatmaps=[heatmaps],\n                            return_batch=True)\n\n        images_aug = batch.images_aug\n        heatmaps_aug = batch.heatmaps_aug\n        assert np.array_equal(images_aug[0], image)\n        assert np.allclose(heatmaps_aug[0].arr_0to1, heatmaps.arr_0to1)\n\n    def test_images_and_segmentation_maps_return_batch(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n\n        batch = aug.augment(images=[image], segmentation_maps=[segmaps],\n                            return_batch=True)\n\n        images_aug = batch.images_aug\n        segmaps_aug = batch.segmentation_maps_aug\n        assert np.array_equal(images_aug[0], image)\n        assert np.allclose(segmaps_aug[0].arr, segmaps.arr)\n\n    def test_images_and_keypoints_return_batch(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n\n        batch = aug.augment(images=[image], keypoints=[keypoints],\n                            return_batch=True)\n\n        images_aug = batch.images_aug\n        keypoints_aug = batch.keypoints_aug\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(keypoints_aug[0], keypoints)\n\n    def test_images_and_polygons_return_batch(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n\n        batch = aug.augment(images=[image], polygons=[polygons],\n                            return_batch=True)\n\n        images_aug = batch.images_aug\n        polygons_aug = batch.polygons_aug\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(polygons_aug[0], polygons)\n\n    def test_images_and_line_strings_return_batch(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        psoi = ia.data.quokka_polygons((128, 128), extract=\"square\")\n        lsoi = ia.LineStringsOnImage([\n            psoi.polygons[0].to_line_string(closed=False)\n        ], shape=psoi.shape)\n\n        batch = aug.augment(images=[image], line_strings=[lsoi],\n                            return_batch=True)\n\n        images_aug = batch.images_aug\n        lsoi_aug = batch.line_strings_aug\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(lsoi_aug[0], lsoi)\n\n    def test_images_and_bounding_boxes_return_batch(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        bbs = ia.data.quokka_bounding_boxes((128, 128), extract=\"square\")\n\n        batch = aug.augment(images=[image], bounding_boxes=[bbs],\n                            return_batch=True)\n\n        images_aug = batch.images_aug\n        bbs_aug = batch.bounding_boxes_aug\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(bbs_aug[0], bbs)\n\n    def test_non_image_data(self):\n        aug = iaa.Identity()\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n\n        batch = aug.augment(segmentation_maps=[segmaps], keypoints=[keypoints],\n                            polygons=[polygons], return_batch=True)\n\n        segmaps_aug = batch.segmentation_maps_aug\n        keypoints_aug = batch.keypoints_aug\n        polygons_aug = batch.polygons_aug\n        assert np.allclose(segmaps_aug[0].arr, segmaps.arr)\n        assert_cbaois_equal(keypoints_aug[0], keypoints)\n        assert_cbaois_equal(polygons_aug[0], polygons)\n\n    def test_non_image_data_unexpected_args_order(self):\n        aug = iaa.Identity()\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n\n        batch = aug.augment(polygons=[polygons], segmentation_maps=[segmaps],\n                            keypoints=[keypoints], return_batch=True)\n\n        segmaps_aug = batch.segmentation_maps_aug\n        keypoints_aug = batch.keypoints_aug\n        polygons_aug = batch.polygons_aug\n        assert np.allclose(segmaps_aug[0].arr, segmaps.arr)\n        assert np.allclose(keypoints_aug[0].to_xy_array(),\n                           keypoints.to_xy_array())\n        for polygon_aug, polygon in zip(polygons_aug[0].polygons,\n                                        polygons.polygons):\n            assert polygon_aug.exterior_almost_equals(polygon)\n\n    def test_with_affine(self):\n        # make sure that augment actually does something\n        aug = iaa.Affine(translate_px={\"x\": 1}, order=0, mode=\"constant\",\n                         cval=0)\n        image = np.zeros((4, 4, 1), dtype=np.uint8) + 255\n        heatmaps = np.ones((1, 4, 4, 1), dtype=np.float32)\n        segmaps = np.ones((1, 4, 4, 1), dtype=np.int32)\n        kps = [(0, 0), (1, 2)]\n        bbs = [(0, 0, 1, 1), (1, 2, 2, 3)]\n        polygons = [(0, 0), (1, 0), (1, 1)]\n        ls = [(0, 0), (1, 0), (1, 1)]\n\n        image_aug = aug.augment(image=image)\n        _, heatmaps_aug = aug.augment(image=image, heatmaps=heatmaps)\n        _, segmaps_aug = aug.augment(image=image, segmentation_maps=segmaps)\n        _, kps_aug = aug.augment(image=image, keypoints=kps)\n        _, bbs_aug = aug.augment(image=image, bounding_boxes=bbs)\n        _, polygons_aug = aug.augment(image=image, polygons=polygons)\n        _, ls_aug = aug.augment(image=image, line_strings=ls)\n\n        # all augmentables must have been moved to the right by 1px\n        assert np.all(image_aug[:, 0] == 0)\n        assert np.all(image_aug[:, 1:] == 255)\n        assert np.allclose(heatmaps_aug[0][:, 0], 0.0)\n        assert np.allclose(heatmaps_aug[0][:, 1:], 1.0)\n        assert np.all(segmaps_aug[0][:, 0] == 0)\n        assert np.all(segmaps_aug[0][:, 1:] == 1)\n        assert np.allclose(kps_aug, [(1, 0), (2, 2)])\n        assert np.allclose(bbs_aug, [(1, 0, 2, 1), (2, 2, 3, 3)])\n        assert np.allclose(polygons_aug, [(1, 0), (2, 0), (2, 1)])\n        assert np.allclose(ls_aug, [(1, 0), (2, 0), (2, 1)])\n\n    def test_alignment(self):\n        # make sure that changes from augment() are aligned and vary between\n        # call\n        aug = iaa.Affine(translate_px={\"x\": (0, 100)}, order=0, mode=\"constant\",\n                         cval=0)\n        image = np.zeros((1, 100, 1), dtype=np.uint8) + 255\n        heatmaps = np.ones((1, 1, 100, 1), dtype=np.float32)\n        segmaps = np.ones((1, 1, 100, 1), dtype=np.int32)\n        kps = [(0, 0)]\n        bbs = [(0, 0, 1, 1)]\n        polygons = [(0, 0), (1, 0), (1, 1)]\n        ls = [(0, 0), (1, 0), (1, 1)]\n\n        seen = []\n        for _ in sm.xrange(10):\n            batch_aug = aug.augment(image=image, heatmaps=heatmaps,\n                                    segmentation_maps=segmaps, keypoints=kps,\n                                    bounding_boxes=bbs, polygons=polygons,\n                                    line_strings=ls, return_batch=True)\n\n            shift_image = np.sum(batch_aug.images_aug[0][0, :] == 0)\n            shift_heatmaps = np.sum(\n                np.isclose(batch_aug.heatmaps_aug[0][0, :, 0], 0.0))\n            shift_segmaps = np.sum(\n                batch_aug.segmentation_maps_aug[0][0, :, 0] == 0)\n            shift_kps = batch_aug.keypoints_aug[0][0]\n            shift_bbs = batch_aug.bounding_boxes_aug[0][0]\n            shift_polygons = batch_aug.polygons_aug[0][0]\n            shift_ls = batch_aug.line_strings_aug[0][0]\n\n            assert len({shift_image, shift_heatmaps, shift_segmaps,\n                        shift_kps, shift_bbs, shift_polygons,\n                        shift_ls}) == 1\n            seen.append(shift_image)\n        assert len(set(seen)) > 7\n\n    def test_alignment_and_same_outputs_in_deterministic_mode(self):\n        # make sure that changes from augment() are aligned\n        # and do NOT vary if the augmenter was already in deterministic mode\n        aug = iaa.Affine(translate_px={\"x\": (0, 100)}, order=0, mode=\"constant\",\n                         cval=0)\n        aug = aug.to_deterministic()\n\n        image = np.zeros((1, 100, 1), dtype=np.uint8) + 255\n        heatmaps = np.ones((1, 1, 100, 1), dtype=np.float32)\n        segmaps = np.ones((1, 1, 100, 1), dtype=np.int32)\n        kps = [(0, 0)]\n        bbs = [(0, 0, 1, 1)]\n        polygons = [(0, 0), (1, 0), (1, 1)]\n        ls = [(0, 0), (1, 0), (1, 1)]\n\n        seen = []\n        for _ in sm.xrange(10):\n            batch_aug = aug.augment(image=image, heatmaps=heatmaps,\n                                    segmentation_maps=segmaps, keypoints=kps,\n                                    bounding_boxes=bbs, polygons=polygons,\n                                    line_strings=ls,\n                                    return_batch=True)\n\n            shift_image = np.sum(batch_aug.images_aug[0][0, :] == 0)\n            shift_heatmaps = np.sum(\n                np.isclose(batch_aug.heatmaps_aug[0][0, :, 0], 0.0))\n            shift_segmaps = np.sum(\n                batch_aug.segmentation_maps_aug[0][0, :, 0] == 0)\n            shift_kps = batch_aug.keypoints_aug[0][0]\n            shift_bbs = batch_aug.bounding_boxes_aug[0][0]\n            shift_polygons = batch_aug.polygons_aug[0][0]\n            shift_ls = batch_aug.line_strings_aug[0][0]\n\n            assert len({shift_image, shift_heatmaps, shift_segmaps,\n                        shift_kps, shift_bbs, shift_polygons,\n                        shift_ls}) == 1\n            seen.append(shift_image)\n        assert len(set(seen)) == 1\n\n    def test_arrays_become_lists_if_augmenter_changes_shapes(self):\n        # make sure that arrays (of images, heatmaps, segmaps) get split to\n        # lists of arrays if the augmenter changes shapes in non-uniform\n        # (between images) ways\n        # we augment 100 images here with rotation of either 0deg or 90deg\n        # and do not resize back to the original image size afterwards, so\n        # shapes change\n        aug = iaa.Rot90([0, 1], keep_size=False)\n\n        # base_arr is (100, 1, 2) array, each containing [[0, 1]]\n        base_arr = np.tile(np.arange(1*2).reshape((1, 2))[np.newaxis, :, :],\n                           (100, 1, 1))\n        images = np.copy(base_arr)[:, :, :, np.newaxis].astype(np.uint8)\n        heatmaps = (\n            np.copy(base_arr)[:, :, :, np.newaxis].astype(np.float32)\n            / np.max(base_arr)\n        )\n        segmaps = np.copy(base_arr)[:, :, :, np.newaxis].astype(np.int32)\n\n        batch_aug = aug.augment(images=images, heatmaps=heatmaps,\n                                segmentation_maps=segmaps,\n                                return_batch=True)\n        assert isinstance(batch_aug.images_aug, list)\n        assert isinstance(batch_aug.heatmaps_aug, list)\n        assert isinstance(batch_aug.segmentation_maps_aug, list)\n        shapes_images = [arr.shape for arr in batch_aug.images_aug]\n        shapes_heatmaps = [arr.shape for arr in batch_aug.heatmaps_aug]\n        shapes_segmaps = [arr.shape for arr in batch_aug.segmentation_maps_aug]\n        assert (\n            [shape[0:2] for shape in shapes_images]\n            == [shape[0:2] for shape in shapes_heatmaps]\n            == [shape[0:2] for shape in shapes_segmaps]\n        )\n        assert len(set(shapes_images)) == 2\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_two_outputs_none_of_them_images(self):\n        aug = iaa.Identity()\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n\n        keypoints_aug, polygons_aug = aug.augment(keypoints=[keypoints],\n                                                  polygons=[polygons])\n\n        assert_cbaois_equal(keypoints_aug[0], keypoints)\n        assert_cbaois_equal(polygons_aug[0], polygons)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_two_outputs_none_of_them_images_inverted(self):\n        aug = iaa.Identity()\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n\n        polygons_aug, keypoints_aug = aug.augment(polygons=[polygons],\n                                                  keypoints=[keypoints])\n\n        assert_cbaois_equal(keypoints_aug[0], keypoints)\n        assert_cbaois_equal(polygons_aug[0], polygons)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_two_outputs_inverted_order_heatmaps(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        heatmaps = ia.data.quokka_heatmap((128, 128), extract=\"square\")\n\n        heatmaps_aug, images_aug = aug.augment(heatmaps=[heatmaps],\n                                               images=[image])\n        assert np.array_equal(images_aug[0], image)\n        assert np.allclose(heatmaps_aug[0].arr_0to1, heatmaps.arr_0to1)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_two_outputs_inverted_order_segmaps(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n\n        segmaps_aug, images_aug = aug.augment(segmentation_maps=[segmaps],\n                                              images=[image])\n\n        assert np.array_equal(images_aug[0], image)\n        assert np.array_equal(segmaps_aug[0].arr, segmaps.arr)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_two_outputs_inverted_order_keypoints(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n\n        keypoints_aug, images_aug = aug.augment(keypoints=[keypoints],\n                                                images=[image])\n\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(keypoints_aug[0], keypoints)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_two_outputs_inverted_order_bbs(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        bbs = ia.data.quokka_bounding_boxes((128, 128), extract=\"square\")\n\n        bbs_aug, images_aug = aug.augment(bounding_boxes=[bbs],\n                                          images=[image])\n\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(bbs_aug[0], bbs)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_two_outputs_inverted_order_polygons(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n\n        polygons_aug, images_aug = aug.augment(polygons=[polygons],\n                                               images=[image])\n\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(polygons_aug[0], polygons)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_two_outputs_inverted_order_line_strings(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        psoi = ia.data.quokka_polygons((128, 128), extract=\"square\")\n        lsoi = ia.LineStringsOnImage([\n            psoi.polygons[0].to_line_string(closed=False)\n        ], shape=psoi.shape)\n\n        lsoi_aug, images_aug = aug.augment(line_strings=[lsoi],\n                                           images=[image])\n\n        assert np.array_equal(images_aug[0], image)\n        assert_cbaois_equal(lsoi_aug[0], lsoi)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_three_inputs_expected_order(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        heatmaps = ia.data.quokka_heatmap((128, 128), extract=\"square\")\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n\n        images_aug, heatmaps_aug, segmaps_aug = aug.augment(\n            images=[image],\n            heatmaps=[heatmaps],\n            segmentation_maps=[segmaps])\n\n        assert np.array_equal(images_aug[0], image)\n        assert np.allclose(heatmaps_aug[0].arr_0to1, heatmaps.arr_0to1)\n        assert np.array_equal(segmaps_aug[0].arr, segmaps.arr)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_three_inputs_expected_order2(self):\n        aug = iaa.Identity()\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n\n        segmaps_aug, keypoints_aug, polygons_aug = aug.augment(\n            segmentation_maps=[segmaps],\n            keypoints=[keypoints],\n            polygons=[polygons])\n\n        assert np.array_equal(segmaps_aug[0].arr, segmaps.arr)\n        assert_cbaois_equal(keypoints_aug[0], keypoints)\n        assert_cbaois_equal(polygons_aug[0], polygons)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_three_inputs_inverted_order(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        heatmaps = ia.data.quokka_heatmap((128, 128), extract=\"square\")\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n\n        segmaps_aug, heatmaps_aug, images_aug = aug.augment(\n            segmentation_maps=[segmaps],\n            heatmaps=[heatmaps],\n            images=[image])\n\n        assert np.array_equal(images_aug[0], image)\n        assert np.allclose(heatmaps_aug[0].arr_0to1, heatmaps.arr_0to1)\n        assert np.array_equal(segmaps_aug[0].arr, segmaps.arr)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_three_inputs_inverted_order2(self):\n        aug = iaa.Identity()\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n\n        polygons_aug, keypoints_aug, segmaps_aug = aug.augment(\n            polygons=[polygons],\n            keypoints=[keypoints],\n            segmentation_maps=[segmaps])\n\n        assert np.array_equal(segmaps_aug[0].arr, segmaps.arr)\n        assert_cbaois_equal(keypoints_aug[0], keypoints)\n        assert_cbaois_equal(polygons_aug[0], polygons)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_all_inputs_expected_order(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        heatmaps = ia.data.quokka_heatmap((128, 128), extract=\"square\")\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n        bbs = ia.data.quokka_bounding_boxes((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n        lsoi = ia.LineStringsOnImage([\n            polygons.polygons[0].to_line_string(closed=False)\n        ], shape=polygons.shape)\n\n        images_aug, heatmaps_aug, segmaps_aug, keypoints_aug, bbs_aug, \\\n            polygons_aug, lsoi_aug = aug.augment(\n                images=[image],\n                heatmaps=[heatmaps],\n                segmentation_maps=[segmaps],\n                keypoints=[keypoints],\n                bounding_boxes=[bbs],\n                polygons=[polygons],\n                line_strings=[lsoi])\n\n        assert np.array_equal(images_aug[0], image)\n        assert np.allclose(heatmaps_aug[0].arr_0to1, heatmaps.arr_0to1)\n        assert np.array_equal(segmaps_aug[0].arr, segmaps.arr)\n        assert_cbaois_equal(keypoints_aug[0], keypoints)\n        assert_cbaois_equal(bbs_aug[0], bbs)\n        assert_cbaois_equal(polygons_aug[0], polygons)\n        assert_cbaois_equal(lsoi_aug[0], lsoi)\n\n    @unittest.skipIf(not IS_PY36_OR_HIGHER,\n                     \"Behaviour is only supported in python 3.6+\")\n    def test_py_gte_36_all_inputs_inverted_order(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        heatmaps = ia.data.quokka_heatmap((128, 128), extract=\"square\")\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n        bbs = ia.data.quokka_bounding_boxes((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n        lsoi = ia.LineStringsOnImage([\n            polygons.polygons[0].to_line_string(closed=False)\n        ], shape=polygons.shape)\n\n        lsoi_aug, polygons_aug, bbs_aug, keypoints_aug, segmaps_aug, \\\n            heatmaps_aug, images_aug = aug.augment(\n                line_strings=[lsoi],\n                polygons=[polygons],\n                bounding_boxes=[bbs],\n                keypoints=[keypoints],\n                segmentation_maps=[segmaps],\n                heatmaps=[heatmaps],\n                images=[image])\n\n        assert np.array_equal(images_aug[0], image)\n        assert np.allclose(heatmaps_aug[0].arr_0to1, heatmaps.arr_0to1)\n        assert np.array_equal(segmaps_aug[0].arr, segmaps.arr)\n        assert_cbaois_equal(keypoints_aug[0], keypoints)\n        assert_cbaois_equal(bbs_aug[0], bbs)\n        assert_cbaois_equal(polygons_aug[0], polygons)\n        assert_cbaois_equal(lsoi_aug[0], lsoi)\n\n    @unittest.skipIf(IS_PY36_OR_HIGHER,\n                     \"Test checks behaviour for python <=3.5\")\n    def test_py_lte_35_calls_without_images_fail(self):\n        aug = iaa.Identity()\n        keypoints = ia.data.quokka_keypoints((128, 128), extract=\"square\")\n        polygons = ia.data.quokka_polygons((128, 128), extract=\"square\")\n\n        got_exception = False\n        try:\n            _ = aug.augment(keypoints=[keypoints], polygons=[polygons])\n        except Exception as exc:\n            msg = \"Requested two outputs from augment() that were not 'images'\"\n            assert msg in str(exc)\n            got_exception = True\n        assert got_exception\n\n    @unittest.skipIf(IS_PY36_OR_HIGHER,\n                     \"Test checks behaviour for python <=3.5\")\n    def test_py_lte_35_calls_with_more_than_three_args_fail(self):\n        aug = iaa.Identity()\n        image = ia.data.quokka((128, 128), extract=\"square\")\n        heatmaps = ia.data.quokka_heatmap((128, 128), extract=\"square\")\n        segmaps = ia.data.quokka_segmentation_map((128, 128), extract=\"square\")\n\n        got_exception = False\n        try:\n            _ = aug.augment(images=[image], heatmaps=[heatmaps],\n                            segmentation_maps=[segmaps])\n        except Exception as exc:\n            assert \"Requested more than two outputs\" in str(exc)\n            got_exception = True\n        assert got_exception\n\n\nclass TestAugmenter___call__(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_with_two_augmentables(self):\n        image = ia.data.quokka(size=(128, 128), extract=\"square\")\n        heatmaps = ia.data.quokka_heatmap(size=(128, 128), extract=\"square\")\n\n        images_aug, heatmaps_aug = iaa.Identity()(images=[image],\n                                                  heatmaps=[heatmaps])\n\n        assert np.array_equal(images_aug[0], image)\n        assert np.allclose(heatmaps_aug[0].arr_0to1, heatmaps.arr_0to1)\n\n\nclass TestAugmenter_pool(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_pool(self):\n        augseq = iaa.Identity()\n\n        mock_Pool = mock.MagicMock()\n        mock_Pool.return_value = mock_Pool\n        mock_Pool.__enter__.return_value = None\n        mock_Pool.__exit__.return_value = None\n        with mock.patch(\"imgaug.multicore.Pool\", mock_Pool):\n            with augseq.pool(processes=2, maxtasksperchild=10, seed=17):\n                pass\n\n        assert mock_Pool.call_count == 1\n        assert mock_Pool.__enter__.call_count == 1\n        assert mock_Pool.__exit__.call_count == 1\n        assert mock_Pool.call_args[0][0] == augseq\n        assert mock_Pool.call_args[1][\"processes\"] == 2\n        assert mock_Pool.call_args[1][\"maxtasksperchild\"] == 10\n        assert mock_Pool.call_args[1][\"seed\"] == 17\n\n\nclass TestAugmenter_find_augmenters_by_name(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def seq(self):\n        noop1 = iaa.Identity(name=\"Identity\")\n        fliplr = iaa.Fliplr(name=\"Fliplr\")\n        flipud = iaa.Flipud(name=\"Flipud\")\n        noop2 = iaa.Identity(name=\"Identity2\")\n        seq2 = iaa.Sequential([flipud, noop2], name=\"Seq2\")\n        seq1 = iaa.Sequential([noop1, fliplr, seq2], name=\"Seq\")\n        return seq1, seq2\n\n    def test_find_top_element(self):\n        seq1, seq2 = self.seq\n\n        augs = seq1.find_augmenters_by_name(\"Seq\")\n\n        assert len(augs) == 1\n        assert augs[0] == seq1\n\n    def test_find_nested_element(self):\n        seq1, seq2 = self.seq\n\n        augs = seq1.find_augmenters_by_name(\"Seq2\")\n\n        assert len(augs) == 1\n        assert augs[0] == seq2\n\n    def test_find_list_of_names(self):\n        seq1, seq2 = self.seq\n\n        augs = seq1.find_augmenters_by_names([\"Seq\", \"Seq2\"])\n\n        assert len(augs) == 2\n        assert augs[0] == seq1\n        assert augs[1] == seq2\n\n    def test_find_by_regex(self):\n        seq1, seq2 = self.seq\n\n        augs = seq1.find_augmenters_by_name(r\"Seq.*\", regex=True)\n\n        assert len(augs) == 2\n        assert augs[0] == seq1\n        assert augs[1] == seq2\n\n\nclass TestAugmenter_find_augmenters(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def seq(self):\n        noop1 = iaa.Identity(name=\"Identity\")\n        fliplr = iaa.Fliplr(name=\"Fliplr\")\n        flipud = iaa.Flipud(name=\"Flipud\")\n        noop2 = iaa.Identity(name=\"Identity2\")\n        seq2 = iaa.Sequential([flipud, noop2], name=\"Seq2\")\n        seq1 = iaa.Sequential([noop1, fliplr, seq2], name=\"Seq\")\n        return seq1, seq2\n\n    def test_find_by_list_of_names(self):\n        def _func(aug, parents):\n            return aug.name in [\"Seq\", \"Seq2\"]\n\n        seq1, seq2 = self.seq\n\n        augs = seq1.find_augmenters(_func)\n\n        assert len(augs) == 2\n        assert augs[0] == seq1\n        assert augs[1] == seq2\n\n    def test_use_parents_arg(self):\n        def _func(aug, parents):\n            return (\n                aug.name in [\"Seq\", \"Seq2\"]\n                and len(parents) > 0\n            )\n        seq1, seq2 = self.seq\n\n        augs = seq1.find_augmenters(_func)\n\n        assert len(augs) == 1\n        assert augs[0] == seq2\n\n    def test_find_by_list_of_names_flat_false(self):\n        def _func(aug, parents):\n            return aug.name in [\"Seq\", \"Seq2\"]\n\n        seq1, seq2 = self.seq\n\n        augs = seq1.find_augmenters(_func, flat=False)\n\n        assert len(augs) == 2\n        assert augs[0] == seq1\n        assert augs[1] == [seq2]\n\n\nclass TestAugmenter_remove(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def seq(self):\n        noop1 = iaa.Identity(name=\"Identity\")\n        fliplr = iaa.Fliplr(name=\"Fliplr\")\n        flipud = iaa.Flipud(name=\"Flipud\")\n        noop2 = iaa.Identity(name=\"Identity2\")\n        seq2 = iaa.Sequential([flipud, noop2], name=\"Seq2\")\n        seq1 = iaa.Sequential([noop1, fliplr, seq2], name=\"Seq\")\n        return seq1\n\n    def test_remove_by_name(self):\n        def _func(aug, parents):\n            return aug.name == \"Seq2\"\n\n        augs = self.seq\n\n        augs = augs.remove_augmenters(_func)\n\n        seqs = augs.find_augmenters_by_name(r\"Seq.*\", regex=True)\n        assert len(seqs) == 1\n        assert seqs[0].name == \"Seq\"\n\n    def test_remove_by_name_and_parents_arg(self):\n        def _func(aug, parents):\n            return aug.name == \"Seq2\" and len(parents) == 0\n\n        augs = self.seq\n\n        augs = augs.remove_augmenters(_func)\n\n        seqs = augs.find_augmenters_by_name(r\"Seq.*\", regex=True)\n        assert len(seqs) == 2\n        assert seqs[0].name == \"Seq\"\n        assert seqs[1].name == \"Seq2\"\n\n    def test_remove_all_without_inplace_removal(self):\n        def _func(aug, parents):\n            return True\n\n        augs = self.seq\n\n        augs = augs.remove_augmenters(_func)\n\n        assert augs is not None\n        assert isinstance(augs, iaa.Identity)\n\n    def test_remove_all_with_inplace_removal(self):\n        def _func(aug, parents):\n            return aug.name == \"Seq\"\n\n        augs = self.seq\n        got_exception = False\n        try:\n            _ = augs.remove_augmenters(_func, copy=False)\n        except Exception as exc:\n            got_exception = True\n            expected = (\n                \"Inplace removal of topmost augmenter requested, \"\n                \"which is currently not possible\")\n            assert expected in str(exc)\n        assert got_exception\n\n    def test_remove_all_without_inplace_removal_and_no_identity(self):\n        def _func(aug, parents):\n            return True\n\n        augs = self.seq\n\n        augs = augs.remove_augmenters(_func, identity_if_topmost=False)\n\n        assert augs is None\n\n    def test_remove_all_without_inplace_removal_and_no_noop(self):\n        def _func(aug, parents):\n            return True\n\n        augs = self.seq\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            augs = augs.remove_augmenters(_func, noop_if_topmost=False)\n        assert len(caught_warnings) == 1\n        assert \"deprecated\" in str(caught_warnings[-1].message)\n\n        assert augs is None\n\n\nclass TestAugmenter_copy_random_state(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        return ia.data.quokka_square(size=(128, 128))\n\n    @property\n    def images(self):\n        return np.array([self.image] * 64, dtype=np.uint8)\n\n    @property\n    def source(self):\n        source = iaa.Sequential([\n            iaa.Fliplr(0.5, name=\"hflip\"),\n            iaa.Dropout(0.05, name=\"dropout\"),\n            iaa.Affine(translate_px=(-10, 10), name=\"translate\",\n                       seed=3),\n            iaa.GaussianBlur(1.0, name=\"blur\", seed=4)\n        ], seed=5)\n        return source\n\n    @property\n    def target(self):\n        target = iaa.Sequential([\n            iaa.Fliplr(0.5, name=\"hflip\"),\n            iaa.Dropout(0.05, name=\"dropout\"),\n            iaa.Affine(translate_px=(-10, 10), name=\"translate\")\n        ])\n        return target\n\n    def test_matching_position(self):\n        def _func(aug, parents):\n            return aug.name == \"blur\"\n\n        images = self.images\n        source = self.source\n        target = self.target\n        source.localize_random_state_()\n\n        target_cprs = target.copy_random_state(source, matching=\"position\")\n\n        source_alt = source.remove_augmenters(_func)\n        images_aug_source = source_alt.augment_images(images)\n        images_aug_target = target_cprs.augment_images(images)\n\n        assert target_cprs.random_state.equals(source_alt.random_state)\n        for i in sm.xrange(3):\n            assert target_cprs[i].random_state.equals(\n                source_alt[i].random_state)\n        assert np.array_equal(images_aug_source, images_aug_target)\n\n    def test_matching_position_copy_determinism(self):\n        def _func(aug, parents):\n            return aug.name == \"blur\"\n\n        images = self.images\n        source = self.source\n        target = self.target\n        source.localize_random_state_()\n        source[0].deterministic = True\n\n        target_cprs = target.copy_random_state(\n            source, matching=\"position\", copy_determinism=True)\n\n        source_alt = source.remove_augmenters(_func)\n        images_aug_source = source_alt.augment_images(images)\n        images_aug_target = target_cprs.augment_images(images)\n\n        assert target_cprs[0].deterministic is True\n        assert np.array_equal(images_aug_source, images_aug_target)\n\n    def test_matching_name(self):\n        def _func(aug, parents):\n            return aug.name == \"blur\"\n\n        images = self.images\n        source = self.source\n        target = self.target\n        source.localize_random_state_()\n\n        target_cprs = target.copy_random_state(source, matching=\"name\")\n\n        source_alt = source.remove_augmenters(_func)\n        images_aug_source = source_alt.augment_images(images)\n        images_aug_target = target_cprs.augment_images(images)\n\n        assert np.array_equal(images_aug_source, images_aug_target)\n\n    def test_matching_name_copy_determinism(self):\n        def _func(aug, parents):\n            return aug.name == \"blur\"\n\n        images = self.images\n        source = self.source\n        target = self.target\n        source.localize_random_state_()\n\n        source_alt = source.remove_augmenters(_func)\n        source_det = source_alt.to_deterministic()\n\n        target_cprs_det = target.copy_random_state(\n            source_det, matching=\"name\", copy_determinism=True)\n\n        images_aug_source1 = source_det.augment_images(images)\n        images_aug_target1 = target_cprs_det.augment_images(images)\n        images_aug_source2 = source_det.augment_images(images)\n        images_aug_target2 = target_cprs_det.augment_images(images)\n        assert np.array_equal(images_aug_source1, images_aug_source2)\n        assert np.array_equal(images_aug_target1, images_aug_target2)\n        assert np.array_equal(images_aug_source1, images_aug_target1)\n        assert np.array_equal(images_aug_source2, images_aug_target2)\n\n    def test_copy_fails_when_source_rngs_are_not_localized__name(self):\n        source = iaa.Fliplr(0.5, name=\"hflip\")\n        target = iaa.Fliplr(0.5, name=\"hflip\")\n        got_exception = False\n        try:\n            _ = target.copy_random_state(source, matching=\"name\")\n        except Exception as exc:\n            got_exception = True\n            assert \"localize_random_state\" in str(exc)\n        assert got_exception\n\n    def test_copy_fails_when_source_rngs_are_not_localized__position(self):\n        source = iaa.Fliplr(0.5, name=\"hflip\")\n        target = iaa.Fliplr(0.5, name=\"hflip\")\n        got_exception = False\n        try:\n            _ = target.copy_random_state(source, matching=\"position\")\n        except Exception as exc:\n            got_exception = True\n            assert \"localize_random_state\" in str(exc)\n        assert got_exception\n\n    def test_copy_fails_when_names_not_match_and_matching_not_tolerant(self):\n        source = iaa.Fliplr(0.5, name=\"hflip-other-name\")\n        target = iaa.Fliplr(0.5, name=\"hflip\")\n        source.localize_random_state_()\n        got_exception = False\n        try:\n            _ = target.copy_random_state(\n                source, matching=\"name\", matching_tolerant=False)\n        except Exception as exc:\n            got_exception = True\n            assert \"not found among source augmenters\" in str(exc)\n        assert got_exception\n\n    def test_copy_fails_for_not_tolerant_position_matching(self):\n        source = iaa.Sequential([iaa.Fliplr(0.5, name=\"hflip\"),\n                                 iaa.Fliplr(0.5, name=\"hflip2\")])\n        target = iaa.Sequential([iaa.Fliplr(0.5, name=\"hflip\")])\n        source.localize_random_state_()\n        got_exception = False\n        try:\n            _ = target.copy_random_state(\n                source, matching=\"position\", matching_tolerant=False)\n        except Exception as exc:\n            got_exception = True\n            assert \"different lengths\" in str(exc)\n        assert got_exception\n\n    def test_copy_fails_for_unknown_matching_method(self):\n        source = iaa.Sequential([iaa.Fliplr(0.5, name=\"hflip\"),\n                                 iaa.Fliplr(0.5, name=\"hflip2\")])\n        target = iaa.Sequential([iaa.Fliplr(0.5, name=\"hflip\")])\n        source.localize_random_state_()\n        got_exception = False\n        try:\n            _ = target.copy_random_state(source, matching=\"test\")\n        except Exception as exc:\n            got_exception = True\n            assert \"Unknown matching method\" in str(exc)\n        assert got_exception\n\n    def test_warn_if_multiple_augmenters_with_same_name(self):\n        source = iaa.Sequential([iaa.Fliplr(0.5, name=\"hflip\"),\n                                 iaa.Fliplr(0.5, name=\"hflip\")])\n        target = iaa.Sequential([iaa.Fliplr(0.5, name=\"hflip\")])\n        source.localize_random_state_()\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n\n            _ = target.copy_random_state(source, matching=\"name\")\n\n        assert len(caught_warnings) == 1\n        assert (\n            \"contains multiple augmenters with the same name\"\n            in str(caught_warnings[-1].message)\n        )\n\n\n# TODO these tests change the input type from list to array. Might be\n#      reasonable to change and test that scenario separetely\nclass TestAugmenterHooks(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        image = np.array([[0, 0, 1],\n                          [0, 0, 1],\n                          [0, 1, 1]], dtype=np.uint8)\n        return np.atleast_3d(image)\n\n    @property\n    def image_lr(self):\n        image_lr = np.array([[1, 0, 0],\n                             [1, 0, 0],\n                             [1, 1, 0]], dtype=np.uint8)\n        return np.atleast_3d(image_lr)\n\n    @property\n    def image_lrud(self):\n        image_lrud = np.array([[1, 1, 0],\n                               [1, 0, 0],\n                               [1, 0, 0]], dtype=np.uint8)\n        return np.atleast_3d(image_lrud)\n\n    def test_preprocessor(self):\n        def preprocessor(images, augmenter, parents):\n            img = np.copy(images)\n            img[0][1, 1, 0] += 1\n            return img\n\n        hooks = ia.HooksImages(preprocessor=preprocessor)\n        seq = iaa.Sequential([iaa.Fliplr(1.0), iaa.Flipud(1.0)])\n\n        images_aug = seq.augment_images([self.image], hooks=hooks)\n\n        expected = np.copy(self.image_lrud)\n        expected[1, 1, 0] = 3\n        assert np.array_equal(images_aug[0], expected)\n\n    def test_postprocessor(self):\n        def postprocessor(images, augmenter, parents):\n            img = np.copy(images)\n            img[0][1, 1, 0] += 1\n            return img\n\n        hooks = ia.HooksImages(postprocessor=postprocessor)\n        seq = iaa.Sequential([iaa.Fliplr(1.0), iaa.Flipud(1.0)])\n\n        images_aug = seq.augment_images([self.image], hooks=hooks)\n\n        expected = np.copy(self.image_lrud)\n        expected[1, 1, 0] = 3\n        assert np.array_equal(images_aug[0], expected)\n\n    def test_propagator(self):\n        def propagator(images, augmenter, parents, default):\n            if \"Seq\" in augmenter.name:\n                return False\n            else:\n                return default\n\n        hooks = ia.HooksImages(propagator=propagator)\n        seq = iaa.Sequential([iaa.Fliplr(1.0), iaa.Flipud(1.0)])\n\n        images_aug = seq.augment_images([self.image], hooks=hooks)\n\n        assert np.array_equal(images_aug[0], self.image)\n\n    def test_activator(self):\n        def activator(images, augmenter, parents, default):\n            if \"Flipud\" in augmenter.name:\n                return False\n            else:\n                return default\n\n        hooks = ia.HooksImages(activator=activator)\n        seq = iaa.Sequential([iaa.Fliplr(1.0), iaa.Flipud(1.0)])\n\n        images_aug = seq.augment_images([self.image], hooks=hooks)\n\n        assert np.array_equal(images_aug[0], self.image_lr)\n\n    def test_activator_keypoints(self):\n        def activator(keypoints_on_images, augmenter, parents, default):\n            return False\n\n        hooks = ia.HooksKeypoints(activator=activator)\n        kps = [ia.Keypoint(x=1, y=0), ia.Keypoint(x=2, y=0),\n               ia.Keypoint(x=2, y=1)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(5, 10, 3))\n        aug = iaa.Affine(translate_px=1)\n\n        keypoints_aug = aug.augment_keypoints(kpsoi, hooks=hooks)\n\n        assert keypoints_equal([keypoints_aug], [kpsoi])\n\n\nclass TestAugmenterWithLoadedImages(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_with_cv2(self):\n        image = np.arange(10*20).astype(np.uint8).reshape((10, 20, 1))\n        image = np.tile(image, (1, 1, 3))\n        image[:, :, 0] += 0\n        image[:, :, 1] += 1\n        image[:, :, 2] += 2\n        images = image[np.newaxis, :, :, :]\n        image_cp = np.copy(image)\n        images_cp = np.copy(images)\n\n        aug_arrs = _InplaceDummyAugmenterImgsArray(1)\n        aug_lists = _InplaceDummyAugmenterImgsList(1)\n\n        with TemporaryDirectory() as dirpath:\n            imgpath = os.path.join(dirpath, \"temp_cv2.png\")\n            imageio.imwrite(imgpath, image)\n            image_reloaded = cv2.imread(imgpath)[:, :, ::-1]\n            images_reloaded = image_reloaded[np.newaxis, :, :, :]\n\n            image_aug = aug_lists(image=image_reloaded)\n            assert image_aug is not image_reloaded\n            assert np.array_equal(image_reloaded, image_cp)\n            assert np.array_equal(image_aug, image_cp + 1)\n\n            image_aug = aug_lists.augment_image(image=image_reloaded)\n            assert image_aug is not image_reloaded\n            assert np.array_equal(image_reloaded, image_cp)\n            assert np.array_equal(image_aug, image_cp + 1)\n\n            images_aug = aug_arrs(images=images_reloaded)\n            assert images_aug is not images_reloaded\n            assert np.array_equal(images_reloaded, images_cp)\n            assert np.array_equal(images_aug, images_cp + 1)\n\n            images_aug = aug_arrs.augment_images(images=images_reloaded)\n            assert images_aug is not images_reloaded\n            assert np.array_equal(images_reloaded, images_cp)\n            assert np.array_equal(images_aug, images_cp + 1)\n\n    def test_with_imageio(self):\n        image = np.arange(10*20).astype(np.uint8).reshape((10, 20, 1))\n        image = np.tile(image, (1, 1, 3))\n        image[:, :, 0] += 0\n        image[:, :, 1] += 1\n        image[:, :, 2] += 2\n        images = image[np.newaxis, :, :, :]\n        image_cp = np.copy(image)\n        images_cp = np.copy(images)\n\n        aug_arrs = _InplaceDummyAugmenterImgsArray(1)\n        aug_lists = _InplaceDummyAugmenterImgsList(1)\n\n        with TemporaryDirectory() as dirpath:\n            imgpath = os.path.join(dirpath, \"temp_imageio.png\")\n            imageio.imwrite(imgpath, image)\n            image_reloaded = imageio.imread(imgpath)\n            images_reloaded = image_reloaded[np.newaxis, :, :, :]\n\n            image_aug = aug_lists(image=image_reloaded)\n            assert image_aug is not image_reloaded\n            assert np.array_equal(image_reloaded, image_cp)\n            assert np.array_equal(image_aug, image_cp + 1)\n\n            image_aug = aug_lists.augment_image(image=image_reloaded)\n            assert image_aug is not image_reloaded\n            assert np.array_equal(image_reloaded, image_cp)\n            assert np.array_equal(image_aug, image_cp + 1)\n\n            images_aug = aug_arrs(images=images_reloaded)\n            assert images_aug is not images_reloaded\n            assert np.array_equal(images_reloaded, images_cp)\n            assert np.array_equal(images_aug, images_cp + 1)\n\n            images_aug = aug_arrs.augment_images(images=images_reloaded)\n            assert images_aug is not images_reloaded\n            assert np.array_equal(images_reloaded, images_cp)\n            assert np.array_equal(images_aug, images_cp + 1)\n\n    def test_with_pil(self):\n        fnames = [\"asarray\", \"array\"]\n        for fname in fnames:\n            with self.subTest(fname=fname):\n                image = np.arange(10*20).astype(np.uint8).reshape((10, 20, 1))\n                image = np.tile(image, (1, 1, 3))\n                image[:, :, 0] += 0\n                image[:, :, 1] += 1\n                image[:, :, 2] += 2\n                images = image[np.newaxis, :, :, :]\n                image_cp = np.copy(image)\n                images_cp = np.copy(images)\n\n                aug_arrs = _InplaceDummyAugmenterImgsArray(1)\n                aug_lists = _InplaceDummyAugmenterImgsList(1)\n\n                with TemporaryDirectory() as dirpath:\n                    imgpath = os.path.join(dirpath,\n                                           \"temp_pil_%s.png\" % (fname,))\n                    imageio.imwrite(imgpath, image)\n                    image_reloaded = getattr(np, fname)(PIL.Image.open(imgpath))\n                    images_reloaded = image_reloaded[np.newaxis, :, :, :]\n\n                    image_aug = aug_lists(image=image_reloaded)\n                    assert image_aug is not image_reloaded\n                    assert np.array_equal(image_reloaded, image_cp)\n                    assert np.array_equal(image_aug, image_cp + 1)\n\n                    image_aug = aug_lists.augment_image(image=image_reloaded)\n                    assert image_aug is not image_reloaded\n                    assert np.array_equal(image_reloaded, image_cp)\n                    assert np.array_equal(image_aug, image_cp + 1)\n\n                    images_aug = aug_arrs(images=images_reloaded)\n                    assert images_aug is not images_reloaded\n                    assert np.array_equal(images_reloaded, images_cp)\n                    assert np.array_equal(images_aug, images_cp + 1)\n\n                    images_aug = aug_arrs.augment_images(images=images_reloaded)\n                    assert images_aug is not images_reloaded\n                    assert np.array_equal(images_reloaded, images_cp)\n                    assert np.array_equal(images_aug, images_cp + 1)\n\n\nclass TestSequential(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        image = np.array([[0, 1, 1],\n                          [0, 0, 1],\n                          [0, 0, 1]], dtype=np.uint8) * 255\n        return np.atleast_3d(image)\n\n    @property\n    def images(self):\n        return np.array([self.image], dtype=np.uint8)\n\n    @property\n    def image_lr(self):\n        image_lr = np.array([[1, 1, 0],\n                             [1, 0, 0],\n                             [1, 0, 0]], dtype=np.uint8) * 255\n        return np.atleast_3d(image_lr)\n\n    @property\n    def images_lr(self):\n        return np.array([self.image_lr], dtype=np.uint8)\n\n    @property\n    def image_ud(self):\n        image_ud = np.array([[0, 0, 1],\n                             [0, 0, 1],\n                             [0, 1, 1]], dtype=np.uint8) * 255\n        return np.atleast_3d(image_ud)\n\n    @property\n    def images_ud(self):\n        return np.array([self.image_ud], dtype=np.uint8)\n\n    @property\n    def image_lr_ud(self):\n        image_lr_ud = np.array([[1, 0, 0],\n                                [1, 0, 0],\n                                [1, 1, 0]], dtype=np.uint8) * 255\n        return np.atleast_3d(image_lr_ud)\n\n    @property\n    def images_lr_ud(self):\n        return np.array([self.image_lr_ud])\n\n    @property\n    def keypoints(self):\n        kps = [ia.Keypoint(x=1, y=0),\n               ia.Keypoint(x=2, y=0),\n               ia.Keypoint(x=2, y=1)]\n        return ia.KeypointsOnImage(kps, shape=self.image.shape)\n\n    @property\n    def keypoints_aug(self):\n        kps = [ia.Keypoint(x=3-1, y=3-0),\n               ia.Keypoint(x=3-2, y=3-0),\n               ia.Keypoint(x=3-2, y=3-1)]\n        return ia.KeypointsOnImage(kps, shape=self.image.shape)\n\n    @property\n    def polygons(self):\n        polygon = ia.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])\n        return ia.PolygonsOnImage([polygon], shape=self.image.shape)\n\n    @property\n    def polygons_aug(self):\n        polygon = ia.Polygon([(3-0, 3-0), (3-2, 3-0), (3-2, 3-2), (3-0, 3-2)])\n        return ia.PolygonsOnImage([polygon], shape=self.image.shape)\n\n    @property\n    def lsoi(self):\n        ls = ia.LineString([(0, 0), (2, 0), (2, 2), (0, 2)])\n        return ia.LineStringsOnImage([ls], shape=self.image.shape)\n\n    @property\n    def lsoi_aug(self):\n        ls = ia.LineString([(3-0, 3-0), (3-2, 3-0), (3-2, 3-2), (3-0, 3-2)])\n        return ia.LineStringsOnImage([ls], shape=self.image.shape)\n\n    @property\n    def bbsoi(self):\n        bb = ia.BoundingBox(x1=0, y1=0, x2=2, y2=2)\n        return ia.BoundingBoxesOnImage([bb], shape=self.image.shape)\n\n    @property\n    def bbsoi_aug(self):\n        x1 = 3-0\n        x2 = 3-2\n        y1 = 3-0\n        y2 = 3-2\n        bb = ia.BoundingBox(x1=min(x1, x2), y1=min(y1, y2),\n                            x2=max(x1, x2), y2=max(y1, y2))\n        return ia.BoundingBoxesOnImage([bb], shape=self.image.shape)\n\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([[0, 0, 1.0],\n                                   [0, 0, 1.0],\n                                   [0, 1.0, 1.0]])\n        return ia.HeatmapsOnImage(heatmaps_arr, shape=self.image.shape)\n\n    @property\n    def heatmaps_aug(self):\n        heatmaps_arr_expected = np.float32([[1.0, 1.0, 0.0],\n                                            [1.0, 0, 0],\n                                            [1.0, 0, 0]])\n        return ia.HeatmapsOnImage(heatmaps_arr_expected, shape=self.image.shape)\n\n    @property\n    def segmaps(self):\n        segmaps_arr = np.int32([[0, 0, 1],\n                                [0, 0, 1],\n                                [0, 1, 1]])\n        return ia.SegmentationMapsOnImage(segmaps_arr, shape=self.image.shape)\n\n    @property\n    def segmaps_aug(self):\n        segmaps_arr_expected = np.int32([[1, 1, 0],\n                                         [1, 0, 0],\n                                         [1, 0, 0]])\n        return ia.SegmentationMapsOnImage(segmaps_arr_expected,\n                                          shape=self.image.shape)\n\n    @property\n    def seq_two_flips(self):\n        return iaa.Sequential([\n            iaa.Fliplr(1.0),\n            iaa.Flipud(1.0)\n        ])\n\n    def test_images__two_flips(self):\n        aug = self.seq_two_flips\n        observed = aug.augment_images(self.images)\n        assert np.array_equal(observed, self.images_lr_ud)\n\n    def test_images__two_flips__deterministic(self):\n        aug = self.seq_two_flips\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_lr_ud)\n\n    def test_images_as_list__two_flips(self):\n        aug = self.seq_two_flips\n\n        observed = aug.augment_images([self.image])\n\n        assert array_equal_lists(observed, [self.image_lr_ud])\n\n    def test_images_as_list__two_flips__deterministic(self):\n        aug = self.seq_two_flips\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_images([self.image])\n\n        assert array_equal_lists(observed, [self.image_lr_ud])\n\n    def test_keypoints__two_flips(self):\n        aug = self.seq_two_flips\n\n        observed = aug.augment_keypoints([self.keypoints])\n\n        assert_cbaois_equal(observed, [self.keypoints_aug])\n\n    def test_keypoints__two_flips__deterministic(self):\n        aug = self.seq_two_flips\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_keypoints([self.keypoints])\n\n        assert_cbaois_equal(observed, [self.keypoints_aug])\n\n    def test_polygons__two_flips(self):\n        aug = self.seq_two_flips\n\n        observed = aug.augment_polygons(self.polygons)\n\n        assert_cbaois_equal(observed, self.polygons_aug)\n\n    def test_polygons__two_flips__deterministic(self):\n        aug = self.seq_two_flips\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_polygons(self.polygons)\n\n        assert_cbaois_equal(observed, self.polygons_aug)\n\n    def test_line_strings__two_flips(self):\n        aug = self.seq_two_flips\n\n        observed = aug.augment_line_strings(self.lsoi)\n\n        assert_cbaois_equal(observed, self.lsoi_aug)\n\n    def test_line_strings__two_flips__deterministic(self):\n        aug = self.seq_two_flips\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_line_strings(self.lsoi)\n\n        assert_cbaois_equal(observed, self.lsoi_aug)\n\n    def test_bounding_boxes__two_flips(self):\n        aug = self.seq_two_flips\n\n        observed = aug.augment_bounding_boxes(self.bbsoi)\n\n        assert_cbaois_equal(observed, self.bbsoi_aug)\n\n    def test_bounding_boxes__two_flips__deterministic(self):\n        aug = self.seq_two_flips\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_bounding_boxes(self.bbsoi)\n\n        assert_cbaois_equal(observed, self.bbsoi_aug)\n\n    def test_heatmaps__two_flips(self):\n        aug = self.seq_two_flips\n        heatmaps = self.heatmaps\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        assert observed.shape == (3, 3, 1)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1.0 - 1e-6 < observed.max_value < 1.0 + 1e-6\n        assert np.allclose(observed.get_arr(),\n                           self.heatmaps_aug.get_arr())\n\n    def test_segmentation_maps__two_flips(self):\n        aug = self.seq_two_flips\n        segmaps = self.segmaps\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        assert observed.shape == (3, 3, 1)\n        assert np.array_equal(observed.get_arr(),\n                              self.segmaps_aug.get_arr())\n\n    def test_children_not_provided(self):\n        aug = iaa.Sequential()\n        image = np.arange(4*4).reshape((4, 4)).astype(np.uint8)\n        observed = aug.augment_image(image)\n        assert np.array_equal(observed, image)\n\n    def test_children_are_none(self):\n        aug = iaa.Sequential(children=None)\n        image = np.arange(4*4).reshape((4, 4)).astype(np.uint8)\n        observed = aug.augment_image(image)\n        assert np.array_equal(observed, image)\n\n    def test_children_is_single_augmenter_without_list(self):\n        aug = iaa.Sequential(iaa.Fliplr(1.0))\n        image = np.arange(4*4).reshape((4, 4)).astype(np.uint8)\n        observed = aug.augment_image(image)\n        assert np.array_equal(observed, np.fliplr(image))\n\n    def test_children_is_a_sequential(self):\n        aug = iaa.Sequential(iaa.Sequential(iaa.Fliplr(1.0)))\n        image = np.arange(4*4).reshape((4, 4)).astype(np.uint8)\n        observed = aug.augment_image(image)\n        assert np.array_equal(observed, np.fliplr(image))\n\n    def test_children_is_list_of_sequentials(self):\n        aug = iaa.Sequential([\n            iaa.Sequential(iaa.Flipud(1.0)),\n            iaa.Sequential(iaa.Fliplr(1.0))\n        ])\n        image = np.arange(4*4).reshape((4, 4)).astype(np.uint8)\n        observed = aug.augment_image(image)\n        assert np.array_equal(observed, np.fliplr(np.flipud(image)))\n\n    def test_randomness__two_flips(self):\n        # 50% horizontal flip, 50% vertical flip\n        aug = iaa.Sequential([\n            iaa.Fliplr(0.5),\n            iaa.Flipud(0.5)\n        ])\n\n        frac_same = self._test_randomness__two_flips__compute_fraction_same(\n            aug, 200)\n        assert np.isclose(frac_same, 0.25, rtol=0, atol=0.1)\n\n    def test_randomness__two_flips__deterministic(self):\n        # 50% horizontal flip, 50% vertical flip\n        aug = iaa.Sequential([\n            iaa.Fliplr(0.5),\n            iaa.Flipud(0.5)\n        ])\n        aug_det = aug.to_deterministic()\n\n        frac_same = self._test_randomness__two_flips__compute_fraction_same(\n            aug_det, 200)\n        assert (\n            np.isclose(frac_same, 0.0, rtol=0, atol=1e-5)\n            or np.isclose(frac_same, 1.0, rtol=0, atol=1e-5)\n        )\n\n    def _test_randomness__two_flips__compute_fraction_same(self, aug,\n                                                           nb_iterations):\n        expected = [self.images, self.images_lr, self.images_ud,\n                    self.images_lr_ud]\n\n        last_aug = None\n        nb_changed_aug = 0\n\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(self.images)\n            if i == 0:\n                last_aug = observed_aug\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                last_aug = observed_aug\n\n            assert np.any([np.array_equal(observed_aug, expected_i)\n                           for expected_i in expected])\n\n        # should be the same in roughly 25% of all cases\n        frac_changed = nb_changed_aug / nb_iterations\n        return 1 - frac_changed\n\n    def test_random_order_true_images(self):\n        aug = iaa.Sequential([\n            iaa.Affine(translate_px={\"x\": 1}, mode=\"constant\", cval=0, order=0),\n            iaa.Fliplr(1.0)\n        ], random_order=True)\n\n        frac_12 = self._test_random_order_images_frac_12(aug, 200)\n\n        assert np.isclose(frac_12, 0.5, 0.075)\n\n    def test_random_order_false_images(self):\n        aug = iaa.Sequential([\n            iaa.Affine(translate_px={\"x\": 1}, mode=\"constant\", cval=0, order=0),\n            iaa.Fliplr(1.0)\n        ], random_order=False)\n\n        frac_12 = self._test_random_order_images_frac_12(aug, 25)\n\n        assert frac_12 >= 1.0 - 1e-4\n\n    def test_random_order_true_deterministic_images(self):\n        aug = iaa.Sequential([\n            iaa.Affine(translate_px={\"x\": 1}, mode=\"constant\", cval=0, order=0),\n            iaa.Fliplr(1.0)\n        ], random_order=True)\n        aug = aug.to_deterministic()\n\n        frac_12 = self._test_random_order_images_frac_12(aug, 25)\n\n        assert (frac_12 >= 1.0-1e-4 or frac_12 <= 0.0+1e-4)\n\n    @classmethod\n    def _test_random_order_images_frac_12(cls, aug, nb_iterations):\n        image = np.uint8([[0, 1],\n                           [2, 3]])\n        image_12 = np.uint8([[0, 0],\n                              [2, 0]])\n        image_21 = np.uint8([[0, 1],\n                              [0, 3]])\n\n        seen = [False, False]\n        for _ in sm.xrange(nb_iterations):\n            observed = aug.augment_images([image])[0]\n            if np.array_equal(observed, image_12):\n                seen[0] = True\n            elif np.array_equal(observed, image_21):\n                seen[1] = True\n            else:\n                assert False\n\n        frac_12 = seen[0] / np.sum(seen)\n        return frac_12\n\n    # TODO add random_order=False\n    def test_random_order_heatmaps(self):\n        aug = iaa.Sequential([\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Fliplr(1.0)\n        ], random_order=True)\n        heatmaps_arr = np.float32([[0, 0, 1.0],\n                                   [0, 0, 1.0],\n                                   [0, 1.0, 1.0]])\n        heatmaps_arr_expected1 = np.float32([[0.0, 0.0, 0.0],\n                                             [0.0, 0.0, 0.0],\n                                             [1.0, 0.0, 0.0]])\n        heatmaps_arr_expected2 = np.float32([[0.0, 1.0, 0.0],\n                                             [0.0, 1.0, 0.0],\n                                             [0.0, 1.0, 1.0]])\n        seen = [False, False]\n        for _ in sm.xrange(100):\n            observed = aug.augment_heatmaps([\n                ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))])[0]\n            if np.allclose(observed.get_arr(), heatmaps_arr_expected1):\n                seen[0] = True\n            elif np.allclose(observed.get_arr(), heatmaps_arr_expected2):\n                seen[1] = True\n            else:\n                assert False\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    # TODO add random_order=False\n    def test_random_order_segmentation_maps(self):\n        aug = iaa.Sequential([\n            iaa.Affine(translate_px={\"x\": 1}),\n            iaa.Fliplr(1.0)\n        ], random_order=True)\n        segmaps_arr = np.int32([[0, 0, 1],\n                                [0, 0, 1],\n                                [0, 1, 1]])\n        segmaps_arr_expected1 = np.int32([[0, 0, 0],\n                                          [0, 0, 0],\n                                          [1, 0, 0]])\n        segmaps_arr_expected2 = np.int32([[0, 1, 0],\n                                          [0, 1, 0],\n                                          [0, 1, 1]])\n        seen = [False, False]\n        for _ in sm.xrange(100):\n            observed = aug.augment_segmentation_maps([\n                SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))])[0]\n            if np.array_equal(observed.get_arr(), segmaps_arr_expected1):\n                seen[0] = True\n            elif np.array_equal(observed.get_arr(), segmaps_arr_expected2):\n                seen[1] = True\n            else:\n                assert False\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    # TODO add random_order=False\n    def test_random_order_keypoints(self):\n        KP = ia.Keypoint\n        kps = [KP(0, 0), KP(2, 0), KP(2, 2)]\n        kps_12 = [KP((0+1)*2, 0), KP((2+1)*2, 0), KP((2+1)*2, 2)]\n        kps_21 = [KP((0*2)+1, 0), KP((2*2)+1, 0), KP((2*2)+1, 2)]\n\n        kpsoi = ia.KeypointsOnImage(kps, shape=(3, 3))\n        kpsoi_12 = ia.KeypointsOnImage(kps_12, shape=(3, 3))\n        kpsoi_21 = ia.KeypointsOnImage(kps_21, shape=(3, 3))\n\n        def func1(keypoints_on_images, random_state, parents, hooks):\n            for kpsoi in keypoints_on_images:\n                for kp in kpsoi.keypoints:\n                    kp.x += 1\n            return keypoints_on_images\n\n        def func2(keypoints_on_images, random_state, parents, hooks):\n            for kpsoi in keypoints_on_images:\n                for kp in kpsoi.keypoints:\n                    kp.x *= 2\n            return keypoints_on_images\n\n        aug_1 = iaa.Lambda(func_keypoints=func1)\n        aug_2 = iaa.Lambda(func_keypoints=func2)\n        seq = iaa.Sequential([aug_1, aug_2], random_order=True)\n\n        seen = [False, False]\n        for _ in sm.xrange(100):\n            observed = seq.augment_keypoints(kpsoi)\n            if np.allclose(observed.to_xy_array(), kpsoi_12.to_xy_array()):\n                seen[0] = True\n            elif np.allclose(observed.to_xy_array(), kpsoi_21.to_xy_array()):\n                seen[1] = True\n            else:\n                assert False\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    # TODO add random_order=False\n    def test_random_order_polygons(self):\n        cba = ia.Polygon([(0, 0), (1, 0), (1, 1)])\n        cba_12 = ia.Polygon([(0, 0), (1, 0), ((1+1)*2, 1)])\n        cba_21 = ia.Polygon([(0, 0), (1, 0), ((1*2)+1, 1)])\n\n        cbaoi = ia.PolygonsOnImage([cba], shape=(3, 3))\n\n        def func1(polygons_on_images, random_state, parents, hooks):\n            for cbaoi_ in polygons_on_images:\n                for cba_ in cbaoi_.items:\n                    cba_.exterior[-1, 0] += 1\n            return polygons_on_images\n\n        def func2(polygons_on_images, random_state, parents, hooks):\n            for cbaoi_ in polygons_on_images:\n                for cba_ in cbaoi_.items:\n                    cba_.exterior[-1, 0] *= 2\n            return polygons_on_images\n\n        aug_1 = iaa.Lambda(func_polygons=func1)\n        aug_2 = iaa.Lambda(func_polygons=func2)\n        seq = iaa.Sequential([aug_1, aug_2], random_order=True)\n\n        seen = [False, False]\n        for _ in sm.xrange(100):\n            observed = seq.augment_polygons(cbaoi)\n            if np.allclose(observed.items[0].coords, cba_12.coords):\n                seen[0] = True\n            elif np.allclose(observed.items[0].coords, cba_21.coords):\n                seen[1] = True\n            else:\n                assert False\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    # TODO add random_order=False\n    def test_random_order_line_strings(self):\n        cba = ia.LineString([(0, 0), (1, 0), (1, 1)])\n        cba_12 = ia.LineString([(0, 0), (1, 0), ((1+1)*2, 1)])\n        cba_21 = ia.LineString([(0, 0), (1, 0), ((1*2)+1, 1)])\n\n        cbaoi = ia.LineStringsOnImage([cba], shape=(3, 3))\n\n        def func1(line_strings_on_images, random_state, parents, hooks):\n            for cbaoi_ in line_strings_on_images:\n                for cba_ in cbaoi_.items:\n                    cba_.coords[-1, 0] += 1\n            return line_strings_on_images\n\n        def func2(line_strings_on_images, random_state, parents, hooks):\n            for cbaoi_ in line_strings_on_images:\n                for cba_ in cbaoi_.items:\n                    cba_.coords[-1, 0] *= 2\n            return line_strings_on_images\n\n        aug_1 = iaa.Lambda(func_line_strings=func1)\n        aug_2 = iaa.Lambda(func_line_strings=func2)\n        seq = iaa.Sequential([aug_1, aug_2], random_order=True)\n\n        seen = [False, False]\n        for _ in sm.xrange(100):\n            observed = seq.augment_line_strings(cbaoi)\n            if np.allclose(observed.items[0].coords, cba_12.coords):\n                seen[0] = True\n            elif np.allclose(observed.items[0].coords, cba_21.coords):\n                seen[1] = True\n            else:\n                assert False\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    # TODO add random_order=False\n    def test_random_order_bounding_boxes(self):\n        bbs = [ia.BoundingBox(x1=1, y1=2, x2=30, y2=40)]\n        bbs_12 = [ia.BoundingBox(x1=(1+1)*2, y1=2, x2=30, y2=40)]\n        bbs_21 = [ia.BoundingBox(x1=(1*2)+1, y1=2, x2=30, y2=40)]\n\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(3, 3))\n        bbsoi_12 = ia.BoundingBoxesOnImage(bbs_12, shape=(3, 3))\n        bbsoi_21 = ia.BoundingBoxesOnImage(bbs_21, shape=(3, 3))\n\n        def func1(bounding_boxes_on_images, random_state, parents, hooks):\n            for bbsoi in bounding_boxes_on_images:\n                for bb in bbsoi.bounding_boxes:\n                    bb.x1 += 1\n            return bounding_boxes_on_images\n\n        def func2(bounding_boxes_on_images, random_state, parents, hooks):\n            for bbsoi in bounding_boxes_on_images:\n                for bb in bbsoi.bounding_boxes:\n                    bb.x1 *= 2\n            return bounding_boxes_on_images\n\n        aug_1 = iaa.Lambda(func_bounding_boxes=func1)\n        aug_2 = iaa.Lambda(func_bounding_boxes=func2)\n        seq = iaa.Sequential([aug_1, aug_2], random_order=True)\n\n        seen = [False, False]\n        for _ in sm.xrange(100):\n            observed = seq.augment_bounding_boxes(bbsoi)\n            if np.allclose(observed.to_xyxy_array(),\n                           bbsoi_12.to_xyxy_array()):\n                seen[0] = True\n            elif np.allclose(observed.to_xyxy_array(),\n                             bbsoi_21.to_xyxy_array()):\n                seen[1] = True\n            else:\n                assert False\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            for random_order in [False, True]:\n                with self.subTest(shape=shape):\n                    image = np.zeros(shape, dtype=np.uint8)\n                    aug = iaa.Sequential([iaa.Identity()],\n                                         random_order=random_order)\n\n                    image_aug = aug(image=image)\n\n                    assert image_aug.dtype.name == \"uint8\"\n                    assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            for random_order in [False, True]:\n                with self.subTest(shape=shape):\n                    image = np.zeros(shape, dtype=np.uint8)\n                    aug = iaa.Sequential([iaa.Identity()],\n                                         random_order=random_order)\n\n                    image_aug = aug(image=image)\n\n                    assert np.all(image_aug == 0)\n                    assert image_aug.dtype.name == \"uint8\"\n                    assert image_aug.shape == shape\n\n    def test_add_to_empty_sequential(self):\n        aug = iaa.Sequential()\n        aug.add(iaa.Fliplr(1.0))\n        image = np.arange(4*4).reshape((4, 4)).astype(np.uint8)\n        observed = aug.augment_image(image)\n        assert np.array_equal(observed, np.fliplr(image))\n\n    def test_add_to_sequential_with_child(self):\n        aug = iaa.Sequential(iaa.Fliplr(1.0))\n        aug.add(iaa.Flipud(1.0))\n        image = np.arange(4*4).reshape((4, 4)).astype(np.uint8)\n        observed = aug.augment_image(image)\n        assert np.array_equal(observed, np.fliplr(np.flipud(image)))\n\n    def test_get_parameters(self):\n        aug1 = iaa.Sequential(iaa.Fliplr(1.0), random_order=False)\n        aug2 = iaa.Sequential(iaa.Fliplr(1.0), random_order=True)\n        assert aug1.get_parameters() == [False]\n        assert aug2.get_parameters() == [True]\n\n    def test_get_children_lists(self):\n        flip = iaa.Fliplr(1.0)\n        aug = iaa.Sequential(flip)\n        assert aug.get_children_lists() == [aug]\n\n    def test_to_deterministic(self):\n        child = iaa.Identity()\n        aug = iaa.Sequential([child])\n\n        aug_det = aug.to_deterministic()\n\n        assert aug_det.random_state is not aug.random_state\n        assert aug_det.deterministic\n        assert aug_det[0].deterministic\n\n    def test___str___and___repr__(self):\n        flip = iaa.Fliplr(1.0)\n        aug = iaa.Sequential(flip, random_order=True)\n        expected = (\n            \"Sequential(\"\n            \"name=%s, random_order=%s, children=[%s], deterministic=%s\"\n            \")\" % (aug.name, \"True\", str(flip), \"False\")\n        )\n        assert aug.__str__() == aug.__repr__() == expected\n\n    def test_other_dtypes_noop__bool(self):\n        for random_order in [False, True]:\n            aug = iaa.Sequential([\n                iaa.Identity(),\n                iaa.Identity()\n            ], random_order=random_order)\n\n            image = np.zeros((3, 3), dtype=bool)\n            image[0, 0] = True\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.name == \"bool\"\n            assert np.all(image_aug == image)\n\n    def test_other_dtypes__noop__uint_int(self):\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n\n        for dtype, random_order in itertools.product(dtypes, [False, True]):\n            with self.subTest(dtype=dtype, random_order=random_order):\n                aug = iaa.Sequential([\n                    iaa.Identity(),\n                    iaa.Identity()\n                ], random_order=random_order)\n\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.name == dtype\n                assert np.array_equal(image_aug, image)\n\n    def test_other_dtypes_noop__float(self):\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n\n        for random_order in [False, True]:\n            for dtype, value in zip(dtypes, values):\n                with self.subTest(dtype=dtype, random_order=random_order):\n                    aug = iaa.Sequential([\n                        iaa.Identity(),\n                        iaa.Identity()\n                    ], random_order=random_order)\n\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[0, 0] = value\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.name == dtype\n                    assert np.all(image_aug == image)\n\n    def test_other_dtypes_flips__bool(self):\n        for random_order in [False, True]:\n            # note that we use 100% probabilities with square images here,\n            # so random_order does not influence the output\n            aug = iaa.Sequential([\n                iaa.Fliplr(1.0),\n                iaa.Flipud(1.0)\n            ], random_order=random_order)\n\n            image = np.zeros((3, 3), dtype=bool)\n            image[0, 0] = True\n            expected = np.zeros((3, 3), dtype=bool)\n            expected[2, 2] = True\n            image_aug = aug.augment_image(image)\n            assert image_aug.dtype.name == \"bool\"\n            assert np.all(image_aug == expected)\n\n    def test_other_dtypes__flips__uint_int(self):\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n\n        for dtype, random_order in itertools.product(dtypes, [False, True]):\n            with self.subTest(dtype=dtype, random_order=random_order):\n                # note that we use 100% probabilities with square images here,\n                # so random_order does not influence the output\n                aug = iaa.Sequential([\n                    iaa.Fliplr(1.0),\n                    iaa.Flipud(1.0)\n                ], random_order=random_order)\n\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n                expected = np.zeros((3, 3), dtype=dtype)\n                expected[2, 2] = value\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.name == dtype\n                assert np.array_equal(image_aug, expected)\n\n    def test_other_dtypes_flips__float(self):\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n\n        for random_order in [False, True]:\n            for dtype, value in zip(dtypes, values):\n                with self.subTest(dtype=dtype, random_order=random_order):\n                    # note that we use 100% probabilities with square images\n                    # here, so random_order does not influence the output\n                    aug = iaa.Sequential([\n                        iaa.Fliplr(1.0),\n                        iaa.Flipud(1.0)\n                    ], random_order=random_order)\n\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[0, 0] = value\n                    expected = np.zeros((3, 3), dtype=dtype)\n                    expected[2, 2] = value\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.name == dtype\n                    assert np.all(image_aug == expected)\n\n    def test_pickleable(self):\n        aug = iaa.Sequential(\n            [iaa.Add(1, seed=1),\n             iaa.Multiply(3, seed=2)],\n            random_order=True,\n            seed=3)\n        runtest_pickleable_uint8_img(aug, iterations=5)\n\n\nclass TestSomeOf(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_children_are_empty_list(self):\n        zeros = np.zeros((3, 3, 1), dtype=np.uint8)\n        aug = iaa.SomeOf(n=0, children=[])\n        observed = aug.augment_image(zeros)\n        assert np.array_equal(observed, zeros)\n\n    def test_children_are_not_provided(self):\n        zeros = np.zeros((3, 3, 1), dtype=np.uint8)\n        aug = iaa.SomeOf(n=0)\n        observed = aug.augment_image(zeros)\n        assert np.array_equal(observed, zeros)\n\n    def test_several_children_and_various_fixed_n(self):\n        zeros = np.zeros((3, 3, 1), dtype=np.uint8)\n        children = [iaa.Add(1), iaa.Add(2), iaa.Add(3)]\n\n        ns = [0, 1, 2, 3, 4, None, (2, None), (2, 2),\n              iap.Deterministic(3)]\n        expecteds = [[0],  # 0\n                     [9*1, 9*2, 9*3],  # 1\n                     [9*1+9*2, 9*1+9*3, 9*2+9*3],  # 2\n                     [9*1+9*2+9*3],  # 3\n                     [9*1+9*2+9*3],  # 4\n                     [9*1+9*2+9*3],  # None\n                     [9*1+9*2, 9*1+9*3, 9*2+9*3, 9*1+9*2+9*3],  # (2, None)\n                     [9*1+9*2, 9*1+9*3, 9*2+9*3],  # (2, 2)\n                     [9*1+9*2+9*3]]  # Deterministic(3)\n\n        for n, expected in zip(ns, expecteds):\n            with self.subTest(n=n):\n                aug = iaa.SomeOf(n=n, children=children)\n                observed = aug.augment_image(zeros)\n                assert np.sum(observed) in expected\n\n    def test_several_children_and_n_as_tuple(self):\n        zeros = np.zeros((1, 1, 1), dtype=np.uint8)\n        augs = [iaa.Add(2**0), iaa.Add(2**1), iaa.Add(2**2)]\n        aug = iaa.SomeOf(n=(0, 3), children=augs)\n\n        nb_iterations = 1000\n        nb_observed = [0, 0, 0, 0]\n        for i in sm.xrange(nb_iterations):\n            observed = aug.augment_image(zeros)\n            s = observed[0, 0, 0]\n            if s == 0:\n                nb_observed[0] += 1\n            else:\n                if s & 2**0 > 0:\n                    nb_observed[1] += 1\n                if s & 2**1 > 0:\n                    nb_observed[2] += 1\n                if s & 2**2 > 0:\n                    nb_observed[3] += 1\n        p_observed = [n/nb_iterations for n in nb_observed]\n        assert np.isclose(p_observed[0], 0.25, rtol=0, atol=0.1)\n        assert np.isclose(p_observed[1], 0.5, rtol=0, atol=0.1)\n        assert np.isclose(p_observed[2], 0.5, rtol=0, atol=0.1)\n        assert np.isclose(p_observed[3], 0.5, rtol=0, atol=0.1)\n\n    def test_several_children_and_various_fixed_n__heatmaps(self):\n        augs = [iaa.Affine(translate_px={\"x\": 1}),\n                iaa.Affine(translate_px={\"x\": 1}),\n                iaa.Affine(translate_px={\"x\": 1})]\n\n        heatmaps_arr = np.float32([[1.0, 0.0, 0.0],\n                                   [1.0, 0.0, 0.0],\n                                   [1.0, 0.0, 0.0]])\n        heatmaps_arr0 = np.float32([[1.0, 0.0, 0.0],\n                                    [1.0, 0.0, 0.0],\n                                    [1.0, 0.0, 0.0]])\n        heatmaps_arr1 = np.float32([[0.0, 1.0, 0.0],\n                                    [0.0, 1.0, 0.0],\n                                    [0.0, 1.0, 0.0]])\n        heatmaps_arr2 = np.float32([[0.0, 0.0, 1.0],\n                                    [0.0, 0.0, 1.0],\n                                    [0.0, 0.0, 1.0]])\n        heatmaps_arr3 = np.float32([[0.0, 0.0, 0.0],\n                                    [0.0, 0.0, 0.0],\n                                    [0.0, 0.0, 0.0]])\n\n        ns = [0, 1, 2, 3, None]\n        expecteds = [[heatmaps_arr0],\n                     [heatmaps_arr1],\n                     [heatmaps_arr2],\n                     [heatmaps_arr3],\n                     [heatmaps_arr3]]\n\n        for n, expected in zip(ns, expecteds):\n            with self.subTest(n=n):\n                heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n                aug = iaa.SomeOf(n=n, children=augs)\n                observed = aug.augment_heatmaps(heatmaps)\n                assert observed.shape == (3, 3, 3)\n                assert np.isclose(observed.min_value, 0.0)\n                assert np.isclose(observed.max_value, 1.0)\n                matches = [\n                    np.allclose(observed.get_arr(), expected_i)\n                    for expected_i in expected]\n                assert np.any(matches)\n\n    def test_several_children_and_various_fixed_n__segmaps(self):\n        augs = [iaa.Affine(translate_px={\"x\": 1}),\n                iaa.Affine(translate_px={\"x\": 1}),\n                iaa.Affine(translate_px={\"x\": 1})]\n        segmaps_arr = np.int32([[1, 0, 0],\n                                [1, 0, 0],\n                                [1, 0, 0]])\n        segmaps_arr0 = np.int32([[1, 0, 0],\n                                 [1, 0, 0],\n                                 [1, 0, 0]])\n        segmaps_arr1 = np.int32([[0, 1, 0],\n                                 [0, 1, 0],\n                                 [0, 1, 0]])\n        segmaps_arr2 = np.int32([[0, 0, 1],\n                                 [0, 0, 1],\n                                 [0, 0, 1]])\n        segmaps_arr3 = np.int32([[0, 0, 0],\n                                 [0, 0, 0],\n                                 [0, 0, 0]])\n\n        ns = [0, 1, 2, 3, None]\n        expecteds = [[segmaps_arr0],\n                     [segmaps_arr1],\n                     [segmaps_arr2],\n                     [segmaps_arr3],\n                     [segmaps_arr3]]\n\n        for n, expected in zip(ns, expecteds):\n            with self.subTest(n=n):\n                segmaps = SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n                aug = iaa.SomeOf(n=n, children=augs)\n                observed = aug.augment_segmentation_maps(segmaps)\n                assert observed.shape == (3, 3, 3)\n                matches = [\n                    np.array_equal(observed.get_arr(), expected_i)\n                    for expected_i in expected]\n                assert np.any(matches)\n\n    def _test_several_children_and_various_fixed_n__cbaois(\n            self, cbaoi, augf_name):\n        augs = [iaa.Affine(translate_px={\"x\": 1}),\n                iaa.Affine(translate_px={\"y\": 1})]\n\n        cbaoi_x = cbaoi.shift(x=1)\n        cbaoi_y = cbaoi.shift(y=1)\n        cbaoi_xy = cbaoi.shift(x=1, y=1)\n\n        ns = [0, 1, 2, None]\n        expecteds = [[cbaoi],\n                     [cbaoi_x, cbaoi_y],\n                     [cbaoi_xy],\n                     [cbaoi_xy]]\n\n        for n, expected in zip(ns, expecteds):\n            with self.subTest(n=n):\n                aug = iaa.SomeOf(n=n, children=augs)\n                cbaoi_aug = getattr(aug, augf_name)(cbaoi)\n                cba = cbaoi_aug.items[0]\n                assert len(cbaoi_aug.items) == len(cbaoi.items)\n                assert cbaoi_aug.shape == (5, 6, 3)\n                if hasattr(cba, \"is_valid\"):\n                    assert cba.is_valid\n                matches = [\n                    cba.coords_almost_equals(cbaoi_i.items[0])\n                    for cbaoi_i in expected\n                ]\n                assert np.any(matches)\n\n    def test_several_children_and_various_fixed_n__keypoints(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(5, 6, 3))\n        self._test_several_children_and_various_fixed_n__cbaois(\n            kpsoi, \"augment_keypoints\")\n\n    def test_several_children_and_various_fixed_n__polygons(self):\n        ps = [ia.Polygon([(0, 0), (3, 0), (3, 3), (0, 3)])]\n        psoi = ia.PolygonsOnImage(ps, shape=(5, 6, 3))\n        self._test_several_children_and_various_fixed_n__cbaois(\n            psoi, \"augment_polygons\")\n\n    def test_several_children_and_various_fixed_n__line_strings(self):\n        ls = [ia.LineString([(0, 0), (3, 0), (3, 3), (0, 3)])]\n        lsoi = ia.LineStringsOnImage(ls, shape=(5, 6, 3))\n        self._test_several_children_and_various_fixed_n__cbaois(\n            lsoi, \"augment_line_strings\")\n\n    def test_several_children_and_various_fixed_n__bounding_boxes(self):\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=3, y2=3)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(5, 6, 3))\n        self._test_several_children_and_various_fixed_n__cbaois(\n            bbsoi, \"augment_bounding_boxes\")\n\n    @classmethod\n    def _test_empty_cbaoi(cls, cbaoi, augf_name):\n        augs = [iaa.Affine(translate_px={\"x\": 1}),\n                iaa.Affine(translate_px={\"y\": 1})]\n        aug = iaa.SomeOf(n=2, children=augs)\n\n        cbaoi_aug = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(cbaoi_aug, cbaoi)\n\n    def test_empty_keypoints_on_image_instance(self):\n        kpsoi = ia.KeypointsOnImage([], shape=(5, 6, 3))\n        self._test_empty_cbaoi(kpsoi, \"augment_keypoints\")\n\n    def test_empty_polygons_on_image_instance(self):\n        psoi = ia.PolygonsOnImage([], shape=(5, 6, 3))\n        self._test_empty_cbaoi(psoi, \"augment_polygons\")\n\n    def test_empty_line_strings_on_image_instance(self):\n        lsoi = ia.LineStringsOnImage([], shape=(5, 6, 3))\n        self._test_empty_cbaoi(lsoi, \"augment_line_strings\")\n\n    def test_empty_bounding_boxes_on_image_instance(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(5, 6, 3))\n        self._test_empty_cbaoi(bbsoi, \"augment_bounding_boxes\")\n\n    def test_random_order_false__images(self):\n        augs = [iaa.Multiply(2.0), iaa.Add(100)]\n        aug = iaa.SomeOf(n=2, children=augs, random_order=False)\n        p_observed = self._test_random_order(aug, 10)\n        assert np.isclose(p_observed[0], 1.0, rtol=0, atol=1e-8)\n        assert np.isclose(p_observed[1], 0.0, rtol=0, atol=1e-8)\n\n    def test_random_order_true__images(self):\n        augs = [iaa.Multiply(2.0), iaa.Add(100)]\n        aug = iaa.SomeOf(n=2, children=augs, random_order=True)\n        p_observed = self._test_random_order(aug, 300)\n        assert np.isclose(p_observed[0], 0.5, rtol=0, atol=0.15)\n        assert np.isclose(p_observed[1], 0.5, rtol=0, atol=0.15)\n\n    @classmethod\n    def _test_random_order(cls, aug, nb_iterations):\n        zeros = np.ones((1, 1, 1), dtype=np.uint8)\n\n        nb_observed = [0, 0]\n        for i in sm.xrange(nb_iterations):\n            observed = aug.augment_image(zeros)\n            s = np.sum(observed)\n            if s == (1*2)+100:\n                nb_observed[0] += 1\n            elif s == (1+100)*2:\n                nb_observed[1] += 1\n            else:\n                raise Exception(\"Unexpected sum: %.8f (@2)\" % (s,))\n\n        p_observed = [n/nb_iterations for n in nb_observed]\n        return p_observed\n\n    @classmethod\n    def _test_images_and_cbaoi_aligned(cls, cbaoi, augf_name):\n        img = np.zeros((3, 3), dtype=np.uint8)\n        img_x = np.copy(img)\n        img_y = np.copy(img)\n        img_xy = np.copy(img)\n        img[1, 1] = 255\n        img_x[1, 2] = 255\n        img_y[2, 1] = 255\n        img_xy[2, 2] = 255\n\n        augs = [\n            iaa.Affine(translate_px={\"x\": 1}, order=0),\n            iaa.Affine(translate_px={\"y\": 1}, order=0)\n        ]\n        cbaoi_x = cbaoi.shift(x=1)\n        cbaoi_y = cbaoi.shift(y=1)\n        cbaoi_xy = cbaoi.shift(x=1, y=1)\n\n        aug = iaa.SomeOf((0, 2), children=augs)\n        seen = [False, False, False, False]\n        for _ in sm.xrange(100):\n            aug_det = aug.to_deterministic()\n            img_aug = aug_det.augment_image(img)\n            cbaoi_aug = getattr(aug_det, augf_name)(cbaoi)\n            if np.array_equal(img_aug, img):\n                assert_cbaois_equal(cbaoi_aug, cbaoi)\n                seen[0] = True\n            elif np.array_equal(img_aug, img_x):\n                assert_cbaois_equal(cbaoi_aug, cbaoi_x)\n                seen[1] = True\n            elif np.array_equal(img_aug, img_y):\n                assert_cbaois_equal(cbaoi_aug, cbaoi_y)\n                seen[2] = True\n            elif np.array_equal(img_aug, img_xy):\n                assert_cbaois_equal(cbaoi_aug, cbaoi_xy)\n                seen[3] = True\n            else:\n                assert False\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_images_and_keypoints_aligned(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(5, 6, 3))\n        self._test_images_and_cbaoi_aligned(kpsoi, \"augment_keypoints\")\n\n    def test_images_and_polygons_aligned(self):\n        ps = [ia.Polygon([(0, 0), (3, 0), (3, 3), (0, 3)])]\n        psoi = ia.PolygonsOnImage(ps, shape=(5, 6, 3))\n        self._test_images_and_cbaoi_aligned(psoi, \"augment_polygons\")\n\n    def test_images_and_line_strings_aligned(self):\n        ls = [ia.LineString([(0, 0), (3, 0), (3, 3), (0, 3)])]\n        lsoi = ia.LineStringsOnImage(ls, shape=(5, 6, 3))\n        self._test_images_and_cbaoi_aligned(lsoi, \"augment_line_strings\")\n\n    def test_images_and_bounding_boxes_aligned(self):\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=3, y2=3)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(5, 6, 3))\n        self._test_images_and_cbaoi_aligned(bbsoi, \"augment_bounding_boxes\")\n\n    def test_invalid_argument_as_children(self):\n        got_exception = False\n        try:\n            _ = iaa.SomeOf(1, children=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_invalid_datatype_as_n(self):\n        got_exception = False\n        try:\n            _ = iaa.SomeOf(False, children=iaa.Fliplr(1.0))\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_invalid_tuple_as_n(self):\n        got_exception = False\n        try:\n            _ = iaa.SomeOf((2, \"test\"), children=iaa.Fliplr(1.0))\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_invalid_none_none_tuple_as_n(self):\n        got_exception = False\n        try:\n            _ = iaa.SomeOf((None, None), children=iaa.Fliplr(1.0))\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_with_children_that_change_shapes_keep_size_false(self):\n        # test for https://github.com/aleju/imgaug/issues/143\n        # (shapes change in child augmenters, leading to problems if input\n        # arrays are assumed to stay input arrays)\n        image = np.zeros((8, 8, 3), dtype=np.uint8)\n        aug = iaa.SomeOf(1, [\n            iaa.Crop((2, 0, 2, 0), keep_size=False),\n            iaa.Crop((1, 0, 1, 0), keep_size=False)\n        ])\n        expected_shapes = [(4, 8, 3), (6, 8, 3)]\n\n        for _ in sm.xrange(10):\n            observed = aug.augment_images(np.uint8([image] * 4))\n            assert isinstance(observed, list)\n            assert np.all([img.shape in expected_shapes for img in observed])\n\n            observed = aug.augment_images([image] * 4)\n            assert isinstance(observed, list)\n            assert np.all([img.shape in expected_shapes for img in observed])\n\n            observed = aug.augment_images(np.uint8([image]))\n            assert isinstance(observed, list)\n            assert np.all([img.shape in expected_shapes for img in observed])\n\n            observed = aug.augment_images([image])\n            assert isinstance(observed, list)\n            assert np.all([img.shape in expected_shapes for img in observed])\n\n            observed = aug.augment_image(image)\n            assert ia.is_np_array(image)\n            assert observed.shape in expected_shapes\n\n    def test_with_children_that_change_shapes_keep_size_true(self):\n        image = np.zeros((8, 8, 3), dtype=np.uint8)\n        aug = iaa.SomeOf(1, [\n            iaa.Crop((2, 0, 2, 0), keep_size=True),\n            iaa.Crop((1, 0, 1, 0), keep_size=True)\n        ])\n        expected_shapes = [(8, 8, 3)]\n\n        for _ in sm.xrange(10):\n            observed = aug.augment_images(np.uint8([image] * 4))\n            assert ia.is_np_array(observed)\n            assert np.all([img.shape in expected_shapes for img in observed])\n\n            observed = aug.augment_images([image] * 4)\n            assert isinstance(observed, list)\n            assert np.all([img.shape in expected_shapes for img in observed])\n\n            observed = aug.augment_images(np.uint8([image]))\n            assert ia.is_np_array(observed)\n            assert np.all([img.shape in expected_shapes for img in observed])\n\n            observed = aug.augment_images([image])\n            assert isinstance(observed, list)\n            assert np.all([img.shape in expected_shapes for img in observed])\n\n            observed = aug.augment_image(image)\n            assert ia.is_np_array(observed)\n            assert observed.shape in expected_shapes\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            for random_order in [False, True]:\n                with self.subTest(shape=shape):\n                    image = np.zeros(shape, dtype=np.uint8)\n                    aug = iaa.SomeOf(\n                        1, [iaa.Identity()], random_order=random_order)\n\n                    image_aug = aug(image=image)\n\n                    assert image_aug.dtype.name == \"uint8\"\n                    assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            for random_order in [False, True]:\n                with self.subTest(shape=shape):\n                    image = np.zeros(shape, dtype=np.uint8)\n                    aug = iaa.SomeOf(\n                        1, [iaa.Identity()], random_order=random_order)\n\n                    image_aug = aug(image=image)\n\n                    assert np.all(image_aug == 0)\n                    assert image_aug.dtype.name == \"uint8\"\n                    assert image_aug.shape == shape\n\n    def test_other_dtypes_via_noop__bool(self):\n        for random_order in [False, True]:\n            with self.subTest(random_order=random_order):\n                aug = iaa.SomeOf(2, [\n                    iaa.Identity(),\n                    iaa.Identity(),\n                    iaa.Identity()\n                ], random_order=random_order)\n\n                image = np.zeros((3, 3), dtype=bool)\n                image[0, 0] = True\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.name == image.dtype.name\n                assert np.all(image_aug == image)\n\n    def test_other_dtypes_via_noop__uint_int(self):\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n        random_orders = [False, True]\n\n        for dtype, random_order in itertools.product(dtypes, random_orders):\n            with self.subTest(dtype=dtype, random_order=random_order):\n                aug = iaa.SomeOf(2, [\n                    iaa.Identity(),\n                    iaa.Identity(),\n                    iaa.Identity()\n                ], random_order=random_order)\n\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n                image_aug = aug.augment_image(image)\n                assert image_aug.dtype.name == dtype\n                assert np.array_equal(image_aug, image)\n\n    def test_other_dtypes_via_noop__float(self):\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n        random_orders = [False, True]\n\n        for random_order in random_orders:\n            for dtype, value in zip(dtypes, values):\n                with self.subTest(dtype=dtype, random_order=random_order):\n                    aug = iaa.SomeOf(2, [\n                        iaa.Identity(),\n                        iaa.Identity(),\n                        iaa.Identity()\n                    ], random_order=random_order)\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[0, 0] = value\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.name == dtype\n                    assert np.all(image_aug == image)\n\n    def test_other_dtypes_via_flip__bool(self):\n        for random_order in [False, True]:\n            with self.subTest(random_order=random_order):\n                aug = iaa.SomeOf(2, [\n                    iaa.Fliplr(1.0),\n                    iaa.Flipud(1.0),\n                    iaa.Identity()\n                ], random_order=random_order)\n\n                image = np.zeros((3, 3), dtype=bool)\n                image[0, 0] = True\n                expected = [np.zeros((3, 3), dtype=bool)\n                            for _ in sm.xrange(3)]\n                expected[0][0, 2] = True\n                expected[1][2, 0] = True\n                expected[2][2, 2] = True\n\n                for _ in sm.xrange(10):\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == image.dtype.name\n                    assert any([np.all(image_aug == expected_i)\n                                for expected_i in expected])\n\n    def test_other_dtypes_via_flip__uint_int(self):\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n        random_orders = [False, True]\n\n        for dtype, random_order in itertools.product(dtypes, random_orders):\n            with self.subTest(dtype=dtype, random_order=random_order):\n                aug = iaa.SomeOf(2, [\n                    iaa.Fliplr(1.0),\n                    iaa.Flipud(1.0),\n                    iaa.Identity()\n                ], random_order=random_order)\n\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n                expected = [np.zeros((3, 3), dtype=dtype)\n                            for _ in sm.xrange(3)]\n                expected[0][0, 2] = value\n                expected[1][2, 0] = value\n                expected[2][2, 2] = value\n\n                for _ in sm.xrange(10):\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert any([np.all(image_aug == expected_i)\n                                for expected_i in expected])\n\n    def test_other_dtypes_via_flip__float(self):\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n        random_orders = [False, True]\n\n        for random_order in random_orders:\n            for dtype, value in zip(dtypes, values):\n                with self.subTest(dtype=dtype, random_order=random_order):\n                    aug = iaa.SomeOf(2, [\n                        iaa.Fliplr(1.0),\n                        iaa.Flipud(1.0),\n                        iaa.Identity()\n                    ], random_order=random_order)\n\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[0, 0] = value\n                    expected = [np.zeros((3, 3), dtype=dtype)\n                                for _ in sm.xrange(3)]\n                    expected[0][0, 2] = value\n                    expected[1][2, 0] = value\n                    expected[2][2, 2] = value\n\n                    for _ in sm.xrange(10):\n                        image_aug = aug.augment_image(image)\n\n                        assert image_aug.dtype.name == dtype\n                        assert any([np.all(image_aug == expected_i)\n                                    for expected_i in expected])\n\n    def test_pickleable(self):\n        aug = iaa.SomeOf((0, 3),\n            [iaa.Add(1, seed=1),\n             iaa.Add(2, seed=2),\n             iaa.Multiply(1.5, seed=3),\n             iaa.Multiply(2.0, seed=4)],\n            random_order=True,\n            seed=5)\n        runtest_pickleable_uint8_img(aug, iterations=5)\n\n    def test_get_children_lists(self):\n        child = iaa.Identity()\n        aug = iaa.SomeOf(1, [child])\n        children_lsts = aug.get_children_lists()\n        assert len(children_lsts) == 1\n        assert len(children_lsts[0]) == 1\n        assert children_lsts[0][0] is child\n\n    def test_to_deterministic(self):\n        child = iaa.Identity()\n        aug = iaa.SomeOf(1, [child])\n\n        aug_det = aug.to_deterministic()\n\n        assert aug_det.random_state is not aug.random_state\n        assert aug_det.deterministic\n        assert aug_det[0].deterministic\n\n\nclass TestOneOf(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_returns_someof(self):\n        child = iaa.Identity()\n        aug = iaa.OneOf(children=child)\n        assert isinstance(aug, iaa.SomeOf)\n        assert aug.n == 1\n        assert aug[0] is child\n\n    def test_single_child_that_is_augmenter(self):\n        zeros = np.zeros((3, 3, 1), dtype=np.uint8)\n        aug = iaa.OneOf(children=iaa.Add(1))\n        observed = aug.augment_image(zeros)\n        assert np.array_equal(observed, zeros + 1)\n\n    def test_single_child_that_is_sequential(self):\n        zeros = np.zeros((3, 3, 1), dtype=np.uint8)\n        aug = iaa.OneOf(children=iaa.Sequential([iaa.Add(1)]))\n        observed = aug.augment_image(zeros)\n        assert np.array_equal(observed, zeros + 1)\n\n    def test_single_child_that_is_list(self):\n        zeros = np.zeros((3, 3, 1), dtype=np.uint8)\n        aug = iaa.OneOf(children=[iaa.Add(1)])\n        observed = aug.augment_image(zeros)\n        assert np.array_equal(observed, zeros + 1)\n\n    def test_three_children(self):\n        zeros = np.zeros((1, 1, 1), dtype=np.uint8)\n        augs = [iaa.Add(1), iaa.Add(2), iaa.Add(3)]\n        aug = iaa.OneOf(augs)\n\n        results = {1: 0, 2: 0, 3: 0}\n        nb_iterations = 1000\n        for _ in sm.xrange(nb_iterations):\n            result = aug.augment_image(zeros)\n            s = int(np.sum(result))\n            results[s] += 1\n\n        expected = int(nb_iterations / len(augs))\n        tolerance = int(nb_iterations * 0.05)\n        for key, val in results.items():\n            assert np.isclose(val, expected, rtol=0, atol=tolerance)\n        assert len(list(results.keys())) == 3\n\n    def test_pickleable(self):\n        aug = iaa.OneOf(\n            [iaa.Add(1, seed=1),\n             iaa.Add(10, seed=2),\n             iaa.Multiply(2.0, seed=3)],\n            seed=4)\n        runtest_pickleable_uint8_img(aug, iterations=5)\n\n    def test_get_children_lists(self):\n        child = iaa.Identity()\n        aug = iaa.OneOf([child])\n        children_lsts = aug.get_children_lists()\n        assert len(children_lsts) == 1\n        assert len(children_lsts[0]) == 1\n        assert children_lsts[0][0] is child\n\n    def test_to_deterministic(self):\n        child = iaa.Identity()\n        aug = iaa.OneOf([child])\n\n        aug_det = aug.to_deterministic()\n\n        assert aug_det.random_state is not aug.random_state\n        assert aug_det.deterministic\n        assert aug_det[0].deterministic\n\n\nclass TestSometimes(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        image = np.array([[0, 1, 1],\n                          [0, 0, 1],\n                          [0, 0, 1]], dtype=np.uint8) * 255\n        return np.atleast_3d(image)\n\n    @property\n    def images(self):\n        return np.uint8([self.image])\n\n    @property\n    def image_lr(self):\n        image_lr = np.array([[1, 1, 0],\n                             [1, 0, 0],\n                             [1, 0, 0]], dtype=np.uint8) * 255\n        return np.atleast_3d(image_lr)\n\n    @property\n    def images_lr(self):\n        return np.uint8([self.image_lr])\n\n    @property\n    def image_ud(self):\n        image_ud = np.array([[0, 0, 1],\n                             [0, 0, 1],\n                             [0, 1, 1]], dtype=np.uint8) * 255\n        return np.atleast_3d(image_ud)\n\n    @property\n    def images_ud(self):\n        return np.uint8([self.image_ud])\n\n    @property\n    def keypoints(self):\n        keypoints = [ia.Keypoint(x=1, y=0),\n                     ia.Keypoint(x=2, y=0),\n                     ia.Keypoint(x=2, y=1)]\n        return ia.KeypointsOnImage(keypoints, shape=self.image.shape)\n\n    @property\n    def keypoints_lr(self):\n        keypoints = [ia.Keypoint(x=3-1, y=0),\n                     ia.Keypoint(x=3-2, y=0),\n                     ia.Keypoint(x=3-2, y=1)]\n        return ia.KeypointsOnImage(keypoints, shape=self.image.shape)\n\n    @property\n    def keypoints_ud(self):\n        keypoints = [ia.Keypoint(x=1, y=3-0),\n                     ia.Keypoint(x=2, y=3-0),\n                     ia.Keypoint(x=2, y=3-1)]\n        return ia.KeypointsOnImage(keypoints, shape=self.image.shape)\n\n    @property\n    def polygons(self):\n        polygons = [ia.Polygon([(0, 0), (2, 0), (2, 2)])]\n        return ia.PolygonsOnImage(polygons, shape=self.image.shape)\n\n    @property\n    def polygons_lr(self):\n        polygons = [ia.Polygon([(3-0, 0), (3-2, 0), (3-2, 2)])]\n        return ia.PolygonsOnImage(polygons, shape=self.image.shape)\n\n    @property\n    def polygons_ud(self):\n        polygons = [ia.Polygon([(0, 3-0), (2, 3-0), (2, 3-2)])]\n        return ia.PolygonsOnImage(polygons, shape=self.image.shape)\n\n    @property\n    def lsoi(self):\n        lss = [ia.LineString([(0, 0), (2, 0), (2, 2)])]\n        return ia.LineStringsOnImage(lss, shape=self.image.shape)\n\n    @property\n    def lsoi_lr(self):\n        lss = [ia.LineString([(3-0, 0), (3-2, 0), (3-2, 2)])]\n        return ia.LineStringsOnImage(lss, shape=self.image.shape)\n\n    @property\n    def lsoi_ud(self):\n        lss = [ia.LineString([(0, 3-0), (2, 3-0), (2, 3-2)])]\n        return ia.LineStringsOnImage(lss, shape=self.image.shape)\n\n    @property\n    def bbsoi(self):\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=1.5, y2=1.0)]\n        return ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)\n\n    @property\n    def bbsoi_lr(self):\n        x1 = 3-0\n        y1 = 0\n        x2 = 3-1.5\n        y2 = 1.0\n        bbs = [ia.BoundingBox(x1=min([x1, x2]), y1=min([y1, y2]),\n                              x2=max([x1, x2]), y2=max([y1, y2]))]\n        return ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)\n\n    @property\n    def bbsoi_ud(self):\n        x1 = 0\n        y1 = 3-0\n        x2 = 1.5\n        y2 = 3-1.0\n        bbs = [ia.BoundingBox(x1=min([x1, x2]), y1=min([y1, y2]),\n                              x2=max([x1, x2]), y2=max([y1, y2]))]\n        return ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)\n\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([[0.0, 0.0, 1.0],\n                                   [0.0, 0.0, 1.0],\n                                   [0.0, 1.0, 1.0]])\n        return ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def heatmaps_lr(self):\n        heatmaps_arr = np.float32([[1.0, 0.0, 0.0],\n                                   [1.0, 0.0, 0.0],\n                                   [1.0, 1.0, 0.0]])\n        return ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def heatmaps_ud(self):\n        heatmaps_arr = np.float32([[0.0, 1.0, 1.0],\n                                   [0.0, 0.0, 1.0],\n                                   [0.0, 0.0, 1.0]])\n        return ia.HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def segmaps(self):\n        segmaps_arr = np.int32([[0, 0, 1],\n                                [0, 0, 1],\n                                [0, 1, 1]])\n        return ia.SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def segmaps_lr(self):\n        segmaps_arr = np.int32([[1, 0, 0],\n                                [1, 0, 0],\n                                [1, 1, 0]])\n        return ia.SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n    @property\n    def segmaps_ud(self):\n        segmaps_arr = np.int32([[0, 1, 1],\n                                [0, 0, 1],\n                                [0, 0, 1]])\n        return ia.SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n    def test_two_branches_always_first__images(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_images(self.images)\n\n        assert np.array_equal(observed, self.images_lr)\n\n    def test_two_branches_always_first__images__deterministic(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n        observed = aug_det.augment_images(self.images)\n        assert np.array_equal(observed, self.images_lr)\n\n    def test_two_branches_always_first__images__list(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        observed = aug.augment_images([self.images[0]])\n        assert array_equal_lists(observed, [self.images_lr[0]])\n\n    def test_two_branches_always_first__images__deterministic__list(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n        observed = aug_det.augment_images([self.images[0]])\n        assert array_equal_lists(observed, [self.images_lr[0]])\n\n    def test_two_branches_always_first__keypoints(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        observed = aug.augment_keypoints(self.keypoints)\n        assert keypoints_equal(observed, self.keypoints_lr)\n\n    def test_two_branches_always_first__keypoints__deterministic(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_keypoints(self.keypoints)\n\n        assert_cbaois_equal(observed, self.keypoints_lr)\n\n    def test_two_branches_always_first__polygons(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_polygons([self.polygons])\n\n        assert_cbaois_equal(observed, [self.polygons_lr])\n\n    def test_two_branches_always_first__polygons__deterministic(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_polygons([self.polygons])\n\n        assert_cbaois_equal(observed, [self.polygons_lr])\n\n    def test_two_branches_always_first__line_strings(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_line_strings([self.lsoi])\n\n        assert_cbaois_equal(observed, [self.lsoi_lr])\n\n    def test_two_branches_always_first__line_strings__deterministic(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_line_strings([self.lsoi])\n\n        assert_cbaois_equal(observed, [self.lsoi_lr])\n\n    def test_two_branches_always_first__bounding_boxes(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_bounding_boxes([self.bbsoi])\n\n        assert_cbaois_equal(observed, [self.bbsoi_lr])\n\n    def test_two_branches_always_first__bounding_boxes__deterministic(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_bounding_boxes([self.bbsoi])\n\n        assert_cbaois_equal(observed, [self.bbsoi_lr])\n\n    def test_two_branches_always_first__heatmaps(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_heatmaps([self.heatmaps])[0]\n\n        assert observed.shape == self.heatmaps.shape\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.array_equal(observed.get_arr(), self.heatmaps_lr.get_arr())\n\n    def test_two_branches_always_first__segmaps(self):\n        aug = iaa.Sometimes(1.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_segmentation_maps(self.segmaps)\n\n        assert observed.shape == self.segmaps.shape\n        assert np.array_equal(observed.get_arr(), self.segmaps_lr.get_arr())\n\n    def test_two_branches_always_second__images(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        observed = aug.augment_images(self.images)\n        assert np.array_equal(observed, self.images_ud)\n\n    def test_two_branches_always_second__images__deterministic(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n        observed = aug_det.augment_images(self.images)\n        assert np.array_equal(observed, self.images_ud)\n\n    def test_two_branches_always_second__images__list(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        observed = aug.augment_images([self.images[0]])\n        assert array_equal_lists(observed, [self.images_ud[0]])\n\n    def test_two_branches_always_second__images__list__deterministic(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n        observed = aug_det.augment_images([self.images[0]])\n        assert array_equal_lists(observed, [self.images_ud[0]])\n\n    def test_two_branches_always_second__keypoints(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_keypoints([self.keypoints])\n\n        assert_cbaois_equal(observed[0], self.keypoints_ud)\n\n    def test_two_branches_always_second__keypoints__deterministic(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_keypoints([self.keypoints])\n\n        assert_cbaois_equal(observed[0], self.keypoints_ud)\n\n    def test_two_branches_always_second__polygons(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_polygons(self.polygons)\n\n        assert_cbaois_equal(observed, self.polygons_ud)\n\n    def test_two_branches_always_second__polygons__deterministic(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_polygons(self.polygons)\n\n        assert_cbaois_equal(observed, self.polygons_ud)\n\n    def test_two_branches_always_second__line_strings(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_line_strings(self.lsoi)\n\n        assert_cbaois_equal(observed, self.lsoi_ud)\n\n    def test_two_branches_always_second__line_strings__deterministic(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_line_strings(self.lsoi)\n\n        assert_cbaois_equal(observed, self.lsoi_ud)\n\n    def test_two_branches_always_second__bounding_boxes(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_bounding_boxes(self.bbsoi)\n\n        assert_cbaois_equal(observed, self.bbsoi_ud)\n\n    def test_two_branches_always_second__bounding_boxes__deterministic(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n\n        observed = aug_det.augment_bounding_boxes(self.bbsoi)\n\n        assert_cbaois_equal(observed, self.bbsoi_ud)\n\n    def test_two_branches_always_second__heatmaps(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_heatmaps(self.heatmaps)\n\n        assert observed.shape == self.heatmaps.shape\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.array_equal(observed.get_arr(), self.heatmaps_ud.get_arr())\n\n    def test_two_branches_always_second__segmaps(self):\n        aug = iaa.Sometimes(0.0, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n\n        observed = aug.augment_segmentation_maps(self.segmaps)\n\n        assert observed.shape == self.segmaps.shape\n        assert np.array_equal(observed.get_arr(), self.segmaps_ud.get_arr())\n\n    def test_two_branches_both_50_percent__images(self):\n        aug = iaa.Sometimes(0.5, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        last_aug = None\n        nb_changed_aug = 0\n        nb_iterations = 500\n        nb_images_if_branch = 0\n        nb_images_else_branch = 0\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(self.images)\n            if i == 0:\n                last_aug = observed_aug\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                last_aug = observed_aug\n\n            if np.array_equal(observed_aug, self.images_lr):\n                nb_images_if_branch += 1\n            elif np.array_equal(observed_aug, self.images_ud):\n                nb_images_else_branch += 1\n            else:\n                raise Exception(\n                    \"Received output doesnt match any expected output.\")\n\n        p_if_branch = nb_images_if_branch / nb_iterations\n        p_else_branch = nb_images_else_branch / nb_iterations\n        p_changed = 1 - (nb_changed_aug / nb_iterations)\n\n        assert np.isclose(p_if_branch, 0.5, rtol=0, atol=0.1)\n        assert np.isclose(p_else_branch, 0.5, rtol=0, atol=0.1)\n        # should be the same in roughly 50% of all cases\n        assert np.isclose(p_changed, 0.5, rtol=0, atol=0.1)\n\n    def test_two_branches_both_50_percent__images__deterministic(self):\n        aug = iaa.Sometimes(0.5, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        aug_det = aug.to_deterministic()\n        last_aug_det = None\n        nb_changed_aug_det = 0\n        nb_iterations = 20\n        for i in sm.xrange(nb_iterations):\n            observed_aug_det = aug_det.augment_images(self.images)\n            if i == 0:\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug_det = observed_aug_det\n\n        assert nb_changed_aug_det == 0\n\n    @classmethod\n    def _test_two_branches_both_50_percent__cbaois(\n            cls, cbaoi, cbaoi_lr, cbaoi_ud, augf_name):\n        def _same_coords(cbaoi1, cbaoi2):\n            assert len(cbaoi1.items) == len(cbaoi2.items)\n            for i1, i2 in zip(cbaoi1.items, cbaoi2.items):\n                if not np.allclose(i1.coords, i2.coords, atol=1e-4, rtol=0):\n                    return False\n            return True\n\n        aug = iaa.Sometimes(0.5, [iaa.Fliplr(1.0)], [iaa.Flipud(1.0)])\n        nb_iterations = 250\n        nb_if_branch = 0\n        nb_else_branch = 0\n        for i in sm.xrange(nb_iterations):\n            cbaoi_aug = getattr(aug, augf_name)(cbaoi)\n\n            # use allclose() instead of coords_almost_equals() for efficiency\n            if _same_coords(cbaoi_aug, cbaoi_lr):\n                nb_if_branch += 1\n            elif _same_coords(cbaoi_aug, cbaoi_ud):\n                nb_else_branch += 1\n            else:\n                raise Exception(\n                    \"Received output doesnt match any expected output.\")\n\n        p_if_branch = nb_if_branch / nb_iterations\n        p_else_branch = nb_else_branch / nb_iterations\n\n        assert np.isclose(p_if_branch, 0.5, rtol=0, atol=0.15)\n        assert np.isclose(p_else_branch, 0.5, rtol=0, atol=0.15)\n\n    def test_two_branches_both_50_percent__keypoints(self):\n        self._test_two_branches_both_50_percent__cbaois(\n            self.keypoints, self.keypoints_lr, self.keypoints_ud,\n            \"augment_keypoints\")\n\n    def test_two_branches_both_50_percent__polygons(self):\n        self._test_two_branches_both_50_percent__cbaois(\n            self.polygons, self.polygons_lr, self.polygons_ud,\n            \"augment_polygons\")\n\n    def test_two_branches_both_50_percent__line_strings(self):\n        self._test_two_branches_both_50_percent__cbaois(\n            self.lsoi, self.lsoi_lr, self.lsoi_ud,\n            \"augment_line_strings\")\n\n    def test_two_branches_both_50_percent__bounding_boxes(self):\n        self._test_two_branches_both_50_percent__cbaois(\n            self.bbsoi, self.bbsoi_lr, self.bbsoi_ud,\n            \"augment_bounding_boxes\")\n\n    def test_one_branch_50_percent__images(self):\n        aug = iaa.Sometimes(0.5, iaa.Fliplr(1.0))\n        last_aug = None\n        nb_changed_aug = 0\n        nb_iterations = 500\n        nb_images_if_branch = 0\n        nb_images_else_branch = 0\n        for i in sm.xrange(nb_iterations):\n            observed_aug = aug.augment_images(self.images)\n            if i == 0:\n                last_aug = observed_aug\n            else:\n                if not np.array_equal(observed_aug, last_aug):\n                    nb_changed_aug += 1\n                last_aug = observed_aug\n\n            if np.array_equal(observed_aug, self.images_lr):\n                nb_images_if_branch += 1\n            elif np.array_equal(observed_aug, self.images):\n                nb_images_else_branch += 1\n            else:\n                raise Exception(\n                    \"Received output doesnt match any expected output.\")\n\n        p_if_branch = nb_images_if_branch / nb_iterations\n        p_else_branch = nb_images_else_branch / nb_iterations\n        p_changed = 1 - (nb_changed_aug / nb_iterations)\n\n        assert np.isclose(p_if_branch, 0.5, rtol=0, atol=0.1)\n        assert np.isclose(p_else_branch, 0.5, rtol=0, atol=0.1)\n        # should be the same in roughly 50% of all cases\n        assert np.isclose(p_changed, 0.5, rtol=0, atol=0.1)\n\n    def test_one_branch_50_percent__images__deterministic(self):\n        aug = iaa.Sometimes(0.5, iaa.Fliplr(1.0))\n        aug_det = aug.to_deterministic()\n        last_aug_det = None\n        nb_changed_aug_det = 0\n        nb_iterations = 10\n        for i in sm.xrange(nb_iterations):\n            observed_aug_det = aug_det.augment_images(self.images)\n            if i == 0:\n                last_aug_det = observed_aug_det\n            else:\n                if not np.array_equal(observed_aug_det, last_aug_det):\n                    nb_changed_aug_det += 1\n                last_aug_det = observed_aug_det\n\n        assert nb_changed_aug_det == 0\n\n    @classmethod\n    def _test_one_branch_50_percent__cbaois(\n            cls, cbaoi, cbaoi_lr, augf_name):\n        def _same_coords(cbaoi1, cbaoi2):\n            assert len(cbaoi1.items) == len(cbaoi2.items)\n            for i1, i2 in zip(cbaoi1.items, cbaoi2.items):\n                if not np.allclose(i1.coords, i2.coords, atol=1e-4, rtol=0):\n                    return False\n            return True\n\n        aug = iaa.Sometimes(0.5, iaa.Fliplr(1.0))\n        nb_iterations = 250\n        nb_if_branch = 0\n        nb_else_branch = 0\n        for i in sm.xrange(nb_iterations):\n            cbaoi_aug = getattr(aug, augf_name)(cbaoi)\n\n            # use allclose() instead of coords_almost_equals() for efficiency\n            if _same_coords(cbaoi_aug, cbaoi_lr):\n                nb_if_branch += 1\n            elif _same_coords(cbaoi_aug, cbaoi):\n                nb_else_branch += 1\n            else:\n                raise Exception(\n                    \"Received output doesnt match any expected output.\")\n\n        p_if_branch = nb_if_branch / nb_iterations\n        p_else_branch = nb_else_branch / nb_iterations\n\n        assert np.isclose(p_if_branch, 0.5, rtol=0, atol=0.15)\n        assert np.isclose(p_else_branch, 0.5, rtol=0, atol=0.15)\n\n    def test_one_branch_50_percent__keypoints(self):\n        self._test_one_branch_50_percent__cbaois(\n            self.keypoints, self.keypoints_lr, \"augment_keypoints\")\n\n    def test_one_branch_50_percent__polygons(self):\n        self._test_one_branch_50_percent__cbaois(\n            self.polygons, self.polygons_lr, \"augment_polygons\")\n\n    def test_one_branch_50_percent__bounding_boxes(self):\n        self._test_one_branch_50_percent__cbaois(\n            self.bbsoi, self.bbsoi_lr, \"augment_bounding_boxes\")\n\n    @classmethod\n    def _test_empty_cbaoi(cls, cbaoi, augf_name):\n        aug = iaa.Sometimes(0.5, iaa.Identity())\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(observed, cbaoi)\n\n    def test_empty_keypoints(self):\n        kpsoi = ia.KeypointsOnImage([], shape=(1, 2, 3))\n        self._test_empty_cbaoi(kpsoi, \"augment_keypoints\")\n\n    def test_empty_polygons(self):\n        psoi = ia.PolygonsOnImage([], shape=(1, 2, 3))\n        self._test_empty_cbaoi(psoi, \"augment_polygons\")\n\n    def test_empty_line_strings(self):\n        lsoi = ia.LineStringsOnImage([], shape=(1, 2, 3))\n        self._test_empty_cbaoi(lsoi, \"augment_line_strings\")\n\n    def test_empty_bounding_boxes(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(1, 2, 3))\n        self._test_empty_cbaoi(bbsoi, \"augment_bounding_boxes\")\n\n    def test_p_is_stochastic_parameter(self):\n        image = np.zeros((1, 1), dtype=np.uint8) + 100\n        images = [image] * 10\n        aug = iaa.Sometimes(\n            p=iap.Binomial(iap.Choice([0.0, 1.0])),\n            then_list=iaa.Add(10))\n\n        seen = [0, 0]\n        for _ in sm.xrange(100):\n            observed = aug.augment_images(images)\n            uq = np.unique(np.uint8(observed))\n            assert len(uq) == 1\n            if uq[0] == 100:\n                seen[0] += 1\n            elif uq[0] == 110:\n                seen[1] += 1\n            else:\n                assert False\n        assert seen[0] > 20\n        assert seen[1] > 20\n\n    def test_bad_datatype_for_p_fails(self):\n        got_exception = False\n        try:\n            _ = iaa.Sometimes(p=\"foo\")\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_bad_datatype_for_then_list_fails(self):\n        got_exception = False\n        try:\n            _ = iaa.Sometimes(p=0.2, then_list=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_bad_datatype_for_else_list_fails(self):\n        got_exception = False\n        try:\n            _ = iaa.Sometimes(p=0.2, then_list=None, else_list=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_two_branches_both_none(self):\n        aug = iaa.Sometimes(0.2, then_list=None, else_list=None)\n        image = np.random.randint(0, 255, size=(16, 16), dtype=np.uint8)\n        observed = aug.augment_image(image)\n        assert np.array_equal(observed, image)\n\n    def test_using_hooks_to_deactivate_propagation(self):\n        image = np.random.randint(0, 255-10, size=(16, 16), dtype=np.uint8)\n        aug = iaa.Sometimes(1.0, iaa.Add(10))\n\n        def _propagator(images, augmenter, parents, default):\n            return False if augmenter == aug else default\n\n        hooks = ia.HooksImages(propagator=_propagator)\n\n        observed1 = aug.augment_image(image)\n        observed2 = aug.augment_image(image, hooks=hooks)\n        assert np.array_equal(observed1, image + 10)\n        assert np.array_equal(observed2, image)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Sometimes(1.0, iaa.Identity())\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Sometimes(1.0, iaa.Identity())\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug == 0)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_get_parameters(self):\n        aug = iaa.Sometimes(0.75)\n        params = aug.get_parameters()\n        assert is_parameter_instance(params[0], iap.Binomial)\n        assert is_parameter_instance(params[0].p, iap.Deterministic)\n        assert 0.75 - 1e-8 < params[0].p.value < 0.75 + 1e-8\n\n    def test___str___and___repr__(self):\n        then_list = iaa.Add(1)\n        else_list = iaa.Add(2)\n        aug = iaa.Sometimes(\n            0.5,\n            then_list=then_list,\n            else_list=else_list,\n            name=\"SometimesTest\")\n\n        expected_then_list = (\n            \"Sequential(\"\n            \"name=SometimesTest-then, \"\n            \"random_order=False, \"\n            \"children=[%s], \"\n            \"deterministic=False\"\n            \")\" % (str(then_list),))\n        expected_else_list = (\n            \"Sequential(\"\n            \"name=SometimesTest-else, \"\n            \"random_order=False, \"\n            \"children=[%s], \"\n            \"deterministic=False\"\n            \")\" % (str(else_list),))\n        expected = (\n            \"Sometimes(\"\n            \"p=%s, name=%s, then_list=%s, else_list=%s, deterministic=%s\"\n            \")\" % (\n                str(aug.p),\n                \"SometimesTest\",\n                expected_then_list,\n                expected_else_list,\n                \"False\"))\n\n        observed_str = aug.__str__()\n        observed_repr = aug.__repr__()\n\n        assert observed_str == expected\n        assert observed_repr == expected\n\n    def test___str___and___repr___with_nones_as_children(self):\n        aug = iaa.Sometimes(\n            0.5,\n            then_list=None,\n            else_list=None,\n            name=\"SometimesTest\")\n\n        expected = (\n            \"Sometimes(\"\n            \"p=%s, \"\n            \"name=%s, \"\n            \"then_list=%s, \"\n            \"else_list=%s, \"\n            \"deterministic=%s\"\n            \")\" % (\n                str(aug.p),\n                \"SometimesTest\",\n                \"None\",\n                \"None\",\n                \"False\"))\n\n        observed_str = aug.__str__()\n        observed_repr = aug.__repr__()\n\n        assert observed_str == expected\n        assert observed_repr == expected\n\n    def test_shapes_changed_by_children__no_keep_size_non_stochastic(self):\n        # Test for https://github.com/aleju/imgaug/issues/143\n        # (shapes change in child augmenters, leading to problems if input\n        # arrays are assumed to stay input arrays)\n        def _assert_all_valid_shapes(images):\n            expected_shapes = [(4, 8, 3), (6, 8, 3)]\n            assert np.all([img.shape in expected_shapes for img in images])\n\n        image = np.zeros((8, 8, 3), dtype=np.uint8)\n        aug = iaa.Sometimes(\n            0.5,\n            iaa.Crop((2, 0, 2, 0), keep_size=False),\n            iaa.Crop((1, 0, 1, 0), keep_size=False)\n        )\n\n        for _ in sm.xrange(10):\n            observed = aug.augment_images(\n                np.uint8([image, image, image, image]))\n            assert isinstance(observed, list) or ia.is_np_array(observed)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images([image, image, image, image])\n            assert isinstance(observed, list)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images(np.uint8([image]))\n            assert isinstance(observed, list) or ia.is_np_array(observed)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images([image])\n            assert isinstance(observed, list)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_image(image)\n            assert ia.is_np_array(image)\n            _assert_all_valid_shapes([observed])\n\n    def test_shapes_changed_by_children__no_keep_size_stochastic(self):\n        def _assert_all_valid_shapes(images):\n            assert np.all([\n                16 <= img.shape[0] <= 30\n                and img.shape[1:] == (32, 3) for img in images\n            ])\n\n        image = np.zeros((32, 32, 3), dtype=np.uint8)\n        aug = iaa.Sometimes(\n            0.5,\n            iaa.Crop(((1, 4), 0, (1, 4), 0), keep_size=False),\n            iaa.Crop(((4, 8), 0, (4, 8), 0), keep_size=False)\n        )\n\n        for _ in sm.xrange(10):\n            observed = aug.augment_images(\n                np.uint8([image, image, image, image]))\n            assert isinstance(observed, list) or ia.is_np_array(observed)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images([image, image, image, image])\n            assert isinstance(observed, list)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images(np.uint8([image]))\n            assert isinstance(observed, list)  or ia.is_np_array(observed)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images([image])\n            assert isinstance(observed, list)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_image(image)\n            assert ia.is_np_array(image)\n            _assert_all_valid_shapes([observed])\n\n    def test_shapes_changed_by_children__keep_size_non_stochastic(self):\n        def _assert_all_valid_shapes(images):\n            expected_shapes = [(8, 8, 3)]\n            assert np.all([img.shape in expected_shapes for img in images])\n\n        image = np.zeros((8, 8, 3), dtype=np.uint8)\n        aug = iaa.Sometimes(\n            0.5,\n            iaa.Crop((2, 0, 2, 0), keep_size=True),\n            iaa.Crop((1, 0, 1, 0), keep_size=True)\n        )\n\n        for _ in sm.xrange(10):\n            observed = aug.augment_images(\n                np.uint8([image, image, image, image]))\n            assert ia.is_np_array(observed)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images([image, image, image, image])\n            assert isinstance(observed, list)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images(np.uint8([image]))\n            assert ia.is_np_array(observed)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images([image])\n            assert isinstance(observed, list)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_image(image)\n            assert ia.is_np_array(observed)\n            _assert_all_valid_shapes([observed])\n\n    def test_shapes_changed_by_children__keep_size_stochastic(self):\n        def _assert_all_valid_shapes(images):\n            # only one shape expected here despite stochastic crop ranges\n            # due to keep_size=True\n            expected_shapes = [(8, 8, 3)]\n            assert np.all([img.shape in expected_shapes for img in images])\n\n        image = np.zeros((8, 8, 3), dtype=np.uint8)\n        aug = iaa.Sometimes(\n            0.5,\n            iaa.Crop(((1, 4), 0, (1, 4), 0), keep_size=True),\n            iaa.Crop(((4, 8), 0, (4, 8), 0), keep_size=True)\n        )\n\n        for _ in sm.xrange(10):\n            observed = aug.augment_images(\n                np.uint8([image, image, image, image]))\n            assert ia.is_np_array(observed)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images([image, image, image, image])\n            assert isinstance(observed, list)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images(np.uint8([image]))\n            assert ia.is_np_array(observed)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_images([image])\n            assert isinstance(observed, list)\n            _assert_all_valid_shapes(observed)\n\n            observed = aug.augment_image(image)\n            assert ia.is_np_array(observed)\n            _assert_all_valid_shapes([observed])\n\n    def test_other_dtypes_via_noop__bool(self):\n        aug = iaa.Sometimes(1.0, iaa.Identity())\n        image = np.zeros((3, 3), dtype=bool)\n        image[0, 0] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.all(image_aug == image)\n\n    def test_other_dtypes_via_noop__uint_int(self):\n        aug = iaa.Sometimes(1.0, iaa.Identity())\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, _center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.array_equal(image_aug, image)\n\n    def test_other_dtypes_via_noop__float(self):\n        aug = iaa.Sometimes(1.0, iaa.Identity())\n\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n        for dtype, value in zip(dtypes, values):\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.all(image_aug == image)\n\n    def test_other_dtypes_via_flip__bool(self):\n        aug = iaa.Sometimes(0.5, iaa.Fliplr(1.0), iaa.Flipud(1.0))\n        image = np.zeros((3, 3), dtype=bool)\n        image[0, 0] = True\n        expected = [np.zeros((3, 3), dtype=bool) for _ in sm.xrange(2)]\n        expected[0][0, 2] = True\n        expected[1][2, 0] = True\n        seen = [False, False]\n        for _ in sm.xrange(100):\n            image_aug = aug.augment_image(image)\n\n            assert image_aug.dtype.name == image.dtype.name\n            if np.all(image_aug == expected[0]):\n                seen[0] = True\n            elif np.all(image_aug == expected[1]):\n                seen[1] = True\n            else:\n                assert False\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_other_dtypes_via_flip__uint_int(self):\n        aug = iaa.Sometimes(0.5, iaa.Fliplr(1.0), iaa.Flipud(1.0))\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, _center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n                expected = [np.zeros((3, 3), dtype=dtype) for _ in sm.xrange(2)]\n                expected[0][0, 2] = value\n                expected[1][2, 0] = value\n                seen = [False, False]\n                for _ in sm.xrange(100):\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    if np.all(image_aug == expected[0]):\n                        seen[0] = True\n                    elif np.all(image_aug == expected[1]):\n                        seen[1] = True\n                    else:\n                        assert False\n                    if np.all(seen):\n                        break\n                assert np.all(seen)\n\n    def test_other_dtypes_via_flip__float(self):\n        aug = iaa.Sometimes(0.5, iaa.Fliplr(1.0), iaa.Flipud(1.0))\n\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n        for dtype, value in zip(dtypes, values):\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3), dtype=dtype)\n                image[0, 0] = value\n                expected = [np.zeros((3, 3), dtype=dtype) for _ in sm.xrange(2)]\n                expected[0][0, 2] = value\n                expected[1][2, 0] = value\n                seen = [False, False]\n                for _ in sm.xrange(100):\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    if np.all(image_aug == expected[0]):\n                        seen[0] = True\n                    elif np.all(image_aug == expected[1]):\n                        seen[1] = True\n                    else:\n                        assert False\n                    if np.all(seen):\n                        break\n                assert np.all(seen)\n\n    def test_pickleable(self):\n        aug = iaa.Sometimes(0.5, iaa.Add(10), [iaa.Add(1), iaa.Multiply(2.0)],\n                            seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5)\n\n    def test_get_children_lists(self):\n        child = iaa.Identity()\n        aug = iaa.Sometimes(0.5, [child])\n        children_lsts = aug.get_children_lists()\n        assert len(children_lsts) == 1\n        assert len(children_lsts[0]) == 1\n        assert children_lsts[0][0] is child\n\n    def test_get_children_lists_both_lists(self):\n        child = iaa.Identity()\n        child2 = iaa.Identity()\n        aug = iaa.Sometimes(0.5, [child], [child2])\n        children_lsts = aug.get_children_lists()\n        assert len(children_lsts) == 2\n        assert len(children_lsts[0]) == 1\n        assert len(children_lsts[1]) == 1\n        assert children_lsts[0][0] is child\n        assert children_lsts[1][0] is child2\n\n    def test_to_deterministic(self):\n        child = iaa.Identity()\n        child2 = iaa.Identity()\n        aug = iaa.Sometimes(0.5, [child], [child2])\n\n        aug_det = aug.to_deterministic()\n\n        assert aug_det.deterministic\n        assert aug_det.random_state is not aug.random_state\n        assert aug_det.then_list[0].deterministic\n        assert aug_det.else_list[0].deterministic\n\n\nclass TestWithChannels(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        base_img = np.zeros((3, 3, 2), dtype=np.uint8)\n        base_img[..., 0] += 100\n        base_img[..., 1] += 200\n        return base_img\n\n    def test_augment_only_channel_0(self):\n        aug = iaa.WithChannels(0, iaa.Add(10))\n        observed = aug.augment_image(self.image)\n        expected = self.image\n        expected[..., 0] += 10\n        assert np.allclose(observed, expected)\n\n    def test_augment_only_channel_1(self):\n        aug = iaa.WithChannels(1, iaa.Add(10))\n        observed = aug.augment_image(self.image)\n        expected = self.image\n        expected[..., 1] += 10\n        assert np.allclose(observed, expected)\n\n    def test_augment_all_channels_via_none(self):\n        aug = iaa.WithChannels(None, iaa.Add(10))\n        observed = aug.augment_image(self.image)\n        expected = self.image + 10\n        assert np.allclose(observed, expected)\n\n    def test_augment_channels_0_and_1_via_list(self):\n        aug = iaa.WithChannels([0, 1], iaa.Add(10))\n        observed = aug.augment_image(self.image)\n        expected = self.image + 10\n        assert np.allclose(observed, expected)\n\n    def test_apply_multiple_augmenters(self):\n        image = np.zeros((3, 3, 2), dtype=np.uint8)\n        image[..., 0] += 5\n        image[..., 1] += 10\n        aug = iaa.WithChannels(1, [iaa.Add(10), iaa.Multiply(2.0)])\n\n        observed = aug.augment_image(image)\n\n        expected = np.copy(image)\n        expected[..., 1] += 10\n        expected[..., 1] *= 2\n        assert np.allclose(observed, expected)\n\n    def test_multiple_images_given_as_array(self):\n        images = np.concatenate([\n            self.image[np.newaxis, ...],\n            self.image[np.newaxis, ...]],\n            axis=0)\n        aug = iaa.WithChannels(1, iaa.Add(10))\n\n        observed = aug.augment_images(images)\n\n        expected = np.copy(images)\n        expected[..., 1] += 10\n        assert np.allclose(observed, expected)\n\n    def test_multiple_images_given_as_list_of_arrays(self):\n        images = [self.image, self.image]\n        aug = iaa.WithChannels(1, iaa.Add(10))\n\n        observed = aug.augment_images(images)\n\n        expected = self.image\n        expected[..., 1] += 10\n        expected = [expected, expected]\n        assert array_equal_lists(observed, expected)\n\n    def test_children_list_is_none(self):\n        aug = iaa.WithChannels(1, children=None)\n        observed = aug.augment_image(self.image)\n        expected = self.image\n        assert np.array_equal(observed, expected)\n\n    def test_channels_is_empty_list(self):\n        aug = iaa.WithChannels([], iaa.Add(10))\n        observed = aug.augment_image(self.image)\n        expected = self.image\n        assert np.array_equal(observed, expected)\n\n    def test_heatmap_augmentation_single_channel(self):\n        heatmap_arr = np.float32([\n            [0.0, 0.0, 1.0],\n            [0.0, 1.0, 1.0],\n            [1.0, 1.0, 1.0]\n        ])\n        heatmap = HeatmapsOnImage(heatmap_arr, shape=(3, 3, 3))\n        affine = iaa.Affine(translate_px={\"x\": 1})\n        aug = iaa.WithChannels(1, children=[affine])\n\n        heatmap_aug = aug.augment_heatmaps(heatmap)\n\n        assert heatmap_aug.shape == (3, 3, 3)\n        assert np.allclose(heatmap_aug.get_arr(), heatmap_arr)\n\n    def test_heatmap_augmentation_multiple_channels(self):\n        heatmap_arr = np.float32([\n            [0.0, 0.0, 1.0],\n            [0.0, 1.0, 1.0],\n            [1.0, 1.0, 1.0]\n        ])\n        heatmap_arr_shifted = np.float32([\n            [0.0, 0.0, 0.0],\n            [0.0, 0.0, 1.0],\n            [0.0, 1.0, 1.0]\n        ])\n        heatmap = HeatmapsOnImage(heatmap_arr, shape=(3, 3, 3))\n        affine = iaa.Affine(translate_px={\"x\": 1})\n        aug = iaa.WithChannels([0, 1, 2], children=[affine])\n\n        heatmap_aug = aug.augment_heatmaps(heatmap)\n\n        assert heatmap_aug.shape == (3, 3, 3)\n        assert np.allclose(heatmap_aug.get_arr(), heatmap_arr_shifted)\n\n    def test_segmentation_map_augmentation_single_channel(self):\n        segmap_arr = np.int32([\n            [0, 0, 1],\n            [0, 1, 1],\n            [1, 1, 1]\n        ])\n        segmap = SegmentationMapsOnImage(segmap_arr, shape=(3, 3, 3))\n\n        aug = iaa.WithChannels(1, children=[iaa.Affine(translate_px={\"x\": 1})])\n        segmap_aug = aug.augment_segmentation_maps(segmap)\n        assert segmap_aug.shape == (3, 3, 3)\n        assert np.array_equal(segmap_aug.get_arr(), segmap_arr)\n\n    def test_segmentation_map_augmentation_multiple_channels(self):\n        segmap_arr = np.int32([\n            [0, 0, 1],\n            [0, 1, 1],\n            [1, 1, 1]\n        ])\n        segmap_arr_shifted = np.int32([\n            [0, 0, 0],\n            [0, 0, 1],\n            [0, 1, 1]\n        ])\n        segmap = SegmentationMapsOnImage(segmap_arr, shape=(3, 3, 3))\n        affine = iaa.Affine(translate_px={\"x\": 1})\n        aug = iaa.WithChannels([0, 1, 2], children=[affine])\n\n        segmap_aug = aug.augment_segmentation_maps(segmap)\n\n        assert segmap_aug.shape == (3, 3, 3)\n        assert np.array_equal(segmap_aug.get_arr(), segmap_arr_shifted)\n\n    @classmethod\n    def _test_cbaoi_augmentation_single_channel(cls, cbaoi, augf_name):\n        affine = iaa.Affine(translate_px={\"x\": 1})\n        aug = iaa.WithChannels(1, children=[affine])\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(observed, cbaoi)\n\n    @classmethod\n    def _test_cbaoi_augmentation_all_channels_via_list(cls, cbaoi, cbaoi_x,\n                                                       augf_name):\n        affine = iaa.Affine(translate_px={\"x\": 1})\n        aug = iaa.WithChannels([0, 1, 2], children=[affine])\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(observed, cbaoi_x)\n\n    @classmethod\n    def _test_cbaoi_augmentation_subset_of_channels(cls, cbaoi, cbaoi_x,\n                                                    augf_name):\n        affine = iaa.Affine(translate_px={\"x\": 1})\n        aug = iaa.WithChannels([0, 1], children=[affine])\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(observed, cbaoi_x)\n\n    @classmethod\n    def _test_cbaoi_augmentation_with_empty_cbaoi(cls, cbaoi, augf_name):\n        affine = iaa.Affine(translate_px={\"x\": 1})\n        aug = iaa.WithChannels([0, 1], children=[affine])\n\n        observed = getattr(aug, augf_name)(cbaoi)\n\n        assert_cbaois_equal(observed, cbaoi)\n\n    def test_keypoint_augmentation_single_channel(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=2)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(5, 6, 3))\n        self._test_cbaoi_augmentation_single_channel(kpsoi, \"augment_keypoints\")\n\n    def test_keypoint_augmentation_all_channels_via_list(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=2)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(5, 6, 3))\n        kpsoi_x = kpsoi.shift(x=1)\n        self._test_cbaoi_augmentation_all_channels_via_list(\n            kpsoi, kpsoi_x, \"augment_keypoints\")\n\n    def test_keypoint_augmentation_subset_of_channels(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=2)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(5, 6, 3))\n        kpsoi_x = kpsoi.shift(x=1)\n        self._test_cbaoi_augmentation_subset_of_channels(\n            kpsoi, kpsoi_x, \"augment_keypoints\")\n\n    def test_keypoint_augmentation_with_empty_keypoints_instance(self):\n        kpsoi = ia.KeypointsOnImage([], shape=(5, 6, 3))\n        self._test_cbaoi_augmentation_with_empty_cbaoi(\n            kpsoi, \"augment_keypoints\")\n\n    def test_polygon_augmentation(self):\n        polygons = [ia.Polygon([(0, 0), (3, 0), (3, 3), (0, 3)])]\n        psoi = ia.PolygonsOnImage(polygons, shape=(5, 6, 3))\n        self._test_cbaoi_augmentation_single_channel(psoi, \"augment_polygons\")\n\n    def test_polygon_augmentation_all_channels_via_list(self):\n        polygons = [ia.Polygon([(0, 0), (3, 0), (3, 3), (0, 3)])]\n        psoi = ia.PolygonsOnImage(polygons, shape=(5, 6, 3))\n        psoi_x = psoi.shift(x=1)\n        self._test_cbaoi_augmentation_all_channels_via_list(\n            psoi, psoi_x, \"augment_polygons\")\n\n    def test_polygon_augmentation_subset_of_channels(self):\n        polygons = [ia.Polygon([(0, 0), (3, 0), (3, 3), (0, 3)])]\n        psoi = ia.PolygonsOnImage(polygons, shape=(5, 6, 3))\n        psoi_x = psoi.shift(x=1)\n        self._test_cbaoi_augmentation_subset_of_channels(\n            psoi, psoi_x, \"augment_polygons\")\n\n    def test_polygon_augmentation_with_empty_polygons_instance(self):\n        psoi = ia.PolygonsOnImage([], shape=(5, 6, 3))\n        self._test_cbaoi_augmentation_with_empty_cbaoi(\n            psoi, \"augment_polygons\")\n\n    def test_line_string_augmentation(self):\n        lss = [ia.LineString([(0, 0), (3, 0), (3, 3), (0, 3)])]\n        lsoi = ia.LineStringsOnImage(lss, shape=(5, 6, 3))\n        self._test_cbaoi_augmentation_single_channel(\n            lsoi, \"augment_line_strings\")\n\n    def test_line_string_augmentation_all_channels_via_list(self):\n        lss = [ia.LineString([(0, 0), (3, 0), (3, 3), (0, 3)])]\n        lsoi = ia.LineStringsOnImage(lss, shape=(5, 6, 3))\n        lsoi_x = lsoi.shift(x=1)\n        self._test_cbaoi_augmentation_all_channels_via_list(\n            lsoi, lsoi_x, \"augment_line_strings\")\n\n    def test_line_string_augmentation_subset_of_channels(self):\n        lss = [ia.LineString([(0, 0), (3, 0), (3, 3), (0, 3)])]\n        lsoi = ia.LineStringsOnImage(lss, shape=(5, 6, 3))\n        lsoi_x = lsoi.shift(x=1)\n        self._test_cbaoi_augmentation_subset_of_channels(\n            lsoi, lsoi_x, \"augment_line_strings\")\n\n    def test_line_string_augmentation_with_empty_polygons_instance(self):\n        lsoi = ia.LineStringsOnImage([], shape=(5, 6, 3))\n        self._test_cbaoi_augmentation_with_empty_cbaoi(\n            lsoi, \"augment_line_strings\")\n\n    def test_bounding_boxes_augmentation(self):\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=1.0, y2=1.5)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(5, 6, 3))\n        self._test_cbaoi_augmentation_single_channel(\n            bbsoi, \"augment_bounding_boxes\")\n\n    def test_bounding_boxes_augmentation_all_channels_via_list(self):\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=1.0, y2=1.5)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(5, 6, 3))\n        bbsoi_x = bbsoi.shift(x=1)\n        self._test_cbaoi_augmentation_all_channels_via_list(\n            bbsoi, bbsoi_x, \"augment_bounding_boxes\")\n\n    def test_bounding_boxes_augmentation_subset_of_channels(self):\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=1.0, y2=1.5)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(5, 6, 3))\n        bbsoi_x = bbsoi.shift(x=1)\n        self._test_cbaoi_augmentation_subset_of_channels(\n            bbsoi, bbsoi_x, \"augment_bounding_boxes\")\n\n    def test_bounding_boxes_augmentation_with_empty_bb_instance(self):\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(5, 6, 3))\n        self._test_cbaoi_augmentation_with_empty_cbaoi(\n            bbsoi, \"augment_bounding_boxes\")\n\n    def test_invalid_datatype_for_channels_fails(self):\n        got_exception = False\n        try:\n            _ = iaa.WithChannels(False, iaa.Add(10))\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_invalid_datatype_for_children_fails(self):\n        got_exception = False\n        try:\n            _ = iaa.WithChannels(1, False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.WithChannels([0], iaa.Add(1))\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.WithChannels([0], iaa.Add(1))\n\n                image_aug = aug(image=image)\n\n                assert np.all(image_aug[..., 0] == 1)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_get_parameters(self):\n        aug = iaa.WithChannels([1], iaa.Add(10))\n        params = aug.get_parameters()\n        assert len(params) == 1\n        assert params[0] == [1]\n\n    def test_get_children_lists(self):\n        children = iaa.Sequential([iaa.Add(10)])\n        aug = iaa.WithChannels(1, children)\n        assert aug.get_children_lists() == [children]\n\n    def test_to_deterministic(self):\n        child = iaa.Identity()\n        aug = iaa.WithChannels(1, [child])\n\n        aug_det = aug.to_deterministic()\n\n        assert aug_det.deterministic\n        assert aug_det.random_state is not aug.random_state\n        assert aug_det.children[0].deterministic\n\n    def test___repr___and___str__(self):\n        children = iaa.Sequential([iaa.Identity()])\n        aug = iaa.WithChannels(1, children, name=\"WithChannelsTest\")\n        expected = (\n            \"WithChannels(\"\n            \"channels=[1], \"\n            \"name=WithChannelsTest, \"\n            \"children=%s, \"\n            \"deterministic=False\"\n            \")\" % (str(children),))\n\n        assert aug.__repr__() == expected\n        assert aug.__str__() == expected\n\n    def test_other_dtypes_via_noop__bool(self):\n        aug = iaa.WithChannels([0], iaa.Identity())\n\n        image = np.zeros((3, 3, 2), dtype=bool)\n        image[0, 0, :] = True\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.all(image_aug == image)\n\n    def test_other_dtypes_via_noop__uint_int(self):\n        aug = iaa.WithChannels([0], iaa.Identity())\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3, 2), dtype=dtype)\n                image[0, 0, :] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.array_equal(image_aug, image)\n\n    def test_other_dtypes_via_noop__float(self):\n        aug = iaa.WithChannels([0], iaa.Identity())\n\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n        for dtype, value in zip(dtypes, values):\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3, 2), dtype=dtype)\n                image[0, 0, :] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.all(image_aug == image)\n\n    def test_other_dtypes_via_flips__bool(self):\n        aug = iaa.WithChannels([0], iaa.Fliplr(1.0))\n\n        image = np.zeros((3, 3, 2), dtype=bool)\n        image[0, 0, :] = True\n        expected = np.zeros((3, 3, 2), dtype=bool)\n        expected[0, 2, 0] = True\n        expected[0, 0, 1] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.name == image.dtype.name\n        assert np.all(image_aug == expected)\n\n    def test_other_dtypes_via_flips__uint_int(self):\n        aug = iaa.WithChannels([0], iaa.Fliplr(1.0))\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3, 2), dtype=dtype)\n                image[0, 0, :] = value\n                expected = np.zeros((3, 3, 2), dtype=dtype)\n                expected[0, 2, 0] = value\n                expected[0, 0, 1] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.array_equal(image_aug, expected)\n\n    def test_other_dtypes_via_flips__float(self):\n        aug = iaa.WithChannels([0], iaa.Fliplr(1.0))\n\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n        for dtype, value in zip(dtypes, values):\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3, 2), dtype=dtype)\n                image[0, 0, :] = value\n                expected = np.zeros((3, 3, 2), dtype=dtype)\n                expected[0, 2, 0] = value\n                expected[0, 0, 1] = value\n\n                image_aug = aug.augment_image(image)\n\n                assert image_aug.dtype.name == dtype\n                assert np.all(image_aug == expected)\n\n    def test_pickleable(self):\n        aug = iaa.WithChannels([0], iaa.Add((1, 10), seed=2),\n                               seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5)\n\n\nclass TestChannelShuffle(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.ChannelShuffle(p=0.9, channels=[0, 2])\n        assert is_parameter_instance(aug.p, iap.Binomial)\n        assert is_parameter_instance(aug.p.p, iap.Deterministic)\n        assert np.allclose(aug.p.p.value, 0.9)\n        assert aug.channels == [0, 2]\n\n    def test_p_is_1(self):\n        aug = iaa.ChannelShuffle(p=1.0)\n        img = np.uint8([0, 1]).reshape((1, 1, 2))\n        expected = [\n            np.uint8([0, 1]).reshape((1, 1, 2)),\n            np.uint8([1, 0]).reshape((1, 1, 2))\n        ]\n        seen = [False, False]\n        for _ in sm.xrange(100):\n            img_aug = aug.augment_image(img)\n            if np.array_equal(img_aug, expected[0]):\n                seen[0] = True\n            elif np.array_equal(img_aug, expected[1]):\n                seen[1] = True\n            else:\n                assert False\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_p_is_0(self):\n        aug = iaa.ChannelShuffle(p=0)\n        img = np.uint8([0, 1]).reshape((1, 1, 2))\n        for _ in sm.xrange(20):\n            img_aug = aug.augment_image(img)\n            assert np.array_equal(img_aug, img)\n\n    def test_p_is_1_and_channels_is_limited_subset(self):\n        aug = iaa.ChannelShuffle(p=1.0, channels=[0, 2])\n        img = np.uint8([0, 1, 2]).reshape((1, 1, 3))\n        expected = [\n            np.uint8([0, 1, 2]).reshape((1, 1, 3)),\n            np.uint8([2, 1, 0]).reshape((1, 1, 3))\n        ]\n        seen = [False, False]\n        for _ in sm.xrange(100):\n            img_aug = aug.augment_image(img)\n            if np.array_equal(img_aug, expected[0]):\n                seen[0] = True\n            elif np.array_equal(img_aug, expected[1]):\n                seen[1] = True\n            else:\n                assert False\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_get_parameters(self):\n        aug = iaa.ChannelShuffle(p=1.0, channels=[0, 2])\n        assert aug.get_parameters()[0] == aug.p\n        assert aug.get_parameters()[1] == aug.channels\n\n    def test_heatmaps_must_not_change(self):\n        aug = iaa.ChannelShuffle(p=1.0)\n        hm = ia.HeatmapsOnImage(np.float32([[0, 0.5, 1.0]]), shape=(4, 4, 3))\n        hm_aug = aug.augment_heatmaps([hm])[0]\n        assert hm_aug.shape == (4, 4, 3)\n        assert hm_aug.arr_0to1.shape == (1, 3, 1)\n        assert np.allclose(hm.arr_0to1, hm_aug.arr_0to1)\n\n    def test_segmentation_maps_must_not_change(self):\n        aug = iaa.ChannelShuffle(p=1.0)\n        segmap = SegmentationMapsOnImage(np.int32([[0, 1, 2]]), shape=(4, 4, 3))\n        segmap_aug = aug.augment_segmentation_maps([segmap])[0]\n        assert segmap_aug.shape == (4, 4, 3)\n        assert segmap_aug.arr.shape == (1, 3, 1)\n        assert np.array_equal(segmap.arr, segmap_aug.arr)\n\n    def test_keypoints_must_not_change(self):\n        aug = iaa.ChannelShuffle(p=1.0)\n        kpsoi = ia.KeypointsOnImage([\n            ia.Keypoint(x=3, y=1), ia.Keypoint(x=2, y=4)\n        ], shape=(10, 10, 3))\n\n        kpsoi_aug = aug.augment_keypoints([kpsoi])[0]\n\n        assert_cbaois_equal(kpsoi_aug, kpsoi)\n\n    def test_polygons_must_not_change(self):\n        aug = iaa.ChannelShuffle(p=1.0)\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (5, 0), (5, 5)])\n        ], shape=(10, 10, 3))\n\n        psoi_aug = aug.augment_polygons(psoi)\n\n        assert_cbaois_equal(psoi_aug, psoi)\n\n    def test_line_strings_must_not_change(self):\n        aug = iaa.ChannelShuffle(p=1.0)\n        lsoi = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (5, 0), (5, 5)])\n        ], shape=(10, 10, 3))\n\n        lsoi_aug = aug.augment_line_strings(lsoi)\n\n        assert_cbaois_equal(lsoi_aug, lsoi)\n\n    def test_bounding_boxes_must_not_change(self):\n        aug = iaa.ChannelShuffle(p=1.0)\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=0, x2=1.0, y2=1.5)\n        ], shape=(10, 10, 3))\n\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n\n        assert_cbaois_equal(bbsoi_aug, bbsoi)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.ChannelShuffle(1.0)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.ChannelShuffle(1.0)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_other_dtypes_bool(self):\n        aug = iaa.ChannelShuffle(p=0.5)\n\n        image = np.zeros((3, 3, 2), dtype=bool)\n        image[0, 0, 0] = True\n        expected = [np.zeros((3, 3, 2), dtype=bool) for _ in sm.xrange(2)]\n        expected[0][0, 0, 0] = True\n        expected[1][0, 0, 1] = True\n        seen = [False, False]\n        for _ in sm.xrange(100):\n            image_aug = aug.augment_image(image)\n\n            assert image_aug.dtype.name == image.dtype.name\n            if np.all(image_aug == expected[0]):\n                seen[0] = True\n            elif np.all(image_aug == expected[1]):\n                seen[1] = True\n            else:\n                assert False\n            if np.all(seen):\n                break\n        assert np.all(seen)\n\n    def test_other_dtypes_uint_int(self):\n        aug = iaa.ChannelShuffle(p=0.5)\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int32\", \"int64\"]\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n                value = max_value\n                image = np.zeros((3, 3, 2), dtype=dtype)\n                image[0, 0, 0] = value\n                expected = [np.zeros((3, 3, 2), dtype=dtype)\n                            for _\n                            in sm.xrange(2)]\n                expected[0][0, 0, 0] = value\n                expected[1][0, 0, 1] = value\n                seen = [False, False]\n                for _ in sm.xrange(100):\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    if np.all(image_aug == expected[0]):\n                        seen[0] = True\n                    elif np.all(image_aug == expected[1]):\n                        seen[1] = True\n                    else:\n                        assert False\n                    if np.all(seen):\n                        break\n                assert np.all(seen)\n\n    def test_other_dtypes_float(self):\n        aug = iaa.ChannelShuffle(p=0.5)\n\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\"float16\", \"float32\", \"float64\"] + f128\n\n        values = [5000, 1000 ** 2, 1000 ** 3, 1000 ** 4]\n        for dtype, value in zip(dtypes, values):\n            with self.subTest(dtype=dtype):\n                image = np.zeros((3, 3, 2), dtype=dtype)\n                image[0, 0, 0] = value\n                expected = [np.zeros((3, 3, 2), dtype=dtype)\n                            for _\n                            in sm.xrange(2)]\n                expected[0][0, 0, 0] = value\n                expected[1][0, 0, 1] = value\n                seen = [False, False]\n                for _ in sm.xrange(100):\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    if np.all(image_aug == expected[0]):\n                        seen[0] = True\n                    elif np.all(image_aug == expected[1]):\n                        seen[1] = True\n                    else:\n                        assert False\n                    if np.all(seen):\n                        break\n                assert np.all(seen)\n\n    def test_pickleable(self):\n        aug = iaa.ChannelShuffle(0.5, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(2, 2, 10))\n\n\nclass TestRemoveCBAsByOutOfImageFraction(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.RemoveCBAsByOutOfImageFraction(0.51)\n        assert np.isclose(aug.fraction, 0.51)\n\n    def test_no_cbas_in_batch(self):\n        aug = iaa.RemoveCBAsByOutOfImageFraction(0.51)\n\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\n            \"uint8\", \"uint16\", \"uint32\", \"uint64\",\n            \"int8\", \"int16\", \"int32\", \"int64\",\n            \"float16\", \"float32\", \"float64\",\n            \"bool\"\n        ] + f128\n\n        for dt in dtypes:\n            arr = np.ones((5, 10, 3), dtype=dt)\n\n            image_aug = aug(image=arr)\n\n            assert image_aug.dtype.name == dt\n            assert image_aug.shape == (5, 10, 3)\n            if arr.dtype.kind == \"f\":\n                assert np.allclose(image_aug, 1.0)\n            else:\n                assert np.all(image_aug == 1)\n\n    def test_keypoints(self):\n        aug = iaa.RemoveCBAsByOutOfImageFraction(0.51)\n        item1 = ia.Keypoint(x=5, y=1)\n        item2 = ia.Keypoint(x=15, y=1)\n        cbaoi = ia.KeypointsOnImage([item1, item2], shape=(10, 10, 3))\n\n        cbaoi_aug = aug(keypoints=cbaoi)\n\n        assert len(cbaoi_aug.items) == 1\n        for item_obs, item_exp in zip(cbaoi_aug.items, [item1]):\n            assert item_obs.coords_almost_equals(item_exp)\n\n    def test_bounding_boxes(self):\n        aug = iaa.RemoveCBAsByOutOfImageFraction(0.51)\n        item1 = ia.BoundingBox(y1=1, x1=5, y2=6, x2=9)\n        item2 = ia.BoundingBox(y1=1, x1=5, y2=6, x2=15)\n        item3 = ia.BoundingBox(y1=1, x1=15, y2=6, x2=25)\n        cbaoi = ia.BoundingBoxesOnImage([item1, item2, item3],\n                                        shape=(10, 10, 3))\n\n        cbaoi_aug = aug(bounding_boxes=cbaoi)\n\n        assert len(cbaoi_aug.items) == 2\n        for item_obs, item_exp in zip(cbaoi_aug.items, [item1, item2]):\n            assert item_obs.coords_almost_equals(item_exp)\n\n    def test_polygons(self):\n        aug = iaa.RemoveCBAsByOutOfImageFraction(0.51)\n        item1 = ia.Polygon([(5, 1), (9, 1), (9, 2), (5, 2)])\n        item2 = ia.Polygon([(5, 1), (15, 1), (15, 2), (5, 2)])\n        item3 = ia.Polygon([(15, 1), (25, 1), (25, 2), (15, 2)])\n        cbaoi = ia.PolygonsOnImage([item1, item2, item3],\n                                   shape=(10, 10, 3))\n\n        cbaoi_aug = aug(polygons=cbaoi)\n\n        assert len(cbaoi_aug.items) == 2\n        for item_obs, item_exp in zip(cbaoi_aug.items, [item1, item2]):\n            assert item_obs.coords_almost_equals(item_exp)\n\n    def test_line_strings(self):\n        aug = iaa.RemoveCBAsByOutOfImageFraction(0.51)\n        item1 = ia.LineString([(5, 1), (9, 1)])\n        item2 = ia.LineString([(5, 1), (15, 1)])\n        item3 = ia.LineString([(15, 1), (25, 1)])\n        cbaoi = ia.LineStringsOnImage([item1, item2, item3],\n                                      shape=(10, 10, 3))\n\n        cbaoi_aug = aug(line_strings=cbaoi)\n\n        assert len(cbaoi_aug.items) == 2\n        for item_obs, item_exp in zip(cbaoi_aug.items, [item1, item2]):\n            assert item_obs.coords_almost_equals(item_exp)\n\n    def test_get_parameters(self):\n        aug = iaa.RemoveCBAsByOutOfImageFraction(0.51)\n        params = aug.get_parameters()\n        assert len(params) == 1\n        assert np.isclose(params[0], 0.51)\n\n    def test_pickleable(self):\n        item1 = ia.Keypoint(x=5, y=1)\n        item2 = ia.Keypoint(x=15, y=1)\n        cbaoi = ia.KeypointsOnImage([item1, item2], shape=(10, 10, 3))\n\n        augmenter = iaa.RemoveCBAsByOutOfImageFraction(0.51)\n        augmenter_pkl = pickle.loads(pickle.dumps(augmenter, protocol=-1))\n\n        for _ in np.arange(3):\n            cbaoi_aug = augmenter(keypoints=cbaoi)\n            cbaoi_aug_pkl = augmenter_pkl(keypoints=cbaoi)\n            assert np.allclose(cbaoi_aug.to_xy_array(), cbaoi_aug_pkl.to_xy_array())\n\n\nclass TestClipCBAsToImagePlanes(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_no_cbas_in_batch(self):\n        aug = iaa.RemoveCBAsByOutOfImageFraction(0.51)\n\n        try:\n            f128 = [np.dtype(\"float128\")]\n        except TypeError:\n            f128 = []  # float128 not known by user system\n\n        dtypes = [\n            \"uint8\", \"uint16\", \"uint32\", \"uint64\",\n            \"int8\", \"int16\", \"int32\", \"int64\",\n            \"float16\", \"float32\", \"float64\",\n            \"bool\"\n        ] + f128\n\n        for dt in dtypes:\n            arr = np.ones((5, 10, 3), dtype=dt)\n\n            image_aug = aug(image=arr)\n\n            assert image_aug.dtype.name == dt\n            assert image_aug.shape == (5, 10, 3)\n            if arr.dtype.kind == \"f\":\n                assert np.allclose(image_aug, 1.0)\n            else:\n                assert np.all(image_aug == 1)\n\n    def test_keypoints(self):\n        aug = iaa.RemoveCBAsByOutOfImageFraction(0.51)\n        item1 = ia.Keypoint(x=5, y=1)\n        item2 = ia.Keypoint(x=15, y=1)\n        cbaoi = ia.KeypointsOnImage([item1, item2], shape=(10, 10, 3))\n\n        cbaoi_aug = aug(keypoints=cbaoi)\n\n        assert len(cbaoi_aug.items) == 1\n        for item_obs, item_exp in zip(cbaoi_aug.items, [item1]):\n            assert item_obs.coords_almost_equals(item_exp)\n\n    def test_bounding_boxes(self):\n        aug = iaa.ClipCBAsToImagePlanes()\n        item1 = ia.BoundingBox(y1=1, x1=5, y2=6, x2=9)\n        item2 = ia.BoundingBox(y1=1, x1=5, y2=6, x2=15)\n        item3 = ia.BoundingBox(y1=1, x1=15, y2=6, x2=25)\n        cbaoi = ia.BoundingBoxesOnImage([item1, item2, item3],\n                                        shape=(10, 10, 3))\n\n        cbaoi_aug = aug(bounding_boxes=cbaoi)\n\n        expected = [\n            ia.BoundingBox(y1=1, x1=5, y2=6, x2=9),\n            ia.BoundingBox(y1=1, x1=5, y2=6, x2=10)\n        ]\n        assert len(cbaoi_aug.items) == len(expected)\n        for item_obs, item_exp in zip(cbaoi_aug.items, expected):\n            assert item_obs.coords_almost_equals(item_exp)\n\n    def test_polygons(self):\n        aug = iaa.ClipCBAsToImagePlanes()\n        item1 = ia.Polygon([(5, 1), (9, 1), (9, 2), (5, 2)])\n        item2 = ia.Polygon([(5, 1), (15, 1), (15, 2), (5, 2)])\n        item3 = ia.Polygon([(15, 1), (25, 1), (25, 2), (15, 2)])\n        cbaoi = ia.PolygonsOnImage([item1, item2, item3],\n                                   shape=(10, 10, 3))\n\n        cbaoi_aug = aug(polygons=cbaoi)\n\n        expected = [\n            ia.Polygon([(5, 1), (9, 1), (9, 2), (5, 2)]),\n            ia.Polygon([(5, 1), (10, 1), (10, 2), (5, 2)])\n        ]\n        assert len(cbaoi_aug.items) == len(expected)\n        for item_obs, item_exp in zip(cbaoi_aug.items, expected):\n            assert item_obs.coords_almost_equals(item_exp)\n\n    def test_line_strings(self):\n        aug = iaa.ClipCBAsToImagePlanes()\n        item1 = ia.LineString([(5, 1), (9, 1)])\n        item2 = ia.LineString([(5, 1), (15, 1)])\n        item3 = ia.LineString([(15, 1), (25, 1)])\n        cbaoi = ia.LineStringsOnImage([item1, item2, item3],\n                                      shape=(10, 10, 3))\n\n        cbaoi_aug = aug(line_strings=cbaoi)\n\n        expected = [\n            ia.LineString([(5, 1), (9, 1)]),\n            ia.LineString([(5, 1), (10, 1)])\n        ]\n        assert len(cbaoi_aug.items) == len(expected)\n        for item_obs, item_exp in zip(cbaoi_aug.items, expected):\n            assert item_obs.coords_almost_equals(item_exp, max_distance=1e-2)\n\n    def test_get_parameters(self):\n        aug = iaa.ClipCBAsToImagePlanes()\n        params = aug.get_parameters()\n        assert len(params) == 0\n\n    def test_pickleable(self):\n        item1 = ia.Keypoint(x=5, y=1)\n        item2 = ia.Keypoint(x=15, y=1)\n        cbaoi = ia.KeypointsOnImage([item1, item2], shape=(10, 10, 3))\n\n        augmenter = iaa.ClipCBAsToImagePlanes()\n        augmenter_pkl = pickle.loads(pickle.dumps(augmenter, protocol=-1))\n\n        for _ in np.arange(3):\n            cbaoi_aug = augmenter(keypoints=cbaoi)\n            cbaoi_aug_pkl = augmenter_pkl(keypoints=cbaoi)\n            assert np.allclose(cbaoi_aug.to_xy_array(), cbaoi_aug_pkl.to_xy_array())\n"
  },
  {
    "path": "test/augmenters/test_mixed_files.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport skimage\nimport skimage.data\nimport six.moves as sm\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug.testutils import (create_random_images, array_equal_lists,\n                              keypoints_equal, reseed, assertWarns)\n\n\n# TODO this should probably be tested just once for Augmenter\ndef test_determinism():\n    reseed()\n\n    images = [\n        ia.data.quokka(size=(128, 128)),\n        ia.data.quokka(size=(64, 64)),\n        ia.data.quokka((128, 256))\n    ]\n    images.extend([ia.data.quokka(size=(16, 16))] * 20)\n\n    keypoints = [\n        ia.KeypointsOnImage([\n            ia.Keypoint(x=20, y=10), ia.Keypoint(x=5, y=5),\n            ia.Keypoint(x=10, y=43)], shape=(50, 60, 3))\n    ] * 20\n\n    augs = [\n        iaa.Sequential([iaa.Fliplr(0.5), iaa.Flipud(0.5)]),\n        iaa.SomeOf(1, [iaa.Fliplr(0.5), iaa.Flipud(0.5)]),\n        iaa.OneOf([iaa.Fliplr(0.5), iaa.Flipud(0.5)]),\n        iaa.Sometimes(0.5, iaa.Fliplr(1.0)),\n        iaa.WithColorspace(\"HSV\", children=iaa.Add((-50, 50))),\n        iaa.Resize((0.5, 0.9)),\n        iaa.CropAndPad(px=(-50, 50)),\n        iaa.Pad(px=(1, 50)),\n        iaa.Crop(px=(1, 50)),\n        iaa.Fliplr(0.5),\n        iaa.Flipud(0.5),\n        iaa.Superpixels(p_replace=(0.25, 1.0), n_segments=(16, 128)),\n        iaa.Grayscale(alpha=(0.1, 1.0)),\n        iaa.GaussianBlur((0.1, 3.0)),\n        iaa.AverageBlur((3, 11)),\n        iaa.MedianBlur((3, 11)),\n        iaa.Sharpen(alpha=(0.1, 1.0), lightness=(0.8, 1.2)),\n        iaa.Emboss(alpha=(0.1, 1.0), strength=(0.8, 1.2)),\n        iaa.EdgeDetect(alpha=(0.1, 1.0)),\n        iaa.DirectedEdgeDetect(alpha=(0.1, 1.0), direction=(0.0, 1.0)),\n        iaa.Add((-50, 50)),\n        iaa.AddElementwise((-50, 50)),\n        iaa.AdditiveGaussianNoise(scale=(0.1, 1.0)),\n        iaa.Multiply((0.6, 1.4)),\n        iaa.MultiplyElementwise((0.6, 1.4)),\n        iaa.Dropout((0.3, 0.5)),\n        iaa.CoarseDropout((0.3, 0.5), size_percent=(0.05, 0.2)),\n        iaa.Invert(0.5),\n        iaa.Affine(scale=(0.7, 1.3), translate_percent=(-0.1, 0.1),\n                   rotate=(-20, 20), shear=(-20, 20), order=ia.ALL,\n                   mode=ia.ALL, cval=(0, 255)),\n        iaa.PiecewiseAffine(scale=(0.1, 0.3)),\n        iaa.ElasticTransformation(alpha=10.0)\n    ]\n\n    augs_affect_geometry = [\n        iaa.Sequential([iaa.Fliplr(0.5), iaa.Flipud(0.5)]),\n        iaa.SomeOf(1, [iaa.Fliplr(0.5), iaa.Flipud(0.5)]),\n        iaa.OneOf([iaa.Fliplr(0.5), iaa.Flipud(0.5)]),\n        iaa.Sometimes(0.5, iaa.Fliplr(1.0)),\n        iaa.Resize((0.5, 0.9)),\n        iaa.CropAndPad(px=(-50, 50)),\n        iaa.Pad(px=(1, 50)),\n        iaa.Crop(px=(1, 50)),\n        iaa.Fliplr(0.5),\n        iaa.Flipud(0.5),\n        iaa.Affine(scale=(0.7, 1.3), translate_percent=(-0.1, 0.1),\n                   rotate=(-20, 20), shear=(-20, 20), order=ia.ALL,\n                   mode=ia.ALL, cval=(0, 255)),\n        iaa.PiecewiseAffine(scale=(0.1, 0.3)),\n        iaa.ElasticTransformation(alpha=(5, 100), sigma=(3, 5))\n    ]\n\n    for aug in augs:\n        aug_det = aug.to_deterministic()\n        images_aug1 = aug_det.augment_images(images)\n        images_aug2 = aug_det.augment_images(images)\n\n        aug_det = aug.to_deterministic()\n        images_aug3 = aug_det.augment_images(images)\n        images_aug4 = aug_det.augment_images(images)\n\n        assert array_equal_lists(images_aug1, images_aug2), \\\n            \"Images (1, 2) expected to be identical for %s\" % (aug.name,)\n\n        assert array_equal_lists(images_aug3, images_aug4), \\\n            \"Images (3, 4) expected to be identical for %s\" % (aug.name,)\n\n        assert not array_equal_lists(images_aug1, images_aug3), \\\n            \"Images (1, 3) expected to be different for %s\" % (aug.name,)\n\n    for aug in augs_affect_geometry:\n        aug_det = aug.to_deterministic()\n        kps_aug1 = aug_det.augment_keypoints(keypoints)\n        kps_aug2 = aug_det.augment_keypoints(keypoints)\n\n        aug_det = aug.to_deterministic()\n        kps_aug3 = aug_det.augment_keypoints(keypoints)\n        kps_aug4 = aug_det.augment_keypoints(keypoints)\n\n        assert keypoints_equal(kps_aug1, kps_aug2), \\\n            \"Keypoints (1, 2) expected to be identical for %s\" % (aug.name,)\n\n        assert keypoints_equal(kps_aug3, kps_aug4), \\\n            \"Keypoints (3, 4) expected to be identical for %s\" % (aug.name,)\n\n        assert not keypoints_equal(kps_aug1, kps_aug3), \\\n            \"Keypoints (1, 3) expected to be different for %s\" % (aug.name,)\n\n\nclass TestKeypointAugmentation(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_many_augmenters(self):\n        keypoints = []\n        for y in sm.xrange(40//5):\n            for x in sm.xrange(60//5):\n                keypoints.append(ia.Keypoint(y=y*5, x=x*5))\n\n        keypoints_oi = ia.KeypointsOnImage(keypoints, shape=(40, 60, 3))\n        keypoints_oi_empty = ia.KeypointsOnImage([], shape=(40, 60, 3))\n\n        augs = [\n            iaa.Add((-5, 5), name=\"Add\"),\n            iaa.AddElementwise((-5, 5), name=\"AddElementwise\"),\n            iaa.AdditiveGaussianNoise(0.01*255, name=\"AdditiveGaussianNoise\"),\n            iaa.Multiply((0.95, 1.05), name=\"Multiply\"),\n            iaa.Dropout(0.01, name=\"Dropout\"),\n            iaa.CoarseDropout(0.01, size_px=6, name=\"CoarseDropout\"),\n            iaa.Invert(0.01, per_channel=True, name=\"Invert\"),\n            iaa.GaussianBlur(sigma=(0.95, 1.05), name=\"GaussianBlur\"),\n            iaa.AverageBlur((3, 5), name=\"AverageBlur\"),\n            iaa.MedianBlur((3, 5), name=\"MedianBlur\"),\n            iaa.Sharpen((0.0, 0.1), lightness=(1.0, 1.2), name=\"Sharpen\"),\n            iaa.Emboss(alpha=(0.0, 0.1), strength=(0.5, 1.5), name=\"Emboss\"),\n            iaa.EdgeDetect(alpha=(0.0, 0.1), name=\"EdgeDetect\"),\n            iaa.DirectedEdgeDetect(alpha=(0.0, 0.1), direction=0,\n                                   name=\"DirectedEdgeDetect\"),\n            iaa.Fliplr(0.5, name=\"Fliplr\"),\n            iaa.Flipud(0.5, name=\"Flipud\"),\n            iaa.Affine(translate_px=(-5, 5), name=\"Affine-translate-px\"),\n            iaa.Affine(translate_percent=(-0.05, 0.05),\n                       name=\"Affine-translate-percent\"),\n            iaa.Affine(rotate=(-20, 20), name=\"Affine-rotate\"),\n            iaa.Affine(shear=(-20, 20), name=\"Affine-shear\"),\n            iaa.Affine(scale=(0.9, 1.1), name=\"Affine-scale\"),\n            iaa.PiecewiseAffine(scale=(0.001, 0.005), name=\"PiecewiseAffine\"),\n            iaa.ElasticTransformation(alpha=(0.1, 0.2), sigma=(0.1, 0.2),\n                                      name=\"ElasticTransformation\"),\n            iaa.BlendAlpha((0.0, 0.1), iaa.Add(10), name=\"BlendAlpha\"),\n            iaa.BlendAlphaElementwise((0.0, 0.1), iaa.Add(10),\n                                      name=\"BlendAlphaElementwise\"),\n            iaa.BlendAlphaSimplexNoise(iaa.Add(10), name=\"BlendAlphaSimplexNoise\"),\n            iaa.BlendAlphaFrequencyNoise(exponent=(-2, 2), foreground=iaa.Add(10),\n                                         name=\"BlendAlphaSimplexNoise\"),\n            iaa.Superpixels(p_replace=0.01, n_segments=64),\n            iaa.Resize(0.5, name=\"Resize\"),\n            iaa.CropAndPad(px=(-10, 10), name=\"CropAndPad\"),\n            iaa.Pad(px=(0, 10), name=\"Pad\"),\n            iaa.Crop(px=(0, 10), name=\"Crop\")\n        ]\n\n        for aug in augs:\n            dss = []\n            for i in sm.xrange(10):\n                aug_det = aug.to_deterministic()\n\n                kp_fully_empty_aug = aug_det.augment_keypoints([])\n                assert kp_fully_empty_aug == []\n\n                kp_first_empty_aug = aug_det.augment_keypoints(keypoints_oi_empty)\n                assert len(kp_first_empty_aug.keypoints) == 0\n\n                kp_image = keypoints_oi.to_keypoint_image(size=5)\n                with assertWarns(self, iaa.SuspiciousSingleImageShapeWarning):\n                    kp_image_aug = aug_det.augment_image(kp_image)\n                kp_image_aug_rev = ia.KeypointsOnImage.from_keypoint_image(\n                    kp_image_aug,\n                    if_not_found_coords={\"x\": -9999, \"y\": -9999},\n                    nb_channels=1\n                )\n                kp_aug = aug_det.augment_keypoints([keypoints_oi])[0]\n                ds = []\n                assert len(kp_image_aug_rev.keypoints) == len(kp_aug.keypoints), (\n                    \"Lost keypoints for '%s' (%d vs expected %d)\" % (\n                        aug.name,\n                        len(kp_aug.keypoints),\n                        len(kp_image_aug_rev.keypoints))\n                )\n\n                gen = zip(kp_aug.keypoints, kp_image_aug_rev.keypoints)\n                for kp_pred, kp_pred_img in gen:\n                    kp_pred_lost = (kp_pred.x == -9999 and kp_pred.y == -9999)\n                    kp_pred_img_lost = (kp_pred_img.x == -9999\n                                        and kp_pred_img.y == -9999)\n\n                    if not kp_pred_lost and not kp_pred_img_lost:\n                        d = np.sqrt((kp_pred.x - kp_pred_img.x) ** 2\n                                    + (kp_pred.y - kp_pred_img.y) ** 2)\n                        ds.append(d)\n                dss.extend(ds)\n                if len(ds) == 0:\n                    print(\"[INFO] No valid keypoints found for '%s' \"\n                          \"in test_keypoint_augmentation()\" % (str(aug),))\n            assert np.average(dss) < 5.0, \\\n                \"Average distance too high (%.2f, with ds: %s)\" \\\n                % (np.average(dss), str(dss))\n\n\n# TODO move these tests to the individual augmenters?\ndef test_unusual_channel_numbers():\n    reseed()\n\n    images = [\n        (0, create_random_images((4, 16, 16))),\n        (1, create_random_images((4, 16, 16, 1))),\n        (2, create_random_images((4, 16, 16, 2))),\n        (4, create_random_images((4, 16, 16, 4))),\n        (5, create_random_images((4, 16, 16, 5))),\n        (10, create_random_images((4, 16, 16, 10))),\n        (20, create_random_images((4, 16, 16, 20)))\n    ]\n\n    augs = [\n        iaa.Add((-5, 5), name=\"Add\"),\n        iaa.AddElementwise((-5, 5), name=\"AddElementwise\"),\n        iaa.AdditiveGaussianNoise(0.01*255, name=\"AdditiveGaussianNoise\"),\n        iaa.Multiply((0.95, 1.05), name=\"Multiply\"),\n        iaa.Dropout(0.01, name=\"Dropout\"),\n        iaa.CoarseDropout(0.01, size_px=6, name=\"CoarseDropout\"),\n        iaa.Invert(0.01, per_channel=True, name=\"Invert\"),\n        iaa.GaussianBlur(sigma=(0.95, 1.05), name=\"GaussianBlur\"),\n        iaa.AverageBlur((3, 5), name=\"AverageBlur\"),\n        iaa.MedianBlur((3, 5), name=\"MedianBlur\"),\n        iaa.Sharpen((0.0, 0.1), lightness=(1.0, 1.2), name=\"Sharpen\"),\n        iaa.Emboss(alpha=(0.0, 0.1), strength=(0.5, 1.5), name=\"Emboss\"),\n        iaa.EdgeDetect(alpha=(0.0, 0.1), name=\"EdgeDetect\"),\n        iaa.DirectedEdgeDetect(alpha=(0.0, 0.1), direction=0,\n                               name=\"DirectedEdgeDetect\"),\n        iaa.Fliplr(0.5, name=\"Fliplr\"),\n        iaa.Flipud(0.5, name=\"Flipud\"),\n        iaa.Affine(translate_px=(-5, 5), name=\"Affine-translate-px\"),\n        iaa.Affine(translate_percent=(-0.05, 0.05),\n                   name=\"Affine-translate-percent\"),\n        iaa.Affine(rotate=(-20, 20), name=\"Affine-rotate\"),\n        iaa.Affine(shear=(-20, 20), name=\"Affine-shear\"),\n        iaa.Affine(scale=(0.9, 1.1), name=\"Affine-scale\"),\n        iaa.PiecewiseAffine(scale=(0.001, 0.005), name=\"PiecewiseAffine\"),\n        iaa.PerspectiveTransform(scale=(0.01, 0.10),\n                                 name=\"PerspectiveTransform\"),\n        iaa.ElasticTransformation(alpha=(0.1, 0.2), sigma=(0.1, 0.2),\n                                  name=\"ElasticTransformation\"),\n        iaa.Sequential([iaa.Add((-5, 5)), iaa.AddElementwise((-5, 5))]),\n        iaa.SomeOf(1, [iaa.Add((-5, 5)), iaa.AddElementwise((-5, 5))]),\n        iaa.OneOf([iaa.Add((-5, 5)), iaa.AddElementwise((-5, 5))]),\n        iaa.Sometimes(0.5, iaa.Add((-5, 5)), name=\"Sometimes\"),\n        iaa.Identity(name=\"Noop\"),\n        iaa.BlendAlpha((0.0, 0.1), iaa.Add(10), name=\"BlendAlpha\"),\n        iaa.BlendAlphaElementwise((0.0, 0.1), iaa.Add(10),\n                                  name=\"BlendAlphaElementwise\"),\n        iaa.BlendAlphaSimplexNoise(iaa.Add(10), name=\"BlendAlphaSimplexNoise\"),\n        iaa.BlendAlphaFrequencyNoise(exponent=(-2, 2),\n                                     foreground=iaa.Add(10),\n                                     name=\"BlendAlphaSimplexNoise\"),\n        iaa.Superpixels(p_replace=0.01, n_segments=64),\n        iaa.Resize({\"height\": 4, \"width\": 4}, name=\"Resize\"),\n        iaa.CropAndPad(px=(-10, 10), name=\"CropAndPad\"),\n        iaa.Pad(px=(0, 10), name=\"Pad\"),\n        iaa.Crop(px=(0, 10), name=\"Crop\")\n    ]\n\n    for aug in augs:\n        for (nb_channels, images_c) in images:\n            if aug.name != \"Resize\":\n                images_aug = aug.augment_images(images_c)\n                assert images_aug.shape == images_c.shape\n                image_aug = aug.augment_image(images_c[0])\n                assert image_aug.shape == images_c[0].shape\n            else:\n                images_aug = aug.augment_images(images_c)\n                image_aug = aug.augment_image(images_c[0])\n                if images_c.ndim == 3:\n                    assert images_aug.shape == (4, 4, 4)\n                    assert image_aug.shape == (4, 4)\n                else:\n                    assert images_aug.shape == (4, 4, 4, images_c.shape[3])\n                    assert image_aug.shape == (4, 4, images_c.shape[3])\n\n\n# TODO move these tests to the individual augmenters?\ndef test_dtype_preservation():\n    reseed()\n\n    size = (4, 16, 16, 3)\n    images = [\n        np.random.uniform(0, 255, size).astype(np.uint8),\n        np.random.uniform(0, 65535, size).astype(np.uint16),\n        np.random.uniform(0, 4294967295, size).astype(np.uint32),\n        np.random.uniform(-128, 127, size).astype(np.int16),\n        np.random.uniform(-32768, 32767, size).astype(np.int32),\n        np.random.uniform(0.0, 1.0, size).astype(np.float32),\n        np.random.uniform(-1000.0, 1000.0, size).astype(np.float16),\n        np.random.uniform(-1000.0, 1000.0, size).astype(np.float32),\n        np.random.uniform(-1000.0, 1000.0, size).astype(np.float64)\n    ]\n\n    default_dtypes = set([arr.dtype for arr in images])\n    # Some dtypes are here removed per augmenter, because the respective\n    # augmenter does not support them. This test currently only checks whether\n    # dtypes are preserved from in- to output for all dtypes that are supported\n    # per augmenter.\n    # dtypes are here removed via list comprehension instead of\n    # `default_dtypes - set([dtype])`, because the latter one simply never\n    # removed the dtype(s) for some reason\n\n    def _not_dts(dts):\n        return [dt for dt in default_dtypes if dt not in dts]\n\n    augs = [\n        (iaa.Add((-5, 5), name=\"Add\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.AddElementwise((-5, 5), name=\"AddElementwise\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.AdditiveGaussianNoise(0.01*255, name=\"AdditiveGaussianNoise\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.Multiply((0.95, 1.05), name=\"Multiply\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.Dropout(0.01, name=\"Dropout\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.CoarseDropout(0.01, size_px=6, name=\"CoarseDropout\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.Invert(0.01, per_channel=True, name=\"Invert\"),\n         default_dtypes),\n        (iaa.GaussianBlur(sigma=(0.95, 1.05), name=\"GaussianBlur\"),\n         _not_dts([np.float16])),\n        (iaa.AverageBlur((3, 5), name=\"AverageBlur\"),\n         _not_dts([np.uint32, np.int32, np.float16])),\n        (iaa.MedianBlur((3, 5), name=\"MedianBlur\"),\n         _not_dts([np.uint32, np.int32, np.float16, np.float64])),\n        (iaa.BilateralBlur((3, 5), name=\"BilateralBlur\"),\n         _not_dts([np.uint16, np.uint32, np.int16, np.int32, np.float16,\n                   np.float64])),\n        (iaa.Sharpen((0.0, 0.1), lightness=(1.0, 1.2), name=\"Sharpen\"),\n         _not_dts([np.uint32, np.int32, np.float16, np.uint32])),\n        (iaa.Emboss(alpha=(0.0, 0.1), strength=(0.5, 1.5), name=\"Emboss\"),\n         _not_dts([np.uint32, np.int32, np.float16, np.uint32])),\n        (iaa.EdgeDetect(alpha=(0.0, 0.1), name=\"EdgeDetect\"),\n         _not_dts([np.uint32, np.int32, np.float16, np.uint32])),\n        (iaa.DirectedEdgeDetect(alpha=(0.0, 0.1), direction=0,\n                                name=\"DirectedEdgeDetect\"),\n         _not_dts([np.uint32, np.int32, np.float16, np.uint32])),\n        (iaa.Fliplr(0.5, name=\"Fliplr\"), default_dtypes),\n        (iaa.Flipud(0.5, name=\"Flipud\"), default_dtypes),\n        (iaa.Affine(translate_px=(-5, 5), name=\"Affine-translate-px\"),\n         _not_dts([np.uint32, np.int32])),\n        (iaa.Affine(translate_percent=(-0.05, 0.05),\n                    name=\"Affine-translate-percent\"),\n         _not_dts([np.uint32, np.int32])),\n        (iaa.Affine(rotate=(-20, 20), name=\"Affine-rotate\"),\n         _not_dts([np.uint32, np.int32])),\n        (iaa.Affine(shear=(-20, 20), name=\"Affine-shear\"),\n         _not_dts([np.uint32, np.int32])),\n        (iaa.Affine(scale=(0.9, 1.1), name=\"Affine-scale\"),\n         _not_dts([np.uint32, np.int32])),\n        (iaa.PiecewiseAffine(scale=(0.001, 0.005), name=\"PiecewiseAffine\"),\n         default_dtypes),\n        (iaa.ElasticTransformation(alpha=(0.1, 0.2), sigma=(0.1, 0.2),\n                                   name=\"ElasticTransformation\"),\n         _not_dts([np.float16])),\n        (iaa.Sequential([iaa.Identity(), iaa.Identity()],\n                        name=\"SequentialNoop\"),\n         default_dtypes),\n        (iaa.SomeOf(1, [iaa.Identity(), iaa.Identity()], name=\"SomeOfNoop\"),\n         default_dtypes),\n        (iaa.OneOf([iaa.Identity(), iaa.Identity()], name=\"OneOfNoop\"),\n         default_dtypes),\n        (iaa.Sometimes(0.5, iaa.Identity(), name=\"SometimesNoop\"),\n         default_dtypes),\n        (iaa.Sequential([iaa.Add((-5, 5)), iaa.AddElementwise((-5, 5))],\n                        name=\"Sequential\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.SomeOf(1,\n                    [iaa.Add((-5, 5)), iaa.AddElementwise((-5, 5))],\n                    name=\"SomeOf\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.OneOf([iaa.Add((-5, 5)), iaa.AddElementwise((-5, 5))],\n                   name=\"OneOf\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.Sometimes(0.5, iaa.Add((-5, 5)), name=\"Sometimes\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.Identity(name=\"Identity\"), default_dtypes),\n        (iaa.BlendAlpha((0.0, 0.1), iaa.Identity(), name=\"BlendAlphaIdentity\"),\n         _not_dts([np.float64])),  # float64 requires float128 support\n        (iaa.BlendAlphaElementwise((0.0, 0.1), iaa.Identity(),\n                                   name=\"BlendAlphaElementwiseIdentity\"),\n         _not_dts([np.float64])),  # float64 requires float128 support\n        (iaa.BlendAlphaSimplexNoise(iaa.Identity(),\n                                    name=\"BlendAlphaSimplexNoiseIdentity\"),\n         _not_dts([np.float64])),  # float64 requires float128 support\n        (iaa.BlendAlphaFrequencyNoise(exponent=(-2, 2),\n                                      foreground=iaa.Identity(),\n                                      name=\"BlendAlphaFrequencyNoiseIdentity\"),\n         _not_dts([np.float64])),\n        (iaa.BlendAlpha((0.0, 0.1), iaa.Add(10), name=\"BlendAlpha\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.BlendAlphaElementwise((0.0, 0.1), iaa.Add(10),\n                                   name=\"BlendAlphaElementwise\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.BlendAlphaSimplexNoise(iaa.Add(10), name=\"BlendAlphaSimplexNoise\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.BlendAlphaFrequencyNoise(exponent=(-2, 2),\n                                      foreground=iaa.Add(10),\n                                      name=\"BlendAlphaFrequencyNoise\"),\n         _not_dts([np.uint32, np.int32, np.float64])),\n        (iaa.Superpixels(p_replace=0.01, n_segments=64),\n         _not_dts([np.float16, np.float32, np.float64])),\n        (iaa.Resize({\"height\": 4, \"width\": 4}, name=\"Resize\"),\n         _not_dts([np.uint16, np.uint32, np.int16, np.int32, np.float32,\n                   np.float16, np.float64])),\n        (iaa.CropAndPad(px=(-10, 10), name=\"CropAndPad\"),\n         _not_dts([np.uint16, np.uint32, np.int16, np.int32, np.float32,\n                   np.float16, np.float64])),\n        (iaa.Pad(px=(0, 10), name=\"Pad\"),\n         _not_dts([np.uint16, np.uint32, np.int16, np.int32, np.float32,\n                   np.float16, np.float64])),\n        (iaa.Crop(px=(0, 10), name=\"Crop\"),\n         _not_dts([np.uint16, np.uint32, np.int16, np.int32, np.float32,\n                   np.float16, np.float64]))\n    ]\n\n    for (aug, allowed_dtypes) in augs:\n        for images_i in images:\n            if images_i.dtype in allowed_dtypes:\n                images_aug = aug.augment_images(images_i)\n                assert images_aug.dtype == images_i.dtype\n"
  },
  {
    "path": "test/augmenters/test_overlay.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport warnings\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\n\nimport imgaug.augmenters as iaa\nimport imgaug.augmenters.overlay as overlay\n\n\nclass Test_blend_alpha(unittest.TestCase):\n    def test_warns_that_it_is_deprecated(self):\n        image_fg = np.zeros((1, 1, 3), dtype=np.uint8)\n        image_bg = np.copy(image_fg)\n        alpha = 1\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            _ = overlay.blend_alpha(image_fg, image_bg, alpha)\n\n        assert len(caught_warnings) == 1\n        assert (\n            \"imgaug.augmenters.blend.blend_alpha\"\n            in str(caught_warnings[-1].message)\n        )\n\n\nclass TestAlpha(unittest.TestCase):\n    def test_warns_that_it_is_deprecated(self):\n        children_fg = iaa.Identity()\n        factor = 1\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            _ = overlay.Alpha(factor, children_fg)\n\n        assert len(caught_warnings) == 2\n        assert (\n            \"imgaug.augmenters.blend.BlendAlpha\"\n            in str(caught_warnings[0].message)\n        )\n\n\nclass TestAlphaElementwise(unittest.TestCase):\n    def test_warns_that_it_is_deprecated(self):\n        children_fg = iaa.Identity()\n        factor = 1\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            _ = overlay.AlphaElementwise(factor, children_fg)\n\n        assert len(caught_warnings) == 2\n        assert (\n            \"imgaug.augmenters.blend.BlendAlphaElementwise\"\n            in str(caught_warnings[0].message)\n        )\n\n\nclass TestSimplexNoiseAlpha(unittest.TestCase):\n    def test_warns_that_it_is_deprecated(self):\n        children_fg = iaa.Identity()\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            _ = overlay.SimplexNoiseAlpha(children_fg)\n\n        assert len(caught_warnings) == 2\n        assert (\n            \"imgaug.augmenters.blend.BlendAlphaSimplexNoise\"\n            in str(caught_warnings[0].message)\n        )\n\n\nclass TestFrequencyNoiseAlpha(unittest.TestCase):\n    def test_warns_that_it_is_deprecated(self):\n        children_fg = iaa.Identity()\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            _ = overlay.FrequencyNoiseAlpha(first=children_fg)\n\n        assert len(caught_warnings) == 2\n        assert (\n            \"imgaug.augmenters.blend.BlendAlphaFrequencyNoise\"\n            in str(caught_warnings[0].message)\n        )\n"
  },
  {
    "path": "test/augmenters/test_pillike.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport functools\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport PIL.Image\nimport PIL.ImageOps\nimport PIL.ImageEnhance\nimport PIL.ImageFilter\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import random as iarandom\nfrom imgaug.testutils import reseed, runtest_pickleable_uint8_img, assertWarns\n\n\ndef _test_shape_hw(func):\n    img = np.arange(20*10).reshape((20, 10)).astype(np.uint8)\n\n    observed = func(np.copy(img))\n\n    expected = func(\n        np.tile(np.copy(img)[:, :, np.newaxis], (1, 1, 3)),\n    )[:, :, 0]\n    assert observed.dtype.name == \"uint8\"\n    assert observed.shape == (20, 10)\n    assert np.array_equal(observed, expected)\n\n\ndef _test_shape_hw1(func):\n    img = np.arange(20*10*1).reshape((20, 10, 1)).astype(np.uint8)\n\n    observed = func(np.copy(img))\n\n    expected = func(\n        np.tile(np.copy(img), (1, 1, 3)),\n    )[:, :, 0:1]\n    assert observed.dtype.name == \"uint8\"\n    assert observed.shape == (20, 10, 1)\n    assert np.array_equal(observed, expected)\n\n\nclass Test_solarize_(unittest.TestCase):\n    @mock.patch(\"imgaug.augmenters.arithmetic.invert_\")\n    def test_mocked_defaults(self, mock_sol):\n        arr = np.zeros((1,), dtype=np.uint8)\n        mock_sol.return_value = \"foo\"\n\n        observed = iaa.pillike.solarize_(arr)\n\n        args = mock_sol.call_args_list[0][0]\n        kwargs = mock_sol.call_args_list[0][1]\n        assert args[0] is arr\n        assert kwargs[\"threshold\"] == 128\n        assert observed == \"foo\"\n\n    @mock.patch(\"imgaug.augmenters.arithmetic.invert_\")\n    def test_mocked(self, mock_sol):\n        arr = np.zeros((1,), dtype=np.uint8)\n        mock_sol.return_value = \"foo\"\n\n        observed = iaa.pillike.solarize_(arr, threshold=5)\n\n        args = mock_sol.call_args_list[0][0]\n        kwargs = mock_sol.call_args_list[0][1]\n        assert args[0] is arr\n        assert kwargs[\"threshold\"] == 5\n        assert observed == \"foo\"\n\n    def test_image_shape_hw(self):\n        func = functools.partial(iaa.pillike.solarize_, threshold=5)\n        _test_shape_hw(func)\n\n    def test_image_shape_hw1(self):\n        func = functools.partial(iaa.pillike.solarize_, threshold=5)\n        _test_shape_hw1(func)\n\n\nclass Test_solarize(unittest.TestCase):\n    def test_compare_with_pil(self):\n        def _solarize_pil(image, threshold):\n            img = PIL.Image.fromarray(image)\n            return np.asarray(PIL.ImageOps.solarize(img, threshold))\n\n        images = [\n            np.mod(np.arange(20*20*3), 255).astype(np.uint8)\\\n                .reshape((20, 20, 3)),\n            iarandom.RNG(0).integers(0, 256, size=(1, 1, 3), dtype=\"uint8\"),\n            iarandom.RNG(1).integers(0, 256, size=(20, 20, 3), dtype=\"uint8\"),\n            iarandom.RNG(2).integers(0, 256, size=(40, 40, 3), dtype=\"uint8\"),\n            iarandom.RNG(0).integers(0, 256, size=(20, 20), dtype=\"uint8\")\n        ]\n\n        for image_idx, image in enumerate(images):\n            for threshold in np.arange(256):\n                with self.subTest(image_idx=image_idx, threshold=threshold):\n                    image_pil = _solarize_pil(image, threshold)\n                    image_iaa = iaa.pillike.solarize(image, threshold)\n                    assert np.array_equal(image_pil, image_iaa)\n\n    def test_image_shape_hw(self):\n        func = functools.partial(iaa.pillike.solarize, threshold=5)\n        _test_shape_hw(func)\n\n    def test_image_shape_hw1(self):\n        func = functools.partial(iaa.pillike.solarize, threshold=5)\n        _test_shape_hw1(func)\n\n\nclass Test_posterize(unittest.TestCase):\n    def test_by_comparison_with_pil(self):\n        image = np.arange(64*64*3).reshape((64, 64, 3))\n        image = np.mod(image, 255).astype(np.uint8)\n        for nb_bits in [1, 2, 3, 4, 5, 6, 7, 8]:\n            image_iaa = iaa.pillike.posterize(np.copy(image), nb_bits)\n            image_pil = np.asarray(\n                PIL.ImageOps.posterize(\n                    PIL.Image.fromarray(image),\n                    nb_bits\n                )\n            )\n\n            assert np.array_equal(image_iaa, image_pil)\n\n    def test_image_shape_hw(self):\n        func = functools.partial(iaa.pillike.posterize, bits=2)\n        _test_shape_hw(func)\n\n    def test_image_shape_hw1(self):\n        func = functools.partial(iaa.pillike.posterize, bits=2)\n        _test_shape_hw1(func)\n\n\nclass Test_equalize(unittest.TestCase):\n    def test_by_comparison_with_pil(self):\n        shapes = [\n            (1, 1),\n            (2, 1),\n            (1, 2),\n            (2, 2),\n            (5, 5),\n            (10, 5),\n            (5, 10),\n            (10, 10),\n            (20, 20),\n            (100, 100),\n            (100, 200),\n            (200, 100),\n            (200, 200)\n        ]\n        shapes = shapes + [shape + (3,) for shape in shapes]\n\n        rng = iarandom.RNG(0)\n        images = [rng.integers(0, 255, size=shape).astype(np.uint8)\n                  for shape in shapes]\n        images = images + [\n            np.full((10, 10), 0, dtype=np.uint8),\n            np.full((10, 10), 128, dtype=np.uint8),\n            np.full((10, 10), 255, dtype=np.uint8)\n        ]\n\n        for i, image in enumerate(images):\n            mask_vals = [False, True] if image.size >= (100*100) else [False]\n            for use_mask in mask_vals:\n                with self.subTest(image_idx=i, shape=image.shape,\n                                  use_mask=use_mask):\n                    mask_np = None\n                    mask_pil = None\n                    if use_mask:\n                        mask_np = np.zeros(image.shape[0:2], dtype=np.uint8)\n                        mask_np[25:75, 25:75] = 1\n                        mask_pil = PIL.Image.fromarray(mask_np).convert(\"L\")\n\n                    image_iaa = iaa.pillike.equalize(image, mask=mask_np)\n                    image_pil = np.asarray(\n                        PIL.ImageOps.equalize(\n                            PIL.Image.fromarray(image),\n                            mask=mask_pil\n                        )\n                    )\n\n                    assert np.array_equal(image_iaa, image_pil)\n\n    def test_unusual_channel_numbers(self):\n        nb_channels_lst = [1, 2, 4, 5, 512, 513]\n        for nb_channels in nb_channels_lst:\n            for size in [20, 100]:\n                with self.subTest(nb_channels=nb_channels,\n                                  size=size):\n                    shape = (size, size, nb_channels)\n                    image = iarandom.RNG(0).integers(50, 150, size=shape)\n                    image = image.astype(np.uint8)\n\n                    image_aug = iaa.pillike.equalize(image)\n\n                    if size > 1:\n                        channelwise_sums = np.sum(image_aug, axis=(0, 1))\n                        assert np.all(channelwise_sums > 0)\n                    assert np.min(image_aug) < 50\n                    assert np.max(image_aug) > 150\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = iaa.pillike.equalize(image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_image_shape_hw(self):\n        func = functools.partial(iaa.pillike.equalize)\n        _test_shape_hw(func)\n\n    # already covered by unusal channel numbers test, but we run this one here\n    # anyways for consistency with other tests and because it works a bit\n    # different\n    def test_image_shape_hw1(self):\n        func = functools.partial(iaa.pillike.equalize)\n        _test_shape_hw1(func)\n\n\nclass Test_autocontrast(unittest.TestCase):\n    def test_by_comparison_with_pil(self):\n        rng = iarandom.RNG(0)\n        shapes = [\n            (1, 1),\n            (10, 10),\n            (1, 1, 3),\n            (1, 2, 3),\n            (2, 1, 3),\n            (2, 2, 3),\n            (5, 3, 3),\n            (10, 5, 3),\n            (5, 10, 3),\n            (10, 10, 3),\n            (20, 10, 3),\n            (20, 40, 3),\n            (50, 60, 3),\n            (100, 100, 3),\n            (200, 100, 3)\n        ]\n        images = [\n            rng.integers(0, 255, size=shape).astype(np.uint8)\n            for shape in shapes\n        ]\n        images = (\n            images\n            + [\n                np.full((1, 1, 3), 0, dtype=np.uint8),\n                np.full((1, 1, 3), 255, dtype=np.uint8),\n                np.full((20, 20, 3), 0, dtype=np.uint8),\n                np.full((20, 20, 3), 255, dtype=np.uint8)\n            ]\n        )\n\n        cutoffs = [0, 1, 2, 10, 50, 90, 99, 100]\n        ignores = [None, 0, 1, 100, 255, [0, 1], [5, 10, 50], [99, 100]]\n\n        for cutoff in cutoffs:\n            for ignore in ignores:\n                for i, image in enumerate(images):\n                    with self.subTest(cutoff=cutoff, ignore=ignore,\n                                      image_idx=i, image_shape=image.shape):\n                        result_pil = np.asarray(\n                            PIL.ImageOps.autocontrast(\n                                PIL.Image.fromarray(image),\n                                cutoff=cutoff,\n                                ignore=ignore\n                            )\n                        )\n                        result_iaa = iaa.pillike.autocontrast(image,\n                                                              cutoff=cutoff,\n                                                              ignore=ignore)\n                        assert np.array_equal(result_pil, result_iaa)\n\n    def test_unusual_channel_numbers(self):\n        nb_channels_lst = [1, 2, 4, 5, 512, 513]\n        for nb_channels in nb_channels_lst:\n            for size in [20]:\n                for cutoff in [0, 1, 10]:\n                    with self.subTest(nb_channels=nb_channels,\n                                      size=size,\n                                      cutoff=cutoff):\n                        shape = (size, size, nb_channels)\n                        image = iarandom.RNG(0).integers(50, 150, size=shape)\n                        image = image.astype(np.uint8)\n\n                        image_aug = iaa.pillike.autocontrast(image,\n                                                             cutoff=cutoff)\n\n                        if size > 1:\n                            channelwise_sums = np.sum(image_aug, axis=(0, 1))\n                            assert np.all(channelwise_sums > 0)\n                        assert np.min(image_aug) < 50\n                        assert np.max(image_aug) > 150\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            for cutoff in [0, 1, 10]:\n                for ignore in [None, 0, 1, [0, 1, 10]]:\n                    with self.subTest(shape=shape, cutoff=cutoff,\n                                      ignore=ignore):\n                        image = np.zeros(shape, dtype=np.uint8)\n\n                        image_aug = iaa.pillike.autocontrast(image,\n                                                             cutoff=cutoff,\n                                                             ignore=ignore)\n\n                        assert image_aug.dtype.name == \"uint8\"\n                        assert image_aug.shape == shape\n\n    def test_image_shape_hw(self):\n        func = functools.partial(iaa.pillike.autocontrast)\n        _test_shape_hw(func)\n\n    # already covered by unusal channel numbers test, but we run this one here\n    # anyways for consistency with other tests and because it works a bit\n    # different\n    def test_image_shape_hw1(self):\n        func = functools.partial(iaa.pillike.autocontrast)\n        _test_shape_hw1(func)\n\n\n# TODO add test for unusual channel numbers\nclass _TestEnhanceFunc(unittest.TestCase):\n    def _test_by_comparison_with_pil(\n            self, func, cls,\n            factors=(0.0, 0.01, 0.1, 0.5, 0.95, 0.99, 1.0, 1.05, 1.5, 2.0,\n                     3.0)):\n        shapes = [(224, 224, 3), (32, 32, 3), (16, 8, 3), (1, 1, 3),\n                  (32, 32, 4)]\n        seeds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n        for seed in seeds:\n            for shape in shapes:\n                for factor in factors:\n                    with self.subTest(shape=shape, seed=seed, factor=factor):\n                        image = iarandom.RNG(seed).integers(\n                            0, 256, size=shape, dtype=\"uint8\")\n\n                        image_iaa = func(image, factor)\n                        image_pil = np.asarray(\n                            cls(\n                                PIL.Image.fromarray(image)\n                            ).enhance(factor)\n                        )\n\n                        assert np.array_equal(image_iaa, image_pil)\n\n    def _test_zero_sized_axes(self, func,\n                              factors=(0.0, 0.4, 1.0)):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            for factor in factors:\n                with self.subTest(shape=shape, factor=factor):\n                    image = np.zeros(shape, dtype=np.uint8)\n\n                    image_aug = func(image, factor=factor)\n\n                    assert image_aug.dtype.name == \"uint8\"\n                    assert image_aug.shape == shape\n\n    @classmethod\n    def _test_image_shape_hw(self, func):\n        func = functools.partial(func, factor=0.2)\n        _test_shape_hw(func)\n\n    @classmethod\n    def _test_image_shape_hw1(self, func):\n        func = functools.partial(func, factor=0.2)\n        _test_shape_hw1(func)\n\n\nclass Test_enhance_color(_TestEnhanceFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.enhance_color,\n                                          PIL.ImageEnhance.Color)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.enhance_color)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.enhance_color)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.enhance_color)\n\n\nclass Test_enhance_contrast(_TestEnhanceFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.enhance_contrast,\n                                          PIL.ImageEnhance.Contrast)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.enhance_contrast)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.enhance_contrast)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.enhance_contrast)\n\n\nclass Test_enhance_brightness(_TestEnhanceFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.enhance_brightness,\n                                          PIL.ImageEnhance.Brightness)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.enhance_brightness)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.enhance_brightness)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.enhance_brightness)\n\n\nclass Test_enhance_sharpness(_TestEnhanceFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.enhance_sharpness,\n                                          PIL.ImageEnhance.Sharpness)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.enhance_brightness,\n                                   factors=[0.0, 0.4, 1.0, 1.5, 2.0])\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.enhance_sharpness)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.enhance_sharpness)\n\n\nclass _TestFilterFunc(unittest.TestCase):\n    def _test_by_comparison_with_pil(self, func, pil_kernel):\n        shapes = [(224, 224, 3), (32, 32, 3), (16, 8, 3), (1, 1, 3),\n                  (32, 32, 4)]\n        seeds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n        for seed in seeds:\n            for shape in shapes:\n                with self.subTest(shape=shape, seed=seed):\n                    image = iarandom.RNG(seed).integers(\n                        0, 256, size=shape, dtype=\"uint8\")\n\n                    image_iaa = func(image)\n                    image_pil = np.asarray(\n                        PIL.Image.fromarray(image).filter(pil_kernel)\n                    )\n\n                    assert np.array_equal(image_iaa, image_pil)\n\n    def _test_zero_sized_axes(self, func):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n\n                image_aug = func(image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    @classmethod\n    def _test_image_shape_hw(self, func):\n        _test_shape_hw(func)\n\n    @classmethod\n    def _test_image_shape_hw1(self, func):\n        _test_shape_hw1(func)\n\n\nclass Test_filter_blur(_TestFilterFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.filter_blur,\n                                          PIL.ImageFilter.BLUR)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.filter_blur)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.filter_blur)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.filter_blur)\n\n\nclass Test_filter_smooth(_TestFilterFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.filter_smooth,\n                                          PIL.ImageFilter.SMOOTH)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.filter_smooth)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.filter_smooth)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.filter_smooth)\n\n\nclass Test_filter_smooth_more(_TestFilterFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.filter_smooth_more,\n                                          PIL.ImageFilter.SMOOTH_MORE)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.filter_smooth_more)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.filter_smooth_more)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.filter_smooth_more)\n\n\nclass Test_filter_edge_enhance(_TestFilterFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.filter_edge_enhance,\n                                          PIL.ImageFilter.EDGE_ENHANCE)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.filter_edge_enhance)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.filter_edge_enhance)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.filter_edge_enhance)\n\n\nclass Test_filter_edge_enhance_more(_TestFilterFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.filter_edge_enhance_more,\n                                          PIL.ImageFilter.EDGE_ENHANCE_MORE)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.filter_edge_enhance_more)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.filter_edge_enhance_more)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.filter_edge_enhance_more)\n\n\nclass Test_filter_find_edges(_TestFilterFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.filter_find_edges,\n                                          PIL.ImageFilter.FIND_EDGES)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.filter_find_edges)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.filter_find_edges)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.filter_find_edges)\n\n\nclass Test_filter_contour(_TestFilterFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.filter_contour,\n                                          PIL.ImageFilter.CONTOUR)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.filter_contour)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.filter_contour)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.filter_contour)\n\n\nclass Test_filter_emboss(_TestFilterFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.filter_emboss,\n                                          PIL.ImageFilter.EMBOSS)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.filter_emboss)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.filter_emboss)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.filter_emboss)\n\n\nclass Test_filter_sharpen(_TestFilterFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.filter_sharpen,\n                                          PIL.ImageFilter.SHARPEN)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.filter_sharpen)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.filter_sharpen)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.filter_sharpen)\n\n\nclass Test_filter_detail(_TestFilterFunc):\n    def test_by_comparison_with_pil(self):\n        self._test_by_comparison_with_pil(iaa.pillike.filter_detail,\n                                          PIL.ImageFilter.DETAIL)\n\n    def test_zero_sized_axes(self):\n        self._test_zero_sized_axes(iaa.pillike.filter_detail)\n\n    def test_image_shape_hw(self):\n        self._test_image_shape_hw(iaa.pillike.filter_detail)\n\n    def test_image_shape_hw1(self):\n        self._test_image_shape_hw1(iaa.pillike.filter_detail)\n\n\nclass Test_warp_affine(unittest.TestCase):\n    def _test_aff_by_comparison_with_pil(self, arg_name, arg_values,\n                                         matrix_gen):\n        shapes = [(64, 64, 3), (32, 32, 3), (16, 8, 3), (1, 1, 3),\n                  (32, 32, 4)]\n        seeds = [1, 2, 3]\n        fillcolors = [None, 0, 128, (0, 255, 0)]\n        for shape in shapes:\n            for seed in seeds:\n                for fillcolor in fillcolors:\n                    for arg_value in arg_values:\n                        with self.subTest(shape=shape, seed=seed,\n                                          fillcolor=fillcolor,\n                                          **{arg_name: arg_value}):\n                            image = iarandom.RNG(seed).integers(\n                                0, 256, size=shape, dtype=\"uint8\")\n\n                            matrix = matrix_gen(arg_value)\n\n                            image_warped = iaa.pillike.warp_affine(\n                                image,\n                                fillcolor=fillcolor,\n                                center=(0.0, 0.0),\n                                **{arg_name: arg_value})\n\n                            image_warped_exp = np.asarray(\n                                PIL.Image.fromarray(\n                                    image\n                                ).transform(shape[0:2][::-1],\n                                            PIL.Image.AFFINE,\n                                            matrix[:2, :].flat,\n                                            fillcolor=fillcolor)\n                            )\n\n                            assert np.array_equal(image_warped,\n                                                  image_warped_exp)\n\n    def test_scale_x_by_comparison_with_pil(self):\n        def _matrix_gen(scale):\n            return np.float32([\n                [1/scale, 0, 0],\n                [0, 1, 0],\n                [0, 0, 1]\n            ])\n\n        self._test_aff_by_comparison_with_pil(\n            \"scale_x\",\n            [0.01, 0.1, 0.9, 1.0, 1.5, 3.0],\n            _matrix_gen\n        )\n\n    def test_scale_y_by_comparison_with_pil(self):\n        def _matrix_gen(scale):\n            return np.float32([\n                [1, 0, 0],\n                [0, 1/scale, 0],\n                [0, 0, 1]\n            ])\n\n        self._test_aff_by_comparison_with_pil(\n            \"scale_y\",\n            [0.01, 0.1, 0.9, 1.0, 1.5, 3.0],\n            _matrix_gen\n        )\n\n    def test_translate_x_by_comparison_with_pil(self):\n        def _matrix_gen(translate):\n            return np.float32([\n                [1, 0, -translate],\n                [0, 1, 0],\n                [0, 0, 1]\n            ])\n\n        self._test_aff_by_comparison_with_pil(\n            \"translate_x_px\",\n            [-50, -10, -1, 0, 1, 10, 50],\n            _matrix_gen\n        )\n\n    def test_translate_y_by_comparison_with_pil(self):\n        def _matrix_gen(translate):\n            return np.float32([\n                [1, 0, 0],\n                [0, 1, -translate],\n                [0, 0, 1]\n            ])\n\n        self._test_aff_by_comparison_with_pil(\n            \"translate_y_px\",\n            [-50, -10, -1, 0, 1, 10, 50],\n            _matrix_gen\n        )\n\n    def test_rotate_by_comparison_with_pil(self):\n        def _matrix_gen(rotate):\n            r = np.deg2rad(rotate)\n            return np.float32([\n                [np.cos(r), np.sin(r), 0],\n                [-np.sin(r), np.cos(r), 0],\n                [0, 0, 1]\n            ])\n\n        self._test_aff_by_comparison_with_pil(\n            \"rotate_deg\",\n            [-50, -10, -1, 0, 1, 10, 50],\n            _matrix_gen\n        )\n\n    def test_shear_x_by_comparison_with_pil(self):\n        def _matrix_gen(shear):\n            s = (-1) * np.deg2rad(shear)\n            return np.float32([\n                [1, np.tanh(s), 0],\n                [0, 1, 0],\n                [0, 0, 1]\n            ])\n\n        self._test_aff_by_comparison_with_pil(\n            \"shear_x_deg\",\n            [-50, -10, -1, 0, 1, 10, 50],\n            _matrix_gen\n        )\n\n    def test_shear_y_by_comparison_with_pil(self):\n        def _matrix_gen(shear):\n            s = (-1) * np.deg2rad(shear)\n            return np.float32([\n                [1, 0, 0],\n                [np.tanh(s), 1, 0],\n                [0, 0, 1]\n            ])\n\n        self._test_aff_by_comparison_with_pil(\n            \"shear_y_deg\",\n            [-50, -10, -1, 0, 1, 10, 50],\n            _matrix_gen\n        )\n\n    def test_scale_x(self):\n        image = np.zeros((100, 100, 3), dtype=np.uint8)\n        image[50, 60] = 255\n\n        image_aug = iaa.pillike.warp_affine(image, scale_x=1.5)\n\n        y, x = np.unravel_index(np.argmax(image_aug[..., 0]),\n                                image_aug.shape[0:2])\n\n        assert 50 - 1 <= y <= 50 + 1\n        assert x > 60\n\n    def test_scale_y(self):\n        image = np.zeros((100, 100, 3), dtype=np.uint8)\n        image[60, 50] = 255\n\n        image_aug = iaa.pillike.warp_affine(image, scale_y=1.5)\n\n        y, x = np.unravel_index(np.argmax(image_aug[..., 0]),\n                                image_aug.shape[0:2])\n\n        assert 50 - 1 <= x <= 50 + 1\n        assert y > 60\n\n    def test_translate_x_px(self):\n        image = np.zeros((20, 20, 3), dtype=np.uint8)\n        image[10, 15] = 255\n\n        image_aug = iaa.pillike.warp_affine(image, translate_x_px=1)\n\n        assert image_aug[10, 15, 0] == 0\n        assert image_aug[10, 16, 0] == 255\n        assert np.all(image_aug[0, :] == 0)\n\n    def test_translate_y_px(self):\n        image = np.zeros((20, 20, 3), dtype=np.uint8)\n        image[15, 10] = 255\n\n        image_aug = iaa.pillike.warp_affine(image, translate_y_px=1)\n\n        assert image_aug[15, 10, 0] == 0\n        assert image_aug[16, 10, 0] == 255\n        assert np.all(image_aug[:, 0] == 0)\n\n    def test_rotate(self):\n        image = np.zeros((20, 20, 3), dtype=np.uint8)\n        image[0, 10] = 255\n\n        image_aug = iaa.pillike.warp_affine(image,\n                                            rotate_deg=45,\n                                            center=(0.0, 0.0))\n\n        assert image_aug[7, 7, 0] == 255\n\n    def test_shear_x(self):\n        image = np.zeros((20, 20, 3), dtype=np.uint8)\n        image[5, 10] = 255\n\n        image_aug = iaa.pillike.warp_affine(image,\n                                            shear_x_deg=20,\n                                            center=(0.0, 0.0))\n\n        y, x = np.unravel_index(np.argmax(image_aug[..., 0]),\n                                image_aug.shape[0:2])\n\n        assert y == 5\n        assert x > 10\n\n    def test_shear_y(self):\n        image = np.zeros((20, 20, 3), dtype=np.uint8)\n        image[10, 15] = 255\n\n        image_aug = iaa.pillike.warp_affine(image,\n                                            shear_y_deg=20,\n                                            center=(0.0, 0.0))\n\n        y, x = np.unravel_index(np.argmax(image_aug[..., 0]),\n                                image_aug.shape[0:2])\n\n        assert y > 10\n        assert x == 15\n\n    def test_fillcolor_is_none(self):\n        image = np.ones((20, 20, 3), dtype=np.uint8)\n\n        image_aug = iaa.pillike.warp_affine(image,\n                                            translate_x_px=1,\n                                            fillcolor=None)\n\n        assert np.all(image_aug[:, :1, :] == 0)\n        assert np.all(image_aug[:, 1:, :] == 1)\n\n    def test_fillcolor_is_int(self):\n        image = np.ones((20, 20, 3), dtype=np.uint8)\n\n        image_aug = iaa.pillike.warp_affine(image,\n                                            translate_x_px=1,\n                                            fillcolor=128)\n\n        assert np.all(image_aug[:, :1, 0] == 128)\n        assert np.all(image_aug[:, :1, 1] == 0)\n        assert np.all(image_aug[:, :1, 2] == 0)\n        assert np.all(image_aug[:, 1:, :] == 1)\n\n    def test_fillcolor_is_int_grayscale(self):\n        image = np.ones((20, 20), dtype=np.uint8)\n\n        image_aug = iaa.pillike.warp_affine(image,\n                                            translate_x_px=1,\n                                            fillcolor=128)\n\n        assert np.all(image_aug[:, :1] == 128)\n        assert np.all(image_aug[:, 1:] == 1)\n\n    def test_fillcolor_is_tuple(self):\n        image = np.ones((20, 20, 3), dtype=np.uint8)\n\n        image_aug = iaa.pillike.warp_affine(image,\n                                            translate_x_px=1,\n                                            fillcolor=(2, 3, 4))\n\n        assert np.all(image_aug[:, :1, 0] == 2)\n        assert np.all(image_aug[:, :1, 1] == 3)\n        assert np.all(image_aug[:, :1, 2] == 4)\n        assert np.all(image_aug[:, 1:, :] == 1)\n\n    def test_fillcolor_is_tuple_more_values_than_channels(self):\n        image = np.ones((20, 20, 3), dtype=np.uint8)\n\n        image_aug = iaa.pillike.warp_affine(image,\n                                            translate_x_px=1,\n                                            fillcolor=(2, 3, 4, 5))\n\n        assert image_aug.shape == (20, 20, 3)\n        assert np.all(image_aug[:, :1, 0] == 2)\n        assert np.all(image_aug[:, :1, 1] == 3)\n        assert np.all(image_aug[:, :1, 2] == 4)\n        assert np.all(image_aug[:, 1:, :] == 1)\n\n    def test_center(self):\n        image = np.zeros((21, 21, 3), dtype=np.uint8)\n        image[2, 10] = 255\n\n        image_aug = iaa.pillike.warp_affine(image,\n                                            rotate_deg=90,\n                                            center=(0.5, 0.5))\n\n        assert image_aug[10, 18, 0] == 255\n\n    def test_image_shape_hw(self):\n        func = functools.partial(iaa.pillike.warp_affine, rotate_deg=90)\n        _test_shape_hw(func)\n\n    def test_image_shape_hw1(self):\n        func = functools.partial(iaa.pillike.warp_affine, rotate_deg=90)\n        _test_shape_hw1(func)\n\n\nclass TestSolarize(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_returns_correct_instance(self):\n        aug = iaa.pillike.Solarize()\n        assert isinstance(aug, iaa.Invert)\n        assert aug.per_channel.value == 0\n        assert aug.min_value is None\n        assert aug.max_value is None\n        assert np.isclose(aug.threshold.value, 128)\n        assert aug.invert_above_threshold.value == 1\n\n    def test_pickleable(self):\n        aug = iaa.pillike.Solarize()\n        runtest_pickleable_uint8_img(aug)\n\n\nclass TestPosterize(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_returns_posterize(self):\n        aug = iaa.pillike.Posterize()\n        assert isinstance(aug, iaa.Posterize)\n\n    def test_pickleable(self):\n        aug = iaa.pillike.Posterize()\n        runtest_pickleable_uint8_img(aug)\n\n\nclass TestEqualize(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @mock.patch(\"imgaug.augmenters.pillike.equalize_\")\n    def test_mocked(self, mock_eq):\n        image = np.arange(1*1*3).astype(np.uint8).reshape((1, 1, 3))\n        mock_eq.return_value = np.copy(image)\n        aug = iaa.pillike.Equalize()\n\n        _image_aug = aug(image=image)\n\n        assert mock_eq.call_count == 1\n        assert np.array_equal(mock_eq.call_args_list[0][0][0], image)\n\n    def test_integrationtest(self):\n        rng = iarandom.RNG(0)\n        for size in [20, 100]:\n            shape = (size, size, 3)\n            image = rng.integers(50, 150, size=shape)\n            image = image.astype(np.uint8)\n            aug = iaa.pillike.Equalize()\n\n            image_aug = aug(image=image)\n\n            if size > 1:\n                channelwise_sums = np.sum(image_aug, axis=(0, 1))\n                assert np.all(channelwise_sums > 0)\n            assert np.min(image_aug) < 50\n            assert np.max(image_aug) > 150\n\n    def test_pickleable(self):\n        aug = iaa.pillike.Equalize()\n        runtest_pickleable_uint8_img(aug)\n\n\nclass TestAutocontrast(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @mock.patch(\"imgaug.augmenters.pillike.autocontrast\")\n    def test_mocked(self, mock_auto):\n        image = np.mod(np.arange(10*10*3), 255)\n        image = image.reshape((10, 10, 3)).astype(np.uint8)\n        mock_auto.return_value = image\n        aug = iaa.pillike.Autocontrast(15)\n\n        _image_aug = aug(image=image)\n\n        assert np.array_equal(mock_auto.call_args_list[0][0][0], image)\n        assert mock_auto.call_args_list[0][0][1] == 15\n\n    @mock.patch(\"imgaug.augmenters.pillike.autocontrast\")\n    def test_per_channel(self, mock_auto):\n        image = np.mod(np.arange(10*10*1), 255)\n        image = image.reshape((10, 10, 1)).astype(np.uint8)\n        image = np.tile(image, (1, 1, 100))\n        mock_auto.return_value = image[..., 0]\n        aug = iaa.pillike.Autocontrast((0, 30), per_channel=True)\n\n        with assertWarns(self, iaa.SuspiciousSingleImageShapeWarning):\n            _image_aug = aug(image=image)\n\n        assert mock_auto.call_count == 100\n        cutoffs = []\n        for i in np.arange(100):\n            assert np.array_equal(mock_auto.call_args_list[i][0][0],\n                                  image[..., i])\n            cutoffs.append(mock_auto.call_args_list[i][0][1])\n        assert len(set(cutoffs)) > 10\n\n    def test_integrationtest(self):\n        image = iarandom.RNG(0).integers(50, 150, size=(100, 100, 3))\n        image = image.astype(np.uint8)\n        aug = iaa.pillike.Autocontrast(10)\n\n        image_aug = aug(image=image)\n\n        assert np.min(image_aug) < 50\n        assert np.max(image_aug) > 150\n\n    def test_integrationtest_per_channel(self):\n        image = iarandom.RNG(0).integers(50, 150, size=(100, 100, 50))\n        image = image.astype(np.uint8)\n        aug = iaa.pillike.Autocontrast(10, per_channel=True)\n\n        with assertWarns(self, iaa.SuspiciousSingleImageShapeWarning):\n            image_aug = aug(image=image)\n\n        assert np.min(image_aug) < 50\n        assert np.max(image_aug) > 150\n\n    def test_pickleable(self):\n        aug = iaa.pillike.Autocontrast((0, 30), per_channel=0.5)\n        runtest_pickleable_uint8_img(aug)\n\n\nclass TestEnhanceColor(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___defaults(self):\n        aug = iaa.pillike.EnhanceColor()\n        assert np.isclose(aug.factor.a.value, 0.0)\n        assert np.isclose(aug.factor.b.value, 3.0)\n\n    def test___init___custom(self):\n        aug = iaa.pillike.EnhanceColor(0.75)\n        assert np.isclose(aug.factor.value, 0.75)\n\n    @mock.patch(\"imgaug.augmenters.pillike.enhance_color\")\n    def test_mocked(self, mock_pilcol):\n        aug = iaa.pillike.EnhanceColor(0.75)\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        mock_pilcol.return_value = np.full((1, 1, 3), 128, dtype=np.uint8)\n\n        image_aug = aug(image=image)\n\n        assert mock_pilcol.call_count == 1\n        assert ia.is_np_array(mock_pilcol.call_args_list[0][0][0])\n        assert np.isclose(mock_pilcol.call_args_list[0][0][1], 0.75, rtol=0,\n                          atol=1e-4)\n        assert np.all(image_aug == 128)\n\n    def test_simple_image(self):\n        aug = iaa.pillike.EnhanceColor(0.0)\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        image[:, :, 0] = 255\n        image[:, :, 1] = 255\n\n        image_aug = aug(image=image)\n\n        assert image_aug[:, :, 2] > 200\n        assert np.all(image_aug[:, :, 0] == image_aug[:, :, 1])\n        assert np.all(image_aug[:, :, 0] == image_aug[:, :, 2])\n\n    def test_batch_contains_no_images(self):\n        aug = iaa.pillike.EnhanceColor(0.75)\n        hm_arr = np.ones((3, 3, 1), dtype=np.float32)\n        hm = ia.HeatmapsOnImage(hm_arr, shape=(3, 3, 3))\n\n        hm_aug = aug(heatmaps=hm)\n\n        assert np.allclose(hm_aug.get_arr(), hm.get_arr())\n\n    def test_get_parameters(self):\n        aug = iaa.pillike.EnhanceColor(0.75)\n        params = aug.get_parameters()\n        assert params[0] is aug.factor\n\n    def test_pickleable(self):\n        aug = iaa.pillike.EnhanceColor((0.1, 2.0))\n        runtest_pickleable_uint8_img(aug)\n\n\n# we don't have to test very much here, because some functions of the base\n# class are already tested via EnhanceColor\nclass TestEnhanceContrast(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @mock.patch(\"imgaug.augmenters.pillike.enhance_contrast\")\n    def test_mocked(self, mock_pilco):\n        aug = iaa.pillike.EnhanceContrast(0.75)\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        mock_pilco.return_value = np.full((1, 1, 3), 128, dtype=np.uint8)\n\n        image_aug = aug(image=image)\n\n        assert mock_pilco.call_count == 1\n        assert ia.is_np_array(mock_pilco.call_args_list[0][0][0])\n        assert np.isclose(mock_pilco.call_args_list[0][0][1], 0.75, rtol=0,\n                          atol=1e-4)\n        assert np.all(image_aug == 128)\n\n    def test_simple_image(self):\n        aug = iaa.pillike.EnhanceContrast(0.0)\n        image = np.full((2, 2, 3), 128, dtype=np.uint8)\n        image[0, :, :] = 200\n\n        image_aug = aug(image=image)\n\n        diff_before = np.average(np.abs(image.astype(np.int32)\n                                        - np.average(image)))\n        diff_after = np.average(np.abs(image_aug.astype(np.int32)\n                                       - np.average(image_aug)))\n        assert diff_after < diff_before\n\n    def test_batch_contains_no_images(self):\n        aug = iaa.pillike.EnhanceContrast(0.75)\n        hm_arr = np.ones((3, 3, 1), dtype=np.float32)\n        hm = ia.HeatmapsOnImage(hm_arr, shape=(3, 3, 3))\n\n        hm_aug = aug(heatmaps=hm)\n\n        assert np.allclose(hm_aug.get_arr(), hm.get_arr())\n\n    def test_pickleable(self):\n        aug = iaa.pillike.EnhanceContrast((0.1, 2.0))\n        runtest_pickleable_uint8_img(aug)\n\n\n# we don't have to test very much here, because some functions of the base\n# class are already tested via EnhanceColor\nclass TestEnhanceBrightness(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @mock.patch(\"imgaug.augmenters.pillike.enhance_brightness\")\n    def test_mocked(self, mock_pilbr):\n        aug = iaa.pillike.EnhanceBrightness(0.75)\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        mock_pilbr.return_value = np.full((1, 1, 3), 128, dtype=np.uint8)\n\n        image_aug = aug(image=image)\n\n        assert mock_pilbr.call_count == 1\n        assert ia.is_np_array(mock_pilbr.call_args_list[0][0][0])\n        assert np.isclose(mock_pilbr.call_args_list[0][0][1], 0.75, rtol=0,\n                          atol=1e-4)\n        assert np.all(image_aug == 128)\n\n    def test_simple_image(self):\n        aug = iaa.pillike.EnhanceBrightness(0.0)\n        image = np.full((2, 2, 3), 255, dtype=np.uint8)\n\n        image_aug = aug(image=image)\n\n        assert np.all(image_aug < 255)\n\n    def test_batch_contains_no_images(self):\n        aug = iaa.pillike.EnhanceBrightness(0.75)\n        hm_arr = np.ones((3, 3, 1), dtype=np.float32)\n        hm = ia.HeatmapsOnImage(hm_arr, shape=(3, 3, 3))\n\n        hm_aug = aug(heatmaps=hm)\n\n        assert np.allclose(hm_aug.get_arr(), hm.get_arr())\n\n    def test_pickleable(self):\n        aug = iaa.pillike.EnhanceBrightness((0.1, 2.0))\n        runtest_pickleable_uint8_img(aug)\n\n\n# we don't have to test very much here, because some functions of the base\n# class are already tested via EnhanceColor\nclass TestEnhanceSharpness(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @mock.patch(\"imgaug.augmenters.pillike.enhance_sharpness\")\n    def test_mocked(self, mock_pilsh):\n        aug = iaa.pillike.EnhanceSharpness(0.75)\n        image = np.zeros((3, 3, 3), dtype=np.uint8)\n        mock_pilsh.return_value = np.full((3, 3, 3), 128, dtype=np.uint8)\n\n        image_aug = aug(image=image)\n\n        assert mock_pilsh.call_count == 1\n        assert ia.is_np_array(mock_pilsh.call_args_list[0][0][0])\n        assert np.isclose(mock_pilsh.call_args_list[0][0][1], 0.75, rtol=0,\n                          atol=1e-4)\n        assert np.all(image_aug == 128)\n\n    def test_simple_image(self):\n        aug = iaa.pillike.EnhanceSharpness(2.0)\n        image = np.full((3, 3, 3), 64, dtype=np.uint8)\n        image[1, 1, :] = 128\n\n        image_aug = aug(image=image)\n\n        assert np.all(image_aug[1, 1, :] > 128)\n\n    def test_batch_contains_no_images(self):\n        aug = iaa.pillike.EnhanceSharpness(0.75)\n        hm_arr = np.ones((3, 3, 1), dtype=np.float32)\n        hm = ia.HeatmapsOnImage(hm_arr, shape=(3, 3, 3))\n\n        hm_aug = aug(heatmaps=hm)\n\n        assert np.allclose(hm_aug.get_arr(), hm.get_arr())\n\n    def test_pickleable(self):\n        aug = iaa.pillike.EnhanceSharpness((0.1, 2.0))\n        runtest_pickleable_uint8_img(aug)\n\n\nclass _TestFilter(unittest.TestCase):\n    def _test___init__(self, cls, func):\n        aug = cls()\n        assert aug.func is func\n\n    def _test_image(self, cls, pil_kernel):\n        image = ia.data.quokka(0.25)\n        image_aug = cls()(image=image)\n        image_aug_pil = PIL.Image.fromarray(image).filter(pil_kernel)\n        assert np.array_equal(image_aug, image_aug_pil)\n\n    def _test_pickleable(self, cls):\n        aug = cls()\n        runtest_pickleable_uint8_img(aug)\n\n\nclass FilterBlur(_TestFilter):\n    def test___init__(self):\n        self._test___init__(iaa.pillike.FilterBlur,\n                            iaa.pillike.filter_blur)\n\n    def test_image(self):\n        self._test_image(iaa.pillike.FilterBlur,\n                         PIL.ImageFilter.BLUR)\n\n    def test_pickleable(self):\n        self._test_pickleable(iaa.pillike.FilterBlur)\n\n\nclass FilterSmooth(_TestFilter):\n    def test___init__(self):\n        self._test___init__(iaa.pillike.FilterSmooth,\n                            iaa.pillike.filter_smooth)\n\n    def test_image(self):\n        self._test_image(iaa.pillike.FilterSmooth,\n                         PIL.ImageFilter.SMOOTH)\n\n    def test_pickleable(self):\n        self._test_pickleable(iaa.pillike.FilterSmooth)\n\n\nclass FilterSmoothMore(_TestFilter):\n    def test___init__(self):\n        self._test___init__(iaa.pillike.FilterSmoothMore,\n                            iaa.pillike.filter_smooth_more)\n\n    def test_image(self):\n        self._test_image(iaa.pillike.FilterSmoothMore,\n                         PIL.ImageFilter.SMOOTH_MORE)\n\n    def test_pickleable(self):\n        self._test_pickleable(iaa.pillike.FilterSmoothMore)\n\n\nclass FilterEdgeEnhance(_TestFilter):\n    def test___init__(self):\n        self._test___init__(iaa.pillike.FilterEdgeEnhance,\n                            iaa.pillike.filter_edge_enhance)\n\n    def test_image(self):\n        self._test_image(iaa.pillike.FilterEdgeEnhance,\n                         PIL.ImageFilter.EDGE_ENHANCE)\n\n    def test_pickleable(self):\n        self._test_pickleable(iaa.pillike.FilterEdgeEnhance)\n\n\nclass FilterEdgeEnhanceMore(_TestFilter):\n    def test___init__(self):\n        self._test___init__(iaa.pillike.FilterEdgeEnhanceMore,\n                            iaa.pillike.filter_edge_enhance_more)\n\n    def test_image(self):\n        self._test_image(iaa.pillike.FilterEdgeEnhanceMore,\n                         PIL.ImageFilter.EDGE_ENHANCE_MORE)\n\n    def test_pickleable(self):\n        self._test_pickleable(iaa.pillike.FilterEdgeEnhanceMore)\n\n\nclass FilterFindEdges(_TestFilter):\n    def test___init__(self):\n        self._test___init__(iaa.pillike.FilterFindEdges,\n                            iaa.pillike.filter_find_edges)\n\n    def test_image(self):\n        self._test_image(iaa.pillike.FilterFindEdges,\n                         PIL.ImageFilter.FIND_EDGES)\n\n    def test_pickleable(self):\n        self._test_pickleable(iaa.pillike.FilterFindEdges)\n\n\nclass FilterContour(_TestFilter):\n    def test___init__(self):\n        self._test___init__(iaa.pillike.FilterContour,\n                            iaa.pillike.filter_contour)\n\n    def test_image(self):\n        self._test_image(iaa.pillike.FilterContour,\n                         PIL.ImageFilter.CONTOUR)\n\n    def test_pickleable(self):\n        self._test_pickleable(iaa.pillike.FilterContour)\n\n\nclass FilterEmboss(_TestFilter):\n    def test___init__(self):\n        self._test___init__(iaa.pillike.FilterEmboss,\n                            iaa.pillike.filter_emboss)\n\n    def test_image(self):\n        self._test_image(iaa.pillike.FilterEmboss,\n                         PIL.ImageFilter.EMBOSS)\n\n    def test_pickleable(self):\n        self._test_pickleable(iaa.pillike.FilterEmboss)\n\n\nclass FilterSharpen(_TestFilter):\n    def test___init__(self):\n        self._test___init__(iaa.pillike.FilterSharpen,\n                            iaa.pillike.filter_sharpen)\n\n    def test_image(self):\n        self._test_image(iaa.pillike.FilterSharpen,\n                         PIL.ImageFilter.SHARPEN)\n\n    def test_pickleable(self):\n        self._test_pickleable(iaa.pillike.FilterSharpen)\n\n\nclass FilterDetail(_TestFilter):\n    def test___init__(self):\n        self._test___init__(iaa.pillike.FilterDetail,\n                            iaa.pillike.filter_detail)\n\n    def test_image(self):\n        self._test_image(iaa.pillike.FilterDetail,\n                         PIL.ImageFilter.DETAIL)\n\n    def test_pickleable(self):\n        self._test_pickleable(iaa.pillike.FilterDetail)\n\n\nclass TestAffine(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @mock.patch(\"imgaug.augmenters.pillike.warp_affine\")\n    def test_mocked(self, mock_pilaff):\n        aug = iaa.pillike.Affine(\n            scale={\"x\": 1.25, \"y\": 1.5},\n            translate_px={\"x\": 10, \"y\": 20},\n            rotate=30,\n            shear={\"x\": 40, \"y\": 50},\n            fillcolor=100,\n            center=(0.1, 0.2)\n        )\n        image = np.zeros((3, 3, 3), dtype=np.uint8)\n        mock_pilaff.return_value = np.full((3, 3, 3), 128, dtype=np.uint8)\n\n        image_aug = aug(image=image)\n\n        assert mock_pilaff.call_count == 1\n\n        args = mock_pilaff.call_args_list[0][0]\n        assert np.all(args[0] == 128)  # due to in-place change\n\n        kwargs = mock_pilaff.call_args_list[0][1]\n        assert np.isclose(kwargs[\"scale_x\"], 1.25)\n        assert np.isclose(kwargs[\"scale_y\"], 1.5)\n        assert np.isclose(kwargs[\"translate_x_px\"], 10)\n        assert np.isclose(kwargs[\"translate_y_px\"], 20)\n        assert np.isclose(kwargs[\"rotate_deg\"], 30)\n        assert np.isclose(kwargs[\"shear_x_deg\"], 40)\n        assert np.isclose(kwargs[\"shear_y_deg\"], 50)\n        assert np.isclose(kwargs[\"fillcolor\"][0], 100)\n        assert np.isclose(kwargs[\"fillcolor\"][1], 100)\n        assert np.isclose(kwargs[\"fillcolor\"][2], 100)\n        assert np.isclose(kwargs[\"center\"][0], 0.1)\n        assert np.isclose(kwargs[\"center\"][1], 0.2)\n        assert np.all(image_aug == 128)\n\n    @mock.patch(\"imgaug.augmenters.pillike.warp_affine\")\n    def test_mocked_translate_percent(self, mock_pilaff):\n        aug = iaa.pillike.Affine(\n            translate_percent={\"x\": 1.2, \"y\": 1.5}\n        )\n        image = np.zeros((20, 50, 3), dtype=np.uint8)\n        mock_pilaff.return_value = np.full((20, 50, 3), 128, dtype=np.uint8)\n\n        image_aug = aug(image=image)\n\n        assert mock_pilaff.call_count == 1\n\n        args = mock_pilaff.call_args_list[0][0]\n        assert np.all(args[0] == 128)  # due to in-place change\n\n        kwargs = mock_pilaff.call_args_list[0][1]\n        assert np.isclose(kwargs[\"scale_x\"], 1.0)\n        assert np.isclose(kwargs[\"scale_y\"], 1.0)\n        assert np.isclose(kwargs[\"translate_x_px\"], 50*1.2)\n        assert np.isclose(kwargs[\"translate_y_px\"], 20*1.5)\n        assert np.isclose(kwargs[\"rotate_deg\"], 0)\n        assert np.isclose(kwargs[\"shear_x_deg\"], 0)\n        assert np.isclose(kwargs[\"shear_y_deg\"], 0)\n        assert np.isclose(kwargs[\"fillcolor\"][0], 0)\n        assert np.isclose(kwargs[\"fillcolor\"][1], 0)\n        assert np.isclose(kwargs[\"fillcolor\"][2], 0)\n        assert np.isclose(kwargs[\"center\"][0], 0.5)\n        assert np.isclose(kwargs[\"center\"][1], 0.5)\n        assert np.all(image_aug == 128)\n\n    def test_parameters_affect_images(self):\n        params = [\n            (\"scale\", {\"x\": 1.3}),\n            (\"scale\", {\"y\": 1.5}),\n            (\"translate_px\", {\"x\": 5}),\n            (\"translate_px\", {\"y\": 10}),\n            (\"translate_percent\", {\"x\": 0.3}),\n            (\"translate_percent\", {\"y\": 0.4}),\n            (\"rotate\", 10),\n            (\"shear\", {\"x\": 20}),\n            (\"shear\", {\"y\": 20})\n        ]\n        image = ia.data.quokka_square((64, 64))\n\n        images_aug = []\n        for param_name, param_val in params:\n            kwargs = {param_name: param_val}\n            aug = iaa.pillike.Affine(**kwargs)\n            image_aug = aug(image=image)\n            images_aug.append(image_aug)\n\n        for i, image_aug in enumerate(images_aug):\n            assert not np.array_equal(image_aug, image)\n            for j, other_image_aug in enumerate(images_aug):\n                if i != j:\n                    assert not np.array_equal(image_aug, other_image_aug)\n\n    def test_batch_contains_no_images(self):\n        aug = iaa.pillike.Affine(translate_px={\"x\": 10})\n        hm_arr = np.ones((3, 3, 1), dtype=np.float32)\n        hm = ia.HeatmapsOnImage(hm_arr, shape=(3, 3, 3))\n\n        with self.assertRaises(AssertionError):\n            _hm_aug = aug(heatmaps=hm)\n\n    def test_get_parameters(self):\n        aug = iaa.pillike.Affine(\n            scale={\"x\": 1.25, \"y\": 1.5},\n            translate_px={\"x\": 10, \"y\": 20},\n            rotate=30,\n            shear={\"x\": 40, \"y\": 50},\n            fillcolor=100,\n            center=(0.1, 0.2)\n        )\n        params = aug.get_parameters()\n        assert params[0] is aug.scale\n        assert params[1] is aug.translate\n        assert params[2] is aug.rotate\n        assert params[3] is aug.shear\n        assert params[4] is aug.cval\n        assert params[5] is aug.center\n\n    def test_pickleable(self):\n        aug = iaa.pillike.Affine(\n            scale={\"x\": 1.25, \"y\": 1.5},\n            translate_px={\"x\": 10, \"y\": 20},\n            rotate=30,\n            shear={\"x\": 40, \"y\": 50},\n            fillcolor=(100, 200),\n            center=\"uniform\"\n        )\n        runtest_pickleable_uint8_img(aug)\n"
  },
  {
    "path": "test/augmenters/test_pooling.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport itertools\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport six.moves as sm\n\nimport imgaug as ia\nimport imgaug.random as iarandom\nimport imgaug.augmenters.pooling as iapooling\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug.testutils import (reseed,\n                              assert_cbaois_equal,\n                              runtest_pickleable_uint8_img,\n                              is_parameter_instance)\n\n\nclass Test_compute_shape_after_pooling(unittest.TestCase):\n    def test_random_shapes_and_kernel_sizes(self):\n        shapes = [\n            (6, 5),\n            (5, 6),\n            (6, 6),\n            (11, 1),\n            (1, 11),\n            (0, 1),\n            (1, 0),\n            (0, 0)\n        ]\n        kernel_sizes = [1, 2, 3, 5]\n        nb_channels_lst = [None, 1, 3, 4]\n\n        # 8*(4*4)*4 = 512 subtests\n        gen = itertools.product(shapes, nb_channels_lst)\n        for shape_nochan, nb_channels in gen:\n            shape = shape_nochan\n            if nb_channels is not None:\n                shape = tuple(list(shape) + [nb_channels])\n            image = np.zeros(shape, dtype=np.uint8)\n\n            for ksize_h, ksize_w in itertools.product(kernel_sizes,\n                                                      kernel_sizes):\n                with self.subTest(shape=shape, ksize_h=ksize_h,\n                                  ksize_w=ksize_w):\n                    image_pooled = ia.avg_pool(image, (ksize_h, ksize_w))\n                    shape_expected = image_pooled.shape\n\n                    shape_observed = iapooling._compute_shape_after_pooling(\n                        shape, ksize_h, ksize_w\n                    )\n\n                    assert shape_observed == shape_expected\n\n\nclass _TestPoolingAugmentersBase(object):\n    def setUp(self):\n        reseed()\n\n    @property\n    def augmenter(self):\n        raise NotImplementedError()\n\n    @mock.patch(\"imgaug.augmenters.pooling._AbstractPoolingBase.\"\n                \"_augment_hms_and_segmaps_by_samples\")\n    def test_augment_segmaps(self, mock_aug_segmaps):\n        from imgaug.augmentables.segmaps import SegmentationMapsOnImage\n        arr = np.int32([\n            [1, 2, 3],\n            [4, 5, 6]\n        ])\n        segmap = SegmentationMapsOnImage(arr, shape=(6, 6, 3))\n        mock_aug_segmaps.return_value = [segmap]\n        rng = iarandom.RNG(0)\n        aug = self.augmenter(2, keep_size=False, seed=rng)\n\n        _ = aug.augment_segmentation_maps(segmap)\n\n        assert mock_aug_segmaps.call_count == 1\n        # call 0, args, arg 0, segmap 0 within segmaps list\n        assert np.array_equal(\n            mock_aug_segmaps.call_args_list[0][0][0][0].arr,\n            segmap.arr)\n\n    def _test_augment_cbaoi__kernel_size_is_noop(self, kernel_size, cbaoi,\n                                                 augf_name):\n        aug = self.augmenter(kernel_size)\n        cbaoi_aug = getattr(aug, augf_name)(cbaoi)\n        assert_cbaois_equal(cbaoi_aug, cbaoi)\n\n    def _test_augment_keypoints__kernel_size_is_noop(self, kernel_size):\n        from imgaug.augmentables.kps import Keypoint, KeypointsOnImage\n        kps = [Keypoint(x=1.5, y=5.5), Keypoint(x=5.5, y=1.5)]\n        kpsoi = KeypointsOnImage(kps, shape=(6, 6, 3))\n        self._test_augment_cbaoi__kernel_size_is_noop(\n            kernel_size, kpsoi, \"augment_keypoints\")\n\n    def test_augment_keypoints__kernel_size_is_zero(self):\n        self._test_augment_keypoints__kernel_size_is_noop(0)\n\n    def test_augment_keypoints__kernel_size_is_one(self):\n        self._test_augment_keypoints__kernel_size_is_noop(1)\n\n    def _test_augment_polygons__kernel_size_is_noop(self, kernel_size):\n        from imgaug.augmentables.polys import Polygon, PolygonsOnImage\n        ps = [Polygon([(1, 1), (2, 1), (2, 2)])]\n        psoi = PolygonsOnImage(ps, shape=(6, 6, 3))\n        self._test_augment_cbaoi__kernel_size_is_noop(\n            kernel_size, psoi, \"augment_polygons\")\n\n    def test_augment_polygons__kernel_size_is_zero(self):\n        self._test_augment_polygons__kernel_size_is_noop(0)\n\n    def test_augment_polygons__kernel_size_is_one(self):\n        self._test_augment_polygons__kernel_size_is_noop(1)\n\n    def _test_augment_line_strings__kernel_size_is_noop(self, kernel_size):\n        from imgaug.augmentables.lines import LineString, LineStringsOnImage\n        ls = [LineString([(1, 1), (2, 1), (2, 2)])]\n        lsoi = LineStringsOnImage(ls, shape=(6, 6, 3))\n        self._test_augment_cbaoi__kernel_size_is_noop(\n            kernel_size, lsoi, \"augment_line_strings\")\n\n    def test_augment_line_strings__kernel_size_is_zero(self):\n        self._test_augment_line_strings__kernel_size_is_noop(0)\n\n    def test_augment_line_strings__kernel_size_is_one(self):\n        self._test_augment_line_strings__kernel_size_is_noop(1)\n\n    def _test_augment_bounding_boxes__kernel_size_is_noop(self, kernel_size):\n        from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage\n        bbs = [BoundingBox(x1=1, y1=2, x2=3, y2=4)]\n        bbsoi = BoundingBoxesOnImage(bbs, shape=(6, 6, 3))\n        self._test_augment_cbaoi__kernel_size_is_noop(\n            kernel_size, bbsoi, \"augment_bounding_boxes\")\n\n    def test_augment_bounding_boxes__kernel_size_is_zero(self):\n        self._test_augment_bounding_boxes__kernel_size_is_noop(0)\n\n    def test_augment_bounding_boxes__kernel_size_is_one(self):\n        self._test_augment_bounding_boxes__kernel_size_is_noop(1)\n\n    def _test_augment_heatmaps__kernel_size_is_noop(self, kernel_size):\n        from imgaug.augmentables.heatmaps import HeatmapsOnImage\n        arr = np.float32([\n            [0.5, 0.6, 0.7],\n            [0.4, 0.5, 0.6]\n        ])\n        heatmaps = HeatmapsOnImage(arr, shape=(6, 6, 3))\n        aug = self.augmenter(kernel_size)\n\n        heatmaps_aug = aug.augment_heatmaps(heatmaps)\n\n        assert heatmaps_aug.shape == (6, 6, 3)\n        assert np.allclose(heatmaps_aug.arr_0to1, arr[..., np.newaxis])\n\n    def test_augment_heatmaps__kernel_size_is_zero(self):\n        self._test_augment_heatmaps__kernel_size_is_noop(0)\n\n    def test_augment_heatmaps__kernel_size_is_one(self):\n        self._test_augment_heatmaps__kernel_size_is_noop(1)\n\n    def _test_augment_segmaps__kernel_size_is_noop(self, kernel_size):\n        from imgaug.augmentables.segmaps import SegmentationMapsOnImage\n        arr = np.int32([\n            [0, 1, 2],\n            [1, 2, 3]\n        ])\n        segmaps = SegmentationMapsOnImage(arr, shape=(6, 6, 3))\n        aug = self.augmenter(kernel_size)\n\n        segmaps_aug = aug.augment_segmentation_maps(segmaps)\n\n        assert segmaps_aug.shape == (6, 6, 3)\n        assert np.allclose(segmaps_aug.arr, arr[..., np.newaxis])\n\n    def test_augment_segmaps__kernel_size_is_zero(self):\n        self._test_augment_segmaps__kernel_size_is_noop(0)\n\n    def test_augment_segmaps__kernel_size_is_one(self):\n        self._test_augment_segmaps__kernel_size_is_noop(1)\n\n    def _test_augment_cbaoi__kernel_size_is_two__keep_size(\n            self, cbaoi, augf_name):\n        aug = self.augmenter(2, keep_size=True)\n        observed = getattr(aug, augf_name)(cbaoi)\n        assert_cbaois_equal(observed, cbaoi)\n\n    def test_augment_keypoints__kernel_size_is_two__keep_size(self):\n        from imgaug.augmentables.kps import Keypoint, KeypointsOnImage\n        kps = [Keypoint(x=1.5, y=5.5), Keypoint(x=5.5, y=1.5)]\n        kpsoi = KeypointsOnImage(kps, shape=(6, 6, 3))\n        self._test_augment_cbaoi__kernel_size_is_two__keep_size(\n            kpsoi, \"augment_keypoints\")\n\n    def test_augment_polygons__kernel_size_is_two__keep_size(self):\n        from imgaug.augmentables.polys import Polygon, PolygonsOnImage\n        polys = [Polygon([(0, 0), (2, 0), (2, 2)])]\n        psoi = PolygonsOnImage(polys, shape=(6, 6, 3))\n        self._test_augment_cbaoi__kernel_size_is_two__keep_size(\n            psoi, \"augment_polygons\")\n\n    def test_augment_line_strings__kernel_size_is_two__keep_size(self):\n        from imgaug.augmentables.lines import LineString, LineStringsOnImage\n        ls = [LineString([(0, 0), (2, 0), (2, 2)])]\n        lsoi = LineStringsOnImage(ls, shape=(6, 6, 3))\n        self._test_augment_cbaoi__kernel_size_is_two__keep_size(\n            lsoi, \"augment_line_strings\")\n\n    def test_augment_bounding_boxes__kernel_size_is_two__keep_size(self):\n        from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage\n        bbs = [BoundingBox(x1=0, y1=0, x2=2, y2=2)]\n        bbsoi = BoundingBoxesOnImage(bbs, shape=(6, 6, 3))\n        self._test_augment_cbaoi__kernel_size_is_two__keep_size(\n            bbsoi, \"augment_bounding_boxes\")\n\n    def _test_augment_cbaoi__kernel_size_is_two__no_keep_size(\n            self, cbaoi, expected, augf_name):\n        aug = self.augmenter(2, keep_size=False)\n        observed = getattr(aug, augf_name)(cbaoi)\n        assert_cbaois_equal(observed, expected)\n\n    def test_augment_keypoints__kernel_size_is_two__no_keep_size(self):\n        from imgaug.augmentables.kps import Keypoint, KeypointsOnImage\n        kps = [Keypoint(x=1.5, y=5.5), Keypoint(x=5.5, y=1.5)]\n        kpsoi = KeypointsOnImage(kps, shape=(6, 6, 3))\n        expected = KeypointsOnImage.from_xy_array(\n            np.float32([\n                [1.5/2, 5.5/2],\n                [5.5/2, 1.5/2]\n            ]),\n            shape=(3, 3, 3))\n        self._test_augment_cbaoi__kernel_size_is_two__no_keep_size(\n            kpsoi, expected, \"augment_keypoints\")\n\n    def test_augment_polygons__kernel_size_is_two__no_keep_size(self):\n        from imgaug.augmentables.polys import Polygon, PolygonsOnImage\n        ps = [Polygon([(1.5, 1.5), (5.5, 1.5), (5.5, 5.5)])]\n        psoi = PolygonsOnImage(ps, shape=(6, 6, 3))\n        expected = PolygonsOnImage([\n            Polygon([(1.5/2, 1.5/2), (5.5/2, 1.5/2), (5.5/2, 5.5/2)])\n        ], shape=(3, 3, 3))\n        self._test_augment_cbaoi__kernel_size_is_two__no_keep_size(\n            psoi, expected, \"augment_polygons\")\n\n    def test_augment_line_strings__kernel_size_is_two__no_keep_size(self):\n        from imgaug.augmentables.lines import LineString, LineStringsOnImage\n        ls = [LineString([(1.5, 1.5), (5.5, 1.5), (5.5, 5.5)])]\n        lsoi = LineStringsOnImage(ls, shape=(6, 6, 3))\n        expected = LineStringsOnImage([\n            LineString([(1.5/2, 1.5/2), (5.5/2, 1.5/2), (5.5/2, 5.5/2)])\n        ], shape=(3, 3, 3))\n        self._test_augment_cbaoi__kernel_size_is_two__no_keep_size(\n            lsoi, expected, \"augment_line_strings\")\n\n    def test_augment_bounding_boxes__kernel_size_is_two__no_keep_size(self):\n        from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage\n        bbs = [BoundingBox(x1=1.5, y1=2.5, x2=3.5, y2=4.5)]\n        bbsoi = BoundingBoxesOnImage(bbs, shape=(6, 6, 3))\n        expected = BoundingBoxesOnImage([\n            BoundingBox(x1=1.5/2, y1=2.5/2, x2=3.5/2, y2=4.5/2)\n        ], shape=(3, 3, 3))\n        self._test_augment_cbaoi__kernel_size_is_two__no_keep_size(\n            bbsoi, expected, \"augment_bounding_boxes\")\n\n    def test_augment_heatmaps__kernel_size_is_two__keep_size(self):\n        from imgaug.augmentables.heatmaps import HeatmapsOnImage\n        arr = np.float32([\n            [0.5, 0.6, 0.7],\n            [0.4, 0.5, 0.6]\n        ])\n        heatmaps = HeatmapsOnImage(arr, shape=(6, 6, 3))\n        aug = self.augmenter(2, keep_size=True)\n\n        heatmaps_aug = aug.augment_heatmaps(heatmaps)\n\n        assert heatmaps_aug.shape == (6, 6, 3)\n        assert np.allclose(heatmaps_aug.arr_0to1, arr[..., np.newaxis])\n\n    def test_augment_heatmaps__kernel_size_is_two__no_keep_size(self):\n        from imgaug.augmentables.heatmaps import HeatmapsOnImage\n        arr = np.float32([\n            [0.5, 0.6, 0.7],\n            [0.4, 0.5, 0.6]\n        ])\n        heatmaps = HeatmapsOnImage(arr, shape=(6, 6, 3))\n        aug = self.augmenter(2, keep_size=False)\n\n        heatmaps_aug = aug.augment_heatmaps(heatmaps)\n\n        expected = heatmaps.resize((1, 2))\n        assert heatmaps_aug.shape == (3, 3, 3)\n        assert heatmaps_aug.arr_0to1.shape == (1, 2, 1)\n        assert np.allclose(heatmaps_aug.arr_0to1, expected.arr_0to1)\n\n    def test_augment_segmaps__kernel_size_is_two__keep_size(self):\n        from imgaug.augmentables.segmaps import SegmentationMapsOnImage\n        arr = np.int32([\n            [0, 1, 2],\n            [1, 2, 3]\n        ])\n        segmaps = SegmentationMapsOnImage(arr, shape=(6, 6, 3))\n        aug = self.augmenter(2, keep_size=True)\n\n        segmaps_aug = aug.augment_segmentation_maps(segmaps)\n\n        assert segmaps_aug.shape == (6, 6, 3)\n        assert np.allclose(segmaps_aug.arr, arr[..., np.newaxis])\n\n    def test_augment_segmaps__kernel_size_is_two__no_keep_size(self):\n        from imgaug.augmentables.segmaps import SegmentationMapsOnImage\n        arr = np.int32([\n            [0, 1, 2],\n            [1, 2, 3]\n        ])\n        segmaps = SegmentationMapsOnImage(arr, shape=(6, 6, 3))\n        aug = self.augmenter(2, keep_size=False)\n\n        segmaps_aug = aug.augment_segmentation_maps(segmaps)\n\n        expected = segmaps.resize((1, 2))\n        assert segmaps_aug.shape == (3, 3, 3)\n        assert segmaps_aug.arr.shape == (1, 2, 1)\n        assert np.allclose(segmaps_aug.arr, expected.arr)\n\n    def _test_augment_keypoints__kernel_size_differs(self, shape,\n                                                     shape_exp):\n        from imgaug.augmentables.kps import Keypoint, KeypointsOnImage\n        kps = [Keypoint(x=1.5, y=5.5), Keypoint(x=5.5, y=1.5)]\n        kpsoi = KeypointsOnImage(kps, shape=shape)\n        aug = self.augmenter(\n            (iap.Deterministic(3), iap.Deterministic(2)),\n            keep_size=False)\n\n        kpsoi_aug = aug.augment_keypoints(kpsoi)\n\n        expected = KeypointsOnImage.from_xy_array(\n            np.float32([\n                [(1.5/shape[1])*shape_exp[1], (5.5/shape[0])*shape_exp[0]],\n                [(5.5/shape[1])*shape_exp[1], (1.5/shape[0])*shape_exp[0]]\n            ]),\n            shape=shape_exp)\n        assert_cbaois_equal(kpsoi_aug, expected)\n\n    def test_augment_keypoints__kernel_size_differs(self):\n        self._test_augment_keypoints__kernel_size_differs((6, 6, 3), (2, 3, 3))\n\n    def test_augment_keypoints__kernel_size_differs__requires_padding(self):\n        self._test_augment_keypoints__kernel_size_differs((5, 6, 3), (2, 3, 3))\n\n    def _test_augment_polygons__kernel_size_differs(self, shape, shape_exp):\n        from imgaug.augmentables.polys import Polygon, PolygonsOnImage\n        polys = [Polygon([(1.5, 5.5), (5.5, 1.5), (5.5, 5.5)])]\n        psoi = PolygonsOnImage(polys, shape=shape)\n        aug = self.augmenter(\n            (iap.Deterministic(3), iap.Deterministic(2)),\n            keep_size=False)\n\n        psoi_aug = aug.augment_polygons(psoi)\n\n        expected = PolygonsOnImage(\n            [Polygon([\n                ((1.5/shape[1])*shape_exp[1], (5.5/shape[0])*shape_exp[0]),\n                ((5.5/shape[1])*shape_exp[1], (1.5/shape[0])*shape_exp[0]),\n                ((5.5/shape[1])*shape_exp[1], (5.5/shape[0])*shape_exp[0])\n            ])],\n            shape=shape_exp)\n        assert_cbaois_equal(psoi_aug, expected)\n\n    def test_augment_polygons__kernel_size_differs(self):\n        self._test_augment_polygons__kernel_size_differs((6, 6, 3), (2, 3, 3))\n\n    def test_augment_polygons__kernel_size_differs__requires_padding(self):\n        self._test_augment_polygons__kernel_size_differs((5, 6, 3), (2, 3, 3))\n\n    def _test_augment_line_strings__kernel_size_differs(self, shape, shape_exp):\n        from imgaug.augmentables.lines import LineString, LineStringsOnImage\n        ls = [LineString([(1.5, 5.5), (5.5, 1.5), (5.5, 5.5)])]\n        lsoi = LineStringsOnImage(ls, shape=shape)\n        aug = self.augmenter(\n            (iap.Deterministic(3), iap.Deterministic(2)),\n            keep_size=False)\n\n        lsoi_aug = aug.augment_line_strings(lsoi)\n\n        expected = LineStringsOnImage(\n            [LineString([\n                ((1.5/shape[1])*shape_exp[1], (5.5/shape[0])*shape_exp[0]),\n                ((5.5/shape[1])*shape_exp[1], (1.5/shape[0])*shape_exp[0]),\n                ((5.5/shape[1])*shape_exp[1], (5.5/shape[0])*shape_exp[0])\n            ])],\n            shape=shape_exp)\n        assert_cbaois_equal(lsoi_aug, expected)\n\n    def test_augment_line_strings__kernel_size_differs(self):\n        self._test_augment_line_strings__kernel_size_differs((6, 6, 3),\n                                                             (2, 3, 3))\n\n    def test_augment_line_strings__kernel_size_differs__requires_padding(self):\n        self._test_augment_line_strings__kernel_size_differs((5, 6, 3),\n                                                             (2, 3, 3))\n\n    def _test_augment_bounding_boxes__kernel_size_differs(self, shape,\n                                                          shape_exp):\n        from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage\n        bbs = [BoundingBox(x1=1.5, y1=2.5, x2=5.5, y2=6.5)]\n        bbsoi = BoundingBoxesOnImage(bbs, shape=shape)\n        aug = self.augmenter(\n            (iap.Deterministic(3), iap.Deterministic(2)),\n            keep_size=False)\n\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n\n        expected = BoundingBoxesOnImage(\n            [BoundingBox(\n                x1=(1.5/shape[1])*shape_exp[1],\n                y1=(2.5/shape[0])*shape_exp[0],\n                x2=(5.5/shape[1])*shape_exp[1],\n                y2=(6.5/shape[0])*shape_exp[0],\n            )],\n            shape=shape_exp)\n        assert_cbaois_equal(bbsoi_aug, expected)\n\n    def test_augment_bounding_boxes__kernel_size_differs(self):\n        self._test_augment_bounding_boxes__kernel_size_differs((6, 6, 3),\n                                                               (2, 3, 3))\n\n    def test_augment_bounding_boxes__kernel_size_differs__requires_pad(self):\n        self._test_augment_bounding_boxes__kernel_size_differs((5, 6, 3),\n                                                               (2, 3, 3))\n\n    def _test_cbaoi_alignment(self, cbaoi, cbaoi_empty,\n                              coords_expected_pooled, coords_expected_nopool,\n                              augf_name):\n        def _same_coords(cbaoi1, coords):\n            assert len(cbaoi1.items) == len(coords)\n            for item, coords_i in zip(cbaoi1.items, coords):\n                if not np.allclose(item.coords, coords_i, atol=1e-4, rtol=0):\n                    return False\n            return True\n\n        aug = self.augmenter((1, 2), keep_size=False)\n        image = np.zeros((40, 40, 1), dtype=np.uint8)\n\n        images_batch = [image, image, image, image]\n        cbaoi_batch = [cbaoi, cbaoi, cbaoi_empty, cbaoi]\n\n        nb_iterations = 10\n        for _ in sm.xrange(nb_iterations):\n            aug_det = aug.to_deterministic()\n            images_aug = aug_det.augment_images(images_batch)\n            cbaois_aug = getattr(aug_det, augf_name)(cbaoi_batch)\n\n            for index in [0, 1, 3]:\n                image_aug = images_aug[index]\n                cbaoi_aug = cbaois_aug[index]\n\n                assert image_aug.shape == cbaoi_aug.shape\n\n                if image_aug.shape == (20, 20, 1):\n                    assert _same_coords(\n                        cbaoi_aug,\n                        coords_expected_pooled)\n                else:\n                    assert _same_coords(\n                        cbaoi_aug,\n                        coords_expected_nopool)\n\n            for index in [2]:\n                image_aug = images_aug[index]\n                cbaoi_aug = cbaois_aug[index]\n\n                assert cbaoi_aug.shape == image_aug.shape\n                assert len(cbaoi_aug.items) == 0\n\n    def test_keypoint_alignment(self):\n        from imgaug.augmentables.kps import Keypoint, KeypointsOnImage\n        kps = [Keypoint(x=10, y=10), Keypoint(x=30, y=30)]\n        kpsoi = KeypointsOnImage(kps, shape=(40, 40, 1))\n        kpsoi_empty = KeypointsOnImage([], shape=(40, 40, 1))\n\n        self._test_cbaoi_alignment(\n            kpsoi, kpsoi_empty,\n            [[(5, 5)], [(15, 15)]],\n            [[(10, 10)], [(30, 30)]],\n            \"augment_keypoints\")\n\n    def test_polygon_alignment(self):\n        from imgaug.augmentables.polys import Polygon, PolygonsOnImage\n        polys = [Polygon([(10, 10), (30, 10), (30, 30)])]\n        psoi = PolygonsOnImage(polys, shape=(40, 40, 1))\n        psoi_empty = PolygonsOnImage([], shape=(40, 40, 1))\n\n        self._test_cbaoi_alignment(\n            psoi, psoi_empty,\n            [[(10/2, 10/2), (30/2, 10/2), (30/2, 30/2)]],\n            [[(10, 10), (30, 10), (30, 30)]],\n            \"augment_polygons\")\n\n    def test_line_strings_alignment(self):\n        from imgaug.augmentables.lines import LineString, LineStringsOnImage\n        lss = [LineString([(10, 10), (30, 10), (30, 30)])]\n        lsoi = LineStringsOnImage(lss, shape=(40, 40, 1))\n        lsoi_empty = LineStringsOnImage([], shape=(40, 40, 1))\n\n        self._test_cbaoi_alignment(\n            lsoi, lsoi_empty,\n            [[(10/2, 10/2), (30/2, 10/2), (30/2, 30/2)]],\n            [[(10, 10), (30, 10), (30, 30)]],\n            \"augment_line_strings\")\n\n    def test_bounding_boxes_alignment(self):\n        from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage\n        bbs = [BoundingBox(x1=10, y1=10, x2=30, y2=30)]\n        bbsoi = BoundingBoxesOnImage(bbs, shape=(40, 40, 1))\n        bbsoi_empty = BoundingBoxesOnImage([], shape=(40, 40, 1))\n\n        self._test_cbaoi_alignment(\n            bbsoi, bbsoi_empty,\n            [[(10/2, 10/2), (30/2, 30/2)]],\n            [[(10, 10), (30, 30)]],\n            \"augment_bounding_boxes\")\n\n    def _test_empty_cbaoi(self, cbaoi, augf_name):\n        aug = self.augmenter(3, keep_size=False)\n        cbaoi_aug = getattr(aug, augf_name)(cbaoi)\n        expected = cbaoi.deepcopy()\n        expected.shape = (2, 2, 3)\n        assert_cbaois_equal(cbaoi_aug, expected)\n\n    def test_empty_keypoints(self):\n        from imgaug.augmentables.kps import KeypointsOnImage\n        cbaoi = KeypointsOnImage([], shape=(5, 6, 3))\n        self._test_empty_cbaoi(cbaoi, \"augment_keypoints\")\n\n    def test_empty_polygons(self):\n        from imgaug.augmentables.polys import PolygonsOnImage\n        cbaoi = PolygonsOnImage([], shape=(5, 6, 3))\n        self._test_empty_cbaoi(cbaoi, \"augment_polygons\")\n\n    def test_empty_line_strings(self):\n        from imgaug.augmentables.lines import LineStringsOnImage\n        cbaoi = LineStringsOnImage([], shape=(5, 6, 3))\n        self._test_empty_cbaoi(cbaoi, \"augment_line_strings\")\n\n    def test_empty_bounding_boxes(self):\n        from imgaug.augmentables.bbs import BoundingBoxesOnImage\n        cbaoi = BoundingBoxesOnImage([], shape=(5, 6, 3))\n        self._test_empty_cbaoi(cbaoi, \"augment_bounding_boxes\")\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n                aug = self.augmenter(3)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n                aug = self.augmenter(3)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_get_parameters(self):\n        aug = self.augmenter(2)\n        params = aug.get_parameters()\n        assert len(params) == 2\n        assert len(params[0]) == 2\n        assert is_parameter_instance(params[0][0], iap.Deterministic)\n        assert params[0][0].value == 2\n        assert params[0][1] is None\n\n    def test_pickleable(self):\n        aug = self.augmenter((1, 7), random_state=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(25, 25, 1))\n\n    def subTest(self, *args, **kwargs):\n        raise NotImplementedError\n\n\n# TODO add test that checks the padding behaviour\nclass TestAveragePooling(unittest.TestCase, _TestPoolingAugmentersBase):\n    @property\n    def augmenter(self):\n        return iaa.AveragePooling\n\n    def test___init___default_settings(self):\n        aug = iaa.AveragePooling(2)\n        assert len(aug.kernel_size) == 2\n        assert is_parameter_instance(aug.kernel_size[0], iap.Deterministic)\n        assert aug.kernel_size[0].value == 2\n        assert aug.kernel_size[1] is None\n        assert aug.keep_size is True\n\n    def test___init___custom_settings(self):\n        aug = iaa.AveragePooling(((2, 4), (5, 6)), keep_size=False)\n        assert len(aug.kernel_size) == 2\n        assert is_parameter_instance(aug.kernel_size[0], iap.DiscreteUniform)\n        assert is_parameter_instance(aug.kernel_size[1], iap.DiscreteUniform)\n        assert aug.kernel_size[0].a.value == 2\n        assert aug.kernel_size[0].b.value == 4\n        assert aug.kernel_size[1].a.value == 5\n        assert aug.kernel_size[1].b.value == 6\n        assert aug.keep_size is False\n\n    def test_augment_images__kernel_size_is_zero(self):\n        aug = iaa.AveragePooling(0)\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n        assert np.array_equal(aug.augment_image(image), image)\n\n    def test_augment_images__kernel_size_is_one(self):\n        aug = iaa.AveragePooling(1)\n        image = np.arange(6*6*3).astype(np.uint8).reshape((6, 6, 3))\n        assert np.array_equal(aug.augment_image(image), image)\n\n    def test_augment_images__kernel_size_is_two__array_of_100s(self):\n        aug = iaa.AveragePooling(2, keep_size=False)\n        image = np.full((6, 6, 3), 100, dtype=np.uint8)\n        image_aug = aug.augment_image(image)\n        diff = np.abs(image_aug.astype(np.int32) - 100)\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (3, 3, 3)\n        assert np.all(diff <= 1)\n\n    def test_augment_images__kernel_size_is_two__custom_array(self):\n        aug = iaa.AveragePooling(2, keep_size=False)\n\n        image = np.uint8([\n            [50-2, 50-1, 120-4, 120+4],\n            [50+1, 50+2, 120+1, 120-1]\n        ])\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 3))\n\n        expected = np.uint8([\n            [50, 120]\n        ])\n        expected = np.tile(expected[:, :, np.newaxis], (1, 1, 3))\n\n        image_aug = aug.augment_image(image)\n        diff = np.abs(image_aug.astype(np.int32) - expected)\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (1, 2, 3)\n        assert np.all(diff <= 1)\n\n    def test_augment_images__kernel_size_is_two__view(self):\n        aug = iaa.AveragePooling(2, keep_size=False)\n\n        image = np.uint8([\n            [50-2, 50-1, 120-4, 120+4],\n            [50+1, 50+2, 120+1, 120-1],\n            [0, 0, 0, 0]\n        ])\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 3))\n        image = image[:2, :, :]\n        assert not image.flags[\"OWNDATA\"]\n        assert image.flags[\"C_CONTIGUOUS\"]\n\n        expected = np.uint8([\n            [50, 120]\n        ])\n        expected = np.tile(expected[:, :, np.newaxis], (1, 1, 3))\n\n        image_aug = aug.augment_image(image)\n\n        diff = np.abs(image_aug.astype(np.int32) - expected)\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (1, 2, 3)\n        assert np.all(diff <= 1)\n\n    def test_augment_images__kernel_size_is_two__non_contiguous(self):\n        aug = iaa.AveragePooling(2, keep_size=False)\n\n        image = np.array([\n            [50-2, 50-1, 120-4, 120+4],\n            [50+1, 50+2, 120+1, 120-1]\n        ], dtype=np.uint8, order=\"F\")\n        assert image.flags[\"OWNDATA\"]\n        assert not image.flags[\"C_CONTIGUOUS\"]\n\n        expected = np.uint8([\n            [50, 120]\n        ])\n\n        image_aug = aug.augment_image(image)\n\n        diff = np.abs(image_aug.astype(np.int32) - expected)\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (1, 2)\n        assert np.all(diff <= 1)\n\n    def test_augment_images__kernel_size_is_two__four_channels(self):\n        aug = iaa.AveragePooling(2, keep_size=False)\n\n        image = np.uint8([\n            [50-2, 50-1, 120-4, 120+4],\n            [50+1, 50+2, 120+1, 120-1]\n        ])\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 4))\n\n        expected = np.uint8([\n            [50, 120]\n        ])\n        expected = np.tile(expected[:, :, np.newaxis], (1, 1, 4))\n\n        image_aug = aug.augment_image(image)\n        diff = np.abs(image_aug.astype(np.int32) - expected)\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (1, 2, 4)\n        assert np.all(diff <= 1)\n\n    def test_augment_images__kernel_size_differs(self):\n        aug = iaa.AveragePooling(\n            (iap.Deterministic(3), iap.Deterministic(2)),\n            keep_size=False)\n\n        image = np.uint8([\n            [50-2, 50-1, 120-4, 120+4],\n            [50+1, 50+2, 120+2, 120-1],\n            [50-5, 50+5, 120-2, 120+1],\n        ])\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 3))\n\n        expected = np.uint8([\n            [50, 120]\n        ])\n        expected = np.tile(expected[:, :, np.newaxis], (1, 1, 3))\n\n        image_aug = aug.augment_image(image)\n        diff = np.abs(image_aug.astype(np.int32) - expected)\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (1, 2, 3)\n        assert np.all(diff <= 1)\n\n    def test_augment_images__kernel_size_differs__requires_padding(self):\n        aug = iaa.AveragePooling(\n            (iap.Deterministic(3), iap.Deterministic(1)),\n            keep_size=False)\n\n        image = np.uint8([\n            [50-2, 50-1, 120-4, 120+4],\n            [50+1, 50+2, 120+2, 120-1]\n        ])\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 3))\n\n        expected = np.uint8([\n            [(50-2 + 50+1 + 50-2)/3,\n             (50-1 + 50+2 + 50-1)/3,\n             (120-4 + 120+2 + 120-4)/3,\n             (120+4 + 120-1 + 120+4)/3]\n        ])\n        expected = np.tile(expected[:, :, np.newaxis], (1, 1, 3))\n\n        image_aug = aug.augment_image(image)\n\n        diff = np.abs(image_aug.astype(np.int32) - expected)\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (1, 4, 3)\n        assert np.all(diff <= 1)\n\n    def test_augment_images__kernel_size_is_two__keep_size(self):\n        aug = iaa.AveragePooling(2, keep_size=True)\n\n        image = np.uint8([\n            [50-2, 50-1, 120-4, 120+4],\n            [50+1, 50+2, 120+1, 120-1]\n        ])\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 3))\n\n        expected = np.uint8([\n            [50, 50, 120, 120],\n            [50, 50, 120, 120]\n        ])\n        expected = np.tile(expected[:, :, np.newaxis], (1, 1, 3))\n\n        image_aug = aug.augment_image(image)\n\n        diff = np.abs(image_aug.astype(np.int32) - expected)\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (2, 4, 3)\n        assert np.all(diff <= 1)\n\n    def test_augment_images__kernel_size_is_two__single_channel(self):\n        aug = iaa.AveragePooling(2, keep_size=False)\n\n        image = np.uint8([\n            [50-2, 50-1, 120-4, 120+4],\n            [50+1, 50+2, 120+1, 120-1]\n        ])\n        image = image[:, :, np.newaxis]\n\n        expected = np.uint8([\n            [50, 120]\n        ])\n        expected = expected[:, :, np.newaxis]\n\n        image_aug = aug.augment_image(image)\n\n        diff = np.abs(image_aug.astype(np.int32) - expected)\n        assert image_aug.dtype.name == \"uint8\"\n        assert image_aug.shape == (1, 2, 1)\n        assert np.all(diff <= 1)\n\n\n# TODO add test that checks the padding behaviour\n# We don't have many tests here, because MaxPooling and AveragePooling derive\n# from the same base class, i.e. they share most of the methods, which are then\n# tested via TestAveragePooling.\nclass TestMaxPooling(unittest.TestCase, _TestPoolingAugmentersBase):\n    @property\n    def augmenter(self):\n        return iaa.MaxPooling\n\n    def test_augment_images(self):\n        aug = iaa.MaxPooling(2, keep_size=False)\n\n        image = np.uint8([\n            [50-2, 50-1, 120-4, 120+4],\n            [50+1, 50+2, 120+1, 120-1]\n        ])\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 3))\n\n        expected = np.uint8([\n            [50+2, 120+4]\n        ])\n        expected = np.tile(expected[:, :, np.newaxis], (1, 1, 3))\n\n        image_aug = aug.augment_image(image)\n        diff = np.abs(image_aug.astype(np.int32) - expected)\n        assert image_aug.shape == (1, 2, 3)\n        assert np.all(diff <= 1)\n\n    def test_augment_images__different_channels(self):\n        aug = iaa.MaxPooling((iap.Deterministic(1), iap.Deterministic(4)),\n                             keep_size=False)\n\n        c1 = np.arange(start=1, stop=8+1).reshape((1, 8, 1))\n        c2 = (100 + np.arange(start=1, stop=8+1)).reshape((1, 8, 1))\n        image = np.dstack([c1, c2]).astype(np.uint8)\n\n        c1_expected = np.uint8([4, 8]).reshape((1, 2, 1))\n        c2_expected = np.uint8([100+4, 100+8]).reshape((1, 2, 1))\n        image_expected = np.dstack([c1_expected, c2_expected])\n\n        image_aug = aug.augment_image(image)\n        diff = np.abs(image_aug.astype(np.int32) - image_expected)\n        assert image_aug.shape == (1, 2, 2)\n        assert np.all(diff <= 1)\n\n\n# TODO add test that checks the padding behaviour\n# We don't have many tests here, because MinPooling and AveragePooling derive\n# from the same base class, i.e. they share most of the methods, which are then\n# tested via TestAveragePooling.\nclass TestMinPooling(unittest.TestCase, _TestPoolingAugmentersBase):\n    @property\n    def augmenter(self):\n        return iaa.MinPooling\n\n    def test_augment_images(self):\n        aug = iaa.MinPooling(2, keep_size=False)\n\n        image = np.uint8([\n            [50-2, 50-1, 120-4, 120+4],\n            [50+1, 50+2, 120+1, 120-1]\n        ])\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 3))\n\n        expected = np.uint8([\n            [50-2, 120-4]\n        ])\n        expected = np.tile(expected[:, :, np.newaxis], (1, 1, 3))\n\n        image_aug = aug.augment_image(image)\n        diff = np.abs(image_aug.astype(np.int32) - expected)\n        assert image_aug.shape == (1, 2, 3)\n        assert np.all(diff <= 1)\n\n    def test_augment_images__different_channels(self):\n        aug = iaa.MinPooling((iap.Deterministic(1), iap.Deterministic(4)),\n                             keep_size=False)\n\n        c1 = np.arange(start=1, stop=8+1).reshape((1, 8, 1))\n        c2 = (100 + np.arange(start=1, stop=8+1)).reshape((1, 8, 1))\n        image = np.dstack([c1, c2]).astype(np.uint8)\n\n        c1_expected = np.uint8([1, 5]).reshape((1, 2, 1))\n        c2_expected = np.uint8([100+1, 100+4]).reshape((1, 2, 1))\n        image_expected = np.dstack([c1_expected, c2_expected])\n\n        image_aug = aug.augment_image(image)\n        diff = np.abs(image_aug.astype(np.int32) - image_expected)\n        assert image_aug.shape == (1, 2, 2)\n        assert np.all(diff <= 1)\n\n\n# TODO add test that checks the padding behaviour\n# We don't have many tests here, because MedianPooling and AveragePooling\n# derive from the same base class, i.e. they share most of the methods, which\n# are then tested via TestAveragePooling.\nclass TestMedianPool(unittest.TestCase, _TestPoolingAugmentersBase):\n    @property\n    def augmenter(self):\n        return iaa.MedianPooling\n\n    def test_augment_images(self):\n        aug = iaa.MedianPooling(3, keep_size=False)\n\n        image = np.uint8([\n            [50-9, 50-8, 50-7, 120-5, 120-5, 120-5],\n            [50-5, 50+0, 50+3, 120-3, 120+0, 120+1],\n            [50+8, 50+9, 50+9, 120+2, 120+3, 120+4]\n        ])\n        image = np.tile(image[:, :, np.newaxis], (1, 1, 3))\n\n        expected = np.uint8([\n            [50, 120]\n        ])\n        expected = np.tile(expected[:, :, np.newaxis], (1, 1, 3))\n\n        image_aug = aug.augment_image(image)\n        diff = np.abs(image_aug.astype(np.int32) - expected)\n        assert image_aug.shape == (1, 2, 3)\n        assert np.all(diff <= 1)\n\n    def test_augment_images__different_channels(self):\n        aug = iaa.MinPooling((iap.Deterministic(1), iap.Deterministic(3)),\n                             keep_size=False)\n\n        c1 = np.arange(start=1, stop=9+1).reshape((1, 9, 1))\n        c2 = (100 + np.arange(start=1, stop=9+1)).reshape((1, 9, 1))\n        image = np.dstack([c1, c2]).astype(np.uint8)\n\n        c1_expected = np.uint8([2, 5, 8]).reshape((1, 3, 1))\n        c2_expected = np.uint8([100+2, 100+5, 100+8]).reshape((1, 3, 1))\n        image_expected = np.dstack([c1_expected, c2_expected])\n\n        image_aug = aug.augment_image(image)\n        diff = np.abs(image_aug.astype(np.int32) - image_expected)\n        assert image_aug.shape == (1, 3, 2)\n        assert np.all(diff <= 1)\n"
  },
  {
    "path": "test/augmenters/test_segmentation.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\nimport warnings\nimport itertools\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport six.moves as sm\n\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug import dtypes as iadt\nfrom imgaug import random as iarandom\nfrom imgaug.testutils import (\n    reseed,\n    runtest_pickleable_uint8_img,\n    temporary_constants,\n    is_parameter_instance\n)\nfrom imgaug.imgaug import _NUMBA_INSTALLED\n\n\n# On systems without numba we are forced to use numpy-based segment\n# replacement. We can thus only on numba systems test both.\n_NP_REPLACE = [True, False] if _NUMBA_INSTALLED else [True]\n\n\ndef _create_replace_np_context(use_np_replace):\n    cnames = [\"imgaug.augmenters.segmentation._NUMBA_INSTALLED\"]\n    values = [not use_np_replace]\n    return temporary_constants(cnames, values)\n\n\nclass TestSuperpixels(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @classmethod\n    def _array_equals_tolerant(cls, a, b, tolerance):\n        # TODO isnt this just np.allclose(a, b, rtol=0, atol=tolerance) ?!\n        diff = np.abs(a.astype(np.int32) - b.astype(np.int32))\n        return np.all(diff <= tolerance)\n\n    @property\n    def base_img(self):\n        base_img = [\n            [255, 255, 255, 0, 0, 0],\n            [255, 235, 255, 0, 20, 0],\n            [250, 250, 250, 5, 5, 5]\n        ]\n        base_img = np.tile(\n            np.array(base_img, dtype=np.uint8)[..., np.newaxis],\n            (1, 1, 3))\n        return base_img\n\n    @property\n    def base_img_superpixels(self):\n        base_img_superpixels = [\n            [251, 251, 251, 4, 4, 4],\n            [251, 251, 251, 4, 4, 4],\n            [251, 251, 251, 4, 4, 4]\n        ]\n        base_img_superpixels = np.tile(\n            np.array(base_img_superpixels, dtype=np.uint8)[..., np.newaxis],\n            (1, 1, 3))\n        return base_img_superpixels\n\n    @property\n    def base_img_superpixels_left(self):\n        base_img_superpixels_left = self.base_img_superpixels\n        base_img_superpixels_left[:, 3:, :] = self.base_img[:, 3:, :]\n        return base_img_superpixels_left\n\n    @property\n    def base_img_superpixels_right(self):\n        base_img_superpixels_right = self.base_img_superpixels\n        base_img_superpixels_right[:, :3, :] = self.base_img[:, :3, :]\n        return base_img_superpixels_right\n\n    def test_p_replace_0_n_segments_2(self):\n        for use_np_replace in _NP_REPLACE:\n            with self.subTest(use_np_replace=use_np_replace):\n                with _create_replace_np_context(use_np_replace):\n                    aug = iaa.Superpixels(p_replace=0, n_segments=2)\n                    observed = aug.augment_image(self.base_img)\n                    expected = self.base_img\n                    assert np.allclose(observed, expected)\n\n    def test_p_replace_1_n_segments_2(self):\n        for use_np_replace in _NP_REPLACE:\n            with self.subTest(use_np_replace=use_np_replace):\n                with _create_replace_np_context(use_np_replace):\n                    aug = iaa.Superpixels(p_replace=1.0, n_segments=2)\n                    observed = aug.augment_image(self.base_img)\n                    expected = self.base_img_superpixels\n                    assert self._array_equals_tolerant(observed, expected, 2)\n\n    def test_p_replace_1_n_segments_stochastic_parameter(self):\n        for use_np_replace in _NP_REPLACE:\n            with self.subTest(use_np_replace=use_np_replace):\n                with _create_replace_np_context(use_np_replace):\n                    aug = iaa.Superpixels(\n                        p_replace=1.0, n_segments=iap.Deterministic(2)\n                    )\n                    observed = aug.augment_image(self.base_img)\n                    expected = self.base_img_superpixels\n                    assert self._array_equals_tolerant(observed, expected, 2)\n\n    def test_p_replace_stochastic_parameter_n_segments_2(self):\n        for use_np_replace in _NP_REPLACE:\n            with self.subTest(use_np_replace=use_np_replace):\n                with _create_replace_np_context(use_np_replace):\n                    aug = iaa.Superpixels(\n                        p_replace=iap.Binomial(iap.Choice([0.0, 1.0])),\n                        n_segments=2\n                    )\n                    observed = aug.augment_image(self.base_img)\n                    assert (\n                        np.allclose(observed, self.base_img)\n                        or self._array_equals_tolerant(\n                            observed, self.base_img_superpixels, 2)\n                    )\n\n    def test_p_replace_050_n_segments_2(self):\n        _eq = self._array_equals_tolerant\n\n        for use_np_replace in _NP_REPLACE:\n            with self.subTest(use_np_replace=use_np_replace):\n                with _create_replace_np_context(use_np_replace):\n                    aug = iaa.Superpixels(p_replace=0.5, n_segments=2)\n                    seen = {\"none\": False, \"left\": False, \"right\": False,\n                            \"both\": False}\n                    for _ in sm.xrange(100):\n                        observed = aug.augment_image(self.base_img)\n                        if _eq(observed, self.base_img, 2):\n                            seen[\"none\"] = True\n                        elif _eq(observed, self.base_img_superpixels_left, 2):\n                            seen[\"left\"] = True\n                        elif _eq(observed, self.base_img_superpixels_right, 2):\n                            seen[\"right\"] = True\n                        elif _eq(observed, self.base_img_superpixels, 2):\n                            seen[\"both\"] = True\n                        else:\n                            raise Exception(\n                                \"Generated superpixels image does not match \"\n                                \"any expected image.\"\n                            )\n                        if np.all(seen.values()):\n                            break\n                    assert np.all(seen.values())\n\n    def test_failure_on_invalid_datatype_for_p_replace(self):\n        # note that assertRaisesRegex does not exist in 2.7\n        got_exception = False\n        try:\n            _ = iaa.Superpixels(p_replace=\"test\", n_segments=100)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_failure_on_invalid_datatype_for_n_segments(self):\n        # note that assertRaisesRegex does not exist in 2.7\n        got_exception = False\n        try:\n            _ = iaa.Superpixels(p_replace=1, n_segments=\"test\")\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape, use_np_replace in itertools.product(shapes, _NP_REPLACE):\n            with self.subTest(shape=shape, use_np_replace=use_np_replace):\n                with _create_replace_np_context(use_np_replace):\n                    image = np.full(shape, 128, dtype=np.uint8)\n                    aug = iaa.Superpixels(p_replace=1.0, n_segments=10)\n\n                    image_aug = aug(image=image)\n\n                    assert image_aug.dtype.name == \"uint8\"\n                    assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape, use_np_replace in itertools.product(shapes, _NP_REPLACE):\n            with self.subTest(shape=shape, use_np_replace=use_np_replace):\n                with _create_replace_np_context(use_np_replace):\n                    image = np.full(shape, 128, dtype=np.uint8)\n                    aug = iaa.Superpixels(p_replace=1.0, n_segments=10)\n\n                    image_aug = aug(image=image)\n\n                    assert image_aug.dtype.name == \"uint8\"\n                    assert image_aug.shape == shape\n\n    def test_get_parameters(self):\n        aug = iaa.Superpixels(\n            p_replace=0.5, n_segments=2, max_size=100, interpolation=\"nearest\")\n        params = aug.get_parameters()\n        assert params[0] is aug.p_replace\n        assert is_parameter_instance(params[0].p, iap.Deterministic)\n        assert params[1] is aug.n_segments\n        assert 0.5 - 1e-4 < params[0].p.value < 0.5 + 1e-4\n        assert params[1].value == 2\n        assert params[2] == 100\n        assert params[3] == \"nearest\"\n\n    def test_other_dtypes_bool(self):\n        for use_np_replace in _NP_REPLACE:\n                with self.subTest(use_np_replace=use_np_replace):\n                    with _create_replace_np_context(use_np_replace):\n                        aug = iaa.Superpixels(p_replace=1.0, n_segments=2)\n                        img = np.array([\n                            [False, False, True, True],\n                            [False, False, True, True]\n                        ], dtype=bool)\n                        img_aug = aug.augment_image(img)\n                        assert img_aug.dtype == img.dtype\n                        assert np.all(img_aug == img)\n\n                        aug = iaa.Superpixels(p_replace=1.0, n_segments=1)\n                        img = np.array([\n                            [True, True, True, True],\n                            [False, True, True, True]\n                        ], dtype=bool)\n                        img_aug = aug.augment_image(img)\n                        assert img_aug.dtype == img.dtype\n                        assert np.all(img_aug)\n\n    def test_other_dtypes_uint_int(self):\n        dtypes = [\"uint8\", \"uint16\", \"uint32\",\n                  \"int8\", \"int16\", \"int32\"]\n        for dtype in dtypes:\n            for use_np_replace in _NP_REPLACE:\n                with self.subTest(dtype=dtype, use_np_replace=use_np_replace):\n                    with _create_replace_np_context(use_np_replace):\n                        dtype = np.dtype(dtype)\n\n                        min_value, center_value, max_value = \\\n                            iadt.get_value_range_of_dtype(dtype)\n\n                        if np.dtype(dtype).kind == \"i\":\n                            values = [\n                                int(center_value), int(0.1 * max_value),\n                                int(0.2 * max_value), int(0.5 * max_value),\n                                max_value-100\n                            ]\n                            values = [((-1)*value, value) for value in values]\n                        else:\n                            values = [(0, int(center_value)),\n                                      (10, int(0.1 * max_value)),\n                                      (10, int(0.2 * max_value)),\n                                      (10, int(0.5 * max_value)),\n                                      (0, max_value),\n                                      (int(center_value),\n                                       max_value)]\n\n                        for v1, v2 in values:\n                            aug = iaa.Superpixels(p_replace=1.0, n_segments=2)\n                            img = np.array([\n                                [v1, v1, v2, v2],\n                                [v1, v1, v2, v2]\n                            ], dtype=dtype)\n                            img_aug = aug.augment_image(img)\n                            assert img_aug.dtype.name == dtype.name\n                            assert np.array_equal(img_aug, img)\n\n                            aug = iaa.Superpixels(p_replace=1.0, n_segments=1)\n                            img = np.array([\n                                [v2, v2, v2, v2],\n                                [v1, v2, v2, v2]\n                            ], dtype=dtype)\n                            img_aug = aug.augment_image(img)\n                            assert img_aug.dtype.name == dtype.name\n                            assert np.all(img_aug == int((7/8)*v2 + (1/8)*v1))\n\n    def test_other_dtypes_float(self):\n        # currently, no float dtype is actually accepted\n        for dtype in []:\n            def _allclose(a, b):\n                atol = 1e-4 if dtype == np.float16 else 1e-8\n                return np.allclose(a, b, atol=atol, rtol=0)\n\n            isize = np.dtype(dtype).itemsize\n            for value in [0, 1.0, 10.0, 1000 ** (isize - 1)]:\n                v1 = (-1) * value\n                v2 = value\n\n                aug = iaa.Superpixels(p_replace=1.0, n_segments=2)\n                img = np.array([\n                    [v1, v1, v2, v2],\n                    [v1, v1, v2, v2]\n                ], dtype=dtype)\n                img_aug = aug.augment_image(img)\n                assert img_aug.dtype == np.dtype(dtype)\n                assert _allclose(img_aug, img)\n\n                aug = iaa.Superpixels(p_replace=1.0, n_segments=1)\n                img = np.array([\n                    [v2, v2, v2, v2],\n                    [v1, v2, v2, v2]\n                ], dtype=dtype)\n                img_aug = aug.augment_image(img)\n                assert img_aug.dtype == np.dtype(dtype)\n                assert _allclose(img_aug, (7/8)*v2 + (1/8)*v1)\n\n    def test_pickleable(self):\n        aug = iaa.Superpixels(p_replace=0.5, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(25, 25, 1))\n\n\nclass Test_segment_voronoi(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_cell_coordinates_is_empty_integrationtest(self):\n        image = np.arange(2*2*3).astype(np.uint8).reshape((2, 2, 3))\n        cell_coordinates = np.zeros((0, 2), dtype=np.float32)\n        replace_mask = np.zeros((0,), dtype=bool)\n\n        image_seg = iaa.segment_voronoi(image, cell_coordinates, replace_mask)\n\n        assert np.array_equal(image, image_seg)\n\n    @classmethod\n    def _test_image_n_channels_integrationtest(cls, nb_channels):\n        image = np.uint8([\n            [0, 1, 200, 201],\n            [2, 3, 202, 203]\n        ])\n        if nb_channels is not None:\n            image = np.tile(image[:, :, np.newaxis], (1, 1, nb_channels))\n            for c in sm.xrange(nb_channels):\n                image[..., c] += c\n        cell_coordinates = np.float32([\n            [1.0, 1.0],\n            [3.0, 1.0]\n        ])\n        replace_mask = np.array([True, True], dtype=bool)\n\n        image_seg = iaa.segment_voronoi(image, cell_coordinates, replace_mask)\n\n        pixels1 = image[0:2, 0:2]\n        pixels2 = image[0:2, 2:4]\n        avg_color1 = np.average(pixels1.astype(np.float32), axis=(0, 1))\n        avg_color2 = np.average(pixels2.astype(np.float32), axis=(0, 1))\n        image_expected = np.uint8([\n            [avg_color1, avg_color1, avg_color2, avg_color2],\n            [avg_color1, avg_color1, avg_color2, avg_color2],\n        ])\n\n        assert np.array_equal(image_seg, image_expected)\n\n    def test_image_has_no_channels_integrationtest(self):\n        self._test_image_n_channels_integrationtest(None)\n\n    def test_image_has_one_channel_integrationtest(self):\n        self._test_image_n_channels_integrationtest(1)\n\n    def test_image_has_three_channels_integrationtest(self):\n        self._test_image_n_channels_integrationtest(3)\n\n    def test_replace_mask_is_all_false_integrationtest(self):\n        image = np.uint8([\n            [0, 1, 200, 201],\n            [2, 3, 202, 203]\n        ])\n        cell_coordinates = np.float32([\n            [1.0, 1.0],\n            [3.0, 1.0]\n        ])\n        replace_mask = np.array([False, False], dtype=bool)\n\n        image_seg = iaa.segment_voronoi(image, cell_coordinates, replace_mask)\n\n        assert np.array_equal(image_seg, image)\n\n    def test_replace_mask_is_mixed_integrationtest(self):\n        image = np.uint8([\n            [0, 1, 200, 201],\n            [2, 3, 202, 203]\n        ])\n        cell_coordinates = np.float32([\n            [1.0, 1.0],\n            [3.0, 1.0]\n        ])\n        replace_mask = np.array([False, True], dtype=bool)\n\n        image_seg = iaa.segment_voronoi(image, cell_coordinates, replace_mask)\n\n        pixels2 = image[0:2, 2:4]\n        avg_color2 = np.sum(pixels2).astype(np.float32) / pixels2.size\n        image_expected = np.uint8([\n            [0, 1, avg_color2, avg_color2],\n            [2, 3, avg_color2, avg_color2],\n        ])\n        assert np.array_equal(image_seg, image_expected)\n\n    def test_replace_mask_is_none_integrationtest(self):\n        image = np.uint8([\n            [0, 1, 200, 201],\n            [2, 3, 202, 203]\n        ])\n        cell_coordinates = np.float32([\n            [1.0, 1.0],\n            [3.0, 1.0]\n        ])\n        replace_mask = None\n\n        image_seg = iaa.segment_voronoi(image, cell_coordinates, replace_mask)\n\n        pixels1 = image[0:2, 0:2]\n        pixels2 = image[0:2, 2:4]\n        avg_color1 = np.sum(pixels1).astype(np.float32) / pixels1.size\n        avg_color2 = np.sum(pixels2).astype(np.float32) / pixels2.size\n        image_expected = np.uint8([\n            [avg_color1, avg_color1, avg_color2, avg_color2],\n            [avg_color1, avg_color1, avg_color2, avg_color2],\n        ])\n        assert np.array_equal(image_seg, image_expected)\n\n    def test_no_cell_coordinates_provided_and_no_channel_integrationtest(self):\n        image = np.uint8([\n            [0, 1, 200, 201],\n            [2, 3, 202, 203]\n        ])\n        cell_coordinates = np.zeros((0, 2), dtype=np.float32)\n        replace_mask = np.zeros((0,), dtype=bool)\n\n        image_seg = iaa.segment_voronoi(image, cell_coordinates, replace_mask)\n\n        assert np.array_equal(image_seg, image)\n\n    def test_no_cell_coordinates_provided_and_3_channels_integrationtest(self):\n        image = np.uint8([\n            [0, 1, 200, 201],\n            [2, 3, 202, 203]\n        ])\n        image = np.tile(image[..., np.newaxis], (1, 1, 3))\n        cell_coordinates = np.zeros((0, 2), dtype=np.float32)\n        replace_mask = np.zeros((0,), dtype=bool)\n\n        image_seg = iaa.segment_voronoi(image, cell_coordinates, replace_mask)\n\n        assert np.array_equal(image_seg, image)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        cell_coordinates = np.float32([\n            [1.0, 1.0],\n            [3.0, 1.0]\n        ])\n        replace_mask = np.array([True, True], dtype=bool)\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n\n                image_aug = iaa.segment_voronoi(image, cell_coordinates,\n                                                replace_mask)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        cell_coordinates = np.float32([\n            [1.0, 1.0],\n            [3.0, 1.0]\n        ])\n        replace_mask = np.array([True, True], dtype=bool)\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n\n                image_aug = iaa.segment_voronoi(image, cell_coordinates,\n                                                replace_mask)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n\nclass TestVoronoi(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___defaults(self):\n        sampler = iaa.RegularGridPointsSampler(1, 1)\n        aug = iaa.Voronoi(sampler)\n        assert aug.points_sampler is sampler\n        assert is_parameter_instance(aug.p_replace, iap.Deterministic)\n        assert aug.p_replace.value == 1\n        assert aug.max_size == 128\n        assert aug.interpolation == \"linear\"\n\n    def test___init___custom_arguments(self):\n        sampler = iaa.RegularGridPointsSampler(1, 1)\n        aug = iaa.Voronoi(sampler, p_replace=0.5, max_size=None,\n                          interpolation=\"cubic\")\n        assert aug.points_sampler is sampler\n        assert is_parameter_instance(aug.p_replace, iap.Binomial)\n        assert np.isclose(aug.p_replace.p.value, 0.5)\n        assert aug.max_size is None\n        assert aug.interpolation == \"cubic\"\n\n    def test_max_size_is_none(self):\n        image = np.zeros((10, 20, 3), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(1, 1)\n        aug = iaa.Voronoi(sampler, max_size=None)\n\n        mock_imresize = mock.MagicMock()\n        mock_imresize.return_value = image\n\n        fname = \"imgaug.imresize_single_image\"\n        with mock.patch(fname, mock_imresize):\n            _image_aug = aug(image=image)\n\n        assert mock_imresize.call_count == 0\n\n    def test_max_size_is_int_image_not_too_large(self):\n        image = np.zeros((10, 20, 3), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(1, 1)\n        aug = iaa.Voronoi(sampler, max_size=100)\n\n        mock_imresize = mock.MagicMock()\n        mock_imresize.return_value = image\n\n        fname = \"imgaug.imresize_single_image\"\n        with mock.patch(fname, mock_imresize):\n            _image_aug = aug(image=image)\n\n        assert mock_imresize.call_count == 0\n\n    def test_max_size_is_int_image_too_large(self):\n        image = np.zeros((10, 20, 3), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(1, 1)\n        aug = iaa.Voronoi(sampler, max_size=10)\n\n        mock_imresize = mock.MagicMock()\n        mock_imresize.return_value = image\n\n        fname = \"imgaug.imresize_single_image\"\n        with mock.patch(fname, mock_imresize):\n            _image_aug = aug(image=image)\n\n        assert mock_imresize.call_count == 1\n        assert mock_imresize.call_args_list[0][0][1] == (5, 10)\n\n    def test_interpolation(self):\n        image = np.zeros((10, 20, 3), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(1, 1)\n        aug = iaa.Voronoi(sampler, max_size=10, interpolation=\"cubic\")\n\n        mock_imresize = mock.MagicMock()\n        mock_imresize.return_value = image\n\n        fname = \"imgaug.imresize_single_image\"\n        with mock.patch(fname, mock_imresize):\n            _image_aug = aug(image=image)\n\n        assert mock_imresize.call_count == 1\n        assert mock_imresize.call_args_list[0][1][\"interpolation\"] == \"cubic\"\n\n    def test_point_sampler_called(self):\n        class LoggedPointSampler(iaa.IPointsSampler):\n            def __init__(self, other):\n                self.other = other\n                self.call_count = 0\n\n            def sample_points(self, images, random_state):\n                self.call_count += 1\n                return self.other.sample_points(images, random_state)\n\n        image = np.zeros((10, 20, 3), dtype=np.uint8)\n        sampler = LoggedPointSampler(iaa.RegularGridPointsSampler(1, 1))\n        aug = iaa.Voronoi(sampler)\n\n        _image_aug = aug(image=image)\n\n        assert sampler.call_count == 1\n\n    def test_point_sampler_returns_no_points_integrationtest(self):\n        class NoPointsPointSampler(iaa.IPointsSampler):\n            def sample_points(self, images, random_state):\n                return [np.zeros((0, 2), dtype=np.float32)]\n\n        image = np.zeros((10, 20, 3), dtype=np.uint8)\n        sampler = NoPointsPointSampler()\n        aug = iaa.Voronoi(sampler)\n\n        image_aug = aug(image=image)\n\n        assert np.array_equal(image_aug, image)\n\n    @classmethod\n    def _test_image_with_n_channels(cls, nb_channels):\n        image = np.zeros((10, 20), dtype=np.uint8)\n        if nb_channels is not None:\n            image = image[..., np.newaxis]\n            image = np.tile(image, (1, 1, nb_channels))\n        sampler = iaa.RegularGridPointsSampler(1, 1)\n        aug = iaa.Voronoi(sampler)\n\n        mock_segment_voronoi = mock.MagicMock()\n        if nb_channels is None:\n            mock_segment_voronoi.return_value = image[..., np.newaxis]\n        else:\n            mock_segment_voronoi.return_value = image\n\n        fname = \"imgaug.augmenters.segmentation.segment_voronoi\"\n        with mock.patch(fname, mock_segment_voronoi):\n            image_aug = aug(image=image)\n\n        assert image_aug.shape == image.shape\n\n    def test_image_with_no_channels(self):\n        self._test_image_with_n_channels(None)\n\n    def test_image_with_one_channel(self):\n        self._test_image_with_n_channels(1)\n\n    def test_image_with_three_channels(self):\n        self._test_image_with_n_channels(3)\n\n    def test_p_replace_is_zero(self):\n        image = np.zeros((50, 50), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(50, 50)\n        aug = iaa.Voronoi(sampler, p_replace=0.0)\n\n        mock_segment_voronoi = mock.MagicMock()\n        mock_segment_voronoi.return_value = image[..., np.newaxis]\n\n        fname = \"imgaug.augmenters.segmentation.segment_voronoi\"\n        with mock.patch(fname, mock_segment_voronoi):\n            _image_aug = aug(image=image)\n\n        replace_mask = mock_segment_voronoi.call_args_list[0][0][2]\n        assert not np.any(replace_mask)\n\n    def test_p_replace_is_one(self):\n        image = np.zeros((50, 50), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(50, 50)\n        aug = iaa.Voronoi(sampler, p_replace=1.0)\n\n        mock_segment_voronoi = mock.MagicMock()\n        mock_segment_voronoi.return_value = image[..., np.newaxis]\n\n        fname = \"imgaug.augmenters.segmentation.segment_voronoi\"\n        with mock.patch(fname, mock_segment_voronoi):\n            _image_aug = aug(image=image)\n\n        replace_mask = mock_segment_voronoi.call_args_list[0][0][2]\n        assert np.all(replace_mask)\n\n    def test_p_replace_is_50_percent(self):\n        image = np.zeros((200, 200), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(200, 200)\n        aug = iaa.Voronoi(sampler, p_replace=0.5)\n\n        mock_segment_voronoi = mock.MagicMock()\n        mock_segment_voronoi.return_value = image[..., np.newaxis]\n\n        fname = \"imgaug.augmenters.segmentation.segment_voronoi\"\n        with mock.patch(fname, mock_segment_voronoi):\n            _image_aug = aug(image=image)\n\n        replace_mask = mock_segment_voronoi.call_args_list[0][0][2]\n        replace_fraction = np.average(replace_mask.astype(np.float32))\n        assert 0.4 <= replace_fraction <= 0.6\n\n    def test_determinism_integrationtest(self):\n        image = np.arange(10*20).astype(np.uint8).reshape((10, 20, 1))\n        image = np.tile(image, (1, 1, 3))\n        image[:, :, 1] += 5\n        image[:, :, 2] += 10\n        sampler = iaa.DropoutPointsSampler(\n            iaa.RegularGridPointsSampler((1, 10), (1, 20)),\n            0.5\n        )\n        aug = iaa.Voronoi(sampler, p_replace=(0.0, 1.0))\n        aug_det = aug.to_deterministic()\n\n        images_aug_a1 = aug(images=[image] * 50)\n        images_aug_a2 = aug(images=[image] * 50)\n\n        images_aug_b1 = aug_det(images=[image] * 50)\n        images_aug_b2 = aug_det(images=[image] * 50)\n\n        same_within_a1 = _all_arrays_identical(images_aug_a1)\n        same_within_a2 = _all_arrays_identical(images_aug_a2)\n\n        same_within_b1 = _all_arrays_identical(images_aug_b1)\n        same_within_b2 = _all_arrays_identical(images_aug_b2)\n\n        same_between_a1_a2 = _array_lists_elementwise_identical(images_aug_a1,\n                                                                images_aug_a2)\n        same_between_b1_b2 = _array_lists_elementwise_identical(images_aug_b1,\n                                                                images_aug_b2)\n\n        assert not same_within_a1\n        assert not same_within_a2\n        assert not same_within_b1\n        assert not same_within_b2\n\n        assert not same_between_a1_a2\n        assert same_between_b1_b2\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        sampler = iaa.RegularGridPointsSampler(50, 50)\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n                aug = iaa.Voronoi(sampler, p_replace=1)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        sampler = iaa.RegularGridPointsSampler(50, 50)\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.full(shape, 128, dtype=np.uint8)\n                aug = iaa.Voronoi(sampler, p_replace=1)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_get_parameters(self):\n        sampler = iaa.RegularGridPointsSampler(1, 1)\n        aug = iaa.Voronoi(sampler, p_replace=0.5, max_size=None,\n                          interpolation=\"cubic\")\n        params = aug.get_parameters()\n        assert params[0] is sampler\n        assert is_parameter_instance(params[1], iap.Binomial)\n        assert np.isclose(params[1].p.value, 0.5)\n        assert params[2] is None\n        assert params[3] == \"cubic\"\n\n    def test_pickleable(self):\n        sampler = iaa.RegularGridPointsSampler(5, 5)\n        aug = iaa.Voronoi(sampler, p_replace=0.5, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=10, shape=(25, 25, 1))\n\n\ndef _all_arrays_identical(arrs):\n    if len(arrs) == 1:\n        return True\n\n    return np.all([np.array_equal(arrs[0], arr_other)\n                   for arr_other in arrs[1:]])\n\n\ndef _array_lists_elementwise_identical(arrs1, arrs2):\n    return np.all([np.array_equal(arr1, arr2)\n                   for arr1, arr2 in zip(arrs1, arrs2)])\n\n\nclass TestUniformVoronoi(unittest.TestCase):\n    def test___init___(self):\n        rs = iarandom.RNG(10)\n\n        mock_voronoi = mock.MagicMock()\n        mock_voronoi.return_value = mock_voronoi\n        fname = \"imgaug.augmenters.segmentation.Voronoi.__init__\"\n        with mock.patch(fname, mock_voronoi):\n            _ = iaa.UniformVoronoi(\n                100,\n                p_replace=0.5,\n                max_size=5,\n                interpolation=\"cubic\",\n                seed=rs,\n                name=\"foo\"\n            )\n\n        assert mock_voronoi.call_count == 1\n        assert isinstance(mock_voronoi.call_args_list[0][1][\"points_sampler\"],\n                          iaa.UniformPointsSampler)\n        assert np.isclose(mock_voronoi.call_args_list[0][1][\"p_replace\"],\n                          0.5)\n        assert mock_voronoi.call_args_list[0][1][\"max_size\"] == 5\n        assert mock_voronoi.call_args_list[0][1][\"interpolation\"] == \"cubic\"\n        assert mock_voronoi.call_args_list[0][1][\"name\"] == \"foo\"\n        assert mock_voronoi.call_args_list[0][1][\"seed\"] is rs\n\n    def test___init___integrationtest(self):\n        rs = iarandom.RNG(10)\n        aug = iaa.UniformVoronoi(\n            100,\n            p_replace=0.5,\n            max_size=5,\n            interpolation=\"cubic\",\n            seed=rs,\n            name=None\n        )\n        assert aug.points_sampler.n_points.value == 100\n        assert np.isclose(aug.p_replace.p.value, 0.5)\n        assert aug.max_size == 5\n        assert aug.interpolation == \"cubic\"\n        assert aug.name == \"UnnamedUniformVoronoi\"\n        assert aug.random_state.equals(rs)\n\n    def test_pickleable(self):\n        aug = iaa.UniformVoronoi((10, 50), p_replace=0.5, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3, shape=(50, 50, 1))\n\n\nclass TestRegularGridVoronoi(unittest.TestCase):\n    def test___init___(self):\n        rs = iarandom.RNG(10)\n\n        mock_voronoi = mock.MagicMock()\n        mock_voronoi.return_value = mock_voronoi\n        fname = \"imgaug.augmenters.segmentation.Voronoi.__init__\"\n        with mock.patch(fname, mock_voronoi):\n            _ = iaa.RegularGridVoronoi(\n                10,\n                20,\n                p_drop_points=0.6,\n                p_replace=0.5,\n                max_size=5,\n                interpolation=\"cubic\",\n                seed=rs,\n                name=\"foo\"\n            )\n\n        assert mock_voronoi.call_count == 1\n        ps = mock_voronoi.call_args_list[0][1][\"points_sampler\"]\n        assert isinstance(ps, iaa.DropoutPointsSampler)\n        assert isinstance(ps.other_points_sampler,\n                          iaa.RegularGridPointsSampler)\n        assert np.isclose(ps.p_drop.p.value, 1-0.6)\n        assert ps.other_points_sampler.n_rows.value == 10\n        assert ps.other_points_sampler.n_cols.value == 20\n        assert np.isclose(mock_voronoi.call_args_list[0][1][\"p_replace\"],\n                          0.5)\n        assert mock_voronoi.call_args_list[0][1][\"max_size\"] == 5\n        assert mock_voronoi.call_args_list[0][1][\"interpolation\"] == \"cubic\"\n        assert mock_voronoi.call_args_list[0][1][\"name\"] == \"foo\"\n        assert mock_voronoi.call_args_list[0][1][\"seed\"] is rs\n\n    def test___init___integrationtest(self):\n        rs = iarandom.RNG(10)\n        aug = iaa.RegularGridVoronoi(\n            10,\n            (10, 30),\n            p_replace=0.5,\n            max_size=5,\n            interpolation=\"cubic\",\n            seed=rs,\n            name=None\n        )\n        assert aug.points_sampler.other_points_sampler.n_rows.value == 10\n        assert is_parameter_instance(\n            aug.points_sampler.other_points_sampler.n_cols,\n            iap.DiscreteUniform\n        )\n        assert aug.points_sampler.other_points_sampler.n_cols.a.value == 10\n        assert aug.points_sampler.other_points_sampler.n_cols.b.value == 30\n        assert np.isclose(aug.p_replace.p.value, 0.5)\n        assert aug.max_size == 5\n        assert aug.interpolation == \"cubic\"\n        assert aug.name == \"UnnamedRegularGridVoronoi\"\n        assert aug.random_state.equals(rs)\n\n    def test_pickleable(self):\n        aug = iaa.RegularGridVoronoi((5, 10), (5, 10), p_replace=0.5,\n                                     seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3, shape=(50, 50, 1))\n\n\nclass TestRelativeRegularGridVoronoi(unittest.TestCase):\n    def test___init___(self):\n        rs = iarandom.RNG(10)\n\n        mock_voronoi = mock.MagicMock()\n        mock_voronoi.return_value = mock_voronoi\n        fname = \"imgaug.augmenters.segmentation.Voronoi.__init__\"\n        with mock.patch(fname, mock_voronoi):\n            _ = iaa.RelativeRegularGridVoronoi(\n                0.1,\n                0.2,\n                p_drop_points=0.6,\n                p_replace=0.5,\n                max_size=5,\n                interpolation=\"cubic\",\n                seed=rs,\n                name=\"foo\"\n            )\n\n        assert mock_voronoi.call_count == 1\n        ps = mock_voronoi.call_args_list[0][1][\"points_sampler\"]\n        assert isinstance(ps, iaa.DropoutPointsSampler)\n        assert isinstance(ps.other_points_sampler,\n                          iaa.RelativeRegularGridPointsSampler)\n        assert np.isclose(ps.p_drop.p.value, 1-0.6)\n        assert np.isclose(ps.other_points_sampler.n_rows_frac.value, 0.1)\n        assert np.isclose(ps.other_points_sampler.n_cols_frac.value, 0.2)\n        assert np.isclose(mock_voronoi.call_args_list[0][1][\"p_replace\"],\n                          0.5)\n        assert mock_voronoi.call_args_list[0][1][\"max_size\"] == 5\n        assert mock_voronoi.call_args_list[0][1][\"interpolation\"] == \"cubic\"\n        assert mock_voronoi.call_args_list[0][1][\"name\"] == \"foo\"\n        assert mock_voronoi.call_args_list[0][1][\"seed\"] is rs\n\n    def test___init___integrationtest(self):\n        rs = iarandom.RNG(10)\n        aug = iaa.RelativeRegularGridVoronoi(\n            0.1,\n            (0.1, 0.3),\n            p_replace=0.5,\n            max_size=5,\n            interpolation=\"cubic\",\n            seed=rs,\n            name=None\n        )\n\n        ps = aug.points_sampler\n        assert np.isclose(ps.other_points_sampler.n_rows_frac.value, 0.1)\n        assert is_parameter_instance(ps.other_points_sampler.n_cols_frac,\n                                     iap.Uniform)\n        assert np.isclose(ps.other_points_sampler.n_cols_frac.a.value, 0.1)\n        assert np.isclose(ps.other_points_sampler.n_cols_frac.b.value, 0.3)\n        assert np.isclose(aug.p_replace.p.value, 0.5)\n        assert aug.max_size == 5\n        assert aug.interpolation == \"cubic\"\n        assert aug.name == \"UnnamedRelativeRegularGridVoronoi\"\n        assert aug.random_state.equals(rs)\n\n    def test_pickleable(self):\n        aug = iaa.RelativeRegularGridVoronoi((0.01, 0.2), (0.01, 0.2),\n                                             p_replace=0.5, seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3, shape=(50, 50, 1))\n\n\n# TODO verify behaviours when image height/width is zero\nclass TestRegularGridPointsSampler(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___(self):\n        sampler = iaa.RegularGridPointsSampler((1, 10), 20)\n        assert is_parameter_instance(sampler.n_rows, iap.DiscreteUniform)\n        assert sampler.n_rows.a.value == 1\n        assert sampler.n_rows.b.value == 10\n        assert sampler.n_cols.value == 20\n\n    def test_sample_single_point(self):\n        image = np.zeros((10, 20, 3), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(1, 1)\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n        assert len(points) == 1\n        assert np.allclose(points[0], [10.0, 5.0])\n\n    def test_sample_points(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(2, 2)\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n        assert len(points) == 4\n        assert np.allclose(points, [\n            [2.5, 2.5],\n            [7.5, 2.5],\n            [2.5, 7.5],\n            [7.5, 7.5]\n        ])\n\n    def test_sample_points_stochastic(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(1, iap.Choice([1, 2]))\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n        matches_single_point = np.allclose(points, [\n            [5.0, 5.0]\n        ])\n        matches_two_points = np.allclose(points, [\n            [2.5, 5.0],\n            [7.5, 5.0]\n        ])\n\n        assert len(points) in [1, 2]\n        assert matches_single_point or matches_two_points\n\n    def test_sample_points_cols_is_zero(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(iap.Deterministic(0), 1)\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n        matches_single_point = np.allclose(points, [\n            [5.0, 5.0]\n        ])\n\n        assert len(points) == 1\n        assert matches_single_point\n\n    def test_sample_points_rows_is_zero(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(1, iap.Deterministic(0))\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n        matches_single_point = np.allclose(points, [\n            [5.0, 5.0]\n        ])\n\n        assert len(points) == 1\n        assert matches_single_point\n\n    def test_sample_points_rows_is_more_than_image_height(self):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(2, 1)\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n        matches_single_point = np.allclose(points, [\n            [0.5, 0.5]\n        ])\n\n        assert len(points) == 1\n        assert matches_single_point\n\n    def test_sample_points_cols_is_more_than_image_width(self):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler(1, 2)\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n        matches_single_point = np.allclose(points, [\n            [0.5, 0.5]\n        ])\n\n        assert len(points) == 1\n        assert matches_single_point\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                sampler = iaa.RegularGridPointsSampler(1, 1)\n\n                points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n                assert len(points) == 1\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                sampler = iaa.RegularGridPointsSampler(1, 1)\n\n                points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n                assert len(points) == 1\n\n    def test_determinism(self):\n        image = np.zeros((500, 500, 1), dtype=np.uint8)\n        sampler = iaa.RegularGridPointsSampler((1, 500), (1, 500))\n        points_seed1_1 = sampler.sample_points([image], 1)[0]\n        points_seed1_2 = sampler.sample_points([image], 1)[0]\n        points_seed2_1 = sampler.sample_points([image], 2)[0]\n\n        assert points_seed1_1.shape == points_seed1_2.shape\n        assert points_seed1_1.shape != points_seed2_1.shape\n\n    def test_conversion_to_string(self):\n        sampler = iaa.RegularGridPointsSampler(10, (10, 30))\n        expected = (\n            \"RegularGridPointsSampler(\"\n            \"%s, \"\n            \"%s\"\n            \")\" % (\n                str(sampler.n_rows),\n                str(sampler.n_cols)\n            )\n        )\n        assert sampler.__str__() == sampler.__repr__() == expected\n\n\nclass TestRelativeRegularGridPointsSampler(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___(self):\n        sampler = iaa.RelativeRegularGridPointsSampler((0.1, 0.2), 0.1)\n        assert is_parameter_instance(sampler.n_rows_frac, iap.Uniform)\n        assert np.isclose(sampler.n_rows_frac.a.value, 0.1)\n        assert np.isclose(sampler.n_rows_frac.b.value, 0.2)\n        assert np.isclose(sampler.n_cols_frac.value, 0.1)\n\n    def test_sample_single_point(self):\n        image = np.zeros((10, 20, 3), dtype=np.uint8)\n        sampler = iaa.RelativeRegularGridPointsSampler(0.001, 0.001)\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n        assert len(points) == 1\n        assert np.allclose(points[0], [10.0, 5.0])\n\n    def test_sample_points(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        sampler = iaa.RelativeRegularGridPointsSampler(0.2, 0.2)\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n        assert len(points) == 4\n        assert np.allclose(points, [\n            [2.5, 2.5],\n            [7.5, 2.5],\n            [2.5, 7.5],\n            [7.5, 7.5]\n        ])\n\n    def test_sample_points_stochastic(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        sampler = iaa.RelativeRegularGridPointsSampler(0.1,\n                                                       iap.Choice([0.1, 0.2]))\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n        matches_single_point = np.allclose(points, [\n            [5.0, 5.0]\n        ])\n        matches_two_points = np.allclose(points, [\n            [2.5, 5.0],\n            [7.5, 5.0]\n        ])\n\n        assert len(points) in [1, 2]\n        assert matches_single_point or matches_two_points\n\n    def test_sample_points_cols_is_zero(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        sampler = iaa.RelativeRegularGridPointsSampler(iap.Deterministic(0.001),\n                                                       0.1)\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n        matches_single_point = np.allclose(points, [\n            [5.0, 5.0]\n        ])\n\n        assert len(points) == 1\n        assert matches_single_point\n\n    def test_sample_points_rows_is_zero(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        sampler = iaa.RelativeRegularGridPointsSampler(0.1,\n                                                       iap.Deterministic(0.001))\n        points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n        matches_single_point = np.allclose(points, [\n            [5.0, 5.0]\n        ])\n\n        assert len(points) == 1\n        assert matches_single_point\n\n    def test_determinism(self):\n        image = np.zeros((500, 500, 1), dtype=np.uint8)\n        sampler = iaa.RelativeRegularGridPointsSampler((0.01, 1.0), (0.1, 1.0))\n        points_seed1_1 = sampler.sample_points([image], 1)[0]\n        points_seed1_2 = sampler.sample_points([image], 1)[0]\n        points_seed2_1 = sampler.sample_points([image], 2)[0]\n\n        assert points_seed1_1.shape == points_seed1_2.shape\n        assert points_seed1_1.shape != points_seed2_1.shape\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                sampler = iaa.RelativeRegularGridPointsSampler(0.01, 0.01)\n\n                points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n                assert len(points) == 1\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                sampler = iaa.RelativeRegularGridPointsSampler(0.01, 0.01)\n\n                points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n                assert len(points) == 1\n\n    def test_conversion_to_string(self):\n        sampler = iaa.RelativeRegularGridPointsSampler(0.01, (0.01, 0.05))\n        expected = (\n            \"RelativeRegularGridPointsSampler(\"\n            \"%s, \"\n            \"%s\"\n            \")\" % (\n                str(sampler.n_rows_frac),\n                str(sampler.n_cols_frac)\n            )\n        )\n        assert sampler.__str__() == sampler.__repr__() == expected\n\n\nclass _FixedPointsSampler(iaa.IPointsSampler):\n    def __init__(self, points):\n        self.points = np.float32(np.copy(points))\n        self.last_random_state = None\n\n    def sample_points(self, images, random_state):\n        self.last_random_state = random_state\n        return np.tile(self.points[np.newaxis, ...], (len(images), 1))\n\n\nclass TestDropoutPointsSampler(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        other = iaa.RegularGridPointsSampler(1, 1)\n        sampler = iaa.DropoutPointsSampler(other, 0.5)\n        assert sampler.other_points_sampler is other\n        assert isinstance(sampler.p_drop, iap.Binomial)\n        assert np.isclose(sampler.p_drop.p.value, 0.5)\n\n    def test_p_drop_is_0_percent(self):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        points = np.linspace(0.0, 1000.0, num=100000)\n        points = np.stack([points, points], axis=-1)\n        other = _FixedPointsSampler(points)\n        sampler = iaa.DropoutPointsSampler(other, 0.0)\n\n        observed = sampler.sample_points([image], 1)[0]\n\n        assert np.allclose(observed, points)\n\n    def test_p_drop_is_100_percent(self):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        points = np.linspace(0.0+0.9, 1000.0-0.9, num=100000)\n        points = np.stack([points, points], axis=-1)\n        other = _FixedPointsSampler(points)\n        sampler = iaa.DropoutPointsSampler(other, 1.0)\n\n        observed = sampler.sample_points([image], 1)[0]\n\n        eps = 1e-4\n        assert len(observed) == 1\n        assert 0.0 + 0.9 - eps <= observed[0][0] <= 1000.0 - 0.9 + eps\n        assert 0.0 + 0.9 - eps <= observed[0][1] <= 1000.0 - 0.9 + eps\n\n    def test_p_drop_is_50_percent(self):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        points = np.linspace(0.0+0.9, 1000.0-0.9, num=100000)\n        points = np.stack([points, points], axis=-1)\n        other = _FixedPointsSampler(points)\n        sampler = iaa.DropoutPointsSampler(other, 0.5)\n\n        observed = sampler.sample_points([image], 1)[0]\n\n        assert 50000 - 1000 <= len(observed) <= 50000 + 1000\n\n    def test_determinism(self):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        points = np.linspace(0.0+0.9, 1000.0-0.9, num=100000)\n        points = np.stack([points, points], axis=-1)\n        other = _FixedPointsSampler(points)\n        sampler = iaa.DropoutPointsSampler(other, (0.3, 0.7))\n\n        observed_s1_1 = sampler.sample_points([image], 1)[0]\n        observed_s1_2 = sampler.sample_points([image], 1)[0]\n        observed_s2_1 = sampler.sample_points([image], 2)[0]\n\n        assert np.allclose(observed_s1_1, observed_s1_2)\n        assert (observed_s1_1.shape != observed_s2_1.shape\n                or not np.allclose(observed_s1_1, observed_s2_1))\n\n    def test_random_state_propagates(self):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        points = np.linspace(0.0+0.9, 1000.0-0.9, num=1)\n        points = np.stack([points, points], axis=-1)\n        other = _FixedPointsSampler(points)\n        sampler = iaa.DropoutPointsSampler(other, 0.5)\n\n        _ = sampler.sample_points([image], 1)[0]\n        rs_s1_1 = other.last_random_state\n        _ = sampler.sample_points([image], 1)[0]\n        rs_s1_2 = other.last_random_state\n        _ = sampler.sample_points([image], 2)[0]\n        rs_s2_1 = other.last_random_state\n\n        assert rs_s1_1.equals(rs_s1_2)\n        assert not rs_s1_1.equals(rs_s2_1)\n\n    def test_conversion_to_string(self):\n        sampler = iaa.DropoutPointsSampler(\n            iaa.RegularGridPointsSampler(10, 20),\n            0.2\n        )\n        expected = (\n            \"DropoutPointsSampler(\"\n            \"RegularGridPointsSampler(\"\n            \"%s, \"\n            \"%s\"\n            \"), \"\n            \"%s\"\n            \")\" % (\n                str(sampler.other_points_sampler.n_rows),\n                str(sampler.other_points_sampler.n_cols),\n                str(sampler.p_drop)\n            )\n        )\n        assert sampler.__str__() == sampler.__repr__() == expected\n\n\nclass TestUniformPointsSampler(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        sampler = iaa.UniformPointsSampler(100)\n        assert is_parameter_instance(sampler.n_points, iap.Deterministic)\n        assert sampler.n_points.value == 100\n\n    def test_sampled_points_not_identical(self):\n        sampler = iaa.UniformPointsSampler(3)\n        images = [np.zeros((1000, 1000, 3), dtype=np.uint8)]\n\n        points = sampler.sample_points(images, 1)[0]\n        points_tpls = [tuple(point) for point in points]\n        n_points = len(points)\n        n_points_uq = len(set(points_tpls))\n\n        assert n_points == 3\n        assert n_points_uq == 3\n\n    def test_sampled_points_uniformly_distributed_by_quadrants(self):\n        # split image into 2x2 quadrants, group all points per quadrant,\n        # assume that at least around N_points/(2*2) points are in each\n        # quadrant\n        sampler = iaa.UniformPointsSampler(10000)\n        images = [np.zeros((1000, 3000, 1), dtype=np.uint8)]\n\n        points = sampler.sample_points(images, 1)[0]\n        points_rel = points.astype(np.float32)\n        points_rel[:, 1] /= 1000\n        points_rel[:, 0] /= 3000\n\n        points_quadrants = np.clip(\n            np.floor(points_rel * 2),\n            0, 1\n        ).astype(np.int32)\n        n_points_per_quadrant = np.zeros((2, 2), dtype=np.int32)\n        np.add.at(\n            n_points_per_quadrant,\n            (points_quadrants[:, 1], points_quadrants[:, 0]),\n            1)\n\n        assert np.all(n_points_per_quadrant > 0.8*(10000/4))\n\n    def test_sampled_points_uniformly_distributed_by_distance_from_origin(self):\n        # Sample N points, compute distances from origin each axis,\n        # split into B bins, assume that each bin contains at least around\n        # N/B points.\n        sampler = iaa.UniformPointsSampler(10000)\n        images = [np.zeros((1000, 3000, 1), dtype=np.uint8)]\n\n        points = sampler.sample_points(images, 1)[0]\n        points_rel = points.astype(np.float32)\n        points_rel[:, 1] /= 1000\n        points_rel[:, 0] /= 3000\n\n        points_bins = np.clip(\n            np.floor(points_rel * 10),\n            0, 1\n        ).astype(np.int32)\n\n        # Don't use euclidean (2d) distance here, but instead axis-wise (1d)\n        # distance. The euclidean distance leads to non-uniform density of\n        # distances, because points on the same \"circle\" have the same\n        # distance, and there are less points close/far away from the origin\n        # that fall on the same circle.\n        points_bincounts_x = np.bincount(points_bins[:, 0])\n        points_bincounts_y = np.bincount(points_bins[:, 1])\n\n        assert np.all(points_bincounts_x > 0.8*(10000/10))\n        assert np.all(points_bincounts_y > 0.8*(10000/10))\n\n    def test_many_images(self):\n        sampler = iaa.UniformPointsSampler(1000)\n        images = [\n            np.zeros((100, 500, 3), dtype=np.uint8),\n            np.zeros((500, 100, 1), dtype=np.uint8)\n        ]\n\n        points = sampler.sample_points(images, 1)\n\n        assert len(points) == 2\n        assert len(points[0]) == 1000\n        assert len(points[1]) == 1000\n        assert not np.allclose(points[0], points[1])\n        assert np.any(points[0][:, 1] < 20)\n        assert np.any(points[0][:, 1] > 0.9*100)\n        assert np.any(points[0][:, 0] < 20)\n        assert np.any(points[0][:, 0] > 0.9*500)\n        assert np.any(points[1][:, 1] < 20)\n        assert np.any(points[1][:, 1] > 0.9*500)\n        assert np.any(points[1][:, 0] < 20)\n        assert np.any(points[1][:, 0] > 0.9*100)\n\n    def test_always_at_least_one_point(self):\n        sampler = iaa.UniformPointsSampler(iap.Deterministic(0))\n        images = [np.zeros((10, 10, 1), dtype=np.uint8)]\n\n        points = sampler.sample_points(images, 1)[0]\n\n        assert len(points) == 1\n\n    def test_n_points_can_vary_between_calls(self):\n        sampler = iaa.UniformPointsSampler(iap.Choice([1, 10]))\n        images = [np.zeros((10, 10, 1), dtype=np.uint8)]\n\n        seen = {1: False, 10: False}\n        for i in sm.xrange(50):\n            points = sampler.sample_points(images, i)[0]\n            seen[len(points)] = True\n            if np.all(seen.values()):\n                break\n\n        assert len(list(seen.keys())) == 2\n        assert np.all(seen.values())\n\n    def test_n_points_can_vary_between_images(self):\n        sampler = iaa.UniformPointsSampler(iap.Choice([1, 10]))\n        images = [\n            np.zeros((10, 10, 1), dtype=np.uint8)\n            for _ in sm.xrange(50)]\n\n        points = sampler.sample_points(images, 1)\n        point_counts = set([len(points_i) for points_i in points])\n\n        assert len(points) == 50\n        assert len(list(point_counts)) == 2\n        assert 1 in point_counts\n        assert 10 in point_counts\n\n    def test_determinism(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        sampler = iaa.UniformPointsSampler(100)\n\n        observed_s1_1 = sampler.sample_points([image], 1)[0]\n        observed_s1_2 = sampler.sample_points([image], 1)[0]\n        observed_s2_1 = sampler.sample_points([image], 2)[0]\n\n        assert np.allclose(observed_s1_1, observed_s1_2)\n        assert not np.allclose(observed_s1_1, observed_s2_1)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                sampler = iaa.UniformPointsSampler(1)\n\n                points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n                # TODO this is not the same as for\n                #      (Relative)RegularGridPointsSampler, which returns in\n                #      this case 0 points\n                assert len(points) == 1\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                sampler = iaa.UniformPointsSampler(1)\n\n                points = sampler.sample_points([image], iarandom.RNG(1))[0]\n\n                assert len(points) == 1\n\n    def test_conversion_to_string(self):\n        sampler = iaa.UniformPointsSampler(10)\n        expected = \"UniformPointsSampler(%s)\" % (\n            str(sampler.n_points)\n        )\n        assert sampler.__str__() == sampler.__repr__() == expected\n\n\nclass TestSubsamplingPointsSampler(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        other = iaa.RegularGridPointsSampler(1, 1)\n        sampler = iaa.SubsamplingPointsSampler(other, 100)\n        assert sampler.other_points_sampler is other\n        assert sampler.n_points_max == 100\n\n    def test_max_is_zero(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        other = iaa.RegularGridPointsSampler(2, 2)\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            sampler = iaa.SubsamplingPointsSampler(other, 0)\n\n        observed = sampler.sample_points([image], 1)[0]\n\n        assert len(observed) == 0\n        assert len(caught_warnings) == 1\n        assert \"n_points_max=0\" in str(caught_warnings[-1].message)\n\n    def test_max_is_above_point_count(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        other = iaa.RegularGridPointsSampler(2, 2)\n        sampler = iaa.SubsamplingPointsSampler(other, 100)\n\n        observed = sampler.sample_points([image], 1)[0]\n\n        assert len(observed) == 4\n        assert np.allclose(observed, [\n            [2.5, 2.5],\n            [7.5, 2.5],\n            [2.5, 7.5],\n            [7.5, 7.5]\n        ])\n\n    def test_max_is_below_point_count(self):\n        image = np.zeros((10, 10, 3), dtype=np.uint8)\n        other = iaa.RegularGridPointsSampler(5, 5)\n        sampler = iaa.SubsamplingPointsSampler(other, 1000)\n\n        observed = sampler.sample_points([image], 1)[0]\n\n        assert len(observed) == 5*5\n\n    def test_max_is_sometimes_below_point_count(self):\n        image = np.zeros((1, 10, 3), dtype=np.uint8)\n        other = iaa.RegularGridPointsSampler(1, (9, 11))\n        sampler = iaa.SubsamplingPointsSampler(other, 1000)\n\n        observed = sampler.sample_points([image] * 100, 1)\n        counts = [len(observed_i) for observed_i in observed]\n        counts_uq = set(counts)\n\n        assert 9 in counts_uq\n        assert 10 in counts_uq\n        assert 11 not in counts_uq\n\n    def test_random_state_propagates(self):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        points = np.linspace(0.0+0.9, 1000.0-0.9, num=1)\n        points = np.stack([points, points], axis=-1)\n        other = _FixedPointsSampler(points)\n        sampler = iaa.SubsamplingPointsSampler(other, 100)\n\n        _ = sampler.sample_points([image], 1)[0]\n        rs_s1_1 = other.last_random_state\n        _ = sampler.sample_points([image], 1)[0]\n        rs_s1_2 = other.last_random_state\n        _ = sampler.sample_points([image], 2)[0]\n        rs_s2_1 = other.last_random_state\n\n        assert rs_s1_1.equals(rs_s1_2)\n        assert not rs_s1_1.equals(rs_s2_1)\n\n    def test_conversion_to_string(self):\n        sampler = iaa.SubsamplingPointsSampler(\n            iaa.RegularGridPointsSampler(10, 20),\n            10\n        )\n        expected = (\n            \"SubsamplingPointsSampler(\"\n            \"RegularGridPointsSampler(\"\n            \"%s, \"\n            \"%s\"\n            \"), \"\n            \"10\"\n            \")\" % (\n                str(sampler.other_points_sampler.n_rows),\n                str(sampler.other_points_sampler.n_cols)\n            )\n        )\n        assert sampler.__str__() == sampler.__repr__() == expected\n"
  },
  {
    "path": "test/augmenters/test_size.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\nimport warnings\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport six.moves as sm\nimport cv2\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug import dtypes as iadt\nfrom imgaug import random as iarandom\nimport imgaug.augmenters.size as iaa_size\nfrom imgaug.testutils import (array_equal_lists, keypoints_equal, reseed,\n                              assert_cbaois_equal,\n                              runtest_pickleable_uint8_img,\n                              is_parameter_instance,\n                              remove_prefetching)\nfrom imgaug.augmentables.heatmaps import HeatmapsOnImage\nfrom imgaug.augmentables.segmaps import SegmentationMapsOnImage\nfrom imgaug.augmenters.size import _prevent_zero_sizes_after_crops_\n\n\nclass Test__prevent_zero_sizes_after_crops_(unittest.TestCase):\n    def test_single_item_arrays_without_crops(self):\n        # axis_sizes, crops_start, crops_end\n        axis_si = np.array([10], dtype=np.int32)\n        crops_s = np.array([0], dtype=np.int32)\n        crops_e = np.array([0], dtype=np.int32)\n\n        cs, ce = _prevent_zero_sizes_after_crops_(\n            axis_si, np.copy(crops_s), np.copy(crops_e)\n        )\n\n        assert np.all(cs == 0)\n        assert np.all(ce == 0)\n\n    def test_single_item_arrays_with_crops_in_bounds(self):\n        # axis_sizes, crops_start, crops_end\n        axis_si = np.array([10], dtype=np.int32)\n        crops_s = np.array([1], dtype=np.int32)\n        crops_e = np.array([2], dtype=np.int32)\n\n        cs, ce = _prevent_zero_sizes_after_crops_(\n            axis_si, np.copy(crops_s), np.copy(crops_e)\n        )\n\n        assert np.all(cs == 1)\n        assert np.all(ce == 2)\n\n    def test_single_item_arrays_with_crops_out_of_bounds(self):\n        # axis_sizes, crops_start, crops_end\n        axis_si = np.array([10], dtype=np.int32)\n        crops_s = np.array([5], dtype=np.int32)\n        crops_e = np.array([20], dtype=np.int32)\n\n        cs, ce = _prevent_zero_sizes_after_crops_(\n            axis_si, np.copy(crops_s), np.copy(crops_e)\n        )\n\n        assert np.all(cs == 0)\n        assert np.all(ce == 9)\n\n    def test_all_crops_zero(self):\n        # axis_sizes, crops_start, crops_end\n        axis_si = np.array([10, 11, 12, 13], dtype=np.int32)\n        crops_s = np.array([0, 0, 0, 0], dtype=np.int32)\n        crops_e = np.array([0, 0, 0, 0], dtype=np.int32)\n\n        cs, ce = _prevent_zero_sizes_after_crops_(\n            axis_si, np.copy(crops_s), np.copy(crops_e)\n        )\n\n        assert np.all(cs == 0)\n        assert np.all(ce == 0)\n\n    def test_all_crops_above_zero_but_none_reaches_zero_size(self):\n        # axis_sizes, crops_start, crops_end\n        axis_si = np.array([10, 11, 12, 13], dtype=np.int32)\n        crops_s = np.array([1, 2, 3, 4], dtype=np.int32)\n        crops_e = np.array([5, 6, 7, 8], dtype=np.int32)\n\n        cs, ce = _prevent_zero_sizes_after_crops_(\n            axis_si, np.copy(crops_s), np.copy(crops_e)\n        )\n\n        assert np.array_equal(cs, crops_s)\n        assert np.array_equal(ce, crops_e)\n\n    def test_some_axes_reach_zero_size(self):\n        axis_si = np.array([10, 11, 12, 13, 14], dtype=np.int32)\n        crops_s = np.array([1, 0, 13, 10, 7], dtype=np.int32)\n        crops_e = np.array([5, 12, 0, 10, 7], dtype=np.int32)\n\n        cs, ce = _prevent_zero_sizes_after_crops_(\n            axis_si, np.copy(crops_s), np.copy(crops_e)\n        )\n\n        assert np.array_equal(cs, [1, 0, 11, 6, 6])\n        assert np.array_equal(ce, [5, 10, 0, 6, 7])\n\n    def test_axis_sizes_of_1(self):\n        # axis_sizes, crops_start, crops_end\n        axis_si = np.array([9, 1, 1, 1, 1, 1, 1, 1], dtype=np.int32)\n        crops_s = np.array([1, 0, 1, 0, 1, 2, 0, 2], dtype=np.int32)\n        crops_e = np.array([5, 0, 0, 1, 1, 0, 2, 2], dtype=np.int32)\n\n        cs, ce = _prevent_zero_sizes_after_crops_(\n            axis_si, np.copy(crops_s), np.copy(crops_e)\n        )\n\n        assert np.array_equal(cs, [1, 0, 0, 0, 0, 0, 0, 0])\n        assert np.array_equal(ce, [5, 0, 0, 0, 0, 0, 0, 0])\n\n    def test_axis_sizes_of_0(self):\n        # axis_sizes, crops_start, crops_end\n        axis_si = np.array([9, 0, 0, 0, 0, 0, 0, 0], dtype=np.int32)\n        crops_s = np.array([1, 0, 1, 0, 1, 2, 0, 2], dtype=np.int32)\n        crops_e = np.array([5, 0, 0, 1, 1, 0, 2, 2], dtype=np.int32)\n\n        cs, ce = _prevent_zero_sizes_after_crops_(\n            axis_si, np.copy(crops_s), np.copy(crops_e)\n        )\n\n        assert np.array_equal(cs, [1, 0, 0, 0, 0, 0, 0, 0])\n        assert np.array_equal(ce, [5, 0, 0, 0, 0, 0, 0, 0])\n\n    def test_with_random_values(self):\n        batch_size = 256\n        for seed in np.arange(100):\n            with self.subTest(seed=seed):\n                rs = iarandom.RNG(seed)\n                axis_sizes = rs.integers(0, 100, size=(batch_size,))\n                crops_start = rs.integers(0, 100, size=(batch_size,))\n                crops_end = rs.integers(0, 100, size=(batch_size,))\n\n                cs, ce = _prevent_zero_sizes_after_crops_(\n                    axis_sizes, np.copy(crops_start), np.copy(crops_end)\n                )\n\n                expected_start = np.zeros((batch_size,), dtype=np.int32)\n                expected_end = np.zeros((batch_size,), dtype=np.int32)\n                gen = enumerate(zip(axis_sizes, crops_start, crops_end))\n                for i, (axs, csi, cei) in gen:\n                    if axs in [0, 1]:\n                        csi = 0\n                        cei = 0\n                    else:\n                        regain = abs(min(axs - csi - cei - 1, 0))\n                        while regain > 0:\n                            csi = csi - np.ceil(regain / 2)\n                            cei = cei - np.floor(regain / 2)\n\n                            if csi < 0:\n                                cei = cei - abs(csi)\n                                csi = 0\n                            if cei < 0:\n                                csi = csi - abs(cei)\n                                cei = 0\n\n                            regain = abs(min(axs - csi - cei, 0))\n                    expected_start[i] = csi\n                    expected_end[i] = cei\n\n                assert np.array_equal(cs, expected_start)\n                assert np.array_equal(ce, expected_end)\n                mask_zeros = (axis_sizes == 0)\n                if np.any(mask_zeros):\n                    assert np.all(\n                        axis_sizes[mask_zeros]\n                        - cs[mask_zeros]\n                        - ce[mask_zeros]\n                        == 0\n                    )\n                if np.any(~mask_zeros):\n                    assert np.all(\n                        axis_sizes[~mask_zeros]\n                        - cs[~mask_zeros]\n                        - ce[~mask_zeros]\n                        >= 1\n                    )\n\n\nclass Test__handle_position_parameter(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_string_uniform(self):\n        observed = iaa_size._handle_position_parameter(\"uniform\")\n        assert isinstance(observed, tuple)\n        assert len(observed) == 2\n        for i in range(2):\n            param = remove_prefetching(observed[i])\n            assert is_parameter_instance(param, iap.Uniform)\n            assert is_parameter_instance(param.a, iap.Deterministic)\n            assert is_parameter_instance(param.b, iap.Deterministic)\n            assert 0.0 - 1e-4 < param.a.value < 0.0 + 1e-4\n            assert 1.0 - 1e-4 < param.b.value < 1.0 + 1e-4\n\n    def test_string_center(self):\n        observed = iaa_size._handle_position_parameter(\"center\")\n        assert isinstance(observed, tuple)\n        assert len(observed) == 2\n        for i in range(2):\n            assert is_parameter_instance(observed[i], iap.Deterministic)\n            assert 0.5 - 1e-4 < observed[i].value < 0.5 + 1e-4\n\n    def test_string_normal(self):\n        observed = iaa_size._handle_position_parameter(\"normal\")\n        assert isinstance(observed, tuple)\n        assert len(observed) == 2\n        for i in range(2):\n            param = remove_prefetching(observed[i])\n            assert is_parameter_instance(param, iap.Clip)\n            assert is_parameter_instance(param.other_param, iap.Normal)\n            assert is_parameter_instance(param.other_param.loc,\n                                         iap.Deterministic)\n            assert is_parameter_instance(param.other_param.scale,\n                                         iap.Deterministic)\n            assert 0.5 - 1e-4 < param.other_param.loc.value < 0.5 + 1e-4\n            assert 0.35/2 - 1e-4 < param.other_param.scale.value < 0.35/2 + 1e-4\n\n    def test_xy_axis_combined_strings(self):\n        pos_x = [\n            (\"left\", 0.0),\n            (\"center\", 0.5),\n            (\"right\", 1.0)\n        ]\n        pos_y = [\n            (\"top\", 0.0),\n            (\"center\", 0.5),\n            (\"bottom\", 1.0)\n        ]\n        for x_str, x_val in pos_x:\n            for y_str, y_val in pos_y:\n                position = \"%s-%s\" % (x_str, y_str)\n                with self.subTest(position=position):\n                    observed = iaa_size._handle_position_parameter(position)\n                    assert is_parameter_instance(observed[0], iap.Deterministic)\n                    assert x_val - 1e-4 < observed[0].value < x_val + 1e-4\n                    assert is_parameter_instance(observed[1], iap.Deterministic)\n                    assert y_val - 1e-4 < observed[1].value < y_val + 1e-4\n\n    def test_stochastic_parameter(self):\n        observed = iaa_size._handle_position_parameter(iap.Poisson(2))\n        assert is_parameter_instance(observed, iap.Poisson)\n\n    def test_tuple_of_floats(self):\n        observed = iaa_size._handle_position_parameter((0.4, 0.6))\n        assert isinstance(observed, tuple)\n        assert len(observed) == 2\n        assert is_parameter_instance(observed[0], iap.Deterministic)\n        assert 0.4 - 1e-4 < observed[0].value < 0.4 + 1e-4\n        assert is_parameter_instance(observed[1], iap.Deterministic)\n        assert 0.6 - 1e-4 < observed[1].value < 0.6 + 1e-4\n\n    def test_tuple_of_floats_outside_value_range_leads_to_failure(self):\n        got_exception = False\n        try:\n            _ = iaa_size._handle_position_parameter((1.2, 0.6))\n        except Exception as e:\n            assert \"must be within the value range\" in str(e)\n            got_exception = True\n        assert got_exception\n\n    def test_tuple_of_stochastic_parameters(self):\n        observed = iaa_size._handle_position_parameter(\n            (iap.Poisson(2), iap.Poisson(3)))\n        assert is_parameter_instance(observed[0], iap.Poisson)\n        assert is_parameter_instance(observed[0].lam, iap.Deterministic)\n        assert 2 - 1e-4 < observed[0].lam.value < 2 + 1e-4\n        assert is_parameter_instance(observed[1], iap.Poisson)\n        assert is_parameter_instance(observed[1].lam, iap.Deterministic)\n        assert 3 - 1e-4 < observed[1].lam.value < 3 + 1e-4\n\n    def test_tuple_of_float_and_stochastic_parameter(self):\n        observed = iaa_size._handle_position_parameter((0.4, iap.Poisson(3)))\n        assert isinstance(observed, tuple)\n        assert len(observed) == 2\n        assert is_parameter_instance(observed[0], iap.Deterministic)\n        assert 0.4 - 1e-4 < observed[0].value < 0.4 + 1e-4\n        assert is_parameter_instance(observed[1], iap.Poisson)\n        assert is_parameter_instance(observed[1].lam, iap.Deterministic)\n        assert 3 - 1e-4 < observed[1].lam.value < 3 + 1e-4\n\n    def test_bad_datatype_leads_to_failure(self):\n        got_exception = False\n        try:\n            _ = iaa_size._handle_position_parameter(False)\n        except Exception as e:\n            assert \"Expected one of the following as position parameter\" in str(e)\n            got_exception = True\n        assert got_exception\n\n\ndef test_pad():\n    # -------\n    # uint, int\n    # -------\n    for dtype in [np.uint8, np.uint16, np.uint32, np.int8, np.int16, np.int32, np.int64]:\n        min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n        arr = np.zeros((3, 3), dtype=dtype) + max_value\n\n        arr_pad = iaa.pad(arr)\n        assert arr_pad.shape == (3, 3)\n        # For some reason, arr_pad.dtype.type == dtype fails here for int64 but not for the other dtypes,\n        # even though int64 is the dtype of arr_pad. Also checked .name and .str for them -- all same value.\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert np.array_equal(arr_pad, arr)\n\n        arr_pad = iaa.pad(arr, top=1)\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert np.all(arr_pad[0, :] == 0)\n\n        arr_pad = iaa.pad(arr, right=1)\n        assert arr_pad.shape == (3, 4)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert np.all(arr_pad[:, -1] == 0)\n\n        arr_pad = iaa.pad(arr, bottom=1)\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert np.all(arr_pad[-1, :] == 0)\n\n        arr_pad = iaa.pad(arr, left=1)\n        assert arr_pad.shape == (3, 4)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert np.all(arr_pad[:, 0] == 0)\n\n        arr_pad = iaa.pad(arr, top=1, right=2, bottom=3, left=4)\n        assert arr_pad.shape == (3+(1+3), 3+(2+4))\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert np.all(arr_pad[0, :] == 0)\n        assert np.all(arr_pad[:, -2:] == 0)\n        assert np.all(arr_pad[-3:, :] == 0)\n        assert np.all(arr_pad[:, :4] == 0)\n\n        arr_pad = iaa.pad(arr, top=1, cval=10)\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert np.all(arr_pad[0, :] == 10)\n\n        arr = np.zeros((3, 3, 3), dtype=dtype) + 127\n        arr_pad = iaa.pad(arr, top=1)\n        assert arr_pad.shape == (4, 3, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert np.all(arr_pad[0, :, 0] == 0)\n        assert np.all(arr_pad[0, :, 1] == 0)\n        assert np.all(arr_pad[0, :, 2] == 0)\n\n        v1 = int(center_value + 0.25 * max_value)\n        v2 = int(center_value + 0.40 * max_value)\n        arr = np.zeros((3, 3), dtype=dtype) + v1\n        arr[1, 1] = v2\n        arr_pad = iaa.pad(arr, top=1, mode=\"maximum\")\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert arr_pad[0, 0] == v1\n        assert arr_pad[0, 1] == v2\n        assert arr_pad[0, 2] == v1\n\n        v1 = int(center_value + 0.25 * max_value)\n        arr = np.zeros((3, 3), dtype=dtype)\n        arr_pad = iaa.pad(arr, top=1, mode=\"constant\", cval=v1)\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert arr_pad[0, 0] == v1\n        assert arr_pad[0, 1] == v1\n        assert arr_pad[0, 2] == v1\n        assert arr_pad[1, 0] == 0\n\n        for nb_channels in [1, 2, 3, 4, 5]:\n            v1 = int(center_value + 0.25 * max_value)\n            arr = np.zeros((3, 3, nb_channels), dtype=dtype)\n            arr_pad = iaa.pad(arr, top=1, mode=\"constant\", cval=v1)\n            assert arr_pad.shape == (4, 3, nb_channels)\n            assert arr_pad.dtype == np.dtype(dtype)\n            assert np.all(arr_pad[0, 0, :] == v1)\n            assert np.all(arr_pad[0, 1, :] == v1)\n            assert np.all(arr_pad[0, 2, :] == v1)\n            assert np.all(arr_pad[1, 0, :] == 0)\n\n        # TODO reactivate this block when np 1.17 pad with mode=linear_ramp\n        #      uint and end_value>edge_value is fixed\n        \"\"\"\n        arr = np.zeros((1, 1), dtype=dtype) + 100\n        arr_pad = iaa.pad(arr, top=4, mode=\"linear_ramp\", cval=100)\n        assert arr_pad.shape == (5, 1)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert arr_pad[0, 0] == 100\n        assert arr_pad[1, 0] == 75\n        assert arr_pad[2, 0] == 50\n        assert arr_pad[3, 0] == 25\n        assert arr_pad[4, 0] == 0\n        \n        arr = np.zeros((1, 1), dtype=dtype) + 100\n        arr_pad = iaa.pad(arr, top=4, mode=\"linear_ramp\", cval=0)\n        assert arr_pad.shape == (5, 1)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert arr_pad[0, 0] == 0\n        assert arr_pad[1, 0] == 25\n        assert arr_pad[2, 0] == 50\n        assert arr_pad[3, 0] == 75\n        assert arr_pad[4, 0] == 100\n        \"\"\"\n\n        # test other channel numbers\n        value = int(center_value + 0.25 * max_value)\n        for nb_channels in [None, 1, 2, 3, 4, 5, 7, 11]:\n            arr = np.full((3, 3), value, dtype=dtype)\n            if nb_channels is not None:\n                arr = arr[..., np.newaxis]\n                arr = np.tile(arr, (1, 1, nb_channels))\n                for c in sm.xrange(nb_channels):\n                    arr[..., c] += c\n            arr_pad = iaa.pad(arr, top=1, mode=\"constant\", cval=0)\n            assert arr_pad.dtype.name == np.dtype(dtype).name\n            if nb_channels is None:\n                assert arr_pad.shape == (4, 3)\n                assert np.all(arr_pad[0, :] == 0)\n                assert np.all(arr_pad[1:, :] == arr)\n            else:\n                assert arr_pad.shape == (4, 3, nb_channels)\n                assert np.all(arr_pad[0, :, :] == 0)\n                assert np.all(arr_pad[1:, :, :] == arr)\n\n        # multi-channel cval\n        value = int(center_value + 0.25 * max_value)\n        arr = np.full((3, 3, 5), value, dtype=dtype)\n        arr_pad = iaa.pad(arr, top=1, mode=\"constant\", cval=(0, 1, 2, 3, 4))\n        assert np.all(arr_pad[0, :, 0] == 0)\n        assert np.all(arr_pad[0, :, 1] == 1)\n        assert np.all(arr_pad[0, :, 2] == 2)\n        assert np.all(arr_pad[0, :, 3] == 3)\n        assert np.all(arr_pad[0, :, 4] == 4)\n\n    # -------\n    # float\n    # -------\n    dtypes = [np.float16, np.float32, np.float64]\n\n    try:\n        # without .type here the dtype(<list>) statements below fail\n        dtypes.append(np.dtype(\"float128\").type)\n    except TypeError:\n        pass  # float128 not known by user system\n\n    for dtype in dtypes:\n        arr = np.zeros((3, 3), dtype=dtype) + 1.0\n\n        def _allclose(a, b):\n            atol = 1e-3 if dtype == np.float16 else 1e-7\n            return np.allclose(a, b, atol=atol, rtol=0)\n\n        arr_pad = iaa.pad(arr)\n        assert arr_pad.shape == (3, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad, arr)\n\n        arr_pad = iaa.pad(arr, top=1)\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad[0, :], dtype([0, 0, 0]))\n\n        arr_pad = iaa.pad(arr, right=1)\n        assert arr_pad.shape == (3, 4)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad[:, -1], dtype([0, 0, 0]))\n\n        arr_pad = iaa.pad(arr, bottom=1)\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad[-1, :], dtype([0, 0, 0]))\n\n        arr_pad = iaa.pad(arr, left=1)\n        assert arr_pad.shape == (3, 4)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad[:, 0], dtype([0, 0, 0]))\n\n        arr_pad = iaa.pad(arr, top=1, right=2, bottom=3, left=4)\n        assert arr_pad.shape == (3+(1+3), 3+(2+4))\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(np.max(arr_pad[0, :]), 0)\n        assert _allclose(np.max(arr_pad[:, -2:]), 0)\n        assert _allclose(np.max(arr_pad[-3, :]), 0)\n        assert _allclose(np.max(arr_pad[:, :4]), 0)\n\n        arr_pad = iaa.pad(arr, top=1, cval=0.2)\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad[0, :], dtype([0.2, 0.2, 0.2]))\n\n        v1 = 1000 ** (np.dtype(dtype).itemsize - 1)\n        arr_pad = iaa.pad(arr, top=1, cval=v1)\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad[0, :], dtype([v1, v1, v1]))\n\n        v1 = (-1000) ** (np.dtype(dtype).itemsize - 1)\n        arr_pad = iaa.pad(arr, top=1, cval=v1)\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad[0, :], dtype([v1, v1, v1]))\n\n        arr = np.zeros((3, 3, 3), dtype=dtype) + 0.5\n        arr_pad = iaa.pad(arr, top=1)\n        assert arr_pad.shape == (4, 3, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad[0, :, 0], dtype([0, 0, 0]))\n        assert _allclose(arr_pad[0, :, 1], dtype([0, 0, 0]))\n        assert _allclose(arr_pad[0, :, 2], dtype([0, 0, 0]))\n\n        arr = np.zeros((3, 3), dtype=dtype) + 0.5\n        arr[1, 1] = 0.75\n        arr_pad = iaa.pad(arr, top=1, mode=\"maximum\")\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad[0, 0], 0.5)\n        assert _allclose(arr_pad[0, 1], 0.75)\n        assert _allclose(arr_pad[0, 2], 0.50)\n\n        arr = np.zeros((3, 3), dtype=dtype)\n        arr_pad = iaa.pad(arr, top=1, mode=\"constant\", cval=0.4)\n        assert arr_pad.shape == (4, 3)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad[0, 0], 0.4)\n        assert _allclose(arr_pad[0, 1], 0.4)\n        assert _allclose(arr_pad[0, 2], 0.4)\n        assert _allclose(arr_pad[1, 0], 0.0)\n\n        for nb_channels in [1, 2, 3, 4, 5]:\n            arr = np.zeros((3, 3, nb_channels), dtype=dtype)\n            arr_pad = iaa.pad(arr, top=1, mode=\"constant\", cval=0.4)\n            assert arr_pad.shape == (4, 3, nb_channels)\n            assert arr_pad.dtype == np.dtype(dtype)\n            assert _allclose(arr_pad[0, 0, :], 0.4)\n            assert _allclose(arr_pad[0, 1, :], 0.4)\n            assert _allclose(arr_pad[0, 2, :], 0.4)\n            assert _allclose(arr_pad[1, 0, :], 0.0)\n\n        arr = np.zeros((1, 1), dtype=dtype) + 0.6\n        arr_pad = iaa.pad(arr, top=4, mode=\"linear_ramp\", cval=1.0)\n        assert arr_pad.shape == (5, 1)\n        assert arr_pad.dtype == np.dtype(dtype)\n        assert _allclose(arr_pad[0, 0], 1.0)\n        assert _allclose(arr_pad[1, 0], 0.9)\n        assert _allclose(arr_pad[2, 0], 0.8)\n        assert _allclose(arr_pad[3, 0], 0.7)\n        assert _allclose(arr_pad[4, 0], 0.6)\n\n        # test other channel numbers\n        value = 1000 ** (np.dtype(dtype).itemsize - 1)\n        for nb_channels in [None, 1, 2, 3, 4, 5, 7, 11]:\n            arr = np.full((3, 3), value, dtype=dtype)\n            if nb_channels is not None:\n                arr = arr[..., np.newaxis]\n                arr = np.tile(arr, (1, 1, nb_channels))\n                for c in sm.xrange(nb_channels):\n                    arr[..., c] += c\n            arr_pad = iaa.pad(arr, top=1, mode=\"constant\", cval=0)\n            assert arr_pad.dtype.name == np.dtype(dtype).name\n            if nb_channels is None:\n                assert arr_pad.shape == (4, 3)\n                assert _allclose(arr_pad[0, :], 0)\n                assert _allclose(arr_pad[1:, :], arr)\n            else:\n                assert arr_pad.shape == (4, 3, nb_channels)\n                assert _allclose(arr_pad[0, :, :], 0)\n                assert _allclose(arr_pad[1:, :, :], arr)\n\n        # multi-channel cval\n        value = int(center_value + 0.25 * max_value)\n        arr = np.full((3, 3, 5), value, dtype=dtype)\n        arr_pad = iaa.pad(arr, top=1, mode=\"constant\", cval=(0, 1, 2, 3, 4))\n        assert _allclose(arr_pad[0, :, 0], 0)\n        assert _allclose(arr_pad[0, :, 1], 1)\n        assert _allclose(arr_pad[0, :, 2], 2)\n        assert _allclose(arr_pad[0, :, 3], 3)\n        assert _allclose(arr_pad[0, :, 4], 4)\n\n    # -------\n    # bool\n    # -------\n    dtype = bool\n    arr = np.zeros((3, 3), dtype=dtype)\n    arr_pad = iaa.pad(arr)\n    assert arr_pad.shape == (3, 3)\n    # For some reason, arr_pad.dtype.type == dtype fails here for int64 but not for the other dtypes,\n    # even though int64 is the dtype of arr_pad. Also checked .name and .str for them -- all same value.\n    assert arr_pad.dtype == np.dtype(dtype)\n    assert np.all(arr_pad == arr)\n\n    arr_pad = iaa.pad(arr, top=1)\n    assert arr_pad.shape == (4, 3)\n    assert arr_pad.dtype == np.dtype(dtype)\n    assert np.all(arr_pad[0, :] == 0)\n\n    arr_pad = iaa.pad(arr, top=1, cval=True)\n    assert arr_pad.shape == (4, 3)\n    assert arr_pad.dtype == np.dtype(dtype)\n    assert np.all(arr_pad[0, :] == 1)\n\n\ndef test_compute_paddings_for_aspect_ratio():\n    arr = np.zeros((4, 4), dtype=np.uint8)\n    top, right, bottom, left = \\\n        iaa.compute_paddings_to_reach_aspect_ratio(arr, 1.0)\n    assert top == 0\n    assert right == 0\n    assert bottom == 0\n    assert left == 0\n\n    arr = np.zeros((1, 4), dtype=np.uint8)\n    top, right, bottom, left = \\\n        iaa.compute_paddings_to_reach_aspect_ratio(arr, 1.0)\n    assert top == 1\n    assert right == 0\n    assert bottom == 2\n    assert left == 0\n\n    arr = np.zeros((4, 1), dtype=np.uint8)\n    top, right, bottom, left = \\\n        iaa.compute_paddings_to_reach_aspect_ratio(arr, 1.0)\n    assert top == 0\n    assert right == 2\n    assert bottom == 0\n    assert left == 1\n\n    arr = np.zeros((2, 4), dtype=np.uint8)\n    top, right, bottom, left = \\\n        iaa.compute_paddings_to_reach_aspect_ratio(arr, 1.0)\n    assert top == 1\n    assert right == 0\n    assert bottom == 1\n    assert left == 0\n\n    arr = np.zeros((4, 2), dtype=np.uint8)\n    top, right, bottom, left = \\\n        iaa.compute_paddings_to_reach_aspect_ratio(arr, 1.0)\n    assert top == 0\n    assert right == 1\n    assert bottom == 0\n    assert left == 1\n\n    arr = np.zeros((4, 4), dtype=np.uint8)\n    top, right, bottom, left = \\\n        iaa.compute_paddings_to_reach_aspect_ratio(arr, 0.5)\n    assert top == 2\n    assert right == 0\n    assert bottom == 2\n    assert left == 0\n\n    arr = np.zeros((4, 4), dtype=np.uint8)\n    top, right, bottom, left = \\\n        iaa.compute_paddings_to_reach_aspect_ratio(arr, 2.0)\n    assert top == 0\n    assert right == 2\n    assert bottom == 0\n    assert left == 2\n\n\ndef test_pad_to_aspect_ratio():\n    for dtype in [np.uint8, np.int32, np.float32]:\n        dtype = np.dtype(dtype)\n\n        # aspect_ratio = 1.0\n        arr = np.zeros((4, 4), dtype=dtype)\n        arr_pad = iaa.pad_to_aspect_ratio(arr, 1.0)\n        assert arr_pad.dtype.name == dtype.name\n        assert arr_pad.shape[0] == 4\n        assert arr_pad.shape[1] == 4\n\n        arr = np.zeros((1, 4), dtype=dtype)\n        arr_pad = iaa.pad_to_aspect_ratio(arr, 1.0)\n        assert arr_pad.dtype.name == dtype.name\n        assert arr_pad.shape[0] == 4\n        assert arr_pad.shape[1] == 4\n\n        arr = np.zeros((4, 1), dtype=dtype)\n        arr_pad = iaa.pad_to_aspect_ratio(arr, 1.0)\n        assert arr_pad.dtype.name == dtype.name\n        assert arr_pad.shape[0] == 4\n        assert arr_pad.shape[1] == 4\n\n        arr = np.zeros((2, 4), dtype=dtype)\n        arr_pad = iaa.pad_to_aspect_ratio(arr, 1.0)\n        assert arr_pad.dtype.name == dtype.name\n        assert arr_pad.shape[0] == 4\n        assert arr_pad.shape[1] == 4\n\n        arr = np.zeros((4, 2), dtype=dtype)\n        arr_pad = iaa.pad_to_aspect_ratio(arr, 1.0)\n        assert arr_pad.dtype.name == dtype.name\n        assert arr_pad.shape[0] == 4\n        assert arr_pad.shape[1] == 4\n\n        # aspect_ratio != 1.0\n        arr = np.zeros((4, 4), dtype=dtype)\n        arr_pad = iaa.pad_to_aspect_ratio(arr, 2.0)\n        assert arr_pad.dtype.name == dtype.name\n        assert arr_pad.shape[0] == 4\n        assert arr_pad.shape[1] == 8\n\n        arr = np.zeros((4, 4), dtype=dtype)\n        arr_pad = iaa.pad_to_aspect_ratio(arr, 0.5)\n        assert arr_pad.dtype.name == dtype.name\n        assert arr_pad.shape[0] == 8\n        assert arr_pad.shape[1] == 4\n\n        # 3d arr\n        arr = np.zeros((4, 2, 3), dtype=dtype)\n        arr_pad = iaa.pad_to_aspect_ratio(arr, 1.0)\n        assert arr_pad.dtype.name == dtype.name\n        assert arr_pad.shape[0] == 4\n        assert arr_pad.shape[1] == 4\n        assert arr_pad.shape[2] == 3\n\n    # cval\n    arr = np.zeros((4, 4), dtype=np.uint8) + 128\n    arr_pad = iaa.pad_to_aspect_ratio(arr, 2.0)\n    assert arr_pad.shape[0] == 4\n    assert arr_pad.shape[1] == 8\n    assert np.max(arr_pad[:, 0:2]) == 0\n    assert np.max(arr_pad[:, -2:]) == 0\n    assert np.max(arr_pad[:, 2:-2]) == 128\n\n    arr = np.zeros((4, 4), dtype=np.uint8) + 128\n    arr_pad = iaa.pad_to_aspect_ratio(arr, 2.0, cval=10)\n    assert arr_pad.shape[0] == 4\n    assert arr_pad.shape[1] == 8\n    assert np.max(arr_pad[:, 0:2]) == 10\n    assert np.max(arr_pad[:, -2:]) == 10\n    assert np.max(arr_pad[:, 2:-2]) == 128\n\n    arr = np.zeros((4, 4), dtype=np.float32) + 0.5\n    arr_pad = iaa.pad_to_aspect_ratio(arr, 2.0, cval=0.0)\n    assert arr_pad.shape[0] == 4\n    assert arr_pad.shape[1] == 8\n    assert 0 - 1e-6 <= np.max(arr_pad[:, 0:2]) <= 0 + 1e-6\n    assert 0 - 1e-6 <= np.max(arr_pad[:, -2:]) <= 0 + 1e-6\n    assert 0.5 - 1e-6 <= np.max(arr_pad[:, 2:-2]) <= 0.5 + 1e-6\n\n    arr = np.zeros((4, 4), dtype=np.float32) + 0.5\n    arr_pad = iaa.pad_to_aspect_ratio(arr, 2.0, cval=0.1)\n    assert arr_pad.shape[0] == 4\n    assert arr_pad.shape[1] == 8\n    assert 0.1 - 1e-6 <= np.max(arr_pad[:, 0:2]) <= 0.1 + 1e-6\n    assert 0.1 - 1e-6 <= np.max(arr_pad[:, -2:]) <= 0.1 + 1e-6\n    assert 0.5 - 1e-6 <= np.max(arr_pad[:, 2:-2]) <= 0.5 + 1e-6\n\n    # mode\n    arr = np.zeros((4, 4), dtype=np.uint8) + 128\n    arr[1:3, 1:3] = 200\n    arr_pad = iaa.pad_to_aspect_ratio(arr, 2.0, mode=\"maximum\")\n    assert arr_pad.shape[0] == 4\n    assert arr_pad.shape[1] == 8\n    assert np.max(arr_pad[0:1, 0:2]) == 128\n    assert np.max(arr_pad[1:3, 0:2]) == 200\n    assert np.max(arr_pad[3:, 0:2]) == 128\n    assert np.max(arr_pad[0:1, -2:]) == 128\n    assert np.max(arr_pad[1:3, -2:]) == 200\n    assert np.max(arr_pad[3:, -2:]) == 128\n\n    # TODO add tests for return_pad_values=True\n\n\nclass Test_compute_paddings_to_reach_multiples_of(unittest.TestCase):\n    def test_zero_height_array(self):\n        arr = np.zeros((0, 2, 3), dtype=np.uint8)\n        paddings = iaa.compute_paddings_to_reach_multiples_of(arr, 2, 2)\n        assert paddings == (1, 0, 1, 0)\n\n    def test_zero_width_array(self):\n        arr = np.zeros((2, 0, 3), dtype=np.uint8)\n        paddings = iaa.compute_paddings_to_reach_multiples_of(arr, 2, 2)\n        assert paddings == (0, 1, 0, 1)\n\n    def test_both_none(self):\n        arr = np.zeros((1, 1, 3), dtype=np.uint8)\n        paddings = iaa.compute_paddings_to_reach_multiples_of(arr, None, None)\n        assert paddings == (0, 0, 0, 0)\n\n    def test_height_is_none(self):\n        arr = np.zeros((1, 1, 3), dtype=np.uint8)\n        paddings = iaa.compute_paddings_to_reach_multiples_of(arr, None, 2)\n        assert paddings == (0, 1, 0, 0)\n\n    def test_width_is_none(self):\n        arr = np.zeros((1, 1, 3), dtype=np.uint8)\n        paddings = iaa.compute_paddings_to_reach_multiples_of(arr, 2, None)\n        assert paddings == (0, 0, 1, 0)\n\n    def test_height_is_one(self):\n        arr = np.zeros((1, 1, 3), dtype=np.uint8)\n        paddings = iaa.compute_paddings_to_reach_multiples_of(arr, 1, 2)\n        assert paddings == (0, 1, 0, 0)\n\n    def test_width_is_one(self):\n        arr = np.zeros((1, 1, 3), dtype=np.uint8)\n        paddings = iaa.compute_paddings_to_reach_multiples_of(arr, 2, 1)\n        assert paddings == (0, 0, 1, 0)\n\n    def test_various_widths(self):\n        nb_channels_lst = [None, 1, 3, 4]\n        amounts = [2, 3, 4, 5, 6, 7, 8, 9]\n        expecteds = [\n            (0, 1, 0, 0),\n            (0, 1, 0, 0),\n            (0, 2, 0, 1),\n            (0, 0, 0, 0),\n            (0, 1, 0, 0),\n            (0, 1, 0, 1),\n            (0, 2, 0, 1),\n            (0, 2, 0, 2)\n        ]\n\n        for amount, expected in zip(amounts, expecteds):\n            for nb_channels in nb_channels_lst:\n                with self.subTest(width_multiple=amount,\n                                  nb_channels=nb_channels):\n                    if nb_channels is None:\n                        arr = np.zeros((3, 5), dtype=np.uint8)\n                    else:\n                        arr = np.zeros((3, 5, nb_channels), dtype=np.uint8)\n\n                    paddings = iaa.compute_paddings_to_reach_multiples_of(\n                        arr, None, amount)\n\n                    assert paddings == expected\n\n    def test_various_heights(self):\n        nb_channels_lst = [None, 1, 3, 4]\n        amounts = [2, 3, 4, 5, 6, 7, 8, 9]\n        expecteds = [\n            (0, 0, 1, 0),\n            (0, 0, 1, 0),\n            (1, 0, 2, 0),\n            (0, 0, 0, 0),\n            (0, 0, 1, 0),\n            (1, 0, 1, 0),\n            (1, 0, 2, 0),\n            (2, 0, 2, 0)\n        ]\n        for amount, expected in zip(amounts, expecteds):\n            for nb_channels in nb_channels_lst:\n                with self.subTest(height_multiple=amount,\n                                  nb_channels=nb_channels):\n                    if nb_channels is None:\n                        arr = np.zeros((5, 3), dtype=np.uint8)\n                    else:\n                        arr = np.zeros((5, 3, nb_channels), dtype=np.uint8)\n\n                    paddings = iaa.compute_paddings_to_reach_multiples_of(\n                        arr, amount, None)\n\n                    assert paddings == expected\n\n\nclass Test_pad_to_multiples_of(unittest.TestCase):\n    @mock.patch(\"imgaug.augmenters.size.compute_paddings_to_reach_multiples_of\")\n    @mock.patch(\"imgaug.augmenters.size.pad\")\n    def test_mocked(self, mock_pad, mock_compute_pads):\n        mock_compute_pads.return_value = (1, 2, 3, 4)\n        mock_pad.return_value = \"padded_array\"\n\n        arr = np.ones((3, 5, 1), dtype=np.uint8)\n\n        arr_padded = iaa.pad_to_multiples_of(\n            arr, 10, 20, mode=\"foo\", cval=100)\n\n        mock_compute_pads.assert_called_once_with(arr, 10, 20)\n        mock_pad.assert_called_once_with(arr, top=1, right=2, bottom=3,\n                                         left=4, mode=\"foo\", cval=100)\n        assert arr_padded == \"padded_array\"\n\n    @mock.patch(\"imgaug.augmenters.size.compute_paddings_to_reach_multiples_of\")\n    @mock.patch(\"imgaug.augmenters.size.pad\")\n    def test_mocked_return_pad_amounts(self, mock_pad, mock_compute_pads):\n        mock_compute_pads.return_value = (1, 2, 3, 4)\n        mock_pad.return_value = \"padded_array\"\n\n        arr = np.ones((3, 5, 1), dtype=np.uint8)\n\n        arr_padded, paddings = iaa.pad_to_multiples_of(\n            arr, 10, 20, mode=\"foo\", cval=100, return_pad_amounts=True)\n\n        mock_compute_pads.assert_called_once_with(arr, 10, 20)\n        mock_pad.assert_called_once_with(arr, top=1, right=2, bottom=3,\n                                         left=4, mode=\"foo\", cval=100)\n        assert arr_padded == \"padded_array\"\n        assert paddings == (1, 2, 3, 4)\n\n    def test_integrationtest(self):\n        dtypes = [np.uint8, np.int32, np.float32]\n        nb_channels_lst = [None, 1, 3, 4]\n\n        for dtype in dtypes:\n            dtype = np.dtype(dtype)\n            for nb_channels in nb_channels_lst:\n                with self.subTest(dtype=dtype.name, nb_channels=nb_channels):\n                    if nb_channels is None:\n                        arr = np.ones((3, 5), dtype=dtype)\n                    else:\n                        arr = np.ones((3, 5, nb_channels), dtype=dtype)\n\n                    arr_padded = iaa.pad_to_multiples_of(arr, 7, 11, cval=2)\n\n                    if nb_channels is None:\n                        base_area = 3*5\n                        new_area = 7*11 - base_area\n                        assert arr_padded.shape == (7, 11)\n                        assert np.sum(arr_padded) == 1*base_area + 2*new_area\n                    else:\n                        base_area = 3*5*nb_channels\n                        new_area = 7*11*nb_channels - base_area\n                        assert arr_padded.shape == (7, 11, nb_channels)\n                        assert np.sum(arr_padded) == 1*base_area + 2*new_area\n\n\nclass TestResize(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image2d(self):\n        # 4x8\n        base_img2d = [\n            [0, 0, 0, 0, 0, 0, 0, 0],\n            [0, 255, 255, 255, 255, 255, 255, 0],\n            [0, 255, 255, 255, 255, 255, 255, 0],\n            [0, 0, 0, 0, 0, 0, 0, 0]\n        ]\n        base_img2d = np.array(base_img2d, dtype=np.uint8)\n        return base_img2d\n\n    @property\n    def image3d(self):\n        base_img3d = np.tile(self.image2d[..., np.newaxis], (1, 1, 3))\n        return base_img3d\n\n    @property\n    def kpsoi2d(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=4, y=1)]\n        return ia.KeypointsOnImage(kps, shape=self.image2d.shape)\n\n    @property\n    def kpsoi3d(self):\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=4, y=1)]\n        return ia.KeypointsOnImage(kps, shape=self.image3d.shape)\n\n    @property\n    def psoi2d(self):\n        polygons = [\n            ia.Polygon([(0, 0), (8, 0), (8, 4)]),\n            ia.Polygon([(1, 1), (7, 1), (7, 3), (1, 3)]),\n        ]\n        return ia.PolygonsOnImage(polygons, shape=self.image2d.shape)\n\n    @property\n    def psoi3d(self):\n        polygons = [\n            ia.Polygon([(0, 0), (8, 0), (8, 4)]),\n            ia.Polygon([(1, 1), (7, 1), (7, 3), (1, 3)]),\n        ]\n        return ia.PolygonsOnImage(polygons, shape=self.image3d.shape)\n\n    @property\n    def lsoi2d(self):\n        lss = [\n            ia.LineString([(0, 0), (8, 0), (8, 4)]),\n            ia.LineString([(1, 1), (7, 1), (7, 3), (1, 3)]),\n        ]\n        return ia.LineStringsOnImage(lss, shape=self.image2d.shape)\n\n    @property\n    def lsoi3d(self):\n        lss = [\n            ia.LineString([(0, 0), (8, 0), (8, 4)]),\n            ia.LineString([(1, 1), (7, 1), (7, 3), (1, 3)]),\n        ]\n        return ia.LineStringsOnImage(lss, shape=self.image3d.shape)\n\n    @property\n    def bbsoi2d(self):\n        bbs = [\n            ia.BoundingBox(x1=0, y1=0, x2=8, y2=4),\n            ia.BoundingBox(x1=1, y1=2, x2=6, y2=3),\n        ]\n        return ia.BoundingBoxesOnImage(bbs, shape=self.image2d.shape)\n\n    @property\n    def bbsoi3d(self):\n        bbs = [\n            ia.BoundingBox(x1=0, y1=0, x2=8, y2=4),\n            ia.BoundingBox(x1=1, y1=2, x2=6, y2=3),\n        ]\n        return ia.BoundingBoxesOnImage(bbs, shape=self.image3d.shape)\n\n    @classmethod\n    def _aspect_ratio(cls, image):\n        return image.shape[1] / image.shape[0]\n\n    def test_resize_to_fixed_int(self):\n        aug = iaa.Resize(12)\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        assert observed2d.shape == (12, 12)\n        assert observed3d.shape == (12, 12, 3)\n        assert 50 < np.average(observed2d) < 205\n        assert 50 < np.average(observed3d) < 205\n\n    def test_resize_to_fixed_float(self):\n        aug = iaa.Resize(0.5)\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        assert observed2d.shape == (2, 4)\n        assert observed3d.shape == (2, 4, 3)\n        assert 50 < np.average(observed2d) < 205\n        assert 50 < np.average(observed3d) < 205\n\n    def test_heatmaps_with_width_int_and_height_int(self):\n        aug = iaa.Resize({\"height\": 8, \"width\": 12})\n        heatmaps_arr = (self.image2d / 255.0).astype(np.float32)\n        heatmaps_aug = aug.augment_heatmaps([\n            HeatmapsOnImage(heatmaps_arr, shape=self.image3d.shape)])[0]\n        assert heatmaps_aug.shape == (8, 12, 3)\n        assert 0 - 1e-6 < heatmaps_aug.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < heatmaps_aug.max_value < 1 + 1e-6\n        assert np.average(heatmaps_aug.get_arr()[0, :]) < 0.05\n        assert np.average(heatmaps_aug.get_arr()[-1, :]) < 0.05\n        assert np.average(heatmaps_aug.get_arr()[:, 0]) < 0.05\n        assert 0.8 < np.average(heatmaps_aug.get_arr()[2:6, 2:10]) < 1 + 1e-6\n\n    def test_segmaps_with_width_int_and_height_int(self):\n        for nb_channels in [None, 1, 10]:\n            aug = iaa.Resize({\"height\": 8, \"width\": 12})\n            segmaps_arr = (self.image2d > 0).astype(np.int32)\n            if nb_channels is not None:\n                segmaps_arr = np.tile(\n                    segmaps_arr[..., np.newaxis], (1, 1, nb_channels))\n            segmaps_aug = aug.augment_segmentation_maps([\n                SegmentationMapsOnImage(segmaps_arr, shape=self.image3d.shape)])[0]\n            assert segmaps_aug.shape == (8, 12, 3)\n            assert segmaps_aug.arr.shape == (8, 12, nb_channels if nb_channels is not None else 1)\n            assert np.all(segmaps_aug.arr[0, 1:-1, :] == 0)\n            assert np.all(segmaps_aug.arr[-1, 1:-1, :] == 0)\n            assert np.all(segmaps_aug.arr[1:-1, 0, :] == 0)\n            assert np.all(segmaps_aug.arr[1:-1, -1, :] == 0)\n            assert np.all(segmaps_aug.arr[2:-2, 2:-2, :] == 1)\n\n    def test_heatmaps_with_diff_size_than_img_and_width_float_height_int(self):\n        aug = iaa.Resize({\"width\": 2.0, \"height\": 16})\n        heatmaps_arr = (self.image2d / 255.0).astype(np.float32)\n        heatmaps = HeatmapsOnImage(\n            heatmaps_arr,\n            shape=(2*self.image3d.shape[0], 2*self.image3d.shape[1], 3))\n        heatmaps_aug = aug.augment_heatmaps([heatmaps])[0]\n        assert heatmaps_aug.shape == (16, int(self.image3d.shape[1]*2*2), 3)\n        assert heatmaps_aug.arr_0to1.shape == (8, 16, 1)\n        assert 0 - 1e-6 < heatmaps_aug.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < heatmaps_aug.max_value < 1 + 1e-6\n        assert np.average(heatmaps_aug.get_arr()[0, :]) < 0.05\n        assert np.average(heatmaps_aug.get_arr()[-1:, :]) < 0.05\n        assert np.average(heatmaps_aug.get_arr()[:, 0]) < 0.05\n        assert 0.8 < np.average(heatmaps_aug.get_arr()[2:6, 2:10]) < 1 + 1e-6\n\n    def test_segmaps_with_diff_size_than_img_and_width_float_height_int(self):\n        aug = iaa.Resize({\"width\": 2.0, \"height\": 16})\n        segmaps_arr = (self.image2d > 0).astype(np.int32)\n        segmaps = SegmentationMapsOnImage(\n            segmaps_arr,\n            shape=(2*self.image3d.shape[0], 2*self.image3d.shape[1], 3))\n        segmaps_aug = aug.augment_segmentation_maps([segmaps])[0]\n        assert segmaps_aug.shape == (16, int(self.image3d.shape[1]*2*2), 3)\n        assert segmaps_aug.arr.shape == (8, 16, 1)\n        assert np.all(segmaps_aug.arr[0, 1:-1, :] == 0)\n        assert np.all(segmaps_aug.arr[-1, 1:-1, :] == 0)\n        assert np.all(segmaps_aug.arr[1:-1, 0, :] == 0)\n        assert np.all(segmaps_aug.arr[1:-1, -1, :] == 0)\n        assert np.all(segmaps_aug.arr[2:-2, 2:-2, :] == 1)\n\n    def test_keypoints_on_3d_img_and_with_width_int_and_height_int(self):\n        aug = iaa.Resize({\"height\": 8, \"width\": 12})\n        kpsoi_aug = aug.augment_keypoints([self.kpsoi3d])[0]\n        assert len(kpsoi_aug.keypoints) == 2\n        assert kpsoi_aug.shape == (8, 12, 3)\n        assert np.allclose(kpsoi_aug.keypoints[0].x, 1.5)\n        assert np.allclose(kpsoi_aug.keypoints[0].y, 4)\n        assert np.allclose(kpsoi_aug.keypoints[1].x, 6)\n        assert np.allclose(kpsoi_aug.keypoints[1].y, 2)\n\n    def test_polygons_on_3d_img_and_with_width_int_and_height_int(self):\n        aug = iaa.Resize({\"width\": 12, \"height\": 8})\n        cbaoi_aug = aug.augment_polygons(self.psoi3d)\n        assert len(cbaoi_aug.items) == 2\n        assert cbaoi_aug.shape == (8, 12, 3)\n        assert cbaoi_aug.items[0].coords_almost_equals(\n            [(0, 0), (12, 0), (12, 8)]\n        )\n        assert cbaoi_aug.items[1].coords_almost_equals(\n            [(1.5, 2), (10.5, 2), (10.5, 6), (1.5, 6)]\n        )\n\n    def test_line_strings_on_3d_img_and_with_width_int_and_height_int(self):\n        aug = iaa.Resize({\"width\": 12, \"height\": 8})\n        cbaoi_aug = aug.augment_line_strings(self.lsoi3d)\n        assert len(cbaoi_aug.items) == 2\n        assert cbaoi_aug.shape == (8, 12, 3)\n        assert cbaoi_aug.items[0].coords_almost_equals(\n            [(0, 0), (12, 0), (12, 8)]\n        )\n        assert cbaoi_aug.items[1].coords_almost_equals(\n            [(1.5, 2), (10.5, 2), (10.5, 6), (1.5, 6)]\n        )\n\n    def test_bounding_boxes_on_3d_img_and_with_width_int_and_height_int(self):\n        aug = iaa.Resize({\"width\": 12, \"height\": 8})\n        bbsoi_aug = aug.augment_bounding_boxes(self.bbsoi3d)\n        assert len(bbsoi_aug.bounding_boxes) == 2\n        assert bbsoi_aug.shape == (8, 12, 3)\n        assert bbsoi_aug.bounding_boxes[0].coords_almost_equals(\n            [(0, 0), (12, 8)]\n        )\n        assert bbsoi_aug.bounding_boxes[1].coords_almost_equals(\n            [((1/8)*12, (2/4)*8), ((6/8)*12, (3/4)*8)]\n        )\n\n    def test_keypoints_on_2d_img_and_with_width_float_and_height_int(self):\n        aug = iaa.Resize({\"width\": 3.0, \"height\": 8})\n        kpsoi_aug = aug.augment_keypoints([self.kpsoi2d])[0]\n        assert len(kpsoi_aug.keypoints) == 2\n        assert kpsoi_aug.shape == (8, 24)\n        assert np.allclose(kpsoi_aug.keypoints[0].x, 3)\n        assert np.allclose(kpsoi_aug.keypoints[0].y, 4)\n        assert np.allclose(kpsoi_aug.keypoints[1].x, 12)\n        assert np.allclose(kpsoi_aug.keypoints[1].y, 2)\n\n    def test_polygons_on_2d_img_and_with_width_float_and_height_int(self):\n        aug = iaa.Resize({\"width\": 3.0, \"height\": 8})\n        cbaoi_aug = aug.augment_polygons(self.psoi2d)\n        assert len(cbaoi_aug.items) == 2\n        assert cbaoi_aug.shape == (8, 24)\n        assert cbaoi_aug.items[0].coords_almost_equals(\n            [(3*0, 0), (3*8, 0), (3*8, 8)]\n        )\n        assert cbaoi_aug.items[1].coords_almost_equals(\n            [(3*1, 2), (3*7, 2), (3*7, 6), (3*1, 6)]\n        )\n\n    def test_line_strings_on_2d_img_and_with_width_float_and_height_int(self):\n        aug = iaa.Resize({\"width\": 3.0, \"height\": 8})\n        cbaoi_aug = aug.augment_line_strings(self.lsoi2d)\n        assert len(cbaoi_aug.items) == 2\n        assert cbaoi_aug.shape == (8, 24)\n        assert cbaoi_aug.items[0].coords_almost_equals(\n            [(3*0, 0), (3*8, 0), (3*8, 8)]\n        )\n        assert cbaoi_aug.items[1].coords_almost_equals(\n            [(3*1, 2), (3*7, 2), (3*7, 6), (3*1, 6)]\n        )\n\n    def test_bounding_boxes_on_2d_img_and_with_width_float_and_height_int(self):\n        aug = iaa.Resize({\"width\": 3.0, \"height\": 8})\n        bbsoi_aug = aug.augment_bounding_boxes(self.bbsoi2d)\n        assert len(bbsoi_aug.bounding_boxes) == 2\n        assert bbsoi_aug.shape == (8, 24)\n        assert bbsoi_aug.bounding_boxes[0].coords_almost_equals(\n            [(3*0, 0), (3*8, 8)]\n        )\n        assert bbsoi_aug.bounding_boxes[1].coords_almost_equals(\n            [(3*1, (2/4)*8), (3*6, (3/4)*8)]\n        )\n\n    def test_empty_keypoints(self):\n        aug = iaa.Resize({\"height\": 8, \"width\": 12})\n        kpsoi = ia.KeypointsOnImage([], shape=(4, 8, 3))\n        kpsoi_aug = aug.augment_keypoints(kpsoi)\n        assert len(kpsoi_aug.keypoints) == 0\n        assert kpsoi_aug.shape == (8, 12, 3)\n\n    def test_empty_polygons(self):\n        aug = iaa.Resize({\"height\": 8, \"width\": 12})\n        psoi = ia.PolygonsOnImage([], shape=(4, 8, 3))\n        psoi_aug = aug.augment_polygons(psoi)\n        assert len(psoi_aug.polygons) == 0\n        assert psoi_aug.shape == (8, 12, 3)\n\n    def test_empty_line_strings(self):\n        aug = iaa.Resize({\"height\": 8, \"width\": 12})\n        lsoi = ia.LineStringsOnImage([], shape=(4, 8, 3))\n        lsoi_aug = aug.augment_line_strings(lsoi)\n        assert len(lsoi_aug.items) == 0\n        assert lsoi_aug.shape == (8, 12, 3)\n\n    def test_empty_bounding_boxes(self):\n        aug = iaa.Resize({\"height\": 8, \"width\": 12})\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(4, 8, 3))\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n        assert len(bbsoi_aug.bounding_boxes) == 0\n        assert bbsoi_aug.shape == (8, 12, 3)\n\n    def test_size_is_list_of_ints(self):\n        aug = iaa.Resize([12, 14])\n        seen2d = [False, False]\n        seen3d = [False, False]\n        for _ in sm.xrange(100):\n            observed2d = aug.augment_image(self.image2d)\n            observed3d = aug.augment_image(self.image3d)\n            assert observed2d.shape in [(12, 12), (14, 14)]\n            assert observed3d.shape in [(12, 12, 3), (14, 14, 3)]\n            if observed2d.shape == (12, 12):\n                seen2d[0] = True\n            else:\n                seen2d[1] = True\n            if observed3d.shape == (12, 12, 3):\n                seen3d[0] = True\n            else:\n                seen3d[1] = True\n            if all(seen2d) and all(seen3d):\n                break\n        assert np.all(seen2d)\n        assert np.all(seen3d)\n\n    def test_size_is_tuple_of_ints(self):\n        aug = iaa.Resize((12, 14))\n        seen2d = [False, False, False]\n        seen3d = [False, False, False]\n        for _ in sm.xrange(100):\n            observed2d = aug.augment_image(self.image2d)\n            observed3d = aug.augment_image(self.image3d)\n            assert observed2d.shape in [(12, 12), (13, 13), (14, 14)]\n            assert observed3d.shape in [(12, 12, 3), (13, 13, 3), (14, 14, 3)]\n            if observed2d.shape == (12, 12):\n                seen2d[0] = True\n            elif observed2d.shape == (13, 13):\n                seen2d[1] = True\n            else:\n                seen2d[2] = True\n            if observed3d.shape == (12, 12, 3):\n                seen3d[0] = True\n            elif observed3d.shape == (13, 13, 3):\n                seen3d[1] = True\n            else:\n                seen3d[2] = True\n            if all(seen2d) and all(seen3d):\n                break\n        assert np.all(seen2d)\n        assert np.all(seen3d)\n\n    def test_size_is_string_keep(self):\n        aug = iaa.Resize(\"keep\")\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        assert observed2d.shape == self.image2d.shape\n        assert observed3d.shape == self.image3d.shape\n\n    # TODO shouldn't this be more an error?\n    def test_size_is_empty_list(self):\n        aug = iaa.Resize([])\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        assert observed2d.shape == self.image2d.shape\n        assert observed3d.shape == self.image3d.shape\n\n    def test_size_is_empty_dict(self):\n        aug = iaa.Resize({})\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        assert observed2d.shape == self.image2d.shape\n        assert observed3d.shape == self.image3d.shape\n\n    def test_change_height_to_fixed_int(self):\n        aug = iaa.Resize({\"height\": 11})\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        assert observed2d.shape == (11, self.image2d.shape[1])\n        assert observed3d.shape == (11, self.image3d.shape[1], 3)\n\n    def test_change_width_to_fixed_int(self):\n        aug = iaa.Resize({\"width\": 13})\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        assert observed2d.shape == (self.image2d.shape[0], 13)\n        assert observed3d.shape == (self.image3d.shape[0], 13, 3)\n\n    def test_change_height_and_width_to_fixed_ints(self):\n        aug = iaa.Resize({\"height\": 12, \"width\": 13})\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        assert observed2d.shape == (12, 13)\n        assert observed3d.shape == (12, 13, 3)\n\n    def test_change_height_to_fixed_int_but_dont_change_width(self):\n        aug = iaa.Resize({\"height\": 12, \"width\": \"keep\"})\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        assert observed2d.shape == (12, self.image2d.shape[1])\n        assert observed3d.shape == (12, self.image3d.shape[1], 3)\n\n    def test_dont_change_height_but_width_to_fixed_int(self):\n        aug = iaa.Resize({\"height\": \"keep\", \"width\": 12})\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        assert observed2d.shape == (self.image2d.shape[0], 12)\n        assert observed3d.shape == (self.image3d.shape[0], 12, 3)\n\n    def test_change_height_to_fixed_int_width_keeps_aspect_ratio(self):\n        aug = iaa.Resize({\"height\": 12, \"width\": \"keep-aspect-ratio\"})\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        aspect_ratio2d = self._aspect_ratio(self.image2d)\n        aspect_ratio3d = self._aspect_ratio(self.image3d)\n        assert observed2d.shape == (12, int(12 * aspect_ratio2d))\n        assert observed3d.shape == (12, int(12 * aspect_ratio3d), 3)\n\n    def test_height_keeps_aspect_ratio_width_changed_to_fixed_int(self):\n        aug = iaa.Resize({\"height\": \"keep-aspect-ratio\", \"width\": 12})\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        aspect_ratio2d = self._aspect_ratio(self.image2d)\n        aspect_ratio3d = self._aspect_ratio(self.image3d)\n        assert observed2d.shape == (int(12 * (1/aspect_ratio2d)), 12)\n        assert observed3d.shape == (int(12 * (1/aspect_ratio3d)), 12, 3)\n\n    # TODO add test for shorter side being tuple, list, stochastic parameter\n    def test_change_shorter_side_by_fixed_int_longer_keeps_aspect_ratio(self):\n        aug = iaa.Resize({\"shorter-side\": 6,\n                          \"longer-side\": \"keep-aspect-ratio\"})\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        aspect_ratio2d = self._aspect_ratio(self.image2d)\n        aspect_ratio3d = self._aspect_ratio(self.image3d)\n        assert observed2d.shape == (6, int(6 * aspect_ratio2d))\n        assert observed3d.shape == (6, int(6 * aspect_ratio3d), 3)\n\n    # TODO add test for longer side being tuple, list, stochastic parameter\n    def test_change_longer_side_by_fixed_int_shorter_keeps_aspect_ratio(self):\n        aug = iaa.Resize({\"longer-side\": 6,\n                          \"shorter-side\": \"keep-aspect-ratio\"})\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n        aspect_ratio2d = self._aspect_ratio(self.image2d)\n        aspect_ratio3d = self._aspect_ratio(self.image3d)\n        assert observed2d.shape == (int(6 * (1/aspect_ratio2d)), 6)\n        assert observed3d.shape == (int(6 * (1/aspect_ratio3d)), 6, 3)\n\n    def test_change_height_by_list_of_ints_width_by_fixed_int(self):\n        aug = iaa.Resize({\"height\": [12, 14], \"width\": 12})\n        seen2d = [False, False]\n        seen3d = [False, False]\n        for _ in sm.xrange(100):\n            observed2d = aug.augment_image(self.image2d)\n            observed3d = aug.augment_image(self.image3d)\n            assert observed2d.shape in [(12, 12), (14, 12)]\n            assert observed3d.shape in [(12, 12, 3), (14, 12, 3)]\n            if observed2d.shape == (12, 12):\n                seen2d[0] = True\n            else:\n                seen2d[1] = True\n            if observed3d.shape == (12, 12, 3):\n                seen3d[0] = True\n            else:\n                seen3d[1] = True\n            if np.all(seen2d) and np.all(seen3d):\n                break\n        assert np.all(seen2d)\n        assert np.all(seen3d)\n\n    def test_change_height_by_fixed_int_width_by_list_of_ints(self):\n        aug = iaa.Resize({\"height\": 12, \"width\": [12, 14]})\n        seen2d = [False, False]\n        seen3d = [False, False]\n        for _ in sm.xrange(100):\n            observed2d = aug.augment_image(self.image2d)\n            observed3d = aug.augment_image(self.image3d)\n            assert observed2d.shape in [(12, 12), (12, 14)]\n            assert observed3d.shape in [(12, 12, 3), (12, 14, 3)]\n            if observed2d.shape == (12, 12):\n                seen2d[0] = True\n            else:\n                seen2d[1] = True\n            if observed3d.shape == (12, 12, 3):\n                seen3d[0] = True\n            else:\n                seen3d[1] = True\n            if np.all(seen2d) and np.all(seen3d):\n                break\n        assert np.all(seen2d)\n        assert np.all(seen3d)\n\n    def test_change_height_by_fixed_int_width_by_stochastic_parameter(self):\n        aug = iaa.Resize({\"height\": 12, \"width\": iap.Choice([12, 14])})\n        seen2d = [False, False]\n        seen3d = [False, False]\n        for _ in sm.xrange(100):\n            observed2d = aug.augment_image(self.image2d)\n            observed3d = aug.augment_image(self.image3d)\n            assert observed2d.shape in [(12, 12), (12, 14)]\n            assert observed3d.shape in [(12, 12, 3), (12, 14, 3)]\n            if observed2d.shape == (12, 12):\n                seen2d[0] = True\n            else:\n                seen2d[1] = True\n            if observed3d.shape == (12, 12, 3):\n                seen3d[0] = True\n            else:\n                seen3d[1] = True\n            if np.all(seen2d) and np.all(seen3d):\n                break\n        assert np.all(seen2d)\n        assert np.all(seen3d)\n\n    def test_change_height_by_tuple_of_ints_width_by_fixed_int(self):\n        aug = iaa.Resize({\"height\": (12, 14), \"width\": 12})\n        seen2d = [False, False, False]\n        seen3d = [False, False, False]\n        for _ in sm.xrange(100):\n            observed2d = aug.augment_image(self.image2d)\n            observed3d = aug.augment_image(self.image3d)\n            assert observed2d.shape in [(12, 12), (13, 12), (14, 12)]\n            assert observed3d.shape in [(12, 12, 3), (13, 12, 3), (14, 12, 3)]\n            if observed2d.shape == (12, 12):\n                seen2d[0] = True\n            elif observed2d.shape == (13, 12):\n                seen2d[1] = True\n            else:\n                seen2d[2] = True\n            if observed3d.shape == (12, 12, 3):\n                seen3d[0] = True\n            elif observed3d.shape == (13, 12, 3):\n                seen3d[1] = True\n            else:\n                seen3d[2] = True\n            if np.all(seen2d) and np.all(seen3d):\n                break\n        assert np.all(seen2d)\n        assert np.all(seen3d)\n\n    def test_size_is_float(self):\n        aug = iaa.Resize(2.0)\n        observed2d = aug.augment_image(self.image2d)\n        observed3d = aug.augment_image(self.image3d)\n\n        intensity_avg = np.average(self.image2d)\n        intensity_low = intensity_avg - 0.2 * np.abs(intensity_avg - 128)\n        intensity_high = intensity_avg + 0.2 * np.abs(intensity_avg - 128)\n        assert observed2d.shape == (self.image2d.shape[0]*2,\n                                    self.image2d.shape[1]*2)\n        assert observed3d.shape == (self.image3d.shape[0]*2,\n                                    self.image3d.shape[1]*2,\n                                    3)\n        assert intensity_low < np.average(observed2d) < intensity_high\n        assert intensity_low < np.average(observed3d) < intensity_high\n\n    def test_size_is_list(self):\n        aug = iaa.Resize([2.0, 4.0])\n        seen2d = [False, False]\n        seen3d = [False, False]\n        expected_shapes_2d = [\n            (self.image2d.shape[0]*2, self.image2d.shape[1]*2),\n            (self.image2d.shape[0]*4, self.image2d.shape[1]*4)]\n        expected_shapes_3d = [\n            (self.image3d.shape[0]*2, self.image3d.shape[1]*2, 3),\n            (self.image3d.shape[0]*4, self.image3d.shape[1]*4, 3)]\n        for _ in sm.xrange(100):\n            observed2d = aug.augment_image(self.image2d)\n            observed3d = aug.augment_image(self.image3d)\n            assert observed2d.shape in expected_shapes_2d\n            assert observed3d.shape in expected_shapes_3d\n            if observed2d.shape == expected_shapes_2d[0]:\n                seen2d[0] = True\n            else:\n                seen2d[1] = True\n            if observed3d.shape == expected_shapes_3d[0]:\n                seen3d[0] = True\n            else:\n                seen3d[1] = True\n            if np.all(seen2d) and np.all(seen3d):\n                break\n        assert np.all(seen2d)\n        assert np.all(seen3d)\n\n    def test_size_is_stochastic_parameter(self):\n        aug = iaa.Resize(iap.Choice([2.0, 4.0]))\n        seen2d = [False, False]\n        seen3d = [False, False]\n        expected_shapes_2d = [\n            (self.image2d.shape[0]*2, self.image2d.shape[1]*2),\n            (self.image2d.shape[0]*4, self.image2d.shape[1]*4)]\n        expected_shapes_3d = [\n            (self.image3d.shape[0]*2, self.image3d.shape[1]*2, 3),\n            (self.image3d.shape[0]*4, self.image3d.shape[1]*4, 3)]\n        for _ in sm.xrange(100):\n            observed2d = aug.augment_image(self.image2d)\n            observed3d = aug.augment_image(self.image3d)\n            assert observed2d.shape in expected_shapes_2d\n            assert observed3d.shape in expected_shapes_3d\n            if observed2d.shape == expected_shapes_2d[0]:\n                seen2d[0] = True\n            else:\n                seen2d[1] = True\n            if observed3d.shape == expected_shapes_3d[0]:\n                seen3d[0] = True\n            else:\n                seen3d[1] = True\n            if all(seen2d) and all(seen3d):\n                break\n        assert np.all(seen2d)\n        assert np.all(seen3d)\n\n    def test_decrease_size_by_tuple_of_floats__one_for_both_sides(self):\n        image2d = self.image2d[0:4, 0:4]\n        image3d = self.image3d[0:4, 0:4, :]\n        aug = iaa.Resize((0.76, 1.0))\n        not_seen2d = set()\n        not_seen3d = set()\n        for size in sm.xrange(3, 4+1):\n            not_seen2d.add((size, size))\n        for size in sm.xrange(3, 4+1):\n            not_seen3d.add((size, size, 3))\n        possible2d = set(list(not_seen2d))\n        possible3d = set(list(not_seen3d))\n        for _ in sm.xrange(100):\n            observed2d = aug.augment_image(image2d)\n            observed3d = aug.augment_image(image3d)\n            assert observed2d.shape in possible2d\n            assert observed3d.shape in possible3d\n            if observed2d.shape in not_seen2d:\n                not_seen2d.remove(observed2d.shape)\n            if observed3d.shape in not_seen3d:\n                not_seen3d.remove(observed3d.shape)\n            if not not_seen2d and not not_seen3d:\n                break\n        assert not not_seen2d\n        assert not not_seen3d\n\n    def test_decrease_size_by_tuples_of_floats__one_per_side(self):\n        image2d = self.image2d[0:4, 0:4]\n        image3d = self.image3d[0:4, 0:4, :]\n        aug = iaa.Resize({\"height\": (0.76, 1.0), \"width\": (0.76, 1.0)})\n        not_seen2d = set()\n        not_seen3d = set()\n        for hsize in sm.xrange(3, 4+1):\n            for wsize in sm.xrange(3, 4+1):\n                not_seen2d.add((hsize, wsize))\n        for hsize in sm.xrange(3, 4+1):\n            for wsize in sm.xrange(3, 4+1):\n                not_seen3d.add((hsize, wsize, 3))\n        possible2d = set(list(not_seen2d))\n        possible3d = set(list(not_seen3d))\n        for _ in sm.xrange(100):\n            observed2d = aug.augment_image(image2d)\n            observed3d = aug.augment_image(image3d)\n            assert observed2d.shape in possible2d\n            assert observed3d.shape in possible3d\n            if observed2d.shape in not_seen2d:\n                not_seen2d.remove(observed2d.shape)\n            if observed3d.shape in not_seen3d:\n                not_seen3d.remove(observed3d.shape)\n            if not not_seen2d and not not_seen3d:\n                break\n        assert not not_seen2d\n        assert not not_seen3d\n\n    def test_bad_datatype_for_size_leads_to_failure(self):\n        got_exception = False\n        try:\n            aug = iaa.Resize(\"foo\")\n            _ = aug.augment_image(self.image2d)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_get_parameters(self):\n        aug = iaa.Resize(size=1, interpolation=\"nearest\")\n        params = aug.get_parameters()\n        assert is_parameter_instance(params[0], iap.Deterministic)\n        assert is_parameter_instance(params[1], iap.Deterministic)\n        assert params[0].value == 1\n        assert params[1].value == \"nearest\"\n\n    def test_dtypes_roughly(self):\n        # most of the dtype testing is done for imresize_many_images()\n        # so we focus here on a rough test that merely checks if the dtype\n        # does not change\n\n        # these dtypes should be kept in sync with imresize_many_images()\n        dtypes = [\n            \"uint8\",\n            \"uint16\",\n            \"int8\",\n            \"int16\",\n            \"float16\",\n            \"float32\",\n            \"float64\",\n            \"bool\"\n        ]\n\n        for dt in dtypes:\n            for ip in [\"nearest\", \"cubic\"]:\n                aug = iaa.Resize({\"height\": 10, \"width\": 20}, interpolation=ip)\n                for is_list in [False, True]:\n                    with self.subTest(dtype=dt, interpolation=ip,\n                                      is_list=is_list):\n                        image = np.full((9, 19, 3), 1, dtype=dt)\n                        images = [image, image]\n                        if not is_list:\n                            images = np.array(images, dtype=dt)\n\n                        images_aug = aug(images=images)\n\n                        if is_list:\n                            assert isinstance(images_aug, list)\n                        else:\n                            assert ia.is_np_array(images_aug)\n\n                        assert len(images_aug) == 2\n                        for image_aug in images_aug:\n                            assert image_aug.dtype.name == dt\n                            assert image_aug.shape == (10, 20, 3)\n                            assert np.all(image_aug >= 1 - 1e-4)\n\n    def test_pickleable(self):\n        aug = iaa.Resize({\"height\": (10, 30), \"width\": (10, 30)},\n                         interpolation=[\"nearest\", \"linear\"],\n                         seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3, shape=(50, 50, 1))\n\n\nclass TestPad(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        base_img = np.array([[0, 0, 0],\n                             [0, 1, 0],\n                             [0, 0, 0]], dtype=np.uint8)\n        return base_img[:, :, np.newaxis]\n\n    @property\n    def images(self):\n        return np.array([self.image])\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        return ia.KeypointsOnImage(kps, shape=self.image.shape)\n\n    @property\n    def psoi(self):\n        polys = [ia.Polygon([(1, 1), (2, 1), (2, 2)])]\n        return ia.PolygonsOnImage(polys, shape=self.image.shape)\n\n    @property\n    def lsoi(self):\n        ls = [ia.LineString([(1, 1), (2, 1), (2, 2)])]\n        return ia.LineStringsOnImage(ls, shape=self.image.shape)\n\n    @property\n    def bbsoi(self):\n        bbs = [ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)]\n        return ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)\n\n    @property\n    def heatmap(self):\n        heatmaps_arr = np.float32([[0, 0, 0],\n                                   [0, 1.0, 0],\n                                   [0, 0, 0]])\n        return ia.HeatmapsOnImage(heatmaps_arr, shape=self.image.shape)\n\n    @property\n    def segmap(self):\n        segmaps_arr = np.int32([[0, 0, 0],\n                                [0, 1, 0],\n                                [0, 0, 0]])\n        return ia.SegmentationMapsOnImage(segmaps_arr, shape=self.image.shape)\n\n    def test___init___pad_mode_is_all(self):\n        aug = iaa.Pad(px=(0, 1, 0, 0),\n                      pad_mode=ia.ALL,\n                      pad_cval=0,\n                      keep_size=False)\n        expected = [\"constant\", \"edge\", \"linear_ramp\", \"maximum\", \"mean\",\n                    \"median\", \"minimum\", \"reflect\", \"symmetric\", \"wrap\"]\n        assert is_parameter_instance(aug.pad_mode, iap.Choice)\n        assert len(aug.pad_mode.a) == len(expected)\n        assert np.all([v in aug.pad_mode.a for v in expected])\n\n    def test___init___pad_mode_is_list(self):\n        aug = iaa.Pad(px=(0, 1, 0, 0),\n                      pad_mode=[\"constant\", \"maximum\"],\n                      pad_cval=0,\n                      keep_size=False)\n        expected = [\"constant\", \"maximum\"]\n        assert is_parameter_instance(aug.pad_mode, iap.Choice)\n        assert len(aug.pad_mode.a) == len(expected)\n        assert np.all([v in aug.pad_mode.a for v in expected])\n\n    def test___init___pad_cval_is_list(self):\n        aug = iaa.Pad(px=(0, 1, 0, 0),\n                      pad_mode=\"constant\",\n                      pad_cval=[50, 100],\n                      keep_size=False)\n        expected = [50, 100]\n        assert is_parameter_instance(aug.pad_cval, iap.Choice)\n        assert len(aug.pad_cval.a) == len(expected)\n        assert np.all([v in aug.pad_cval.a for v in expected])\n\n    def test_pad_images_by_1px_each_side_on_its_own(self):\n        # test pad by 1 pixel on each side\n        pads = [\n            (1, 0, 0, 0),\n            (0, 1, 0, 0),\n            (0, 0, 1, 0),\n            (0, 0, 0, 1),\n        ]\n        for pad in pads:\n            with self.subTest(px=pad):\n                aug = iaa.Pad(px=pad, keep_size=False)\n\n                top, right, bottom, left = pad\n\n                base_img_padded = np.pad(\n                    self.image,\n                    ((top, bottom), (left, right), (0, 0)),\n                    mode=\"constant\",\n                    constant_values=0)\n                observed = aug.augment_images(self.images)\n                assert np.array_equal(observed, np.array([base_img_padded]))\n\n                observed = aug.augment_images([self.image])\n                assert array_equal_lists(observed, [base_img_padded])\n\n    def _test_pad_cbaoi_by_1px_each_side_on_its_own(self, cbaoi, augf_name):\n        pads = [\n            (1, 0, 0, 0),\n            (0, 1, 0, 0),\n            (0, 0, 1, 0),\n            (0, 0, 0, 1),\n        ]\n        for pad in pads:\n            with self.subTest(px=pad):\n                aug = iaa.Pad(px=pad, keep_size=False)\n\n                top, right, bottom, left = pad\n\n                image_padded_shape = list(self.image.shape)\n                image_padded_shape[0] += top + bottom\n                image_padded_shape[1] += left + right\n\n                observed = getattr(aug, augf_name)(cbaoi)\n\n                expected = cbaoi.shift(x=left, y=top)\n                expected.shape = tuple(image_padded_shape)\n                assert_cbaois_equal(observed, expected)\n\n    def test_pad_keypoints_by_1px_each_side_on_its_own(self):\n        self._test_pad_cbaoi_by_1px_each_side_on_its_own(\n            self.kpsoi, \"augment_keypoints\")\n\n    def test_pad_polygons_by_1px_each_side_on_its_own(self):\n        self._test_pad_cbaoi_by_1px_each_side_on_its_own(\n            self.psoi, \"augment_polygons\")\n\n    def test_pad_line_strings_by_1px_each_side_on_its_own(self):\n        self._test_pad_cbaoi_by_1px_each_side_on_its_own(\n            self.lsoi, \"augment_line_strings\")\n\n    def test_pad_bounding_boxes_by_1px_each_side_on_its_own(self):\n        self._test_pad_cbaoi_by_1px_each_side_on_its_own(\n            self.bbsoi, \"augment_bounding_boxes\")\n\n    def test_pad_heatmaps_by_1px_each_side_on_its_own(self):\n        pads = [\n            (1, 0, 0, 0),\n            (0, 1, 0, 0),\n            (0, 0, 1, 0),\n            (0, 0, 0, 1),\n        ]\n        for pad in pads:\n            with self.subTest(px=pad):\n                aug = iaa.Pad(px=pad, keep_size=False)\n\n                top, right, bottom, left = pad\n\n                heatmaps_arr = self.heatmap.get_arr()\n                heatmaps_arr_padded = np.pad(\n                    heatmaps_arr,\n                    ((top, bottom), (left, right)),\n                    mode=\"constant\",\n                    constant_values=0)\n                heatmaps = [ia.HeatmapsOnImage(\n                    heatmaps_arr, shape=self.image.shape)]\n                image_padded_shape = list(self.image.shape)\n                image_padded_shape[0] += top + bottom\n                image_padded_shape[1] += left + right\n\n                observed = aug.augment_heatmaps(heatmaps)[0]\n\n                assert observed.shape == tuple(image_padded_shape)\n                assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n                assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n                assert np.array_equal(observed.get_arr(), heatmaps_arr_padded)\n\n    def test_pad_segmaps_by_1px_each_side_on_its_own(self):\n        pads = [\n            (1, 0, 0, 0),\n            (0, 1, 0, 0),\n            (0, 0, 1, 0),\n            (0, 0, 0, 1),\n        ]\n        for pad in pads:\n            with self.subTest(px=pad):\n                aug = iaa.Pad(px=pad, keep_size=False)\n\n                top, right, bottom, left = pad\n\n                segmaps_arr = self.segmap.get_arr()\n                segmaps_arr_padded = np.pad(\n                    segmaps_arr,\n                    ((top, bottom), (left, right)),\n                    mode=\"constant\",\n                    constant_values=0)\n                segmaps = [SegmentationMapsOnImage(\n                    segmaps_arr, shape=self.image.shape)]\n                image_padded_shape = list(self.image.shape)\n                image_padded_shape[0] += top + bottom\n                image_padded_shape[1] += left + right\n\n                observed = aug.augment_segmentation_maps(segmaps)[0]\n\n                assert observed.shape == tuple(image_padded_shape)\n                assert np.array_equal(observed.get_arr(), segmaps_arr_padded)\n\n    # TODO split up, add similar tests for polygons/LS/BBs\n    def test_pad_each_side_on_its_own_by_tuple_of_ints(self):\n        def _to_range_tuple(val):\n            return val if isinstance(val, tuple) else (val, val)\n\n        pads = [\n            ((0, 2), 0, 0, 0),\n            (0, (0, 2), 0, 0),\n            (0, 0, (0, 2), 0),\n            (0, 0, 0, (0, 2)),\n        ]\n        for pad in pads:\n            with self.subTest(px=pad):\n                aug = iaa.Pad(px=pad, keep_size=False)\n                aug_det = aug.to_deterministic()\n\n                top, right, bottom, left = pad\n\n                images_padded = []\n                keypoints_padded = []\n                top_range = _to_range_tuple(top)\n                right_range = _to_range_tuple(right)\n                bottom_range = _to_range_tuple(bottom)\n                left_range = _to_range_tuple(left)\n\n                top_values = sm.xrange(top_range[0], top_range[1]+1)\n                right_values = sm.xrange(right_range[0], right_range[1]+1)\n                bottom_values = sm.xrange(bottom_range[0], bottom_range[1]+1)\n                left_values = sm.xrange(left_range[0], left_range[1]+1)\n\n                for top_val in top_values:\n                    for right_val in right_values:\n                        for bottom_val in bottom_values:\n                            for left_val in left_values:\n                                images_padded.append(\n                                    np.pad(\n                                        self.image,\n                                        ((top_val, bottom_val),\n                                         (left_val, right_val),\n                                         (0, 0)),\n                                        mode=\"constant\",\n                                        constant_values=0\n                                    )\n                                )\n                                keypoints_padded.append(\n                                    self.kpsoi.shift(x=left_val, y=top_val))\n\n                movements = []\n                movements_det = []\n                for i in sm.xrange(100):\n                    observed = aug.augment_images(self.images)\n\n                    matches = [\n                        (1 if np.array_equal(observed,\n                                             np.array([base_img_padded]))\n                         else 0)\n                        for base_img_padded\n                        in images_padded\n                    ]\n                    movements.append(np.argmax(np.array(matches)))\n                    assert any([val == 1 for val in matches])\n\n                    observed = aug_det.augment_images(self.images)\n                    matches = [\n                        (1 if np.array_equal(observed,\n                                             np.array([base_img_padded]))\n                         else 0)\n                        for base_img_padded\n                        in images_padded\n                    ]\n                    movements_det.append(np.argmax(np.array(matches)))\n                    assert any([val == 1 for val in matches])\n\n                    observed = aug.augment_images([self.image])\n                    assert any([\n                        array_equal_lists(observed, [base_img_padded])\n                        for base_img_padded\n                        in images_padded])\n\n                    observed = aug.augment_keypoints(self.kpsoi)\n                    assert any([\n                        keypoints_equal(observed, kp)\n                        for kp\n                        in keypoints_padded])\n\n                assert len(set(movements)) == 3\n                assert len(set(movements_det)) == 1\n\n    # TODO split up, add similar tests for polygons/LS/BBs\n    def test_pad_each_side_on_its_own_by_list_of_ints(self):\n        # test pad by list of exact pixel values\n        pads = [\n            ([0, 2], 0, 0, 0),\n            (0, [0, 2], 0, 0),\n            (0, 0, [0, 2], 0),\n            (0, 0, 0, [0, 2]),\n        ]\n        for pad in pads:\n            top, right, bottom, left = pad\n            aug = iaa.Pad(px=pad, keep_size=False)\n            aug_det = aug.to_deterministic()\n\n            images_padded = []\n            keypoints_padded = []\n            top_range = top if isinstance(top, list) else [top]\n            right_range = right if isinstance(right, list) else [right]\n            bottom_range = bottom if isinstance(bottom, list) else [bottom]\n            left_range = left if isinstance(left, list) else [left]\n\n            for top_val in top_range:\n                for right_val in right_range:\n                    for bottom_val in bottom_range:\n                        for left_val in left_range:\n                            images_padded.append(\n                                np.pad(\n                                    self.image,\n                                    ((top_val, bottom_val),\n                                     (left_val, right_val),\n                                     (0, 0)),\n                                    mode=\"constant\",\n                                    constant_values=0\n                                )\n                            )\n                            keypoints_padded.append(\n                                self.kpsoi.shift(x=left_val, y=top_val))\n\n            movements = []\n            movements_det = []\n            for i in sm.xrange(100):\n                observed = aug.augment_images(self.images)\n                matches = [\n                    (1 if np.array_equal(observed,\n                                         np.array([base_img_padded]))\n                     else 0)\n                    for base_img_padded\n                    in images_padded]\n                movements.append(np.argmax(np.array(matches)))\n                assert any([val == 1 for val in matches])\n\n                observed = aug_det.augment_images(self.images)\n                matches = [\n                    (1 if np.array_equal(observed,\n                                         np.array([base_img_padded]))\n                     else 0)\n                    for base_img_padded\n                    in images_padded]\n                movements_det.append(np.argmax(np.array(matches)))\n                assert any([val == 1 for val in matches])\n\n                observed = aug.augment_images([self.image])\n                assert any([\n                    array_equal_lists(observed, [base_img_padded])\n                    for base_img_padded\n                    in images_padded])\n\n                observed = aug.augment_keypoints(self.kpsoi)\n                assert any([\n                    keypoints_equal(observed, kp)\n                    for kp\n                    in keypoints_padded])\n\n            assert len(set(movements)) == 2\n            assert len(set(movements_det)) == 1\n\n    def test_pad_heatmaps_smaller_than_img_by_tuple_of_ints_without_ks(self):\n        # pad smaller heatmaps\n        # heatmap is (6, 4), image is (6, 16)\n        # image is padded by (2, 4, 2, 4)\n        # expected image size: (10, 24)\n        # expected heatmap size: (10, 6)\n        aug = iaa.Pad(px=(2, 4, 2, 4), keep_size=False)\n        heatmaps_arr_small = np.float32([\n            [0, 0, 0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 0, 0, 0]\n        ])\n        top, bottom, left, right = 2, 2, 1, 1\n        heatmaps_arr_small_padded = np.pad(\n            heatmaps_arr_small,\n            ((top, bottom), (left, right)),\n            mode=\"constant\",\n            constant_values=0)\n        heatmaps = [ia.HeatmapsOnImage(heatmaps_arr_small, shape=(6, 16))]\n        observed = aug.augment_heatmaps(heatmaps)[0]\n\n        assert observed.shape == (10, 24)\n        assert observed.arr_0to1.shape == (10, 6, 1)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.arr_0to1[..., 0], heatmaps_arr_small_padded)\n\n    def test_pad_segmaps_smaller_than_img_by_tuple_of_ints_without_ks(self):\n        # pad smaller segmaps\n        # same sizes and paddings as above\n        aug = iaa.Pad(px=(2, 4, 2, 4), keep_size=False)\n        segmaps_arr_small = np.int32([\n            [0, 0, 0, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 0, 0, 0]\n        ])\n        segmaps = [SegmentationMapsOnImage(segmaps_arr_small, shape=(6, 16))]\n        top, bottom, left, right = 2, 2, 1, 1\n        segmaps_arr_small_padded = np.pad(\n            segmaps_arr_small,\n            ((top, bottom), (left, right)),\n            mode=\"constant\",\n            constant_values=0)\n\n        observed = aug.augment_segmentation_maps(segmaps)[0]\n\n        assert observed.shape == (10, 24)\n        assert observed.arr.shape == (10, 6, 1)\n        assert np.array_equal(observed.arr[..., 0], segmaps_arr_small_padded)\n\n    def test_pad_heatmaps_smaller_than_img_by_tuple_of_ints_with_ks(self):\n        # pad smaller heatmaps, with keep_size=True\n        # heatmap is (6, 4), image is (6, 16)\n        # image is padded by (2, 4, 2, 4)\n        # expected image size: (10, 24) -> (6, 16) after resize\n        # expected heatmap size: (10, 6) -> (6, 4) after resize\n        aug = iaa.Pad(px=(2, 4, 2, 4), keep_size=True)\n        heatmaps_arr_small = np.float32([\n            [0, 0, 0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 0, 0, 0]\n        ])\n        top, bottom, left, right = 2, 2, 1, 1\n        heatmaps_arr_small_padded = np.pad(\n            heatmaps_arr_small,\n            ((top, bottom), (left, right)),\n            mode=\"constant\",\n            constant_values=0)\n        heatmaps = [ia.HeatmapsOnImage(heatmaps_arr_small, shape=(6, 16))]\n\n        observed = aug.augment_heatmaps(heatmaps)[0]\n\n        assert observed.shape == (6, 16)\n        assert observed.arr_0to1.shape == (6, 4, 1)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(\n            observed.arr_0to1[..., 0],\n            np.clip(\n                ia.imresize_single_image(\n                    heatmaps_arr_small_padded,\n                    (6, 4),\n                    interpolation=\"cubic\"),\n                0, 1.0\n            )\n        )\n\n    def test_pad_segmaps_smaller_than_img_by_tuple_of_ints_with_keep_size(self):\n        # pad smaller segmaps, with keep_size=True\n        # same sizes and paddings as above\n        aug = iaa.Pad(px=(2, 4, 2, 4), keep_size=True)\n        segmaps_arr_small = np.int32([\n            [0, 0, 0, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 0, 0, 0]\n        ])\n        top, bottom, left, right = 2, 2, 1, 1\n        segmaps_arr_small_padded = np.pad(\n            segmaps_arr_small,\n            ((top, bottom), (left, right)),\n            mode=\"constant\",\n            constant_values=0)\n        segmaps = [SegmentationMapsOnImage(segmaps_arr_small, shape=(6, 16))]\n\n        observed = aug.augment_segmentation_maps(segmaps)[0]\n\n        assert observed.shape == (6, 16)\n        assert observed.arr.shape == (6, 4, 1)\n        assert np.array_equal(\n            observed.arr[..., 0],\n            ia.imresize_single_image(\n                segmaps_arr_small_padded,\n                (6, 4),\n                interpolation=\"nearest\"\n            ),\n        )\n\n    def test_pad_keypoints_by_tuple_of_fixed_ints_without_keep_size(self):\n        aug = iaa.Pad((2, 0, 4, 4), keep_size=False)\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=0)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(4, 4, 3))\n        kpsoi_aug = aug.augment_keypoints([kpsoi])[0]\n        assert kpsoi_aug.shape == (10, 8, 3)\n        assert len(kpsoi_aug.keypoints) == 2\n        assert np.allclose(kpsoi_aug.keypoints[0].x, 4+1)\n        assert np.allclose(kpsoi_aug.keypoints[0].y, 2+2)\n        assert np.allclose(kpsoi_aug.keypoints[1].x, 4+3)\n        assert np.allclose(kpsoi_aug.keypoints[1].y, 2+0)\n\n    def test_pad_keypoints_by_tuple_of_fixed_ints_with_keep_size(self):\n        aug = iaa.Pad((2, 0, 4, 4), keep_size=True)\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=0)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(4, 4, 3))\n        kpsoi_aug = aug.augment_keypoints([kpsoi])[0]\n        assert kpsoi_aug.shape == (4, 4, 3)\n        assert len(kpsoi_aug.keypoints) == 2\n        assert np.allclose(kpsoi_aug.keypoints[0].x, ((4+1)/8)*4)\n        assert np.allclose(kpsoi_aug.keypoints[0].y, ((2+2)/10)*4)\n        assert np.allclose(kpsoi_aug.keypoints[1].x, ((4+3)/8)*4)\n        assert np.allclose(kpsoi_aug.keypoints[1].y, ((2+0)/10)*4)\n\n    def test_pad_polygons_by_tuple_of_fixed_ints_without_keep_size(self):\n        aug = iaa.Pad((2, 0, 4, 4), keep_size=False)\n        polygons = [ia.Polygon([(0, 0), (4, 0), (4, 4), (0, 4)]),\n                    ia.Polygon([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        psoi = ia.PolygonsOnImage(polygons, shape=(4, 4, 3))\n        psoi_aug = aug.augment_polygons([psoi, psoi])\n        assert len(psoi_aug) == 2\n        for psoi_aug_i in psoi_aug:\n            assert psoi_aug_i.shape == (10, 8, 3)\n            assert len(psoi_aug_i.items) == 2\n            assert psoi_aug_i.items[0].coords_almost_equals(\n                [(4, 2), (8, 2), (8, 6), (4, 6)])\n            assert psoi_aug_i.items[1].coords_almost_equals(\n                [(5, 3), (9, 3), (9, 7), (5, 7)])\n\n    def test_pad_polygons_by_tuple_of_fixed_ints_with_keep_size(self):\n        aug = iaa.Pad((2, 0, 4, 4), keep_size=True)\n        polygons = [ia.Polygon([(0, 0), (4, 0), (4, 4), (0, 4)]),\n                    ia.Polygon([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        psoi = ia.PolygonsOnImage(polygons, shape=(4, 4, 3))\n        psoi_aug = aug.augment_polygons([psoi, psoi])\n        assert len(psoi_aug) == 2\n        for psoi_aug_i in psoi_aug:\n            assert psoi_aug_i.shape == (4, 4, 3)\n            assert len(psoi_aug_i.items) == 2\n            assert psoi_aug_i.items[0].coords_almost_equals(\n                [(4*(4/8), 4*(2/10)),\n                 (4*(8/8), 4*(2/10)),\n                 (4*(8/8), 4*(6/10)),\n                 (4*(4/8), 4*(6/10))]\n            )\n            assert psoi_aug_i.items[1].coords_almost_equals(\n                [(4*(5/8), 4*(3/10)),\n                 (4*(9/8), 4*(3/10)),\n                 (4*(9/8), 4*(7/10)),\n                 (4*(5/8), 4*(7/10))]\n            )\n\n    def test_pad_line_strings_by_tuple_of_fixed_ints_without_keep_size(self):\n        aug = iaa.Pad((2, 0, 4, 4), keep_size=False)\n        lss = [ia.LineString([(0, 0), (4, 0), (4, 4), (0, 4)]),\n               ia.LineString([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        cbaoi = ia.LineStringsOnImage(lss, shape=(4, 4, 3))\n        cbaoi_aug = aug.augment_line_strings([cbaoi, cbaoi])\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (10, 8, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(4, 2), (8, 2), (8, 6), (4, 6)])\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(5, 3), (9, 3), (9, 7), (5, 7)])\n\n    def test_pad_line_strings_by_tuple_of_fixed_ints_with_keep_size(self):\n        aug = iaa.Pad((2, 0, 4, 4), keep_size=True)\n        lss = [ia.LineString([(0, 0), (4, 0), (4, 4), (0, 4)]),\n               ia.LineString([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        cbaoi = ia.LineStringsOnImage(lss, shape=(4, 4, 3))\n        cbaoi_aug = aug.augment_line_strings([cbaoi, cbaoi])\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (4, 4, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(4*(4/8), 4*(2/10)),\n                 (4*(8/8), 4*(2/10)),\n                 (4*(8/8), 4*(6/10)),\n                 (4*(4/8), 4*(6/10))]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(4*(5/8), 4*(3/10)),\n                 (4*(9/8), 4*(3/10)),\n                 (4*(9/8), 4*(7/10)),\n                 (4*(5/8), 4*(7/10))]\n            )\n\n    def test_pad_bounding_boxes_by_tuple_of_fixed_ints_without_keep_size(self):\n        aug = iaa.Pad((2, 0, 4, 4), keep_size=False)\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=4, y2=4),\n               ia.BoundingBox(x1=1, y1=1, x2=3, y2=4)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(4, 4, 3))\n        bbsoi_aug = aug.augment_bounding_boxes([bbsoi, bbsoi])\n        assert len(bbsoi_aug) == 2\n        for bbsoi_aug_i in bbsoi_aug:\n            assert bbsoi_aug_i.shape == (10, 8, 3)\n            assert len(bbsoi_aug_i.bounding_boxes) == 2\n            assert bbsoi_aug_i.bounding_boxes[0].coords_almost_equals(\n                [(4+0, 2+0), (4+4, 2+4)]\n            )\n            assert bbsoi_aug_i.bounding_boxes[1].coords_almost_equals(\n                [(4+1, 2+1), (4+3, 2+4)]\n            )\n\n    def test_pad_bounding_boxes_by_tuple_of_fixed_ints_with_keep_size(self):\n        aug = iaa.Pad((2, 0, 4, 4), keep_size=True)\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=4, y2=4),\n               ia.BoundingBox(x1=1, y1=1, x2=3, y2=4)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(4, 4, 3))\n        bbsoi_aug = aug.augment_bounding_boxes([bbsoi, bbsoi])\n        assert len(bbsoi_aug) == 2\n        for bbsoi_aug_i in bbsoi_aug:\n            assert bbsoi_aug_i.shape == (4, 4, 3)\n            assert len(bbsoi_aug_i.bounding_boxes) == 2\n            assert bbsoi_aug_i.bounding_boxes[0].coords_almost_equals(\n                [(4*((4+0)/8), 4*((2+0)/10)), (4*((4+4)/8), 4*((2+4)/10))]\n            )\n            assert bbsoi_aug_i.bounding_boxes[1].coords_almost_equals(\n                [(4*((4+1)/8), 4*((2+1)/10)), (4*((4+3)/8), 4*((2+4)/10))]\n            )\n\n    def test_pad_mode_is_stochastic_parameter(self):\n        aug = iaa.Pad(px=(0, 1, 0, 0),\n                      pad_mode=iap.Choice([\"constant\", \"maximum\", \"edge\"]),\n                      pad_cval=0,\n                      keep_size=False)\n\n        image = np.zeros((1, 2), dtype=np.uint8)\n        image[0, 0] = 100\n        image[0, 1] = 50\n\n        seen = [0, 0, 0]\n        for _ in sm.xrange(300):\n            observed = aug.augment_image(image)\n            if observed[0, 2] == 0:\n                seen[0] += 1\n            elif observed[0, 2] == 100:\n                seen[1] += 1\n            elif observed[0, 2] == 50:\n                seen[2] += 1\n            else:\n                assert False\n        assert np.all([100 - 50 < v < 100 + 50 for v in seen])\n\n    def test_bad_datatype_for_pad_mode_causes_failure(self):\n        got_exception = False\n        try:\n            _aug = iaa.Pad(px=(0, 1, 0, 0),\n                           pad_mode=False,\n                           pad_cval=0,\n                           keep_size=False)\n        except Exception as exc:\n            assert \"Expected pad_mode to be \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_pad_heatmaps_with_pad_mode_set(self):\n        # pad modes, heatmaps (always uses constant padding)\n        aug = iaa.Pad(px=(0, 1, 0, 0),\n                      pad_mode=\"edge\",\n                      pad_cval=0,\n                      keep_size=False)\n        heatmaps_arr = np.ones((3, 3, 1), dtype=np.float32)\n        heatmaps = HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        assert np.sum(observed.get_arr() <= 1e-4) == 3\n\n    def test_pad_segmaps_with_pad_mode_set(self):\n        # pad modes, segmaps (always uses constant padding)\n        aug = iaa.Pad(px=(0, 1, 0, 0),\n                      pad_mode=\"edge\",\n                      pad_cval=0,\n                      keep_size=False)\n        segmaps_arr = np.ones((3, 3, 1), dtype=np.int32)\n        segmaps = SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        assert np.sum(observed.get_arr() == 0) == 3\n\n    def test_pad_cval_is_int(self):\n        aug = iaa.Pad(px=(0, 1, 0, 0),\n                      pad_mode=\"constant\",\n                      pad_cval=100,\n                      keep_size=False)\n        image = np.zeros((1, 1), dtype=np.uint8)\n        observed = aug.augment_image(image)\n        assert observed[0, 0] == 0\n        assert observed[0, 1] == 100\n\n    def test_pad_cval_is_stochastic_parameter(self):\n        aug = iaa.Pad(px=(0, 1, 0, 0),\n                      pad_mode=\"constant\",\n                      pad_cval=iap.Choice([50, 100]),\n                      keep_size=False)\n        image = np.zeros((1, 1), dtype=np.uint8)\n        seen = [0, 0]\n        for _ in sm.xrange(200):\n            observed = aug.augment_image(image)\n            if observed[0, 1] == 50:\n                seen[0] += 1\n            elif observed[0, 1] == 100:\n                seen[1] += 1\n            else:\n                assert False\n        assert np.all([100 - 50 < v < 100 + 50 for v in seen])\n\n    def test_pad_cval_is_tuple(self):\n        aug = iaa.Pad(px=(0, 1, 0, 0),\n                      pad_mode=\"constant\",\n                      pad_cval=(50, 52),\n                      keep_size=False)\n        image = np.zeros((1, 1), dtype=np.uint8)\n\n        seen = [0, 0, 0]\n        for _ in sm.xrange(300):\n            observed = aug.augment_image(image)\n\n            if observed[0, 1] == 50:\n                seen[0] += 1\n            elif observed[0, 1] == 51:\n                seen[1] += 1\n            elif observed[0, 1] == 52:\n                seen[2] += 1\n            else:\n                assert False\n        assert np.all([100 - 50 < v < 100 + 50 for v in seen])\n\n    def test_invalid_pad_cval_datatype_leads_to_failure(self):\n        got_exception = False\n        try:\n            _aug = iaa.Pad(px=(0, 1, 0, 0),\n                           pad_mode=\"constant\",\n                           pad_cval=\"test\",\n                           keep_size=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_pad_heatmaps_with_cval_set(self):\n        # pad cvals, heatmaps (should always use cval 0)\n        aug = iaa.Pad(px=(0, 1, 0, 0),\n                      pad_mode=\"constant\",\n                      pad_cval=255,\n                      keep_size=False)\n        heatmaps_arr = np.zeros((3, 3, 1), dtype=np.float32)\n        heatmaps = HeatmapsOnImage(heatmaps_arr, shape=(3, 3, 3))\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        assert np.sum(observed.get_arr() > 1e-4) == 0\n\n    def test_pad_segmaps_with_cval_set(self):\n        # pad cvals, segmaps (should always use cval 0)\n        aug = iaa.Pad(px=(0, 1, 0, 0),\n                      pad_mode=\"constant\",\n                      pad_cval=255,\n                      keep_size=False)\n        segmaps_arr = np.zeros((3, 3, 1), dtype=np.int32)\n        segmaps = SegmentationMapsOnImage(segmaps_arr, shape=(3, 3, 3))\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        assert np.sum(observed.get_arr() > 0) == 0\n\n    def test_pad_all_sides_by_100_percent_without_keep_size(self):\n        aug = iaa.Pad(percent=1.0, keep_size=False)\n        image = np.zeros((4, 4), dtype=np.uint8) + 1\n\n        observed = aug.augment_image(image)\n\n        assert observed.shape == (4+4+4, 4+4+4)\n        assert np.sum(observed[4:-4, 4:-4]) == 4*4\n        assert np.sum(observed) == 4*4\n\n    def test_pad_all_sides_by_stochastic_param_without_keep_size(self):\n        aug = iaa.Pad(percent=iap.Deterministic(1.0), keep_size=False)\n        image = np.zeros((4, 4), dtype=np.uint8) + 1\n\n        observed = aug.augment_image(image)\n\n        assert observed.shape == (4+4+4, 4+4+4)\n        assert np.sum(observed[4:-4, 4:-4]) == 4*4\n        assert np.sum(observed) == 4*4\n\n    def test_pad_by_tuple_of_two_floats_dont_sample_independently_noks(self):\n        aug = iaa.Pad(percent=(1.0, 2.0),\n                      sample_independently=False,\n                      keep_size=False)\n        image = np.zeros((4, 4), dtype=np.uint8) + 1\n\n        observed = aug.augment_image(image)\n\n        assert np.sum(observed) == 4*4\n        assert (observed.shape[0] - 4) % 2 == 0\n        assert (observed.shape[1] - 4) % 2 == 0\n\n    def test_bad_datatype_for_percent_leads_to_failure_without_keep_size(self):\n        got_exception = False\n        try:\n            _ = iaa.Pad(percent=\"test\", keep_size=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_pad_each_side_by_100_percent_without_keep_size(self):\n        image = np.zeros((4, 4), dtype=np.uint8)\n        image[0, 0] = 255\n        image[3, 0] = 255\n        image[0, 3] = 255\n        image[3, 3] = 255\n        height, width = image.shape[0:2]\n        pads = [\n            (1.0, 0, 0, 0),\n            (0, 1.0, 0, 0),\n            (0, 0, 1.0, 0),\n            (0, 0, 0, 1.0),\n        ]\n        for pad in pads:\n            with self.subTest(pad=pad):\n                top, right, bottom, left = pad\n                top_px = int(top * height)\n                right_px = int(right * width)\n                bottom_px = int(bottom * height)\n                left_px = int(left * width)\n                aug = iaa.Pad(percent=pad, keep_size=False)\n                image_padded = np.pad(\n                    image,\n                    ((top_px, bottom_px), (left_px, right_px)),\n                    mode=\"constant\",\n                    constant_values=0)\n\n                observed = aug.augment_image(image)\n\n                assert np.array_equal(observed, image_padded)\n\n    def _test_pad_cba_each_side_by_100_percent_without_keep_size(\n            self, augf_name, cbaoi):\n        height, width = cbaoi.shape[0:2]\n        pads = [\n            (1.0, 0, 0, 0),\n            (0, 1.0, 0, 0),\n            (0, 0, 1.0, 0),\n            (0, 0, 0, 1.0),\n        ]\n        for pad in pads:\n            with self.subTest(pad=pad):\n                top, right, bottom, left = pad\n                top_px = int(top * height)\n                left_px = int(left * width)\n                aug = iaa.Pad(percent=pad, keep_size=False)\n                cbaoi_moved = cbaoi.shift(x=left_px, y=top_px)\n                cbaoi_moved.shape = (\n                    int(height+top*height+bottom*height),\n                    int(width+left*width+right*width)\n                )\n\n                observed = getattr(aug, augf_name)(cbaoi)\n\n                assert_cbaois_equal(observed, cbaoi_moved)\n\n    def test_pad_keypoints_each_side_by_100_percent_without_keep_size(self):\n        height, width = (4, 4)\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=3, y=3),\n               ia.Keypoint(x=3, y=3)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(height, width))\n        self._test_pad_cba_each_side_by_100_percent_without_keep_size(\n            \"augment_keypoints\", kpsoi)\n\n    def test_pad_polygons_each_side_by_100_percent_without_keep_size(self):\n        height, width = (4, 4)\n        polys = [ia.Polygon([(0, 0), (4, 0), (4, 4)]),\n                 ia.Polygon([(1, 2), (2, 3), (0, 4)])]\n        psoi = ia.PolygonsOnImage(polys, shape=(height, width))\n        self._test_pad_cba_each_side_by_100_percent_without_keep_size(\n            \"augment_polygons\", psoi)\n\n    def test_pad_line_strings_each_side_by_100_percent_without_keep_size(self):\n        height, width = (4, 4)\n        lss = [ia.LineString([(0, 0), (4, 0), (4, 4)]),\n               ia.LineString([(1, 2), (2, 3), (0, 4)])]\n        lsoi = ia.LineStringsOnImage(lss, shape=(height, width))\n        self._test_pad_cba_each_side_by_100_percent_without_keep_size(\n            \"augment_line_strings\", lsoi)\n\n    def test_pad_bbs_each_side_by_100_percent_without_keep_size(self):\n        height, width = (4, 4)\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=4, y2=4),\n               ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(height, width))\n        self._test_pad_cba_each_side_by_100_percent_without_keep_size(\n            \"augment_bounding_boxes\", bbsoi)\n\n    def test_pad_heatmaps_smaller_than_img_by_floats_without_keep_size(self):\n        # pad smaller heatmaps\n        # heatmap is (6, 4), image is (6, 16)\n        # image is padded by (0.5, 0.25, 0.5, 0.25)\n        # expected image size: (12, 24)\n        # expected heatmap size: (12, 6)\n        aug = iaa.Pad(percent=(0.5, 0.25, 0.5, 0.25), keep_size=False)\n        heatmaps_arr_small = np.float32([\n            [0, 0, 0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 0, 0, 0]\n        ])\n        top, bottom, left, right = 3, 3, 1, 1\n        heatmaps_arr_small_padded = np.pad(\n            heatmaps_arr_small,\n            ((top, bottom), (left, right)),\n            mode=\"constant\",\n            constant_values=0)\n        heatmaps = [ia.HeatmapsOnImage(heatmaps_arr_small, shape=(6, 16))]\n\n        observed = aug.augment_heatmaps(heatmaps)[0]\n\n        assert observed.shape == (12, 24)\n        assert observed.arr_0to1.shape == (12, 6, 1)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.arr_0to1[..., 0], heatmaps_arr_small_padded)\n\n    def test_pad_segmaps_smaller_than_img_by_floats_without_keep_size(self):\n        aug = iaa.Pad(percent=(0.5, 0.25, 0.5, 0.25), keep_size=False)\n        segmaps_arr_small = np.int32([\n            [0, 0, 0, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 0, 0, 0]\n        ])\n        top, bottom, left, right = 3, 3, 1, 1\n        segmaps_arr_small_padded = np.pad(\n            segmaps_arr_small,\n            ((top, bottom), (left, right)),\n            mode=\"constant\",\n            constant_values=0)\n        segmaps = [SegmentationMapsOnImage(segmaps_arr_small, shape=(6, 16))]\n\n        observed = aug.augment_segmentation_maps(segmaps)[0]\n\n        assert observed.shape == (12, 24)\n        assert observed.arr.shape == (12, 6, 1)\n        assert np.array_equal(observed.arr[..., 0], segmaps_arr_small_padded)\n\n    def test_pad_heatmaps_smaller_than_img_by_floats_with_keep_size(self):\n        # pad smaller heatmaps, with keep_size=True\n        # heatmap is (6, 4), image is (6, 16)\n        # image is padded by (0.5, 0.25, 0.5, 0.25)\n        # expected image size: (12, 24) -> (6, 16) after resize\n        # expected heatmap size: (12, 6) -> (6, 4) after resize\n        aug = iaa.Pad(percent=(0.5, 0.25, 0.5, 0.25), keep_size=True)\n        heatmaps_arr_small = np.float32([\n            [0, 0, 0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 1.0, 1.0, 0],\n            [0, 0, 0, 0]\n        ])\n        top, bottom, left, right = 3, 3, 1, 1\n        heatmaps_arr_small_padded = np.pad(\n            heatmaps_arr_small,\n            ((top, bottom), (left, right)),\n            mode=\"constant\",\n            constant_values=0)\n        heatmaps = [ia.HeatmapsOnImage(heatmaps_arr_small, shape=(6, 16))]\n\n        observed = aug.augment_heatmaps(heatmaps)[0]\n        assert observed.shape == (6, 16)\n        assert observed.arr_0to1.shape == (6, 4, 1)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(\n            observed.arr_0to1[..., 0],\n            np.clip(\n                ia.imresize_single_image(\n                    heatmaps_arr_small_padded, (6, 4), interpolation=\"cubic\"),\n                0, 1.0\n            )\n        )\n\n    def test_pad_segmaps_smaller_than_img_by_floats_with_keep_size(self):\n        aug = iaa.Pad(percent=(0.5, 0.25, 0.5, 0.25), keep_size=True)\n        segmaps_arr_small = np.int32([\n            [0, 0, 0, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 1, 1, 0],\n            [0, 0, 0, 0]\n        ])\n        top, bottom, left, right = 3, 3, 1, 1\n        segmaps_arr_small_padded = np.pad(\n            segmaps_arr_small,\n            ((top, bottom), (left, right)),\n            mode=\"constant\",\n            constant_values=0)\n        segmaps = [SegmentationMapsOnImage(segmaps_arr_small, shape=(6, 16))]\n\n        observed = aug.augment_segmentation_maps(segmaps)[0]\n\n        assert observed.shape == (6, 16)\n        assert observed.arr.shape == (6, 4, 1)\n        assert np.array_equal(\n            observed.arr[..., 0],\n            ia.imresize_single_image(\n                segmaps_arr_small_padded, (6, 4), interpolation=\"nearest\")\n        )\n\n    def test_pad_keypoints_by_floats_without_keep_size(self):\n        aug = iaa.Pad(percent=(0.5, 0, 1.0, 1.0), keep_size=False)\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=0)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(4, 4, 3))\n        kpsoi_aug = aug.augment_keypoints([kpsoi])[0]\n        assert kpsoi_aug.shape == (10, 8, 3)\n        assert len(kpsoi_aug.keypoints) == 2\n        assert np.allclose(kpsoi_aug.keypoints[0].x, 4+1)\n        assert np.allclose(kpsoi_aug.keypoints[0].y, 2+2)\n        assert np.allclose(kpsoi_aug.keypoints[1].x, 4+3)\n        assert np.allclose(kpsoi_aug.keypoints[1].y, 2+0)\n\n    def test_pad_keypoints_by_floats_with_keep_size(self):\n        aug = iaa.Pad(percent=(0.5, 0, 1.0, 1.0), keep_size=True)\n        kps = [ia.Keypoint(x=1, y=2), ia.Keypoint(x=3, y=0)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(4, 4, 3))\n        kpsoi_aug = aug.augment_keypoints([kpsoi])[0]\n        assert kpsoi_aug.shape == (4, 4, 3)\n        assert len(kpsoi_aug.keypoints) == 2\n        assert np.allclose(kpsoi_aug.keypoints[0].x, ((4+1)/8)*4)\n        assert np.allclose(kpsoi_aug.keypoints[0].y, ((2+2)/10)*4)\n        assert np.allclose(kpsoi_aug.keypoints[1].x, ((4+3)/8)*4)\n        assert np.allclose(kpsoi_aug.keypoints[1].y, ((2+0)/10)*4)\n\n    def test_pad_polygons_by_floats_without_keep_size(self):\n        aug = iaa.Pad(percent=(0.5, 0, 1.0, 1.0), keep_size=False)\n        cbaoi = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (4, 0), (4, 4), (0, 4)]),\n            ia.Polygon([(1, 1), (5, 1), (5, 5), (1, 5)])\n        ], shape=(4, 4, 3))\n        cbaoi_aug = aug.augment_polygons([cbaoi, cbaoi])\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (10, 8, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(4, 2), (8, 2), (8, 6), (4, 6)]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(5, 3), (9, 3), (9, 7), (5, 7)]\n            )\n\n    def test_pad_polygons_by_floats_with_keep_size(self):\n        # polygons, with keep_size=True\n        aug = iaa.Pad(percent=(0.5, 0, 1.0, 1.0), keep_size=True)\n        cbaoi = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (4, 0), (4, 4), (0, 4)]),\n            ia.Polygon([(1, 1), (5, 1), (5, 5), (1, 5)])\n        ], shape=(4, 4, 3))\n        cbaoi_aug = aug.augment_polygons([cbaoi, cbaoi])\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (4, 4, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(4*(4/8), 4*(2/10)),\n                 (4*(8/8), 4*(2/10)),\n                 (4*(8/8), 4*(6/10)),\n                 (4*(4/8), 4*(6/10))]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(4*(5/8), 4*(3/10)),\n                 (4*(9/8), 4*(3/10)),\n                 (4*(9/8), 4*(7/10)),\n                 (4*(5/8), 4*(7/10))]\n            )\n\n    def test_pad_line_strings_by_floats_without_keep_size(self):\n        aug = iaa.Pad(percent=(0.5, 0, 1.0, 1.0), keep_size=False)\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (4, 0), (4, 4), (0, 4)]),\n            ia.LineString([(1, 1), (5, 1), (5, 5), (1, 5)])\n        ], shape=(4, 4, 3))\n        cbaoi_aug = aug.augment_line_strings([cbaoi, cbaoi])\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (10, 8, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(4, 2), (8, 2), (8, 6), (4, 6)]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(5, 3), (9, 3), (9, 7), (5, 7)]\n            )\n\n    def test_pad_line_strings_by_floats_with_keep_size(self):\n        # polygons, with keep_size=True\n        aug = iaa.Pad(percent=(0.5, 0, 1.0, 1.0), keep_size=True)\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (4, 0), (4, 4), (0, 4)]),\n            ia.LineString([(1, 1), (5, 1), (5, 5), (1, 5)])\n        ], shape=(4, 4, 3))\n        cbaoi_aug = aug.augment_line_strings([cbaoi, cbaoi])\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (4, 4, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(4*(4/8), 4*(2/10)),\n                 (4*(8/8), 4*(2/10)),\n                 (4*(8/8), 4*(6/10)),\n                 (4*(4/8), 4*(6/10))]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(4*(5/8), 4*(3/10)),\n                 (4*(9/8), 4*(3/10)),\n                 (4*(9/8), 4*(7/10)),\n                 (4*(5/8), 4*(7/10))]\n            )\n\n    def test_pad_bounding_boxes_by_floats_without_keep_size(self):\n        aug = iaa.Pad(percent=(0.5, 0, 1.0, 1.0), keep_size=False)\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=4, y2=4),\n               ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(4, 4, 3))\n        bbsoi_aug = aug.augment_bounding_boxes([bbsoi, bbsoi])\n        assert len(bbsoi_aug) == 2\n        for bbsoi_aug_i in bbsoi_aug:\n            assert bbsoi_aug_i.shape == (10, 8, 3)\n            assert len(bbsoi_aug_i.bounding_boxes) == 2\n            assert bbsoi_aug_i.bounding_boxes[0].coords_almost_equals(\n                [(int(1.0*4+0), int(0.5*4+0)),\n                 (int(1.0*4+4), int(0.5*4+4))]\n            )\n            assert bbsoi_aug_i.bounding_boxes[1].coords_almost_equals(\n                [(int(1.0*4+1), int(0.5*4+2)),\n                 (int(1.0*4+3), int(0.5*4+4))]\n            )\n\n    def test_pad_bounding_boxes_by_floats_with_keep_size(self):\n        # BBs, with keep_size=True\n        aug = iaa.Pad(percent=(0.5, 0, 1.0, 1.0), keep_size=True)\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=4, y2=4),\n               ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(4, 4, 3))\n        bbsoi_aug = aug.augment_bounding_boxes([bbsoi, bbsoi])\n        assert len(bbsoi_aug) == 2\n        for bbsoi_aug_i in bbsoi_aug:\n            assert bbsoi_aug_i.shape == (4, 4, 3)\n            assert len(bbsoi_aug_i.bounding_boxes) == 2\n            assert bbsoi_aug_i.bounding_boxes[0].coords_almost_equals(\n                [(4*(4/8), 4*(2/10)),\n                 (4*(8/8), 4*(6/10))]\n            )\n            assert bbsoi_aug_i.bounding_boxes[1].coords_almost_equals(\n                [(4*(5/8), 4*(4/10)),\n                 (4*(7/8), 4*(6/10))]\n            )\n\n    def test_pad_by_tuple_of_floats_at_top_side_without_keep_size(self):\n        # test pad by range of percentages\n        aug = iaa.Pad(percent=((0, 1.0), 0, 0, 0), keep_size=False)\n        seen = [0, 0, 0, 0, 0]\n        for _ in sm.xrange(500):\n            observed = aug.augment_image(\n                np.zeros((4, 4), dtype=np.uint8) + 255)\n            n_padded = 0\n            while np.all(observed[0, :] == 0):\n                n_padded += 1\n                observed = observed[1:, :]\n            seen[n_padded] += 1\n        # note that we cant just check for 100-50 < x < 100+50 here. The\n        # first and last value (0px and 4px) padding have half the\n        # probability of occuring compared to the other values. E.g. 0px is\n        # padded if sampled p falls in range [0, 0.125). 1px is padded if\n        # sampled p falls in range [0.125, 0.375].\n        assert np.all([v > 30 for v in seen])\n\n    def test_pad_by_tuple_of_floats_at_right_side_without_keep_size(self):\n        aug = iaa.Pad(percent=(0, (0, 1.0), 0, 0), keep_size=False)\n        seen = [0, 0, 0, 0, 0]\n        for _ in sm.xrange(500):\n            observed = aug.augment_image(np.zeros((4, 4), dtype=np.uint8) + 255)\n            n_padded = 0\n            while np.all(observed[:, -1] == 0):\n                n_padded += 1\n                observed = observed[:, 0:-1]\n            seen[n_padded] += 1\n        assert np.all([v > 30 for v in seen])\n\n    def test_pad_by_list_of_floats_at_top_side_without_keep_size(self):\n        aug = iaa.Pad(percent=([0.0, 1.0], 0, 0, 0), keep_size=False)\n        seen = [0, 0, 0, 0, 0]\n        for _ in sm.xrange(500):\n            observed = aug.augment_image(\n                np.zeros((4, 4), dtype=np.uint8) + 255)\n            n_padded = 0\n            while np.all(observed[0, :] == 0):\n                n_padded += 1\n                observed = observed[1:, :]\n            seen[n_padded] += 1\n        assert 250 - 50 < seen[0] < 250 + 50\n        assert seen[1] == 0\n        assert seen[2] == 0\n        assert seen[3] == 0\n        assert 250 - 50 < seen[4] < 250 + 50\n\n    def test_pad_by_list_of_floats_at_right_side_without_keep_size(self):\n        aug = iaa.Pad(percent=(0, [0.0, 1.0], 0, 0), keep_size=False)\n        seen = [0, 0, 0, 0, 0]\n        for _ in sm.xrange(500):\n            observed = aug.augment_image(\n                np.zeros((4, 4), dtype=np.uint8) + 255)\n            n_padded = 0\n            while np.all(observed[:, -1] == 0):\n                n_padded += 1\n                observed = observed[:, 0:-1]\n            seen[n_padded] += 1\n        assert 250 - 50 < seen[0] < 250 + 50\n        assert seen[1] == 0\n        assert seen[2] == 0\n        assert seen[3] == 0\n        assert 250 - 50 < seen[4] < 250 + 50\n\n    @classmethod\n    def _test_pad_empty_cba(cls, augf_name, cbaoi):\n        aug = iaa.Pad(px=(1, 2, 3, 4), keep_size=False)\n\n        cbaoi_aug = getattr(aug, augf_name)(cbaoi)\n\n        expected = cbaoi.deepcopy()\n        expected.shape = tuple(\n            [1+expected.shape[0]+3, 4+expected.shape[1]+2]\n            + list(expected.shape[2:]))\n        assert_cbaois_equal(cbaoi_aug, expected)\n\n    def test_pad_empty_keypoints(self):\n        cbaoi = ia.KeypointsOnImage([], shape=(2, 4, 3))\n        self._test_pad_empty_cba(\"augment_keypoints\", cbaoi)\n\n    def test_pad_empty_polygons(self):\n        cbaoi = ia.PolygonsOnImage([], shape=(2, 4, 3))\n        self._test_pad_empty_cba(\"augment_polygons\", cbaoi)\n\n    def test_pad_empty_line_strings(self):\n        cbaoi = ia.LineStringsOnImage([], shape=(2, 4, 3))\n        self._test_pad_empty_cba(\"augment_line_strings\", cbaoi)\n\n    def test_pad_empty_bounding_boxes(self):\n        cbaoi = ia.BoundingBoxesOnImage([], shape=(2, 4, 3))\n        self._test_pad_empty_cba(\"augment_bounding_boxes\", cbaoi)\n\n    def test_zero_sized_axes_no_keep_size(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Pad(px=1, keep_size=False)\n\n                image_aug = aug(image=image)\n\n                expected_height = shape[0] + 2\n                expected_width = shape[1] + 2\n                expected_shape = tuple([expected_height, expected_width]\n                                       + list(shape[2:]))\n                assert image_aug.shape == expected_shape\n\n    def test_zero_sized_axes_keep_size(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Pad(px=1, keep_size=True)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_pad_other_dtypes_bool_by_int_without_keep_size(self):\n        aug = iaa.Pad(px=(1, 0, 0, 0), keep_size=False)\n        mask = np.zeros((4, 3), dtype=bool)\n        mask[2, 1] = True\n        image = np.zeros((3, 3), dtype=bool)\n        image[1, 1] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.name == image.dtype.name\n        assert image_aug.shape == (4, 3)\n        assert np.all(image_aug[~mask] == 0)\n        assert np.all(image_aug[mask] == 1)\n\n    def test_pad_other_dtypes_uint_int_by_int_without_keep_size(self):\n        aug = iaa.Pad(px=(1, 0, 0, 0), keep_size=False)\n\n        mask = np.zeros((4, 3), dtype=bool)\n        mask[2, 1] = True\n\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int16\", \"int32\", \"int64\"]\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                if np.dtype(dtype).kind == \"i\":\n                    values = [\n                        1, 5, 10, 100, int(0.1 * max_value),\n                        int(0.2 * max_value), int(0.5 * max_value),\n                        max_value - 100, max_value]\n                    values = values + [(-1) * value for value in values]\n                else:\n                    values = [\n                        1, 5, 10, 100, int(center_value),\n                        int(0.1 * max_value), int(0.2 * max_value),\n                        int(0.5 * max_value), max_value - 100, max_value]\n\n                for value in values:\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[1, 1] = value\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.name == dtype\n                    assert image_aug.shape == (4, 3)\n                    assert np.all(image_aug[~mask] == 0)\n                    assert np.all(image_aug[mask] == value)\n\n    def test_pad_other_dtypes_float_by_int_without_keep_size(self):\n        aug = iaa.Pad(px=(1, 0, 0, 0), keep_size=False)\n\n        mask = np.zeros((4, 3), dtype=bool)\n        mask[2, 1] = True\n\n        try:\n            high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n        except AttributeError:\n            high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                def _isclose(a, b):\n                    atol = 1e-4 if dtype == np.float16 else 1e-8\n                    return np.isclose(a, b, atol=atol, rtol=0)\n\n                isize = np.dtype(dtype).itemsize\n                values = [0.01, 1.0, 10.0, 100.0, 500 ** (isize - 1),\n                          1000 ** (isize - 1)]\n                values = values + [(-1) * value for value in values]\n                values = values + [min_value, max_value]\n                for value in values:\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[1, 1] = value\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype == np.dtype(dtype)\n                    assert image_aug.shape == (4, 3)\n                    assert np.all(_isclose(image_aug[~mask], 0))\n                    assert np.all(_isclose(image_aug[mask],\n                                           high_res_dt(value)))\n\n    def test_pickleable(self):\n        aug = iaa.Pad((0, 10), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5)\n\n\nclass TestCrop(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def image(self):\n        base_img = np.array([[0, 0, 0],\n                             [0, 1, 0],\n                             [0, 0, 0]], dtype=np.uint8)\n        base_img = base_img[:, :, np.newaxis]\n        return base_img\n\n    @property\n    def images(self):\n        return np.array([self.image])\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=0), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=2)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=self.image.shape)\n        return kpsoi\n\n    @property\n    def psoi(self):\n        ps = [ia.Polygon([(1, 1), (2, 1), (2, 2)])]\n        psoi = ia.PolygonsOnImage(ps, shape=self.image.shape)\n        return psoi\n\n    @property\n    def lsoi(self):\n        ls = [ia.LineString([(1, 1), (2, 1), (2, 2)])]\n        lsoi = ia.LineStringsOnImage(ls, shape=self.image.shape)\n        return lsoi\n\n    @property\n    def bbsoi(self):\n        bbs = [ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=self.image.shape)\n        return bbsoi\n\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.float32([[0, 0, 0],\n                                   [0, 1.0, 0],\n                                   [0, 0, 0]])\n        return [ia.HeatmapsOnImage(heatmaps_arr, shape=self.image.shape)]\n\n    @property\n    def segmaps(self):\n        segmaps_arr = np.int32([[0, 0, 0],\n                                [0, 1, 0],\n                                [0, 0, 0]])\n        return [ia.SegmentationMapsOnImage(segmaps_arr, shape=self.image.shape)]\n\n    # TODO split up and add polys/LS/BBs\n    def test_crop_by_fixed_int_on_each_side_on_its_own(self):\n        # test crop by 1 pixel on each side\n        crops = [\n            (1, 0, 0, 0),\n            (0, 1, 0, 0),\n            (0, 0, 1, 0),\n            (0, 0, 0, 1),\n        ]\n        for crop in crops:\n            with self.subTest(px=crop):\n                aug = iaa.Crop(px=crop, keep_size=False)\n\n                top, right, bottom, left = crop\n                height, width = self.image.shape[0:2]\n\n                base_img_cropped = self.image[top:height-bottom,\n                                              left:width-right,\n                                              :]\n\n                observed = aug.augment_images(self.images)\n                assert np.array_equal(observed, np.array([base_img_cropped]))\n\n                observed = aug.augment_images([self.image])\n                assert array_equal_lists(observed, [base_img_cropped])\n\n                keypoints_moved = self.kpsoi.shift(x=-left, y=-top)\n                observed = aug.augment_keypoints(self.kpsoi)\n                assert keypoints_equal(observed, keypoints_moved)\n\n                heatmaps_arr = self.heatmaps[0].get_arr()\n                height, width = heatmaps_arr.shape[0:2]\n                heatmaps_arr_cropped = heatmaps_arr[top:height-bottom,\n                                                    left:width-right]\n                observed = aug.augment_heatmaps(self.heatmaps)[0]\n                assert observed.shape == base_img_cropped.shape\n                assert np.array_equal(observed.get_arr(), heatmaps_arr_cropped)\n\n                segmaps_arr = self.segmaps[0].get_arr()\n                height, width = segmaps_arr.shape[0:2]\n                segmaps_arr_cropped = segmaps_arr[top:height-bottom,\n                                                  left:width-right]\n                observed = aug.augment_segmentation_maps(self.segmaps)[0]\n                assert observed.shape == base_img_cropped.shape\n                assert np.array_equal(observed.get_arr(), segmaps_arr_cropped)\n\n    # TODO split up and add polys/LS/BBs\n    def test_crop_by_tuple_of_ints_on_each_side_on_its_own(self):\n        def _to_range_tuple(val):\n            return val if isinstance(val, tuple) else (val, val)\n\n        crops = [\n            ((0, 2), 0, 0, 0),\n            (0, (0, 2), 0, 0),\n            (0, 0, (0, 2), 0),\n            (0, 0, 0, (0, 2)),\n        ]\n        for crop in crops:\n            with self.subTest(px=crop):\n                aug = iaa.Crop(px=crop, keep_size=False)\n                aug_det = aug.to_deterministic()\n\n                top, right, bottom, left = crop\n                height, width = self.image.shape[0:2]\n\n                top_range = _to_range_tuple(top)\n                right_range = _to_range_tuple(right)\n                bottom_range = _to_range_tuple(bottom)\n                left_range = _to_range_tuple(left)\n\n                top_values = sm.xrange(top_range[0], top_range[1]+1)\n                right_values = sm.xrange(right_range[0], right_range[1]+1)\n                bottom_values = sm.xrange(bottom_range[0], bottom_range[1]+1)\n                left_values = sm.xrange(left_range[0], left_range[1]+1)\n\n                images_cropped = []\n                keypoints_cropped = []\n                for top_val in top_values:\n                    for right_val in right_values:\n                        for bottom_val in bottom_values:\n                            for left_val in left_values:\n                                images_cropped.append(\n                                    self.image[top_val:height-bottom_val,\n                                               left_val:width-right_val,\n                                               :]\n                                )\n                                keypoints_cropped.append(\n                                    self.kpsoi.shift(\n                                        x=-left_val, y=-top_val)\n                                )\n\n                movements = []\n                movements_det = []\n                for i in sm.xrange(100):\n                    observed = aug.augment_images(self.images)\n\n                    matches = [\n                        (1\n                         if np.array_equal(observed,\n                                           np.array([base_img_cropped]))\n                         else 0)\n                        for base_img_cropped\n                        in images_cropped]\n                    movements.append(np.argmax(np.array(matches)))\n                    assert any([val == 1 for val in matches])\n\n                    observed = aug_det.augment_images(self.images)\n                    matches = [\n                        (1\n                         if np.array_equal(observed,\n                                           np.array([base_img_cropped]))\n                         else 0)\n                        for base_img_cropped\n                        in images_cropped]\n                    movements_det.append(np.argmax(np.array(matches)))\n                    assert any([val == 1 for val in matches])\n\n                    observed = aug.augment_images([self.image])\n                    assert any([array_equal_lists(observed, [base_img_cropped])\n                                for base_img_cropped\n                                in images_cropped])\n\n                    observed = aug.augment_keypoints(self.kpsoi)\n                    assert any([keypoints_equal(observed, kp)\n                                for kp\n                                in keypoints_cropped])\n\n                assert len(set(movements)) == 3\n                assert len(set(movements_det)) == 1\n\n    # TODO split up and add polys/LS/BBs\n    def test_crop_by_list_of_ints_on_each_side_on_its_own(self):\n        # test crop by list of exact pixel values\n        crops = [\n            ([0, 2], 0, 0, 0),\n            (0, [0, 2], 0, 0),\n            (0, 0, [0, 2], 0),\n            (0, 0, 0, [0, 2]),\n        ]\n        for crop in crops:\n            with self.subTest(px=crop):\n                aug = iaa.Crop(px=crop, keep_size=False)\n                aug_det = aug.to_deterministic()\n\n                top, right, bottom, left = crop\n                height, width = self.image.shape[0:2]\n\n                top_range = top if isinstance(top, list) else [top]\n                right_range = right if isinstance(right, list) else [right]\n                bottom_range = bottom if isinstance(bottom, list) else [bottom]\n                left_range = left if isinstance(left, list) else [left]\n\n                images_cropped = []\n                keypoints_cropped = []\n                for top_val in top_range:\n                    for right_val in right_range:\n                        for bottom_val in bottom_range:\n                            for left_val in left_range:\n                                images_cropped.append(\n                                    self.image[top_val:height-bottom_val,\n                                               left_val:width-right_val,\n                                               :]\n                                )\n                                keypoints_cropped.append(\n                                    self.kpsoi.shift(\n                                        x=-left_val, y=-top_val)\n                                )\n\n                movements = []\n                movements_det = []\n                for i in sm.xrange(100):\n                    observed = aug.augment_images(self.images)\n                    matches = [\n                        (1\n                         if np.array_equal(observed,\n                                           np.array([base_img_cropped]))\n                         else 0)\n                        for base_img_cropped\n                        in images_cropped]\n                    movements.append(np.argmax(np.array(matches)))\n                    assert any([val == 1 for val in matches])\n\n                    observed = aug_det.augment_images(self.images)\n                    matches = [\n                        (1\n                         if np.array_equal(observed,\n                                           np.array([base_img_cropped]))\n                         else 0)\n                        for base_img_cropped in images_cropped]\n                    movements_det.append(np.argmax(np.array(matches)))\n                    assert any([val == 1 for val in matches])\n\n                    observed = aug.augment_images([self.image])\n                    assert any([array_equal_lists(observed, [base_img_cropped])\n                                for base_img_cropped\n                                in images_cropped])\n\n                    observed = aug.augment_keypoints(self.kpsoi)\n                    assert any([keypoints_equal(observed, kp)\n                                for kp\n                                in keypoints_cropped])\n\n                assert len(set(movements)) == 2\n                assert len(set(movements_det)) == 1\n\n    def test_crop_heatmaps_smaller_than_img_by_fixed_ints_without_ks(self):\n        # crop smaller heatmaps\n        # heatmap is (6, 8), image is (6, 16)\n        # image is cropped by (1, 4, 1, 4)\n        # expected image size: (4, 8)\n        # expected heatmap size: (4, 4)\n        aug = iaa.Crop(px=(1, 4, 1, 4), keep_size=False)\n        heatmaps_arr_small = np.zeros((6, 8), dtype=np.float32)\n        heatmaps_arr_small[1:-1, 1:-1] = 1.0\n        heatmaps = HeatmapsOnImage(heatmaps_arr_small, shape=(6, 16))\n        top, bottom, left, right = 1, 1, 2, 2\n        heatmaps_arr_small_cropped = \\\n            heatmaps_arr_small[top:-bottom, left:-right]\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        assert observed.shape == (4, 8)\n        assert observed.arr_0to1.shape == (4, 4, 1)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.arr_0to1[..., 0],\n                           heatmaps_arr_small_cropped)\n\n    def test_crop_segmaps_smaller_than_img_by_fixed_ints_without_ks(self):\n        aug = iaa.Crop(px=(1, 4, 1, 4), keep_size=False)\n        segmaps_arr_small = np.zeros((6, 8), dtype=np.int32)\n        segmaps_arr_small[1:-1, 1:-1] = 1\n        segmaps = SegmentationMapsOnImage(segmaps_arr_small, shape=(6, 16))\n        top, bottom, left, right = 1, 1, 2, 2\n        segmaps_arr_small_cropped = segmaps_arr_small[top:-bottom, left:-right]\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        assert observed.shape == (4, 8)\n        assert observed.arr.shape == (4, 4, 1)\n        assert np.array_equal(observed.arr[..., 0], segmaps_arr_small_cropped)\n\n    def test_crop_heatmaps_smaller_than_img_by_fixed_ints_with_ks(self):\n        # crop smaller heatmaps, with keep_size=True\n        # heatmap is (6, 8), image is (6, 16)\n        # image is cropped by (1, 4, 1, 4)\n        # expected image size: (4, 8) -> (6, 16) after resize\n        # expected heatmap size: (4, 4) -> (6, 4) after resize\n        aug = iaa.Crop(px=(1, 4, 1, 4), keep_size=True)\n        heatmaps_arr_small = np.zeros((6, 8), dtype=np.float32)\n        heatmaps_arr_small[1:-1, 1:-1] = 1.0\n        heatmaps = HeatmapsOnImage(heatmaps_arr_small, shape=(6, 16))\n        top, bottom, left, right = 1, 1, 2, 2\n        heatmaps_arr_small_cropped = \\\n            heatmaps_arr_small[top:-bottom, left:-right]\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        assert observed.shape == (6, 16)\n        assert observed.arr_0to1.shape == (6, 8, 1)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(\n            observed.arr_0to1[..., 0],\n            np.clip(\n                ia.imresize_single_image(\n                    heatmaps_arr_small_cropped,\n                    (6, 8),\n                    interpolation=\"cubic\"),\n                0,\n                1.0\n            )\n        )\n\n    def test_crop_segmaps_smaller_than_img_by_fixed_ints_with_ks(self):\n        aug = iaa.Crop(px=(1, 4, 1, 4), keep_size=True)\n        segmaps_arr_small = np.zeros((6, 8), dtype=np.int32)\n        segmaps_arr_small[1:-1, 1:-1] = 1\n        segmaps = SegmentationMapsOnImage(segmaps_arr_small, shape=(6, 16))\n        top, bottom, left, right = 1, 1, 2, 2\n        segmaps_arr_small_cropped = segmaps_arr_small[top:-bottom, left:-right]\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        assert observed.shape == (6, 16)\n        assert observed.arr.shape == (6, 8, 1)\n        assert np.array_equal(\n            observed.arr[..., 0],\n            ia.imresize_single_image(\n                segmaps_arr_small_cropped,\n                (6, 8),\n                interpolation=\"nearest\"),\n        )\n\n    def test_crop_keypoints_by_fixed_ints_without_keep_size(self):\n        aug = iaa.Crop((1, 0, 4, 4), keep_size=False)\n        kps = [ia.Keypoint(x=3, y=6), ia.Keypoint(x=8, y=5)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(14, 14, 3))\n\n        kpsoi_aug = aug.augment_keypoints([kpsoi])[0]\n\n        assert kpsoi_aug.shape == (9, 10, 3)\n        assert len(kpsoi_aug.keypoints) == 2\n        assert np.allclose(kpsoi_aug.keypoints[0].x, 3-4)\n        assert np.allclose(kpsoi_aug.keypoints[0].y, 6-1)\n        assert np.allclose(kpsoi_aug.keypoints[1].x, 8-4)\n        assert np.allclose(kpsoi_aug.keypoints[1].y, 5-1)\n\n    def test_crop_keypoints_by_fixed_ints_with_keep_size(self):\n        aug = iaa.Crop((1, 0, 4, 4), keep_size=True)\n        kps = [ia.Keypoint(x=3, y=6), ia.Keypoint(x=8, y=5)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(14, 14, 3))\n\n        kpsoi_aug = aug.augment_keypoints([kpsoi])[0]\n\n        assert kpsoi_aug.shape == (14, 14, 3)\n        assert len(kpsoi_aug.keypoints) == 2\n        assert np.allclose(kpsoi_aug.keypoints[0].x, ((3-4)/10)*14)\n        assert np.allclose(kpsoi_aug.keypoints[0].y, ((6-1)/9)*14)\n        assert np.allclose(kpsoi_aug.keypoints[1].x, ((8-4)/10)*14)\n        assert np.allclose(kpsoi_aug.keypoints[1].y, ((5-1)/9)*14)\n\n    def test_crop_polygons_by_fixed_ints_without_keep_size(self):\n        aug = iaa.Crop((1, 0, 4, 4), keep_size=False)\n        polygons = [ia.Polygon([(0, 0), (4, 0), (4, 4), (0, 4)]),\n                    ia.Polygon([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        cbaoi = ia.PolygonsOnImage(polygons, shape=(10, 10, 3))\n\n        cbaoi_aug = aug.augment_polygons([cbaoi, cbaoi])\n\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (5, 6, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(0-4, 0-1), (4-4, 0-1), (4-4, 4-1), (0-4, 4-1)]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(1-4, 1-1), (5-4, 1-1), (5-4, 5-1), (1-4, 5-1)]\n            )\n\n    def test_crop_polygons_by_fixed_ints_with_keep_size(self):\n        aug = iaa.Crop((1, 0, 4, 4), keep_size=True)\n        polygons = [ia.Polygon([(0, 0), (4, 0), (4, 4), (0, 4)]),\n                    ia.Polygon([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        cbaoi = ia.PolygonsOnImage(polygons, shape=(10, 10, 3))\n\n        cbaoi_aug = aug.augment_polygons([cbaoi, cbaoi])\n\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (10, 10, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(10*(-4/6), 10*(-1/5)),\n                 (10*(0/6), 10*(-1/5)),\n                 (10*(0/6), 10*(3/5)),\n                 (10*(-4/6), 10*(3/5))]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(10*(-3/6), 10*(0/5)),\n                 (10*(1/6), 10*(0/5)),\n                 (10*(1/6), 10*(4/5)),\n                 (10*(-3/6), 10*(4/5))]\n            )\n\n    def test_crop_line_strings_by_fixed_ints_without_keep_size(self):\n        aug = iaa.Crop((1, 0, 4, 4), keep_size=False)\n        lss = [ia.LineString([(0, 0), (4, 0), (4, 4), (0, 4)]),\n               ia.LineString([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        cbaoi = ia.LineStringsOnImage(lss, shape=(10, 10, 3))\n\n        cbaoi_aug = aug.augment_line_strings([cbaoi, cbaoi])\n\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (5, 6, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(0-4, 0-1), (4-4, 0-1), (4-4, 4-1), (0-4, 4-1)]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(1-4, 1-1), (5-4, 1-1), (5-4, 5-1), (1-4, 5-1)]\n            )\n\n    def test_crop_line_strings_by_fixed_ints_with_keep_size(self):\n        aug = iaa.Crop((1, 0, 4, 4), keep_size=True)\n        lss = [ia.LineString([(0, 0), (4, 0), (4, 4), (0, 4)]),\n               ia.LineString([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        cbaoi = ia.LineStringsOnImage(lss, shape=(10, 10, 3))\n\n        cbaoi_aug = aug.augment_line_strings([cbaoi, cbaoi])\n\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (10, 10, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(10*(-4/6), 10*(-1/5)),\n                 (10*(0/6), 10*(-1/5)),\n                 (10*(0/6), 10*(3/5)),\n                 (10*(-4/6), 10*(3/5))]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(10*(-3/6), 10*(0/5)),\n                 (10*(1/6), 10*(0/5)),\n                 (10*(1/6), 10*(4/5)),\n                 (10*(-3/6), 10*(4/5))]\n            )\n\n    def test_crop_bounding_boxes_by_fixed_ints_without_keep_size(self):\n        aug = iaa.Crop((1, 0, 4, 4), keep_size=False)\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=10, y2=10),\n               ia.BoundingBox(x1=1, y1=2, x2=9, y2=10)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(10, 10, 3))\n\n        bbsoi_aug = aug.augment_bounding_boxes([bbsoi, bbsoi])\n\n        assert len(bbsoi_aug) == 2\n        for bbsoi_aug_i in bbsoi_aug:\n            assert bbsoi_aug_i.shape == (5, 6, 3)\n            assert len(bbsoi_aug_i.bounding_boxes) == 2\n            assert bbsoi_aug_i.bounding_boxes[0].coords_almost_equals(\n                [(0-4, 0-1), (10-4, 10-1)]\n            )\n            assert bbsoi_aug_i.bounding_boxes[1].coords_almost_equals(\n                [(1-4, 2-1), (9-4, 10-1)]\n            )\n\n    def test_crop_bounding_boxes_by_fixed_ints_with_keep_size(self):\n        aug = iaa.Crop((1, 0, 4, 4), keep_size=True)\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=10, y2=10),\n               ia.BoundingBox(x1=1, y1=2, x2=9, y2=10)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(10, 10, 3))\n\n        bbsoi_aug = aug.augment_bounding_boxes([bbsoi, bbsoi])\n\n        assert len(bbsoi_aug) == 2\n        for bbsoi_aug_i in bbsoi_aug:\n            assert bbsoi_aug_i.shape == (10, 10, 3)\n            assert len(bbsoi_aug_i.bounding_boxes) == 2\n            assert bbsoi_aug_i.bounding_boxes[0].coords_almost_equals(\n                [(10*(-4/6), 10*(-1/5)),\n                 (10*(6/6), 10*(9/5))]\n            )\n            assert bbsoi_aug_i.bounding_boxes[1].coords_almost_equals(\n                [(10*(-3/6), 10*(1/5)),\n                 (10*(5/6), 10*(9/5))]\n            )\n\n    def test_crop_by_one_fixed_float_without_keep_size(self):\n        aug = iaa.Crop(percent=0.1, keep_size=False)\n        image = np.random.randint(0, 255, size=(50, 50), dtype=np.uint8)\n\n        observed = aug.augment_image(image)\n\n        assert observed.shape == (40, 40)\n        assert np.all(observed == image[5:-5, 5:-5])\n\n    def test_crop_by_stochastic_parameter_without_keep_size(self):\n        aug = iaa.Crop(percent=iap.Deterministic(0.1), keep_size=False)\n        image = np.random.randint(0, 255, size=(50, 50), dtype=np.uint8)\n\n        observed = aug.augment_image(image)\n\n        assert observed.shape == (40, 40)\n        assert np.all(observed == image[5:-5, 5:-5])\n\n    def test_crop_by_tuple_of_two_floats_without_keep_size(self):\n        aug = iaa.Crop(percent=(0.1, 0.2), keep_size=False)\n        image = np.random.randint(0, 255, size=(50, 50), dtype=np.uint8)\n\n        observed = aug.augment_image(image)\n\n        assert 30 <= observed.shape[0] <= 40\n        assert 30 <= observed.shape[1] <= 40\n\n    def test_invalid_datatype_for_percent_parameter_fails(self):\n        got_exception = False\n        try:\n            _ = iaa.Crop(percent=\"test\", keep_size=False)\n        except Exception as exc:\n            assert \"Expected \" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    def test_crop_by_fixed_float_on_each_side_on_its_own(self):\n        image = np.random.randint(0, 255, size=(50, 50), dtype=np.uint8)\n        height, width = image.shape[0:2]\n        crops = [\n            (0.1, 0, 0, 0),\n            (0, 0.1, 0, 0),\n            (0, 0, 0.1, 0),\n            (0, 0, 0, 0.1),\n        ]\n        for crop in crops:\n            with self.subTest(percent=crop):\n                aug = iaa.Crop(percent=crop, keep_size=False)\n\n                top, right, bottom, left = crop\n                top_px = int(round(top * height))\n                right_px = int(round(right * width))\n                bottom_px = int(round(bottom * height))\n                left_px = int(round(left * width))\n\n                # dont use :-bottom_px and ;-right_px here, because these\n                # values can be 0\n                image_cropped = image[top_px:50-bottom_px, left_px:50-right_px]\n                observed = aug.augment_image(image)\n                assert np.array_equal(observed, image_cropped)\n\n    def _test_crop_cba_by_fixed_float_on_each_side_on_its_own(\n            self, augf_name, cbaoi):\n        height, width = cbaoi.shape[0:2]\n        crops = [\n            (0.1, 0, 0, 0),\n            (0, 0.1, 0, 0),\n            (0, 0, 0.1, 0),\n            (0, 0, 0, 0.1),\n        ]\n        for crop in crops:\n            with self.subTest(augf_name=augf_name, percent=crop):\n                aug = iaa.Crop(percent=crop, keep_size=False)\n\n                top, right, bottom, left = crop\n                top_px = int(round(top * height))\n                right_px = int(round(right * width))\n                left_px = int(round(left * width))\n                bottom_px = int(round(bottom * height))\n\n                observed = getattr(aug, augf_name)(cbaoi)\n\n                expected = cbaoi.shift(x=-left_px, y=-top_px)\n                expected.shape = tuple(\n                    [expected.shape[0] - top_px - bottom_px,\n                     expected.shape[1] - left_px - right_px]\n                    + list(expected.shape[2:])\n                )\n                assert_cbaois_equal(observed, expected)\n\n    def test_crop_keypoints_by_fixed_float_on_each_side_on_its_own(self):\n        height, width = (50, 50)\n        kps = [ia.Keypoint(x=10, y=11), ia.Keypoint(x=20, y=21),\n               ia.Keypoint(x=30, y=31)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(height, width))\n        self._test_crop_cba_by_fixed_float_on_each_side_on_its_own(\n            \"augment_keypoints\", kpsoi)\n\n    def test_crop_polygons_by_fixed_float_on_each_side_on_its_own(self):\n        height, width = (50, 50)\n        polygons = [ia.Polygon([(0, 0), (40, 0), (40, 40), (0, 40)]),\n                    ia.Polygon([(10, 10), (50, 10), (50, 50), (10, 50)])]\n        psoi = ia.PolygonsOnImage(polygons, shape=(height, width, 3))\n        self._test_crop_cba_by_fixed_float_on_each_side_on_its_own(\n            \"augment_polygons\", psoi)\n\n    def test_crop_line_strings_by_fixed_float_on_each_side_on_its_own(self):\n        height, width = (50, 50)\n        lss = [ia.LineString([(0, 0), (40, 0), (40, 40), (0, 40)]),\n               ia.LineString([(10, 10), (50, 10), (50, 50), (10, 50)])]\n        lsoi = ia.LineStringsOnImage(lss, shape=(height, width, 3))\n        self._test_crop_cba_by_fixed_float_on_each_side_on_its_own(\n            \"augment_line_strings\", lsoi)\n\n    def test_crop_bounding_boxes_by_fixed_float_on_each_side_on_its_own(self):\n        height, width = (50, 50)\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=40, y2=40),\n               ia.BoundingBox(x1=10, y1=10, x2=30, y2=40)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(height, width, 3))\n        self._test_crop_cba_by_fixed_float_on_each_side_on_its_own(\n            \"augment_bounding_boxes\", bbsoi)\n\n    def test_crop_heatmaps_smaller_than_img_by_fixed_floats_without_ks(self):\n        # crop smaller heatmaps\n        # heatmap is (8, 12), image is (16, 32)\n        # image is cropped by (0.25, 0.25, 0.25, 0.25)\n        # expected image size: (8, 16)\n        # expected heatmap size: (4, 6)\n        aug = iaa.Crop(percent=(0.25, 0.25, 0.25, 0.25), keep_size=False)\n        heatmaps_arr_small = np.zeros((8, 12), dtype=np.float32)\n        heatmaps_arr_small[2:-2, 4:-4] = 1.0\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr_small, shape=(16, 32))\n        top, bottom, left, right = 2, 2, 3, 3\n        heatmaps_arr_small_cropped = \\\n            heatmaps_arr_small[top:-bottom, left:-right]\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        assert observed.shape == (8, 16)\n        assert observed.arr_0to1.shape == (4, 6, 1)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(observed.arr_0to1[..., 0], heatmaps_arr_small_cropped)\n\n    def test_crop_segmaps_smaller_than_img_by_fixed_floats_without_ks(self):\n        aug = iaa.Crop(percent=(0.25, 0.25, 0.25, 0.25), keep_size=False)\n        segmaps_arr_small = np.zeros((8, 12), dtype=np.int32)\n        segmaps_arr_small[2:-2, 4:-4] = 1\n        segmaps = SegmentationMapsOnImage(segmaps_arr_small, shape=(16, 32))\n        top, bottom, left, right = 2, 2, 3, 3\n        segmaps_arr_small_cropped = segmaps_arr_small[top:-bottom, left:-right]\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        assert observed.shape == (8, 16)\n        assert observed.arr.shape == (4, 6, 1)\n        assert np.array_equal(observed.arr[..., 0], segmaps_arr_small_cropped)\n\n    def test_crop_heatmaps_smaller_than_img_by_fixed_floats_with_ks(self):\n        # crop smaller heatmaps, with keep_size=True\n        # heatmap is (8, 12), image is (16, 32)\n        # image is cropped by (0.25, 0.25, 0.25, 0.25)\n        # expected image size: (8, 16) -> (16, 32) after resize\n        # expected heatmap size: (4, 6) -> (8, 12) after resize\n        aug = iaa.Crop(percent=(0.25, 0.25, 0.25, 0.25), keep_size=True)\n        heatmaps_arr_small = np.zeros((8, 12), dtype=np.float32)\n        heatmaps_arr_small[2:-2, 4:-4] = 1.0\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr_small, shape=(16, 32))\n        top, bottom, left, right = 2, 2, 3, 3\n        heatmaps_arr_small_cropped = \\\n            heatmaps_arr_small[top:-bottom, left:-right]\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        assert observed.shape == (16, 32)\n        assert observed.arr_0to1.shape == (8, 12, 1)\n        assert 0 - 1e-6 < observed.min_value < 0 + 1e-6\n        assert 1 - 1e-6 < observed.max_value < 1 + 1e-6\n        assert np.allclose(\n            observed.arr_0to1[..., 0],\n            np.clip(\n                ia.imresize_single_image(\n                    heatmaps_arr_small_cropped,\n                    (8, 12),\n                    interpolation=\"cubic\"),\n                0,\n                1.0\n            )\n        )\n\n    def test_crop_segmaps_smaller_than_img_by_fixed_floats_with_ks(self):\n        aug = iaa.Crop(percent=(0.25, 0.25, 0.25, 0.25), keep_size=True)\n        segmaps_arr_small = np.zeros((8, 12), dtype=np.int32)\n        segmaps_arr_small[2:-2, 4:-4] = 1\n        segmaps = SegmentationMapsOnImage(segmaps_arr_small, shape=(16, 32))\n        top, bottom, left, right = 2, 2, 3, 3\n        segmaps_arr_small_cropped = segmaps_arr_small[top:-bottom, left:-right]\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        assert observed.shape == (16, 32)\n        assert observed.arr.shape == (8, 12, 1)\n        assert np.allclose(\n            observed.arr[..., 0],\n            ia.imresize_single_image(\n                segmaps_arr_small_cropped,\n                (8, 12),\n                interpolation=\"nearest\")\n        )\n\n    def test_crop_keypoints_by_fixed_floats_without_keep_size(self):\n        aug = iaa.Crop(percent=(0.25, 0, 0.5, 0.1), keep_size=False)\n        kps = [ia.Keypoint(x=12, y=10), ia.Keypoint(x=8, y=12)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(16, 20, 3))\n\n        kpsoi_aug = aug.augment_keypoints([kpsoi])[0]\n\n        assert kpsoi_aug.shape == (4, 18, 3)\n        assert len(kpsoi_aug.keypoints) == 2\n        assert np.allclose(kpsoi_aug.keypoints[0].x, 12-2)\n        assert np.allclose(kpsoi_aug.keypoints[0].y, 10-4)\n        assert np.allclose(kpsoi_aug.keypoints[1].x, 8-2)\n        assert np.allclose(kpsoi_aug.keypoints[1].y, 12-4)\n\n    def test_crop_keypoints_by_fixed_floats_with_keep_size(self):\n        aug = iaa.Crop(percent=(0.25, 0, 0.5, 0.1), keep_size=True)\n        kps = [ia.Keypoint(x=12, y=10), ia.Keypoint(x=8, y=12)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(16, 20, 3))\n\n        kpsoi_aug = aug.augment_keypoints([kpsoi])[0]\n\n        assert kpsoi_aug.shape == (16, 20, 3)\n        assert len(kpsoi_aug.keypoints) == 2\n        assert np.allclose(kpsoi_aug.keypoints[0].x, ((12-2)/18)*20)\n        assert np.allclose(kpsoi_aug.keypoints[0].y, ((10-4)/4)*16)\n        assert np.allclose(kpsoi_aug.keypoints[1].x, ((8-2)/18)*20)\n        assert np.allclose(kpsoi_aug.keypoints[1].y, ((12-4)/4)*16)\n\n    def test_crop_polygons_by_fixed_floats_without_keep_size(self):\n        aug = iaa.Crop(percent=(0.2, 0, 0.5, 0.1), keep_size=False)\n        polygons = [ia.Polygon([(0, 0), (4, 0), (4, 4), (0, 4)]),\n                    ia.Polygon([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        cbaoi = ia.PolygonsOnImage(polygons, shape=(10, 10, 3))\n\n        cbaoi_aug = aug.augment_polygons([cbaoi, cbaoi])\n\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (3, 9, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(0-1, 0-2), (4-1, 0-2), (4-1, 4-2), (0-1, 4-2)]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(1-1, 1-2), (5-1, 1-2), (5-1, 5-2), (1-1, 5-2)]\n            )\n\n    def test_crop_polygons_by_fixed_floats_with_keep_size(self):\n        aug = iaa.Crop(percent=(0.2, 0, 0.5, 0.1), keep_size=True)\n        polygons = [ia.Polygon([(0, 0), (4, 0), (4, 4), (0, 4)]),\n                    ia.Polygon([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        cbaoi = ia.PolygonsOnImage(polygons, shape=(10, 10, 3))\n\n        cbaoi_aug = aug.augment_polygons([cbaoi, cbaoi])\n\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (10, 10, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(10*(-1/9), 10*(-2/3)),\n                 (10*(3/9), 10*(-2/3)),\n                 (10*(3/9), 10*(2/3)),\n                 (10*(-1/9), 10*(2/3))]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(10*(0/9), 10*(-1/3)),\n                 (10*(4/9), 10*(-1/3)),\n                 (10*(4/9), 10*(3/3)),\n                 (10*(0/9), 10*(3/3))]\n            )\n\n    def test_crop_line_strings_by_fixed_floats_without_keep_size(self):\n        aug = iaa.Crop(percent=(0.2, 0, 0.5, 0.1), keep_size=False)\n        lss = [ia.LineString([(0, 0), (4, 0), (4, 4), (0, 4)]),\n               ia.LineString([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        cbaoi = ia.LineStringsOnImage(lss, shape=(10, 10, 3))\n\n        cbaoi_aug = aug.augment_line_strings([cbaoi, cbaoi])\n\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (3, 9, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(0-1, 0-2), (4-1, 0-2), (4-1, 4-2), (0-1, 4-2)]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(1-1, 1-2), (5-1, 1-2), (5-1, 5-2), (1-1, 5-2)]\n            )\n\n    def test_crop_line_strings_by_fixed_floats_with_keep_size(self):\n        aug = iaa.Crop(percent=(0.2, 0, 0.5, 0.1), keep_size=True)\n        lss = [ia.LineString([(0, 0), (4, 0), (4, 4), (0, 4)]),\n               ia.LineString([(1, 1), (5, 1), (5, 5), (1, 5)])]\n        cbaoi = ia.LineStringsOnImage(lss, shape=(10, 10, 3))\n\n        cbaoi_aug = aug.augment_line_strings([cbaoi, cbaoi])\n\n        assert len(cbaoi_aug) == 2\n        for cbaoi_aug_i in cbaoi_aug:\n            assert cbaoi_aug_i.shape == (10, 10, 3)\n            assert len(cbaoi_aug_i.items) == 2\n            assert cbaoi_aug_i.items[0].coords_almost_equals(\n                [(10*(-1/9), 10*(-2/3)),\n                 (10*(3/9), 10*(-2/3)),\n                 (10*(3/9), 10*(2/3)),\n                 (10*(-1/9), 10*(2/3))]\n            )\n            assert cbaoi_aug_i.items[1].coords_almost_equals(\n                [(10*(0/9), 10*(-1/3)),\n                 (10*(4/9), 10*(-1/3)),\n                 (10*(4/9), 10*(3/3)),\n                 (10*(0/9), 10*(3/3))]\n            )\n\n    def test_crop_bounding_boxes_by_fixed_floats_without_keep_size(self):\n        aug = iaa.Crop(percent=(0.2, 0, 0.5, 0.1), keep_size=False)\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=4, y2=4),\n               ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(10, 10, 3))\n\n        bbsoi_aug = aug.augment_bounding_boxes([bbsoi, bbsoi])\n\n        assert len(bbsoi_aug) == 2\n        for bbsoi_aug_i in bbsoi_aug:\n            assert bbsoi_aug_i.shape == (3, 9, 3)\n            assert len(bbsoi_aug_i.bounding_boxes) == 2\n            assert bbsoi_aug_i.bounding_boxes[0].coords_almost_equals(\n                [(0-1, 0-2), (4-1, 4-2)]\n            )\n            assert bbsoi_aug_i.bounding_boxes[1].coords_almost_equals(\n                [(1-1, 2-2), (3-1, 4-2)]\n            )\n\n    def test_crop_bounding_boxes_by_fixed_floats_with_keep_size(self):\n        aug = iaa.Crop(percent=(0.2, 0, 0.5, 0.1), keep_size=True)\n        bbs = [ia.BoundingBox(x1=0, y1=0, x2=4, y2=4),\n               ia.BoundingBox(x1=1, y1=2, x2=3, y2=4)]\n        bbsoi = ia.BoundingBoxesOnImage(bbs, shape=(10, 10, 3))\n\n        bbsoi_aug = aug.augment_bounding_boxes([bbsoi, bbsoi])\n\n        assert len(bbsoi_aug) == 2\n        for bbsoi_aug_i in bbsoi_aug:\n            assert bbsoi_aug_i.shape == (10, 10, 3)\n            assert len(bbsoi_aug_i.bounding_boxes) == 2\n            assert bbsoi_aug_i.bounding_boxes[0].coords_almost_equals(\n                [(10*((0-1)/9), 10*((0-2)/3)),\n                 (10*((4-1)/9), 10*((4-2)/3))]\n            )\n            assert bbsoi_aug_i.bounding_boxes[1].coords_almost_equals(\n                [(10*((1-1)/9), 10*((2-2)/3)),\n                 (10*((3-1)/9), 10*((4-2)/3))]\n            )\n\n    def test_crop_by_tuple_of_floats_on_top_side_without_ks(self):\n        aug = iaa.Crop(percent=((0, 0.1), 0, 0, 0), keep_size=False)\n        image = np.zeros((40, 40), dtype=np.uint8)\n        seen = [0, 0, 0, 0, 0]\n        for _ in sm.xrange(500):\n            observed = aug.augment_image(image)\n            n_cropped = 40 - observed.shape[0]\n            seen[n_cropped] += 1\n        # note that we cant just check for 100-50 < x < 100+50 here. The first\n        # and last value (0px and 4px) have half the probability of occuring\n        # compared to the other values. E.g. 0px is cropped if sampled p\n        # falls in range [0, 0.125). 1px is cropped if sampled p falls in\n        # range [0.125, 0.375].\n        assert np.all([v > 30 for v in seen])\n\n    def test_crop_by_tuple_of_floats_on_right_side_without_ks(self):\n        aug = iaa.Crop(percent=(0, (0, 0.1), 0, 0), keep_size=False)\n        image = np.zeros((40, 40), dtype=np.uint8) + 255\n        seen = [0, 0, 0, 0, 0]\n        for _ in sm.xrange(500):\n            observed = aug.augment_image(image)\n            n_cropped = 40 - observed.shape[1]\n            seen[n_cropped] += 1\n        assert np.all([v > 30 for v in seen])\n\n    def test_crop_by_list_of_floats_on_top_side_without_ks(self):\n        aug = iaa.Crop(percent=([0.0, 0.1], 0, 0, 0), keep_size=False)\n        image = np.zeros((40, 40), dtype=np.uint8) + 255\n        seen = [0, 0, 0, 0, 0]\n        for _ in sm.xrange(500):\n            observed = aug.augment_image(image)\n            n_cropped = 40 - observed.shape[0]\n            seen[n_cropped] += 1\n        assert 250 - 50 < seen[0] < 250 + 50\n        assert seen[1] == 0\n        assert seen[2] == 0\n        assert seen[3] == 0\n        assert 250 - 50 < seen[4] < 250 + 50\n\n    def test_crop_by_list_of_floats_on_right_side_without_ks(self):\n        aug = iaa.Crop(percent=(0, [0.0, 0.1], 0, 0), keep_size=False)\n        image = np.zeros((40, 40), dtype=np.uint8) + 255\n        seen = [0, 0, 0, 0, 0]\n        for _ in sm.xrange(500):\n            observed = aug.augment_image(image)\n            n_cropped = 40 - observed.shape[1]\n            seen[n_cropped] += 1\n        assert 250 - 50 < seen[0] < 250 + 50\n        assert seen[1] == 0\n        assert seen[2] == 0\n        assert seen[3] == 0\n        assert 250 - 50 < seen[4] < 250 + 50\n\n    @classmethod\n    def _test_crop_empty_cba(cls, augf_name, cbaoi):\n        aug = iaa.Crop(px=(1, 2, 3, 4), keep_size=False)\n\n        cbaoi_aug = getattr(aug, augf_name)(cbaoi)\n\n        expected = cbaoi.deepcopy()\n        expected.shape = tuple(\n            [expected.shape[0]-1-3, expected.shape[1]-2-4]\n            + list(expected.shape[2:]))\n        assert_cbaois_equal(cbaoi_aug, expected)\n\n    def test_pad_empty_keypoints(self):\n        cbaoi = ia.KeypointsOnImage([], shape=(12, 14, 3))\n        self._test_crop_empty_cba(\"augment_keypoints\", cbaoi)\n\n    def test_pad_empty_polygons(self):\n        cbaoi = ia.PolygonsOnImage([], shape=(12, 14, 3))\n        self._test_crop_empty_cba(\"augment_polygons\", cbaoi)\n\n    def test_pad_empty_line_strings(self):\n        cbaoi = ia.LineStringsOnImage([], shape=(12, 14, 3))\n        self._test_crop_empty_cba(\"augment_line_strings\", cbaoi)\n\n    def test_pad_empty_bounding_boxes(self):\n        cbaoi = ia.BoundingBoxesOnImage([], shape=(12, 14, 3))\n        self._test_crop_empty_cba(\"augment_bounding_boxes\", cbaoi)\n\n    def test_zero_sized_axes_no_keep_size(self):\n        # we also use height/width 2 here, because a height/width of 1 is\n        # actually not changed due to prevent_zero_size\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Crop(px=1, keep_size=False)\n\n                with warnings.catch_warnings(record=True) as caught_warnings:\n                    image_aug = aug(image=image)\n\n                # we don't check the number of warnings here as it varies by\n                # shape\n                for warning in caught_warnings:\n                    assert (\n                        \"crop amounts in CropAndPad\"\n                        in str(warning.message)\n                    )\n\n                expected_height = 0 if shape[0] == 0 else 1\n                expected_width = 0 if shape[1] == 0 else 1\n                expected_shape = tuple([expected_height, expected_width]\n                                       + list(shape[2:]))\n                assert image_aug.shape == expected_shape\n\n    def test_zero_sized_axes_keep_size(self):\n        # we also use height/width 2 here, because a height/width of 1 is\n        # actually not changed due to prevent_zero_size\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Crop(px=1, keep_size=True)\n\n                with warnings.catch_warnings(record=True) as caught_warnings:\n                    image_aug = aug(image=image)\n\n                # we don't check the number of warnings here as it varies by\n                # shape\n                for warning in caught_warnings:\n                    assert (\n                        \"crop amounts in CropAndPad\"\n                        in str(warning.message)\n                    )\n\n                assert image_aug.shape == image.shape\n\n    def test_other_dtypes_bool(self):\n        aug = iaa.Crop(px=(1, 0, 0, 0), keep_size=False)\n        mask = np.zeros((2, 3), dtype=bool)\n        mask[0, 1] = True\n\n        image = np.zeros((3, 3), dtype=bool)\n        image[1, 1] = True\n        image_aug = aug.augment_image(image)\n        assert image_aug.dtype.name == image.dtype.name\n        assert image_aug.shape == (2, 3)\n        assert np.all(image_aug[~mask] == 0)\n        assert np.all(image_aug[mask] == 1)\n\n    def test_other_dtypes_uint_int(self):\n        aug = iaa.Crop(px=(1, 0, 0, 0), keep_size=False)\n        mask = np.zeros((2, 3), dtype=bool)\n        mask[0, 1] = True\n\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int16\", \"int32\", \"int64\"]\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                if np.dtype(dtype).kind == \"i\":\n                    values = [\n                        1, 5, 10, 100, int(0.1 * max_value),\n                        int(0.2 * max_value), int(0.5 * max_value),\n                        max_value - 100, max_value]\n                    values = values + [(-1) * value for value in values]\n                else:\n                    values = [\n                        1, 5, 10, 100, int(center_value), int(0.1 * max_value),\n                        int(0.2 * max_value), int(0.5 * max_value),\n                        max_value - 100, max_value]\n\n                for value in values:\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[1, 1] = value\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype.name == dtype\n                    assert image_aug.shape == (2, 3)\n                    assert np.all(image_aug[~mask] == 0)\n                    assert np.all(image_aug[mask] == value)\n\n    def test_other_dtypes_float(self):\n        aug = iaa.Crop(px=(1, 0, 0, 0), keep_size=False)\n        mask = np.zeros((2, 3), dtype=bool)\n        mask[0, 1] = True\n\n        try:\n            high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n        except AttributeError:\n            high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n\n        for dtype in dtypes:\n            with self.subTest(dtype=dtype):\n                min_value, center_value, max_value = \\\n                    iadt.get_value_range_of_dtype(dtype)\n\n                def _isclose(a, b):\n                    atol = 1e-4 if dtype == np.float16 else 1e-8\n                    return np.isclose(a, b, atol=atol, rtol=0)\n\n                isize = np.dtype(dtype).itemsize\n                values = [0.01, 1.0, 10.0, 100.0, 500 ** (isize - 1),\n                          1000 ** (isize - 1)]\n                values = values + [(-1) * value for value in values]\n                values = values + [min_value, max_value]\n                for value in values:\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[1, 1] = value\n                    image_aug = aug.augment_image(image)\n                    assert image_aug.dtype == np.dtype(dtype)\n                    assert image_aug.shape == (2, 3)\n                    assert np.all(_isclose(image_aug[~mask], 0))\n                    assert np.all(_isclose(image_aug[mask],\n                                           high_res_dt(value)))\n\n    def test_pickleable(self):\n        aug = iaa.Crop((0, 10), seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(30, 30, 1))\n\n\nclass TestPadToFixedSize(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_image2d_that_needs_to_be_padded_on_both_sides(self):\n        aug = iaa.PadToFixedSize(height=5, width=5)\n        image = np.uint8([[255]])\n\n        observed = aug.augment_image(image)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (5, 5)\n\n    def test_image3d_that_needs_to_be_padded_on_both_sides(self):\n        aug = iaa.PadToFixedSize(height=5, width=5)\n        image3d = np.atleast_3d(np.uint8([[255]]))\n\n        observed = aug.augment_image(image3d)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (5, 5, 1)\n\n    def test_image3d_rgb_that_needs_to_be_padded_on_both_sides(self):\n        aug = iaa.PadToFixedSize(height=5, width=5)\n        image3d_rgb = np.tile(\n            np.atleast_3d(np.uint8([[255]])),\n            (1, 1, 3)\n        )\n\n        observed = aug.augment_image(image3d_rgb)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (5, 5, 3)\n\n    # why does this exist when there is already a test for other float dtypes?\n    def test_image2d_with_other_dtypes(self):\n        aug = iaa.PadToFixedSize(height=5, width=5)\n        image = np.uint8([[255]])\n\n        for dtype in [\"float32\", \"float64\", \"int32\"]:\n            with self.subTest(dtype=dtype):\n                observed = aug.augment_image(image.astype(dtype))\n\n                assert observed.dtype.name == dtype\n                assert observed.shape == (5, 5)\n\n    def test_image_with_height_being_too_small(self):\n        aug = iaa.PadToFixedSize(height=5, width=5)\n        image = np.zeros((1, 5, 3), dtype=np.uint8)\n\n        observed = aug.augment_image(image)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (5, 5, 3)\n\n    def test_image_with_width_being_too_small(self):\n        aug = iaa.PadToFixedSize(height=5, width=5)\n        image = np.zeros((5, 1, 3), dtype=np.uint8)\n\n        observed = aug.augment_image(image)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (5, 5, 3)\n\n    def test_image_fullfills_exactly_min_shape(self):\n        # change no side when all sides have exactly desired size\n        aug = iaa.PadToFixedSize(height=5, width=5)\n        img5x5 = np.zeros((5, 5, 3), dtype=np.uint8)\n        img5x5[2, 2, :] = 255\n\n        observed = aug.augment_image(img5x5)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (5, 5, 3)\n        assert np.array_equal(observed, img5x5)\n\n    def test_image_that_is_larger_than_min_shape(self):\n        # change no side when all sides have larger than desired size\n        aug = iaa.PadToFixedSize(height=5, width=5)\n        img6x6 = np.zeros((6, 6, 3), dtype=np.uint8)\n        img6x6[3, 3, :] = 255\n\n        observed = aug.augment_image(img6x6)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (6, 6, 3)\n        assert np.array_equal(observed, img6x6)\n\n    def test_too_small_image_with_width_none(self):\n        aug = iaa.PadToFixedSize(height=5, width=None)\n        image = np.zeros((4, 4, 3), dtype=np.uint8)\n\n        observed = aug.augment_image(image)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (5, 4, 3)\n\n    def test_too_small_image_with_height_none(self):\n        aug = iaa.PadToFixedSize(height=None, width=5)\n        image = np.zeros((4, 4, 3), dtype=np.uint8)\n\n        observed = aug.augment_image(image)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (4, 5, 3)\n\n    def test_image_pad_mode(self):\n        # make sure that pad mode is recognized\n        aug = iaa.PadToFixedSize(height=4, width=4, pad_mode=\"edge\")\n        aug.position = (iap.Deterministic(0.5), iap.Deterministic(0.5))\n        img2x2 = np.uint8([\n            [50, 100],\n            [150, 200]\n        ])\n\n        observed = aug.augment_image(img2x2)\n\n        expected = np.uint8([\n            [50, 50, 100, 100],\n            [50, 50, 100, 100],\n            [150, 150, 200, 200],\n            [150, 150, 200, 200]\n        ])\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (4, 4)\n        assert np.array_equal(observed, expected)\n\n    def test_image_pad_at_left_top(self):\n        # explicit non-center position test\n        aug = iaa.PadToFixedSize(\n            height=3, width=3, pad_mode=\"constant\", pad_cval=128,\n            position=\"left-top\")\n        img1x1 = np.uint8([[255]])\n        observed = aug.augment_image(img1x1)\n        expected = np.uint8([\n            [128, 128, 128],\n            [128, 128, 128],\n            [128, 128, 255]\n        ])\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (3, 3)\n        assert np.array_equal(observed, expected)\n\n    def test_image_pad_at_right_bottom(self):\n        aug = iaa.PadToFixedSize(\n            height=3, width=3, pad_mode=\"constant\", pad_cval=128,\n            position=\"right-bottom\")\n        img1x1 = np.uint8([[255]])\n\n        observed = aug.augment_image(img1x1)\n\n        expected = np.uint8([\n            [255, 128, 128],\n            [128, 128, 128],\n            [128, 128, 128]\n        ])\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (3, 3)\n        assert np.array_equal(observed, expected)\n\n    def test_image_pad_at_bottom_center_given_as_tuple_of_floats(self):\n        aug = iaa.PadToFixedSize(\n            height=3, width=3, pad_mode=\"constant\", pad_cval=128,\n            position=(0.5, 1.0))\n        img1x1 = np.uint8([[255]])\n\n        observed = aug.augment_image(img1x1)\n\n        expected = np.uint8([\n            [128, 255, 128],\n            [128, 128, 128],\n            [128, 128, 128]\n        ])\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (3, 3)\n        assert np.array_equal(observed, expected)\n\n    def test_keypoints__image_already_fullfills_min_shape(self):\n        # keypoint test with shape not being changed\n        aug = iaa.PadToFixedSize(\n            height=3, width=3, pad_mode=\"edge\", position=\"center\")\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)], shape=(3, 3))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        expected = ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_keypoints_pad_at_center(self):\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"center\")\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)], shape=(3, 3))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        # padding happens at right/bottom, so KP doesn't move\n        expected = ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_keypoints_pad_at_center__2px(self):\n        aug = iaa.PadToFixedSize(\n            height=5, width=5, pad_mode=\"edge\", position=\"center\")\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)], shape=(3, 3))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        expected = ia.KeypointsOnImage([ia.Keypoint(x=2, y=2)], shape=(5, 5))\n        assert_cbaois_equal(observed, expected)\n\n    def test_keypoints_pad_at_left_top(self):\n        # keypoint test with explicit non-center position\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"left-top\")\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)], shape=(3, 3))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        expected = ia.KeypointsOnImage([ia.Keypoint(x=2, y=2)], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_keypoints_pad_at_right_bottom(self):\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"right-bottom\")\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)], shape=(3, 3))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        expected = ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_keypoints_empty(self):\n        aug = iaa.PadToFixedSize(height=5, width=6)\n        kpsoi = ia.KeypointsOnImage([], shape=(3, 3))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        expected = ia.KeypointsOnImage([], shape=(5, 6))\n        assert_cbaois_equal(observed, expected)\n\n    def test_polygons__image_already_fullfills_min_shape(self):\n        # polygons test with shape not being changed\n        aug = iaa.PadToFixedSize(\n            height=3, width=3, pad_mode=\"edge\", position=\"center\")\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        expected = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_polygons_pad_at_center(self):\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"center\")\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        # padding happens at right/bottom, so poly doesn't move\n        expected = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (3, 0), (3, 3)])\n        ], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_polygons_pad_at_center__2px(self):\n        aug = iaa.PadToFixedSize(\n            height=5, width=5, pad_mode=\"edge\", position=\"center\")\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        # padding happens at right/bottom, so poly doesn't move\n        expected = ia.PolygonsOnImage([\n            ia.Polygon([(0+1, 0+1), (3+1, 0+1), (3+1, 3+1)])\n        ], shape=(5, 5))\n        assert_cbaois_equal(observed, expected)\n\n    def test_polygons_pad_at_left_top(self):\n        # polygon test with explicit non-center position\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"left-top\")\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        expected = ia.PolygonsOnImage([\n            ia.Polygon([(1+0, 1+0), (1+3, 1+0), (1+3, 1+3)])\n        ], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_polygons_pad_at_right_bottom(self):\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"right-bottom\")\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        expected = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (3, 0), (3, 3)])\n        ], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_polygons_empty(self):\n        aug = iaa.PadToFixedSize(height=5, width=6)\n        psoi = ia.PolygonsOnImage([], shape=(3, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        expected = ia.PolygonsOnImage([], shape=(5, 6))\n        assert_cbaois_equal(observed, expected)\n\n    def test_line_strings__image_already_fullfills_min_shape(self):\n        # line string test with shape not being changed\n        aug = iaa.PadToFixedSize(\n            height=3, width=3, pad_mode=\"edge\", position=\"center\")\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_line_strings(cbaoi)\n\n        expected = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_line_strings_pad_at_center(self):\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"center\")\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_line_strings(cbaoi)\n\n        # padding happens at right/bottom, so LS doesn't move\n        expected = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (3, 0), (3, 3)])\n        ], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_line_strings_pad_at_center__2px(self):\n        aug = iaa.PadToFixedSize(\n            height=5, width=5, pad_mode=\"edge\", position=\"center\")\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_line_strings(cbaoi)\n\n        expected = ia.LineStringsOnImage([\n            ia.LineString([(0+1, 0+1), (3+1, 0+1), (3+1, 3+1)])\n        ], shape=(5, 5))\n        assert_cbaois_equal(observed, expected)\n\n    def test_line_strings_pad_at_left_top(self):\n        # line string test with explicit non-center position\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"left-top\")\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_line_strings(cbaoi)\n\n        expected = ia.LineStringsOnImage([\n            ia.LineString([(1+0, 1+0), (1+3, 1+0), (1+3, 1+3)])\n        ], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_line_strings_pad_at_right_bottom(self):\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"right-bottom\")\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (3, 0), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_line_strings(cbaoi)\n\n        expected = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (3, 0), (3, 3)])\n        ], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_line_strings_empty(self):\n        aug = iaa.PadToFixedSize(height=5, width=6)\n        cbaoi = ia.LineStringsOnImage([], shape=(3, 3))\n\n        observed = aug.augment_line_strings(cbaoi)\n\n        expected = ia.LineStringsOnImage([], shape=(5, 6))\n        assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes__image_already_fullfills_min_shape(self):\n        # bounding boxes test with shape not being changed\n        aug = iaa.PadToFixedSize(\n            height=3, width=3, pad_mode=\"edge\", position=\"center\")\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3),\n        ], shape=(3, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        expected = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3),\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes_pad_at_center(self):\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"center\")\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3),\n        ], shape=(3, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        # aug adds a columns at the right and row at the bottom,\n        # i.e. BB is not affected\n        expected = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3),\n        ], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes_pad_at_center__2px(self):\n        aug = iaa.PadToFixedSize(\n            height=5, width=5, pad_mode=\"edge\", position=\"center\")\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3),\n        ], shape=(3, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        expected = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0+1, y1=1+1, x2=2+1, y2=3+1),\n        ], shape=(5, 5))\n        assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes_pad_at_left_top(self):\n        # bounding boxes test with explicit non-center position\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"left-top\")\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3),\n        ], shape=(3, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        expected = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0+1, y1=1+1, x2=2+1, y2=3+1),\n        ], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes_pad_at_right_bottom(self):\n        aug = iaa.PadToFixedSize(\n            height=4, width=4, pad_mode=\"edge\", position=\"right-bottom\")\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3),\n        ], shape=(3, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        expected = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3),\n        ], shape=(4, 4))\n        assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes_empty(self):\n        aug = iaa.PadToFixedSize(height=5, width=6)\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(3, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        expected = ia.BoundingBoxesOnImage([], shape=(5, 6))\n        assert_cbaois_equal(observed, expected)\n\n    def test_heatmaps__pad_mode_should_be_ignored(self):\n        # basic heatmaps test\n        # pad_mode should be ignored for heatmaps\n        aug = iaa.PadToFixedSize(\n            height=3, width=3, pad_mode=\"edge\", position=\"center\")\n        heatmaps_arr = np.zeros((1, 1, 1), dtype=np.float32) + 1.0\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(1, 1, 3))\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        expected = np.float32([\n            [0, 0, 0],\n            [0, 1.0, 0],\n            [0, 0, 0]\n        ])\n        expected = expected[..., np.newaxis]\n        assert observed.shape == (3, 3, 3)\n        assert np.allclose(observed.arr_0to1, expected)\n\n    def test_heatmaps_smaller_than_image__pad_mode_should_be_ignored(self):\n        # heatmaps with size unequal to image\n        # pad_mode should be ignored for heatmaps\n        aug = iaa.PadToFixedSize(\n            height=32, width=32, pad_mode=\"edge\", position=\"left-top\")\n        heatmaps_arr = np.zeros((15, 15, 1), dtype=np.float32) + 1.0\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(30, 30, 3))\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        expected = np.zeros((16, 16, 1), dtype=np.float32) + 1.0\n        expected[:, 0, 0] = 0.0\n        expected[0, :, 0] = 0.0\n        assert observed.shape == (32, 32, 3)\n        assert np.allclose(observed.arr_0to1, expected)\n\n    def test_segmaps__pad_mode_should_be_ignored(self):\n        # basic segmaps test\n        # pad_mode should be ignored for segmaps\n        aug = iaa.PadToFixedSize(\n            height=3, width=3, pad_mode=\"edge\", position=\"center\")\n        segmaps_arr = np.ones((1, 1, 1), dtype=np.int32)\n        segmaps = SegmentationMapsOnImage(segmaps_arr, shape=(1, 1, 3))\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        expected = np.int32([\n            [0, 0, 0],\n            [0, 1, 0],\n            [0, 0, 0]\n        ])\n        expected = expected[..., np.newaxis]\n        assert observed.shape == (3, 3, 3)\n        assert np.array_equal(observed.arr, expected)\n\n    def test_segmaps_smaller_than_image__pad_mode_should_be_ignored(self):\n        # segmaps with size unequal to image\n        # pad_mode should be ignored for segmaps\n        aug = iaa.PadToFixedSize(\n            height=32, width=32, pad_mode=\"edge\", position=\"left-top\")\n        segmaps_arr = np.ones((15, 15, 1), dtype=np.int32)\n        segmaps = SegmentationMapsOnImage(segmaps_arr, shape=(30, 30, 3))\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        expected = np.ones((16, 16, 1), dtype=np.int32)\n        expected[:, 0, 0] = 0\n        expected[0, :, 0] = 0\n        assert observed.shape == (32, 32, 3)\n        assert np.array_equal(observed.arr, expected)\n\n    def test_get_parameters(self):\n        aug = iaa.PadToFixedSize(width=20, height=10, pad_mode=\"edge\",\n                                 pad_cval=10, position=\"center\")\n        params = aug.get_parameters()\n        assert params[0] == 20\n        assert params[1] == 10\n        assert params[2].value == \"edge\"\n        assert params[3].value == 10\n        assert np.isclose(params[4][0].value, 0.5)\n        assert np.isclose(params[4][1].value, 0.5)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.PadToFixedSize(height=1, width=1)\n\n                image_aug = aug(image=image)\n\n                expected_height = 1\n                expected_width = 1\n                expected_shape = tuple([expected_height, expected_width]\n                                       + list(shape[2:]))\n                assert image_aug.shape == expected_shape\n\n    def test_other_dtypes_bool(self):\n        aug = iaa.PadToFixedSize(height=4, width=3, position=\"center-top\")\n        mask = np.zeros((4, 3), dtype=bool)\n        mask[2, 1] = True\n        image = np.zeros((3, 3), dtype=bool)\n        image[1, 1] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.name == image.dtype.name\n        assert image_aug.shape == (4, 3)\n        assert np.all(image_aug[~mask] == 0)\n        assert np.all(image_aug[mask] == 1)\n\n    def test_other_dtypes_uint_int(self):\n        aug = iaa.PadToFixedSize(height=4, width=3, position=\"center-top\")\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int16\", \"int32\", \"int64\"]\n\n        mask = np.zeros((4, 3), dtype=bool)\n        mask[2, 1] = True\n\n        for dtype in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            if np.dtype(dtype).kind == \"i\":\n                values = [\n                    1, 5, 10, 100, int(0.1 * max_value),\n                    int(0.2 * max_value), int(0.5 * max_value),\n                    max_value - 100, max_value]\n                values = values + [(-1) * value for value in values]\n            else:\n                values = [\n                    1, 5, 10, 100, int(center_value), int(0.1 * max_value),\n                    int(0.2 * max_value), int(0.5 * max_value),\n                    max_value - 100, max_value]\n\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[1, 1] = value\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert image_aug.shape == (4, 3)\n                    assert np.all(image_aug[~mask] == 0)\n                    assert np.all(image_aug[mask] == value)\n\n    def test_other_dtypes_float(self):\n        aug = iaa.PadToFixedSize(height=4, width=3, position=\"center-top\")\n\n        try:\n            high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n        except AttributeError:\n            high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n\n        mask = np.zeros((4, 3), dtype=bool)\n        mask[2, 1] = True\n\n        for dtype in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            def _isclose(a, b):\n                atol = 1e-4 if dtype == \"float16\" else 1e-8\n                return np.isclose(a, b, atol=atol, rtol=0)\n\n            isize = np.dtype(dtype).itemsize\n            values = [0.01, 1.0, 10.0, 100.0, 500 ** (isize - 1),\n                      1000 ** (isize - 1)]\n            values = values + [(-1) * value for value in values]\n            values = values + [min_value, max_value]\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[1, 1] = value\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert image_aug.shape == (4, 3)\n                    assert np.all(_isclose(image_aug[~mask], 0))\n                    assert np.all(_isclose(image_aug[mask],\n                                           high_res_dt(value)))\n\n    def test_pickleable(self):\n        aug = iaa.PadToFixedSize(20, 20, position=\"uniform\", seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(10, 10, 1))\n\n\nclass TestCenterPadToFixedSize(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_image2d(self):\n        for _ in np.arange(10):\n            image = np.arange(4*4*3).astype(np.uint8).reshape((4, 4, 3))\n            aug = iaa.CenterPadToFixedSize(height=5, width=5)\n\n            observed = aug(image=image)\n\n            expected = iaa.pad(image, right=1, bottom=1)\n            assert np.array_equal(observed, expected)\n\n    def test_pickleable(self):\n        aug = iaa.CenterPadToFixedSize(height=20, width=15)\n        runtest_pickleable_uint8_img(aug, shape=(10, 10, 3))\n\n\nclass TestCropToFixedSize(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_image2d_that_needs_to_be_cropped_on_both_sides(self):\n        aug = iaa.CropToFixedSize(height=1, width=1)\n        image = np.uint8([\n            [128, 129, 130],\n            [131, 132, 133],\n            [134, 135, 136]\n        ])\n\n        observed = aug.augment_image(image)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (1, 1)\n\n    def test_image3d_that_needs_to_be_cropped_on_both_sides(self):\n        aug = iaa.CropToFixedSize(height=1, width=1)\n        image = np.uint8([\n            [128, 129, 130],\n            [131, 132, 133],\n            [134, 135, 136]\n        ])\n        image3d = np.atleast_3d(image)\n\n        observed = aug.augment_image(image3d)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (1, 1, 1)\n\n    def test_image3d_rgb_that_needs_to_be_cropped_on_both_sides(self):\n        aug = iaa.CropToFixedSize(height=1, width=1)\n        image = np.uint8([\n            [128, 129, 130],\n            [131, 132, 133],\n            [134, 135, 136]\n        ])\n        image3d_rgb = np.tile(\n            np.atleast_3d(image),\n            (1, 1, 3)\n        )\n\n        observed = aug.augment_image(image3d_rgb)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (1, 1, 3)\n\n    def test_image2d_with_other_dtypes(self):\n        aug = iaa.CropToFixedSize(height=1, width=1)\n        image = np.uint8([\n            [128, 129, 130],\n            [131, 132, 133],\n            [134, 135, 136]\n        ])\n\n        for dtype in [\"float32\", \"float64\", \"int32\"]:\n            with self.subTest(dtype=dtype):\n                observed = aug.augment_image(image.astype(dtype))\n\n                assert observed.dtype.name == dtype\n                assert observed.shape == (1, 1)\n\n    def test_image_with_height_being_too_large(self):\n        # change only one side when other side has already desired size\n        aug = iaa.CropToFixedSize(height=1, width=5)\n        image = np.zeros((3, 5, 3), dtype=np.uint8)\n\n        observed = aug.augment_image(image)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (1, 5, 3)\n\n    def test_image_with_width_being_too_large(self):\n        aug = iaa.CropToFixedSize(height=5, width=1)\n        image = np.zeros((5, 3, 3), dtype=np.uint8)\n\n        observed = aug.augment_image(image)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (5, 1, 3)\n\n    def test_image_fullfills_exactly_max_shape(self):\n        # change no side when all sides have exactly desired size\n        aug = iaa.CropToFixedSize(height=5, width=5)\n        img5x5 = np.zeros((5, 5, 3), dtype=np.uint8)\n        img5x5[2, 2, :] = 255\n\n        observed = aug.augment_image(img5x5)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (5, 5, 3)\n        assert np.array_equal(observed, img5x5)\n\n    def test_image_that_is_smaller_than_max_shape(self):\n        # change no side when all sides have smaller than desired size\n        aug = iaa.CropToFixedSize(height=5, width=5)\n        img4x4 = np.zeros((4, 4, 3), dtype=np.uint8)\n        img4x4[2, 2, :] = 255\n\n        observed = aug.augment_image(img4x4)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (4, 4, 3)\n        assert np.array_equal(observed, img4x4)\n\n    def test_too_large_image_with_width_none(self):\n        aug = iaa.CropToFixedSize(height=5, width=None)\n        image = np.zeros((6, 6, 3), dtype=np.uint8)\n\n        observed = aug.augment_image(image)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (5, 6, 3)\n\n    def test_too_large_image_with_height_none(self):\n        aug = iaa.CropToFixedSize(height=None, width=5)\n        image = np.zeros((6, 6, 3), dtype=np.uint8)\n\n        observed = aug.augment_image(image)\n\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (6, 5, 3)\n\n    def test_image_crop_at_left_top(self):\n        # explicit non-center position test\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"left-top\")\n        img5x5 = np.arange(25, dtype=np.uint8).reshape((5, 5))\n\n        observed = aug.augment_image(img5x5)\n\n        expected = img5x5[2:, 2:]\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (3, 3)\n        assert np.array_equal(observed, expected)\n\n    def test_image_crop_at_right_bottom(self):\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"right-bottom\")\n        img5x5 = np.arange(25, dtype=np.uint8).reshape((5, 5))\n\n        observed = aug.augment_image(img5x5)\n\n        expected = img5x5[:3, :3]\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (3, 3)\n        assert np.array_equal(observed, expected)\n\n    def test_image_crop_at_bottom_center_given_as_tuple_of_floats(self):\n        aug = iaa.CropToFixedSize(height=3, width=3, position=(0.5, 1.0))\n        img5x5 = np.arange(25, dtype=np.uint8).reshape((5, 5))\n\n        observed = aug.augment_image(img5x5)\n\n        expected = img5x5[:3, 1:4]\n        assert observed.dtype.name == \"uint8\"\n        assert observed.shape == (3, 3)\n        assert np.array_equal(observed, expected)\n\n    def test_keypoints__image_already_fullfills_max_shape(self):\n        # keypoint test with shape not being changed\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"center\")\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)], shape=(3, 3))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        expected = ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_keypoints_crop_at_center(self):\n        # basic keypoint test\n        aug = iaa.CropToFixedSize(height=1, width=1, position=\"center\")\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=1, y=1)], shape=(3, 3))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        expected = ia.KeypointsOnImage([ia.Keypoint(x=0, y=0)], shape=(1, 1))\n        assert_cbaois_equal(observed, expected)\n\n    def test_keypoints_crop_at_left_top(self):\n        # keypoint test with explicit non-center position\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"left-top\")\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=2, y=2)], shape=(5, 5))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        expected = ia.KeypointsOnImage([ia.Keypoint(x=0, y=0)], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_keypoints_crop_at_right_bottom(self):\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"right-bottom\")\n        kpsoi = ia.KeypointsOnImage([ia.Keypoint(x=2, y=2)], shape=(5, 5))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        expected = ia.KeypointsOnImage([ia.Keypoint(x=2, y=2)], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_keypoints_empty(self):\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"center\")\n        kpsoi = ia.KeypointsOnImage([], shape=(5, 4))\n\n        observed = aug.augment_keypoints(kpsoi)\n\n        expected = ia.KeypointsOnImage([], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_polygons__image_already_fullfills_max_shape(self):\n        # polygons test with shape not being changed\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"center\")\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(1, 1), (3, 1), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        expected = ia.PolygonsOnImage([\n            ia.Polygon([(1, 1), (3, 1), (3, 3)])\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_polygons_crop_at_center(self):\n        # basic polygons test\n        aug = iaa.CropToFixedSize(height=1, width=1, position=\"center\")\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(1, 1), (3, 1), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_polygons(psoi)\n\n        expected = ia.PolygonsOnImage([\n            ia.Polygon([(1-1, 1-1), (3-1, 1-1), (3-1, 3-1)])\n        ], shape=(1, 1))\n        assert_cbaois_equal(observed, expected)\n\n    def test_polygons_crop_at_left_top(self):\n        # polygons test with explicit non-center position\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"left-top\")\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(1, 1), (3, 1), (3, 3)])\n        ], shape=(5, 5))\n\n        observed = aug.augment_polygons(psoi)\n\n        expected = ia.PolygonsOnImage([\n            ia.Polygon([(1-2, 1-2), (3-2, 1-2), (3-2, 3-2)])\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_polygons_crop_at_right_bottom(self):\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"right-bottom\")\n        psoi = ia.PolygonsOnImage([\n            ia.Polygon([(1, 1), (3, 1), (3, 3)])\n        ], shape=(5, 5))\n\n        observed = aug.augment_polygons(psoi)\n\n        expected = ia.PolygonsOnImage([\n            ia.Polygon([(1, 1), (3, 1), (3, 3)])\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_polygons_empty(self):\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"center\")\n        psoi = ia.PolygonsOnImage([], shape=(5, 4))\n\n        observed = aug.augment_polygons(psoi)\n\n        expected = ia.PolygonsOnImage([], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_line_strings__image_already_fullfills_max_shape(self):\n        # line strings test with shape not being changed\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"center\")\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(1, 1), (3, 1), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_line_strings(cbaoi)\n\n        expected = ia.LineStringsOnImage([\n            ia.LineString([(1, 1), (3, 1), (3, 3)])\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_line_strings_crop_at_center(self):\n        # basic line strings test\n        aug = iaa.CropToFixedSize(height=1, width=1, position=\"center\")\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(1, 1), (3, 1), (3, 3)])\n        ], shape=(3, 3))\n\n        observed = aug.augment_line_strings(cbaoi)\n\n        expected = ia.LineStringsOnImage([\n            ia.LineString([(1-1, 1-1), (3-1, 1-1), (3-1, 3-1)])\n        ], shape=(1, 1))\n        assert_cbaois_equal(observed, expected)\n\n    def test_line_strings_crop_at_left_top(self):\n        # polygons test with explicit non-center position\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"left-top\")\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(1, 1), (3, 1), (3, 3)])\n        ], shape=(5, 5))\n\n        observed = aug.augment_line_strings(cbaoi)\n\n        expected = ia.LineStringsOnImage([\n            ia.LineString([(1-2, 1-2), (3-2, 1-2), (3-2, 3-2)])\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_line_strings_crop_at_right_bottom(self):\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"right-bottom\")\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(1, 1), (3, 1), (3, 3)])\n        ], shape=(5, 5))\n\n        observed = aug.augment_line_strings(cbaoi)\n\n        expected = ia.LineStringsOnImage([\n            ia.LineString([(1, 1), (3, 1), (3, 3)])\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_line_strings_empty(self):\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"center\")\n        cbaoi = ia.LineStringsOnImage([], shape=(5, 4))\n\n        observed = aug.augment_line_strings(cbaoi)\n\n        expected = ia.LineStringsOnImage([], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes__image_already_fullfills_max_shape(self):\n        # bounding boxes test with shape not being changed\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"center\")\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)\n        ], shape=(3, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        expected = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes_crop_at_center(self):\n        # basic bounding boxes test\n        aug = iaa.CropToFixedSize(height=1, width=1, position=\"center\")\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)\n        ], shape=(3, 3))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        expected = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0-1, y1=1-1, x2=2-1, y2=3-1)\n        ], shape=(1, 1))\n        assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes_crop_at_left_top(self):\n        # bounding boxes test with explicit non-center position\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"left-top\")\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)\n        ], shape=(5, 5))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        expected = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0-2, y1=1-2, x2=2-2, y2=3-2)\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes_crop_at_right_bottom(self):\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"right-bottom\")\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)\n        ], shape=(5, 5))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        expected = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=2, y2=3)\n        ], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_bounding_boxes_empty(self):\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"center\")\n        bbsoi = ia.BoundingBoxesOnImage([], shape=(5, 4))\n\n        observed = aug.augment_bounding_boxes(bbsoi)\n\n        expected = ia.BoundingBoxesOnImage([], shape=(3, 3))\n        assert_cbaois_equal(observed, expected)\n\n    def test_heatmaps(self):\n        # basic heatmaps test\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"center\")\n        heatmaps_arr = np.zeros((5, 5, 1), dtype=np.float32) + 1.0\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(5, 5, 3))\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        expected = np.zeros((3, 3, 1), dtype=np.float32) + 1.0\n        assert observed.shape == (3, 3, 3)\n        assert np.allclose(observed.arr_0to1, expected)\n\n    def test_heatmaps_crop_at_left_top(self):\n        # heatmaps, crop at non-center position\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"left-top\")\n        heatmaps_arr = np.linspace(\n            0.0, 1.0, 5 * 5 * 1).reshape((5, 5, 1)).astype(np.float32)\n        heatmaps_oi = ia.HeatmapsOnImage(heatmaps_arr, shape=(5, 5, 3))\n\n        observed = aug.augment_heatmaps([heatmaps_oi])[0]\n\n        expected = heatmaps_arr[2:, 2:, :]\n        assert observed.shape == (3, 3, 3)\n        assert np.allclose(observed.arr_0to1, expected)\n\n    def test_heatmaps_crop_at_right_bottom(self):\n        # heatmaps, crop at non-center position\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"right-bottom\")\n        heatmaps_arr = np.linspace(\n            0.0, 1.0, 5 * 5 * 1).reshape((5, 5, 1)).astype(np.float32)\n        heatmaps_oi = ia.HeatmapsOnImage(heatmaps_arr, shape=(5, 5, 3))\n\n        observed = aug.augment_heatmaps([heatmaps_oi])[0]\n\n        expected = heatmaps_arr[:3, :3, :]\n        assert observed.shape == (3, 3, 3)\n        assert np.allclose(observed.arr_0to1, expected)\n\n    def test_heatmaps_smaller_than_image(self):\n        # heatmaps with size unequal to image\n        aug = iaa.CropToFixedSize(height=32, width=32, position=\"left-top\")\n        heatmaps_arr = np.zeros((17, 17, 1), dtype=np.float32) + 1.0\n        heatmaps = ia.HeatmapsOnImage(heatmaps_arr, shape=(34, 34, 3))\n\n        observed = aug.augment_heatmaps([heatmaps])[0]\n\n        expected = np.zeros((16, 16, 1), dtype=np.float32) + 1.0\n        assert observed.shape == (32, 32, 3)\n        assert np.allclose(observed.arr_0to1, expected)\n\n    def test_segmaps_crop_at_center(self):\n        # basic segmaps test\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"center\")\n        segmaps_arr = np.ones((5, 5, 1), dtype=np.int32)\n        segmaps = SegmentationMapsOnImage(segmaps_arr, shape=(5, 5, 3))\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        expected = np.ones((3, 3, 1), dtype=np.int32)\n        assert observed.shape == (3, 3, 3)\n        assert np.array_equal(observed.arr, expected)\n\n    def test_segmaps_crop_at_left_top(self):\n        # segmaps, crop at non-center position\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"left-top\")\n        segmaps_arr = np.arange(5*5).reshape((5, 5, 1)).astype(np.int32)\n        segmaps_oi = SegmentationMapsOnImage(segmaps_arr, shape=(5, 5, 3))\n\n        observed = aug.augment_segmentation_maps([segmaps_oi])[0]\n\n        expected = segmaps_arr[2:, 2:, :]\n        assert observed.shape == (3, 3, 3)\n        assert np.array_equal(observed.arr, expected)\n\n    def test_segmaps_crop_at_right_bottom(self):\n        # segmaps, crop at non-center position\n        aug = iaa.CropToFixedSize(height=3, width=3, position=\"right-bottom\")\n        segmaps_arr = np.arange(5*5).reshape((5, 5, 1)).astype(np.int32)\n        segmaps_oi = SegmentationMapsOnImage(segmaps_arr, shape=(5, 5, 3))\n\n        observed = aug.augment_segmentation_maps([segmaps_oi])[0]\n\n        expected = segmaps_arr[:3, :3, :]\n        assert observed.shape == (3, 3, 3)\n        assert np.array_equal(observed.arr, expected)\n\n    def test_segmaps_smaller_than_image(self):\n        # segmaps with size unequal to image\n        aug = iaa.CropToFixedSize(height=32, width=32, position=\"left-top\")\n        segmaps_arr = np.ones((17, 17, 1), dtype=np.int32)\n        segmaps = SegmentationMapsOnImage(segmaps_arr, shape=(34, 34, 3))\n\n        observed = aug.augment_segmentation_maps([segmaps])[0]\n\n        expected = np.ones((16, 16, 1), dtype=np.int32)\n        assert observed.shape == (32, 32, 3)\n        assert np.array_equal(observed.arr, expected)\n\n    def test_get_parameters(self):\n        aug = iaa.CropToFixedSize(width=20, height=10, position=\"center\")\n        params = aug.get_parameters()\n        assert params[0] == 20\n        assert params[1] == 10\n        assert np.isclose(params[2][0].value, 0.5)\n        assert np.isclose(params[2][1].value, 0.5)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.CropToFixedSize(height=1, width=1)\n\n                image_aug = aug(image=image)\n\n                expected_height = 0 if shape[0] == 0 else 1\n                expected_width = 0 if shape[1] == 0 else 1\n                expected_shape = tuple([expected_height, expected_width]\n                                       + list(shape[2:]))\n                assert image_aug.shape == expected_shape\n\n    def test_other_dtypes_bool(self):\n        aug = iaa.CropToFixedSize(height=2, width=3, position=\"center-top\")\n        mask = np.zeros((2, 3), dtype=bool)\n        mask[0, 1] = True\n        image = np.zeros((3, 3), dtype=bool)\n        image[1, 1] = True\n\n        image_aug = aug.augment_image(image)\n\n        assert image_aug.dtype.name == image.dtype.name\n        assert image_aug.shape == (2, 3)\n        assert np.all(image_aug[~mask] == 0)\n        assert np.all(image_aug[mask] == 1)\n\n    def test_other_dtypes_uint_int(self):\n        aug = iaa.CropToFixedSize(height=2, width=3, position=\"center-top\")\n        mask = np.zeros((2, 3), dtype=bool)\n        mask[0, 1] = True\n\n        dtypes = [\"uint8\", \"uint16\", \"uint32\", \"uint64\",\n                  \"int8\", \"int16\", \"int32\", \"int64\"]\n\n        for dtype in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            if np.dtype(dtype).kind == \"i\":\n                values = [\n                    1, 5, 10, 100, int(0.1 * max_value), int(0.2 * max_value),\n                    int(0.5 * max_value), max_value - 100, max_value]\n                values = values + [(-1) * value for value in values]\n            else:\n                values = [\n                    1, 5, 10, 100, int(center_value), int(0.1 * max_value),\n                    int(0.2 * max_value), int(0.5 * max_value),\n                    max_value - 100, max_value]\n\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[1, 1] = value\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert image_aug.shape == (2, 3)\n                    assert np.all(image_aug[~mask] == 0)\n                    assert np.all(image_aug[mask] == value)\n\n    def test_other_dtypes_float(self):\n        aug = iaa.CropToFixedSize(height=2, width=3, position=\"center-top\")\n        mask = np.zeros((2, 3), dtype=bool)\n        mask[0, 1] = True\n\n        try:\n            high_res_dt = np.float128\n            dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n        except AttributeError:\n            high_res_dt = np.float64\n            dtypes = [\"float16\", \"float32\", \"float64\"]\n\n        for dtype in dtypes:\n            min_value, center_value, max_value = \\\n                iadt.get_value_range_of_dtype(dtype)\n\n            def _isclose(a, b):\n                atol = 1e-4 if dtype == \"float16\" else 1e-8\n                return np.isclose(a, b, atol=atol, rtol=0)\n\n            isize = np.dtype(dtype).itemsize\n            values = [0.01, 1.0, 10.0, 100.0, 500 ** (isize - 1),\n                      1000 ** (isize - 1)]\n            values = values + [(-1) * value for value in values]\n            values = values + [min_value, max_value]\n            for value in values:\n                with self.subTest(dtype=dtype, value=value):\n                    image = np.zeros((3, 3), dtype=dtype)\n                    image[1, 1] = value\n\n                    image_aug = aug.augment_image(image)\n\n                    assert image_aug.dtype.name == dtype\n                    assert image_aug.shape == (2, 3)\n                    assert np.all(_isclose(image_aug[~mask], 0))\n                    assert np.all(_isclose(image_aug[mask],\n                                           high_res_dt(value)))\n\n    def test_pickleable(self):\n        aug = iaa.CropToFixedSize(10, 10, position=\"uniform\", seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(20, 20, 1))\n\n\nclass TestCenterCropToFixedSize(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_on_single_image(self):\n        for _ in np.arange(10):\n            image = np.arange(11*11*2).astype(np.uint8).reshape((11, 11, 2))\n            aug = iaa.CenterCropToFixedSize(width=3, height=3)\n\n            observed = aug(image=image)\n\n            assert np.array_equal(observed, image[5-1:5+2, 5-1:5+2, :])\n\n    def test_pickleable(self):\n        aug = iaa.CenterCropToFixedSize(height=12, width=10)\n        runtest_pickleable_uint8_img(aug, shape=(15, 20, 3))\n\n\nclass TestCropToMultiplesOf(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.CropToMultiplesOf(width_multiple=1, height_multiple=2,\n                                    position=\"center\")\n        assert aug.width_multiple == 1\n        assert aug.height_multiple == 2\n        assert np.isclose(aug.position[0].value, 0.5)\n        assert np.isclose(aug.position[1].value, 0.5)\n\n    def test_multiples_are_1(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToMultiplesOf(1, 1, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image)\n\n    def test_on_3x3_image__no_change(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToMultiplesOf(3, 3, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image)\n\n    def test_on_3x3_image__with_change(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToMultiplesOf(2, 2, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:2, 0:2, :])\n\n    def test_on_3x3_image__only_width_changed(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToMultiplesOf(height_multiple=3, width_multiple=2,\n                                    position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:3, 0:2, :])\n\n    def test_on_3x3_image__only_height_changed(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToMultiplesOf(height_multiple=2, width_multiple=3,\n                                    position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:2, 0:3, :])\n\n    def test_on_3x4_image(self):\n        image = np.arange((3*4*3)).astype(np.uint8).reshape((3, 4, 3))\n        aug = iaa.CropToMultiplesOf(2, 2, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:2, 0:4, :])\n\n    def test_on_7x9_image(self):\n        image = np.arange((7*9*3)).astype(np.uint8).reshape((7, 9, 3))\n        aug = iaa.CropToMultiplesOf(height_multiple=5, width_multiple=6,\n                                    position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[1:6, 1:7, :])\n\n    def test_width_multiple_is_none(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToMultiplesOf(height_multiple=2, width_multiple=None,\n                                    position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:2, 0:3, :])\n\n    def test_height_multiple_is_none(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToMultiplesOf(height_multiple=None, width_multiple=2,\n                                    position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:3, 0:2, :])\n\n    def test_heatmaps(self):\n        # segmaps are implemented in the same way in CropToFixesSize\n        # and already tested there, so there is no need to test them again here\n        arr = np.linspace(0, 1.0, 50*50).astype(np.float32).reshape((50, 50, 1))\n        heatmap = ia.HeatmapsOnImage(arr, shape=(99, 99, 3))\n        aug = iaa.CropToMultiplesOf(height_multiple=50, width_multiple=50,\n                                    position=\"center\")\n\n        observed = aug(heatmaps=heatmap)\n\n        assert observed.shape == (50, 50, 3)\n        assert np.allclose(observed.arr_0to1,\n                           heatmap.arr_0to1[12:-13, 12:-13, :])\n\n    def test_keypoints(self):\n        kps = [ia.Keypoint(x=2, y=3)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(8, 4, 3))\n        aug = iaa.CropToMultiplesOf(height_multiple=5, width_multiple=2,\n                                    position=\"center\")\n\n        observed = aug(keypoints=kpsoi)\n\n        assert observed.keypoints[0].x == 2\n        assert observed.keypoints[0].y == 2\n\n    def test_get_parameters(self):\n        aug = iaa.CropToMultiplesOf(width_multiple=1, height_multiple=2,\n                                    position=\"center\")\n\n        params = aug.get_parameters()\n\n        assert params[0] == 1\n        assert params[1] == 2\n        assert np.isclose(params[2][0].value, 0.5)\n        assert np.isclose(params[2][1].value, 0.5)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.CropToMultiplesOf(2, 2)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_pickleable(self):\n        aug = iaa.CropToMultiplesOf(5, 5, position=\"uniform\", seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(14, 14, 1))\n\n\nclass TestCenterCropToMultiplesOf(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_on_3x4_image(self):\n        for _ in np.arange(10):\n            image = np.mod(np.arange((17*14*3)), 255)\n            image = image.astype(np.uint8).reshape((17, 14, 3))\n            aug = iaa.CenterCropToMultiplesOf(height_multiple=5,\n                                              width_multiple=5)\n\n            observed = aug(image=image)\n\n            assert np.array_equal(observed, image[1:-1, 2:-2, :])\n\n    def test_pickleable(self):\n        aug = iaa.CenterCropToMultiplesOf(5, 5)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(14, 14, 1))\n\n\nclass TestPadToMultiplesOf(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.PadToMultiplesOf(width_multiple=1, height_multiple=2,\n                                   position=\"center\")\n        assert aug.width_multiple == 1\n        assert aug.height_multiple == 2\n        assert np.isclose(aug.position[0].value, 0.5)\n        assert np.isclose(aug.position[1].value, 0.5)\n\n    def test_multiples_are_1(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToMultiplesOf(1, 1, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image)\n\n    def test_on_3x3_image__no_change(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToMultiplesOf(3, 3, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image)\n\n    def test_on_3x3_image__with_change(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToMultiplesOf(2, 2, position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, bottom=1, right=1)\n        assert np.array_equal(observed, expected)\n\n    def test_on_3x3_image__only_width_changed(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToMultiplesOf(height_multiple=3, width_multiple=2,\n                                   position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, right=1)\n        assert np.array_equal(observed, expected)\n\n    def test_on_3x3_image__only_height_changed(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToMultiplesOf(height_multiple=2, width_multiple=3,\n                                   position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, bottom=1)\n        assert np.array_equal(observed, expected)\n\n    def test_on_3x4_image(self):\n        image = np.arange((3*4*3)).astype(np.uint8).reshape((3, 4, 3))\n        aug = iaa.PadToMultiplesOf(2, 2, position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, bottom=1)\n        assert np.array_equal(observed, expected)\n\n    def test_on_7x9_image(self):\n        image = np.arange((7*9*3)).astype(np.uint8).reshape((7, 9, 3))\n        aug = iaa.PadToMultiplesOf(height_multiple=5, width_multiple=6,\n                                   position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, top=1, bottom=2, left=1, right=2)\n        assert np.array_equal(observed, expected)\n\n    def test_on_7x9_image__cval(self):\n        image = np.arange((7*9*3)).astype(np.uint8).reshape((7, 9, 3))\n        aug = iaa.PadToMultiplesOf(height_multiple=5, width_multiple=6,\n                                   pad_cval=100,\n                                   position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, top=1, bottom=2, left=1, right=2, cval=100)\n        assert np.array_equal(observed, expected)\n\n    def test_on_7x9_image__mode(self):\n        image = np.arange((7*9*3)).astype(np.uint8).reshape((7, 9, 3))\n        aug = iaa.PadToMultiplesOf(height_multiple=5, width_multiple=6,\n                                   pad_mode=\"edge\",\n                                   position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, top=1, bottom=2, left=1, right=2, mode=\"edge\")\n        assert np.array_equal(observed, expected)\n\n    def test_width_multiple_is_none(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToMultiplesOf(height_multiple=2, width_multiple=None,\n                                   position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, bottom=1)\n        assert np.array_equal(observed, expected)\n\n    def test_height_multiple_is_none(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToMultiplesOf(height_multiple=None, width_multiple=2,\n                                   position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, right=1)\n        assert np.array_equal(observed, expected)\n\n    def test_heatmaps(self):\n        # segmaps are implemented in the same way in PadToFixesSize\n        # and already tested there, so there is no need to test them again here\n        arr = np.linspace(0, 1.0, 51*51).astype(np.float32).reshape((51, 51, 1))\n        heatmap = ia.HeatmapsOnImage(arr, shape=(101, 101, 3))\n        aug = iaa.PadToMultiplesOf(height_multiple=100, width_multiple=100,\n                                   position=\"center\")\n\n        observed = aug(heatmaps=heatmap)\n\n        expected = heatmap.pad(top=25, bottom=25, left=25, right=25)\n        assert observed.shape == (200, 200, 3)\n        assert np.allclose(observed.arr_0to1, expected.arr_0to1)\n\n    def test_keypoints(self):\n        kps = [ia.Keypoint(x=2, y=3)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(8, 4, 3))\n        aug = iaa.PadToMultiplesOf(height_multiple=5, width_multiple=2,\n                                   position=\"center\")\n\n        observed = aug(keypoints=kpsoi)\n\n        assert observed.keypoints[0].x == 2\n        assert observed.keypoints[0].y == 4\n\n    def test_get_parameters(self):\n        aug = iaa.PadToMultiplesOf(width_multiple=1, height_multiple=2,\n                                   pad_cval=5, pad_mode=\"edge\",\n                                   position=\"center\")\n\n        params = aug.get_parameters()\n\n        assert params[0] == 1\n        assert params[1] == 2\n        assert params[2].value == \"edge\"\n        assert params[3].value == 5\n        assert np.isclose(params[4][0].value, 0.5)\n        assert np.isclose(params[4][1].value, 0.5)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.PadToMultiplesOf(2, 2)\n\n                image_aug = aug(image=image)\n\n                expected_height = 2\n                expected_width = 2\n                expected_shape = tuple([expected_height, expected_width]\n                                       + list(shape[2:]))\n                assert image_aug.shape == expected_shape\n\n    def test_pickleable(self):\n        aug = iaa.PadToMultiplesOf(5, 5, position=\"uniform\", seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(11, 11, 1))\n\n\nclass TestCenterPadToMultiplesOf(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_on_3x4_image(self):\n        for _ in np.arange(10):\n            image = np.arange((3*6*3)).astype(np.uint8).reshape((3, 6, 3))\n            aug = iaa.CenterPadToMultiplesOf(5, 5)\n\n            observed = aug(image=image)\n\n            expected = iaa.pad(image, top=1, right=2, bottom=1, left=2)\n            assert np.array_equal(observed, expected)\n\n    def test_pickleable(self):\n        aug = iaa.CenterPadToMultiplesOf(5, 5)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(11, 11, 1))\n\n\nclass TestCropToPowersOf(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.CropToPowersOf(width_base=2, height_base=3,\n                                 position=\"center\")\n        assert aug.width_base == 2\n        assert aug.height_base == 3\n        assert np.isclose(aug.position[0].value, 0.5)\n        assert np.isclose(aug.position[1].value, 0.5)\n\n    def test_on_3x3_image__no_change(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToPowersOf(3, 3, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image)\n\n    def test_on_3x3_image__with_change(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToPowersOf(2, 2, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:2, 0:2, :])\n\n    def test_on_3x3_image__only_width_changed(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToPowersOf(height_base=3, width_base=2,\n                                 position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:3, 0:2, :])\n\n    def test_on_3x3_image__only_height_changed(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToPowersOf(height_base=2, width_base=3,\n                                 position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:2, 0:3, :])\n\n    def test_on_3x4_image(self):\n        image = np.arange((3*4*3)).astype(np.uint8).reshape((3, 4, 3))\n        aug = iaa.CropToPowersOf(2, 2, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:2, 0:4, :])\n\n    def test_on_17x26_image(self):\n        image = np.mod(\n            np.arange((17*26*3)),\n            255\n        ).astype(np.uint8).reshape((17, 26, 3))\n        aug = iaa.CropToPowersOf(height_base=2, width_base=3,\n                                 position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:16, 8:17, :])\n\n    def test_does_not_crop_towards_exponent_of_zero(self):\n        # Test for: axis_size < B,\n        # this should not lead to crops that result in exponent of B^0=1,\n        # i.e. the respective axes should simply not be changed.\n        image = np.arange((3*4*3)).astype(np.uint8).reshape((3, 4, 3))\n        aug = iaa.CropToPowersOf(10, 10, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image)\n\n    def test_width_base_is_none(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToPowersOf(height_base=2, width_base=None,\n                                 position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:2, 0:3, :])\n\n    def test_height_base_is_none(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.CropToPowersOf(height_base=None, width_base=2,\n                                 position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:3, 0:2, :])\n\n    def test_heatmaps(self):\n        # segmaps are implemented in the same way in CropToFixesSize\n        # and already tested there, so there is no need to test them again here\n        arr = np.linspace(0, 1.0, 50*50).astype(np.float32).reshape((50, 50, 1))\n        heatmap = ia.HeatmapsOnImage(arr, shape=(99, 99, 3))\n        aug = iaa.CropToPowersOf(height_base=50, width_base=50,\n                                 position=\"center\")\n\n        observed = aug(heatmaps=heatmap)\n\n        assert observed.shape == (50, 50, 3)\n        assert np.allclose(observed.arr_0to1,\n                           heatmap.arr_0to1[12:-13, 12:-13, :])\n\n    def test_keypoints(self):\n        kps = [ia.Keypoint(x=2, y=3)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(8, 4, 3))\n        aug = iaa.CropToPowersOf(height_base=5, width_base=2,\n                                 position=\"center\")\n\n        observed = aug(keypoints=kpsoi)\n\n        assert observed.keypoints[0].x == 2\n        assert observed.keypoints[0].y == 2\n\n    def test_get_parameters(self):\n        aug = iaa.CropToPowersOf(width_base=1, height_base=2,\n                                 position=\"center\")\n\n        params = aug.get_parameters()\n\n        assert params[0] == 1\n        assert params[1] == 2\n        assert np.isclose(params[2][0].value, 0.5)\n        assert np.isclose(params[2][1].value, 0.5)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.CropToPowersOf(2, 2)\n\n                image_aug = aug(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_pickleable(self):\n        aug = iaa.CropToPowersOf(2, 2, position=\"uniform\", seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(15, 15, 1))\n\n\nclass TestCenterCropToPowersOf(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_on_3x3_image__with_change(self):\n        for _ in np.arange(10):\n            image = np.arange((11*13*3)).astype(np.uint8).reshape((11, 13, 3))\n            aug = iaa.CenterCropToPowersOf(height_base=2, width_base=3)\n\n            observed = aug(image=image)\n\n            assert np.array_equal(observed, image[1:-2, 2:-2, :])\n\n    def test_pickleable(self):\n        aug = iaa.CenterCropToPowersOf(2, 2)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(15, 15, 1))\n\n\nclass TestPadToPowersOf(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.PadToPowersOf(width_base=2, height_base=3,\n                                position=\"center\")\n        assert aug.width_base == 2\n        assert aug.height_base == 3\n        assert np.isclose(aug.position[0].value, 0.5)\n        assert np.isclose(aug.position[1].value, 0.5)\n\n    def test_on_3x3_image__no_change(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToPowersOf(3, 3, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image)\n\n    def test_on_3x3_image__with_change(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToPowersOf(2, 2, position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, bottom=1, right=1)\n        assert np.array_equal(observed, expected)\n\n    def test_on_3x3_image__only_width_changed(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToPowersOf(height_base=3, width_base=2,\n                                position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, right=1)\n        assert np.array_equal(observed, expected)\n\n    def test_on_3x3_image__only_height_changed(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToPowersOf(height_base=2, width_base=3,\n                                position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, bottom=1)\n        assert np.array_equal(observed, expected)\n\n    def test_on_3x4_image(self):\n        image = np.arange((3*4*3)).astype(np.uint8).reshape((3, 4, 3))\n        aug = iaa.PadToPowersOf(2, 2, position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, bottom=1)\n        assert np.array_equal(observed, expected)\n\n    def test_on_7x22_image(self):\n        image = np.mod(\n            np.arange((7*22*3)),\n            255\n        ).astype(np.uint8).reshape((7, 22, 3))\n        aug = iaa.PadToPowersOf(height_base=12, width_base=2,\n                                position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, top=2, bottom=3, left=5, right=5)\n        assert np.array_equal(observed, expected)\n\n    def test_on_7x22_image__cval(self):\n        image = np.mod(\n            np.arange((7*22*3)),\n            255\n        ).astype(np.uint8).reshape((7, 22, 3))\n        aug = iaa.PadToPowersOf(height_base=12, width_base=2,\n                                pad_cval=100,\n                                position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, top=2, bottom=3, left=5, right=5, cval=100)\n        assert np.array_equal(observed, expected)\n\n    def test_on_7x22_image__mode(self):\n        image = np.mod(\n            np.arange((7*22*3)),\n            255\n        ).astype(np.uint8).reshape((7, 22, 3))\n        aug = iaa.PadToPowersOf(height_base=12, width_base=2,\n                                pad_mode=\"edge\",\n                                position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, top=2, bottom=3, left=5, right=5, mode=\"edge\")\n        assert np.array_equal(observed, expected)\n\n    def test_width_base_is_none(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToPowersOf(height_base=2, width_base=None,\n                                position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, bottom=1)\n        assert np.array_equal(observed, expected)\n\n    def test_height_base_is_none(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToPowersOf(height_base=None, width_base=2,\n                                position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, right=1)\n        assert np.array_equal(observed, expected)\n\n    def test_heatmaps(self):\n        # segmaps are implemented in the same way in PadToFixesSize\n        # and already tested there, so there is no need to test them again here\n        arr = np.linspace(0, 1.0, 51*51).astype(np.float32).reshape((51, 51, 1))\n        heatmap = ia.HeatmapsOnImage(arr, shape=(101, 101, 3))\n        aug = iaa.PadToPowersOf(height_base=200, width_base=200,\n                                position=\"center\")\n\n        observed = aug(heatmaps=heatmap)\n\n        expected = heatmap.pad(top=25, bottom=25, left=25, right=25)\n        assert observed.shape == (200, 200, 3)\n        assert np.allclose(observed.arr_0to1, expected.arr_0to1)\n\n    def test_keypoints(self):\n        kps = [ia.Keypoint(x=2, y=3)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(14, 4, 3))\n        aug = iaa.PadToPowersOf(height_base=4, width_base=2,\n                                position=\"center\")\n\n        observed = aug(keypoints=kpsoi)\n\n        assert observed.keypoints[0].x == 2\n        assert observed.keypoints[0].y == 4\n\n    def test_get_parameters(self):\n        aug = iaa.PadToPowersOf(width_base=1, height_base=2,\n                                pad_cval=5, pad_mode=\"edge\",\n                                position=\"center\")\n\n        params = aug.get_parameters()\n\n        assert params[0] == 1\n        assert params[1] == 2\n        assert params[2].value == \"edge\"\n        assert params[3].value == 5\n        assert np.isclose(params[4][0].value, 0.5)\n        assert np.isclose(params[4][1].value, 0.5)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.PadToPowersOf(2, 2)\n\n                image_aug = aug(image=image)\n\n                expected_height = 2\n                expected_width = 2\n                expected_shape = tuple([expected_height, expected_width]\n                                       + list(shape[2:]))\n                assert image_aug.shape == expected_shape\n\n    def test_pickleable(self):\n        aug = iaa.PadToPowersOf(2, 2, position=\"uniform\", seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(9, 9, 1))\n\n\nclass TestCenterPadToPowersOf(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_on_3x3_image__with_change(self):\n        for _ in np.arange(10):\n            image = np.arange((5*13*3)).astype(np.uint8).reshape((5, 13, 3))\n            aug = iaa.CenterPadToPowersOf(height_base=2, width_base=2)\n\n            observed = aug(image=image)\n\n            expected = iaa.pad(image, top=1, right=2, bottom=2, left=1)\n            assert np.array_equal(observed, expected)\n\n    def test_pickleable(self):\n        aug = iaa.CenterPadToPowersOf(2, 2)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(9, 9, 1))\n\n\nclass TestCropToAspectRatio(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.CropToAspectRatio(2.0, position=\"center\")\n        assert np.isclose(aug.aspect_ratio, 2.0)\n        assert np.isclose(aug.position[0].value, 0.5)\n        assert np.isclose(aug.position[1].value, 0.5)\n\n    def test_on_4x4_image__no_change(self):\n        image = np.arange((4*4*3)).astype(np.uint8).reshape((4, 4, 3))\n        aug = iaa.CropToAspectRatio(1.0, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image)\n\n    def test_on_4x4_image__with_change__wider(self):\n        image = np.arange((4*4*3)).astype(np.uint8).reshape((4, 4, 3))\n        aug = iaa.CropToAspectRatio(2.0, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[1:3, 0:4, :])\n\n    def test_on_4x4_image__with_change__higher(self):\n        image = np.arange((4*4*3)).astype(np.uint8).reshape((4, 4, 3))\n        aug = iaa.CropToAspectRatio(0.5, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[0:4, 1:3, :])\n\n    def test_on_5x4_image__with_change__wider(self):\n        image = np.arange((5*4*3)).astype(np.uint8).reshape((5, 4, 3))\n        aug = iaa.CropToAspectRatio(2.0, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[1:3, 0:4, :])\n\n    def test_on_5x4_image__with_change__higher(self):\n        image = np.arange((5*4*3)).astype(np.uint8).reshape((5, 4, 3))\n        aug = iaa.CropToAspectRatio(0.5, position=\"center\")\n\n        observed = aug(image=image)\n\n        # Here it could either crop 1px or 2px from the width (leading to\n        # aspect ratios of 3/5=0.6 or 2/5=0.4. The underlying method rather\n        # crops one pixel too few than one too many, hence we only crop 1px\n        # here.\n        assert np.array_equal(observed, image[0:5, 0:3, :])\n\n    def test_unreachable_aspect_ratio__wider(self):\n        image = np.arange((5*4*3)).astype(np.uint8).reshape((5, 4, 3))\n        aug = iaa.CropToAspectRatio(20.0, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[2:3, 0:4, :])\n\n    def test_unreachable_aspect_ratio__higher(self):\n        image = np.arange((5*4*3)).astype(np.uint8).reshape((5, 4, 3))\n        aug = iaa.CropToAspectRatio(0.01, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[:, 1:2, :])\n\n    def test_heatmaps(self):\n        # segmaps are implemented in the same way in CropToFixesSize\n        # and already tested there, so there is no need to test them again here\n        arr = np.linspace(0, 1.0, 50*50).astype(np.float32).reshape((50, 50, 1))\n        heatmap = ia.HeatmapsOnImage(arr, shape=(100, 100, 3))\n        aug = iaa.CropToAspectRatio(2.0, position=\"center\")\n\n        observed = aug(heatmaps=heatmap)\n\n        assert observed.shape == (50, 100, 3)\n        assert np.allclose(observed.arr_0to1,\n                           heatmap.arr_0to1[12:-13, :, :])\n\n    def test_keypoints(self):\n        kps = [ia.Keypoint(x=2, y=3)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(8, 8, 3))\n        aug = iaa.CropToAspectRatio(2.0, position=\"center\")\n\n        observed = aug(keypoints=kpsoi)\n\n        assert observed.keypoints[0].x == 2\n        assert observed.keypoints[0].y == 1\n\n    def test_get_parameters(self):\n        aug = iaa.CropToAspectRatio(2.0, position=\"center\")\n\n        params = aug.get_parameters()\n\n        assert np.isclose(params[0], 2.0)\n        assert np.isclose(params[1][0].value, 0.5)\n        assert np.isclose(params[1][1].value, 0.5)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            for aspect_ratio in [2.0, 1.0, 0.5]:\n                with self.subTest(shape=shape, aspect_ratio=aspect_ratio):\n                    image = np.zeros(shape, dtype=np.uint8)\n                    aug = iaa.CropToAspectRatio(aspect_ratio)\n\n                    image_aug = aug(image=image)\n\n                    assert image_aug.shape == image.shape\n\n    def test_pickleable(self):\n        aug = iaa.CropToAspectRatio(1.0, position=\"uniform\", seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(20, 10, 1))\n\n\nclass TestCenterCropToAspectRatio(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_on_5x4_image__with_change__wider(self):\n        for _ in np.arange(10):\n            image = np.arange((5*4*3)).astype(np.uint8).reshape((5, 4, 3))\n            aug = iaa.CenterCropToAspectRatio(2.0)\n\n            observed = aug(image=image)\n\n            assert np.array_equal(observed, image[1:3, 0:4, :])\n\n    def test_pickleable(self):\n        aug = iaa.CenterCropToAspectRatio(1.0)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(20, 10, 1))\n\n\nclass TestPadToAspectRatio(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        aug = iaa.PadToAspectRatio(2.0, position=\"center\")\n        assert np.isclose(aug.aspect_ratio, 2.0)\n        assert np.isclose(aug.position[0].value, 0.5)\n        assert np.isclose(aug.position[1].value, 0.5)\n\n    def test_on_3x3_image__no_change(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToAspectRatio(1.0, position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image)\n\n    def test_on_3x3_image__with_change__wider(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToAspectRatio(2.0, position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, left=1, right=2)\n        assert np.array_equal(observed, expected)\n\n    def test_on_3x3_image__with_change__higher(self):\n        image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n        aug = iaa.PadToAspectRatio(0.5, position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, top=1, bottom=2)\n        assert np.array_equal(observed, expected)\n\n    def test_on_3x4_image(self):\n        image = np.arange((3*4*3)).astype(np.uint8).reshape((3, 4, 3))\n        aug = iaa.PadToAspectRatio(2.0, position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, left=1, right=1)\n        assert np.array_equal(observed, expected)\n\n    def test_on_7x22_image__height_padded_even_though_ratio_is_wide(self):\n        image = np.mod(\n            np.arange((7*22*3)),\n            255\n        ).astype(np.uint8).reshape((7, 22, 3))\n        aug = iaa.PadToAspectRatio(2.0, position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, top=2, bottom=2)\n        assert np.array_equal(observed, expected)\n\n    def test_on_10x6_image__cval(self):\n        image = np.arange((10*6*3)).astype(np.uint8).reshape((10, 6, 3))\n        aug = iaa.PadToAspectRatio(0.5, pad_cval=100, position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, top=1, bottom=1, cval=100)\n        assert np.array_equal(observed, expected)\n\n    def test_on_10x6_image__mode(self):\n        image = np.arange((10*6*3)).astype(np.uint8).reshape((10, 6, 3))\n        aug = iaa.PadToAspectRatio(0.5,\n                                   pad_mode=\"edge\",\n                                   position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, top=1, bottom=1, mode=\"edge\")\n        assert np.array_equal(observed, expected)\n\n    def test_heatmaps(self):\n        # segmaps are implemented in the same way in PadToFixesSize\n        # and already tested there, so there is no need to test them again here\n        arr = np.linspace(0, 1.0, 50*50).astype(np.float32).reshape((50, 50, 1))\n        heatmap = ia.HeatmapsOnImage(arr, shape=(100, 100, 3))\n        aug = iaa.PadToAspectRatio(2.0, position=\"center\")\n\n        observed = aug(heatmaps=heatmap)\n\n        expected = heatmap.pad(top=0, bottom=0, left=25, right=25)\n        assert observed.shape == (100, 200, 3)\n        assert np.allclose(observed.arr_0to1, expected.arr_0to1)\n\n    def test_keypoints(self):\n        kps = [ia.Keypoint(x=2, y=3)]\n        kpsoi = ia.KeypointsOnImage(kps, shape=(10, 5, 3))\n        aug = iaa.PadToAspectRatio(1.0, position=\"center\")\n\n        observed = aug(keypoints=kpsoi)\n\n        assert observed.keypoints[0].x == 2+2\n        assert observed.keypoints[0].y == 3\n\n    def test_get_parameters(self):\n        aug = iaa.PadToAspectRatio(2.0,\n                                   pad_cval=5, pad_mode=\"edge\",\n                                   position=\"center\")\n\n        params = aug.get_parameters()\n\n        assert np.isclose(params[0], 2.0)\n        assert params[1].value == \"edge\"\n        assert params[2].value == 5\n        assert np.isclose(params[3][0].value, 0.5)\n        assert np.isclose(params[3][1].value, 0.5)\n\n    def test_zero_sized_axes__wider(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.PadToAspectRatio(2.0)\n\n                image_aug = aug(image=image)\n\n                height, width = shape[0:2]\n                if width == 0 and height == 0:\n                    h_exp, w_exp = (1, 2)\n                elif width == 0:\n                    h_exp, w_exp = (height, height * 2)\n                else:  # height == 0\n                    h_exp, w_exp = (1, 2)\n\n                expected_shape = tuple([h_exp, w_exp] + list(shape[2:]))\n                assert image_aug.shape == expected_shape\n\n    def test_zero_sized_axes__higher(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.PadToAspectRatio(0.5)\n\n                image_aug = aug(image=image)\n\n                height, width = shape[0:2]\n                if width == 0 and height == 0:\n                    h_exp, w_exp = (2, 1)\n                elif height == 0:\n                    h_exp, w_exp = (width * 2, width)\n                else:  # width == 0\n                    h_exp, w_exp = (2, 1)\n\n                expected_shape = tuple([h_exp, w_exp] + list(shape[2:]))\n                assert image_aug.shape == expected_shape\n\n    def test_pickleable(self):\n        aug = iaa.PadToAspectRatio(2.0, position=\"uniform\", seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(10, 10, 1))\n\n\nclass TestCenterPadToAspectRatio(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_on_3x3_image(self):\n        for _ in np.arange(10):\n            image = np.arange((3*3*3)).astype(np.uint8).reshape((3, 3, 3))\n            aug = iaa.CenterPadToAspectRatio(2.0)\n\n            observed = aug(image=image)\n\n            expected = iaa.pad(image, left=1, right=2)\n            assert np.array_equal(observed, expected)\n\n    def test_pickleable(self):\n        aug = iaa.CenterPadToAspectRatio(2.0)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(10, 10, 1))\n\n\nclass TestCropToSquare(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_on_7x4_image(self):\n        image = np.arange((7*4*3)).astype(np.uint8).reshape((7, 4, 3))\n        aug = iaa.CropToSquare(position=\"center\")\n\n        observed = aug(image=image)\n\n        assert np.array_equal(observed, image[1:5, 0:4, :])\n\n    def test_pickleable(self):\n        aug = iaa.CropToSquare(position=\"uniform\")\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(10, 15, 1))\n\n\nclass TestCenterCropToSquare(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_on_7x4_image(self):\n        for _ in np.arange(10):\n            image = np.arange((7*4*3)).astype(np.uint8).reshape((7, 4, 3))\n            aug = iaa.CenterCropToSquare()\n\n            observed = aug(image=image)\n\n            assert np.array_equal(observed, image[1:5, 0:4, :])\n\n    def test_pickleable(self):\n        aug = iaa.CenterCropToSquare()\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(10, 15, 1))\n\n\nclass TestPadToSquare(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_on_7x4_image(self):\n        image = np.arange((7*4*3)).astype(np.uint8).reshape((7, 4, 3))\n        aug = iaa.PadToSquare(position=\"center\")\n\n        observed = aug(image=image)\n\n        expected = iaa.pad(image, left=1, right=2)\n        assert np.array_equal(observed, expected)\n\n    def test_pickleable(self):\n        aug = iaa.PadToSquare(position=\"uniform\")\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(10, 15, 1))\n\n\nclass TestCenterPadToSquare(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_on_7x4_image(self):\n        for _ in np.arange(10):\n            image = np.arange((7*4*3)).astype(np.uint8).reshape((7, 4, 3))\n            aug = iaa.CenterPadToSquare()\n\n            observed = aug(image=image)\n\n            expected = iaa.pad(image, left=1, right=2)\n            assert np.array_equal(observed, expected)\n\n    def test_pickleable(self):\n        aug = iaa.CenterPadToSquare()\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(10, 15, 1))\n\n\nclass TestKeepSizeByResize(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @property\n    def children(self):\n        return iaa.Crop((1, 0, 0, 0), keep_size=False)\n\n    @property\n    def kpsoi(self):\n        kps = [ia.Keypoint(x=0, y=1), ia.Keypoint(x=1, y=1),\n               ia.Keypoint(x=2, y=3)]\n        return ia.KeypointsOnImage(kps, shape=(4, 4, 3))\n\n    @property\n    def heatmaps(self):\n        heatmaps_arr = np.linspace(\n            0.0, 1.0, 4*4*1).reshape((4, 4, 1)).astype(np.float32)\n        return HeatmapsOnImage(heatmaps_arr, shape=(4, 4, 1))\n\n    @property\n    def heatmaps_cubic(self):\n        heatmaps_arr = self.heatmaps.get_arr()\n        heatmaps_oi_cubic = HeatmapsOnImage(\n            heatmaps_arr[1:, :, :], shape=(3, 4, 3)\n        ).resize((4, 4), interpolation=\"cubic\")\n        heatmaps_oi_cubic.shape = (4, 4, 3)\n        return heatmaps_oi_cubic\n\n    @property\n    def heatmaps_nearest(self):\n        heatmaps_arr = self.heatmaps.get_arr()\n        heatmaps_oi_nearest = HeatmapsOnImage(\n            heatmaps_arr[1:, :, :], shape=(3, 4, 1)\n        ).resize((4, 4), interpolation=\"nearest\")\n        heatmaps_oi_nearest.shape = (4, 4, 3)\n        return heatmaps_oi_nearest\n\n    @property\n    def segmaps(self):\n        segmaps_arr = np.arange(4*4*1).reshape((4, 4, 1)).astype(np.int32)\n        return SegmentationMapsOnImage(segmaps_arr, shape=(4, 4, 1))\n\n    @property\n    def segmaps_nearest(self):\n        segmaps_arr = self.segmaps.get_arr()\n        segmaps_oi_nearest = SegmentationMapsOnImage(\n            segmaps_arr[1:, :, :], shape=(3, 4, 1)\n        ).resize((4, 4), interpolation=\"nearest\")\n        segmaps_oi_nearest.shape = (4, 4, 3)\n        return segmaps_oi_nearest\n\n    def test__draw_samples_each_one_interpolation(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=\"nearest\",\n            interpolation_heatmaps=\"linear\",\n            interpolation_segmaps=\"cubic\")\n\n        samples, samples_heatmaps, samples_segmaps = aug._draw_samples(\n            1000, iarandom.RNG(1))\n\n        assert \"nearest\" in samples\n        assert len(set(samples)) == 1\n        assert \"linear\" in samples_heatmaps\n        assert len(set(samples_heatmaps)) == 1\n        assert \"cubic\" in samples_segmaps\n        assert len(set(samples_segmaps)) == 1\n\n    def test__draw_samples_each_one_interpolation_via_cv2_constants(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=cv2.INTER_LINEAR,\n            interpolation_heatmaps=cv2.INTER_NEAREST,\n            interpolation_segmaps=cv2.INTER_CUBIC)\n\n        samples, samples_heatmaps, samples_segmaps = aug._draw_samples(\n            1000, iarandom.RNG(1))\n\n        assert cv2.INTER_LINEAR in samples\n        assert len(set(samples)) == 1\n        assert cv2.INTER_NEAREST in samples_heatmaps\n        assert len(set(samples_heatmaps)) == 1\n        assert cv2.INTER_CUBIC in samples_segmaps\n        assert len(set(samples_segmaps)) == 1\n\n    def test__draw_samples_with_images_no_resize_and_others_same_as_imgs(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=iaa.KeepSizeByResize.NO_RESIZE,\n            interpolation_heatmaps=iaa.KeepSizeByResize.SAME_AS_IMAGES,\n            interpolation_segmaps=iaa.KeepSizeByResize.SAME_AS_IMAGES)\n\n        samples, samples_heatmaps, samples_segmaps = aug._draw_samples(\n            1000, iarandom.RNG(1))\n\n        assert iaa.KeepSizeByResize.NO_RESIZE in samples\n        assert len(set(samples)) == 1\n        assert iaa.KeepSizeByResize.NO_RESIZE in samples_heatmaps\n        assert len(set(samples_heatmaps)) == 1\n        assert iaa.KeepSizeByResize.NO_RESIZE in samples_segmaps\n        assert len(set(samples_segmaps)) == 1\n\n    def test__draw_samples_list_of_interpolations_incl_same_as_images(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=[\"cubic\", \"nearest\"],\n            interpolation_heatmaps=[\n                \"linear\", iaa.KeepSizeByResize.SAME_AS_IMAGES],\n            interpolation_segmaps=[\n                \"linear\", iaa.KeepSizeByResize.SAME_AS_IMAGES])\n\n        samples, samples_heatmaps, samples_segmaps = aug._draw_samples(\n            5000, iarandom.RNG(1))\n\n        assert \"cubic\" in samples\n        assert \"nearest\" in samples\n        assert len(set(samples)) == 2\n        assert \"linear\" in samples_heatmaps\n        assert \"nearest\" in samples_heatmaps\n        assert len(set(samples_heatmaps)) == 3\n        assert np.isclose(\n            np.sum(samples == samples_heatmaps) / samples_heatmaps.size,\n            0.5,\n            rtol=0, atol=0.1)\n        assert \"linear\" in samples_segmaps\n        assert \"nearest\" in samples_segmaps\n        assert len(set(samples_segmaps)) == 3\n        assert np.isclose(\n            np.sum(samples == samples_segmaps) / samples_segmaps.size,\n            0.5,\n            rtol=0, atol=0.1)\n\n    def test__draw_samples_list_of_each_two_interpolations(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=iap.Choice([\"cubic\", \"linear\"]),\n            interpolation_heatmaps=iap.Choice([\"linear\", \"nearest\"]),\n            interpolation_segmaps=iap.Choice([\"linear\", \"nearest\"]))\n\n        samples, samples_heatmaps, samples_segmaps = aug._draw_samples(\n            10000, iarandom.RNG(1))\n\n        assert \"cubic\" in samples\n        assert \"linear\" in samples\n        assert len(set(samples)) == 2\n        assert \"linear\" in samples_heatmaps\n        assert \"nearest\" in samples_heatmaps\n        assert len(set(samples_heatmaps)) == 2\n        assert \"linear\" in samples_segmaps\n        assert \"nearest\" in samples_segmaps\n        assert len(set(samples_segmaps)) == 2\n\n    def test_image_interpolation_is_cubic(self):\n        aug = iaa.KeepSizeByResize(self.children, interpolation=\"cubic\")\n        img = np.arange(0, 4*4*3, 1).reshape((4, 4, 3)).astype(np.uint8)\n\n        observed = aug.augment_image(img)\n\n        assert observed.shape == (4, 4, 3)\n        assert observed.dtype.type == np.uint8\n        expected = ia.imresize_single_image(\n            img[1:, :, :], img.shape[0:2], interpolation=\"cubic\")\n        assert np.allclose(observed, expected)\n\n    def test_image_interpolation_is_no_resize(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=iaa.KeepSizeByResize.NO_RESIZE)\n        img = np.arange(0, 4*4*3, 1).reshape((4, 4, 3)).astype(np.uint8)\n\n        observed = aug.augment_image(img)\n\n        expected = img[1:, :, :]\n        assert observed.shape == (3, 4, 3)\n        assert observed.dtype.type == np.uint8\n        assert np.allclose(observed, expected)\n\n    def test_images_input_is_single_array(self):\n        # input is single array, children turn in into list of arrays()\n        # => must be combined to a single output array\n        images = np.zeros((10, 100, 100), dtype=np.uint8)\n        aug = iaa.KeepSizeByResize(iaa.Crop((0, 40), keep_size=False))\n\n        images_aug = aug(images=images)\n\n        assert images.dtype.name == \"uint8\"\n        assert images.shape == (10, 100, 100)\n\n    def test_keypoints_interpolation_is_cubic(self):\n        aug = iaa.KeepSizeByResize(self.children, interpolation=\"cubic\")\n        kpsoi = self.kpsoi\n\n        kpoi_aug = aug.augment_keypoints([kpsoi])[0]\n        \n        assert kpoi_aug.shape == (4, 4, 3)\n        assert np.isclose(kpoi_aug.keypoints[0].x, 0, rtol=0, atol=1e-4)\n        assert np.isclose(kpoi_aug.keypoints[0].y,\n                          ((1-1)/3)*4,\n                          rtol=0, atol=1e-4)\n        assert np.isclose(kpoi_aug.keypoints[1].x, 1, rtol=0, atol=1e-4)\n        assert np.isclose(kpoi_aug.keypoints[1].y,\n                          ((1-1)/3)*4,\n                          rtol=0, atol=1e-4)\n        assert np.isclose(kpoi_aug.keypoints[2].x, 2, rtol=0, atol=1e-4)\n        assert np.isclose(kpoi_aug.keypoints[2].y,\n                          ((3-1)/3)*4,\n                          rtol=0, atol=1e-4)\n\n    def test_keypoints_interpolation_is_no_resize(self):\n        aug = iaa.KeepSizeByResize(\n            self.children, interpolation=iaa.KeepSizeByResize.NO_RESIZE)\n        kpsoi = self.kpsoi\n\n        kpoi_aug = aug.augment_keypoints([kpsoi])[0]\n\n        assert kpoi_aug.shape == (3, 4, 3)\n        assert np.isclose(kpoi_aug.keypoints[0].x, 0, rtol=0, atol=1e-4)\n        assert np.isclose(kpoi_aug.keypoints[0].y, 0, rtol=0, atol=1e-4)\n        assert np.isclose(kpoi_aug.keypoints[1].x, 1, rtol=0, atol=1e-4)\n        assert np.isclose(kpoi_aug.keypoints[1].y, 0, rtol=0, atol=1e-4)\n        assert np.isclose(kpoi_aug.keypoints[2].x, 2, rtol=0, atol=1e-4)\n        assert np.isclose(kpoi_aug.keypoints[2].y, 2, rtol=0, atol=1e-4)\n\n    def test_polygons_interpolation_is_cubic(self):\n        aug = iaa.KeepSizeByResize(self.children, interpolation=\"cubic\")\n        cbaoi = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (3, 0), (3, 3)])\n        ], shape=(4, 4, 3))\n\n        cbaoi_aug = aug.augment_polygons(cbaoi)\n\n        assert cbaoi_aug.shape == (4, 4, 3)\n        assert np.allclose(\n            cbaoi_aug.items[0].coords,\n            [(0, ((0-1)/3)*4),\n             (3, ((0-1)/3)*4),\n             (3, ((3-1)/3)*4)]\n        )\n\n    def test_polygons_interpolation_is_no_resize(self):\n        aug = iaa.KeepSizeByResize(\n            self.children, interpolation=iaa.KeepSizeByResize.NO_RESIZE)\n        cbaoi = ia.PolygonsOnImage([\n            ia.Polygon([(0, 0), (3, 0), (3, 3)])\n        ], shape=(4, 4, 3))\n\n        cbaoi_aug = aug.augment_polygons(cbaoi)\n\n        assert cbaoi_aug.shape == (3, 4, 3)\n        assert np.allclose(\n            cbaoi_aug.items[0].coords,\n            [(0, 0-1),\n             (3, 0-1),\n             (3, 3-1)]\n        )\n\n    def test_line_strings_interpolation_is_cubic(self):\n        aug = iaa.KeepSizeByResize(self.children, interpolation=\"cubic\")\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (3, 0), (3, 3)])\n        ], shape=(4, 4, 3))\n\n        cbaoi_aug = aug.augment_line_strings(cbaoi)\n\n        assert cbaoi_aug.shape == (4, 4, 3)\n        assert np.allclose(\n            cbaoi_aug.items[0].coords,\n            [(0, ((0-1)/3)*4),\n             (3, ((0-1)/3)*4),\n             (3, ((3-1)/3)*4)]\n        )\n\n    def test_line_strings_interpolation_is_no_resize(self):\n        aug = iaa.KeepSizeByResize(\n            self.children, interpolation=iaa.KeepSizeByResize.NO_RESIZE)\n        cbaoi = ia.LineStringsOnImage([\n            ia.LineString([(0, 0), (3, 0), (3, 3)])\n        ], shape=(4, 4, 3))\n\n        cbaoi_aug = aug.augment_line_strings(cbaoi)\n\n        assert cbaoi_aug.shape == (3, 4, 3)\n        assert np.allclose(\n            cbaoi_aug.items[0].coords,\n            [(0, 0-1),\n             (3, 0-1),\n             (3, 3-1)]\n        )\n\n    def test_bounding_boxes_interpolation_is_cubic(self):\n        aug = iaa.KeepSizeByResize(self.children, interpolation=\"cubic\")\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=3, y2=4)\n        ], shape=(4, 4, 3))\n\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n\n        assert bbsoi_aug.shape == (4, 4, 3)\n        assert np.allclose(\n            bbsoi_aug.bounding_boxes[0].coords,\n            [(0, ((1-1)/3)*4),\n             (3, ((4-1)/3)*4)]\n        )\n\n    def test_bounding_boxes_interpolation_is_no_resize(self):\n        aug = iaa.KeepSizeByResize(\n            self.children, interpolation=iaa.KeepSizeByResize.NO_RESIZE)\n        bbsoi = ia.BoundingBoxesOnImage([\n            ia.BoundingBox(x1=0, y1=1, x2=3, y2=4)\n        ], shape=(4, 4, 3))\n\n        bbsoi_aug = aug.augment_bounding_boxes(bbsoi)\n\n        assert bbsoi_aug.shape == (3, 4, 3)\n        assert np.allclose(\n            bbsoi_aug.bounding_boxes[0].coords,\n            [(0, 1-1),\n             (3, 4-1)]\n        )\n\n    def test_heatmaps_specific_interpolation_set_to_no_nearest(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=\"cubic\",\n            interpolation_heatmaps=\"nearest\")\n\n        heatmaps_oi = self.heatmaps\n        heatmaps_oi_nearest = self.heatmaps_nearest\n\n        heatmaps_oi_aug = aug.augment_heatmaps([heatmaps_oi])[0]\n\n        assert heatmaps_oi_aug.arr_0to1.shape == (4, 4, 1)\n        assert np.allclose(heatmaps_oi_aug.arr_0to1,\n                           heatmaps_oi_nearest.arr_0to1)\n\n    def test_heatmaps_specific_interpolation_set_to_list_of_two(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=\"cubic\",\n            interpolation_heatmaps=[\"nearest\", \"cubic\"])\n\n        heatmaps_oi = self.heatmaps\n        heatmaps_oi_cubic = self.heatmaps_cubic\n        heatmaps_oi_nearest = self.heatmaps_nearest\n\n        hmoi_aug = aug.augment_heatmaps([heatmaps_oi])[0]\n\n        assert hmoi_aug.arr_0to1.shape == (4, 4, 1)\n        assert (\n            np.allclose(hmoi_aug.arr_0to1, heatmaps_oi_nearest.arr_0to1)\n            or np.allclose(hmoi_aug.arr_0to1, heatmaps_oi_cubic.arr_0to1)\n        )\n\n    def test_heatmaps_specific_interpolation_set_to_no_resize(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=\"cubic\",\n            interpolation_heatmaps=iaa.KeepSizeByResize.NO_RESIZE)\n\n        heatmaps_oi = self.heatmaps\n\n        heatmaps_oi_aug = aug.augment_heatmaps([heatmaps_oi])[0]\n\n        assert heatmaps_oi_aug.arr_0to1.shape == (3, 4, 1)\n        assert np.allclose(\n            heatmaps_oi_aug.arr_0to1, heatmaps_oi.arr_0to1[1:, :, :])\n\n    def test_heatmaps_specific_interpolation_set_to_same_as_images(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=\"cubic\",\n            interpolation_heatmaps=iaa.KeepSizeByResize.SAME_AS_IMAGES)\n\n        heatmaps_oi = self.heatmaps\n        heatmaps_oi_cubic = self.heatmaps_cubic\n\n        heatmaps_oi_aug = aug.augment_heatmaps([heatmaps_oi])[0]\n\n        assert heatmaps_oi_aug.arr_0to1.shape == (4, 4, 1)\n        assert np.allclose(\n            heatmaps_oi_aug.arr_0to1, heatmaps_oi_cubic.arr_0to1)\n\n    def test_segmaps_general_interpolation_set_to_cubic(self):\n        aug = iaa.KeepSizeByResize(self.children, interpolation=\"cubic\")\n        segmaps_oi = self.segmaps\n        segmaps_oi_nearest = self.segmaps_nearest\n\n        segmaps_oi_aug = aug.augment_segmentation_maps([segmaps_oi])[0]\n\n        assert segmaps_oi_aug.arr.shape == (4, 4, 1)\n        assert np.array_equal(segmaps_oi_aug.arr, segmaps_oi_nearest.arr)\n\n    def test_segmaps_specific_interpolation_set_to_nearest(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=\"cubic\",\n            interpolation_segmaps=\"nearest\")\n        segmaps_oi = self.segmaps\n        segmaps_oi_nearest = self.segmaps_nearest\n\n        segmaps_oi_aug = aug.augment_segmentation_maps([segmaps_oi])[0]\n\n        assert segmaps_oi_aug.arr.shape == (4, 4, 1)\n        assert np.array_equal(segmaps_oi_aug.arr, segmaps_oi_nearest.arr)\n\n    def test_segmaps_specific_interpolation_set_to_no_resize(self):\n        aug = iaa.KeepSizeByResize(\n            self.children,\n            interpolation=\"cubic\",\n            interpolation_segmaps=iaa.KeepSizeByResize.NO_RESIZE)\n        segmaps_oi = self.segmaps\n\n        segmaps_oi_aug = aug.augment_segmentation_maps([segmaps_oi])[0]\n\n        assert segmaps_oi_aug.arr.shape == (3, 4, 1)\n        assert np.array_equal(segmaps_oi_aug.arr, segmaps_oi.arr[1:, :, :])\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1),\n            (0, 2),\n            (2, 0),\n            (0, 2, 0),\n            (2, 0, 0),\n            (0, 2, 1),\n            (2, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.KeepSizeByResize(\n                    iaa.CropToFixedSize(height=1, width=1)\n                )\n\n                image_aug = aug(image=image)\n\n                assert image_aug.shape == image.shape\n\n    def test_pickleable(self):\n        aug = iaa.KeepSizeByResize([\n            iaa.CropToFixedSize(10, 10, position=\"uniform\", seed=1)\n        ], interpolation=[\"nearest\", \"linear\"], seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=5, shape=(15, 15, 1))\n\n    def test_get_children_lists(self):\n        child = iaa.Identity()\n        aug = iaa.KeepSizeByResize([child])\n        children_lsts = aug.get_children_lists()\n        assert len(children_lsts) == 1\n        assert len(children_lsts[0]) == 1\n        assert children_lsts[0][0] is child\n\n    def test_to_deterministic(self):\n        child = iaa.Identity()\n        aug = iaa.KeepSizeByResize([child])\n\n        aug_det = aug.to_deterministic()\n\n        assert aug_det.deterministic\n        assert aug_det.random_state is not aug.random_state\n        assert aug_det.children[0].deterministic\n"
  },
  {
    "path": "test/augmenters/test_weather.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport cv2\n\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\nfrom imgaug import parameters as iap\nfrom imgaug.testutils import (reseed, runtest_pickleable_uint8_img,\n                              is_parameter_instance)\n\n\nclass _TwoValueParam(iap.StochasticParameter):\n    def __init__(self, v1, v2):\n        super(_TwoValueParam, self).__init__()\n        self.v1 = v1\n        self.v2 = v2\n\n    def _draw_samples(self, size, random_state):\n        arr = np.full(size, self.v1, dtype=np.float32)\n        arr[1::2] = self.v2\n        return arr\n\n\nclass TestFastSnowyLandscape(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        # check parameters\n        aug = iaa.FastSnowyLandscape(\n            lightness_threshold=[100, 200],\n            lightness_multiplier=[1.0, 4.0])\n        assert is_parameter_instance(aug.lightness_threshold, iap.Choice)\n        assert len(aug.lightness_threshold.a) == 2\n        assert aug.lightness_threshold.a[0] == 100\n        assert aug.lightness_threshold.a[1] == 200\n\n        assert is_parameter_instance(aug.lightness_multiplier, iap.Choice)\n        assert len(aug.lightness_multiplier.a) == 2\n        assert np.allclose(aug.lightness_multiplier.a[0], 1.0)\n        assert np.allclose(aug.lightness_multiplier.a[1], 4.0)\n\n    def test_basic_functionality(self):\n        # basic functionality test\n        aug = iaa.FastSnowyLandscape(\n            lightness_threshold=100,\n            lightness_multiplier=2.0)\n        image = np.arange(0, 6*6*3).reshape((6, 6, 3)).astype(np.uint8)\n        image_hls = cv2.cvtColor(image, cv2.COLOR_RGB2HLS)\n        mask = (image_hls[..., 1] < 100)\n        expected = np.copy(image_hls).astype(np.float32)\n        expected[..., 1][mask] *= 2.0\n        expected = np.clip(np.round(expected), 0, 255).astype(np.uint8)\n        expected = cv2.cvtColor(expected, cv2.COLOR_HLS2RGB)\n        observed = aug.augment_image(image)\n        assert np.array_equal(observed, expected)\n\n    def test_vary_lightness_threshold(self):\n        # test when varying lightness_threshold between images\n        image = np.arange(0, 6*6*3).reshape((6, 6, 3)).astype(np.uint8)\n        image_hls = cv2.cvtColor(image, cv2.COLOR_RGB2HLS)\n\n        aug = iaa.FastSnowyLandscape(\n            lightness_threshold=_TwoValueParam(75, 125),\n            lightness_multiplier=2.0)\n\n        mask = (image_hls[..., 1] < 75)\n        expected1 = np.copy(image_hls).astype(np.float64)\n        expected1[..., 1][mask] *= 2.0\n        expected1 = np.clip(np.round(expected1), 0, 255).astype(np.uint8)\n        expected1 = cv2.cvtColor(expected1, cv2.COLOR_HLS2RGB)\n\n        mask = (image_hls[..., 1] < 125)\n        expected2 = np.copy(image_hls).astype(np.float64)\n        expected2[..., 1][mask] *= 2.0\n        expected2 = np.clip(np.round(expected2), 0, 255).astype(np.uint8)\n        expected2 = cv2.cvtColor(expected2, cv2.COLOR_HLS2RGB)\n\n        observed = aug.augment_images([image] * 4)\n\n        assert np.array_equal(observed[0], expected1)\n        assert np.array_equal(observed[1], expected2)\n        assert np.array_equal(observed[2], expected1)\n        assert np.array_equal(observed[3], expected2)\n\n    def test_vary_lightness_multiplier(self):\n        # test when varying lightness_multiplier between images\n        image = np.arange(0, 6*6*3).reshape((6, 6, 3)).astype(np.uint8)\n        image_hls = cv2.cvtColor(image, cv2.COLOR_RGB2HLS)\n\n        aug = iaa.FastSnowyLandscape(\n            lightness_threshold=100,\n            lightness_multiplier=_TwoValueParam(1.5, 2.0))\n\n        mask = (image_hls[..., 1] < 100)\n        expected1 = np.copy(image_hls).astype(np.float64)\n        expected1[..., 1][mask] *= 1.5\n        expected1 = np.clip(np.round(expected1), 0, 255).astype(np.uint8)\n        expected1 = cv2.cvtColor(expected1, cv2.COLOR_HLS2RGB)\n\n        mask = (image_hls[..., 1] < 100)\n        expected2 = np.copy(image_hls).astype(np.float64)\n        expected2[..., 1][mask] *= 2.0\n        expected2 = np.clip(np.round(expected2), 0, 255).astype(np.uint8)\n        expected2 = cv2.cvtColor(expected2, cv2.COLOR_HLS2RGB)\n\n        observed = aug.augment_images([image] * 4)\n\n        assert np.array_equal(observed[0], expected1)\n        assert np.array_equal(observed[1], expected2)\n        assert np.array_equal(observed[2], expected1)\n        assert np.array_equal(observed[3], expected2)\n\n    def test_from_colorspace(self):\n        # test BGR colorspace\n        aug = iaa.FastSnowyLandscape(\n            lightness_threshold=100,\n            lightness_multiplier=2.0,\n            from_colorspace=\"BGR\")\n        image = np.arange(0, 6*6*3).reshape((6, 6, 3)).astype(np.uint8)\n        image_hls = cv2.cvtColor(image, cv2.COLOR_BGR2HLS)\n        mask = (image_hls[..., 1] < 100)\n        expected = np.copy(image_hls).astype(np.float32)\n        expected[..., 1][mask] *= 2.0\n        expected = np.clip(np.round(expected), 0, 255).astype(np.uint8)\n        expected = cv2.cvtColor(expected, cv2.COLOR_HLS2BGR)\n        observed = aug.augment_image(image)\n        assert np.array_equal(observed, expected)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0, 3),\n            (0, 1, 3),\n            (1, 0, 3)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.FastSnowyLandscape(100, 1.5,\n                                             from_colorspace=\"RGB\")\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.FastSnowyLandscape(lightness_threshold=(50, 150),\n                                     lightness_multiplier=(1.0, 3.0),\n                                     seed=1)\n        runtest_pickleable_uint8_img(aug)\n\n\n# only a very rough test here currently, because the augmenter is fairly hard\n# to test\n# TODO add more tests, improve testability\nclass TestClouds(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @classmethod\n    def _test_very_roughly(cls, nb_channels):\n        if nb_channels is None:\n            img = np.zeros((100, 100), dtype=np.uint8)\n        else:\n            img = np.zeros((100, 100, nb_channels), dtype=np.uint8)\n        imgs_aug = iaa.Clouds().augment_images([img] * 5)\n        assert 20 < np.average(imgs_aug) < 250\n        assert np.max(imgs_aug) > 150\n\n        for img_aug in imgs_aug:\n            img_aug_f32 = img_aug.astype(np.float32)\n            grad_x = img_aug_f32[:, 1:] - img_aug_f32[:, :-1]\n            grad_y = img_aug_f32[1:, :] - img_aug_f32[:-1, :]\n\n            assert np.sum(np.abs(grad_x)) > 5 * img.shape[1]\n            assert np.sum(np.abs(grad_y)) > 5 * img.shape[0]\n\n    def test_very_roughly_three_channels(self):\n        self._test_very_roughly(3)\n\n    def test_very_roughly_one_channel(self):\n        self._test_very_roughly(1)\n\n    def test_very_roughly_no_channel(self):\n        self._test_very_roughly(None)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Clouds()\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Clouds()\n\n                image_aug = aug(image=image)\n\n                assert np.any(image_aug > 0)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.Clouds(seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3, shape=(20, 20, 3))\n\n\n# only a very rough test here currently, because the augmenter is fairly hard\n# to test\n# TODO add more tests, improve testability\nclass TestFog(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @classmethod\n    def _test_very_roughly(cls, nb_channels):\n        if nb_channels is None:\n            img = np.zeros((100, 100), dtype=np.uint8)\n        else:\n            img = np.zeros((100, 100, nb_channels), dtype=np.uint8)\n        imgs_aug = iaa.Clouds().augment_images([img] * 5)\n        assert 50 < np.average(imgs_aug) < 255\n        assert np.max(imgs_aug) > 100\n\n        for img_aug in imgs_aug:\n            img_aug_f32 = img_aug.astype(np.float32)\n            grad_x = img_aug_f32[:, 1:] - img_aug_f32[:, :-1]\n            grad_y = img_aug_f32[1:, :] - img_aug_f32[:-1, :]\n\n            assert np.sum(np.abs(grad_x)) > 1 * img.shape[1]\n            assert np.sum(np.abs(grad_y)) > 1 * img.shape[0]\n\n    def test_very_roughly_three_channels(self):\n        self._test_very_roughly(3)\n\n    def test_very_roughly_one_channel(self):\n        self._test_very_roughly(1)\n\n    def test_very_roughly_no_channel(self):\n        self._test_very_roughly(None)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Fog()\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_unusual_channel_numbers(self):\n        shapes = [\n            (1, 1, 4),\n            (1, 1, 5),\n            (1, 1, 512),\n            (1, 1, 513)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Fog()\n\n                image_aug = aug(image=image)\n\n                assert np.any(image_aug > 0)\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.Fog(seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3, shape=(20, 20, 3))\n\n\n# only a very rough test here currently, because the augmenter is fairly hard\n# to test\n# TODO add more tests, improve testability\nclass TestSnowflakes(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def _test_very_roughly(self, nb_channels):\n        if nb_channels is None:\n            img = np.zeros((100, 100), dtype=np.uint8)\n        else:\n            img = np.zeros((100, 100, nb_channels), dtype=np.uint8)\n\n        imgs_aug = iaa.Snowflakes().augment_images([img] * 5)\n        assert 0.01 < np.average(imgs_aug) < 100\n        assert np.max(imgs_aug) > 100\n\n        for img_aug in imgs_aug:\n            img_aug_f32 = img_aug.astype(np.float32)\n            grad_x = img_aug_f32[:, 1:] - img_aug_f32[:, :-1]\n            grad_y = img_aug_f32[1:, :] - img_aug_f32[:-1, :]\n\n            assert np.sum(np.abs(grad_x)) > 5 * img.shape[1]\n            assert np.sum(np.abs(grad_y)) > 5 * img.shape[0]\n\n        # test density\n        imgs_aug_undense = iaa.Snowflakes(\n            density=0.001,\n            density_uniformity=0.99).augment_images([img] * 5)\n        imgs_aug_dense = iaa.Snowflakes(\n            density=0.1,\n            density_uniformity=0.99).augment_images([img] * 5)\n        assert (\n            np.average(imgs_aug_undense)\n            < np.average(imgs_aug_dense)\n        )\n\n        # test density_uniformity\n        imgs_aug_ununiform = iaa.Snowflakes(\n            density=0.4,\n            density_uniformity=0.1).augment_images([img] * 30)\n        imgs_aug_uniform = iaa.Snowflakes(\n            density=0.4,\n            density_uniformity=0.9).augment_images([img] * 30)\n\n        ununiform_uniformity = np.average([\n            self._measure_uniformity(img_aug)\n            for img_aug in imgs_aug_ununiform])\n        uniform_uniformity = np.average([\n            self._measure_uniformity(img_aug)\n            for img_aug in imgs_aug_uniform])\n\n        assert ununiform_uniformity < uniform_uniformity\n\n    def test_very_roughly_three_channels(self):\n        self._test_very_roughly(3)\n\n    def test_very_roughly_one_channel(self):\n        self._test_very_roughly(1)\n\n    def test_very_roughly_no_channels(self):\n        self._test_very_roughly(None)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0, 3),\n            (0, 1, 3),\n            (1, 0, 3)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Snowflakes()\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.Snowflakes(seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3, shape=(20, 20, 3))\n\n    @classmethod\n    def _measure_uniformity(cls, image, patch_size=5, n_patches=50):\n        pshalf = (patch_size-1) // 2\n        image_f32 = image.astype(np.float32)\n        grad_x = image_f32[:, 1:] - image_f32[:, :-1]\n        grad_y = image_f32[1:, :] - image_f32[:-1, :]\n        grad = np.abs(grad_x[1:, :] + grad_y[:, 1:])\n        points_y = np.random.randint(0, image.shape[0], size=(n_patches,))\n        points_x = np.random.randint(0, image.shape[0], size=(n_patches,))\n        stds = []\n        for y, x in zip(points_y, points_x):\n            bb = ia.BoundingBox(\n                x1=x-pshalf,\n                y1=y-pshalf,\n                x2=x+pshalf,\n                y2=y+pshalf)\n            patch = bb.extract_from_image(grad)\n            stds.append(np.std(patch))\n        return 1 / (1+np.std(stds))\n\n\nclass TestSnowflakesLayer(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_large_snowflakes_size(self):\n        # Test for PR #471\n        # Snowflakes size is achieved via downscaling. Large values for\n        # snowflakes_size lead to more downscaling. Hence, values close to 1.0\n        # incur risk that the image is downscaled to (0, 0) or similar values.\n        aug = iaa.SnowflakesLayer(\n                density=0.95,\n                density_uniformity=0.5,\n                flake_size=1.0,\n                flake_size_uniformity=0.5,\n                angle=0.0,\n                speed=0.5,\n                blur_sigma_fraction=0.001\n            )\n\n        nb_seen = 0\n        for _ in np.arange(50):\n            image = np.zeros((16, 16, 3), dtype=np.uint8)\n\n            image_aug = aug.augment_image(image)\n\n            assert np.std(image_aug) < 1\n            if np.average(image_aug) > 128:\n                nb_seen += 1\n        assert nb_seen > 30  # usually around 45\n\n\n# only a very rough test here currently, because the augmenter is fairly hard\n# to test\n# TODO add more tests, improve testability\nclass TestRain(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    @classmethod\n    def _test_very_roughly(cls, nb_channels):\n        if nb_channels is None:\n            img = np.zeros((100, 100), dtype=np.uint8)\n        else:\n            img = np.zeros((100, 100, nb_channels), dtype=np.uint8)\n\n        imgs_aug = iaa.Rain()(images=[img] * 5)\n        assert 5 < np.average(imgs_aug) < 200\n        assert np.max(imgs_aug) > 70\n\n        for img_aug in imgs_aug:\n            img_aug_f32 = img_aug.astype(np.float32)\n            grad_x = img_aug_f32[:, 1:] - img_aug_f32[:, :-1]\n            grad_y = img_aug_f32[1:, :] - img_aug_f32[:-1, :]\n\n            assert np.sum(np.abs(grad_x)) > 10 * img.shape[1]\n            assert np.sum(np.abs(grad_y)) > 10 * img.shape[0]\n\n    def test_very_roughly_three_channels(self):\n        self._test_very_roughly(3)\n\n    def test_very_roughly_one_channel(self):\n        self._test_very_roughly(1)\n\n    def test_very_roughly_no_channels(self):\n        self._test_very_roughly(None)\n\n    def test_zero_sized_axes(self):\n        shapes = [\n            (0, 0, 3),\n            (0, 1, 3),\n            (1, 0, 3)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                aug = iaa.Rain()\n\n                image_aug = aug(image=image)\n\n                assert image_aug.dtype.name == \"uint8\"\n                assert image_aug.shape == shape\n\n    def test_pickleable(self):\n        aug = iaa.Rain(seed=1)\n        runtest_pickleable_uint8_img(aug, iterations=3, shape=(20, 20, 3))\n"
  },
  {
    "path": "test/requirements.txt",
    "content": "# 4.6.0 currently causes crashes, see https://github.com/pytest-dev/pytest/issues/5358\npytest>=3.0.5,<4.6.0\n#\n# add subTest() support for pytest.\n# only available for py3.4+\npytest-subtests; python_version >= '3.4'\nmock; python_version < '3.3'  # unittest.mock does not exist in older versions\nunittest2; python_version < '3.4'  # in 3.4, self.subTest was added\nxdoctest >= 0.7.2\n\n# used in imgaug.augmenters.imgcorrupt\n# that library has scikit-image 15+ as a requirement, which does not exist\n# in <=3.4, so it is not tested here\nimagecorruptions; python_version >= '3.5'\n"
  },
  {
    "path": "test/run_doctests.sh",
    "content": "#!/bin/bash\n# Note: requires xdoctest 0.7.0\nxdoctest imgaug --global-exec=\"import imgaug as ia\\nfrom imgaug import augmenters as iaa\"\n"
  },
  {
    "path": "test/run_tests.sh",
    "content": "#!/bin/bash\n# This is expected to be executed from /imgaug, not from /imgaug/test.\n# That way it is ensured that you use the current code of the library in\n# the imgaug/imgaug/ subfolder, not the installed version of the library.\n#\n# The command below executes all tests.\n# To execute only one specific test, use e.g.\n# pytest ./test/augmenters/test_geometric.py::TestRot90::test_empty_polygons\npython -m pytest ./test --verbose --xdoctest-modules -s --durations=20 -Walways\n"
  },
  {
    "path": "test/test_data.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport warnings\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\n\nimport imgaug as ia\nimport imgaug.data as iadata\nfrom imgaug.data import _quokka_normalize_extract, _compute_resized_shape\n\n\nclass Test__quokka_normalize_extract(unittest.TestCase):\n    def test_string_square(self):\n        observed = _quokka_normalize_extract(\"square\")\n        assert isinstance(observed, ia.BoundingBox)\n        assert observed.x1 == 0\n        assert observed.y1 == 0\n        assert observed.x2 == 643\n        assert observed.y2 == 643\n\n    def test_tuple(self):\n        observed = _quokka_normalize_extract((1, 1, 644, 642))\n        assert isinstance(observed, ia.BoundingBox)\n        assert observed.x1 == 1\n        assert observed.y1 == 1\n        assert observed.x2 == 644\n        assert observed.y2 == 642\n\n    def test_boundingbox(self):\n        observed = _quokka_normalize_extract(ia.BoundingBox(x1=1, y1=1, x2=644, y2=642))\n        assert isinstance(observed, ia.BoundingBox)\n        assert observed.x1 == 1\n        assert observed.y1 == 1\n        assert observed.x2 == 644\n        assert observed.y2 == 642\n\n    def test_boundingboxesonimage(self):\n        observed = _quokka_normalize_extract(\n            ia.BoundingBoxesOnImage([\n                    ia.BoundingBox(x1=1, y1=1, x2=644, y2=642)\n                ],\n                shape=(643, 960, 3)\n            )\n        )\n        assert isinstance(observed, ia.BoundingBox)\n        assert observed.x1 == 1\n        assert observed.y1 == 1\n        assert observed.x2 == 644\n        assert observed.y2 == 642\n\n    def test_wrong_input_type(self):\n        got_exception = False\n        try:\n            _ = _quokka_normalize_extract(False)\n        except Exception as exc:\n            assert \"Expected 'square' or tuple\" in str(exc)\n            got_exception = True\n        assert got_exception\n\n\nclass Test__compute_resized_shape(unittest.TestCase):\n    def test_to_shape_is_tuple_of_ints_2d(self):\n        from_shape = (10, 15, 3)\n        to_shape = (20, 30)\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == (20, 30, 3)\n\n    def test_to_shape_is_tuple_of_ints_3d(self):\n        from_shape = (10, 15, 3)\n        to_shape = (20, 30, 3)\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == (20, 30, 3)\n\n    def test_to_shape_is_tuple_of_floats(self):\n        from_shape = (10, 15, 3)\n        to_shape = (2.0, 3.0)\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == (20, 45, 3)\n\n    def test_to_shape_is_float_and_int(self):\n        # tuple of int and float\n        from_shape = (10, 15, 3)\n        to_shape = (2.0, 25)\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == (20, 25, 3)\n\n    def test_to_shape_is_int_and_float(self):\n        from_shape = (10, 17, 3)\n        to_shape = (15, 2.0)\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == (15, 34, 3)\n\n    def test_to_shape_is_none(self):\n        from_shape = (10, 10, 3)\n        to_shape = None\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == from_shape\n\n    def test_to_shape_is_int_and_none(self):\n        from_shape = (10, 15, 3)\n        to_shape = (2.0, None)\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == (20, 15, 3)\n\n    def test_to_shape_is_none_and_int(self):\n        from_shape = (10, 15, 3)\n        to_shape = (None, 25)\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == (10, 25, 3)\n\n    def test_to_shape_is_single_int(self):\n        from_shape = (10, 15, 3)\n        to_shape = 20\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == (20, 20, 3)\n\n    def test_to_shape_is_float(self):\n        from_shape = (10, 15, 3)\n        to_shape = 2.0\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == (20, 30, 3)\n\n    def test_from_shape_and_to_shape_are_arrays(self):\n        # from/to shape as arrays\n        from_shape = (10, 10, 3)\n        to_shape = (20, 30, 3)\n        observed = _compute_resized_shape(\n            np.zeros(from_shape),\n            np.zeros(to_shape)\n        )\n        assert observed == to_shape\n\n    def test_from_shape_is_2d_and_to_shape_is_2d(self):\n        # from_shape is 2D\n        from_shape = (10, 15)\n        to_shape = (20, 30)\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == to_shape\n\n    def test_from_shape_is_2d_and_to_shape_is_3d(self):\n        from_shape = (10, 15)\n        to_shape = (20, 30, 3)\n        observed = _compute_resized_shape(from_shape, to_shape)\n        assert observed == (20, 30, 3)\n\n\n# we are intentionally a bit looser here with atol=0.1, because\n# apparently on some systems there are small differences in what\n# exactly is loaded, see issue #414\nclass Test_quokka(unittest.TestCase):\n    def test_no_parameters(self):\n        img = iadata.quokka()\n        assert img.shape == (643, 960, 3)\n        assert np.allclose(\n            np.average(img, axis=(0, 1)),\n            [107.93576659, 118.18765066, 122.99378564],\n            rtol=0, atol=0.1\n        )\n\n    def test_extract_square(self):\n        img = iadata.quokka(extract=\"square\")\n        assert img.shape == (643, 643, 3)\n        assert np.allclose(\n            np.average(img, axis=(0, 1)),\n            [111.25929196, 121.19431175, 125.71316898],\n            rtol=0, atol=0.1\n        )\n\n    def test_size_tuple_of_ints(self):\n        img = iadata.quokka(size=(642, 959))\n        assert img.shape == (642, 959, 3)\n        assert np.allclose(\n            np.average(img, axis=(0, 1)),\n            [107.84615822, 118.09832412, 122.90446467],\n            rtol=0, atol=0.1\n        )\n\n\n# we are intentionally a bit looser here with atol=0.1, because apparently\n# on some systems there are small differences in what exactly is loaded,\n# see issue #414\nclass Test_quokka_square(unittest.TestCase):\n    def test_standard_call(self):\n        img = iadata.quokka_square()\n        assert img.shape == (643, 643, 3)\n        assert np.allclose(\n            np.average(img, axis=(0, 1)),\n            [111.25929196, 121.19431175, 125.71316898],\n            rtol=0, atol=0.1\n        )\n\n# we are intentionally a bit looser here with atol=0.1, because apparently\n# on some systems there are small differences in what exactly is loaded,\n# see issue #414\nclass Test_quokka_heatmap(unittest.TestCase):\n    def test_no_parameters(self):\n        hm = iadata.quokka_heatmap()\n        assert hm.shape == (643, 960, 3)\n        assert hm.arr_0to1.shape == (643, 960, 1)\n        assert np.allclose(\n            np.average(hm.arr_0to1),\n            0.57618505,\n            rtol=0,\n            atol=1e-3\n        )\n\n    def test_extract_square(self):\n        hm = iadata.quokka_heatmap(extract=\"square\")\n        assert hm.shape == (643, 643, 3)\n        assert hm.arr_0to1.shape == (643, 643, 1)\n        # TODO this value is 0.48026073 in python 2.7, while 0.48026952 in\n        #      3.7 -- why?\n        assert np.allclose(\n            np.average(hm.arr_0to1),\n            0.48026952,\n            rtol=0,\n            atol=1e-3\n        )\n\n    def test_size_tuple_of_ints(self):\n        hm = iadata.quokka_heatmap(size=(642, 959))\n        assert hm.shape == (642, 959, 3)\n        assert hm.arr_0to1.shape == (642, 959, 1)\n        assert np.allclose(\n            np.average(hm.arr_0to1),\n            0.5762454,\n            rtol=0,\n            atol=1e-3\n        )\n\n\nclass Test_quokka_segmentation_map(unittest.TestCase):\n    def test_no_parameters(self):\n        segmap = iadata.quokka_segmentation_map()\n        assert segmap.shape == (643, 960, 3)\n        assert segmap.arr.shape == (643, 960, 1)\n        assert np.allclose(np.average(segmap.arr), 0.3016427, rtol=0, atol=1e-3)\n\n    def test_extract_square(self):\n        segmap = iadata.quokka_segmentation_map(extract=\"square\")\n        assert segmap.shape == (643, 643, 3)\n        assert segmap.arr.shape == (643, 643, 1)\n        assert np.allclose(np.average(segmap.arr), 0.450353, rtol=0, atol=1e-3)\n\n    def test_size_is_tuple_of_ints(self):\n        segmap = iadata.quokka_segmentation_map(size=(642, 959))\n        assert segmap.shape == (642, 959, 3)\n        assert segmap.arr.shape == (642, 959, 1)\n        assert np.allclose(np.average(segmap.arr), 0.30160266, rtol=0, atol=1e-3)\n\n\nclass Test_quokka_keypoints(unittest.TestCase):\n    def test_non_parameters(self):\n        kpsoi = iadata.quokka_keypoints()\n        assert len(kpsoi.keypoints) > 0\n        assert np.allclose(kpsoi.keypoints[0].x, 163.0)\n        assert np.allclose(kpsoi.keypoints[0].y, 78.0)\n        assert kpsoi.shape == (643, 960, 3)\n\n    def test_non_square_vs_square(self):\n        kpsoi = iadata.quokka_keypoints()\n        img = iadata.quokka()\n\n        patches = []\n        for kp in kpsoi.keypoints:\n            bb = ia.BoundingBox(x1=kp.x-1, x2=kp.x+2, y1=kp.y-1, y2=kp.y+2)\n            patches.append(bb.extract_from_image(img))\n\n        img_square = iadata.quokka(extract=\"square\")\n        kpsoi_square = iadata.quokka_keypoints(extract=\"square\")\n\n        assert len(kpsoi.keypoints) == len(kpsoi_square.keypoints)\n        assert kpsoi_square.shape == (643, 643, 3)\n        for kp, patch in zip(kpsoi_square.keypoints, patches):\n            bb = ia.BoundingBox(x1=kp.x-1, x2=kp.x+2, y1=kp.y-1, y2=kp.y+2)\n            patch_square = bb.extract_from_image(img_square)\n            assert np.average(\n                np.abs(\n                    patch.astype(np.float32)\n                    - patch_square.astype(np.float32)\n                )\n            ) < 1.0\n\n    def test_size_is_tuple_of_ints(self):\n        kpsoi = iadata.quokka_keypoints()\n        kpsoi_resized = iadata.quokka_keypoints(size=(642, 959))\n        assert kpsoi_resized.shape == (642, 959, 3)\n        assert len(kpsoi.keypoints) == len(kpsoi_resized.keypoints)\n        for kp, kp_resized in zip(kpsoi.keypoints, kpsoi_resized.keypoints):\n            d = np.sqrt(\n                (kp.x - kp_resized.x) ** 2\n                + (kp.y - kp_resized.y) ** 2\n            )\n            assert d < 1.0\n\n\nclass Test_quokka_bounding_boxes(unittest.TestCase):\n    def test_no_parameters(self):\n        bbsoi = iadata.quokka_bounding_boxes()\n        assert len(bbsoi.bounding_boxes) > 0\n        bb0 = bbsoi.bounding_boxes[0]\n        assert np.allclose(bb0.x1, 148.0)\n        assert np.allclose(bb0.y1, 50.0)\n        assert np.allclose(bb0.x2, 550.0)\n        assert np.allclose(bb0.y2, 642.0)\n        assert bbsoi.shape == (643, 960, 3)\n\n    def test_non_square_vs_square(self):\n        bbsoi = iadata.quokka_bounding_boxes()\n        img = iadata.quokka()\n        patches = []\n        for bb in bbsoi.bounding_boxes:\n            patches.append(bb.extract_from_image(img))\n\n        img_square = iadata.quokka(extract=\"square\")\n        bbsoi_square = iadata.quokka_bounding_boxes(extract=\"square\")\n        assert len(bbsoi.bounding_boxes) == len(bbsoi_square.bounding_boxes)\n        assert bbsoi_square.shape == (643, 643, 3)\n\n        for bb, patch in zip(bbsoi_square.bounding_boxes, patches):\n            patch_square = bb.extract_from_image(img_square)\n            assert np.average(\n                np.abs(\n                    patch.astype(np.float32)\n                    - patch_square.astype(np.float32)\n                )\n            ) < 1.0\n\n    def test_size_is_tuple_of_ints(self):\n        bbsoi = iadata.quokka_bounding_boxes()\n        bbsoi_resized = iadata.quokka_bounding_boxes(size=(642, 959))\n        assert bbsoi_resized.shape == (642, 959, 3)\n        assert len(bbsoi.bounding_boxes) == len(bbsoi_resized.bounding_boxes)\n        for bb, bb_resized in zip(bbsoi.bounding_boxes,\n                                  bbsoi_resized.bounding_boxes):\n            d = np.sqrt(\n                (bb.center_x - bb_resized.center_x) ** 2\n                + (bb.center_y - bb_resized.center_y) ** 2\n            )\n            assert d < 1.0\n"
  },
  {
    "path": "test/test_dtypes.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport warnings\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\n\nfrom imgaug import dtypes as iadt\nfrom imgaug.testutils import ensure_deprecation_warning\n\n\nclass Test_normalize_dtypes(unittest.TestCase):\n    @mock.patch(\"imgaug.dtypes.normalize_dtype\")\n    def test_single_non_list(self, mock_nd):\n        mock_nd.return_value = \"foo\"\n        dtypes = iadt.normalize_dtypes(\"int16\")\n        assert dtypes == [\"foo\"]\n        assert mock_nd.call_count == 1\n        assert mock_nd.call_args_list[0][0][0] == \"int16\"\n\n    def test_single_dtype(self):\n        dtypes = iadt.normalize_dtypes(np.dtype(\"int16\"))\n        assert isinstance(dtypes, list)\n        assert len(dtypes) == 1\n        assert isinstance(dtypes[0], np.dtype)\n        assert dtypes[0].name == \"int16\"\n\n    def test_empty_list(self):\n        dtypes = iadt.normalize_dtypes([])\n        assert isinstance(dtypes, list)\n        assert len(dtypes) == 0\n\n    @mock.patch(\"imgaug.dtypes.normalize_dtype\")\n    def test_list_of_dtype_names(self, mock_nd):\n        mock_nd.return_value = \"foo\"\n        dtypes = iadt.normalize_dtypes([\"int16\", \"int32\"])\n        assert dtypes == [\"foo\", \"foo\"]\n        assert mock_nd.call_count == 2\n        assert mock_nd.call_args_list[0][0][0] == \"int16\"\n        assert mock_nd.call_args_list[1][0][0] == \"int32\"\n\n\nclass Test_normalize_dtype(unittest.TestCase):\n    def test_dtype(self):\n        dtype = iadt.normalize_dtype(np.dtype(\"int16\"))\n        assert isinstance(dtype, np.dtype)\n        assert dtype.name == \"int16\"\n\n    def test_dtype_name(self):\n        dtype = iadt.normalize_dtype(\"int16\")\n        assert isinstance(dtype, np.dtype)\n        assert dtype.name == \"int16\"\n\n    def test_dtype_name_short(self):\n        dtype = iadt.normalize_dtype(\"i2\")\n        assert isinstance(dtype, np.dtype)\n        assert dtype.name == \"int16\"\n\n    def test_dtype_function(self):\n        dtype = iadt.normalize_dtype(np.int16)\n        assert isinstance(dtype, np.dtype)\n        assert dtype.name == \"int16\"\n\n    def test_ndarray(self):\n        arr = np.zeros((1,), dtype=np.int16)\n        dtype = iadt.normalize_dtype(arr)\n        assert isinstance(dtype, np.dtype)\n        assert dtype.name == \"int16\"\n\n    def test_numpy_scalar(self):\n        scalar = np.int16(0)\n        dtype = iadt.normalize_dtype(scalar)\n        assert isinstance(dtype, np.dtype)\n        assert dtype.name == \"int16\"\n\n\n# change_dtype_() is already indirectly tested via Test_change_dtypes_(),\n# so were don't have to be very thorough here\nclass Test_change_dtype_(unittest.TestCase):\n    def test_no_clip_no_round(self):\n        arr = np.array([[0.0, 0.1, 0.9, 127.0+1.0, -128.0-1.0]],\n                       dtype=np.float32)\n        dtype = np.int8\n\n        observed = iadt.change_dtype_(np.copy(arr), dtype,\n                                      clip=False, round=False)\n\n        expected = np.array([[0, 0, 0, -128+1-1, 127-1+1]], dtype=np.int8)\n        assert observed.dtype.name == \"int8\"\n        assert np.array_equal(observed, expected)\n\n    def test_clip_and_round(self):\n        arr = np.array([[0.0, 0.1, 0.9, 127.0+1.0, -128.0-1.0]],\n                       dtype=np.float32)\n        dtype = np.int8\n\n        observed = iadt.restore_dtypes_(np.copy(arr), dtype)\n\n        expected = np.array([[0, 0, 1, 127, -128]], dtype=np.int8)\n        assert observed.dtype.name == \"int8\"\n        assert np.array_equal(observed, expected)\n\n    def test_dtype_not_changed(self):\n        arr = np.array([[-128, -1, 0, 1, 127]], dtype=np.int8)\n        dtype = np.int8\n\n        observed = iadt.restore_dtypes_(arr, dtype,\n                                        clip=False, round=False)\n\n        assert observed is arr\n\n    @mock.patch('numpy.round')\n    def test_no_round_if_dtype_not_changed(self, mock_round):\n        arr = np.array([[-128, -1, 0, 1, 127]], dtype=np.int8)\n        dtype = np.int8\n\n        observed = iadt.restore_dtypes_(arr, dtype, clip=False)\n\n        assert observed is arr\n        assert mock_round.call_count == 0\n\n    def test_round_float_dtypes(self):\n        arr = np.array([[-128, -1.1, 0.7, 1.1, 127]], dtype=np.float32)\n        dtype = np.int8\n\n        observed = iadt.restore_dtypes_(np.copy(arr), dtype, clip=False)\n\n        expected = np.array([[-128, -1, 1, 1, 127]], dtype=np.int8)\n        assert observed.dtype.name == \"int8\"\n        assert np.array_equal(observed, expected)\n\n    @mock.patch('numpy.round')\n    def test_dont_round_non_float_dtypes(self, mock_round):\n        arr = np.array([[-128, -1, 0, 1, 127]], dtype=np.int8)\n        dtype = np.float32\n\n        _ = iadt.restore_dtypes_(np.copy(arr), dtype, clip=False)\n\n        assert mock_round.call_count == 0\n\n    def test_int16_to_int8(self):\n        arr = np.zeros((1,), dtype=np.int16) + 1\n        observed = iadt.change_dtype_(arr, np.int8, clip=False, round=False)\n        assert observed.shape == (1,)\n        assert observed.dtype.name == \"int8\"\n        assert np.all(observed == 1)\n\n    def test_int16_to_int8_with_overflow(self):\n        arr = np.zeros((1,), dtype=np.int16) + 128\n        observed = iadt.change_dtype_(arr, np.int8, clip=False, round=False)\n        assert observed.shape == (1,)\n        assert observed.dtype.name == \"int8\"\n        assert np.all(observed == -128)\n\n    def test_float32_to_int8(self):\n        arr = np.zeros((1,), dtype=np.int32) + 1\n        observed = iadt.change_dtype_(arr, np.int8, clip=False, round=False)\n        assert observed.shape == (1,)\n        assert observed.dtype.name == \"int8\"\n        assert np.all(observed == 1)\n\n    def test_float32_to_int8_with_overflow(self):\n        arr = np.zeros((1,), dtype=np.int32) + 1\n        observed = iadt.change_dtype_(arr, np.int8, clip=False, round=False)\n        assert observed.shape == (1,)\n        assert observed.dtype.name == \"int8\"\n        assert np.all(observed == 1)\n\n    def test_dtype_given_as_string(self):\n        arr = np.zeros((1,), dtype=np.int8) + 1\n        observed = iadt.change_dtype_(arr, \"int16\", clip=False, round=False)\n        assert observed.shape == (1,)\n        assert observed.dtype.name == \"int16\"\n        assert np.all(observed == 1)\n\n\nclass Test_change_dtypes_(unittest.TestCase):\n    def test_array_input_single_dtype_no_clip_no_round(self):\n        arr = np.array([[0.0, 0.1, 0.9, 127.0+1.0, -128.0-1.0]],\n                       dtype=np.float32)\n        dtype = np.int8\n\n        observed = iadt.restore_dtypes_(np.copy(arr), dtype,\n                                        clip=False, round=False)\n\n        expected = np.array([[0, 0, 0, -128+1-1, 127-1+1]], dtype=np.int8)\n        assert observed.dtype.name == \"int8\"\n        assert np.array_equal(observed, expected)\n\n    def test_array_input_single_dtype_with_clip_no_round(self):\n        arr = np.array([[0.0, 0.1, 0.9, 127.0+1.0, -128.0-1.0]],\n                       dtype=np.float32)\n        dtype = np.int8\n\n        observed = iadt.restore_dtypes_(np.copy(arr), dtype,\n                                        clip=True, round=False)\n\n        expected = np.array([[0, 0, 0, 127, -128]], dtype=np.int8)\n        assert observed.dtype.name == \"int8\"\n        assert np.array_equal(observed, expected)\n\n    def test_array_input_single_dtype_no_clip_with_round(self):\n        arr = np.array([[0.0, 0.1, 0.9, 127.0+1.0, -128.0-1.0]],\n                       dtype=np.float32)\n        dtype = np.int8\n\n        observed = iadt.restore_dtypes_(np.copy(arr), dtype,\n                                        clip=False, round=True)\n\n        expected = np.array([[0, 0, 1, -128+1-1, 127-1+1]], dtype=np.int8)\n        assert observed.dtype.name == \"int8\"\n        assert np.array_equal(observed, expected)\n\n    def test_array_input_fail_if_many_different_dtypes(self):\n        arr = np.array([\n            [0.0, 0.1, 0.9, 127.0+1.0, -128.0-1.0],\n            [0.0, 0.1, 0.9, 127.0+1.0, -128.0-1.0],\n        ], dtype=np.float32)\n        dtypes = [np.int8, np.int16]\n\n        with self.assertRaises(AssertionError) as context:\n            _observed = iadt.restore_dtypes_(np.copy(arr), dtypes,\n                                             clip=False, round=False)\n\n        assert (\n            \"or an iterable of N times the *same* dtype\"\n            in str(context.exception)\n        )\n\n    def test_array_input_many_dtypes_no_clip_no_round(self):\n        arr = np.array([\n            [0.0, 0.1, 0.9, 127.0+0.0, -128.0-0.0],\n            [0.0, 0.1, 0.9, 127.0+1.0, -128.0-1.0],\n        ], dtype=np.float32)\n        dtypes = [np.int8, np.int8]\n\n        observed = iadt.restore_dtypes_(np.copy(arr), dtypes,\n                                        clip=False, round=False)\n\n        expected = np.array([\n            [0, 0, 0, 127, -128],\n            [0, 0, 0, -128+1-1, 127-1+1]\n        ], dtype=np.int8)\n        assert observed.dtype.name == \"int8\"\n        assert np.array_equal(observed, expected)\n\n    def test_empty_array_input(self):\n        arr = np.zeros((0, 5), dtype=np.float32)\n        dtypes = np.int8\n\n        observed = iadt.restore_dtypes_(np.copy(arr), dtypes,\n                                        clip=False, round=False)\n\n        assert observed.dtype.name == \"int8\"\n        assert observed.shape == (0, 5)\n\n    def test_empty_list_input(self):\n        arrs = []\n        dtypes = np.int8\n\n        observed = iadt.restore_dtypes_(arrs, dtypes,\n                                        clip=False, round=False)\n\n        assert len(observed) == 0\n\n    def test_many_items_list_input_single_dtype(self):\n        arrs = [\n            np.array([0.0, 0.1, 0.9, 127.0+0.0, -128.0-0.0], dtype=np.float32),\n            np.array([0.0, 0.1, 0.9, 127.0+1.0, -128.0-1.0], dtype=np.float32)\n        ]\n        dtypes = np.int8\n\n        observed = iadt.restore_dtypes_(\n            [np.copy(arr) for arr in arrs],\n            dtypes,\n            clip=False,\n            round=False)\n\n        expected = [\n            np.array([0, 0, 0, 127, -128], dtype=np.int8),\n            np.array([0, 0, 0, -128+1-1, 127-1+1], dtype=np.int8)\n        ]\n        assert len(observed) == 2\n        assert observed[0].dtype.name == \"int8\"\n        assert observed[1].dtype.name == \"int8\"\n        assert np.array_equal(observed[0], expected[0])\n        assert np.array_equal(observed[1], expected[1])\n\n    def test_many_items_list_input_many_dtypes(self):\n        arrs = [\n            np.array([0.0, 0.1, 0.9, 127.0+1.0, -128.0-1.0], dtype=np.float32),\n            np.array([0.0, 0.1, 0.9, 127.0+1.0, -128.0-1.0], dtype=np.float32)\n        ]\n        dtypes = [np.int8, np.int16]\n\n        observed = iadt.restore_dtypes_(\n            [np.copy(arr) for arr in arrs],\n            dtypes,\n            clip=False,\n            round=False)\n\n        expected = [\n            np.array([0, 0, 0, -128+1-1, 127-1+1], dtype=np.int8),\n            np.array([0, 0, 0, 127+1, -128-1], dtype=np.int16)\n        ]\n        assert len(observed) == 2\n        assert observed[0].dtype.name == \"int8\"\n        assert observed[1].dtype.name == \"int16\"\n        assert np.array_equal(observed[0], expected[0])\n        assert np.array_equal(observed[1], expected[1])\n\n    def test_invalid_input(self):\n        arr = False\n\n        with self.assertRaises(Exception) as context:\n            _ = iadt.restore_dtypes_(arr, np.int8)\n\n        assert \"Expected numpy array or \" in str(context.exception)\n\n    def test_int_to_float(self):\n        arr = np.array([[-100, -1, 0, 1, 100]], dtype=np.int8)\n        dtype = np.float32\n\n        observed = iadt.restore_dtypes_(np.copy(arr), dtype,\n                                        clip=False, round=False)\n\n        expected = np.array([[-100.0, -1.0, 0.0, 1.0, 100.0]],\n                            dtype=np.float32)\n        assert observed.dtype.name == \"float32\"\n        assert np.allclose(observed, expected)\n\n    def test_increase_float_resolution(self):\n        arr = np.array([[-100.0, -1.0, 0.0, 1.0, 100.0]], dtype=np.float32)\n        dtype = np.float64\n\n        observed = iadt.restore_dtypes_(np.copy(arr), dtype,\n                                        clip=False, round=False)\n\n        expected = np.array([[-100.0, -1.0, 0.0, 1.0, 100.0]],\n                            dtype=np.float32)\n        assert observed.dtype.name == \"float64\"\n        assert np.allclose(observed, expected)\n\n    def test_int_to_uint(self):\n        arr = np.array([[-100, -1, 0, 1, 100]], dtype=np.int8)\n        dtype = np.uint8\n\n        observed = iadt.restore_dtypes_(np.copy(arr), dtype,\n                                        clip=False, round=False)\n\n        expected = np.array([[255-100+1, 255-1+1, 0, 1, 100]],\n                            dtype=np.uint8)\n        assert observed.dtype.name == \"uint8\"\n        assert np.allclose(observed, expected)\n\n    def test_int_to_uint_with_clip(self):\n        arr = np.array([[-100, -1, 0, 1, 100]], dtype=np.int8)\n        dtype = np.uint8\n\n        observed = iadt.restore_dtypes_(np.copy(arr), dtype,\n                                        clip=True, round=False)\n\n        expected = np.array([[0, 0, 0, 1, 100]], dtype=np.uint8)\n        assert observed.dtype.name == \"uint8\"\n        assert np.allclose(observed, expected)\n\n\n# TODO is the copy_* function still used anywhere\nclass Test_copy_dtypes_for_restore(unittest.TestCase):\n    def test_images_as_list(self):\n        # TODO using dtype=np.bool is causing this to fail as it ends up\n        #      being <type bool> instead of <type 'numpy.bool_'>.\n        #      Any problems from that for the library?\n        images = [\n            np.zeros((1, 1, 3), dtype=np.uint8),\n            np.zeros((10, 16, 3), dtype=np.float32),\n            np.zeros((20, 10, 6), dtype=np.int32)\n        ]\n\n        dtypes_copy = iadt.copy_dtypes_for_restore(images, force_list=False)\n        assert np.all([\n            dtype_observed.name == dtype_expected\n            for dtype_observed, dtype_expected\n            in zip(\n                dtypes_copy,\n                [\"uint8\", \"float32\", \"int32\"]\n            )\n        ])\n\n    def test_images_as_single_array(self):\n        dts = [\"uint8\", \"float32\", \"int32\"]\n        for dt in dts:\n            with self.subTest(dtype=dt):\n                images = np.zeros((10, 16, 32, 3), dtype=dt)\n                dtypes_copy = iadt.copy_dtypes_for_restore(images)\n                assert isinstance(dtypes_copy, np.dtype)\n                assert dtypes_copy.name == dt\n\n    def test_images_as_single_array_force_list(self):\n        dts = [\"uint8\", \"float32\", \"int32\"]\n        for dt in dts:\n            with self.subTest(dtype=dt):\n                images = np.zeros((10, 16, 32, 3), dtype=dt)\n                dtypes_copy = iadt.copy_dtypes_for_restore(images,\n                                                           force_list=True)\n                assert isinstance(dtypes_copy, list)\n                assert np.all([dtype_i.name == dt for dtype_i in dtypes_copy])\n\n\nclass Test_increase_itemsize_of_dtype(unittest.TestCase):\n    def test_factor_is_1(self):\n        dts = [\n            np.int8, np.int16, np.int32, np.int64,\n            np.uint8, np.uint16, np.uint32, np.uint64,\n            np.float16, np.float32, np.float64\n        ]\n        for dt in dts:\n            dt = np.dtype(dt)\n            with self.subTest(dtype=dt.name):\n                dt_increased = iadt.increase_itemsize_of_dtype(dt, 1)\n                assert dt_increased.name == dt.name\n\n    def test_factor_is_2(self):\n        dts = [\n            np.int8, np.int16, np.int32,\n            np.uint8, np.uint16, np.uint32,\n            np.float16, np.float32\n        ]\n        expecteds = [\n            np.int16, np.int32, np.int64,\n            np.uint16, np.uint32, np.uint64,\n            np.float32, np.float64\n        ]\n        for dt, expected in zip(dts, expecteds):\n            dt = np.dtype(dt)\n            expected = np.dtype(expected)\n            with self.subTest(dtype=dt.name):\n                dt_increased = iadt.increase_itemsize_of_dtype(dt, 2)\n                assert dt_increased.name == expected.name\n\n    def test_dtype_as_string(self):\n        dt_names = [\n            \"int8\", \"int16\", \"int32\",\n            \"uint8\", \"uint16\", \"uint32\",\n            \"float16\", \"float32\"\n        ]\n        expecteds = [\n            np.int16, np.int32, np.int64,\n            np.uint16, np.uint32, np.uint64,\n            np.float32, np.float64\n        ]\n        for dt_name, expected in zip(dt_names, expecteds):\n            expected = np.dtype(expected)\n            with self.subTest(dtype=dt_name):\n                dt_increased = iadt.increase_itemsize_of_dtype(dt_name, 2)\n                assert dt_increased.name == expected.name\n\n    def test_unknown_dtype(self):\n        with self.assertRaises(TypeError) as context:\n            _ = iadt.increase_itemsize_of_dtype(np.uint64, 2)\n\n        assert (\n            \"Unable to create a numpy dtype matching\"\n            in str(context.exception))\n\n\nclass Test_get_minimal_dtype(unittest.TestCase):\n    def test_with_dtype_function(self):\n        dt_funcs = [\n            np.int8, np.int16, np.int32, np.int64,\n            np.uint8, np.uint16, np.uint32, np.uint64,\n            np.float16, np.float32, np.float64,\n            np.bool_\n        ]\n\n        for dt_func in dt_funcs:\n            with self.subTest(dtype=np.dtype(dt_func).name):\n                inputs = [dt_func]\n                promoted_dt = iadt.get_minimal_dtype(inputs)\n                assert promoted_dt.name == np.dtype(dt_func).name\n\n    def test_with_lists_of_identical_dtypes(self):\n        dts = [\n            np.int8, np.int16, np.int32, np.int64,\n            np.uint8, np.uint16, np.uint32, np.uint64,\n            np.float16, np.float32, np.float64,\n            np.bool_\n        ]\n\n        for dt in dts:\n            dt = np.dtype(dt)\n            for length in [1, 2, 3]:\n                with self.subTest(dtype=dt.name, length=length):\n                    inputs = [dt for _ in range(length)]\n                    promoted_dt = iadt.get_minimal_dtype(inputs)\n                    assert promoted_dt.name == dt.name\n\n    def test_with_lists_of_identical_dtype_arrays(self):\n        dts = [\n            np.int8, np.int16, np.int32, np.int64,\n            np.uint8, np.uint16, np.uint32, np.uint64,\n            np.float16, np.float32, np.float64,\n            np.bool_\n        ]\n\n        for dt in dts:\n            dt = np.dtype(dt)\n            for length in [1, 2, 3]:\n                with self.subTest(dtype=dt.name, length=length):\n                    inputs = [np.zeros((1, 1, 3), dtype=dt)\n                              for _ in range(length)]\n                    promoted_dt = iadt.get_minimal_dtype(inputs)\n                    assert promoted_dt.name == dt.name\n\n    def test_with_lists_of_different_arrays(self):\n        dt_lists = [\n            [np.uint8, np.uint16],\n            [np.uint8, np.uint32],\n            [np.uint8, np.int8],\n            [np.uint8, np.bool_],\n            [np.int8, np.int16],\n            [np.float16, np.float32],\n            [np.uint8, np.float32],\n            [np.uint8, np.int8, np.int16],\n            [np.uint8, np.int8, np.bool_],\n            [np.uint8, np.int8, np.float32],\n        ]\n        expecteds = [\n            np.uint16,\n            np.uint32,\n            np.int16,\n            np.uint8,\n            np.int16,\n            np.float32,\n            np.float32,\n            np.int16,\n            np.int16,\n            np.float32\n        ]\n        for dt_list, expected in zip(dt_lists, expecteds):\n            expected = np.dtype(expected)\n            dt_list = [np.dtype(dt) for dt in dt_list]\n            dt_names = \", \".join([dt.name for dt in dt_list])\n            with self.subTest(dtypes=dt_names):\n                promoted_dt = iadt.get_minimal_dtype(dt_list)\n                assert promoted_dt.name == expected.name\n\n    @mock.patch(\"imgaug.dtypes.increase_itemsize_of_dtype\")\n    def test_calls_increase_itemsize_factor(self, mock_iibf):\n        dt = np.int8\n        factor = 2\n\n        _ = iadt.get_minimal_dtype([dt], factor)\n\n        assert mock_iibf.call_count == 1\n\n\nclass Test_promote_array_dtypes_(unittest.TestCase):\n    @mock.patch(\"imgaug.dtypes.get_minimal_dtype\")\n    @mock.patch(\"imgaug.dtypes.change_dtypes_\")\n    def test_calls_subfunctions(self, mock_cd, mock_gmd):\n        mock_gmd.return_value = np.dtype(\"int16\")\n        mock_cd.return_value = \"foo\"\n        arrays = [np.zeros((1,), dtype=np.int8)]\n\n        observed = iadt.promote_array_dtypes_(arrays)\n\n        assert mock_gmd.call_count == 1\n        assert mock_cd.call_count == 1\n        # call 0, args, arg 0, dtype 0\n        assert mock_gmd.call_args_list[0][0][0][0].name == \"int8\"\n        assert mock_gmd.call_args_list[0][1][\"increase_itemsize_factor\"] == 1\n        assert mock_cd.call_args_list[0][0][0] is arrays\n        assert observed == \"foo\"\n\n    @mock.patch(\"imgaug.dtypes.get_minimal_dtype\")\n    @mock.patch(\"imgaug.dtypes.change_dtypes_\")\n    def test_calls_subfunctions_dtypes_set(self, mock_cd, mock_gmd):\n        mock_gmd.return_value = np.dtype(\"int16\")\n        mock_cd.return_value = \"foo\"\n        arrays = [np.zeros((1,), dtype=np.int8)]\n\n        observed = iadt.promote_array_dtypes_(\n            arrays,\n            dtypes=[\"float32\"])\n\n        assert mock_gmd.call_count == 1\n        assert mock_cd.call_count == 1\n        # call 0, args, arg 0, dtype 0\n        assert mock_gmd.call_args_list[0][0][0][0] == \"float32\"\n        assert mock_gmd.call_args_list[0][1][\"increase_itemsize_factor\"] == 1\n        assert mock_cd.call_args_list[0][0][0] is arrays\n        assert observed == \"foo\"\n\n    @mock.patch(\"imgaug.dtypes.get_minimal_dtype\")\n    @mock.patch(\"imgaug.dtypes.change_dtypes_\")\n    def test_calls_subfunctions_increase_itemsize_factor_set(self, mock_cd,\n                                                             mock_gmd):\n        mock_gmd.return_value = np.dtype(\"int16\")\n        mock_cd.return_value = \"foo\"\n        arrays = [np.zeros((1,), dtype=np.int8)]\n\n        observed = iadt.promote_array_dtypes_(\n            arrays,\n            increase_itemsize_factor=2)\n\n        assert mock_gmd.call_count == 1\n        assert mock_cd.call_count == 1\n        # call 0, args, arg 0, dtype 0\n        assert mock_gmd.call_args_list[0][0][0][0].name == \"int8\"\n        assert mock_gmd.call_args_list[0][1][\"increase_itemsize_factor\"] == 2\n        assert mock_cd.call_args_list[0][0][0] is arrays\n        assert observed == \"foo\"\n\n    def test_promote_single_array(self):\n        arr = np.zeros((1,), dtype=np.int8)\n        observed = iadt.promote_array_dtypes_(arr)\n        assert observed.dtype.name == \"int8\"\n\n    def test_promote_single_array_single_dtype_set(self):\n        arr = np.zeros((1,), dtype=np.int8)\n        observed = iadt.promote_array_dtypes_(arr, np.int16)\n        assert observed.dtype.name == \"int16\"\n\n    def test_promote_single_array_dtypes_set(self):\n        arr = np.zeros((1,), dtype=np.int8)\n        observed = iadt.promote_array_dtypes_(arr, [np.int16])\n        assert observed.dtype.name == \"int16\"\n\n    def test_promote_single_array_increase_itemsize_factor_set(self):\n        arr = np.zeros((1,), dtype=np.int8)\n        observed = iadt.promote_array_dtypes_(arr, increase_itemsize_factor=2)\n        assert observed.dtype.name == \"int16\"\n\n    def test_promote_list_of_single_array(self):\n        arrays = [np.zeros((1,), dtype=np.int8)]\n        observed = iadt.promote_array_dtypes_(arrays,\n                                              increase_itemsize_factor=2)\n        assert observed[0].dtype.name == \"int16\"\n\n    def test_promote_list_of_two_arrays(self):\n        arrays = [np.zeros((1,), dtype=np.int8),\n                  np.zeros((1,), dtype=np.int16)]\n        observed = iadt.promote_array_dtypes_(arrays,\n                                              increase_itemsize_factor=2)\n        assert observed[0].dtype.name == \"int32\"\n        assert observed[1].dtype.name == \"int32\"\n\n    def test_promote_list_of_two_arrays_dtypes_set(self):\n        arrays = [np.zeros((1,), dtype=np.int8),\n                  np.zeros((1,), dtype=np.int16)]\n        observed = iadt.promote_array_dtypes_(arrays,\n                                              dtypes=[np.float32, np.float64])\n        assert observed[0].dtype.name == \"float64\"\n        assert observed[1].dtype.name == \"float64\"\n\n    def test_promote_list_of_three_arrays(self):\n        arrays = [np.zeros((1,), dtype=np.int8),\n                  np.zeros((1,), dtype=np.int16),\n                  np.zeros((1,), dtype=np.uint8)]\n        observed = iadt.promote_array_dtypes_(arrays,\n                                              increase_itemsize_factor=2)\n        assert observed[0].dtype.name == \"int32\"\n        assert observed[1].dtype.name == \"int32\"\n        assert observed[2].dtype.name == \"int32\"\n\n\nclass Test_increase_array_resolutions_(unittest.TestCase):\n    def test_single_array_factor_1(self):\n        arr = np.zeros((1,), dtype=np.int8)\n        observed = iadt.increase_array_resolutions_(arr, 1)\n        assert observed.dtype.name == \"int8\"\n\n    def test_single_array_factor_2(self):\n        arr = np.zeros((1,), dtype=np.int8)\n        observed = iadt.increase_array_resolutions_(arr, 2)\n        assert observed.dtype.name == \"int16\"\n\n    def test_list_of_one_array(self):\n        arr = np.zeros((1,), dtype=np.int8)\n        observed = iadt.increase_array_resolutions_([arr], 2)\n        assert observed[0].dtype.name == \"int16\"\n\n    def test_list_of_two_arrays(self):\n        arrays = [\n            np.zeros((1,), dtype=np.int8),\n            np.zeros((1,), dtype=np.int16)\n        ]\n        observed = iadt.increase_array_resolutions_(arrays, 2)\n        assert observed[0].dtype.name == \"int16\"\n        assert observed[1].dtype.name == \"int32\"\n\n\nclass Test_get_value_range_of_dtype(unittest.TestCase):\n    def test_bool(self):\n        minv, center, maxv = iadt.get_value_range_of_dtype(np.dtype(bool))\n        assert minv == 0\n        assert center is None\n        assert maxv == 1\n\n    def test_uint8_string_name(self):\n        assert (\n            iadt.get_value_range_of_dtype(\"uint8\")\n            == iadt.get_value_range_of_dtype(np.dtype(\"uint8\"))\n        )\n\n    def test_uint8(self):\n        minv, center, maxv = iadt.get_value_range_of_dtype(np.dtype(\"uint8\"))\n        assert minv == 0\n        assert np.isclose(center, 0.5*255)\n        assert maxv == 255\n\n    def test_uint16(self):\n        minv, center, maxv = iadt.get_value_range_of_dtype(np.dtype(\"uint16\"))\n        assert minv == 0\n        assert np.isclose(center, 0.5*65535)\n        assert maxv == 65535\n\n    def test_int8(self):\n        minv, center, maxv = iadt.get_value_range_of_dtype(np.dtype(\"int8\"))\n        assert minv == -128\n        assert np.isclose(center, -0.5)\n        assert maxv == 127\n\n    def test_int16(self):\n        minv, center, maxv = iadt.get_value_range_of_dtype(np.dtype(\"int16\"))\n        assert minv == -32768\n        assert np.isclose(center, -0.5)\n        assert maxv == 32767\n\n    def test_float16(self):\n        minv, center, maxv = iadt.get_value_range_of_dtype(np.dtype(\"float16\"))\n        assert minv < 100.0\n        assert np.isclose(center, 0.0)\n        assert maxv > 100.0\n\n\n# TODO extend tests towards all dtypes and actual minima/maxima of value ranges\n# TODO what happens if both bounds are negative, but input dtype is uint*?\nclass Test_clip_(unittest.TestCase):\n    def test_values_hit_lower_bound_int32(self):\n        arr = np.int32([0, 1, 2, 3, 4, 5])\n        observed = iadt.clip_(arr, 0, 10)\n        assert np.array_equal(observed, np.int32([0, 1, 2, 3, 4, 5]))\n\n    def test_values_hit_lower_and_upper_bound_int32(self):\n        arr = np.int32([0, 1, 2, 3, 4, 5])\n        observed = iadt.clip_(arr, 0, 5)\n        assert np.array_equal(observed, np.int32([0, 1, 2, 3, 4, 5]))\n\n    def test_values_hit_lower_bound_exceed_upper_bound_int32(self):\n        arr = np.int32([0, 1, 2, 3, 4, 5])\n        observed = iadt.clip_(arr, 0, 4)\n        assert np.array_equal(observed, np.int32([0, 1, 2, 3, 4, 4]))\n\n    def test_values_exceed_lower_bound_float32(self):\n        arr = np.float32([-1.0])\n        observed = iadt.clip_(arr, 0, 1)\n        assert np.allclose(observed, np.float32([0.0]))\n\n    def test_values_hit_lower_bound_float32(self):\n        arr = np.float32([-1.0])\n        observed = iadt.clip_(arr, -1.0, 1)\n        assert np.allclose(observed, np.float32([-1.0]))\n\n    def test_values_hit_lower_bound_uint32(self):\n        arr = np.uint32([0])\n        observed = iadt.clip_(arr, 0, 1)\n        assert np.array_equal(observed, np.uint32([0]))\n\n    def test_values_hit_upper_bound_uint32(self):\n        arr = np.uint32([1])\n        observed = iadt.clip_(arr, 0, 1)\n        assert np.array_equal(observed, np.uint32([1]))\n\n    def test_values_exceed_upper_bound_uint32(self):\n        arr = np.uint32([2])\n        observed = iadt.clip_(arr, 0, 1)\n        assert np.array_equal(observed, np.uint32([1]))\n\n    def test_values_hit_upper_bound_negative_lower_bound_uint32(self):\n        arr = np.uint32([1])\n        observed = iadt.clip_(arr, -1, 1)\n        assert np.array_equal(observed, np.uint32([1]))\n\n    def test_values_exceed_upper_bound_negative_lower_bound_uint32(self):\n        arr = np.uint32([10])\n        observed = iadt.clip_(arr, -1, 1)\n        assert np.array_equal(observed, np.uint32([1]))\n\n    def test_values_hit_upper_bound_int8(self):\n        arr = np.int8([127])\n        observed = iadt.clip_(arr, 0, 127)\n        assert np.array_equal(observed, np.int8([127]))\n\n    def test_values_within_bounds_upper_bound_is_dtype_limit_int8(self):\n        arr = np.int8([127])\n        observed = iadt.clip_(arr, 0, 128)\n        assert np.array_equal(observed, np.int8([127]))\n\n    def test_values_hit_upper_bound_negative_lower_bound_int8(self):\n        arr = np.int8([127])\n        observed = iadt.clip_(arr, -1, 127)\n        assert np.array_equal(observed, np.int8([127]))\n\n    def test_both_bounds_are_none_int8(self):\n        arr = np.int8([1])\n        observed = iadt.clip_(arr, None, None)\n        assert np.array_equal(observed, np.int8([1]))\n\n    def test_lower_bound_is_none_int8(self):\n        arr = np.int8([1])\n        observed = iadt.clip_(arr, None, 10)\n        assert np.array_equal(observed, np.int8([1]))\n\n    def test_upper_bound_is_none_int8(self):\n        arr = np.int8([1])\n        observed = iadt.clip_(arr, -10, None)\n        assert np.array_equal(observed, np.int8([1]))\n\n    def test_values_exceed_upper_bound_and_lower_bound_is_none_int8(self):\n        arr = np.int8([10])\n        observed = iadt.clip_(arr, None, 1)\n        assert np.array_equal(observed, np.int8([1]))\n\n    def test_values_exceed_lower_bound_and_upper_bound_is_none_int8(self):\n        arr = np.int8([-10])\n        observed = iadt.clip_(arr, -1, None)\n        assert np.array_equal(observed, np.int8([-1]))\n\n    def test_numpy_scalar_hits_lower_bound_int8(self):\n        # single value arrays, shape == tuple()\n        arr = np.int8(-10)\n        observed = iadt.clip_(arr, -10, 10)\n        assert np.array_equal(observed, np.int8(-10))\n\n    def test_numpy_scalar_exceeds_lower_bound_int8(self):\n        arr = np.int8(-10)\n        observed = iadt.clip_(arr, -1, 10)\n        assert np.array_equal(observed, np.int8(-1))\n\n    def test_numpy_scalar_exceeds_upper_bound_int8(self):\n        arr = np.int8(10)\n        observed = iadt.clip_(arr, -10, 1)\n        assert np.array_equal(observed, np.int8(1))\n\n\nclass Test_clip_to_dtype_value_range(unittest.TestCase):\n    def test_clip_to_wider_dtype(self):\n        arr = np.array([-10, -1, 0, 1, 10, 255, 256], dtype=np.int16)\n\n        arr_clipped = iadt.clip_to_dtype_value_range_(\n            np.copy(arr), np.int32, validate=False)\n\n        assert np.array_equal(arr_clipped, arr)\n        assert arr_clipped.dtype.name == \"int16\"\n\n    def test_clip_to_wider_dtype_given_by_name(self):\n        arr = np.array([-10, -1, 0, 1, 10, 255, 256], dtype=np.int16)\n\n        arr_clipped = iadt.clip_to_dtype_value_range_(\n            np.copy(arr), \"int32\", validate=False)\n\n        assert np.array_equal(arr_clipped, arr)\n        assert arr_clipped.dtype.name == \"int16\"\n\n    def test_clip_to_wider_dtype_different_kind(self):\n        arr = np.array([-10, -1, 0, 1, 10, 255, 256], dtype=np.int16)\n\n        arr_clipped = iadt.clip_to_dtype_value_range_(\n            np.copy(arr), np.float64, validate=False)\n\n        assert np.array_equal(arr_clipped, arr)\n        assert arr_clipped.dtype.name == \"int16\"\n\n    def test_clip_to_same_dtype(self):\n        arr = np.array([-10, -1, 0, 1, 10, 255, 256], dtype=np.int16)\n\n        arr_clipped = iadt.clip_to_dtype_value_range_(\n            np.copy(arr), np.int16, validate=False)\n\n        assert np.array_equal(arr_clipped, arr)\n        assert arr_clipped.dtype.name == \"int16\"\n\n    def test_clip_to_narrower_dtype(self):\n        arr = np.array([-10, -1, 0, 1, 10, 255, 256], dtype=np.int16)\n\n        arr_clipped = iadt.clip_to_dtype_value_range_(\n            np.copy(arr), np.int8, validate=False)\n\n        expected = np.array([-10, -1, 0, 1, 10, 127, 127], dtype=np.int16)\n        assert np.array_equal(arr_clipped, expected)\n        assert arr_clipped.dtype.name == \"int16\"\n\n    def test_dtype_is_array(self):\n        arr = np.array([-10, -1, 0, 1, 10, 255, 256], dtype=np.int16)\n        dt_arr = np.array([1], dtype=np.int32)\n\n        arr_clipped = iadt.clip_to_dtype_value_range_(\n            np.copy(arr), dt_arr, validate=False)\n\n        assert np.array_equal(arr_clipped, arr)\n        assert arr_clipped.dtype.name == \"int16\"\n\n    def test_validate_true_all_values_within_value_range(self):\n        arr = np.array([-10, -1, 0, 1, 10, 126, 127], dtype=np.int16)\n\n        arr_clipped = iadt.clip_to_dtype_value_range_(\n            np.copy(arr), np.int8, validate=True)\n\n        assert np.array_equal(arr_clipped, arr)\n        assert arr_clipped.dtype.name == \"int16\"\n\n    def test_validate_true_min_value_outside_value_range(self):\n        arr = np.array([-200, -1, 0, 1, 10, 126, 127], dtype=np.int16)\n\n        with self.assertRaises(AssertionError) as context:\n            _ = iadt.clip_to_dtype_value_range_(\n                np.copy(arr), np.int8, validate=True)\n\n        assert (\n            \"Minimum value of array is outside of allowed value range \"\n            \"(-200.0000 vs -128.0000 to 127.0000).\" in str(context.exception))\n\n    def test_validate_true_max_value_outside_value_range(self):\n        arr = np.array([-10, -1, 0, 1, 10, 126, 200], dtype=np.int16)\n\n        with self.assertRaises(AssertionError) as context:\n            _ = iadt.clip_to_dtype_value_range_(\n                np.copy(arr), np.int8, validate=True)\n\n        assert (\n            \"Maximum value of array is outside of allowed value range \"\n            \"(200.0000 vs -128.0000 to 127.0000).\" in str(context.exception))\n\n    def test_validate_too_few_values(self):\n        arr = np.array([-10, 0, 200], dtype=np.int16)\n\n        _ = iadt.clip_to_dtype_value_range_(np.copy(arr), np.int8, validate=2)\n\n    def test_validate_enough_values(self):\n        arr = np.array([-10, 0, 200], dtype=np.int16)\n\n        with self.assertRaises(AssertionError) as context:\n            _ = iadt.clip_to_dtype_value_range_(\n                np.copy(arr), np.int8, validate=3)\n\n        assert (\n            \"Maximum value of array is outside of allowed value range \"\n            \"(200.0000 vs -128.0000 to 127.0000).\" in str(context.exception))\n\n    def test_validate_too_many_values(self):\n        arr = np.array([-10, 0, 200], dtype=np.int16)\n\n        with self.assertRaises(AssertionError) as context:\n            _ = iadt.clip_to_dtype_value_range_(\n                np.copy(arr), np.int8, validate=100)\n\n        assert (\n            \"Maximum value of array is outside of allowed value range \"\n            \"(200.0000 vs -128.0000 to 127.0000).\" in str(context.exception))\n\n    def test_validate_values_set(self):\n        arr = np.array([-10, -1, 0, 1, 10, 126, 200], dtype=np.int16)\n\n        with self.assertRaises(AssertionError) as context:\n            _ = iadt.clip_to_dtype_value_range_(\n                np.copy(arr), np.int8, validate=True,\n                validate_values=(-5, 201))\n\n        assert (\n            \"Maximum value of array is outside of allowed value range \"\n            \"(201.0000 vs -128.0000 to 127.0000).\" in str(context.exception))\n\n\nclass Test_gate_dtypes_strs(unittest.TestCase):\n    def test_standard_scenario_all_allowed(self):\n        for _ in range(3):\n            dtypes = {np.dtype(\"uint8\"), np.dtype(\"uint8\"),\n                      np.dtype(\"float32\"), np.dtype(\"int64\")}\n            allowed = \"uint8 float32 int64\"\n            disallowed = \"bool\"\n\n            iadt.gate_dtypes_strs(dtypes, allowed, disallowed)\n\n    def test_standard_scenario_one_disallowed(self):\n        for _ in range(3):\n            dtypes = {np.dtype(\"uint8\"), np.dtype(\"uint8\"),\n                      np.dtype(\"float32\"), np.dtype(\"int64\")}\n            allowed = \"uint8 float32\"\n            disallowed = \"bool int64\"\n\n            with self.assertRaises(ValueError) as context:\n                iadt.gate_dtypes_strs(dtypes, allowed, disallowed)\n            assert \"Got dtype 'int64'\" in str(context.exception)\n\n    def test_empty_set_as_dtypes(self):\n        for _ in range(3):\n            dtypes = set()\n            allowed = \"uint8 float32 int64\"\n            disallowed = \"bool\"\n\n            iadt.gate_dtypes_strs(dtypes, allowed, disallowed)\n\n    def test_allowed_dtype_does_not_exist(self):\n        dtypes = {np.dtype(\"uint8\")}\n\n        with self.assertRaises(KeyError):\n            iadt.gate_dtypes_strs(dtypes, \"uint8 int1000\", \"int8\")\n\n    def test_disallowed_dtype_does_not_exist(self):\n        dtypes = {np.dtype(\"uint8\")}\n\n        with self.assertRaises(KeyError):\n            iadt.gate_dtypes_strs(dtypes, \"uint8\", \"int8 int1000\")\n\n    def test_overlap_between_allowed_and_disallowed(self):\n        dtypes = {np.dtype(\"uint8\"), np.dtype(\"float32\"), np.dtype(\"int64\")}\n        allowed = \"uint8 float32 int64\"\n        disallowed = \"uint8 bool\"\n\n        with self.assertRaises(AssertionError) as context:\n            iadt.gate_dtypes_strs(dtypes, allowed, disallowed)\n\n        assert (\n            \"Expected 'allowed' and 'disallowed' dtypes to not contain \"\n            \"the same dtypes\" in str(context.exception)\n        )\n\n    def test_single_array_allowed(self):\n        arr = np.zeros((1, 1, 3), dtype=np.int8)\n\n        iadt.gate_dtypes_strs(arr, \"int8\", \"\")\n\n    def test_single_array_disallowed(self):\n        arr = np.zeros((1, 1, 3), dtype=np.int8)\n\n        with self.assertRaises(ValueError) as context:\n            iadt.gate_dtypes_strs(arr, \"uint8\", \"int8\")\n\n        assert \"Got dtype 'int8'\" in str(context.exception)\n\n    def test_list_of_single_array(self):\n        arr = np.zeros((1, 1, 3), dtype=np.int8)\n\n        iadt.gate_dtypes_strs([arr], \"int8\", \"\")\n\n    def test_list_of_two_arrays_same_dtypes(self):\n        arrays = [\n            np.zeros((1, 1, 3), dtype=np.int8),\n            np.zeros((1, 1, 3), dtype=np.int8)\n        ]\n\n        iadt.gate_dtypes_strs(arrays, \"int8\", \"\")\n\n    def test_list_of_two_arrays_different_dtypes(self):\n        arrays = [\n            np.zeros((1, 1, 3), dtype=np.int8),\n            np.zeros((1, 1, 3), dtype=np.uint8)\n        ]\n\n        iadt.gate_dtypes_strs(arrays, \"int8 uint8\", \"\")\n\n    def test_list_of_two_arrays_same_dtypes_one_disallowed(self):\n        arrays = [\n            np.zeros((1, 1, 3), dtype=np.int8),\n            np.zeros((1, 1, 3), dtype=np.uint8)\n        ]\n\n        with self.assertRaises(ValueError) as context:\n            iadt.gate_dtypes_strs(arrays, \"int8\", \"uint8\")\n\n        assert \"Got dtype 'uint8', which\" in str(context.exception)\n\n\nclass Test_gate_dtypes(unittest.TestCase):\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_standard_scenario_all_allowed(self):\n        dtypes = [np.dtype(\"uint8\"), np.dtype(\"uint8\"),\n                  np.dtype(\"float32\"), np.dtype(\"int64\")]\n        allowed = [np.dtype(\"uint8\"), np.dtype(\"float32\"),\n                   np.dtype(\"int64\")]\n        disallowed = [np.dtype(\"bool\")]\n\n        iadt.gate_dtypes(dtypes, allowed, disallowed)\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_standard_scenario_one_disallowed(self):\n        dtypes = [np.dtype(\"uint8\"), np.dtype(\"uint8\"),\n                  np.dtype(\"float32\"), np.dtype(\"int64\")]\n        allowed = [np.dtype(\"uint8\"), np.dtype(\"float32\")]\n        disallowed = [np.dtype(\"bool\"), np.dtype(\"int64\")]\n\n        with self.assertRaises(ValueError) as context:\n            iadt.gate_dtypes(dtypes, allowed, disallowed)\n        assert \"Got dtype 'int64'\" in str(context.exception)\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_standard_scenario_all_allowed_dtype_names(self):\n        dtypes = [np.dtype(\"uint8\"), np.dtype(\"uint8\"), np.dtype(\"float32\"),\n                  np.dtype(\"int64\")]\n        allowed = [\"uint8\", \"float32\", \"int64\"]\n        disallowed = [\"bool\"]\n\n        iadt.gate_dtypes(dtypes, allowed, disallowed)\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_standard_scenario_one_disallowed_dtype_names(self):\n        dtypes = [np.dtype(\"uint8\"), np.dtype(\"uint8\"), np.dtype(\"float32\"),\n                  np.dtype(\"int64\")]\n        allowed = [\"uint8\", \"float32\"]\n        disallowed = [\"bool\", \"int64\"]\n\n        with self.assertRaises(ValueError) as context:\n            iadt.gate_dtypes(dtypes, allowed, disallowed)\n        assert \"Got dtype 'int64'\" in str(context.exception)\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_standard_scenario_all_allowed_dtype_functions(self):\n        dtypes = [np.dtype(\"uint8\"), np.dtype(\"uint8\"), np.dtype(\"float32\"),\n                  np.dtype(\"int64\")]\n        allowed = [np.uint8, np.float32, np.int64]\n        disallowed = [np.bool_]\n\n        iadt.gate_dtypes(dtypes, allowed, disallowed)\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_standard_scenario_one_disallowed_dtype_functions(self):\n        dtypes = [np.dtype(\"uint8\"), np.dtype(\"uint8\"), np.dtype(\"float32\"),\n                  np.dtype(\"int64\")]\n        allowed = [np.uint8, np.float32]\n        disallowed = [np.bool_, np.int64]\n\n        with self.assertRaises(ValueError) as context:\n            iadt.gate_dtypes(dtypes, allowed, disallowed)\n        assert \"Got dtype 'int64'\" in str(context.exception)\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_single_array_allowed(self):\n        arr = np.zeros((1, 1, 3), dtype=np.int8)\n\n        iadt.gate_dtypes(arr, [\"int8\"], [])\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_single_array_disallowed(self):\n        arr = np.zeros((1, 1, 3), dtype=np.int8)\n\n        with self.assertRaises(ValueError) as context:\n            iadt.gate_dtypes(arr, [\"uint8\"], [\"int8\"])\n\n        assert \"Got dtype 'int8'\" in str(context.exception)\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_list_of_single_array(self):\n        arr = np.zeros((1, 1, 3), dtype=np.int8)\n\n        iadt.gate_dtypes([arr], [np.dtype(\"int8\")], [])\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_list_of_two_arrays_same_dtypes(self):\n        arrays = [\n            np.zeros((1, 1, 3), dtype=np.int8),\n            np.zeros((1, 1, 3), dtype=np.int8)\n        ]\n\n        iadt.gate_dtypes(arrays, [np.dtype(\"int8\")], [])\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_list_of_two_arrays_different_dtypes(self):\n        arrays = [\n            np.zeros((1, 1, 3), dtype=np.int8),\n            np.zeros((1, 1, 3), dtype=np.uint8)\n        ]\n\n        iadt.gate_dtypes(arrays, [\"int8\", \"uint8\"], [])\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_list_of_two_arrays_same_dtypes_one_disallowed(self):\n        arrays = [\n            np.zeros((1, 1, 3), dtype=np.int8),\n            np.zeros((1, 1, 3), dtype=np.uint8)\n        ]\n\n        with self.assertRaises(ValueError) as context:\n            iadt.gate_dtypes(arrays, \"int8\", \"uint8\")\n\n        assert \"Got dtype 'uint8', which\" in str(context.exception)\n\n    @ensure_deprecation_warning(\"imgaug.dtypes.gate_dtypes_strs\")\n    def test_single_dtype_function(self):\n        dtype = np.int8\n\n        iadt.gate_dtypes(dtype, [\"int8\"], [])\n\n\nclass Test__gate_dtypes(unittest.TestCase):\n    def test_standard_scenario_all_allowed(self):\n        dtypes = {np.dtype(\"uint8\"), np.dtype(\"uint8\"), np.dtype(\"float32\"),\n                  np.dtype(\"int64\")}\n        allowed = {np.dtype(\"uint8\"), np.dtype(\"float32\"), np.dtype(\"int64\")}\n        disallowed = {np.dtype(\"bool\")}\n\n        iadt._gate_dtypes(dtypes, allowed, disallowed)\n\n    def test_standard_scenario_one_disallowed(self):\n        dtypes = {np.dtype(\"uint8\"), np.dtype(\"uint8\"), np.dtype(\"float32\"),\n                  np.dtype(\"int64\")}\n        allowed = {np.dtype(\"uint8\"), np.dtype(\"float32\")}\n        disallowed = {np.dtype(\"bool\"), np.dtype(\"int64\")}\n\n        with self.assertRaises(ValueError) as context:\n            iadt._gate_dtypes(dtypes, allowed, disallowed)\n        assert \"Got dtype 'int64'\" in str(context.exception)\n\n    def test_all_allowed_input_is_list_of_dtypes(self):\n        dtypes = {np.dtype(\"uint8\"), np.dtype(\"uint8\"), np.dtype(\"float32\"),\n                  np.dtype(\"int64\")}\n        allowed = {np.dtype(\"uint8\"), np.dtype(\"float32\"), np.dtype(\"int64\")}\n        disallowed = {np.dtype(\"bool\")}\n\n        iadt._gate_dtypes(dtypes, allowed, disallowed)\n\n    def test_all_allowed_input_is_list_of_dtype_functions(self):\n        dtypes = {np.dtype(\"uint8\"), np.dtype(\"uint8\"), np.dtype(\"float32\"),\n                  np.dtype(\"int64\")}\n        allowed = {np.dtype(\"uint8\"), np.dtype(\"float32\"), np.dtype(\"int64\")}\n        disallowed = {np.dtype(\"bool\")}\n\n        iadt._gate_dtypes(dtypes, allowed, disallowed)\n\n    def test_empty_set_as_dtypes(self):\n        dtypes = set()\n        allowed = {np.dtype(\"uint8\"), np.dtype(\"float32\"), np.dtype(\"int64\")}\n        disallowed = {np.dtype(\"bool\")}\n\n        iadt._gate_dtypes(dtypes, allowed, disallowed)\n\n    def test_single_dtype_allowed(self):\n        dtype = np.dtype(\"int8\")\n\n        iadt._gate_dtypes({dtype}, {np.dtype(\"int8\")}, None)\n\n    def test_single_dtype_disallowed(self):\n        dtype = np.dtype(\"int8\")\n\n        with self.assertRaises(ValueError) as context:\n            iadt._gate_dtypes({dtype}, {np.dtype(\"uint8\")}, {np.dtype(\"int8\")})\n\n        assert \"Got dtype 'int8', which\" in str(context.exception)\n\n    def test_single_dtype_disallowed_augmenter_set(self):\n        class _DummyAugmenter(object):\n            def __init__(self):\n                self.name = \"foo\"\n\n        dtype = np.dtype(\"int8\")\n        dummy_augmenter = _DummyAugmenter()\n\n        with self.assertRaises(ValueError) as context:\n            iadt._gate_dtypes(\n                {dtype},\n                {np.dtype(\"uint8\")},\n                {np.dtype(\"int8\")},\n                augmenter=dummy_augmenter\n            )\n\n        assert \"Got dtype 'int8' in augmenter 'foo'\" in str(context.exception)\n\n    def test_list_of_two_dtypes_both_same(self):\n        dtypes = {\n            np.dtype(\"int8\"),\n            np.dtype(\"int8\")\n        }\n\n        iadt._gate_dtypes(dtypes, {np.dtype(\"int8\")}, None)\n\n    def test_list_of_two_dtypes_both_different(self):\n        dtypes = {\n            np.dtype(\"int8\"),\n            np.dtype(\"uint8\")\n        }\n\n        iadt._gate_dtypes(dtypes, {np.dtype(\"int8\"), np.dtype(\"uint8\")}, None)\n\n    def test_list_of_two_dtypes_both_different_one_disallowed(self):\n        dtypes = {\n            np.dtype(\"int8\"),\n            np.dtype(\"uint8\")\n        }\n\n        with self.assertRaises(ValueError) as context:\n            iadt._gate_dtypes(dtypes, {np.dtype(\"int8\")}, {np.dtype(\"uint8\")})\n\n        assert \"Got dtype 'uint8', which\" in str(context.exception)\n\n    def test_dtype_not_in_allowed_or_disallowed(self):\n        dtypes = {\n            np.dtype(\"int8\"),\n            np.dtype(\"float32\")\n        }\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            iadt._gate_dtypes(dtypes, {np.dtype(\"int8\")}, {np.dtype(\"uint8\")})\n\n        assert len(caught_warnings) == 1\n        assert (\n            \"Got dtype 'float32', which\" in str(caught_warnings[-1].message))\n\n    def test_dtype_not_in_allowed_or_disallowed_augmenter_set(self):\n        class _DummyAugmenter(object):\n            def __init__(self):\n                self.name = \"foo\"\n\n        dtypes = {\n            np.dtype(\"int8\"),\n            np.dtype(\"float32\")\n        }\n        dummy_augmenter = _DummyAugmenter()\n\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            iadt._gate_dtypes(\n                dtypes,\n                {np.dtype(\"int8\")},\n                {np.dtype(\"uint8\")},\n                augmenter=dummy_augmenter\n            )\n\n        assert len(caught_warnings) == 1\n        assert (\n            \"Got dtype 'float32' in augmenter 'foo'\"\n            in str(caught_warnings[-1].message))\n"
  },
  {
    "path": "test/test_imgaug.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport time\nimport warnings\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport matplotlib\nmatplotlib.use('Agg')  # fix execution of tests involving matplotlib on travis\nimport numpy as np\nimport six.moves as sm\nimport cv2\n\nimport imgaug as ia\nfrom imgaug import dtypes as iadt\nimport imgaug.random as iarandom\nfrom imgaug.testutils import assertWarns\n\n# TODO clean up this file\n\n\ndef main():\n    time_start = time.time()\n\n    test_is_np_array()\n    test_is_single_integer()\n    test_is_single_float()\n    test_is_single_number()\n    test_is_iterable()\n    test_is_string()\n    test_is_single_bool()\n    test_is_integer_array()\n    test_is_float_array()\n    test_is_callable()\n    test_caller_name()\n    # test_seed()\n    # test_current_random_state()\n    # test_new_random_state()\n    # test_dummy_random_state()\n    # test_copy_random_state()\n    # test_derive_random_state()\n    # test_derive_random_states()\n    # test_forward_random_state()\n    # test_angle_between_vectors()\n    test_compute_line_intersection_point()\n    test_draw_text()\n    test_imresize_many_images()\n    test_imresize_single_image()\n    test_pool()\n    test_avg_pool()\n    test_max_pool()\n    test_min_pool()\n    test_draw_grid()\n    # test_show_grid()\n    # test_do_assert()\n    # test_HooksImages_is_activated()\n    # test_HooksImages_is_propagating()\n    # test_HooksImages_preprocess()\n    # test_HooksImages_postprocess()\n    test_classes_and_functions_marked_deprecated()\n\n    time_end = time.time()\n    print(\"<%s> Finished without errors in %.4fs.\" % (__file__, time_end - time_start,))\n\n\ndef test_is_np_array():\n    class _Dummy(object):\n        pass\n    values_true = [\n        np.zeros((1, 2), dtype=np.uint8),\n        np.zeros((64, 64, 3), dtype=np.uint8),\n        np.zeros((1, 2), dtype=np.float32),\n        np.zeros((100,), dtype=np.float64)\n    ]\n    values_false = [\n        \"A\", \"BC\", \"1\", True, False, (1.0, 2.0), [1.0, 2.0], _Dummy(),\n        -100, 1, 0, 1, 100, -1.2, -0.001, 0.0, 0.001, 1.2, 1e-4\n    ]\n    for value in values_true:\n        assert ia.is_np_array(value) is True\n    for value in values_false:\n        assert ia.is_np_array(value) is False\n\n\ndef test_is_single_integer():\n    assert ia.is_single_integer(\"A\") is False\n    assert ia.is_single_integer(None) is False\n    assert ia.is_single_integer(1.2) is False\n    assert ia.is_single_integer(1.0) is False\n    assert ia.is_single_integer(np.ones((1,), dtype=np.float32)[0]) is False\n    assert ia.is_single_integer(1) is True\n    assert ia.is_single_integer(1234) is True\n    assert ia.is_single_integer(np.ones((1,), dtype=np.uint8)[0]) is True\n    assert ia.is_single_integer(np.ones((1,), dtype=np.int32)[0]) is True\n\n\ndef test_is_single_float():\n    assert ia.is_single_float(\"A\") is False\n    assert ia.is_single_float(None) is False\n    assert ia.is_single_float(1.2) is True\n    assert ia.is_single_float(1.0) is True\n    assert ia.is_single_float(np.ones((1,), dtype=np.float32)[0]) is True\n    assert ia.is_single_float(1) is False\n    assert ia.is_single_float(1234) is False\n    assert ia.is_single_float(np.ones((1,), dtype=np.uint8)[0]) is False\n    assert ia.is_single_float(np.ones((1,), dtype=np.int32)[0]) is False\n\n\ndef test_caller_name():\n    assert ia.caller_name() == 'test_caller_name'\n\n\nclass TestDeprecatedDataFunctions(unittest.TestCase):\n    def test_quokka(self):\n        with assertWarns(self, ia.DeprecationWarning):\n            img = ia.quokka()\n            assert ia.is_np_array(img)\n\n    def test_quokka_square(self):\n        with assertWarns(self, ia.DeprecationWarning):\n            img = ia.quokka_square()\n            assert ia.is_np_array(img)\n\n    def test_quokka_heatmap(self):\n        with assertWarns(self, ia.DeprecationWarning):\n            result = ia.quokka_heatmap()\n            assert isinstance(result, ia.HeatmapsOnImage)\n\n    def test_quokka_segmentation_map(self):\n        with assertWarns(self, ia.DeprecationWarning):\n            result = ia.quokka_segmentation_map()\n            assert isinstance(result, ia.SegmentationMapsOnImage)\n\n    def test_quokka_keypoints(self):\n        with assertWarns(self, ia.DeprecationWarning):\n            result = ia.quokka_keypoints()\n            assert isinstance(result, ia.KeypointsOnImage)\n\n    def test_quokka_bounding_boxes(self):\n        with assertWarns(self, ia.DeprecationWarning):\n            result = ia.quokka_bounding_boxes()\n            assert isinstance(result, ia.BoundingBoxesOnImage)\n\n\ndef test_is_single_number():\n    class _Dummy(object):\n        pass\n    values_true = [-100, 1, 0, 1, 100, -1.2, -0.001, 0.0, 0.001, 1.2, 1e-4]\n    values_false = [\"A\", \"BC\", \"1\", True, False, (1.0, 2.0), [1.0, 2.0], _Dummy(), np.zeros((1, 2), dtype=np.uint8)]\n    for value in values_true:\n        assert ia.is_single_number(value) is True\n    for value in values_false:\n        assert ia.is_single_number(value) is False\n\n\ndef test_is_iterable():\n    class _Dummy(object):\n        pass\n    values_true = [\n        [0, 1, 2],\n        [\"A\", \"X\"],\n        [[123], [456, 789]],\n        [],\n        (1, 2, 3),\n        (1,),\n        tuple(),\n        \"A\",\n        \"ABC\",\n        \"\",\n        np.zeros((100,), dtype=np.uint8)\n    ]\n    values_false = [1, 100, 0, -100, -1, 1.2, -1.2, True, False, _Dummy()]\n    for value in values_true:\n        assert ia.is_iterable(value) is True, value\n    for value in values_false:\n        assert ia.is_iterable(value) is False\n\n\ndef test_is_string():\n    class _Dummy(object):\n        pass\n    values_true = [\"A\", \"BC\", \"1\", \"\"]\n    values_false = [-100, 1, 0, 1, 100, -1.2, -0.001, 0.0, 0.001, 1.2, 1e-4, True, False, (1.0, 2.0), [1.0, 2.0],\n                    _Dummy(), np.zeros((1, 2), dtype=np.uint8)]\n    for value in values_true:\n        assert ia.is_string(value) is True\n    for value in values_false:\n        assert ia.is_string(value) is False\n\n\ndef test_is_single_bool():\n    class _Dummy(object):\n        pass\n    values_true = [False, True]\n    values_false = [-100, 1, 0, 1, 100, -1.2, -0.001, 0.0, 0.001, 1.2, 1e-4, (1.0, 2.0), [1.0, 2.0], _Dummy(),\n                    np.zeros((1, 2), dtype=np.uint8), np.zeros((1,), dtype=bool)]\n    for value in values_true:\n        assert ia.is_single_bool(value) is True\n    for value in values_false:\n        assert ia.is_single_bool(value) is False\n\n\ndef test_is_integer_array():\n    class _Dummy(object):\n        pass\n    values_true = [\n        np.zeros((1, 2), dtype=np.uint8),\n        np.zeros((100,), dtype=np.uint8),\n        np.zeros((1, 2), dtype=np.uint16),\n        np.zeros((1, 2), dtype=np.int32),\n        np.zeros((1, 2), dtype=np.int64)\n    ]\n    values_false = [\n        \"A\", \"BC\", \"1\", \"\", -100, 1, 0, 1, 100, -1.2, -0.001, 0.0, 0.001, 1.2, 1e-4, True, False,\n        (1.0, 2.0), [1.0, 2.0], _Dummy(),\n        np.zeros((1, 2), dtype=np.float16),\n        np.zeros((100,), dtype=np.float32),\n        np.zeros((1, 2), dtype=np.float64),\n        np.zeros((1, 2), dtype=np.bool)\n    ]\n    for value in values_true:\n        assert ia.is_integer_array(value) is True\n    for value in values_false:\n        assert ia.is_integer_array(value) is False\n\n\ndef test_is_float_array():\n    class _Dummy(object):\n        pass\n\n    values_true = [\n        np.zeros((1, 2), dtype=np.float16),\n        np.zeros((100,), dtype=np.float32),\n        np.zeros((1, 2), dtype=np.float64)\n    ]\n    values_false = [\n        \"A\", \"BC\", \"1\", \"\", -100, 1, 0, 1, 100, -1.2, -0.001, 0.0, 0.001, 1.2, 1e-4, True, False,\n        (1.0, 2.0), [1.0, 2.0], _Dummy(),\n        np.zeros((1, 2), dtype=np.uint8),\n        np.zeros((100,), dtype=np.uint8),\n        np.zeros((1, 2), dtype=np.uint16),\n        np.zeros((1, 2), dtype=np.int32),\n        np.zeros((1, 2), dtype=np.int64),\n        np.zeros((1, 2), dtype=np.bool)\n    ]\n    for value in values_true:\n        assert ia.is_float_array(value) is True\n    for value in values_false:\n        assert ia.is_float_array(value) is False\n\n\ndef test_is_callable():\n    def _dummy_func():\n        pass\n\n    _dummy_func2 = lambda x: x\n\n    class _Dummy1(object):\n        pass\n\n    class _Dummy2(object):\n        def __call__(self):\n            pass\n\n    class _Dummy3(object):\n        def foo(self):\n            pass\n\n    class _Dummy4(object):\n        @classmethod\n        def foo(cls):\n            pass\n\n    class _Dummy5(object):\n        @classmethod\n        def foo(cls):\n            pass\n\n    values_true = [_dummy_func, _dummy_func2, _Dummy2(), _Dummy3().foo,\n                   _Dummy4.foo, _Dummy5.foo]\n    values_false = [\n        \"A\", \"BC\", \"1\", \"\", -100, 1, 0, 1, 100, -1.2, -0.001, 0.0, 0.001, 1.2,\n        1e-4, True, False, (1.0, 2.0), [1.0, 2.0], _Dummy1(),\n        np.zeros((1, 2), dtype=np.uint8)]\n    for value in values_true:\n        assert ia.is_callable(value) is True\n    for value in values_false:\n        assert ia.is_callable(value) is False\n\n\n@mock.patch(\"imgaug.random.seed\")\ndef test_seed(mock_seed):\n    ia.seed(10017)\n    mock_seed.assert_called_once_with(10017)\n\n\ndef test_current_random_state():\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        rng = ia.current_random_state()\n\n    assert rng.is_global_rng()\n    assert len(caught_warnings) == 1\n    assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n\n@mock.patch(\"imgaug.random.RNG\")\ndef test_new_random_state__induce_pseudo_random(mock_rng):\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        _ = ia.new_random_state(seed=None, fully_random=False)\n\n    assert mock_rng.create_pseudo_random_.call_count == 1\n    assert len(caught_warnings) == 1\n    assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n\n@mock.patch(\"imgaug.random.RNG\")\ndef test_new_random_state__induce_fully_random(mock_rng):\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        _ = ia.new_random_state(seed=None, fully_random=True)\n\n    assert mock_rng.create_fully_random.call_count == 1\n    assert len(caught_warnings) == 1\n    assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n\n@mock.patch(\"imgaug.random.RNG\")\ndef test_new_random_state__use_seed(mock_rng):\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        _ = ia.new_random_state(seed=1)\n\n    mock_rng.assert_called_once_with(1)\n    assert len(caught_warnings) == 1\n    assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n\n@mock.patch(\"imgaug.random.RNG\")\ndef test_dummy_random_state(mock_rng):\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        _ = ia.dummy_random_state()\n\n    mock_rng.assert_called_once_with(1)\n    assert len(caught_warnings) == 1\n    assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n\n@mock.patch(\"imgaug.random.copy_generator\")\n@mock.patch(\"imgaug.random.copy_generator_unless_global_generator\")\ndef test_copy_random_state__not_global(mock_copy_gen_glob, mock_copy_gen):\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        gen = iarandom.convert_seed_to_generator(1)\n        _ = ia.copy_random_state(gen, force_copy=False)\n\n    assert mock_copy_gen.call_count == 0\n    mock_copy_gen_glob.assert_called_once_with(gen)\n    assert len(caught_warnings) == 1\n    assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n\n@mock.patch(\"imgaug.random.copy_generator\")\n@mock.patch(\"imgaug.random.copy_generator_unless_global_generator\")\ndef test_copy_random_state__also_global(mock_copy_gen_glob, mock_copy_gen):\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        gen = iarandom.convert_seed_to_generator(1)\n        _ = ia.copy_random_state(gen, force_copy=True)\n\n    mock_copy_gen.assert_called_once_with(gen)\n    assert mock_copy_gen_glob.call_count == 0\n    assert len(caught_warnings) == 1\n    assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n\n@mock.patch(\"imgaug.random.derive_generator_\")\ndef test_derive_random_state(mock_derive):\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        gen = iarandom.convert_seed_to_generator(1)\n        _ = ia.derive_random_state(gen)\n\n    mock_derive.assert_called_once_with(gen)\n    assert len(caught_warnings) == 1\n    assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n\n@mock.patch(\"imgaug.random.derive_generators_\")\ndef test_derive_random_states(mock_derive):\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n\n        gen = iarandom.convert_seed_to_generator(1)\n        _ = ia.derive_random_states(gen, n=2)\n\n    mock_derive.assert_called_once_with(gen, n=2)\n    assert len(caught_warnings) == 1\n    assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n\n@mock.patch(\"imgaug.random.advance_generator_\")\ndef test_forward_random_state(mock_advance):\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n        \n        gen = iarandom.convert_seed_to_generator(1)\n        _ = ia.forward_random_state(gen)\n\n    mock_advance.assert_called_once_with(gen)\n    assert len(caught_warnings) == 1\n    assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n\ndef test_compute_line_intersection_point():\n    # intersecting lines\n    line1 = (0, 0, 1, 0)\n    line2 = (0.5, -1, 0.5, 1)\n    point = ia.compute_line_intersection_point(\n        line1[0], line1[1], line1[2], line1[3],\n        line2[0], line2[1], line2[2], line2[3]\n    )\n    assert np.allclose(point[0], 0.5)\n    assert np.allclose(point[1], 0)\n\n    # intersection point outside of defined interval of one line, should not change anything\n    line1 = (0, 0, 1, 0)\n    line2 = (0.5, -1, 0.5, -0.5)\n    point = ia.compute_line_intersection_point(\n        line1[0], line1[1], line1[2], line1[3],\n        line2[0], line2[1], line2[2], line2[3]\n    )\n    assert np.allclose(point[0], 0.5)\n    assert np.allclose(point[1], 0)\n\n    # touching lines\n    line1 = (0, 0, 1, 0)\n    line2 = (0.5, -1, 0.5, 0)\n    point = ia.compute_line_intersection_point(\n        line1[0], line1[1], line1[2], line1[3],\n        line2[0], line2[1], line2[2], line2[3]\n    )\n    assert np.allclose(point[0], 0.5)\n    assert np.allclose(point[1], 0)\n\n    # parallel, not intersecting lines\n    line1 = (0, 0, 1, 0)\n    line2 = (0, -0.1, 1, -0.1)\n    point = ia.compute_line_intersection_point(\n        line1[0], line1[1], line1[2], line1[3],\n        line2[0], line2[1], line2[2], line2[3]\n    )\n    assert point is False\n\n    # parallel and overlapping lines (infinite intersection points)\n    line1 = (0, 0, 1, 0)\n    line2 = (0.1, 0, 1, 0)\n    point = ia.compute_line_intersection_point(\n        line1[0], line1[1], line1[2], line1[3],\n        line2[0], line2[1], line2[2], line2[3]\n    )\n    assert point is False\n\n\ndef test_draw_text():\n    # make roughly sure that shape of drawn text matches expected text\n    img = np.zeros((20, 50, 3), dtype=np.uint8)\n    img_text = ia.draw_text(img, y=5, x=5, text=\"---------\", size=10, color=[255, 255, 255])\n    assert np.max(img_text) == 255\n    assert np.min(img_text) == 0\n    assert np.sum(img_text == 255) / np.sum(img_text == 0)\n    first_row = None\n    last_row = None\n    first_col = None\n    last_col = None\n    for i in range(img.shape[0]):\n        if np.max(img_text[i, :, :]) == 255:\n            first_row = i\n            break\n    for i in range(img.shape[0]-1, 0, -1):\n        if np.max(img_text[i, :, :]) == 255:\n            last_row = i\n            break\n    for i in range(img.shape[1]):\n        if np.max(img_text[:, i, :]) == 255:\n            first_col = i\n            break\n    for i in range(img.shape[1]-1, 0, -1):\n        if np.max(img_text[:, i, :]) == 255:\n            last_col = i\n            break\n    bb = ia.BoundingBox(x1=first_col, y1=first_row, x2=last_col, y2=last_row)\n    assert bb.width > 4.0*bb.height\n\n    # test x\n    img = np.zeros((20, 100, 3), dtype=np.uint8)\n    img_text1 = ia.draw_text(img, y=5, x=5, text=\"XXXXXXX\", size=10, color=[255, 255, 255])\n    img_text2 = ia.draw_text(img, y=5, x=50, text=\"XXXXXXX\", size=10, color=[255, 255, 255])\n    first_col1 = None\n    first_col2 = None\n    for i in range(img.shape[1]):\n        if np.max(img_text1[:, i, :]) == 255:\n            first_col1 = i\n            break\n    for i in range(img.shape[1]):\n        if np.max(img_text2[:, i, :]) == 255:\n            first_col2 = i\n            break\n    assert 0 < first_col1 < 10\n    assert 45 < first_col2 < 55\n\n    # test y\n    img = np.zeros((100, 20, 3), dtype=np.uint8)\n    img_text1 = ia.draw_text(img, y=5, x=5, text=\"XXXXXXX\", size=10, color=[255, 255, 255])\n    img_text2 = ia.draw_text(img, y=50, x=5, text=\"XXXXXXX\", size=10, color=[255, 255, 255])\n    first_row1 = None\n    first_row2 = None\n    for i in range(img.shape[0]):\n        if np.max(img_text1[i, :, :]) == 255:\n            first_row1 = i\n            break\n    for i in range(img.shape[0]):\n        if np.max(img_text2[i, :, :]) == 255:\n            first_row2 = i\n            break\n    assert 0 < first_row1 < 15\n    assert 45 < first_row2 < 60\n\n    # test size\n    img = np.zeros((100, 100, 3), dtype=np.uint8)\n    img_text_small = ia.draw_text(img, y=5, x=5, text=\"X\", size=10, color=[255, 255, 255])\n    img_text_large = ia.draw_text(img, y=5, x=5, text=\"X\", size=50, color=[255, 255, 255])\n    nb_filled_small = np.sum(img_text_small > 10)\n    nb_filled_large = np.sum(img_text_large > 10)\n    assert nb_filled_large > 2*nb_filled_small\n\n    # text color\n    img = np.zeros((20, 20, 3), dtype=np.uint8)\n    img_text = ia.draw_text(img, y=5, x=5, text=\"X\", size=10, color=[128, 129, 130])\n    maxcol = np.max(img_text, axis=(0, 1))\n    assert maxcol[0] == 128\n    assert maxcol[1] == 129\n    assert maxcol[2] == 130\n\n\ndef test_imresize_many_images():\n    interpolations = [None,\n                      \"nearest\", \"linear\", \"area\", \"cubic\",\n                      cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_AREA, cv2.INTER_CUBIC]\n\n    for c in [1, 3]:\n        image1 = np.zeros((16, 16, c), dtype=np.uint8) + 255\n        image2 = np.zeros((16, 16, c), dtype=np.uint8)\n        image3 = np.pad(\n            np.zeros((8, 8, c), dtype=np.uint8) + 255,\n            ((4, 4), (4, 4), (0, 0)),\n            mode=\"constant\",\n            constant_values=0\n        )\n\n        image1_small = np.zeros((8, 8, c), dtype=np.uint8) + 255\n        image2_small = np.zeros((8, 8, c), dtype=np.uint8)\n        image3_small = np.pad(\n            np.zeros((4, 4, c), dtype=np.uint8) + 255,\n            ((2, 2), (2, 2), (0, 0)),\n            mode=\"constant\",\n            constant_values=0\n        )\n\n        image1_large = np.zeros((32, 32, c), dtype=np.uint8) + 255\n        image2_large = np.zeros((32, 32, c), dtype=np.uint8)\n        image3_large = np.pad(\n            np.zeros((16, 16, c), dtype=np.uint8) + 255,\n            ((8, 8), (8, 8), (0, 0)),\n            mode=\"constant\",\n            constant_values=0\n        )\n\n        images = np.uint8([image1, image2, image3])\n        images_small = np.uint8([image1_small, image2_small, image3_small])\n        images_large = np.uint8([image1_large, image2_large, image3_large])\n\n        for images_this_iter in [images, list(images)]:  # test for ndarray and list(ndarray) input\n            for interpolation in interpolations:\n                images_same_observed = ia.imresize_many_images(images_this_iter, (16, 16), interpolation=interpolation)\n                for image_expected, image_observed in zip(images_this_iter, images_same_observed):\n                    diff = np.abs(image_expected.astype(np.int32) - image_observed.astype(np.int32))\n                    assert np.sum(diff) == 0\n\n            for interpolation in interpolations:\n                images_small_observed = ia.imresize_many_images(images_this_iter, (8, 8), interpolation=interpolation)\n                for image_expected, image_observed in zip(images_small, images_small_observed):\n                    diff = np.abs(image_expected.astype(np.int32) - image_observed.astype(np.int32))\n                    diff_fraction = np.sum(diff) / (image_observed.size * 255)\n                    assert diff_fraction < 0.5\n\n            for interpolation in interpolations:\n                images_large_observed = ia.imresize_many_images(images_this_iter, (32, 32), interpolation=interpolation)\n                for image_expected, image_observed in zip(images_large, images_large_observed):\n                    diff = np.abs(image_expected.astype(np.int32) - image_observed.astype(np.int32))\n                    diff_fraction = np.sum(diff) / (image_observed.size * 255)\n                    assert diff_fraction < 0.5\n\n    # test size given as single int\n    images = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n    observed = ia.imresize_many_images(images, 8)\n    assert observed.shape == (1, 8, 8, 3)\n\n    # test size given as single float\n    images = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n    observed = ia.imresize_many_images(images, 2.0)\n    assert observed.shape == (1, 8, 8, 3)\n\n    images = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n    observed = ia.imresize_many_images(images, 0.5)\n    assert observed.shape == (1, 2, 2, 3)\n\n    # test size given as (float, float)\n    images = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n    observed = ia.imresize_many_images(images, (2.0, 2.0))\n    assert observed.shape == (1, 8, 8, 3)\n\n    images = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n    observed = ia.imresize_many_images(images, (0.5, 0.5))\n    assert observed.shape == (1, 2, 2, 3)\n\n    images = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n    observed = ia.imresize_many_images(images, (2.0, 0.5))\n    assert observed.shape == (1, 8, 2, 3)\n\n    images = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n    observed = ia.imresize_many_images(images, (0.5, 2.0))\n    assert observed.shape == (1, 2, 8, 3)\n\n    # test size given as int+float or float+int\n    images = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n    observed = ia.imresize_many_images(images, (11, 2.0))\n    assert observed.shape == (1, 11, 8, 3)\n\n    images = np.zeros((1, 4, 4, 3), dtype=np.uint8)\n    observed = ia.imresize_many_images(images, (2.0, 11))\n    assert observed.shape == (1, 8, 11, 3)\n\n    # test no channels\n    images = np.zeros((1, 4, 4), dtype=np.uint8)\n    images_rs = ia.imresize_many_images(images, (2, 2))\n    assert images_rs.shape == (1, 2, 2)\n\n    images = [np.zeros((4, 4), dtype=np.uint8)]\n    images_rs = ia.imresize_many_images(images, (2, 2))\n    assert isinstance(images_rs, list)\n    assert images_rs[0].shape == (2, 2)\n\n    # test len 0 input\n    observed = ia.imresize_many_images(np.zeros((0, 8, 8, 3), dtype=np.uint8), (4, 4))\n    assert ia.is_np_array(observed)\n    assert observed.dtype.type == np.uint8\n    assert len(observed) == 0\n\n    observed = ia.imresize_many_images([], (4, 4))\n    assert isinstance(observed, list)\n    assert len(observed) == 0\n\n    # test images with zero height/width\n    shapes = [(0, 4, 3), (4, 0, 3), (0, 0, 3)]\n    for shape in shapes:\n        images = [np.zeros(shape, dtype=np.uint8)]\n        got_exception = False\n        try:\n            _ = ia.imresize_many_images(images, sizes=(2, 2))\n        except Exception as exc:\n            assert (\n                \"Cannot resize images, because at least one image has a height \"\n                \"and/or width and/or number of channels of zero.\"\n                in str(exc)\n            )\n            got_exception = True\n        assert got_exception\n\n    # test invalid sizes\n    sizes_all = [(-1, 2)]\n    sizes_all = sizes_all\\\n        + [(float(a), b) for a, b in sizes_all]\\\n        + [(a, float(b)) for a, b in sizes_all]\\\n        + [(float(a), float(b)) for a, b in sizes_all]\\\n        + [(-a, -b) for a, b in sizes_all]\\\n        + [(-float(a), -b) for a, b in sizes_all]\\\n        + [(-a, -float(b)) for a, b in sizes_all]\\\n        + [(-float(a), -float(b)) for a, b in sizes_all]\n    sizes_all = sizes_all\\\n        + [(b, a) for a, b in sizes_all]\n    sizes_all = sizes_all\\\n        + [-1.0, -1]\n    for sizes in sizes_all:\n        images = [np.zeros((4, 4, 3), dtype=np.uint8)]\n        got_exception = False\n        try:\n            _ = ia.imresize_many_images(images, sizes=sizes)\n        except Exception as exc:\n            assert \">= 0\" in str(exc)\n            got_exception = True\n        assert got_exception\n\n    # test list input but all with same shape\n    images = [np.zeros((8, 8, 3), dtype=np.uint8) for _ in range(2)]\n    observed = ia.imresize_many_images(images, (4, 4))\n    assert isinstance(observed, list)\n    assert all([image.shape == (4, 4, 3) for image in observed])\n    assert all([image.dtype.type == np.uint8 for image in observed])\n\n    # test multiple shapes\n    images = [np.zeros((8, 8, 3), dtype=np.uint8), np.zeros((4, 4), dtype=np.uint8)]\n    observed = ia.imresize_many_images(images, (4, 4))\n    assert observed[0].shape == (4, 4, 3)\n    assert observed[1].shape == (4, 4)\n    assert observed[0].dtype == np.uint8\n    assert observed[1].dtype == np.uint8\n\n    ###################\n    # test other dtypes\n    ###################\n    # interpolation=\"nearest\"\n    image = np.zeros((4, 4), dtype=bool)\n    image[1, :] = True\n    image[2, :] = True\n    expected = np.zeros((3, 3), dtype=bool)\n    expected[1, :] = True\n    expected[2, :] = True\n    image_rs = ia.imresize_many_images([image], (3, 3), interpolation=\"nearest\")[0]\n    assert image_rs.dtype.type == image.dtype.type\n    assert np.all(image_rs == expected)\n\n    for dtype in [np.uint8, np.uint16, np.int8, np.int16, np.int32]:\n        min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n        for value in [min_value, max_value]:\n            image = np.zeros((4, 4), dtype=dtype)\n            image[1, :] = value\n            image[2, :] = value\n            expected = np.zeros((3, 3), dtype=dtype)\n            expected[1, :] = value\n            expected[2, :] = value\n            image_rs = ia.imresize_many_images([image], (3, 3), interpolation=\"nearest\")[0]\n            assert image_rs.dtype.type == dtype\n            assert np.all(image_rs == expected)\n\n    for dtype in [np.float16, np.float32, np.float64]:\n        isize = np.dtype(dtype).itemsize\n        for value in [0.5, -0.5, 1.0, -1.0, 10.0, -10.0, -1000 ** (isize-1), 1000 * (isize+1)]:\n            image = np.zeros((4, 4), dtype=dtype)\n            image[1, :] = value\n            image[2, :] = value\n            expected = np.zeros((3, 3), dtype=dtype)\n            expected[1, :] = value\n            expected[2, :] = value\n            image_rs = ia.imresize_many_images([image], (3, 3), interpolation=\"nearest\")[0]\n            assert image_rs.dtype.type == dtype\n            assert np.allclose(image_rs, expected, rtol=0, atol=1e-8)\n\n    # other interpolations\n    for ip in [\"linear\", \"cubic\", \"area\"]:\n        mask = np.zeros((4, 4), dtype=np.uint8)\n        mask[1, :] = 255\n        mask[2, :] = 255\n        mask = ia.imresize_many_images([mask], (3, 3), interpolation=ip)[0]\n        mask = mask.astype(np.float64) / 255.0\n\n        image = np.zeros((4, 4), dtype=bool)\n        image[1, :] = True\n        image[2, :] = True\n        expected = mask > 0.5\n        image_rs = ia.imresize_many_images([image], (3, 3), interpolation=ip)[0]\n        assert image_rs.dtype.type == image.dtype.type\n        assert np.all(image_rs == expected)\n\n        for dtype in [np.uint8, np.uint16, np.int8, np.int16]:\n            min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n            dynamic_range = max_value - min_value\n            for value in [min_value+1, max_value-1]:\n                image = np.zeros((4, 4), dtype=dtype)\n                image[1, :] = value\n                image[2, :] = value\n                expected = np.round(mask * value).astype(dtype)\n                image_rs = ia.imresize_many_images([image], (3, 3), interpolation=ip)[0]\n                assert image_rs.dtype.type == dtype\n                diff = np.abs(image_rs.astype(np.int64) - expected.astype(np.int64))\n                assert np.all(diff < 2 * (1/255) * dynamic_range)\n\n        mask = np.zeros((4, 4), dtype=np.float64)\n        mask[1, :] = 1.0\n        mask[2, :] = 1.0\n        mask = ia.imresize_many_images([mask], (3, 3), interpolation=ip)[0]\n        mask = mask.astype(np.float64)\n\n        for dtype in [np.float16, np.float32, np.float64]:\n            isize = np.dtype(dtype).itemsize\n\n            for value in [0.5, -0.5, 1.0, -1.0, 10.0, -10.0, -1000 ** (isize-1), 1000 * (isize+1)]:\n                image = np.zeros((4, 4), dtype=dtype)\n                image[1, :] = value\n                image[2, :] = value\n                expected = (mask * np.float64(value)).astype(dtype)\n                image_rs = ia.imresize_many_images([image], (3, 3), interpolation=ip)[0]\n                assert image_rs.dtype.type == dtype\n                # Our basis for the expected image is derived from uint8 as that is most likely to work, so we will\n                # have to accept here deviations of around 1/255.\n                atol = np.float64(1 / 255) * np.abs(np.float64(value)) + 1e-8\n                assert np.allclose(image_rs, expected, rtol=0, atol=atol)\n                # Expect at least one cell to have a difference between observed and expected image of approx. 0,\n                # currently we seem to be able to get away with this despite the above mentioned inaccuracy.\n                assert np.any(np.isclose(image_rs, expected, rtol=0, atol=1e-4))\n\n\ndef test_imresize_single_image():\n    for c in [-1, 1, 3]:\n        image1 = np.zeros((16, 16, abs(c)), dtype=np.uint8) + 255\n        image2 = np.zeros((16, 16, abs(c)), dtype=np.uint8)\n        image3 = np.pad(\n            np.zeros((8, 8, abs(c)), dtype=np.uint8) + 255,\n            ((4, 4), (4, 4), (0, 0)),\n            mode=\"constant\",\n            constant_values=0\n        )\n\n        image1_small = np.zeros((8, 8, abs(c)), dtype=np.uint8) + 255\n        image2_small = np.zeros((8, 8, abs(c)), dtype=np.uint8)\n        image3_small = np.pad(\n            np.zeros((4, 4, abs(c)), dtype=np.uint8) + 255,\n            ((2, 2), (2, 2), (0, 0)),\n            mode=\"constant\",\n            constant_values=0\n        )\n\n        image1_large = np.zeros((32, 32, abs(c)), dtype=np.uint8) + 255\n        image2_large = np.zeros((32, 32, abs(c)), dtype=np.uint8)\n        image3_large = np.pad(\n            np.zeros((16, 16, abs(c)), dtype=np.uint8) + 255,\n            ((8, 8), (8, 8), (0, 0)),\n            mode=\"constant\",\n            constant_values=0\n        )\n\n        images = np.uint8([image1, image2, image3])\n        images_small = np.uint8([image1_small, image2_small, image3_small])\n        images_large = np.uint8([image1_large, image2_large, image3_large])\n\n        if c == -1:\n            images = images[:, :, 0]\n            images_small = images_small[:, :, 0]\n            images_large = images_large[:, :, 0]\n\n        interpolations = [None,\n                          \"nearest\", \"linear\", \"area\", \"cubic\",\n                          cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_AREA, cv2.INTER_CUBIC]\n\n        for interpolation in interpolations:\n            for image in images:\n                image_observed = ia.imresize_single_image(image, (16, 16), interpolation=interpolation)\n                diff = np.abs(image.astype(np.int32) - image_observed.astype(np.int32))\n                assert np.sum(diff) == 0\n\n        for interpolation in interpolations:\n            for image, image_expected in zip(images, images_small):\n                image_observed = ia.imresize_single_image(image, (8, 8), interpolation=interpolation)\n                diff = np.abs(image_expected.astype(np.int32) - image_observed.astype(np.int32))\n                diff_fraction = np.sum(diff) / (image_observed.size * 255)\n                assert diff_fraction < 0.5\n\n        for interpolation in interpolations:\n            for image, image_expected in zip(images, images_large):\n                image_observed = ia.imresize_single_image(image, (32, 32), interpolation=interpolation)\n                diff = np.abs(image_expected.astype(np.int32) - image_observed.astype(np.int32))\n                diff_fraction = np.sum(diff) / (image_observed.size * 255)\n                assert diff_fraction < 0.5\n\n\ndef test_pool():\n    # -----\n    # uint, int\n    # -----\n    for dtype in [np.uint8, np.uint16, np.uint32, np.int8, np.int16, np.int32]:\n        min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n        for func in [np.min, np.average, np.max]:\n            arr = np.array([\n                [0, 1, 2, 3],\n                [4, 5, 6, 7],\n                [8, 9, 10, 11],\n                [12, 13, 14, 15]\n            ], dtype=dtype)\n            arr_pooled = ia.pool(arr, 2, func)\n            assert arr_pooled.shape == (2, 2)\n            assert arr_pooled.dtype == np.dtype(dtype)\n            assert arr_pooled[0, 0] == int(func([0, 1, 4, 5]))\n            assert arr_pooled[0, 1] == int(func([2, 3, 6, 7]))\n            assert arr_pooled[1, 0] == int(func([8, 9, 12, 13]))\n            assert arr_pooled[1, 1] == int(func([10, 11, 14, 15]))\n\n            arr = np.array([\n                [0, 1, 2, 3],\n                [4, 5, 6, 7],\n                [8, 9, 10, 11],\n                [12, 13, 14, 15]\n            ], dtype=dtype)\n            arr = np.tile(arr[:, :, np.newaxis], (1, 1, 3))\n            arr[..., 1] += 1\n            arr[..., 2] += 2\n            arr_pooled = ia.pool(arr, 2, func)\n            assert arr_pooled.shape == (2, 2, 3)\n            assert arr_pooled.dtype == np.dtype(dtype)\n            for c in sm.xrange(3):\n                assert arr_pooled[0, 0, c] == int(func([0, 1, 4, 5])) + c\n                assert arr_pooled[0, 1, c] == int(func([2, 3, 6, 7])) + c\n                assert arr_pooled[1, 0, c] == int(func([8, 9, 12, 13])) + c\n                assert arr_pooled[1, 1, c] == int(func([10, 11, 14, 15])) + c\n\n            for value in [min_value, min_value+50, min_value+100, 0, 10, max_value,\n                          int(center_value + 0.10*max_value),\n                          int(center_value + 0.20*max_value),\n                          int(center_value + 0.25*max_value),\n                          int(center_value + 0.33*max_value)]:\n                arr = np.full((4, 4), value, dtype=dtype)\n                arr_pooled = ia.pool(arr, 2, func)\n                assert arr_pooled.shape == (2, 2)\n                assert arr_pooled.dtype == np.dtype(dtype)\n                assert np.all(arr_pooled == value)\n\n                arr = np.full((4, 4, 3), value, dtype=dtype)\n                arr_pooled = ia.pool(arr, 2, func)\n                assert arr_pooled.shape == (2, 2, 3)\n                assert arr_pooled.dtype == np.dtype(dtype)\n                assert np.all(arr_pooled == value)\n\n    # -----\n    # float\n    # -----\n    try:\n        high_res_dt = np.float128\n        dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n    except AttributeError:\n        high_res_dt = np.float64\n        dtypes = [\"float16\", \"float32\", \"float64\"]\n\n    for dtype in dtypes:\n        dtype = np.dtype(dtype)\n\n        def _allclose(a, b):\n            atol = 1e-4 if dtype == np.float16 else 1e-8\n            return np.allclose(a, b, atol=atol, rtol=0)\n\n        for func in [np.min, np.average, np.max]:\n            arr = np.array([\n                [0, 1, 2, 3],\n                [4, 5, 6, 7],\n                [8, 9, 10, 11],\n                [12, 13, 14, 15]\n            ], dtype=dtype)\n            arr_pooled = ia.pool(arr, 2, func)\n            assert arr_pooled.shape == (2, 2)\n            assert arr_pooled.dtype == np.dtype(dtype)\n            assert arr_pooled[0, 0] == func([0, 1, 4, 5])\n            assert arr_pooled[0, 1] == func([2, 3, 6, 7])\n            assert arr_pooled[1, 0] == func([8, 9, 12, 13])\n            assert arr_pooled[1, 1] == func([10, 11, 14, 15])\n\n            arr = np.array([\n                [0, 1, 2, 3],\n                [4, 5, 6, 7],\n                [8, 9, 10, 11],\n                [12, 13, 14, 15]\n            ], dtype=dtype)\n            arr = np.tile(arr[:, :, np.newaxis], (1, 1, 3))\n            arr[..., 1] += 1\n            arr[..., 2] += 2\n            arr_pooled = ia.pool(arr, 2, func)\n            assert arr_pooled.shape == (2, 2, 3)\n            assert arr_pooled.dtype == np.dtype(dtype)\n            for c in sm.xrange(3):\n                assert arr_pooled[0, 0, c] == func([0, 1, 4, 5]) + c\n                assert arr_pooled[0, 1, c] == func([2, 3, 6, 7]) + c\n                assert arr_pooled[1, 0, c] == func([8, 9, 12, 13]) + c\n                assert arr_pooled[1, 1, c] == func([10, 11, 14, 15]) + c\n\n            isize = np.dtype(dtype).itemsize\n            for value in [(-1) * (1000 ** (isize-1)), -50.0, 0.0, 50.0, 1000 ** (isize-1)]:\n                arr = np.full((4, 4), value, dtype=dtype)\n                arr_pooled = ia.pool(arr, 2, func)\n                dt = np.result_type(arr_pooled, 1.)\n                y = np.array(arr_pooled, dtype=dt, copy=False, subok=True)\n                assert arr_pooled.shape == (2, 2)\n                assert arr_pooled.dtype == np.dtype(dtype)\n                assert _allclose(arr_pooled, high_res_dt(value))\n\n                arr = np.full((4, 4, 3), value, dtype=dtype)\n                arr_pooled = ia.pool(arr, 2, func)\n                assert arr_pooled.shape == (2, 2, 3)\n                assert arr_pooled.dtype == np.dtype(dtype)\n                assert _allclose(arr_pooled, high_res_dt(value))\n\n    # ----\n    # bool\n    # ----\n    arr = np.zeros((4, 4), dtype=bool)\n    arr[0, 0] = True\n    arr[0, 1] = True\n    arr[1, 0] = True\n    arr_pooled = ia.pool(arr, 2, np.min)\n    assert arr_pooled.dtype == arr.dtype\n    assert np.all(arr_pooled == 0)\n\n    arr_pooled = ia.pool(arr, 2, np.average)\n    assert arr_pooled.dtype == arr.dtype\n    assert np.all(arr_pooled[0, 0] == 1)\n    assert np.all(arr_pooled[:, 1] == 0)\n    assert np.all(arr_pooled[1, :] == 0)\n\n    arr_pooled = ia.pool(arr, 2, np.max)\n    assert arr_pooled.dtype == arr.dtype\n    assert np.all(arr_pooled[0, 0] == 1)\n    assert np.all(arr_pooled[:, 1] == 0)\n    assert np.all(arr_pooled[1, :] == 0)\n\n    # preserve_dtype off\n    arr = np.uint8([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15]\n    ])\n    arr_pooled = ia.pool(arr, 2, np.average, preserve_dtype=False)\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == np.float64\n    assert np.allclose(arr_pooled[0, 0], np.average([0, 1, 4, 5]))\n    assert np.allclose(arr_pooled[0, 1], np.average([2, 3, 6, 7]))\n    assert np.allclose(arr_pooled[1, 0], np.average([8, 9, 12, 13]))\n    assert np.allclose(arr_pooled[1, 1], np.average([10, 11, 14, 15]))\n\n    # maximum function\n    arr = np.uint8([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15]\n    ])\n    arr_pooled = ia.pool(arr, 2, np.max)\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.max([0, 1, 4, 5]))\n    assert arr_pooled[0, 1] == int(np.max([2, 3, 6, 7]))\n    assert arr_pooled[1, 0] == int(np.max([8, 9, 12, 13]))\n    assert arr_pooled[1, 1] == int(np.max([10, 11, 14, 15]))\n\n    # 3d array\n    arr = np.uint8([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15]\n    ])\n    arr = np.tile(arr[..., np.newaxis], (1, 1, 3))\n    arr_pooled = ia.pool(arr, 2, np.average)\n    assert arr_pooled.shape == (2, 2, 3)\n    assert np.array_equal(arr_pooled[..., 0], arr_pooled[..., 1])\n    assert np.array_equal(arr_pooled[..., 1], arr_pooled[..., 2])\n    arr_pooled = arr_pooled[..., 0]\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.average([0, 1, 4, 5]))\n    assert arr_pooled[0, 1] == int(np.average([2, 3, 6, 7]))\n    assert arr_pooled[1, 0] == int(np.average([8, 9, 12, 13]))\n    assert arr_pooled[1, 1] == int(np.average([10, 11, 14, 15]))\n\n    # block_size per axis\n    arr = np.float32([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15]\n    ])\n    arr_pooled = ia.pool(arr, (2, 1), np.average)\n    assert arr_pooled.shape == (2, 4)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert np.allclose(arr_pooled[0, 0], np.average([0, 4]))\n    assert np.allclose(arr_pooled[0, 1], np.average([1, 5]))\n    assert np.allclose(arr_pooled[0, 2], np.average([2, 6]))\n    assert np.allclose(arr_pooled[0, 3], np.average([3, 7]))\n    assert np.allclose(arr_pooled[1, 0], np.average([8, 12]))\n    assert np.allclose(arr_pooled[1, 1], np.average([9, 13]))\n    assert np.allclose(arr_pooled[1, 2], np.average([10, 14]))\n    assert np.allclose(arr_pooled[1, 3], np.average([11, 15]))\n\n    # cval\n    arr = np.uint8([\n        [0, 1, 2],\n        [4, 5, 6],\n        [8, 9, 10]\n    ])\n    arr_pooled = ia.pool(arr, 2, np.average)\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.average([0, 1, 4, 5]))\n    assert arr_pooled[0, 1] == int(np.average([2, 0, 6, 0]))\n    assert arr_pooled[1, 0] == int(np.average([8, 9, 0, 0]))\n    assert arr_pooled[1, 1] == int(np.average([10, 0, 0, 0]))\n\n    arr = np.uint8([\n        [0, 1],\n        [4, 5]\n    ])\n    arr_pooled = ia.pool(arr, (4, 1), np.average)\n    assert arr_pooled.shape == (1, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.average([0, 4, 0, 0]))\n    assert arr_pooled[0, 1] == int(np.average([1, 5, 0, 0]))\n\n    arr = np.uint8([\n        [0, 1, 2],\n        [4, 5, 6],\n        [8, 9, 10]\n    ])\n    arr_pooled = ia.pool(arr, 2, np.average, pad_cval=22)\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.average([0, 1, 4, 5]))\n    assert arr_pooled[0, 1] == int(np.average([2, 22, 6, 22]))\n    assert arr_pooled[1, 0] == int(np.average([8, 9, 22, 22]))\n    assert arr_pooled[1, 1] == int(np.average([10, 22, 22, 22]))\n\n    # padding mode\n    arr = np.uint8([\n        [0, 1, 2],\n        [4, 5, 6],\n        [8, 9, 10]\n    ])\n    arr_pooled = ia.pool(arr, 2, np.average, pad_mode=\"edge\")\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.average([0, 1, 4, 5]))\n    assert arr_pooled[0, 1] == int(np.average([2, 2, 6, 6]))\n    assert arr_pooled[1, 0] == int(np.average([8, 9, 8, 9]))\n    assert arr_pooled[1, 1] == int(np.average([10, 10, 10, 10]))\n\n    # same as above, but with float32 to make averages more accurate\n    arr = np.float32([\n        [0, 1, 2],\n        [4, 5, 6],\n        [8, 9, 10]\n    ])\n    arr_pooled = ia.pool(arr, 2, np.average, pad_mode=\"edge\")\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert np.isclose(arr_pooled[0, 0], np.average([0, 1, 4, 5]))\n    assert np.isclose(arr_pooled[0, 1], np.average([2, 2, 6, 6]))\n    assert np.isclose(arr_pooled[1, 0], np.average([8, 9, 8, 9]))\n    assert np.isclose(arr_pooled[1, 1], np.average([10, 10, 10, 10]))\n\n\n# TODO add test that verifies the default padding mode\ndef test_avg_pool():\n    # very basic test, as avg_pool() just calls pool(), which is tested in test_pool()\n    arr = np.uint8([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15]\n    ])\n    arr_pooled = ia.avg_pool(arr, 2)\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    # add 1e-4 here to force 0.5 to be rounded up, as that's how OpenCV\n    # handles it\n    assert arr_pooled[0, 0] == int(np.round(1e-4 + np.average([0, 1, 4, 5])))\n    assert arr_pooled[0, 1] == int(np.round(1e-4 + np.average([2, 3, 6, 7])))\n    assert arr_pooled[1, 0] == int(np.round(1e-4 + np.average([8, 9, 12, 13])))\n    assert arr_pooled[1, 1] == int(np.round(1e-4 + np.average([10, 11, 14, 15])))\n\n\n# TODO add test that verifies the default padding mode\ndef test_max_pool():\n    # very basic test, as max_pool() just calls pool(), which is tested in\n    # test_pool()\n    arr = np.uint8([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15]\n    ])\n    arr_pooled = ia.max_pool(arr, 2)\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.max([0, 1, 4, 5]))\n    assert arr_pooled[0, 1] == int(np.max([2, 3, 6, 7]))\n    assert arr_pooled[1, 0] == int(np.max([8, 9, 12, 13]))\n    assert arr_pooled[1, 1] == int(np.max([10, 11, 14, 15]))\n\n\n# TODO add test that verifies the default padding mode\ndef test_min_pool():\n    # very basic test, as min_pool() just calls pool(), which is tested in\n    # test_pool()\n    arr = np.uint8([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15]\n    ])\n\n    arr_pooled = ia.min_pool(arr, 2)\n\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.min([0, 1, 4, 5]))\n    assert arr_pooled[0, 1] == int(np.min([2, 3, 6, 7]))\n    assert arr_pooled[1, 0] == int(np.min([8, 9, 12, 13]))\n    assert arr_pooled[1, 1] == int(np.min([10, 11, 14, 15]))\n\n\n# TODO add test that verifies the default padding mode\ndef test_median_pool():\n    # very basic test, as median_pool() just calls pool(), which is tested in\n    # test_pool()\n    arr = np.uint8([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15]\n    ])\n\n    arr_pooled = ia.median_pool(arr, 2)\n\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.median([0, 1, 4, 5]))\n    assert arr_pooled[0, 1] == int(np.median([2, 3, 6, 7]))\n    assert arr_pooled[1, 0] == int(np.median([8, 9, 12, 13]))\n    assert arr_pooled[1, 1] == int(np.median([10, 11, 14, 15]))\n\n\n# TODO add test that verifies the default padding mode\ndef test_median_pool_ksize_1_3():\n    # very basic test, as median_pool() just calls pool(), which is tested in\n    # test_pool()\n    arr = np.uint8([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15]\n    ])\n\n    arr_pooled = ia.median_pool(arr, (1, 3))\n\n    assert arr_pooled.shape == (4, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.median([0, 1, 2]))\n    assert arr_pooled[0, 1] == int(np.median([3, 2, 1]))\n    assert arr_pooled[1, 0] == int(np.median([4, 5, 6]))\n    assert arr_pooled[1, 1] == int(np.median([7, 6, 5]))\n    assert arr_pooled[2, 0] == int(np.median([8, 9, 10]))\n    assert arr_pooled[2, 1] == int(np.median([11, 10, 9]))\n    assert arr_pooled[3, 0] == int(np.median([12, 13, 14]))\n    assert arr_pooled[3, 1] == int(np.median([15, 14, 13]))\n\n\ndef test_median_pool_ksize_3():\n    # After padding:\n    # [5, 4, 5, 6, 7, 6],\n    # [1, 0, 1, 2, 3, 2],\n    # [5, 4, 5, 6, 7, 6],\n    # [9, 8, 9, 10, 11, 10],\n    # [13, 12, 13, 14, 15, 14],\n    # [9, 8, 9, 10, 11, 10]\n    arr = np.uint8([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15]\n    ])\n\n    arr_pooled = ia.median_pool(arr, 3)\n\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.median([5, 4, 5, 1, 0, 1, 5, 4, 5]))\n    assert arr_pooled[0, 1] == int(np.median([6, 7, 6, 2, 3, 2, 6, 7, 6]))\n    assert arr_pooled[1, 0] == int(np.median([9, 8, 9, 13, 12, 13, 9, 8, 9]))\n    assert arr_pooled[1, 1] == int(np.median([10, 11, 10, 14, 15, 13, 10, 11,\n                                              10]))\n\n\ndef test_median_pool_ksize_3_view():\n    # After padding:\n    # [5, 4, 5, 6, 7, 6],\n    # [1, 0, 1, 2, 3, 2],\n    # [5, 4, 5, 6, 7, 6],\n    # [9, 8, 9, 10, 11, 10],\n    # [13, 12, 13, 14, 15, 14],\n    # [9, 8, 9, 10, 11, 10]\n    arr = np.uint8([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15],\n        [0, 0, 0, 0]\n    ])\n\n    arr_in = arr[0:4, :]\n    assert arr_in.flags[\"OWNDATA\"] is False\n    assert arr_in.flags[\"C_CONTIGUOUS\"] is True\n    arr_pooled = ia.median_pool(arr_in, 3)\n\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.median([5, 4, 5, 1, 0, 1, 5, 4, 5]))\n    assert arr_pooled[0, 1] == int(np.median([6, 7, 6, 2, 3, 2, 6, 7, 6]))\n    assert arr_pooled[1, 0] == int(np.median([9, 8, 9, 13, 12, 13, 9, 8, 9]))\n    assert arr_pooled[1, 1] == int(np.median([10, 11, 10, 14, 15, 13, 10, 11,\n                                              10]))\n\n\ndef test_median_pool_ksize_3_non_contiguous():\n    # After padding:\n    # [5, 4, 5, 6, 7, 6],\n    # [1, 0, 1, 2, 3, 2],\n    # [5, 4, 5, 6, 7, 6],\n    # [9, 8, 9, 10, 11, 10],\n    # [13, 12, 13, 14, 15, 14],\n    # [9, 8, 9, 10, 11, 10]\n    arr = np.array([\n        [0, 1, 2, 3],\n        [4, 5, 6, 7],\n        [8, 9, 10, 11],\n        [12, 13, 14, 15]\n    ], dtype=np.uint8, order=\"F\")\n\n    assert arr.flags[\"OWNDATA\"] is True\n    assert arr.flags[\"C_CONTIGUOUS\"] is False\n    arr_pooled = ia.median_pool(arr, 3)\n\n    assert arr_pooled.shape == (2, 2)\n    assert arr_pooled.dtype == arr.dtype.type\n    assert arr_pooled[0, 0] == int(np.median([5, 4, 5, 1, 0, 1, 5, 4, 5]))\n    assert arr_pooled[0, 1] == int(np.median([6, 7, 6, 2, 3, 2, 6, 7, 6]))\n    assert arr_pooled[1, 0] == int(np.median([9, 8, 9, 13, 12, 13, 9, 8, 9]))\n    assert arr_pooled[1, 1] == int(np.median([10, 11, 10, 14, 15, 13, 10, 11,\n                                              10]))\n\n\ndef test_draw_grid():\n    # bool\n    dtype = bool\n    image = np.zeros((2, 2, 3), dtype=dtype)\n\n    image[0, 0] = False\n    image[0, 1] = True\n    image[1, 0] = True\n    image[1, 1] = False\n\n    grid = ia.draw_grid([image], rows=1, cols=1)\n    assert grid.dtype == np.dtype(dtype)\n    assert np.array_equal(grid, image)\n\n    grid = ia.draw_grid(np.array([image], dtype=dtype), rows=1, cols=1)\n    assert grid.dtype == np.dtype(dtype)\n    assert np.array_equal(grid, image)\n\n    grid = ia.draw_grid([image, image, image, image], rows=2, cols=2)\n    expected = np.vstack([\n        np.hstack([image, image]),\n        np.hstack([image, image])\n    ])\n    assert grid.dtype == np.dtype(dtype)\n    assert np.array_equal(grid, expected)\n\n    grid = ia.draw_grid([image, image], rows=1, cols=2)\n    expected = np.hstack([image, image])\n    assert grid.dtype == np.dtype(dtype)\n    assert np.array_equal(grid, expected)\n\n    grid = ia.draw_grid([image, image, image, image], rows=2, cols=None)\n    expected = np.vstack([\n        np.hstack([image, image]),\n        np.hstack([image, image])\n    ])\n    assert grid.dtype == np.dtype(dtype)\n    assert np.array_equal(grid, expected)\n\n    grid = ia.draw_grid([image, image, image, image], rows=None, cols=2)\n    expected = np.vstack([\n        np.hstack([image, image]),\n        np.hstack([image, image])\n    ])\n    assert grid.dtype == np.dtype(dtype)\n    assert np.array_equal(grid, expected)\n\n    grid = ia.draw_grid([image, image, image, image], rows=None, cols=None)\n    expected = np.vstack([\n        np.hstack([image, image]),\n        np.hstack([image, image])\n    ])\n    assert grid.dtype == np.dtype(dtype)\n    assert np.array_equal(grid, expected)\n\n    # int, uint\n    for dtype in [np.uint8, np.uint16, np.uint32, np.uint64, np.int8, np.int16, np.int32, np.int64]:\n        min_value, center_value, max_value = iadt.get_value_range_of_dtype(dtype)\n\n        image = np.zeros((2, 2, 3), dtype=dtype)\n\n        image[0, 0] = min_value\n        image[0, 1] = center_value\n        image[1, 0] = center_value + int(0.3 * max_value)\n        image[1, 1] = max_value\n\n        grid = ia.draw_grid([image], rows=1, cols=1)\n        assert grid.dtype == np.dtype(dtype)\n        assert np.array_equal(grid, image)\n\n        grid = ia.draw_grid(np.array([image], dtype=dtype), rows=1, cols=1)\n        assert grid.dtype == np.dtype(dtype)\n        assert np.array_equal(grid, image)\n\n        grid = ia.draw_grid([image, image, image, image], rows=2, cols=2)\n        expected = np.vstack([\n            np.hstack([image, image]),\n            np.hstack([image, image])\n        ])\n        assert np.array_equal(grid, expected)\n\n        grid = ia.draw_grid([image, image], rows=1, cols=2)\n        expected = np.hstack([image, image])\n        assert grid.dtype == np.dtype(dtype)\n        assert np.array_equal(grid, expected)\n\n        grid = ia.draw_grid([image, image, image, image], rows=2, cols=None)\n        expected = np.vstack([\n            np.hstack([image, image]),\n            np.hstack([image, image])\n        ])\n        assert grid.dtype == np.dtype(dtype)\n        assert np.array_equal(grid, expected)\n\n        grid = ia.draw_grid([image, image, image, image], rows=None, cols=2)\n        expected = np.vstack([\n            np.hstack([image, image]),\n            np.hstack([image, image])\n        ])\n        assert grid.dtype == np.dtype(dtype)\n        assert np.array_equal(grid, expected)\n\n        grid = ia.draw_grid([image, image, image, image], rows=None, cols=None)\n        expected = np.vstack([\n            np.hstack([image, image]),\n            np.hstack([image, image])\n        ])\n        assert grid.dtype == np.dtype(dtype)\n        assert np.array_equal(grid, expected)\n\n    # float\n    try:\n        _high_res_dt = np.float128\n        dtypes = [\"float16\", \"float32\", \"float64\", \"float128\"]\n    except AttributeError:\n        _high_res_dt = np.float64\n        dtypes = [\"float16\", \"float32\", \"float64\"]\n\n    for dtype in dtypes:\n        dtype = np.dtype(dtype)\n\n        def _allclose(a, b):\n            atol = 1e-4 if dtype == np.float16 else 1e-8\n            return np.allclose(a, b, atol=atol, rtol=0)\n\n        image = np.zeros((2, 2, 3), dtype=dtype)\n\n        isize = np.dtype(dtype).itemsize\n        image[0, 0] = (-1) * (1000 ** (isize-1))\n        image[0, 1] = -10.0\n        image[1, 0] = 10.0\n        image[1, 1] = 1000 ** (isize-1)\n\n        grid = ia.draw_grid([image], rows=1, cols=1)\n        assert grid.dtype == np.dtype(dtype)\n        assert _allclose(grid, image)\n\n        grid = ia.draw_grid(np.array([image], dtype=dtype), rows=1, cols=1)\n        assert grid.dtype == np.dtype(dtype)\n        assert _allclose(grid, image)\n\n        grid = ia.draw_grid([image, image, image, image], rows=2, cols=2)\n        expected = np.vstack([\n            np.hstack([image, image]),\n            np.hstack([image, image])\n        ])\n        assert grid.dtype == np.dtype(dtype)\n        assert _allclose(grid, expected)\n\n        grid = ia.draw_grid([image, image], rows=1, cols=2)\n        expected = np.hstack([image, image])\n        assert grid.dtype == np.dtype(dtype)\n        assert _allclose(grid, expected)\n\n        grid = ia.draw_grid([image, image, image, image], rows=2, cols=None)\n        expected = np.vstack([\n            np.hstack([image, image]),\n            np.hstack([image, image])\n        ])\n        assert grid.dtype == np.dtype(dtype)\n        assert _allclose(grid, expected)\n\n        grid = ia.draw_grid([image, image, image, image], rows=None, cols=2)\n        expected = np.vstack([\n            np.hstack([image, image]),\n            np.hstack([image, image])\n        ])\n        assert grid.dtype == np.dtype(dtype)\n        assert _allclose(grid, expected)\n\n        grid = ia.draw_grid([image, image, image, image], rows=None, cols=None)\n        expected = np.vstack([\n            np.hstack([image, image]),\n            np.hstack([image, image])\n        ])\n        assert grid.dtype == np.dtype(dtype)\n        assert _allclose(grid, expected)\n\n\ndef test_classes_and_functions_marked_deprecated():\n    import imgaug.imgaug as iia\n\n    # class\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n        _kp = iia.Keypoint(x=1, y=2)\n        assert len(caught_warnings) == 1\n        assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n    # function\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n        _result = iia.compute_geometric_median(np.float32([[0, 0]]))\n        assert len(caught_warnings) == 1\n        assert \"is deprecated\" in str(caught_warnings[-1].message)\n\n    # no deprecated warning for calls to imgaug.<name>\n    with warnings.catch_warnings(record=True) as caught_warnings:\n        warnings.simplefilter(\"always\")\n        _kp = ia.Keypoint(x=1, y=2)\n        assert len(caught_warnings) == 0\n\n\nclass Test_apply_lut(unittest.TestCase):\n    def test_2d_image(self):\n        table = np.mod(np.arange(256) + 10, 256).astype(np.uint8)\n\n        image = np.uint8([\n            [0, 50, 100, 245, 254, 255],\n            [1, 51, 101, 246, 255, 0]\n        ])\n\n        image_aug = ia.apply_lut(image, table)\n\n        expected = np.uint8([\n            [10, 60, 110, 255, 8, 9],\n            [11, 61, 111, 0, 9, 10]\n        ])\n        assert np.array_equal(image_aug, expected)\n        assert image_aug is not image\n        assert image_aug.shape == (2, 6)\n        assert image_aug.dtype.name == \"uint8\"\n\n\nclass Test_apply_lut_(unittest.TestCase):\n    def test_2d_image(self):\n        table = np.mod(np.arange(256) + 10, 256).astype(np.uint8)\n        tables = [\n            (\"array-1d\", table),\n            (\"array-2d\", table[:, np.newaxis]),\n            (\"array-3d\", table[np.newaxis, :, np.newaxis]),\n            (\"list\", [table])\n        ]\n\n        for subtable_descr, subtable in tables:\n            with self.subTest(table_type=subtable_descr):\n                image = np.uint8([\n                    [0, 50, 100, 245, 254, 255],\n                    [1, 51, 101, 246, 255, 0]\n                ])\n\n                image_aug = ia.apply_lut_(image, subtable)\n\n                expected = np.uint8([\n                    [10, 60, 110, 255, 8, 9],\n                    [11, 61, 111, 0, 9, 10]\n                ])\n                assert np.array_equal(image_aug, expected)\n                assert image_aug is image\n                assert image_aug.shape == (2, 6)\n                assert image_aug.dtype.name == \"uint8\"\n\n    def test_HW1_image(self):\n        table = np.mod(np.arange(256) + 10, 256).astype(np.uint8)\n        tables = [\n            (\"array-1d\", table),\n            (\"array-2d\", table[:, np.newaxis]),\n            (\"array-3d\", table[np.newaxis, :, np.newaxis]),\n            (\"list\", [table])\n        ]\n\n        for subtable_descr, subtable in tables:\n            with self.subTest(table_type=subtable_descr):\n                image = np.uint8([\n                    [0, 50, 100, 245, 254, 255],\n                    [1, 51, 101, 246, 255, 0]\n                ])\n                image = image[:, :, np.newaxis]\n\n                image_aug = ia.apply_lut_(image, subtable)\n\n                expected = np.uint8([\n                    [10, 60, 110, 255, 8, 9],\n                    [11, 61, 111, 0, 9, 10]\n                ])\n                expected = expected[:, :, np.newaxis]\n                assert np.array_equal(image_aug, expected)\n                # (H,W,1) images always lead to a copy\n                assert image_aug is not image\n                assert image_aug.shape == (2, 6, 1)\n                assert image_aug.dtype.name == \"uint8\"\n\n    def test_HWC_image(self):\n        # Base table, mapping all values to value+10.\n        # For channels C>0 we additionally add +C below.\n        table_base = np.mod(np.arange(256) + 10, 256).astype(np.int32)\n        nb_channels_lst = [2, 3, 4, 5, 511, 512, 513, 512*2-1, 512*2, 512*2+1]\n\n        for nb_channels in nb_channels_lst:\n            # Create channelwise LUT.\n            tables = []\n            for c in np.arange(nb_channels):\n                tables.append(np.mod(table_base + c, 256).astype(np.uint8))\n\n            tables_by_type = [\n                (\"array-1d\", table_base.astype(np.uint8)),\n                (\"array-2d\", np.stack(tables, axis=-1)),\n                (\"array-3d\", np.stack(tables, axis=-1).reshape((1, 256, -1))),\n                (\"list\", tables)\n            ]\n\n            for subtable_descr, subtable in tables_by_type:\n                with self.subTest(nb_channels=nb_channels,\n                                  table_type=subtable_descr):\n                    # Create a normalized lut table, so that we can easily\n                    # find the projected value via x,y,c coordinates.\n                    # In case of array-1d, all channels are treated the same\n                    # way.\n                    if subtable_descr == \"array-1d\":\n                        tables_3d = np.stack([table_base] * nb_channels,\n                                             axis=-1)\n                    else:\n                        tables_3d = np.stack(tables, axis=-1).reshape(\n                            (256, -1))\n\n                    image = np.int32([\n                        [0, 50, 100, 245, 254, 255],\n                        [1, 51, 101, 246, 255, 0]\n                    ])\n                    image = image[:, :, np.newaxis]\n                    image = np.tile(image, (1, 1, nb_channels))\n                    for c in np.arange(nb_channels):\n                        image[:, :, c] += c\n                    image = np.mod(image, 256).astype(np.uint8)\n                    image_orig = np.copy(image)\n\n                    image_aug = ia.apply_lut_(image, subtable)\n\n                    # Reproduce effect of a LUT mapping on the input\n                    # image.\n                    expected = np.zeros_like(image_orig)\n                    for c in np.arange(nb_channels):\n                        for x in np.arange(image.shape[1]):\n                            for y in np.arange(image.shape[0]):\n                                v = image_orig[y, x, c]\n                                v_proj = tables_3d[v, c]\n                                expected[y, x, c] = v_proj\n\n                    assert np.array_equal(image_aug, expected)\n                    if nb_channels < 512:\n                        assert image_aug is image\n                    assert image_aug.shape == (2, 6, nb_channels)\n                    assert image_aug.dtype.name == \"uint8\"\n\n    def test_image_is_noncontiguous(self):\n        table = np.mod(np.arange(256) + 10, 256).astype(np.uint8)\n\n        image = np.uint8([\n            [0, 50, 100, 245, 254, 255],\n            [1, 51, 101, 246, 255, 0]\n        ])\n        image = np.fliplr(image)\n        assert image.flags[\"C_CONTIGUOUS\"] is False\n\n        image_aug = ia.apply_lut_(image, table)\n\n        expected = np.uint8([\n            [10, 60, 110, 255, 8, 9],\n            [11, 61, 111, 0, 9, 10]\n        ])\n        assert np.array_equal(np.fliplr(image_aug), expected)\n        assert image_aug is not image  # non-contiguous should lead to copy\n        assert image_aug.shape == (2, 6)\n        assert image_aug.dtype.name == \"uint8\"\n\n    def test_image_is_view(self):\n        table = np.mod(np.arange(256) + 10, 256).astype(np.uint8)\n\n        image = np.uint8([\n            [0, 50, 100, 245, 254, 255],\n            [1, 51, 101, 246, 255, 0]\n        ])\n        image = image[:, 1:4]\n        assert image.flags[\"OWNDATA\"] is False\n\n        image_aug = ia.apply_lut_(image, table)\n\n        expected = np.uint8([\n            [60, 110, 255],\n            [61, 111, 0]\n        ])\n        assert np.array_equal(image_aug, expected)\n        assert image_aug is not image  # non-owndata should lead to copy\n        assert image_aug.shape == (2, 3)\n        assert image_aug.dtype.name == \"uint8\"\n\n    def test_zero_sized_axes(self):\n        table = np.mod(np.arange(256) + 10, 256).astype(np.uint8)\n        shapes = [\n            (0, 0),\n            (0, 1),\n            (1, 0),\n            (0, 1, 0),\n            (1, 0, 0),\n            (0, 1, 1),\n            (1, 0, 1)\n        ]\n\n        for shape in shapes:\n            with self.subTest(shape=shape):\n                image = np.zeros(shape, dtype=np.uint8)\n                image_aug = ia.apply_lut_(image, table)\n                assert image_aug.shape == shape\n                assert image_aug.dtype.name == \"uint8\"\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "test/test_multicore.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport time\nimport multiprocessing\nimport pickle\nfrom collections import defaultdict\nimport warnings\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\nimport six.moves as sm\n\nimport imgaug as ia\nimport imgaug.multicore as multicore\nimport imgaug.random as iarandom\nfrom imgaug import augmenters as iaa\nfrom imgaug.testutils import reseed\nfrom imgaug.augmentables.batches import Batch, UnnormalizedBatch\n\nIS_SUPPORTING_CONTEXTS = (sys.version_info[0] == 3\n                          and sys.version_info[1] >= 4)\n\n\nclass clean_context():\n    def __init__(self):\n        self.old_context = None\n\n    def __enter__(self):\n        self.old_context = multicore._CONTEXT\n        multicore._CONTEXT = None\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        multicore._CONTEXT = self.old_context\n\n\nclass Test__get_context(unittest.TestCase):\n    @unittest.skipUnless(not IS_SUPPORTING_CONTEXTS,\n                         \"Behaviour happens only in python <=3.3\")\n    @mock.patch(\"imgaug.imgaug.warn\")\n    @mock.patch(\"platform.version\")\n    def test_mocked_nixos_python2(self, mock_version, mock_warn):\n        with clean_context():\n            mock_version.return_value = \"NixOS\"\n            _ctx = multicore._get_context()\n            assert mock_warn.call_count == 1\n\n    @unittest.skipUnless(IS_SUPPORTING_CONTEXTS,\n                         \"Behaviour is only supported in python 3.4+\")\n    @mock.patch(\"platform.version\")\n    @mock.patch(\"multiprocessing.get_context\")\n    def test_mocked_nixos_python3(self, mock_gctx, mock_version):\n        with clean_context():\n            mock_version.return_value = \"NixOS\"\n            _ctx = multicore._get_context()\n            mock_gctx.assert_called_once_with(\"spawn\")\n\n    @unittest.skipUnless(not IS_SUPPORTING_CONTEXTS,\n                         \"Behaviour happens only in python <=3.3\")\n    @mock.patch(\"platform.version\")\n    def test_mocked_no_nixos_python2(self, mock_version):\n        with clean_context():\n            mock_version.return_value = \"Ubuntu\"\n            ctx = multicore._get_context()\n            assert ctx is multiprocessing\n\n    @unittest.skipUnless(IS_SUPPORTING_CONTEXTS,\n                         \"Behaviour is only supported in python 3.4+\")\n    @mock.patch(\"platform.system\")\n    @mock.patch(\"multiprocessing.get_context\")\n    @mock.patch(\"platform.version\")\n    def test_mocked_no_nixos_python3(self, mock_version, mock_gctx, mock_system):\n        with clean_context():\n            mock_version.return_value = \"Ubuntu\"\n            mock_system.return_value = \"Linux\"\n            _ctx = multicore._get_context()\n            assert mock_gctx.call_count == 1\n            assert mock_gctx.call_args_list[0][0][0] is None\n\n    @unittest.skipUnless(IS_SUPPORTING_CONTEXTS,\n                         \"Behaviour is only supported in python 3.4+\")\n    @mock.patch.object(sys, \"version_info\")\n    @mock.patch(\"platform.system\")\n    @mock.patch(\"multiprocessing.get_context\")\n    @mock.patch(\"platform.version\")\n    def test_mocked_mac_and_37_cause_spawn(\n            self,\n            mock_version,\n            mock_gctx,\n            mock_system,\n            mock_vi\n    ):\n        with clean_context():\n            def version_info(index):\n                if isinstance(index, slice):\n                    return 3, 7\n                return 3 if index == 0 else 7\n\n            mock_vi.__getitem__.side_effect = version_info\n\n            mock_version.return_value = \"foo\"\n            mock_system.return_value = \"Darwin\"\n            _ctx = multicore._get_context()\n            mock_gctx.assert_called_once_with(\"spawn\")\n\n\nclass TestPool(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___seed_out_of_bounds(self):\n        augseq = iaa.Identity()\n        with self.assertRaises(AssertionError) as context:\n            _ = multicore.Pool(augseq, seed=iarandom.SEED_MAX_VALUE + 100)\n        assert \"Expected `seed` to be\" in str(context.exception)\n\n    def test_property_pool(self):\n        mock_Pool = mock.MagicMock()\n        mock_Pool.return_value = mock_Pool\n        mock_Pool.close.return_value = None\n        mock_Pool.join.return_value = None\n\n        # We cannot just mock multiprocessing.Pool here, because of using\n        # a custom context. We would have to mock each possible context's\n        # Pool() method or overwrite here the Pool() method of the\n        # actually used context.\n        with mock.patch(\"multiprocessing.pool.Pool\", mock_Pool):\n            augseq = iaa.Identity()\n            pool_config = multicore.Pool(\n                augseq, processes=1, maxtasksperchild=4, seed=123)\n            with pool_config as pool:\n                assert pool.processes == 1\n            assert pool._pool is None\n        assert mock_Pool.call_count == 1\n        assert mock_Pool.close.call_count == 1\n        assert mock_Pool.join.call_count == 1\n        # see\n        # https://github.com/\n        # python/cpython/blob/master/Lib/multiprocessing/context.py\n        # L119 (method Pool()) for an example of how Pool() is called\n        # internally.\n        assert mock_Pool.call_args[0][0] == 1  # processes\n        assert mock_Pool.call_args[0][1] is multicore._Pool_initialize_worker\n        assert mock_Pool.call_args[0][2] == (augseq, 123)\n        assert mock_Pool.call_args[0][3] == 4\n\n    def test_processes(self):\n        augseq = iaa.Identity()\n        mock_Pool = mock.MagicMock()\n        mock_cpu_count = mock.Mock()\n\n        # We cannot just mock multiprocessing.Pool here, because of using\n        # a custom context. We would have to mock each possible context's\n        # Pool() method or overwrite here the Pool() method of the\n        # actually used context.\n        patch_pool = mock.patch(\"multiprocessing.pool.Pool\", mock_Pool)\n\n        # Multiprocessing seems to always access os.cpu_count to get the\n        # current count of cpu cores.\n        # See\n        # https://github.com/\n        # python/cpython/blob/master/Lib/multiprocessing/context.py\n        # L41.\n        fname = (\"os.cpu_count\" if IS_SUPPORTING_CONTEXTS\n                 else \"multiprocessing.cpu_count\")\n        patch_cpu_count = mock.patch(fname, mock_cpu_count)\n\n        with patch_pool, patch_cpu_count:\n            # (cpu cores available, processes requested, processes started)\n            combos = [\n                (1, 1, 1),\n                (2, 1, 1),\n                (3, 1, 1),\n                (1, 2, 2),\n                (3, 2, 2),\n                (1, None, None),\n                (2, None, None),\n                (3, None, None),\n                (1, -1, 1),\n                (2, -1, 1),\n                (3, -1, 2),\n                (4, -2, 2)\n            ]\n\n            for cores_available, processes_req, expected in combos:\n                with self.subTest(cpu_count_available=cores_available,\n                                  processes_requested=processes_req):\n                    mock_cpu_count.return_value = cores_available\n                    with multicore.Pool(augseq,\n                                        processes=processes_req) as _pool:\n                        pass\n\n                    if expected is None:\n                        assert mock_Pool.call_args[0][0] is None\n                    else:\n                        assert mock_Pool.call_args[0][0] == expected\n\n    @mock.patch(\"multiprocessing.pool.Pool\")\n    def test_cpu_count_does_not_exist(self, mock_pool):\n        def _side_effect():\n            raise NotImplementedError\n\n        old_method = multicore._get_context().cpu_count\n        mock_cpu_count = mock.Mock()\n        mock_cpu_count.side_effect = _side_effect\n        multicore._get_context().cpu_count = mock_cpu_count\n\n        augseq = iaa.Identity()\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            warnings.simplefilter(\"always\")\n            with multicore.Pool(augseq, processes=-1):\n                pass\n\n        assert mock_cpu_count.call_count == 1\n        assert mock_pool.call_count == 1\n        # 'processes' arg to Pool was expected to be set to None as cpu_count\n        # produced an error\n        assert mock_pool.call_args_list[0][0][0] is None\n\n        assert len(caught_warnings) == 1\n        assert (\n            \"Could not find method multiprocessing.cpu_count(). \"\n            in str(caught_warnings[-1].message))\n\n        multicore._get_context().cpu_count = old_method\n\n    @classmethod\n    def _test_map_batches_both(cls, call_async):\n        for clazz in [Batch, UnnormalizedBatch]:\n            augseq = iaa.Identity()\n            mock_Pool = mock.MagicMock()\n            mock_Pool.return_value = mock_Pool\n            mock_Pool.map.return_value = \"X\"\n            mock_Pool.map_async.return_value = \"X\"\n            with mock.patch(\"multiprocessing.pool.Pool\", mock_Pool):\n                batches = [\n                    clazz(images=[ia.data.quokka()]),\n                    clazz(images=[ia.data.quokka()+1])\n                ]\n                with multicore.Pool(augseq, processes=1) as pool:\n                    if call_async:\n                        _ = pool.map_batches_async(batches)\n                    else:\n                        _ = pool.map_batches(batches)\n\n                if call_async:\n                    to_check = mock_Pool.map_async\n                else:\n                    to_check = mock_Pool.map\n\n                assert to_check.call_count == 1\n\n                # args, arg 0\n                assert to_check.call_args[0][0] == multicore._Pool_starworker\n\n                # args, arg 1 (batches with ids), tuple 0,\n                # entry 0 in tuple (=> batch id)\n                assert to_check.call_args[0][1][0][0] == 0\n\n                # args, arg 1 (batches with ids), tuple 0,\n                # entry 1 in tuple (=> batch)\n                assert np.array_equal(\n                    to_check.call_args[0][1][0][1].images_unaug,\n                    batches[0].images_unaug)\n\n                # args, arg 1 (batches with ids), tuple 1,\n                # entry 0 in tuple (=> batch id)\n                assert to_check.call_args[0][1][1][0] == 1\n\n                # args, arg 1 (batches with ids), tuple 1,\n                # entry 1 in tuple (=> batch)\n                assert np.array_equal(\n                    to_check.call_args[0][1][1][1].images_unaug,\n                    batches[1].images_unaug)\n\n    def test_map_batches(self):\n        self._test_map_batches_both(call_async=False)\n\n    def test_map_batches_async(self):\n        self._test_map_batches_both(call_async=True)\n\n    @classmethod\n    def _test_imap_batches_both(cls, call_unordered):\n        for clazz in [Batch, UnnormalizedBatch]:\n            batches = [clazz(images=[ia.data.quokka()]),\n                       clazz(images=[ia.data.quokka()+1])]\n\n            def _generate_batches():\n                for batch in batches:\n                    yield batch\n\n            augseq = iaa.Identity()\n            mock_Pool = mock.MagicMock()\n            mock_Pool.return_value = mock_Pool\n            mock_Pool.imap.return_value = batches\n            mock_Pool.imap_unordered.return_value = batches\n            with mock.patch(\"multiprocessing.pool.Pool\", mock_Pool):\n                with multicore.Pool(augseq, processes=1) as pool:\n                    gen = _generate_batches()\n                    if call_unordered:\n                        _ = list(pool.imap_batches_unordered(gen))\n                    else:\n                        _ = list(pool.imap_batches(gen))\n\n                if call_unordered:\n                    to_check = mock_Pool.imap_unordered\n                else:\n                    to_check = mock_Pool.imap\n\n                assert to_check.call_count == 1\n\n                assert to_check.call_args[0][0] == multicore._Pool_starworker\n\n                # convert generator to list, make it subscriptable\n                arg_batches = list(to_check.call_args[0][1])\n\n                # args, arg 1 (batches with ids), tuple 0,\n                # entry 0 in tuple (=> batch id)\n                assert arg_batches[0][0] == 0\n\n                # tuple 0, entry 1 in tuple (=> batch)\n                assert np.array_equal(\n                    arg_batches[0][1].images_unaug,\n                    batches[0].images_unaug)\n\n                # tuple 1, entry 0 in tuple (=> batch id)\n                assert arg_batches[1][0] == 1\n\n                # tuple 1, entry 1 in tuple (=> batch)\n                assert np.array_equal(\n                    arg_batches[1][1].images_unaug,\n                    batches[1].images_unaug)\n\n    @classmethod\n    def _test_imap_batches_both_output_buffer_size(cls, call_unordered,\n                                                   timeout=0.075):\n        batches = [\n            ia.Batch(images=[np.full((1, 1), i, dtype=np.uint8)])\n            for i in range(8)]\n\n        def _generate_batches(times):\n            for batch in batches:\n                yield batch\n                times.append(time.time())\n\n        def callfunc(pool, gen, output_buffer_size):\n            func = (\n                pool.imap_batches_unordered\n                if call_unordered\n                else pool.imap_batches\n            )\n\n            for v in func(gen, output_buffer_size=output_buffer_size):\n                yield v\n\n        def contains_all_ids(inputs):\n            arrs = np.uint8([batch.images_aug for batch in inputs])\n            ids_uq = np.unique(arrs)\n            return (\n                len(ids_uq) == len(batches)\n                and np.all(0 <= ids_uq)\n                and np.all(ids_uq < len(batches))\n            )\n\n        augseq = iaa.Identity()\n        with multicore.Pool(augseq, processes=1) as pool:\n            # no output buffer limit, there should be no noteworthy lag\n            # for any batch requested from _generate_batches()\n            times = []\n            gen = callfunc(pool, _generate_batches(times), None)\n            result = next(gen)\n            time.sleep(timeout)\n            result = [result] + list(gen)\n            times = np.float64(times)\n            times_diffs = times[1:] - times[0:-1]\n            assert np.all(times_diffs < timeout * 1.01)\n            assert contains_all_ids(result)\n\n            # with output buffer limit, but set to the number of batches,\n            # i.e. should again not lead to any lag\n            times = []\n            gen = callfunc(pool, _generate_batches(times), len(batches))\n            result = next(gen)\n            time.sleep(timeout)\n            result = [result] + list(gen)\n            times = np.float64(times)\n            times_diffs = times[1:] - times[0:-1]\n            assert np.all(times_diffs < timeout * 1.01)\n            assert contains_all_ids(result)\n\n            # With output buffer limit of #batches/2 (=4), followed by a\n            # timeout after starting the loading process. This should quickly\n            # load batches until the buffer is full, then wait until the\n            # batches are requested from the buffer (i.e. after the timeout\n            # ended) and then proceed to produce batches at the speed at which\n            # they are requested. This should lead to a measureable lag between\n            # batch 4 and 5 (matching the timeout).\n            times = []\n            gen = callfunc(pool, _generate_batches(times), 4)\n            result = next(gen)\n            time.sleep(timeout)\n            result = [result] + list(gen)\n            times = np.float64(times)\n            times_diffs = times[1:] - times[0:-1]\n            # use -1 here because we have N-1 times for N batches as\n            # diffs denote diffs between Nth and N+1th batch\n            assert np.all(times_diffs[0:4-1] < timeout * 1.01)\n            assert np.all(times_diffs[4-1:4-1+1] >= timeout * 0.99)\n            assert np.all(times_diffs[4-1+1:] < timeout * 1.01)\n            assert contains_all_ids(result)\n\n    def test_imap_batches(self):\n        self._test_imap_batches_both(call_unordered=False)\n\n    def test_imap_batches_unordered(self):\n        self._test_imap_batches_both(call_unordered=True)\n\n    def test_imap_batches_output_buffer_size(self):\n        self._test_imap_batches_both_output_buffer_size(call_unordered=False)\n\n    def test_imap_batches_unordered_output_buffer_size(self):\n        self._test_imap_batches_both_output_buffer_size(call_unordered=True)\n\n    @classmethod\n    def _assert_each_augmentation_not_more_than_once(cls, batches_aug):\n        sum_to_vecs = defaultdict(list)\n        for batch in batches_aug:\n            assert not np.array_equal(batch.images_aug[0], batch.images_aug[1])\n\n            vec = batch.images_aug.flatten()\n            vecsum = int(np.sum(vec))\n            if vecsum in sum_to_vecs:\n                for other_vec in sum_to_vecs[vecsum]:\n                    assert not np.array_equal(vec, other_vec)\n            else:\n                sum_to_vecs[vecsum].append(vec)\n\n    def test_augmentations_with_seed_match(self):\n        nb_batches = 60\n        augseq = iaa.AddElementwise((0, 255))\n        image = np.zeros((10, 10, 1), dtype=np.uint8)\n        batch = ia.Batch(images=np.uint8([image, image]))\n        batches = [batch.deepcopy() for _ in sm.xrange(nb_batches)]\n\n        # seed=1\n        with multicore.Pool(augseq, processes=2, maxtasksperchild=30,\n                            seed=1) as pool:\n            batches_aug1 = pool.map_batches(batches, chunksize=2)\n\n        # seed=1\n        with multicore.Pool(augseq, processes=2, seed=1) as pool:\n            batches_aug2 = pool.map_batches(batches, chunksize=1)\n        # seed=2\n        with multicore.Pool(augseq, processes=2, seed=2) as pool:\n            batches_aug3 = pool.map_batches(batches, chunksize=1)\n\n        assert len(batches_aug1) == nb_batches\n        assert len(batches_aug2) == nb_batches\n        assert len(batches_aug3) == nb_batches\n\n        for b1, b2, b3 in zip(batches_aug1, batches_aug2, batches_aug3):\n            # images were augmented\n            assert not np.array_equal(b1.images_unaug, b1.images_aug)\n            assert not np.array_equal(b2.images_unaug, b2.images_aug)\n            assert not np.array_equal(b3.images_unaug, b3.images_aug)\n\n            # original images still the same\n            assert np.array_equal(b1.images_unaug, batch.images_unaug)\n            assert np.array_equal(b2.images_unaug, batch.images_unaug)\n            assert np.array_equal(b3.images_unaug, batch.images_unaug)\n\n            # augmentations for same seed are the same\n            assert np.array_equal(b1.images_aug, b2.images_aug)\n\n            # augmentations for different seeds are different\n            assert not np.array_equal(b1.images_aug, b3.images_aug)\n\n        # make sure that batches for the two pools with same seed did not\n        # repeat within results (only between the results of the two pools)\n        for batches_aug in [batches_aug1, batches_aug2, batches_aug3]:\n            self._assert_each_augmentation_not_more_than_once(batches_aug)\n\n    def test_augmentations_with_seed_match_for_images_and_keypoints(self):\n        augseq = iaa.AddElementwise((0, 255))\n        image = np.zeros((10, 10, 1), dtype=np.uint8)\n        # keypoints here will not be changed by augseq, but they will induce\n        # deterministic mode to start in augment_batches() as each batch\n        # contains images AND keypoints\n        kps = ia.KeypointsOnImage([ia.Keypoint(x=2, y=0)], shape=(10, 10, 1))\n        batch = ia.Batch(images=np.uint8([image, image]), keypoints=[kps, kps])\n        batches = [batch.deepcopy() for _ in sm.xrange(60)]\n\n        # seed=1\n        with multicore.Pool(augseq, processes=2, maxtasksperchild=30,\n                            seed=1) as pool:\n            batches_aug1 = pool.map_batches(batches, chunksize=2)\n        # seed=1\n        with multicore.Pool(augseq, processes=2, seed=1) as pool:\n            batches_aug2 = pool.map_batches(batches, chunksize=1)\n        # seed=2\n        with multicore.Pool(augseq, processes=2, seed=2) as pool:\n            batches_aug3 = pool.map_batches(batches, chunksize=1)\n\n        assert len(batches_aug1) == 60\n        assert len(batches_aug2) == 60\n        assert len(batches_aug3) == 60\n\n        for batches_aug in [batches_aug1, batches_aug2, batches_aug3]:\n            for batch in batches_aug:\n                for keypoints_aug in batch.keypoints_aug:\n                    assert keypoints_aug.keypoints[0].x == 2\n                    assert keypoints_aug.keypoints[0].y == 0\n\n        for b1, b2, b3 in zip(batches_aug1, batches_aug2, batches_aug3):\n            # images were augmented\n            assert not np.array_equal(b1.images_unaug, b1.images_aug)\n            assert not np.array_equal(b2.images_unaug, b2.images_aug)\n            assert not np.array_equal(b3.images_unaug, b3.images_aug)\n\n            # original images still the same\n            assert np.array_equal(b1.images_unaug, batch.images_unaug)\n            assert np.array_equal(b2.images_unaug, batch.images_unaug)\n            assert np.array_equal(b3.images_unaug, batch.images_unaug)\n\n            # augmentations for same seed are the same\n            assert np.array_equal(b1.images_aug, b2.images_aug)\n\n            # augmentations for different seeds are different\n            assert not np.array_equal(b1.images_aug, b3.images_aug)\n\n        # make sure that batches for the two pools with same seed did not\n        # repeat within results (only between the results of the two pools)\n        for batches_aug in [batches_aug1, batches_aug2, batches_aug3]:\n            self._assert_each_augmentation_not_more_than_once(batches_aug)\n\n    def test_augmentations_without_seed_differ(self):\n        augseq = iaa.AddElementwise((0, 255))\n        image = np.zeros((10, 10, 1), dtype=np.uint8)\n        batch = ia.Batch(images=np.uint8([image, image]))\n        batches = [batch.deepcopy() for _ in sm.xrange(20)]\n\n        with multicore.Pool(augseq, processes=2, maxtasksperchild=5) as pool:\n            batches_aug = pool.map_batches(batches, chunksize=2)\n        with multicore.Pool(augseq, processes=2) as pool:\n            batches_aug.extend(pool.map_batches(batches, chunksize=1))\n\n        assert len(batches_aug) == 2*20\n\n        self._assert_each_augmentation_not_more_than_once(batches_aug)\n\n    def test_augmentations_without_seed_differ_for_images_and_keypoints(self):\n        augseq = iaa.AddElementwise((0, 255))\n        image = np.zeros((10, 10, 1), dtype=np.uint8)\n        # keypoints here will not be changed by augseq, but they will\n        # induce deterministic mode to start in augment_batches() as each\n        # batch contains images AND keypoints\n        kps = ia.KeypointsOnImage([ia.Keypoint(x=2, y=0)], shape=(10, 10, 1))\n        batch = ia.Batch(images=np.uint8([image, image]), keypoints=[kps, kps])\n        batches = [batch.deepcopy() for _ in sm.xrange(20)]\n\n        with multicore.Pool(augseq, processes=2, maxtasksperchild=5) as pool:\n            batches_aug = pool.map_batches(batches, chunksize=2)\n        with multicore.Pool(augseq, processes=2) as pool:\n            batches_aug.extend(pool.map_batches(batches, chunksize=1))\n\n        assert len(batches_aug) == 2*20\n\n        for batch in batches_aug:\n            for keypoints_aug in batch.keypoints_aug:\n                assert keypoints_aug.keypoints[0].x == 2\n                assert keypoints_aug.keypoints[0].y == 0\n\n        self._assert_each_augmentation_not_more_than_once(batches_aug)\n\n    def test_inputs_not_lost(self):\n        \"\"\"Test to make sure that inputs (e.g. images) are never lost.\"\"\"\n        def _assert_contains_all_ids(batches_aug):\n            # batch.images_unaug\n            ids = set()\n            for batch_aug in batches_aug:\n                ids.add(int(batch_aug.images_unaug.flat[0]))\n                ids.add(int(batch_aug.images_unaug.flat[1]))\n            for idx in sm.xrange(2*100):\n                assert idx in ids\n            assert len(ids) == 200\n\n            # batch.images_aug\n            ids = set()\n            for batch_aug in batches_aug:\n                ids.add(int(batch_aug.images_aug.flat[0]))\n                ids.add(int(batch_aug.images_aug.flat[1]))\n            for idx in sm.xrange(2*100):\n                assert idx in ids\n            assert len(ids) == 200\n\n        augseq = iaa.Identity()\n        image = np.zeros((1, 1, 1), dtype=np.uint8)\n        # creates batches containing images with ids from 0 to 199 (one pair\n        # of consecutive ids per batch)\n        batches = [\n            ia.Batch(images=np.uint8([image + b_idx*2, image + b_idx*2+1]))\n            for b_idx\n            in sm.xrange(100)]\n\n        with multicore.Pool(augseq, processes=2, maxtasksperchild=25) as pool:\n            batches_aug = pool.map_batches(batches)\n            _assert_contains_all_ids(batches_aug)\n\n        with multicore.Pool(augseq, processes=2, maxtasksperchild=25,\n                            seed=1) as pool:\n            batches_aug = pool.map_batches(batches)\n            _assert_contains_all_ids(batches_aug)\n\n        with multicore.Pool(augseq, processes=3, seed=2) as pool:\n            batches_aug = pool.map_batches(batches)\n            _assert_contains_all_ids(batches_aug)\n\n        with multicore.Pool(augseq, processes=2, seed=None) as pool:\n            batches_aug = pool.map_batches(batches)\n            _assert_contains_all_ids(batches_aug)\n\n            batches_aug = pool.map_batches(batches)\n            _assert_contains_all_ids(batches_aug)\n\n    def test_close(self):\n        augseq = iaa.Identity()\n        with multicore.Pool(augseq, processes=2) as pool:\n            pool.close()\n\n    def test_terminate(self):\n        augseq = iaa.Identity()\n        with multicore.Pool(augseq, processes=2) as pool:\n            pool.terminate()\n\n    def test_join(self):\n        augseq = iaa.Identity()\n        with multicore.Pool(augseq, processes=2) as pool:\n            pool.close()\n            pool.join()\n\n    @mock.patch(\"multiprocessing.pool.Pool\")\n    def test_join_via_mock(self, mock_pool):\n        # According to codecov, the join() does not get beyond its initial\n        # if statement in the test_join() test, even though it should be.\n        # Might be a simple travis multicore problem?\n        # It is tested here again via some mocking.\n        mock_pool.return_value = mock_pool\n        mock_pool.join.return_value = True\n        with multicore.Pool(iaa.Identity(), processes=2) as pool:\n            pool.join()\n\n            # Make sure that __exit__ does not call close(), which would then\n            # call join() again and we would get a call_count of 2\n            pool._pool = None\n\n        assert mock_pool.join.call_count == 1\n\n\n# This should already be part of the Pool tests, but according to codecov\n# it is not tested. Likely some travis error related to running multiple\n# python processes.\nclass Test_Pool_initialize_worker(unittest.TestCase):\n    def tearDown(self):\n        # without this, other tests can break as e.g. the functions in\n        # multicore assert that _WORKER_AUGSEQ is None\n        multicore.Pool._WORKER_AUGSEQ = None\n        multicore.Pool._WORKER_SEED_START = None\n\n    @mock.patch(\"imgaug.multicore.Pool\")\n    def test_with_seed_start(self, mock_ia_pool):\n        augseq = mock.MagicMock()\n        multicore._Pool_initialize_worker(augseq, 1)\n        assert mock_ia_pool._WORKER_SEED_START == 1\n        assert mock_ia_pool._WORKER_AUGSEQ is augseq\n        assert augseq.localize_random_state_.call_count == 1\n\n    @mock.patch.object(sys, 'version_info')\n    @mock.patch(\"time.time_ns\", create=True)  # doesnt exist in <=3.6\n    @mock.patch(\"imgaug.random.seed\")\n    @mock.patch(\"multiprocessing.current_process\")\n    def test_without_seed_start_simulate_py37_or_higher(self,\n                                                        mock_cp,\n                                                        mock_ia_seed,\n                                                        mock_time_ns,\n                                                        mock_vi):\n        def version_info(index):\n            return 3 if index == 0 else 7\n\n        mock_vi.__getitem__.side_effect = version_info\n        mock_time_ns.return_value = 1\n        mock_cp.return_value = mock.MagicMock()\n        mock_cp.return_value.name = \"foo\"\n        augseq = mock.MagicMock()\n\n        multicore._Pool_initialize_worker(augseq, None)\n\n        assert mock_time_ns.call_count == 1\n        assert mock_ia_seed.call_count == 1\n        assert augseq.seed_.call_count == 1\n\n        seed_global = mock_ia_seed.call_args_list[0][0][0]\n        seed_local = augseq.seed_.call_args_list[0][0][0]\n        assert seed_global != seed_local\n\n    @mock.patch.object(sys, 'version_info')\n    @mock.patch(\"time.time\")\n    @mock.patch(\"imgaug.random.seed\")\n    @mock.patch(\"multiprocessing.current_process\")\n    def test_without_seed_start_simulate_py36_or_lower(self,\n                                                       mock_cp,\n                                                       mock_ia_seed,\n                                                       mock_time,\n                                                       mock_vi):\n        def version_info(index):\n            return 3 if index == 0 else 6\n\n        mock_vi.__getitem__.side_effect = version_info\n        mock_time.return_value = 1\n        mock_cp.return_value = mock.MagicMock()\n        mock_cp.return_value.name = \"foo\"\n        augseq = mock.MagicMock()\n\n        multicore._Pool_initialize_worker(augseq, None)\n\n        assert mock_time.call_count == 1\n        assert mock_ia_seed.call_count == 1\n        assert augseq.seed_.call_count == 1\n\n        seed_global = mock_ia_seed.call_args_list[0][0][0]\n        seed_local = augseq.seed_.call_args_list[0][0][0]\n        assert seed_global != seed_local\n\n    @mock.patch(\"imgaug.random.seed\")\n    def test_without_seed_start(self, mock_ia_seed):\n        augseq = mock.MagicMock()\n\n        multicore._Pool_initialize_worker(augseq, None)\n        time.sleep(0.01)\n        multicore._Pool_initialize_worker(augseq, None)\n\n        seed_global_call_1 = mock_ia_seed.call_args_list[0][0][0]\n        seed_local_call_1 = augseq.seed_.call_args_list[0][0][0]\n        seed_global_call_2 = mock_ia_seed.call_args_list[0][0][0]\n        seed_local_call_2 = augseq.seed_.call_args_list[0][0][0]\n        assert (\n            seed_global_call_1\n            != seed_local_call_1\n            != seed_global_call_2\n            != seed_local_call_2\n        ), \"Got seeds: %d, %d, %d, %d\" % (\n            seed_global_call_1, seed_local_call_1,\n            seed_global_call_2, seed_local_call_2)\n        assert mock_ia_seed.call_count == 2\n        assert augseq.seed_.call_count == 2\n\n\n# This should already be part of the Pool tests, but according to codecov\n# it is not tested. Likely some travis error related to running multiple\n# python processes.\nclass Test_Pool_worker(unittest.TestCase):\n    def tearDown(self):\n        # without this, other tests can break as e.g. the functions in\n        # multicore assert that _WORKER_AUGSEQ is None\n        multicore.Pool._WORKER_AUGSEQ = None\n        multicore.Pool._WORKER_SEED_START = None\n\n    def test_without_seed_start(self):\n        augseq = mock.MagicMock()\n        augseq.augment_batch_.return_value = \"augmented_batch_\"\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        batch = UnnormalizedBatch(images=[image])\n\n        multicore.Pool._WORKER_AUGSEQ = augseq\n        result = multicore._Pool_worker(1, batch)\n\n        assert result == \"augmented_batch_\"\n        assert augseq.augment_batch_.call_count == 1\n        augseq.augment_batch_.assert_called_once_with(batch)\n\n    @mock.patch(\"imgaug.random.seed\")\n    def test_with_seed_start(self, mock_ia_seed):\n        augseq = mock.MagicMock()\n        augseq.augment_batch_.return_value = \"augmented_batch_\"\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        batch = UnnormalizedBatch(images=[image])\n        batch_idx = 1\n        seed_start = 10\n\n        multicore.Pool._WORKER_AUGSEQ = augseq\n        multicore.Pool._WORKER_SEED_START = seed_start\n        result = multicore._Pool_worker(batch_idx, batch)\n\n        # expected seeds used\n        seed = seed_start + batch_idx\n        seed_global_expected = (\n            iarandom.SEED_MIN_VALUE\n            + (seed - 10**9)\n            % (iarandom.SEED_MAX_VALUE - iarandom.SEED_MIN_VALUE)\n        )\n        seed_local_expected = (\n            iarandom.SEED_MIN_VALUE\n            + seed\n            % (iarandom.SEED_MAX_VALUE - iarandom.SEED_MIN_VALUE)\n        )\n\n        assert result == \"augmented_batch_\"\n        assert augseq.augment_batch_.call_count == 1\n        augseq.augment_batch_.assert_called_once_with(batch)\n        mock_ia_seed.assert_called_once_with(seed_global_expected)\n        augseq.seed_.assert_called_once_with(seed_local_expected)\n\n\n# This should already be part of the Pool tests, but according to codecov\n# it is not tested. Likely some travis error related to running multiple\n# python processes.\nclass Test_Pool_starworker(unittest.TestCase):\n    def tearDown(self):\n        # without this, other tests can break as e.g. the functions in\n        # multicore assert that _WORKER_AUGSEQ is None\n        multicore.Pool._WORKER_AUGSEQ = None\n        multicore.Pool._WORKER_SEED_START = None\n\n    @mock.patch(\"imgaug.multicore._Pool_worker\")\n    def test_simple_call(self, mock_worker):\n        image = np.zeros((1, 1, 3), dtype=np.uint8)\n        batch = UnnormalizedBatch(images=[image])\n        batch_idx = 1\n        mock_worker.return_value = \"returned_batch\"\n\n        result = multicore._Pool_starworker((batch_idx, batch))\n\n        assert result == \"returned_batch\"\n        mock_worker.assert_called_once_with(batch_idx, batch)\n\n\n# ---------\n# loading function used in TestBatchLoader.test_basic_functionality()\n# it is outside of the test as putting it inside of it caused issues\n# with spawn mode not being able to pickle this method, see issue #414.\ndef _batch_loader_load_func():\n    for _ in sm.xrange(20):\n        yield ia.Batch(images=np.zeros((2, 4, 4, 3), dtype=np.uint8))\n# ---------\n\n\n# Note that BatchLoader is deprecated\nclass TestBatchLoader(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_basic_functionality(self):\n        warnings.simplefilter(\"always\")\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            for nb_workers in [1, 2]:\n                # repeat these tests many times to catch rarer race conditions\n                for _ in sm.xrange(5):\n                    loader = multicore.BatchLoader(\n                        _batch_loader_load_func, queue_size=2,\n                        nb_workers=nb_workers, threaded=True)\n                    loaded = []\n                    counter = 0\n                    while ((not loader.all_finished()\n                            or not loader.queue.empty())\n                            and counter < 1000):\n                        try:\n                            batch = loader.queue.get(timeout=0.001)\n                            loaded.append(batch)\n                        except:\n                            pass\n                        counter += 1\n                    assert len(loaded) == 20*nb_workers, \\\n                        \"Expected %d to be loaded by threads, got %d for %d \" \\\n                        \"workers at counter %d.\" % (\n                            20*nb_workers, len(loaded), nb_workers, counter\n                        )\n\n                    loader = multicore.BatchLoader(\n                        _batch_loader_load_func, queue_size=200,\n                        nb_workers=nb_workers, threaded=True)\n                    loader.terminate()\n                    assert loader.all_finished()\n\n                    loader = multicore.BatchLoader(\n                        _batch_loader_load_func, queue_size=2,\n                        nb_workers=nb_workers, threaded=False)\n                    loaded = []\n                    counter = 0\n                    while ((not loader.all_finished()\n                            or not loader.queue.empty())\n                            and counter < 1000):\n                        try:\n                            batch = loader.queue.get(timeout=0.001)\n                            loaded.append(batch)\n                        except:\n                            pass\n                        counter += 1\n                    assert len(loaded) == 20*nb_workers, \\\n                        \"Expected %d to be loaded by background processes, \" \\\n                        \"got %d for %d workers at counter %d.\" % (\n                            20*nb_workers, len(loaded), nb_workers, counter\n                        )\n\n                    loader = multicore.BatchLoader(\n                        _batch_loader_load_func, queue_size=200,\n                        nb_workers=nb_workers, threaded=False)\n                    loader.terminate()\n                    assert loader.all_finished()\n\n            assert len(caught_warnings) > 0\n            for warning in caught_warnings:\n                assert \"is deprecated\" in str(warning.message)\n\n\n# Note that BackgroundAugmenter is deprecated\nclass TestBackgroundAugmenter(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_augment_images_worker(self):\n        warnings.simplefilter(\"always\")\n        with warnings.catch_warnings(record=True) as caught_warnings:\n            def gen():\n                yield ia.Batch(images=np.zeros((1, 4, 4, 3), dtype=np.uint8))\n            bl = multicore.BatchLoader(gen(), queue_size=2)\n            bgaug = multicore.BackgroundAugmenter(bl, iaa.Identity(),\n                                                  queue_size=1, nb_workers=1)\n\n            queue_source = multiprocessing.Queue(2)\n            queue_target = multiprocessing.Queue(2)\n            queue_source.put(\n                pickle.dumps(\n                    ia.Batch(images=np.zeros((1, 4, 8, 3), dtype=np.uint8)),\n                    protocol=-1\n                )\n            )\n            queue_source.put(pickle.dumps(None, protocol=-1))\n            bgaug._augment_images_worker(iaa.Add(1), queue_source,\n                                         queue_target, 1)\n\n            batch_aug = pickle.loads(queue_target.get())\n            assert isinstance(batch_aug, ia.Batch)\n            assert batch_aug.images_unaug is not None\n            assert batch_aug.images_unaug.dtype == np.uint8\n            assert batch_aug.images_unaug.shape == (1, 4, 8, 3)\n            assert np.array_equal(\n                batch_aug.images_unaug,\n                np.zeros((1, 4, 8, 3), dtype=np.uint8))\n            assert batch_aug.images_aug is not None\n            assert batch_aug.images_aug.dtype == np.uint8\n            assert batch_aug.images_aug.shape == (1, 4, 8, 3)\n            assert np.array_equal(\n                batch_aug.images_aug,\n                np.zeros((1, 4, 8, 3), dtype=np.uint8) + 1)\n\n            finished_signal = pickle.loads(queue_target.get())\n            assert finished_signal is None\n\n            source_finished_signal = pickle.loads(queue_source.get())\n            assert source_finished_signal is None\n\n            assert queue_source.empty()\n            assert queue_target.empty()\n\n            queue_source.close()\n            queue_target.close()\n            queue_source.join_thread()\n            queue_target.join_thread()\n            bl.terminate()\n            bgaug.terminate()\n\n        assert len(caught_warnings) > 0\n        for warning in caught_warnings:\n            assert \"is deprecated\" in str(warning.message)\n"
  },
  {
    "path": "test/test_parameters.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport itertools\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport matplotlib\nmatplotlib.use('Agg')  # fix execution of tests involving matplotlib on travis\nimport numpy as np\nimport six.moves as sm\nimport skimage\nimport skimage.data\nimport skimage.morphology\nimport scipy\nimport scipy.special\n\nimport imgaug as ia\nimport imgaug.random as iarandom\nfrom imgaug import parameters as iap\nfrom imgaug.testutils import reseed, is_parameter_instance\n\n\ndef _eps(arr):\n    if ia.is_np_array(arr) and arr.dtype.kind == \"f\":\n        return np.finfo(arr.dtype).eps\n    return 1e-4\n\n\nclass Test_handle_continuous_param(unittest.TestCase):\n    def test_value_range_is_none(self):\n        result = iap.handle_continuous_param(\n            1, \"[test1]\",\n            value_range=None, tuple_to_uniform=True, list_to_choice=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_value_range_is_tuple_of_nones(self):\n        result = iap.handle_continuous_param(\n            1, \"[test1b]\",\n            value_range=(None, None),\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_param_is_stochastic_parameter(self):\n        result = iap.handle_continuous_param(\n            iap.Deterministic(1), \"[test2]\",\n            value_range=None, tuple_to_uniform=True, list_to_choice=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_value_range_is_tuple_of_integers(self):\n        result = iap.handle_continuous_param(\n            1, \"[test3]\",\n            value_range=(0, 10),\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_param_is_outside_of_value_range(self):\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_continuous_param(\n                1, \"[test4]\",\n                value_range=(2, 12),\n                tuple_to_uniform=True,\n                list_to_choice=True)\n        self.assertTrue(\"[test4]\" in str(context.exception))\n\n    def test_param_is_inside_value_range_and_no_lower_bound(self):\n        # value within value range (without lower bound)\n        result = iap.handle_continuous_param(\n            1, \"[test5]\",\n            value_range=(None, 12),\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_param_is_outside_of_value_range_and_no_lower_bound(self):\n        # value outside of value range (without lower bound)\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_continuous_param(\n                1, \"[test6]\",\n                value_range=(None, 0),\n                tuple_to_uniform=True,\n                list_to_choice=True)\n        self.assertTrue(\"[test6]\" in str(context.exception))\n\n    def test_param_is_inside_value_range_and_no_upper_bound(self):\n        # value within value range (without upper bound)\n        result = iap.handle_continuous_param(\n            1, \"[test7]\",\n            value_range=(-1, None),\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_param_is_outside_of_value_range_and_no_upper_bound(self):\n        # value outside of value range (without upper bound)\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_continuous_param(\n                1, \"[test8]\",\n                value_range=(2, None),\n                tuple_to_uniform=True,\n                list_to_choice=True)\n        self.assertTrue(\"[test8]\" in str(context.exception))\n\n    def test_tuple_as_value_but_no_tuples_allowed(self):\n        # tuple as value, but no tuples allowed\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_continuous_param(\n                (1, 2), \"[test9]\",\n                value_range=None,\n                tuple_to_uniform=False,\n                list_to_choice=True)\n        self.assertTrue(\"[test9]\" in str(context.exception))\n\n    def test_tuple_as_value_and_tuples_allowed(self):\n        # tuple as value and tuple allowed\n        result = iap.handle_continuous_param(\n            (1, 2), \"[test10]\",\n            value_range=None,\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Uniform))\n\n    def test_tuple_as_value_and_tuples_allowed_and_inside_value_range(self):\n        # tuple as value and tuple allowed and tuple within value range\n        result = iap.handle_continuous_param(\n            (1, 2), \"[test11]\",\n            value_range=(0, 10),\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Uniform))\n\n    def test_tuple_value_and_allowed_and_partially_outside_value_range(self):\n        # tuple as value and tuple allowed and tuple partially outside of\n        # value range\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_continuous_param(\n                (1, 2), \"[test12]\",\n                value_range=(1.5, 13),\n                tuple_to_uniform=True,\n                list_to_choice=True)\n        self.assertTrue(\"[test12]\" in str(context.exception))\n\n    def test_tuple_value_and_allowed_and_fully_outside_value_range(self):\n        # tuple as value and tuple allowed and tuple fully outside of value\n        # range\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_continuous_param(\n                (1, 2), \"[test13]\",\n                value_range=(3, 13),\n                tuple_to_uniform=True,\n                list_to_choice=True)\n        self.assertTrue(\"[test13]\" in str(context.exception))\n\n    def test_list_as_value_but_no_lists_allowed(self):\n        # list as value, but no list allowed\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_continuous_param(\n                [1, 2, 3], \"[test14]\",\n                value_range=None,\n                tuple_to_uniform=True,\n                list_to_choice=False)\n        self.assertTrue(\"[test14]\" in str(context.exception))\n\n    def test_list_as_value_and_lists_allowed(self):\n        # list as value and list allowed\n        result = iap.handle_continuous_param(\n            [1, 2, 3], \"[test15]\",\n            value_range=None,\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Choice))\n\n    def test_list_value_and_allowed_and_partially_outside_value_range(self):\n        # list as value and list allowed and list partially outside of value\n        # range\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_continuous_param(\n                [1, 2], \"[test16]\",\n                value_range=(1.5, 13),\n                tuple_to_uniform=True,\n                list_to_choice=True)\n        self.assertTrue(\"[test16]\" in str(context.exception))\n\n    def test_list_value_and_allowed_and_fully_outside_of_value_range(self):\n        # list as value and list allowed and list fully outside of value range\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_continuous_param(\n                [1, 2], \"[test17]\",\n                value_range=(3, 13),\n                tuple_to_uniform=True,\n                list_to_choice=True)\n        self.assertTrue(\"[test17]\" in str(context.exception))\n\n    def test_value_inside_value_range_and_value_range_given_as_callable(self):\n        # single value within value range given as callable\n        def _value_range(x):\n            return -1 < x < 1\n\n        result = iap.handle_continuous_param(\n            1, \"[test18]\",\n            value_range=_value_range,\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_bad_datatype_as_value_range(self):\n        # bad datatype for value range\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_continuous_param(\n                1, \"[test19]\",\n                value_range=False,\n                tuple_to_uniform=True,\n                list_to_choice=True)\n        self.assertTrue(\n            \"Unexpected input for value_range\" in str(context.exception))\n\n\nclass Test_handle_discrete_param(unittest.TestCase):\n    def test_float_value_inside_value_range_but_no_floats_allowed(self):\n        # float value without value range when no float value is allowed\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_discrete_param(\n                1.5, \"[test0]\",\n                value_range=None,\n                tuple_to_uniform=True,\n                list_to_choice=True, allow_floats=False)\n        self.assertTrue(\"[test0]\" in str(context.exception))\n\n    def test_value_range_is_none(self):\n        # value without value range\n        result = iap.handle_discrete_param(\n            1, \"[test1]\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=True, prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_value_range_is_tuple_of_nones(self):\n        # value without value range as (None, None)\n        result = iap.handle_discrete_param(\n            1, \"[test1b]\", value_range=(None, None), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=True, prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_value_is_stochastic_parameter(self):\n        # stochastic parameter\n        result = iap.handle_discrete_param(\n            iap.Deterministic(1), \"[test2]\", value_range=None,\n            tuple_to_uniform=True, list_to_choice=True, allow_floats=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_value_inside_value_range(self):\n        # value within value range\n        result = iap.handle_discrete_param(\n            1, \"[test3]\", value_range=(0, 10), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=True, prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_value_outside_value_range(self):\n        # value outside of value range\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_discrete_param(\n                1, \"[test4]\", value_range=(2, 12), tuple_to_uniform=True,\n                list_to_choice=True, allow_floats=True, prefetch=False)\n        self.assertTrue(\"[test4]\" in str(context.exception))\n\n    def test_value_inside_value_range_no_lower_bound(self):\n        # value within value range (without lower bound)\n        result = iap.handle_discrete_param(\n            1, \"[test5]\", value_range=(None, 12), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=True, prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_value_outside_value_range_no_lower_bound(self):\n        # value outside of value range (without lower bound)\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_discrete_param(\n                1, \"[test6]\", value_range=(None, 0), tuple_to_uniform=True,\n                list_to_choice=True, allow_floats=True, prefetch=False)\n        self.assertTrue(\"[test6]\" in str(context.exception))\n\n    def test_value_inside_value_range_no_upper_bound(self):\n        # value within value range (without upper bound)\n        result = iap.handle_discrete_param(\n            1, \"[test7]\", value_range=(-1, None), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=True, prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_value_outside_value_range_no_upper_bound(self):\n        # value outside of value range (without upper bound)\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_discrete_param(\n                1, \"[test8]\", value_range=(2, None), tuple_to_uniform=True,\n                list_to_choice=True, allow_floats=True)\n        self.assertTrue(\"[test8]\" in str(context.exception))\n\n    def test_value_is_tuple_but_no_tuples_allowed(self):\n        # tuple as value, but no tuples allowed\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_discrete_param(\n                (1, 2), \"[test9]\", value_range=None, tuple_to_uniform=False,\n                list_to_choice=True, allow_floats=True)\n        self.assertTrue(\"[test9]\" in str(context.exception))\n\n    def test_value_is_tuple_and_tuples_allowed(self):\n        # tuple as value and tuple allowed\n        result = iap.handle_discrete_param(\n            (1, 2), \"[test10]\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=True, prefetch=False)\n        self.assertTrue(isinstance(result, iap.DiscreteUniform))\n\n    def test_value_tuple_and_allowed_and_inside_value_range(self):\n        # tuple as value and tuple allowed and tuple within value range\n        result = iap.handle_discrete_param(\n            (1, 2), \"[test11]\", value_range=(0, 10), tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=True, prefetch=False)\n        self.assertTrue(isinstance(result, iap.DiscreteUniform))\n\n    def test_value_tuple_and_allowed_and_inside_vr_allow_floats_false(self):\n        # tuple as value and tuple allowed and tuple within value range with\n        # allow_floats=False\n        result = iap.handle_discrete_param(\n            (1, 2), \"[test11b]\", value_range=(0, 10),\n            tuple_to_uniform=True, list_to_choice=True, allow_floats=False,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.DiscreteUniform))\n\n    def test_value_tuple_and_allowed_and_partially_outside_value_range(self):\n        # tuple as value and tuple allowed and tuple partially outside of\n        # value range\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_discrete_param(\n                (1, 3), \"[test12]\", value_range=(2, 13), tuple_to_uniform=True,\n                list_to_choice=True, allow_floats=True)\n        self.assertTrue(\"[test12]\" in str(context.exception))\n\n    def test_value_tuple_and_allowed_and_fully_outside_value_range(self):\n        # tuple as value and tuple allowed and tuple fully outside of value\n        # range\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_discrete_param(\n                (1, 2), \"[test13]\", value_range=(3, 13), tuple_to_uniform=True,\n                list_to_choice=True, allow_floats=True)\n        self.assertTrue(\"[test13]\" in str(context.exception))\n\n    def test_value_list_but_not_allowed(self):\n        # list as value, but no list allowed\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_discrete_param(\n                [1, 2, 3], \"[test14]\", value_range=None, tuple_to_uniform=True,\n                list_to_choice=False, allow_floats=True)\n        self.assertTrue(\"[test14]\" in str(context.exception))\n\n    def test_value_list_and_allowed(self):\n        # list as value and list allowed\n        result = iap.handle_discrete_param(\n            [1, 2, 3], \"[test15]\", value_range=None, tuple_to_uniform=True,\n            list_to_choice=True, allow_floats=True, prefetch=False)\n        self.assertTrue(isinstance(result, iap.Choice))\n\n    def test_value_list_and_allowed_and_partially_outside_value_range(self):\n        # list as value and list allowed and list partially outside of value range\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_discrete_param(\n                [1, 3], \"[test16]\", value_range=(2, 13), tuple_to_uniform=True,\n                list_to_choice=True, allow_floats=True)\n        self.assertTrue(\"[test16]\" in str(context.exception))\n\n    def test_value_list_and_allowed_and_fully_outside_value_range(self):\n        # list as value and list allowed and list fully outside of value range\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_discrete_param(\n                [1, 2], \"[test17]\", value_range=(3, 13), tuple_to_uniform=True,\n                list_to_choice=True, allow_floats=True)\n        self.assertTrue(\"[test17]\" in str(context.exception))\n\n    def test_value_inside_value_range_given_as_callable(self):\n        # single value within value range given as callable\n        def _value_range(x):\n            return -1 < x < 1\n\n        result = iap.handle_discrete_param(\n            1, \"[test18]\",\n            value_range=_value_range,\n            tuple_to_uniform=True,\n            list_to_choice=True,\n            prefetch=False)\n        self.assertTrue(isinstance(result, iap.Deterministic))\n\n    def test_bad_datatype_as_value_range(self):\n        # bad datatype for value range\n        with self.assertRaises(Exception) as context:\n            _ = iap.handle_discrete_param(\n                1, \"[test19]\", value_range=False, tuple_to_uniform=True,\n                list_to_choice=True)\n        self.assertTrue(\n            \"Unexpected input for value_range\" in str(context.exception))\n\n\nclass Test_handle_categorical_string_param(unittest.TestCase):\n    def test_arg_is_all(self):\n        valid_values = [\"class1\", \"class2\"]\n\n        param = iap.handle_categorical_string_param(\n            ia.ALL, \"foo\", valid_values)\n\n        assert is_parameter_instance(param, iap.Choice)\n        assert param.a == valid_values\n\n    def test_arg_is_str(self):\n        param = iap.handle_categorical_string_param(\"class1\", \"foo\")\n        assert is_parameter_instance(param, iap.Deterministic)\n        assert param.value == \"class1\"\n\n    def test_arg_is_valid_str(self):\n        valid_values = [\"class1\", \"class2\"]\n\n        param = iap.handle_categorical_string_param(\n            \"class1\", \"foo\", valid_values)\n\n        assert is_parameter_instance(param, iap.Deterministic)\n        assert param.value == \"class1\"\n\n    def test_arg_is_invalid_str(self):\n        valid_values = [\"class1\", \"class2\"]\n\n        with self.assertRaises(AssertionError) as ctx:\n            _param = iap.handle_categorical_string_param(\n                \"class3\", \"foo\", valid_values)\n\n        expected = (\n            \"Expected parameter 'foo' to be one of: class1, class2. \"\n            \"Got: class3.\")\n        assert expected == str(ctx.exception)\n\n    def test_arg_is_list(self):\n        param = iap.handle_categorical_string_param([\"class1\", \"class3\"],\n                                                    \"foo\")\n\n        assert is_parameter_instance(param, iap.Choice)\n        assert param.a == [\"class1\", \"class3\"]\n\n    def test_arg_is_valid_list(self):\n        valid_values = [\"class1\", \"class2\", \"class3\"]\n\n        param = iap.handle_categorical_string_param(\n            [\"class1\", \"class3\"], \"foo\", valid_values)\n\n        assert is_parameter_instance(param, iap.Choice)\n        assert param.a == [\"class1\", \"class3\"]\n\n    def test_arg_is_list_with_invalid_types(self):\n        valid_values = [\"class1\", \"class2\", \"class3\"]\n\n        with self.assertRaises(AssertionError) as ctx:\n            _param = iap.handle_categorical_string_param(\n                [\"class1\", False], \"foo\", valid_values)\n\n        expected = (\n            \"Expected list provided for parameter 'foo' to only contain \"\n            \"strings, got types: str, bool.\"\n        )\n        assert expected in str(ctx.exception)\n\n    def test_arg_is_invalid_list(self):\n        valid_values = [\"class1\", \"class2\", \"class3\"]\n\n        with self.assertRaises(AssertionError) as ctx:\n            _param = iap.handle_categorical_string_param(\n                [\"class1\", \"class4\"], \"foo\", valid_values)\n\n        expected = (\n            \"Expected list provided for parameter 'foo' to only contain \"\n            \"the following allowed strings: class1, class2, class3. \"\n            \"Got strings: class1, class4.\"\n        )\n        assert expected in str(ctx.exception)\n\n    def test_arg_is_stochastic_param(self):\n        param = iap.Deterministic(\"class1\")\n\n        param_out = iap.handle_categorical_string_param(\n            param, \"foo\", [\"class1\"], prefetch=False)\n\n        assert param_out is param\n\n    def test_arg_is_invalid_datatype(self):\n        with self.assertRaises(Exception) as ctx:\n            _ = iap.handle_categorical_string_param(\n                False, \"foo\", [\"class1\"])\n\n        expected = \"Expected parameter 'foo' to be imgaug.ALL\"\n        assert expected in str(ctx.exception)\n\n\nclass Test_handle_probability_param(unittest.TestCase):\n    def test_bool_like_values(self):\n        for val in [True, False, 0, 1, 0.0, 1.0]:\n            with self.subTest(param=val):\n                p = iap.handle_probability_param(val, \"[test1]\", prefetch=False)\n                assert isinstance(p, iap.Deterministic)\n                assert p.value == int(val)\n\n    def test_float_probabilities(self):\n        for val in [0.0001, 0.001, 0.01, 0.1, 0.9, 0.99, 0.999, 0.9999]:\n            with self.subTest(param=val):\n                p = iap.handle_probability_param(val, \"[test2]\", prefetch=False)\n                assert is_parameter_instance(p, iap.Binomial)\n                assert is_parameter_instance(p.p, iap.Deterministic)\n                assert val-1e-8 < p.p.value < val+1e-8\n\n    def test_probability_is_stochastic_parameter(self):\n        det = iap.Deterministic(1)\n        p = iap.handle_probability_param(det, \"[test3]\", prefetch=False)\n        assert p == det\n\n    def test_probability_has_bad_datatype(self):\n        with self.assertRaises(Exception) as context:\n            _p = iap.handle_probability_param(\"test\", \"[test4]\")\n        self.assertTrue(\"Expected \" in str(context.exception))\n\n    def test_probability_is_negative(self):\n        with self.assertRaises(AssertionError):\n            _p = iap.handle_probability_param(-0.01, \"[test5]\")\n\n    def test_probability_is_above_100_percent(self):\n        with self.assertRaises(AssertionError):\n            _p = iap.handle_probability_param(1.01, \"[test6]\")\n\n\nclass Test_force_np_float_dtype(unittest.TestCase):\n    def test_common_dtypes(self):\n        dtypes = [\n            (\"float16\", \"float16\"),\n            (\"float32\", \"float32\"),\n            (\"float64\", \"float64\"),\n            (\"uint8\", \"float64\"),\n            (\"int32\", \"float64\")\n        ]\n        for dtype_in, expected in dtypes:\n            with self.subTest(dtype_in=dtype_in):\n                arr = np.zeros((1,), dtype=dtype_in)\n                observed = iap.force_np_float_dtype(arr).dtype\n                assert observed.name == expected\n\n\nclass Test_both_np_float_if_one_is_float(unittest.TestCase):\n    def test_float16_float32(self):\n        a1 = np.zeros((1,), dtype=np.float16)\n        b1 = np.zeros((1,), dtype=np.float32)\n        a2, b2 = iap.both_np_float_if_one_is_float(a1, b1)\n        assert a2.dtype.name == \"float16\"\n        assert b2.dtype.name == \"float32\"\n\n    def test_float16_int32(self):\n        a1 = np.zeros((1,), dtype=np.float16)\n        b1 = np.zeros((1,), dtype=np.int32)\n        a2, b2 = iap.both_np_float_if_one_is_float(a1, b1)\n        assert a2.dtype.name == \"float16\"\n        assert b2.dtype.name == \"float64\"\n\n    def test_int32_float16(self):\n        a1 = np.zeros((1,), dtype=np.int32)\n        b1 = np.zeros((1,), dtype=np.float16)\n        a2, b2 = iap.both_np_float_if_one_is_float(a1, b1)\n        assert a2.dtype.name == \"float64\"\n        assert b2.dtype.name == \"float16\"\n\n    def test_int32_uint8(self):\n        a1 = np.zeros((1,), dtype=np.int32)\n        b1 = np.zeros((1,), dtype=np.uint8)\n        a2, b2 = iap.both_np_float_if_one_is_float(a1, b1)\n        assert a2.dtype.name == \"float64\"\n        assert b2.dtype.name == \"float64\"\n\n\nclass Test_draw_distributions_grid(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_basic_functionality(self):\n        params = [mock.Mock(), mock.Mock()]\n        params[0].draw_distribution_graph.return_value = \\\n            np.zeros((1, 1, 3), dtype=np.uint8)\n        params[1].draw_distribution_graph.return_value = \\\n            np.zeros((1, 1, 3), dtype=np.uint8)\n\n        draw_grid_mock = mock.Mock()\n        draw_grid_mock.return_value = np.zeros((4, 3, 2), dtype=np.uint8)\n        with mock.patch('imgaug.imgaug.draw_grid', draw_grid_mock):\n            grid_observed = iap.draw_distributions_grid(\n                params, rows=2, cols=3, graph_sizes=(20, 21),\n                sample_sizes=[(1, 2), (3, 4)], titles=[\"A\", \"B\"])\n\n        assert grid_observed.shape == (4, 3, 2)\n        assert params[0].draw_distribution_graph.call_count == 1\n        assert params[1].draw_distribution_graph.call_count == 1\n        assert params[0].draw_distribution_graph.call_args[1][\"size\"] == (1, 2)\n        assert params[0].draw_distribution_graph.call_args[1][\"title\"] == \"A\"\n        assert params[1].draw_distribution_graph.call_args[1][\"size\"] == (3, 4)\n        assert params[1].draw_distribution_graph.call_args[1][\"title\"] == \"B\"\n        assert draw_grid_mock.call_count == 1\n        assert draw_grid_mock.call_args[0][0][0].shape == (20, 21, 3)\n        assert draw_grid_mock.call_args[0][0][1].shape == (20, 21, 3)\n        assert draw_grid_mock.call_args[1][\"rows\"] == 2\n        assert draw_grid_mock.call_args[1][\"cols\"] == 3\n\n\nclass Test_draw_distributions_graph(unittest.TestCase):\n    def test_basic_functionality(self):\n        # this test is very rough as we get a not-very-well-defined image out\n        # of the function\n        param = iap.Uniform(0.0, 1.0)\n\n        graph_img = param.draw_distribution_graph(title=None, size=(10000,),\n                                                  bins=100)\n\n        # at least 10% of the image should be white-ish (background)\n        nb_white = np.sum(graph_img[..., :] > [200, 200, 200])\n        nb_all = np.prod(graph_img.shape)\n\n        graph_img_title = param.draw_distribution_graph(title=\"test\",\n                                                        size=(10000,),\n                                                        bins=100)\n\n        assert graph_img.ndim == 3\n        assert graph_img.shape[2] == 3\n        assert nb_white > 0.1 * nb_all\n        assert graph_img_title.ndim == 3\n        assert graph_img_title.shape[2] == 3\n        assert not np.array_equal(graph_img_title, graph_img)\n\n\nclass TestStochasticParameter(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_copy(self):\n        other_param = iap.Uniform(1.0, 10.0)\n        param = iap.Discretize(other_param)\n        other_param.a = [1.0]\n        param_copy = param.copy()\n\n        param.other_param.a[0] += 1\n\n        assert isinstance(param_copy, iap.Discretize)\n        assert isinstance(param_copy.other_param, iap.Uniform)\n        assert param_copy.other_param.a[0] == param.other_param.a[0]\n\n    def test_deepcopy(self):\n        other_param = iap.Uniform(1.0, 10.0)\n        param = iap.Discretize(other_param)\n        other_param.a = [1.0]\n        param_copy = param.deepcopy()\n\n        param.other_param.a[0] += 1\n\n        assert isinstance(param_copy, iap.Discretize)\n        assert isinstance(param_copy.other_param, iap.Uniform)\n        assert param_copy.other_param.a[0] != param.other_param.a[0]\n\n\nclass TestStochasticParameterOperators(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_multiply_stochasic_params(self):\n        param1 = iap.Normal(0, 1)\n        param2 = iap.Uniform(-1.0, 1.0)\n\n        param3 = param1 * param2\n\n        assert isinstance(param3, iap.Multiply)\n        assert param3.other_param == param1\n        assert param3.val == param2\n\n    def test_multiply_stochastic_param_with_integer(self):\n        param1 = iap.Normal(0, 1)\n\n        param3 = param1 * 2\n\n        assert isinstance(param3, iap.Multiply)\n        assert param3.other_param == param1\n        assert isinstance(param3.val, iap.Deterministic)\n        assert param3.val.value == 2\n\n    def test_multiply_integer_with_stochastic_param(self):\n        param1 = iap.Normal(0, 1)\n\n        param3 = 2 * param1\n\n        assert isinstance(param3, iap.Multiply)\n        assert isinstance(param3.other_param, iap.Deterministic)\n        assert param3.other_param.value == 2\n        assert param3.val == param1\n\n    def test_multiply_string_with_stochastic_param_should_fail(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = \"test\" * param1\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_multiply_stochastic_param_with_string_should_fail(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = param1 * \"test\"\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_divide_stochastic_params(self):\n        # Divide (__truediv__)\n        param1 = iap.Normal(0, 1)\n        param2 = iap.Uniform(-1.0, 1.0)\n\n        param3 = param1 / param2\n\n        assert isinstance(param3, iap.Divide)\n        assert param3.other_param == param1\n        assert param3.val == param2\n\n    def test_divide_stochastic_param_by_integer(self):\n        param1 = iap.Normal(0, 1)\n\n        param3 = param1 / 2\n\n        assert isinstance(param3, iap.Divide)\n        assert param3.other_param == param1\n        assert isinstance(param3.val, iap.Deterministic)\n        assert param3.val.value == 2\n\n    def test_divide_integer_by_stochastic_param(self):\n        param1 = iap.Normal(0, 1)\n\n        param3 = 2 / param1\n\n        assert isinstance(param3, iap.Divide)\n        assert isinstance(param3.other_param, iap.Deterministic)\n        assert param3.other_param.value == 2\n        assert param3.val == param1\n\n    def test_divide_string_by_stochastic_param_should_fail(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = \"test\" / param1\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_divide_stochastic_param_by_string_should_fail(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = param1 / \"test\"\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_div_stochastic_params(self):\n        # Divide (__div__)\n        param1 = iap.Normal(0, 1)\n        param2 = iap.Uniform(-1.0, 1.0)\n\n        param3 = param1.__div__(param2)\n\n        assert isinstance(param3, iap.Divide)\n        assert param3.other_param == param1\n        assert param3.val == param2\n\n    def test_div_stochastic_param_by_integer(self):\n        param1 = iap.Normal(0, 1)\n\n        param3 = param1.__div__(2)\n\n        assert isinstance(param3, iap.Divide)\n        assert param3.other_param == param1\n        assert isinstance(param3.val, iap.Deterministic)\n        assert param3.val.value == 2\n\n    def test_div_stochastic_param_by_string_should_fail(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = param1.__div__(\"test\")\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_rdiv_stochastic_param_by_integer(self):\n        # Divide (__rdiv__)\n        param1 = iap.Normal(0, 1)\n\n        param3 = param1.__rdiv__(2)\n\n        assert isinstance(param3, iap.Divide)\n        assert isinstance(param3.other_param, iap.Deterministic)\n        assert param3.other_param.value == 2\n        assert param3.val == param1\n\n    def test_rdiv_stochastic_param_by_string_should_fail(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = param1.__rdiv__(\"test\")\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_floordiv_stochastic_params(self):\n        # Divide (__floordiv__)\n        param1_int = iap.DiscreteUniform(0, 10)\n        param2_int = iap.Choice([1, 2])\n\n        param3 = param1_int // param2_int\n\n        assert isinstance(param3, iap.Discretize)\n        assert isinstance(param3.other_param, iap.Divide)\n        assert param3.other_param.other_param == param1_int\n        assert param3.other_param.val == param2_int\n\n    def test_floordiv_symbol_stochastic_param_by_integer(self):\n        param1_int = iap.DiscreteUniform(0, 10)\n\n        param3 = param1_int // 2\n\n        assert isinstance(param3, iap.Discretize)\n        assert isinstance(param3.other_param, iap.Divide)\n        assert param3.other_param.other_param == param1_int\n        assert isinstance(param3.other_param.val, iap.Deterministic)\n        assert param3.other_param.val.value == 2\n\n    def test_floordiv_symbol_integer_by_stochastic_param(self):\n        param1_int = iap.DiscreteUniform(0, 10)\n\n        param3 = 2 // param1_int\n\n        assert isinstance(param3, iap.Discretize)\n        assert isinstance(param3.other_param, iap.Divide)\n        assert isinstance(param3.other_param.other_param, iap.Deterministic)\n        assert param3.other_param.other_param.value == 2\n        assert param3.other_param.val == param1_int\n\n    def test_floordiv_symbol_string_by_stochastic_should_fail(self):\n        param1_int = iap.DiscreteUniform(0, 10)\n\n        with self.assertRaises(Exception) as context:\n            _ = \"test\" // param1_int\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_floordiv_symbol_stochastic_param_by_string_should_fail(self):\n        param1_int = iap.DiscreteUniform(0, 10)\n\n        with self.assertRaises(Exception) as context:\n            _ = param1_int // \"test\"\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_add_stochastic_params(self):\n        param1 = iap.Normal(0, 1)\n        param2 = iap.Uniform(-1.0, 1.0)\n\n        param3 = param1 + param2\n\n        assert isinstance(param3, iap.Add)\n        assert param3.other_param == param1\n        assert param3.val == param2\n\n    def test_add_integer_to_stochastic_param(self):\n        param1 = iap.Normal(0, 1)\n\n        param3 = param1 + 2\n\n        assert isinstance(param3, iap.Add)\n        assert param3.other_param == param1\n        assert isinstance(param3.val, iap.Deterministic)\n        assert param3.val.value == 2\n\n    def test_add_stochastic_param_to_integer(self):\n        param1 = iap.Normal(0, 1)\n\n        param3 = 2 + param1\n\n        assert isinstance(param3, iap.Add)\n        assert isinstance(param3.other_param, iap.Deterministic)\n        assert param3.other_param.value == 2\n        assert param3.val == param1\n\n    def test_add_stochastic_param_to_string(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = \"test\" + param1\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_add_string_to_stochastic_param(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = param1 + \"test\"\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_subtract_stochastic_params(self):\n        param1 = iap.Normal(0, 1)\n        param2 = iap.Uniform(-1.0, 1.0)\n\n        param3 = param1 - param2\n\n        assert isinstance(param3, iap.Subtract)\n        assert param3.other_param == param1\n        assert param3.val == param2\n\n    def test_subtract_integer_from_stochastic_param(self):\n        param1 = iap.Normal(0, 1)\n\n        param3 = param1 - 2\n\n        assert isinstance(param3, iap.Subtract)\n        assert param3.other_param == param1\n        assert isinstance(param3.val, iap.Deterministic)\n        assert param3.val.value == 2\n\n    def test_subtract_stochastic_param_from_integer(self):\n        param1 = iap.Normal(0, 1)\n\n        param3 = 2 - param1\n\n        assert isinstance(param3, iap.Subtract)\n        assert isinstance(param3.other_param, iap.Deterministic)\n        assert param3.other_param.value == 2\n        assert param3.val == param1\n\n    def test_subtract_stochastic_param_from_string_should_fail(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = \"test\" - param1\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_subtract_string_from_stochastic_param_should_fail(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = param1 - \"test\"\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_exponentiate_stochastic_params(self):\n        param1 = iap.Normal(0, 1)\n        param2 = iap.Uniform(-1.0, 1.0)\n\n        param3 = param1 ** param2\n\n        assert isinstance(param3, iap.Power)\n        assert param3.other_param == param1\n        assert param3.val == param2\n\n    def test_exponentiate_stochastic_param_by_integer(self):\n        param1 = iap.Normal(0, 1)\n\n        param3 = param1 ** 2\n\n        assert isinstance(param3, iap.Power)\n        assert param3.other_param == param1\n        assert isinstance(param3.val, iap.Deterministic)\n        assert param3.val.value == 2\n\n    def test_exponentiate_integer_by_stochastic_param(self):\n        param1 = iap.Normal(0, 1)\n\n        param3 = 2 ** param1\n\n        assert isinstance(param3, iap.Power)\n        assert isinstance(param3.other_param, iap.Deterministic)\n        assert param3.other_param.value == 2\n        assert param3.val == param1\n\n    def test_exponentiate_string_by_stochastic_param(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = \"test\" ** param1\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n    def test_exponentiate_stochastic_param_by_string(self):\n        param1 = iap.Normal(0, 1)\n\n        with self.assertRaises(Exception) as context:\n            _ = param1 ** \"test\"\n\n        self.assertTrue(\"Invalid datatypes\" in str(context.exception))\n\n\nclass TestAutoPrefetcher(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_does_not_prefetch_at_first_call(self):\n        other_param = mock.Mock()\n        other_param.draw_samples.return_value = np.zeros((100,), dtype=np.uint8)\n        param = iap.AutoPrefetcher(other_param, 10)\n        rng = iarandom.RNG(0)\n\n        _samples = param.draw_samples((1,), rng)\n\n        # rng is currently not identical in call args,\n        # because draw_samples() creates a new one with same state\n        assert other_param.draw_samples.call_count == 1\n        assert other_param.draw_samples.call_args_list[0][0][0] == (1,)\n        assert other_param.draw_samples.call_args_list[0][0][1].equals(rng)\n        assert param.samples is None\n        assert param.index == 0\n        assert param.last_rng_idx == rng._idx\n\n    def test_prefetches_at_second_call(self):\n        other_param = mock.Mock()\n        other_param.draw_samples.return_value = np.zeros((100,), dtype=np.uint8)\n        param = iap.AutoPrefetcher(other_param, 10)\n        rng = iarandom.RNG(0)\n\n        _samples = param.draw_samples((1,), rng)\n        _samples = param.draw_samples((1,), rng)\n\n        # rng is currently not identical in call args,\n        # because draw_samples() creates a new one with same state\n        assert other_param.draw_samples.call_count == 2\n        assert other_param.draw_samples.call_args_list[0][0][0] == (1,)\n        assert other_param.draw_samples.call_args_list[0][0][1].equals(rng)\n        assert other_param.draw_samples.call_args_list[1][0][0] == (10,)\n        assert other_param.draw_samples.call_args_list[1][0][1].equals(rng)\n        # (100,) because that's what the mock always returns\n        assert param.samples.shape == (100,)\n        assert param.index == 1\n        assert param.last_rng_idx == rng._idx\n\n    def test_nb_prefetch_is_evenly_divisible_by_requested_sizes(self):\n        other_param = iap.DeterministicList(np.arange(200))\n        param = iap.AutoPrefetcher(other_param, 100)\n        rng = iarandom.RNG(0)\n\n        samples1 = param.draw_samples((50,), rng)\n        samples2 = param.draw_samples((50,), rng)\n        samples3 = param.draw_samples((50,), rng)\n        samples4 = param.draw_samples((50,), rng)\n\n        # first call is not prefetched, second+ is, so first and second are\n        # here identical\n        assert np.array_equal(samples1, np.arange(50))\n        assert np.array_equal(samples2, np.arange(50))\n        assert np.array_equal(samples3, 50 + np.arange(50))\n        assert np.array_equal(samples4, np.arange(50))\n\n    def test_nb_prefetch_is_not_evenly_divisible_by_requested_sizes(self):\n        other_param = iap.DeterministicList(np.arange(200))\n        param = iap.AutoPrefetcher(other_param, 100)\n        rng = iarandom.RNG(0)\n\n        samples1 = param.draw_samples((40,), rng)\n        samples2 = param.draw_samples((40,), rng)\n        samples3 = param.draw_samples((40,), rng)\n        samples4 = param.draw_samples((40,), rng)\n\n        # first call is not prefetched, second+ is, so first and second are\n        # here identical\n        assert np.array_equal(samples1, np.arange(40))\n        assert np.array_equal(samples2, np.arange(40))\n        assert np.array_equal(samples3, 40 + np.arange(40))\n        assert np.array_equal(\n            samples4,\n            np.concatenate([\n                80 + np.arange(20),\n                np.arange(20)\n            ], axis=0)\n        )\n\n    def test_exactly_as_many_components_requested_as_nb_prefetch_allows(self):\n        other_param = iap.DeterministicList(np.arange(200))\n        param = iap.AutoPrefetcher(other_param, 40)\n        rng = iarandom.RNG(0)\n\n        samples1 = param.draw_samples((40,), rng)\n        samples2 = param.draw_samples((40,), rng)\n        samples3 = param.draw_samples((40,), rng)\n\n        assert np.array_equal(samples1, np.arange(40))\n        assert np.array_equal(samples2, np.arange(40))\n        assert np.array_equal(samples3, np.arange(40))\n\n    def test_more_components_requested_than_nb_prefetch_allows(self):\n        other_param = iap.DeterministicList(np.arange(200))\n        param = iap.AutoPrefetcher(other_param, 10)\n        rng = iarandom.RNG(0)\n\n        samples1 = param.draw_samples((40,), rng)\n        samples2 = param.draw_samples((40,), rng)\n        samples3 = param.draw_samples((40,), rng)\n\n        assert np.array_equal(samples1, np.arange(40))\n        assert np.array_equal(samples2, np.arange(40))\n        assert np.array_equal(samples3, np.arange(40))\n\n    def test_size_is_tuple(self):\n        other_param = iap.DeterministicList(np.arange(200))\n        param = iap.AutoPrefetcher(other_param, 50)\n        rng = iarandom.RNG(0)\n\n        samples1 = param.draw_samples((2, 3, 4), rng)  # 24 samples\n        samples2 = param.draw_samples((1, 5, 2), rng)  # 10 samples\n        samples3 = param.draw_samples((10, 2), rng)  # 20 samples\n\n        assert np.array_equal(samples1, np.arange(2*3*4).reshape((2, 3, 4)))\n        assert np.array_equal(samples2, np.arange(1*5*2).reshape((1, 5, 2)))\n        assert np.array_equal(samples3,\n                              (1*5*2) + np.arange(10*2).reshape((10, 2)))\n\n    def test_to_string_first_call(self):\n        other_param = iap.DeterministicList(np.arange(200))\n        param = iap.AutoPrefetcher(other_param, 10)\n        other_param_str = str(other_param)\n\n        expected = (\n            \"AutoPrefetcher(\"\n            \"nb_prefetch=10, \"\n            \"samples=None (dtype None), \"\n            \"index=0, \"\n            \"last_rng_idx=None, \"\n            \"other_param=%s\"\n            \")\" % (other_param_str,)\n        )\n        assert str(param) == repr(param) == expected\n\n    def test_to_string_second_call(self):\n        # use astype(int64) here, because otherwise in windows the array\n        # seems to become int32, causing the assertion below to fail\n        other_param = iap.DeterministicList(np.arange(200).astype(np.int64))\n        param = iap.AutoPrefetcher(other_param, 10)\n        other_param_str = str(other_param)\n\n        rng = iarandom.RNG(0)\n\n        _ = param.draw_samples((2,), rng)\n        _ = param.draw_samples((2,), rng)\n\n        expected = (\n            \"AutoPrefetcher(\"\n            \"nb_prefetch=10, \"\n            \"samples=(10,) (dtype int64), \"\n            \"index=2, \"\n            \"last_rng_idx=%d, \"\n            \"other_param=%s\"\n            \")\" % (rng._idx, other_param_str)\n        )\n        assert str(param) == repr(param) == expected\n\n\nclass TestBinomial(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___p_is_zero(self):\n        param = iap.Binomial(0)\n        expected = \"Binomial(%s)\" % (str(param.p),)\n        assert \"Deterministic(int 0)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test___init___p_is_one(self):\n        param = iap.Binomial(1.0)\n        expected = \"Binomial(%s)\" % (str(param.p),)\n        assert \"Deterministic(float 1.00000000)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_p_is_zero(self):\n        param = iap.Binomial(0)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample == 0\n        assert np.all(samples == 0)\n\n    def test_p_is_one(self):\n        param = iap.Binomial(1.0)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample == 1\n        assert np.all(samples == 1)\n\n    def test_p_is_50_percent(self):\n        param = iap.Binomial(0.5)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10000,))\n        unique, counts = np.unique(samples, return_counts=True)\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10000,)\n        assert sample in [0, 1]\n        assert len(unique) == 2\n        for val, count in zip(unique, counts):\n            if val == 0:\n                assert 5000 - 500 < count < 5000 + 500\n            elif val == 1:\n                assert 5000 - 500 < count < 5000 + 500\n            else:\n                assert False\n\n    def test_p_is_list(self):\n        param = iap.Binomial(iap.Choice([0.25, 0.75]))\n        for _ in sm.xrange(10):\n            samples = param.draw_samples((1000,))\n            p = np.sum(samples) / samples.size\n            assert (\n                (0.25 - 0.05 < p < 0.25 + 0.05)\n                or (0.75 - 0.05 < p < 0.75 + 0.05)\n            )\n\n    def test_p_is_tuple(self):\n        param = iap.Binomial((0.0, 1.0))\n\n        last_p = 0.5\n        diffs = []\n        for _ in sm.xrange(30):\n            samples = param.draw_samples((1000,))\n            p = np.sum(samples).astype(np.float32) / samples.size\n            diffs.append(abs(p - last_p))\n            last_p = p\n        nb_p_changed = sum([diff > 0.05 for diff in diffs])\n\n        assert nb_p_changed > 15\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.Binomial(0.5)\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.array_equal(samples1, samples2)\n\n\nclass TestChoice(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Choice([0, 1, 2])\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == \"Choice(a=[0, 1, 2], replace=True, p=None)\"\n        )\n\n    def test_value_is_list(self):\n        param = iap.Choice([0, 1, 2])\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample in [0, 1, 2]\n        assert np.all(\n            np.logical_or(\n                np.logical_or(samples == 0, samples == 1),\n                samples == 2\n            )\n        )\n\n    def test_sampled_values_match_expected_counts(self):\n        param = iap.Choice([0, 1, 2])\n\n        samples = param.draw_samples((10000,))\n        expected = 10000/3\n        expected_tolerance = expected * 0.05\n        for v in [0, 1, 2]:\n            count = np.sum(samples == v)\n            assert (\n                expected - expected_tolerance\n                < count <\n                expected + expected_tolerance\n            )\n\n    def test_value_is_list_containing_negative_number(self):\n        param = iap.Choice([-1, 1])\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample in [-1, 1]\n        assert np.all(np.logical_or(samples == -1, samples == 1))\n\n    def test_value_is_list_of_floats(self):\n        param = iap.Choice([-1.2, 1.7])\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert (\n            (\n                -1.2 - _eps(sample)\n                < sample <\n                -1.2 + _eps(sample)\n            )\n            or\n            (\n                1.7 - _eps(sample)\n                < sample <\n                1.7 + _eps(sample)\n            )\n        )\n        assert np.all(\n            np.logical_or(\n                np.logical_and(\n                    -1.2 - _eps(sample) < samples,\n                    samples < -1.2 + _eps(sample)\n                ),\n                np.logical_and(\n                    1.7 - _eps(sample) < samples,\n                    samples < 1.7 + _eps(sample)\n                )\n            )\n        )\n\n    def test_value_is_list_of_strings(self):\n        param = iap.Choice([\"first\", \"second\", \"third\"])\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample in [\"first\", \"second\", \"third\"]\n        assert np.all(\n            np.logical_or(\n                np.logical_or(\n                    samples == \"first\",\n                    samples == \"second\"\n                ),\n                samples == \"third\"\n            )\n        )\n\n    def test_sample_without_replacing(self):\n        param = iap.Choice([1+i for i in sm.xrange(100)], replace=False)\n\n        samples = param.draw_samples((50,))\n        seen = [0 for _ in sm.xrange(100)]\n        for sample in samples:\n            seen[sample-1] += 1\n\n        assert all([count in [0, 1] for count in seen])\n\n    def test_non_uniform_probabilities_over_elements(self):\n        param = iap.Choice([0, 1], p=[0.25, 0.75])\n\n        samples = param.draw_samples((10000,))\n        unique, counts = np.unique(samples, return_counts=True)\n\n        assert len(unique) == 2\n        for val, count in zip(unique, counts):\n            if val == 0:\n                assert 2500 - 500 < count < 2500 + 500\n            elif val == 1:\n                assert 7500 - 500 < count < 7500 + 500\n            else:\n                assert False\n\n    def test_list_contains_stochastic_parameter(self):\n        param = iap.Choice([iap.Choice([0, 1]), 2])\n\n        samples = param.draw_samples((10000,))\n        unique, counts = np.unique(samples, return_counts=True)\n\n        assert len(unique) == 3\n        for val, count in zip(unique, counts):\n            if val in [0, 1]:\n                assert 2500 - 500 < count < 2500 + 500\n            elif val == 2:\n                assert 5000 - 500 < count < 5000 + 500\n            else:\n                assert False\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.Choice([-1, 0, 1, 2, 3])\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.array_equal(samples1, samples2)\n\n    def test_value_is_bad_datatype(self):\n        with self.assertRaises(Exception) as context:\n            _ = iap.Choice(123)\n\n        self.assertTrue(\n            \"Expected a to be an iterable\" in str(context.exception))\n\n    def test_p_is_bad_datatype(self):\n        with self.assertRaises(Exception) as context:\n            _ = iap.Choice([1, 2], p=123)\n\n        self.assertTrue(\"Expected p to be\" in str(context.exception))\n\n    def test_value_and_p_have_unequal_lengths(self):\n        with self.assertRaises(Exception) as context:\n            _ = iap.Choice([1, 2], p=[1])\n\n        self.assertTrue(\"Expected lengths of\" in str(context.exception))\n\n\nclass TestDiscreteUniform(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.DiscreteUniform(0, 2)\n        expected = \"DiscreteUniform(%s, %s)\" % (str(param.a), str(param.b))\n        assert \"Deterministic(int 0)\" in str(param)\n        assert \"Deterministic(int 2)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_bounds_are_ints(self):\n        param = iap.DiscreteUniform(0, 2)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample in [0, 1, 2]\n        assert np.all(\n            np.logical_or(\n                np.logical_or(samples == 0, samples == 1),\n                samples == 2\n            )\n        )\n\n    def test_samples_match_expected_counts(self):\n        param = iap.DiscreteUniform(0, 2)\n\n        samples = param.draw_samples((10000,))\n        expected = 10000/3\n        expected_tolerance = expected * 0.05\n        for v in [0, 1, 2]:\n            count = np.sum(samples == v)\n            assert (\n                expected - expected_tolerance\n                < count <\n                expected + expected_tolerance\n            )\n\n    def test_lower_bound_is_negative(self):\n        param = iap.DiscreteUniform(-1, 1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample in [-1, 0, 1]\n        assert np.all(\n            np.logical_or(\n                np.logical_or(samples == -1, samples == 0),\n                samples == 1\n            )\n        )\n\n    def test_bounds_are_floats(self):\n        param = iap.DiscreteUniform(-1.2, 1.2)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample in [-1, 0, 1]\n        assert np.all(\n            np.logical_or(\n                np.logical_or(\n                    samples == -1, samples == 0\n                ),\n                samples == 1\n            )\n        )\n\n    def test_lower_and_upper_bound_have_wrong_order(self):\n        param = iap.DiscreteUniform(1, -1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample in [-1, 0, 1]\n        assert np.all(\n            np.logical_or(\n                np.logical_or(\n                    samples == -1, samples == 0\n                ),\n                samples == 1\n            )\n        )\n\n    def test_lower_and_upper_bound_are_the_same(self):\n        param = iap.DiscreteUniform(1, 1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((100,))\n\n        assert sample == 1\n        assert np.all(samples == 1)\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.Uniform(-1, 1)\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.array_equal(samples1, samples2)\n\n\nclass TestPoisson(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Poisson(1)\n        expected = \"Poisson(%s)\" % (str(param.lam),)\n        assert \"Deterministic(int 1)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_draw_sample(self):\n        param = iap.Poisson(1)\n\n        sample = param.draw_sample()\n\n        assert sample.shape == tuple()\n        assert 0 <= sample\n\n    def test_via_comparison_to_np_poisson(self):\n        param = iap.Poisson(1)\n\n        samples = param.draw_samples((100, 1000))\n        samples_direct = iarandom.RNG(1234).poisson(\n            lam=1, size=(100, 1000))\n        assert samples.shape == (100, 1000)\n\n        for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:\n            count_direct = int(np.sum(samples_direct == i))\n            count = np.sum(samples == i)\n            tolerance = max(count_direct * 0.1, 250)\n            assert count_direct - tolerance < count < count_direct + tolerance\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.Poisson(1)\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.array_equal(samples1, samples2)\n\n\nclass TestNormal(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Normal(0, 1)\n        assert \"Deterministic(int 0)\" in str(param)\n        assert \"Deterministic(int 1)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == \"Normal(loc=%s, scale=%s)\" % (str(param.loc), str(param.scale))\n        )\n\n    def test_draw_sample(self):\n        param = iap.Normal(0, 1)\n\n        sample = param.draw_sample()\n\n        assert sample.shape == tuple()\n\n    def test_via_comparison_to_np_normal(self):\n        param = iap.Normal(0, 1)\n\n        samples = param.draw_samples((100, 1000))\n        samples_direct = iarandom.RNG(1234).normal(loc=0, scale=1,\n                                                            size=(100, 1000))\n        samples = np.clip(samples, -1, 1)\n        samples_direct = np.clip(samples_direct, -1, 1)\n        nb_bins = 10\n        hist, _ = np.histogram(samples, bins=nb_bins, range=(-1.0, 1.0),\n                               density=False)\n        hist_direct, _ = np.histogram(samples_direct, bins=nb_bins,\n                                      range=(-1.0, 1.0), density=False)\n        tolerance = 0.05\n        for nb_samples, nb_samples_direct in zip(hist, hist_direct):\n            density = nb_samples / samples.size\n            density_direct = nb_samples_direct / samples_direct.size\n            assert (\n                density_direct - tolerance\n                < density <\n                density_direct + tolerance\n            )\n\n    def test_loc_is_stochastic_parameter(self):\n        param = iap.Normal(iap.Choice([-100, 100]), 1)\n\n        seen = [0, 0]\n        for _ in sm.xrange(1000):\n            samples = param.draw_samples((100,))\n            exp = np.mean(samples)\n\n            if -100 - 10 < exp < -100 + 10:\n                seen[0] += 1\n            elif 100 - 10 < exp < 100 + 10:\n                seen[1] += 1\n            else:\n                assert False\n        assert 500 - 100 < seen[0] < 500 + 100\n        assert 500 - 100 < seen[1] < 500 + 100\n\n    def test_scale(self):\n        param1 = iap.Normal(0, 1)\n        param2 = iap.Normal(0, 100)\n\n        samples1 = param1.draw_samples((1000,))\n        samples2 = param2.draw_samples((1000,))\n\n        assert np.std(samples1) < np.std(samples2)\n        assert 100 - 10 < np.std(samples2) < 100 + 10\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.Normal(0, 1)\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.allclose(samples1, samples2)\n\n\nclass TestTruncatedNormal(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.TruncatedNormal(0, 1)\n        expected = (\n            \"TruncatedNormal(\"\n            \"loc=%s, \"\n            \"scale=%s, \"\n            \"low=%s, \"\n            \"high=%s\"\n            \")\" % (\n                str(param.loc),\n                str(param.scale),\n                str(param.low),\n                str(param.high)\n            )\n        )\n        assert \"Deterministic(int 0)\" in str(param)\n        assert \"Deterministic(int 1)\" in str(param)\n        assert \"Deterministic(float -inf)\" in str(param)\n        assert \"Deterministic(float inf)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test___init___custom_range(self):\n        param = iap.TruncatedNormal(0, 1, low=-100, high=50.0)\n        expected = (\n            \"TruncatedNormal(\"\n            \"loc=%s, \"\n            \"scale=%s, \"\n            \"low=%s, \"\n            \"high=%s\"\n            \")\" % (\n                str(param.loc),\n                str(param.scale),\n                str(param.low),\n                str(param.high)\n            )\n        )\n        assert \"Deterministic(int 0)\" in str(param)\n        assert \"Deterministic(int 1)\" in str(param)\n        assert \"Deterministic(int -100)\" in str(param)\n        assert \"Deterministic(float 50.00000000)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_scale_is_zero(self):\n        param = iap.TruncatedNormal(0.5, 0, low=-10, high=10)\n        samples = param.draw_samples((100,))\n        assert np.allclose(samples, 0.5)\n\n    def test_scale(self):\n        param1 = iap.TruncatedNormal(0.0, 0.1, low=-100, high=100)\n        param2 = iap.TruncatedNormal(0.0, 5.0, low=-100, high=100)\n        samples1 = param1.draw_samples((1000,))\n        samples2 = param2.draw_samples((1000,))\n        assert np.std(samples1) < np.std(samples2)\n        assert np.isclose(np.std(samples1), 0.1, rtol=0, atol=0.20)\n        assert np.isclose(np.std(samples2), 5.0, rtol=0, atol=0.40)\n\n    def test_loc_is_stochastic_parameter(self):\n        param = iap.TruncatedNormal(iap.Choice([-100, 100]), 0.01,\n                                    low=-1000, high=1000)\n\n        seen = [0, 0]\n        for _ in sm.xrange(200):\n            samples = param.draw_samples((5,))\n            observed = np.mean(samples)\n\n            dist1 = np.abs(-100 - observed)\n            dist2 = np.abs(100 - observed)\n\n            if dist1 < 1:\n                seen[0] += 1\n            elif dist2 < 1:\n                seen[1] += 1\n            else:\n                assert False\n        assert np.isclose(seen[0], 100, rtol=0, atol=20)\n        assert np.isclose(seen[1], 100, rtol=0, atol=20)\n\n    def test_samples_are_within_bounds(self):\n        param = iap.TruncatedNormal(0, 10.0, low=-5, high=7.5)\n\n        samples = param.draw_samples((1000,))\n\n        # are all within bounds\n        assert np.all(samples >= -5.0 - 1e-4)\n        assert np.all(samples <= 7.5 + 1e-4)\n\n        # at least some samples close to bounds\n        assert np.any(samples <= -4.5)\n        assert np.any(samples >= 7.0)\n\n        # at least some samples close to loc\n        assert np.any(np.abs(samples) < 0.5)\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.TruncatedNormal(0, 1)\n\n        samples1 = param.draw_samples((10, 5), random_state=1234)\n        samples2 = param.draw_samples((10, 5), random_state=1234)\n\n        assert np.allclose(samples1, samples2)\n\n    def test_samples_different_values_for_different_seeds(self):\n        param = iap.TruncatedNormal(0, 1)\n\n        samples1 = param.draw_samples((10, 5), random_state=1234)\n        samples2 = param.draw_samples((10, 5), random_state=2345)\n\n        assert not np.allclose(samples1, samples2)\n\n\nclass TestLaplace(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Laplace(0, 1)\n        expected = \"Laplace(loc=%s, scale=%s)\" % (\n            str(param.loc),\n            str(param.scale)\n        )\n        assert \"Deterministic(int 0)\" in str(param)\n        assert \"Deterministic(int 1)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_draw_sample(self):\n        param = iap.Laplace(0, 1)\n\n        sample = param.draw_sample()\n\n        assert sample.shape == tuple()\n\n    def test_via_comparison_to_np_laplace(self):\n        param = iap.Laplace(0, 1)\n\n        samples = param.draw_samples((100, 1000))\n        samples_direct = iarandom.RNG(1234).laplace(loc=0, scale=1,\n                                                             size=(100, 1000))\n\n        assert samples.shape == (100, 1000)\n\n        samples = np.clip(samples, -1, 1)\n        samples_direct = np.clip(samples_direct, -1, 1)\n        nb_bins = 10\n        hist, _ = np.histogram(samples, bins=nb_bins, range=(-1.0, 1.0),\n                               density=False)\n        hist_direct, _ = np.histogram(samples_direct, bins=nb_bins,\n                                      range=(-1.0, 1.0), density=False)\n        tolerance = 0.05\n        for nb_samples, nb_samples_direct in zip(hist, hist_direct):\n            density = nb_samples / samples.size\n            density_direct = nb_samples_direct / samples_direct.size\n            assert (\n                density_direct - tolerance\n                < density <\n                density_direct + tolerance\n            )\n\n    def test_loc_is_stochastic_parameter(self):\n        param = iap.Laplace(iap.Choice([-100, 100]), 1)\n\n        seen = [0, 0]\n        for _ in sm.xrange(1000):\n            samples = param.draw_samples((100,))\n            exp = np.mean(samples)\n\n            if -100 - 10 < exp < -100 + 10:\n                seen[0] += 1\n            elif 100 - 10 < exp < 100 + 10:\n                seen[1] += 1\n            else:\n                assert False\n\n        assert 500 - 100 < seen[0] < 500 + 100\n        assert 500 - 100 < seen[1] < 500 + 100\n\n    def test_scale(self):\n        param1 = iap.Laplace(0, 1)\n        param2 = iap.Laplace(0, 100)\n\n        samples1 = param1.draw_samples((1000,))\n        samples2 = param2.draw_samples((1000,))\n\n        assert np.var(samples1) < np.var(samples2)\n\n    def test_scale_is_zero(self):\n        param1 = iap.Laplace(1, 0)\n\n        samples = param1.draw_samples((100,))\n\n        assert np.all(np.logical_and(\n            samples > 1 - _eps(samples),\n            samples < 1 + _eps(samples)\n        ))\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.Laplace(0, 1)\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.allclose(samples1, samples2)\n\n\nclass TestChiSquare(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.ChiSquare(1)\n        expected = \"ChiSquare(df=%s)\" % (str(param.df),)\n        assert \"Deterministic(int 1)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_draw_sample(self):\n        param = iap.ChiSquare(1)\n\n        sample = param.draw_sample()\n\n        assert sample.shape == tuple()\n        assert 0 <= sample\n\n    def test_via_comparison_to_np_chisquare(self):\n        param = iap.ChiSquare(1)\n\n        samples = param.draw_samples((100, 1000))\n        samples_direct = iarandom.RNG(1234).chisquare(df=1,\n                                                               size=(100, 1000))\n\n        assert samples.shape == (100, 1000)\n        assert np.all(0 <= samples)\n\n        samples = np.clip(samples, 0, 3)\n        samples_direct = np.clip(samples_direct, 0, 3)\n        nb_bins = 10\n        hist, _ = np.histogram(samples, bins=nb_bins, range=(0, 3.0),\n                               density=False)\n        hist_direct, _ = np.histogram(samples_direct, bins=nb_bins,\n                                      range=(0, 3.0), density=False)\n        tolerance = 0.05\n        for nb_samples, nb_samples_direct in zip(hist, hist_direct):\n            density = nb_samples / samples.size\n            density_direct = nb_samples_direct / samples_direct.size\n            assert (\n                density_direct - tolerance\n                < density <\n                density_direct + tolerance\n            )\n\n    def test_df_is_stochastic_parameter(self):\n        param = iap.ChiSquare(iap.Choice([1, 10]))\n\n        seen = [0, 0]\n        for _ in sm.xrange(1000):\n            samples = param.draw_samples((100,))\n            exp = np.mean(samples)\n\n            if 1 - 1.0 < exp < 1 + 1.0:\n                seen[0] += 1\n            elif 10 - 4.0 < exp < 10 + 4.0:\n                seen[1] += 1\n            else:\n                assert False\n\n        assert 500 - 100 < seen[0] < 500 + 100\n        assert 500 - 100 < seen[1] < 500 + 100\n\n    def test_larger_df_leads_to_more_variance(self):\n        param1 = iap.ChiSquare(1)\n        param2 = iap.ChiSquare(10)\n\n        samples1 = param1.draw_samples((1000,))\n        samples2 = param2.draw_samples((1000,))\n\n        assert np.var(samples1) < np.var(samples2)\n        assert 2*1 - 1.0 < np.var(samples1) < 2*1 + 1.0\n        assert 2*10 - 5.0 < np.var(samples2) < 2*10 + 5.0\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.ChiSquare(1)\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.allclose(samples1, samples2)\n\n\nclass TestWeibull(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Weibull(1)\n        expected = \"Weibull(a=%s)\" % (str(param.a),)\n        assert \"Deterministic(int 1)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_draw_sample(self):\n        param = iap.Weibull(1)\n\n        sample = param.draw_sample()\n\n        assert sample.shape == tuple()\n        assert 0 <= sample\n\n    def test_via_comparison_to_np_weibull(self):\n        param = iap.Weibull(1)\n\n        samples = param.draw_samples((100, 1000))\n        samples_direct = iarandom.RNG(1234).weibull(a=1, size=(100, 1000))\n\n        assert samples.shape == (100, 1000)\n        assert np.all(0 <= samples)\n\n        samples = np.clip(samples, 0, 2)\n        samples_direct = np.clip(samples_direct, 0, 2)\n        nb_bins = 10\n        hist, _ = np.histogram(samples, bins=nb_bins, range=(0, 2.0),\n                               density=False)\n        hist_direct, _ = np.histogram(samples_direct, bins=nb_bins,\n                                      range=(0, 2.0), density=False)\n        tolerance = 0.05\n        for nb_samples, nb_samples_direct in zip(hist, hist_direct):\n            density = nb_samples / samples.size\n            density_direct = nb_samples_direct / samples_direct.size\n            assert (\n                density_direct - tolerance\n                < density <\n                density_direct + tolerance\n            )\n\n    def test_argument_is_stochastic_parameter(self):\n        param = iap.Weibull(iap.Choice([1, 0.5]))\n\n        expected_first = scipy.special.gamma(1 + 1/1)\n        expected_second = scipy.special.gamma(1 + 1/0.5)\n        seen = [0, 0]\n        for _ in sm.xrange(100):\n            samples = param.draw_samples((50000,))\n            observed = np.mean(samples)\n\n            matches_first = (\n                expected_first - 0.2 * expected_first\n                < observed <\n                expected_first + 0.2 * expected_first\n            )\n            matches_second = (\n                expected_second - 0.2 * expected_second\n                < observed <\n                expected_second + 0.2 * expected_second\n            )\n\n            if matches_first:\n                seen[0] += 1\n            elif matches_second:\n                seen[1] += 1\n            else:\n                assert False\n\n        assert 50 - 25 < seen[0] < 50 + 25\n        assert 50 - 25 < seen[1] < 50 + 25\n\n    def test_different_strengths(self):\n        param1 = iap.Weibull(1)\n        param2 = iap.Weibull(0.5)\n\n        samples1 = param1.draw_samples((10000,))\n        samples2 = param2.draw_samples((10000,))\n        expected_first = (\n            scipy.special.gamma(1 + 2/1)\n            - (scipy.special.gamma(1 + 1/1))**2\n        )\n        expected_second = (\n            scipy.special.gamma(1 + 2/0.5)\n            - (scipy.special.gamma(1 + 1/0.5))**2\n        )\n\n        assert np.var(samples1) < np.var(samples2)\n        assert (\n            expected_first - 0.2 * expected_first\n            < np.var(samples1) <\n            expected_first + 0.2 * expected_first\n        )\n        assert (\n            expected_second - 0.2 * expected_second\n            < np.var(samples2) <\n            expected_second + 0.2 * expected_second\n        )\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.Weibull(1)\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.allclose(samples1, samples2)\n\n\nclass TestUniform(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Uniform(0, 1.0)\n        expected = \"Uniform(%s, %s)\" % (str(param.a), str(param.b))\n        assert \"Deterministic(int 0)\" in str(param.a)\n        assert \"Deterministic(float 1.00000000)\" in str(param.b)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_draw_sample(self):\n        param = iap.Uniform(0, 1.0)\n\n        sample = param.draw_sample()\n\n        assert sample.shape == tuple()\n        assert 0 - _eps(sample) < sample < 1.0 + _eps(sample)\n\n    def test_draw_samples(self):\n        param = iap.Uniform(0, 1.0)\n\n        samples = param.draw_samples((10, 5))\n\n        assert samples.shape == (10, 5)\n        assert np.all(\n            np.logical_and(\n                0 - _eps(samples) < samples,\n                samples < 1.0 + _eps(samples)\n            )\n        )\n\n    def test_via_density_histogram(self):\n        param = iap.Uniform(0, 1.0)\n\n        samples = param.draw_samples((10000,))\n        nb_bins = 10\n        hist, _ = np.histogram(samples, bins=nb_bins, range=(0.0, 1.0),\n                               density=False)\n        density_expected = 1.0/nb_bins\n        density_tolerance = 0.05\n        for nb_samples in hist:\n            density = nb_samples / samples.size\n            assert (\n                density_expected - density_tolerance\n                < density <\n                density_expected + density_tolerance\n            )\n\n    def test_negative_value(self):\n        param = iap.Uniform(-1.0, 1.0)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert -1.0 - _eps(sample) < sample < 1.0 + _eps(sample)\n        assert np.all(\n            np.logical_and(\n                -1.0 - _eps(samples) < samples,\n                samples < 1.0 + _eps(samples)\n            )\n        )\n\n    def test_wrong_argument_order(self):\n        param = iap.Uniform(1.0, -1.0)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert -1.0 - _eps(sample) < sample < 1.0 + _eps(sample)\n        assert np.all(\n            np.logical_and(\n                -1.0 - _eps(samples) < samples,\n                samples < 1.0 + _eps(samples)\n            )\n        )\n\n    def test_arguments_are_integers(self):\n        param = iap.Uniform(-1, 1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert -1.0 - _eps(sample) < sample < 1.0 + _eps(sample)\n        assert np.all(\n            np.logical_and(\n                -1.0 - _eps(samples) < samples,\n                samples < 1.0 + _eps(samples)\n            )\n        )\n\n    def test_arguments_are_identical(self):\n        param = iap.Uniform(1, 1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert 1.0 - _eps(sample) < sample < 1.0 + _eps(sample)\n        assert np.all(\n            np.logical_and(\n                1.0 - _eps(samples) < samples,\n                samples < 1.0 + _eps(samples)\n            )\n        )\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.Uniform(-1.0, 1.0)\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.allclose(samples1, samples2)\n\n\nclass TestBeta(unittest.TestCase):\n    @classmethod\n    def _mean(cls, alpha, beta):\n        return alpha / (alpha + beta)\n\n    @classmethod\n    def _var(cls, alpha, beta):\n        return (alpha * beta) / ((alpha + beta)**2 * (alpha + beta + 1))\n\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Beta(0.5, 0.5)\n        expected = \"Beta(%s, %s)\" % (str(param.alpha), str(param.beta))\n        assert \"Deterministic(float 0.50000000)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_draw_sample(self):\n        param = iap.Beta(0.5, 0.5)\n\n        sample = param.draw_sample()\n\n        assert sample.shape == tuple()\n        assert 0 - _eps(sample) < sample < 1.0 + _eps(sample)\n\n    def test_draw_samples(self):\n        param = iap.Beta(0.5, 0.5)\n\n        samples = param.draw_samples((100, 1000))\n\n        assert samples.shape == (100, 1000)\n        assert np.all(\n            np.logical_and(\n                0 - _eps(samples) <= samples,\n                samples <= 1.0 + _eps(samples)\n            )\n        )\n\n    def test_via_comparison_to_np_beta(self):\n        param = iap.Beta(0.5, 0.5)\n\n        samples = param.draw_samples((100, 1000))\n        samples_direct = iarandom.RNG(1234).beta(\n            a=0.5, b=0.5, size=(100, 1000))\n\n        nb_bins = 10\n        hist, _ = np.histogram(samples, bins=nb_bins, range=(0, 1.0),\n                               density=False)\n        hist_direct, _ = np.histogram(samples_direct, bins=nb_bins,\n                                      range=(0, 1.0), density=False)\n        tolerance = 0.05\n        for nb_samples, nb_samples_direct in zip(hist, hist_direct):\n            density = nb_samples / samples.size\n            density_direct = nb_samples_direct / samples_direct.size\n            assert (\n                density_direct - tolerance\n                < density <\n                density_direct + tolerance\n            )\n\n    def test_argument_is_stochastic_parameter(self):\n        param = iap.Beta(iap.Choice([0.5, 2]), 0.5)\n\n        expected_first = self._mean(0.5, 0.5)\n        expected_second = self._mean(2, 0.5)\n        seen = [0, 0]\n        for _ in sm.xrange(100):\n            samples = param.draw_samples((10000,))\n            observed = np.mean(samples)\n\n            if expected_first - 0.05 < observed < expected_first + 0.05:\n                seen[0] += 1\n            elif expected_second - 0.05 < observed < expected_second + 0.05:\n                seen[1] += 1\n            else:\n                assert False\n\n        assert 50 - 25 < seen[0] < 50 + 25\n        assert 50 - 25 < seen[1] < 50 + 25\n\n    def test_compare_curves_of_different_arguments(self):\n        param1 = iap.Beta(2, 2)\n        param2 = iap.Beta(0.5, 0.5)\n\n        samples1 = param1.draw_samples((10000,))\n        samples2 = param2.draw_samples((10000,))\n\n        expected_first = self._var(2, 2)\n        expected_second = self._var(0.5, 0.5)\n\n        assert np.var(samples1) < np.var(samples2)\n        assert (\n            expected_first - 0.1 * expected_first\n            < np.var(samples1) <\n            expected_first + 0.1 * expected_first\n        )\n        assert (\n            expected_second - 0.1 * expected_second\n            < np.var(samples2) <\n            expected_second + 0.1 * expected_second\n        )\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.Beta(0.5, 0.5)\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.allclose(samples1, samples2)\n\n\nclass TestDeterministic(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        pairs = [\n            (0, \"Deterministic(int 0)\"),\n            (1.0, \"Deterministic(float 1.00000000)\"),\n            (\"test\", \"Deterministic(test)\")\n        ]\n        for value, expected in pairs:\n            with self.subTest(value=value):\n                param = iap.Deterministic(value)\n                assert (\n                    param.__str__()\n                    == param.__repr__()\n                    == expected\n                )\n\n    def test_samples_same_values_for_same_seeds(self):\n        values = [\n            -100, -54, -1, 0, 1, 54, 100,\n            -100.0, -54.3, -1.0, 0.1, 0.0, 0.1, 1.0, 54.4, 100.0\n        ]\n        for value in values:\n            with self.subTest(value=value):\n                param = iap.Deterministic(value)\n\n                rs1 = iarandom.RNG(123456)\n                rs2 = iarandom.RNG(123456)\n\n                samples1 = param.draw_samples(20, random_state=rs1)\n                samples2 = param.draw_samples(20, random_state=rs2)\n\n                assert np.array_equal(samples1, samples2)\n\n    def test_draw_sample_int(self):\n        values = [-100, -54, -1, 0, 1, 54, 100]\n        for value in values:\n            with self.subTest(value=value):\n                param = iap.Deterministic(value)\n\n                sample1 = param.draw_sample()\n                sample2 = param.draw_sample()\n\n                assert sample1.shape == tuple()\n                assert sample1 == sample2\n\n    def test_draw_sample_float(self):\n        values = [-100.0, -54.3, -1.0, 0.1, 0.0, 0.1, 1.0, 54.4, 100.0]\n        for value in values:\n            with self.subTest(value=value):\n                param = iap.Deterministic(value)\n\n                sample1 = param.draw_sample()\n                sample2 = param.draw_sample()\n\n                assert sample1.shape == tuple()\n                assert np.isclose(\n                    sample1, sample2, rtol=0, atol=_eps(sample1))\n\n    def test_draw_samples_int(self):\n        values = [-100, -54, -1, 0, 1, 54, 100]\n        shapes = [10, 10, (5, 3), (5, 3), (4, 5, 3), (4, 5, 3)]\n        for value, shape in itertools.product(values, shapes):\n            with self.subTest(value=value, shape=shape):\n                param = iap.Deterministic(value)\n\n                samples = param.draw_samples(shape)\n                shape_expected = (\n                    shape\n                    if isinstance(shape, tuple)\n                    else tuple([shape]))\n\n                assert samples.shape == shape_expected\n                assert np.all(samples == value)\n\n    def test_draw_samples_float(self):\n        values = [-100.0, -54.3, -1.0, 0.1, 0.0, 0.1, 1.0, 54.4, 100.0]\n        shapes = [10, 10, (5, 3), (5, 3), (4, 5, 3), (4, 5, 3)]\n        for value, shape in itertools.product(values, shapes):\n            with self.subTest(value=value, shape=shape):\n                param = iap.Deterministic(value)\n\n                samples = param.draw_samples(shape)\n                shape_expected = (\n                    shape\n                    if isinstance(shape, tuple)\n                    else tuple([shape]))\n\n                assert samples.shape == shape_expected\n                assert np.allclose(samples, value, rtol=0, atol=_eps(samples))\n\n    def test_argument_is_stochastic_parameter(self):\n        seen = [0, 0]\n        for _ in sm.xrange(200):\n            param = iap.Deterministic(iap.Choice([0, 1]))\n            seen[param.value] += 1\n\n        assert 100 - 50 < seen[0] < 100 + 50\n        assert 100 - 50 < seen[1] < 100 + 50\n\n    def test_argument_has_invalid_type(self):\n        with self.assertRaises(Exception) as context:\n            _ = iap.Deterministic([1, 2, 3])\n\n        self.assertTrue(\n            \"Expected StochasticParameter object or number or string\"\n            in str(context.exception))\n\n\nclass TestDeterministicList(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___with_array(self):\n        values = np.arange(1*2*3).reshape((1, 2, 3))\n        param = iap.DeterministicList(values)\n        assert np.array_equal(param.values, values.flatten())\n\n    def test___init___with_list_int(self):\n        values = [[1, 2], [3, 4]]\n        param = iap.DeterministicList(values)\n        assert np.array_equal(param.values, [1, 2, 3, 4])\n        assert param.values.dtype.name == \"int32\"\n\n    def test___init___with_list_float(self):\n        values = [[1.1, 2.2], [3.3, 4.4]]\n        param = iap.DeterministicList(values)\n        assert np.allclose(param.values, [1.1, 2.2, 3.3, 4.4])\n        assert param.values.dtype.name == \"float32\"\n\n    def test_samples_same_values_for_same_seeds(self):\n        values = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110]\n        param = iap.DeterministicList(values)\n\n        rs1 = iarandom.RNG(123456)\n        rs2 = iarandom.RNG(123456)\n\n        samples1 = param.draw_samples(10, random_state=rs1)\n        samples2 = param.draw_samples(10, random_state=rs2)\n\n        assert np.array_equal(samples1, samples2)\n\n    def test_draw_sample_int(self):\n        values = [10, 20, 30, 40, 50]\n        param = iap.DeterministicList(values)\n\n        sample1 = param.draw_sample()\n        sample2 = param.draw_sample()\n\n        assert sample1.shape == tuple()\n        assert sample1 == sample2\n\n    def test_draw_sample_float(self):\n        values = [10.1, 20.2, 30.3, 40.4, 50.5]\n        param = iap.DeterministicList(values)\n\n        sample1 = param.draw_sample()\n        sample2 = param.draw_sample()\n\n        assert sample1.shape == tuple()\n        assert np.isclose(\n            sample1, sample2, rtol=0, atol=_eps(sample1))\n\n    def test_draw_samples_int(self):\n        values = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]\n        shapes = [3, (2, 3), (2, 3, 1)]\n        expecteds = [\n            [10, 20, 30],\n            [[10, 20, 30], [40, 50, 60]],\n            [[[10], [20], [30]], [[40], [50], [60]]]\n        ]\n        param = iap.DeterministicList(values)\n        for shape, expected in zip(shapes, expecteds):\n            with self.subTest(shape=shape):\n                samples = param.draw_samples(shape)\n\n                shape_expected = (\n                    shape\n                    if isinstance(shape, tuple)\n                    else tuple([shape]))\n\n                assert samples.shape == shape_expected\n                assert np.array_equal(samples, expected)\n\n    def test_draw_samples_float(self):\n        values = [10.1, 20.2, 30.3, 40.4, 50.5, 60.6, 70.7, 80.8, 90.9, 100.10]\n        shapes = [3, (2, 3), (2, 3, 1)]\n        expecteds = [\n            [10.1, 20.2, 30.3],\n            [[10.1, 20.2, 30.3], [40.4, 50.5, 60.6]],\n            [[[10.1], [20.2], [30.3]], [[40.4], [50.5], [60.6]]]\n        ]\n        param = iap.DeterministicList(values)\n        for shape, expected in zip(shapes, expecteds):\n            with self.subTest(shape=shape):\n                samples = param.draw_samples(shape)\n\n                shape_expected = (\n                    shape\n                    if isinstance(shape, tuple)\n                    else tuple([shape]))\n\n                assert samples.shape == shape_expected\n                assert np.allclose(samples, expected, rtol=0, atol=1e-5)\n\n    def test_draw_samples_cycles_when_shape_too_large(self):\n        values = [10, 20, 30]\n        param = iap.DeterministicList(values)\n\n        shapes = [(6,), (7,), (8,), (9,), (3, 3)]\n        expecteds = [\n            [10, 20, 30, 10, 20, 30],\n            [10, 20, 30, 10, 20, 30, 10],\n            [10, 20, 30, 10, 20, 30, 10, 20],\n            [10, 20, 30, 10, 20, 30, 10, 20, 30],\n            [[10, 20, 30],\n             [10, 20, 30],\n             [10, 20, 30]]\n        ]\n\n        for shape, expected in zip(shapes, expecteds):\n            with self.subTest(shape=shape):\n                samples = param.draw_samples(shape)\n\n                assert np.array_equal(samples, expected)\n\n    def test___str___and___repr___float(self):\n        param = iap.DeterministicList([10.1, 20.2, 30.3])\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == \"DeterministicList([10.1000, 20.2000, 30.3000])\"\n        )\n\n    def test___str___and___repr___intlike(self):\n        param = iap.DeterministicList([10, 20, 30])\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == \"DeterministicList([10, 20, 30])\"\n        )\n\n\nclass TestFromLowerResolution(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init___size_percent(self):\n        param = iap.FromLowerResolution(other_param=iap.Deterministic(0),\n                                        size_percent=1, method=\"nearest\")\n\n        expected = (\n            \"FromLowerResolution(size_percent=%s, method=%s, other_param=%s)\"\n        ) % (\n            str(param.size_percent),\n            str(param.method),\n            str(param.other_param)\n        )\n        assert \"Deterministic(int 1)\" in str(param)\n        assert \"Deterministic(nearest)\" in str(param)\n        assert \"Deterministic(int 0)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test___init___size_px(self):\n        param = iap.FromLowerResolution(other_param=iap.Deterministic(0),\n                                        size_px=1, method=\"nearest\")\n\n        expected = (\n            \"FromLowerResolution(size_px=%s, method=%s, other_param=%s)\"\n        ) % (\n            str(param.size_px),\n            str(param.method),\n            str(param.other_param)\n        )\n        assert \"Deterministic(int 1)\" in str(param)\n        assert \"Deterministic(nearest)\" in str(param)\n        assert \"Deterministic(int 0)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_binomial_hwc(self):\n        param = iap.FromLowerResolution(iap.Binomial(0.5), size_px=8)\n\n        samples = param.draw_samples((8, 8, 1))\n        uq = np.unique(samples)\n\n        assert samples.shape == (8, 8, 1)\n        assert len(uq) == 2\n        assert 0 in uq\n        assert 1 in uq\n\n    def test_binomial_nhwc(self):\n        param = iap.FromLowerResolution(iap.Binomial(0.5), size_px=8)\n\n        samples_nhwc = param.draw_samples((1, 8, 8, 1))\n        uq = np.unique(samples_nhwc)\n\n        assert samples_nhwc.shape == (1, 8, 8, 1)\n        assert len(uq) == 2\n        assert 0 in uq\n        assert 1 in uq\n\n    def test_draw_samples_with_too_many_dimensions(self):\n        # (N, H, W, C, something) causing error\n        param = iap.FromLowerResolution(iap.Binomial(0.5), size_px=8)\n\n        with self.assertRaises(Exception) as context:\n            _ = param.draw_samples((1, 8, 8, 1, 1))\n\n        self.assertTrue(\n            \"FromLowerResolution can only generate samples of shape\"\n            in str(context.exception)\n        )\n\n    def test_binomial_hw3(self):\n        # C=3\n        param = iap.FromLowerResolution(iap.Binomial(0.5), size_px=8)\n\n        samples = param.draw_samples((8, 8, 3))\n        uq = np.unique(samples)\n\n        assert samples.shape == (8, 8, 3)\n        assert len(uq) == 2\n        assert 0 in uq\n        assert 1 in uq\n\n    def test_different_size_px_arguments(self):\n        # different sizes in px\n        param1 = iap.FromLowerResolution(iap.Binomial(0.5), size_px=2)\n        param2 = iap.FromLowerResolution(iap.Binomial(0.5), size_px=16)\n\n        seen_components = [0, 0]\n        seen_pixels = [0, 0]\n        for _ in sm.xrange(100):\n            samples1 = param1.draw_samples((16, 16, 1))\n            samples2 = param2.draw_samples((16, 16, 1))\n            _, num1 = skimage.morphology.label(samples1, connectivity=1,\n                                               background=0, return_num=True)\n            _, num2 = skimage.morphology.label(samples2, connectivity=1,\n                                               background=0, return_num=True)\n            seen_components[0] += num1\n            seen_components[1] += num2\n            seen_pixels[0] += np.sum(samples1 == 1)\n            seen_pixels[1] += np.sum(samples2 == 1)\n\n        assert seen_components[0] < seen_components[1]\n        assert (\n            seen_pixels[0] / seen_components[0]\n            > seen_pixels[1] / seen_components[1]\n        )\n\n    def test_different_size_px_arguments_with_tuple(self):\n        # different sizes in px, one given as tuple (a, b)\n        param1 = iap.FromLowerResolution(iap.Binomial(0.5), size_px=2)\n        param2 = iap.FromLowerResolution(iap.Binomial(0.5), size_px=(2, 16))\n\n        seen_components = [0, 0]\n        seen_pixels = [0, 0]\n        for _ in sm.xrange(400):\n            samples1 = param1.draw_samples((16, 16, 1))\n            samples2 = param2.draw_samples((16, 16, 1))\n            _, num1 = skimage.morphology.label(samples1, connectivity=1,\n                                               background=0, return_num=True)\n            _, num2 = skimage.morphology.label(samples2, connectivity=1,\n                                               background=0, return_num=True)\n            seen_components[0] += num1\n            seen_components[1] += num2\n            seen_pixels[0] += np.sum(samples1 == 1)\n            seen_pixels[1] += np.sum(samples2 == 1)\n\n        assert seen_components[0] < seen_components[1]\n        assert (\n            seen_pixels[0] / seen_components[0]\n            > seen_pixels[1] / seen_components[1]\n        )\n\n    def test_different_size_px_argument_with_stochastic_parameters(self):\n        # different sizes in px, given as StochasticParameter\n        param1 = iap.FromLowerResolution(iap.Binomial(0.5),\n                                         size_px=iap.Deterministic(1))\n        param2 = iap.FromLowerResolution(iap.Binomial(0.5),\n                                         size_px=iap.Choice([8, 16]))\n\n        seen_components = [0, 0]\n        seen_pixels = [0, 0]\n        for _ in sm.xrange(100):\n            samples1 = param1.draw_samples((16, 16, 1))\n            samples2 = param2.draw_samples((16, 16, 1))\n            _, num1 = skimage.morphology.label(samples1, connectivity=1,\n                                               background=0, return_num=True)\n            _, num2 = skimage.morphology.label(samples2, connectivity=1,\n                                               background=0, return_num=True)\n            seen_components[0] += num1\n            seen_components[1] += num2\n            seen_pixels[0] += np.sum(samples1 == 1)\n            seen_pixels[1] += np.sum(samples2 == 1)\n\n        assert seen_components[0] < seen_components[1]\n        assert (\n            seen_pixels[0] / seen_components[0]\n            > seen_pixels[1] / seen_components[1]\n        )\n\n    def test_size_px_has_invalid_datatype(self):\n        # bad datatype for size_px\n        with self.assertRaises(Exception) as context:\n            _ = iap.FromLowerResolution(iap.Binomial(0.5), size_px=False)\n\n        self.assertTrue(\"Expected \" in str(context.exception))\n\n    def test_min_size(self):\n        # min_size\n        param1 = iap.FromLowerResolution(iap.Binomial(0.5), size_px=2)\n        param2 = iap.FromLowerResolution(iap.Binomial(0.5), size_px=1,\n                                         min_size=16)\n\n        seen_components = [0, 0]\n        seen_pixels = [0, 0]\n        for _ in sm.xrange(100):\n            samples1 = param1.draw_samples((16, 16, 1))\n            samples2 = param2.draw_samples((16, 16, 1))\n            _, num1 = skimage.morphology.label(samples1, connectivity=1,\n                                               background=0, return_num=True)\n            _, num2 = skimage.morphology.label(samples2, connectivity=1,\n                                               background=0, return_num=True)\n            seen_components[0] += num1\n            seen_components[1] += num2\n            seen_pixels[0] += np.sum(samples1 == 1)\n            seen_pixels[1] += np.sum(samples2 == 1)\n\n        assert seen_components[0] < seen_components[1]\n        assert (\n            seen_pixels[0] / seen_components[0]\n            > seen_pixels[1] / seen_components[1]\n        )\n\n    def test_size_percent(self):\n        # different sizes in percent\n        param1 = iap.FromLowerResolution(iap.Binomial(0.5), size_percent=0.01)\n        param2 = iap.FromLowerResolution(iap.Binomial(0.5), size_percent=0.8)\n\n        seen_components = [0, 0]\n        seen_pixels = [0, 0]\n        for _ in sm.xrange(100):\n            samples1 = param1.draw_samples((16, 16, 1))\n            samples2 = param2.draw_samples((16, 16, 1))\n            _, num1 = skimage.morphology.label(samples1, connectivity=1,\n                                               background=0, return_num=True)\n            _, num2 = skimage.morphology.label(samples2, connectivity=1,\n                                               background=0, return_num=True)\n            seen_components[0] += num1\n            seen_components[1] += num2\n            seen_pixels[0] += np.sum(samples1 == 1)\n            seen_pixels[1] += np.sum(samples2 == 1)\n\n        assert seen_components[0] < seen_components[1]\n        assert (\n            seen_pixels[0] / seen_components[0]\n            > seen_pixels[1] / seen_components[1]\n        )\n\n    def test_size_percent_as_stochastic_parameters(self):\n        # different sizes in percent, given as StochasticParameter\n        param1 = iap.FromLowerResolution(iap.Binomial(0.5),\n                                         size_percent=iap.Deterministic(0.01))\n        param2 = iap.FromLowerResolution(iap.Binomial(0.5),\n                                         size_percent=iap.Choice([0.4, 0.8]))\n\n        seen_components = [0, 0]\n        seen_pixels = [0, 0]\n        for _ in sm.xrange(100):\n            samples1 = param1.draw_samples((16, 16, 1))\n            samples2 = param2.draw_samples((16, 16, 1))\n            _, num1 = skimage.morphology.label(samples1, connectivity=1,\n                                               background=0, return_num=True)\n            _, num2 = skimage.morphology.label(samples2, connectivity=1,\n                                               background=0, return_num=True)\n            seen_components[0] += num1\n            seen_components[1] += num2\n            seen_pixels[0] += np.sum(samples1 == 1)\n            seen_pixels[1] += np.sum(samples2 == 1)\n\n        assert seen_components[0] < seen_components[1]\n        assert (\n            seen_pixels[0] / seen_components[0]\n            > seen_pixels[1] / seen_components[1]\n        )\n\n    def test_size_percent_has_invalid_datatype(self):\n        # bad datatype for size_percent\n        with self.assertRaises(Exception) as context:\n            _ = iap.FromLowerResolution(iap.Binomial(0.5), size_percent=False)\n\n        self.assertTrue(\"Expected \" in str(context.exception))\n\n    def test_method(self):\n        # method given as StochasticParameter\n        param = iap.FromLowerResolution(\n            iap.Binomial(0.5), size_px=4,\n            method=iap.Choice([\"nearest\", \"linear\"]))\n\n        seen = [0, 0]\n        for _ in sm.xrange(200):\n            samples = param.draw_samples((16, 16, 1))\n            nb_in_between = np.sum(\n                np.logical_and(0.05 < samples, samples < 0.95))\n            if nb_in_between == 0:\n                seen[0] += 1\n            else:\n                seen[1] += 1\n\n        assert 100 - 50 < seen[0] < 100 + 50\n        assert 100 - 50 < seen[1] < 100 + 50\n\n    def test_method_has_invalid_datatype(self):\n        # bad datatype for method\n        with self.assertRaises(Exception) as context:\n            _ = iap.FromLowerResolution(iap.Binomial(0.5), size_px=4,\n                                        method=False)\n\n        self.assertTrue(\"Expected \" in str(context.exception))\n\n    def test_samples_same_values_for_same_seeds(self):\n        # multiple calls with same random_state\n        param = iap.FromLowerResolution(iap.Binomial(0.5), size_px=2)\n\n        samples1 = param.draw_samples((10, 5, 1),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5, 1),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.allclose(samples1, samples2)\n\n\nclass TestClip(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Clip(iap.Deterministic(0), -1, 1)\n        expected = \"Clip(%s, -1.000000, 1.000000)\" % (str(param.other_param),)\n        assert \"Deterministic(int 0)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_value_within_bounds(self):\n        param = iap.Clip(iap.Deterministic(0), -1, 1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample == 0\n        assert np.all(samples == 0)\n\n    def test_value_exactly_at_upper_bound(self):\n        param = iap.Clip(iap.Deterministic(1), -1, 1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample == 1\n        assert np.all(samples == 1)\n\n    def test_value_exactly_at_lower_bound(self):\n        param = iap.Clip(iap.Deterministic(-1), -1, 1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample == -1\n        assert np.all(samples == -1)\n\n    def test_value_is_within_bounds_and_float(self):\n        param = iap.Clip(iap.Deterministic(0.5), -1, 1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert 0.5 - _eps(sample) < sample < 0.5 + _eps(sample)\n        assert np.all(\n            np.logical_and(\n                0.5 - _eps(sample) <= samples,\n                samples <= 0.5 + _eps(sample)\n            )\n        )\n\n    def test_value_is_above_upper_bound(self):\n        param = iap.Clip(iap.Deterministic(2), -1, 1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample == 1\n        assert np.all(samples == 1)\n\n    def test_value_is_below_lower_bound(self):\n        param = iap.Clip(iap.Deterministic(-2), -1, 1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample == -1\n        assert np.all(samples == -1)\n\n    def test_value_is_sometimes_without_bounds_sometimes_beyond(self):\n        param = iap.Clip(iap.Choice([0, 2]), -1, 1)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample in [0, 1]\n        assert np.all(np.logical_or(samples == 0, samples == 1))\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.Clip(iap.Choice([0, 2]), -1, 1)\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.array_equal(samples1, samples2)\n\n    def test_lower_bound_is_none(self):\n        param = iap.Clip(iap.Deterministic(0), None, 1)\n\n        sample = param.draw_sample()\n        expected = \"Clip(%s, None, 1.000000)\" % (str(param.other_param),)\n        assert sample == 0\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_upper_bound_is_none(self):\n        param = iap.Clip(iap.Deterministic(0), 0, None)\n\n        sample = param.draw_sample()\n        expected = \"Clip(%s, 0.000000, None)\" % (str(param.other_param),)\n        assert sample == 0\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_both_bounds_are_none(self):\n        param = iap.Clip(iap.Deterministic(0), None, None)\n\n        sample = param.draw_sample()\n        expected = \"Clip(%s, None, None)\" % (str(param.other_param),)\n        assert sample == 0\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n\nclass TestDiscretize(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Discretize(iap.Deterministic(0))\n        expected = \"Discretize(%s, round=True)\" % (param.other_param,)\n        assert \"Deterministic(int 0)\" in str(param.other_param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_applied_to_deterministic(self):\n        values = [-100.2, -54.3, -1.0, -1, -0.7, -0.00043,\n                  0,\n                  0.00043, 0.7, 1.0, 1, 54.3, 100.2]\n        for value in values:\n            with self.subTest(value=value):\n                param = iap.Discretize(iap.Deterministic(value))\n                value_expected = np.round(\n                    np.float64([value])\n                ).astype(np.int32)[0]\n\n                sample = param.draw_sample()\n                samples = param.draw_samples((10, 5))\n\n                assert sample.shape == tuple()\n                assert samples.shape == (10, 5)\n                assert sample == value_expected\n                assert np.all(samples == value_expected)\n\n    # TODO why are these tests applied to DiscreteUniform instead of Uniform?\n    def test_applied_to_discrete_uniform(self):\n        param_orig = iap.DiscreteUniform(0, 1)\n        param = iap.Discretize(param_orig)\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 5))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (10, 5)\n        assert sample in [0, 1]\n        assert np.all(np.logical_or(samples == 0, samples == 1))\n\n    def test_applied_to_discrete_uniform_with_wider_range(self):\n        param_orig = iap.DiscreteUniform(0, 2)\n        param = iap.Discretize(param_orig)\n\n        samples1 = param_orig.draw_samples((10000,))\n        samples2 = param.draw_samples((10000,))\n\n        assert np.all(np.abs(samples1 - samples2) < 0.2*(10000/3))\n\n    def test_round(self):\n        param_orig = iap.Uniform(0, 1.99)\n        param_round = iap.Discretize(param_orig)\n        param_no_round = iap.Discretize(param_orig, round=False)\n\n        samples_round = param_round.draw_samples((10000,))\n        samples_no_round = param_no_round.draw_samples((10000,))\n\n        uq_round = np.unique(samples_round)\n        uq_no_round = np.unique(samples_no_round)\n\n        assert np.all([v in uq_round for v in [0, 1, 2]])\n        assert np.all([v in uq_no_round for v in [0, 1]])\n\n    def test_samples_same_values_for_same_seeds(self):\n        param_orig = iap.DiscreteUniform(0, 2)\n        param = iap.Discretize(param_orig)\n\n        samples1 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((10, 5),\n                                      random_state=iarandom.RNG(1234))\n\n        assert np.array_equal(samples1, samples2)\n\n\nclass TestMultiply(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Multiply(iap.Deterministic(0), 1, elementwise=False)\n        expected = \"Multiply(%s, %s, False)\" % (\n            str(param.other_param),\n            str(param.val)\n        )\n        assert \"Deterministic(int 0)\" in str(param.other_param)\n        assert \"Deterministic(int 1)\" in str(param.val)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_multiply_example_integer_values(self):\n        values_int = [-100, -54, -1, 0, 1, 54, 100]\n\n        for v1, v2 in itertools.product(values_int, values_int):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Multiply(iap.Deterministic(v1), v2)\n\n                samples = p.draw_samples((2, 3))\n\n                assert p.draw_sample() == v1 * v2\n                assert samples.dtype.kind == \"i\"\n                assert np.array_equal(\n                    samples,\n                    np.zeros((2, 3), dtype=np.int64) + v1 * v2\n                )\n\n    def test_multiply_example_integer_values_both_deterministic(self):\n        values_int = [-100, -54, -1, 0, 1, 54, 100]\n\n        for v1, v2 in itertools.product(values_int, values_int):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Multiply(iap.Deterministic(v1), iap.Deterministic(v2))\n\n                samples = p.draw_samples((2, 3))\n\n                assert p.draw_sample() == v1 * v2\n                assert samples.dtype.name == \"int32\"\n                assert np.array_equal(\n                    samples,\n                    np.zeros((2, 3), dtype=np.int32) + v1 * v2\n                )\n\n    def test_multiply_example_float_values(self):\n        values_float = [-100.0, -54.3, -1.0, 0.1, 0.0, 0.1, 1.0, 54.4, 100.0]\n        for v1, v2 in itertools.product(values_float, values_float):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Multiply(iap.Deterministic(v1), v2)\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert np.isclose(sample, v1 * v2, atol=1e-3, rtol=0)\n                assert samples.dtype.kind == \"f\"\n                assert np.allclose(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float32) + v1 * v2\n                )\n\n    def test_multiply_example_float_values_both_deterministic(self):\n        values_float = [-100.0, -54.3, -1.0, 0.1, 0.0, 0.1, 1.0, 54.4, 100.0]\n        for v1, v2 in itertools.product(values_float, values_float):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Multiply(iap.Deterministic(v1), iap.Deterministic(v2))\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert np.isclose(sample, v1 * v2, atol=1e-3, rtol=0)\n                assert samples.dtype.kind == \"f\"\n                assert np.allclose(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float32) + v1 * v2\n                )\n\n    def test_multiply_by_stochastic_parameter(self):\n        param = iap.Multiply(iap.Deterministic(1.0),\n                             (1.0, 2.0),\n                             elementwise=False)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.0 * 1.0 - _eps(samples))\n        assert np.all(samples < 1.0 * 2.0 + _eps(samples))\n        assert (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_multiply_by_stochastic_parameter_elementwise(self):\n        param = iap.Multiply(iap.Deterministic(1.0),\n                             (1.0, 2.0),\n                             elementwise=True)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.0 * 1.0 - _eps(samples))\n        assert np.all(samples < 1.0 * 2.0 + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_multiply_stochastic_parameter_by_fixed_value(self):\n        param = iap.Multiply(iap.Uniform(1.0, 2.0),\n                             1.0,\n                             elementwise=False)\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.0 * 1.0 - _eps(samples))\n        assert np.all(samples < 2.0 * 1.0 + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_multiply_stochastic_parameter_by_fixed_value_elementwise(self):\n        param = iap.Multiply(iap.Uniform(1.0, 2.0), 1.0, elementwise=True)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.0 * 1.0 - _eps(samples))\n        assert np.all(samples < 2.0 * 1.0 + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n\nclass TestDivide(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Divide(iap.Deterministic(0), 1, elementwise=False)\n        expected = \"Divide(%s, %s, False)\" % (\n            str(param.other_param), str(param.val)\n        )\n        assert \"Deterministic(int 0)\" in str(param.other_param)\n        assert \"Deterministic(int 1)\" in str(param.val)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_divide_integers(self):\n        values_int = [-100, -54, -1, 0, 1, 54, 100]\n\n        for v1, v2 in itertools.product(values_int, values_int):\n            if v2 == 0:\n                v2 = 1\n\n            with self.subTest(left=v1, right=v2):\n                p = iap.Divide(iap.Deterministic(v1), v2)\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert sample == (v1 / v2)\n                assert samples.dtype.kind == \"f\"\n                assert np.array_equal(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float64) + (v1 / v2)\n                )\n\n    def test_divide_integers_both_deterministic(self):\n        values_int = [-100, -54, -1, 0, 1, 54, 100]\n\n        for v1, v2 in itertools.product(values_int, values_int):\n            if v2 == 0:\n                v2 = 1\n\n            with self.subTest(left=v1, right=v2):\n                p = iap.Divide(iap.Deterministic(v1), iap.Deterministic(v2))\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert sample == (v1 / v2)\n                assert samples.dtype.kind == \"f\"\n                assert np.array_equal(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float64) + (v1 / v2)\n                )\n\n    def test_divide_floats(self):\n        values_float = [-100.0, -54.3, -1.0, 0.1, 0.0, 0.1, 1.0, 54.4, 100.0]\n\n        for v1, v2 in itertools.product(values_float, values_float):\n            if v2 == 0:\n                v2 = 1\n\n            with self.subTest(left=v1, right=v2):\n                p = iap.Divide(iap.Deterministic(v1), v2)\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert (\n                    (v1 / v2) - _eps(sample)\n                    <= sample <=\n                    (v1 / v2) + _eps(sample)\n                )\n                assert samples.dtype.kind == \"f\"\n                assert np.allclose(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float64) + (v1 / v2)\n                )\n\n    def test_divide_floats_both_deterministic(self):\n        values_float = [-100.0, -54.3, -1.0, 0.1, 0.0, 0.1, 1.0, 54.4, 100.0]\n\n        for v1, v2 in itertools.product(values_float, values_float):\n            if v2 == 0:\n                v2 = 1\n\n            with self.subTest(left=v1, right=v2):\n                p = iap.Divide(iap.Deterministic(v1), iap.Deterministic(v2))\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert (\n                    (v1 / v2) - _eps(sample)\n                    <= sample <=\n                    (v1 / v2) + _eps(sample)\n                )\n                assert samples.dtype.kind == \"f\"\n                assert np.allclose(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float64) + (v1 / v2)\n                )\n\n    def test_divide_by_stochastic_parameter(self):\n        param = iap.Divide(iap.Deterministic(1.0),\n                           (1.0, 2.0),\n                           elementwise=False)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > (1.0 / 2.0) - _eps(samples))\n        assert np.all(samples < (1.0 / 1.0) + _eps(samples))\n        assert (\n            samples_sorted[0] - _eps(samples)\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples)\n        )\n\n    def test_divide_by_stochastic_parameter_elementwise(self):\n        param = iap.Divide(iap.Deterministic(1.0),\n                           (1.0, 2.0),\n                           elementwise=True)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > (1.0 / 2.0) - _eps(samples))\n        assert np.all(samples < (1.0 / 1.0) + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples)\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples)\n        )\n\n    def test_divide_stochastic_parameter_by_float(self):\n        param = iap.Divide(iap.Uniform(1.0, 2.0),\n                           1.0,\n                           elementwise=False)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > (1.0 / 1.0) - _eps(samples))\n        assert np.all(samples < (2.0 / 1.0) + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples)\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples)\n        )\n\n    def test_divide_stochastic_parameter_by_float_elementwise(self):\n        param = iap.Divide(iap.Uniform(1.0, 2.0),\n                           1.0,\n                           elementwise=True)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > (1.0 / 1.0) - _eps(samples))\n        assert np.all(samples < (2.0 / 1.0) + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted)\n            < samples_sorted[-1]\n            < samples_sorted[-1]\n            < samples_sorted[0] + _eps(samples_sorted)\n        )\n\n    def test_divide_by_stochastic_parameter_that_can_by_zero(self):\n        # test division by zero automatically being converted to division by 1\n        param = iap.Divide(2,\n                           iap.Choice([0, 2]),\n                           elementwise=True)\n\n        samples = param.draw_samples((10, 20))\n        samples_unique = np.sort(np.unique(samples.flatten()))\n\n        assert samples_unique[0] == 1 and samples_unique[1] == 2\n\n    def test_divide_by_zero(self):\n        param = iap.Divide(iap.Deterministic(1), 0, elementwise=False)\n\n        sample = param.draw_sample()\n\n        assert sample == 1\n\n\nclass TestAdd(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Add(iap.Deterministic(0), 1, elementwise=False)\n        expected = \"Add(%s, %s, False)\" % (\n            str(param.other_param),\n            str(param.val)\n        )\n        assert \"Deterministic(int 0)\" in str(param.other_param)\n        assert \"Deterministic(int 1)\" in str(param.val)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_add_integers(self):\n        values_int = [-100, -54, -1, 0, 1, 54, 100]\n\n        for v1, v2 in itertools.product(values_int, values_int):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Add(iap.Deterministic(v1), v2)\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert sample == v1 + v2\n                assert samples.dtype.kind == \"i\"\n                assert np.array_equal(\n                    samples,\n                    np.zeros((2, 3), dtype=np.int32) + v1 + v2\n                )\n\n    def test_add_integers_both_deterministic(self):\n        values_int = [-100, -54, -1, 0, 1, 54, 100]\n\n        for v1, v2 in itertools.product(values_int, values_int):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Add(iap.Deterministic(v1), iap.Deterministic(v2))\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert sample == v1 + v2\n                assert samples.dtype.kind == \"i\"\n                assert np.array_equal(\n                    samples,\n                    np.zeros((2, 3), dtype=np.int32) + v1 + v2\n                )\n\n    def test_add_floats(self):\n        values_float = [-100.0, -54.3, -1.0, 0.1, 0.0, 0.1, 1.0, 54.4, 100.0]\n        for v1, v2 in itertools.product(values_float, values_float):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Add(iap.Deterministic(v1), v2)\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert np.isclose(sample, v1 + v2, atol=1e-3, rtol=0)\n                assert samples.dtype.kind == \"f\"\n                assert np.allclose(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float32) + v1 + v2\n                )\n\n    def test_add_floats_both_deterministic(self):\n        values_float = [-100.0, -54.3, -1.0, 0.1, 0.0, 0.1, 1.0, 54.4, 100.0]\n        for v1, v2 in itertools.product(values_float, values_float):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Add(iap.Deterministic(v1), iap.Deterministic(v2))\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert np.isclose(sample, v1 + v2, atol=1e-3, rtol=0)\n                assert samples.dtype.kind == \"f\"\n                assert np.allclose(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float32) + v1 + v2\n                )\n\n    def test_add_stochastic_parameter(self):\n        param = iap.Add(iap.Deterministic(1.0), (1.0, 2.0), elementwise=False)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples >= 1.0 + 1.0 - _eps(samples))\n        assert np.all(samples <= 1.0 + 2.0 + _eps(samples))\n        assert (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1]\n            < samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_add_stochastic_parameter_elementwise(self):\n        param = iap.Add(iap.Deterministic(1.0), (1.0, 2.0), elementwise=True)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples >= 1.0 + 1.0 - _eps(samples))\n        assert np.all(samples <= 1.0 + 2.0 + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1]\n            < samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_add_to_stochastic_parameter(self):\n        param = iap.Add(iap.Uniform(1.0, 2.0), 1.0, elementwise=False)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples >= 1.0 + 1.0 - _eps(samples))\n        assert np.all(samples <= 2.0 + 1.0 + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1]\n            < samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_add_to_stochastic_parameter_elementwise(self):\n        param = iap.Add(iap.Uniform(1.0, 2.0), 1.0, elementwise=True)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples >= 1.0 + 1.0 - _eps(samples))\n        assert np.all(samples <= 2.0 + 1.0 + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1]\n            < samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n\nclass TestSubtract(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Subtract(iap.Deterministic(0), 1, elementwise=False)\n        expected = \"Subtract(%s, %s, False)\" % (\n            str(param.other_param),\n            str(param.val)\n        )\n        assert \"Deterministic(int 0)\" in str(param)\n        assert \"Deterministic(int 1)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_subtract_integers(self):\n        values_int = [-100, -54, -1, 0, 1, 54, 100]\n\n        for v1, v2 in itertools.product(values_int, values_int):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Subtract(iap.Deterministic(v1), v2)\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert sample == v1 - v2\n                assert samples.dtype.kind == \"i\"\n                assert np.array_equal(\n                    samples,\n                    np.zeros((2, 3), dtype=np.int64) + v1 - v2\n                )\n\n    def test_subtract_integers_both_deterministic(self):\n        values_int = [-100, -54, -1, 0, 1, 54, 100]\n\n        for v1, v2 in itertools.product(values_int, values_int):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Subtract(iap.Deterministic(v1), iap.Deterministic(v2))\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert sample == v1 - v2\n                assert samples.dtype.kind == \"i\"\n                assert np.array_equal(\n                    samples,\n                    np.zeros((2, 3), dtype=np.int64) + v1 - v2\n                )\n\n    def test_subtract_floats(self):\n        values_float = [-100.0, -54.3, -1.0, 0.1, 0.0, 0.1, 1.0, 54.4, 100.0]\n        for v1, v2 in itertools.product(values_float, values_float):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Subtract(iap.Deterministic(v1), v2)\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert v1 - v2 - _eps(sample) < sample < v1 - v2 + _eps(sample)\n                assert samples.dtype.kind == \"f\"\n                assert np.allclose(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float64) + v1 - v2\n                )\n\n    def test_subtract_floats_both_deterministic(self):\n        values_float = [-100.0, -54.3, -1.0, 0.1, 0.0, 0.1, 1.0, 54.4, 100.0]\n        for v1, v2 in itertools.product(values_float, values_float):\n            with self.subTest(left=v1, right=v2):\n                p = iap.Subtract(iap.Deterministic(v1), iap.Deterministic(v2))\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert v1 - v2 - _eps(sample) < sample < v1 - v2 + _eps(sample)\n                assert samples.dtype.kind == \"f\"\n                assert np.allclose(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float64) + v1 - v2\n                )\n\n    def test_subtract_stochastic_parameter(self):\n        param = iap.Subtract(iap.Deterministic(1.0),\n                             (1.0, 2.0),\n                             elementwise=False)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.0 - 2.0 - _eps(samples))\n        assert np.all(samples < 1.0 - 1.0 + _eps(samples))\n        assert (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_subtract_stochastic_parameter_elementwise(self):\n        param = iap.Subtract(iap.Deterministic(1.0),\n                             (1.0, 2.0),\n                             elementwise=True)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.0 - 2.0 - _eps(samples))\n        assert np.all(samples < 1.0 - 1.0 + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_subtract_from_stochastic_parameter(self):\n        param = iap.Subtract(iap.Uniform(1.0, 2.0), 1.0, elementwise=False)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.0 - 1.0 - _eps(samples))\n        assert np.all(samples < 2.0 - 1.0 + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_subtract_from_stochastic_parameter_elementwise(self):\n        param = iap.Subtract(iap.Uniform(1.0, 2.0), 1.0, elementwise=True)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.0 - 1.0 - _eps(samples))\n        assert np.all(samples < 2.0 - 1.0 + _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n\nclass TestPower(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Power(iap.Deterministic(0), 1, elementwise=False)\n        expected = \"Power(%s, %s, False)\" % (\n            str(param.other_param),\n            str(param.val)\n        )\n        assert \"Deterministic(int 0)\" in str(param)\n        assert \"Deterministic(int 1)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_pairs(self):\n        values = [\n            -100, -54, -1, 0, 1, 54, 100,\n            -100.0, -54.0, -1.0, 0.0, 1.0, 54.0, 100.0\n        ]\n        exponents = [-2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2]\n\n        for base, exponent in itertools.product(values, exponents):\n            if base < 0 and ia.is_single_float(exponent):\n                continue\n            if base == 0 and exponent < 0:\n                continue\n\n            with self.subTest(base=base, exponent=exponent):\n                p = iap.Power(iap.Deterministic(base), exponent)\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert (\n                    base ** exponent - _eps(sample)\n                    < sample <\n                    base ** exponent + _eps(sample)\n                )\n                assert samples.dtype.kind == \"f\"\n                assert np.allclose(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float64) + base ** exponent\n                )\n\n    def test_pairs_both_deterministic(self):\n        values = [\n            -100, -54, -1, 0, 1, 54, 100,\n            -100.0, -54.0, -1.0, 0.0, 1.0, 54.0, 100.0\n        ]\n        exponents = [-2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2]\n\n        for base, exponent in itertools.product(values, exponents):\n            if base < 0 and ia.is_single_float(exponent):\n                continue\n            if base == 0 and exponent < 0:\n                continue\n\n            with self.subTest(base=base, exponent=exponent):\n                p = iap.Power(iap.Deterministic(base), iap.Deterministic(exponent))\n\n                sample = p.draw_sample()\n                samples = p.draw_samples((2, 3))\n\n                assert (\n                    base ** exponent - _eps(sample)\n                    < sample <\n                    base ** exponent + _eps(sample)\n                )\n                assert samples.dtype.kind == \"f\"\n                assert np.allclose(\n                    samples,\n                    np.zeros((2, 3), dtype=np.float64) + base ** exponent\n                )\n\n    def test_exponent_is_stochastic_parameter(self):\n        param = iap.Power(iap.Deterministic(1.5),\n                          (1.0, 2.0),\n                          elementwise=False)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.5 ** 1.0 - 2 * _eps(samples))\n        assert np.all(samples < 1.5 ** 2.0 + 2 * _eps(samples))\n        assert (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_exponent_is_stochastic_parameter_elementwise(self):\n        param = iap.Power(iap.Deterministic(1.5),\n                          (1.0, 2.0),\n                          elementwise=True)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.5 ** 1.0 - 2 * _eps(samples))\n        assert np.all(samples < 1.5 ** 2.0 + 2 * _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_value_is_uniform(self):\n        param = iap.Power(iap.Uniform(1.0, 2.0), 1.0, elementwise=False)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.0 ** 1.0 - 2 * _eps(samples))\n        assert np.all(samples < 2.0 ** 1.0 + 2 * _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n    def test_value_is_uniform_elementwise(self):\n        param = iap.Power(iap.Uniform(1.0, 2.0), 1.0, elementwise=True)\n\n        samples = param.draw_samples((10, 20))\n        samples_sorted = np.sort(samples.flatten())\n\n        assert samples.shape == (10, 20)\n        assert np.all(samples > 1.0 ** 1.0 - 2 * _eps(samples))\n        assert np.all(samples < 2.0 ** 1.0 + 2 * _eps(samples))\n        assert not (\n            samples_sorted[0] - _eps(samples_sorted[0])\n            < samples_sorted[-1] <\n            samples_sorted[0] + _eps(samples_sorted[0])\n        )\n\n\nclass TestAbsolute(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Absolute(iap.Deterministic(0))\n        expected = \"Absolute(%s)\" % (str(param.other_param),)\n        assert \"Deterministic(int 0)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_fixed_values(self):\n        simple_values = [-1.5, -1, -1.0, -0.1, 0, 0.0, 0.1, 1, 1.0, 1.5]\n\n        for value in simple_values:\n            with self.subTest(value=value):\n                param = iap.Absolute(iap.Deterministic(value))\n\n                sample = param.draw_sample()\n                samples = param.draw_samples((10, 5))\n\n                assert sample.shape == tuple()\n                assert samples.shape == (10, 5)\n                if ia.is_single_float(value):\n                    assert (\n                        abs(value) - _eps(sample)\n                        < sample <\n                        abs(value) + _eps(sample)\n                    )\n                    assert np.all(abs(value) - _eps(samples) < samples)\n                    assert np.all(samples < abs(value) + _eps(samples))\n                else:\n                    assert sample == abs(value)\n                    assert np.all(samples == abs(value))\n\n    def test_value_is_stochastic_parameter(self):\n        param = iap.Absolute(iap.Choice([-3, -1, 1, 3]))\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((10, 10))\n        samples_uq = np.sort(np.unique(samples))\n\n        assert sample.shape == tuple()\n        assert sample in [3, 1]\n        assert samples.shape == (10, 10)\n        assert len(samples_uq) == 2\n        assert samples_uq[0] == 1 and samples_uq[1] == 3\n\n\nclass TestRandomSign(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.RandomSign(iap.Deterministic(0), 0.5)\n        expected = \"RandomSign(%s, 0.50)\" % (str(param.other_param),)\n        assert \"Deterministic(int 0)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_value_is_deterministic(self):\n        param = iap.RandomSign(iap.Deterministic(1))\n\n        samples = param.draw_samples((1000,))\n        n_positive = np.sum(samples == 1)\n        n_negative = np.sum(samples == -1)\n\n        assert samples.shape == (1000,)\n        assert n_positive + n_negative == 1000\n        assert 350 < n_positive < 750\n\n    def test_value_is_deterministic_many_samples(self):\n        param = iap.RandomSign(iap.Deterministic(1))\n\n        seen = [0, 0]\n        for _ in sm.xrange(1000):\n            sample = param.draw_sample()\n            assert sample.shape == tuple()\n            if sample == 1:\n                seen[1] += 1\n            else:\n                seen[0] += 1\n        n_negative, n_positive = seen\n\n        assert n_positive + n_negative == 1000\n        assert 350 < n_positive < 750\n\n    def test_value_is_stochastic_parameter(self):\n        param = iap.RandomSign(iap.Choice([1, 2]))\n\n        samples = param.draw_samples((4000,))\n        seen = [0, 0, 0, 0]\n        seen[0] = np.sum(samples == -2)\n        seen[1] = np.sum(samples == -1)\n        seen[2] = np.sum(samples == 1)\n        seen[3] = np.sum(samples == 2)\n\n        assert np.sum(seen) == 4000\n        assert all([700 < v < 1300 for v in seen])\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.RandomSign(iap.Choice([1, 2]))\n\n        samples1 = param.draw_samples((100, 10),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((100, 10),\n                                      random_state=iarandom.RNG(1234))\n\n        assert samples1.shape == (100, 10)\n        assert samples2.shape == (100, 10)\n        assert np.array_equal(samples1, samples2)\n        assert np.sum(samples1 == -2) > 50\n        assert np.sum(samples1 == -1) > 50\n        assert np.sum(samples1 == 1) > 50\n        assert np.sum(samples1 == 2) > 50\n\n\nclass TestForceSign(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.ForceSign(iap.Deterministic(0), True, \"invert\", 1)\n        expected = \"ForceSign(%s, True, invert, 1)\" % (str(param.other_param),)\n        assert \"Deterministic(int 0)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_single_sample_positive(self):\n        param = iap.ForceSign(iap.Deterministic(1), positive=True,\n                              mode=\"invert\")\n\n        sample = param.draw_sample()\n\n        assert sample.shape == tuple()\n        assert sample == 1\n\n    def test_single_sample_negative(self):\n        param = iap.ForceSign(iap.Deterministic(1), positive=False,\n                              mode=\"invert\")\n\n        sample = param.draw_sample()\n\n        assert sample.shape == tuple()\n        assert sample == -1\n\n    def test_many_samples_positive(self):\n        param = iap.ForceSign(iap.Deterministic(1), positive=True,\n                              mode=\"invert\")\n\n        samples = param.draw_samples(100)\n\n        assert samples.shape == (100,)\n        assert np.all(samples == 1)\n\n    def test_many_samples_negative(self):\n        param = iap.ForceSign(iap.Deterministic(1), positive=False,\n                              mode=\"invert\")\n\n        samples = param.draw_samples(100)\n\n        assert samples.shape == (100,)\n        assert np.all(samples == -1)\n\n    def test_many_samples_negative_value_to_positive(self):\n        param = iap.ForceSign(iap.Deterministic(-1), positive=True,\n                              mode=\"invert\")\n\n        samples = param.draw_samples(100)\n\n        assert samples.shape == (100,)\n        assert np.all(samples == 1)\n\n    def test_many_samples_negative_value_to_negative(self):\n        param = iap.ForceSign(iap.Deterministic(-1), positive=False,\n                              mode=\"invert\")\n\n        samples = param.draw_samples(100)\n\n        assert samples.shape == (100,)\n        assert np.all(samples == -1)\n\n    def test_many_samples_stochastic_value_to_positive(self):\n        param = iap.ForceSign(iap.Choice([-2, 1]), positive=True,\n                              mode=\"invert\")\n\n        samples = param.draw_samples(1000)\n        n_twos = np.sum(samples == 2)\n        n_ones = np.sum(samples == 1)\n\n        assert samples.shape == (1000,)\n        assert n_twos + n_ones == 1000\n        assert 200 < n_twos < 700\n        assert 200 < n_ones < 700\n\n    def test_many_samples_stochastic_value_to_positive_reroll(self):\n        param = iap.ForceSign(iap.Choice([-2, 1]), positive=True,\n                              mode=\"reroll\")\n\n        samples = param.draw_samples(1000)\n        n_twos = np.sum(samples == 2)\n        n_ones = np.sum(samples == 1)\n\n        assert samples.shape == (1000,)\n        assert n_twos + n_ones == 1000\n        assert n_twos > 0\n        assert n_ones > 0\n\n    def test_many_samples_stochastic_value_to_positive_reroll_max_count(self):\n        param = iap.ForceSign(iap.Choice([-2, 1]), positive=True,\n                              mode=\"reroll\", reroll_count_max=100)\n\n        samples = param.draw_samples(100)\n        n_twos = np.sum(samples == 2)\n        n_ones = np.sum(samples == 1)\n\n        assert samples.shape == (100,)\n        assert n_twos + n_ones == 100\n        assert n_twos < 5\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.ForceSign(iap.Choice([-2, 1]),\n                              positive=True,\n                              mode=\"invert\")\n\n        samples1 = param.draw_samples((100, 10),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((100, 10),\n                                      random_state=iarandom.RNG(1234))\n\n        assert samples1.shape == (100, 10)\n        assert samples2.shape == (100, 10)\n        assert np.array_equal(samples1, samples2)\n\n\nclass TestPositive(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_many_samples_reroll(self):\n        param = iap.Positive(iap.Deterministic(-1),\n                             mode=\"reroll\",\n                             reroll_count_max=1)\n\n        samples = param.draw_samples((100,))\n\n        assert samples.shape == (100,)\n        assert np.all(samples == 1)\n\n\nclass TestNegative(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test_many_samples_reroll(self):\n        param = iap.Negative(iap.Deterministic(1),\n                             mode=\"reroll\",\n                             reroll_count_max=1)\n\n        samples = param.draw_samples((100,))\n\n        assert samples.shape == (100,)\n        assert np.all(samples == -1)\n\n\nclass TestIterativeNoiseAggregator(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.IterativeNoiseAggregator(iap.Deterministic(0),\n                                             iterations=(1, 3),\n                                             aggregation_method=\"max\")\n        expected = \"IterativeNoiseAggregator(%s, %s, %s)\" % (\n            str(param.other_param),\n            str(param.iterations),\n            str(param.aggregation_method)\n        )\n        assert \"Deterministic(int 0)\" in str(param)\n        assert \"Deterministic(int 1)\" in str(param)\n        assert \"Deterministic(int 3)\" in str(param)\n        assert \"Deterministic(max)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_value_is_deterministic_max_1_iter(self):\n        param = iap.IterativeNoiseAggregator(iap.Deterministic(1),\n                                             iterations=1,\n                                             aggregation_method=\"max\")\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((2, 4))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (2, 4)\n        assert sample == 1\n        assert np.all(samples == 1)\n\n    def test_value_is_stochastic_avg_200_iter(self):\n        param = iap.IterativeNoiseAggregator(iap.Choice([0, 50]),\n                                             iterations=200,\n                                             aggregation_method=\"avg\")\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((2, 4))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (2, 4)\n        assert 25 - 10 < sample < 25 + 10\n        assert np.all(np.logical_and(25 - 10 < samples, samples < 25 + 10))\n\n    def test_value_is_stochastic_max_100_iter(self):\n        param = iap.IterativeNoiseAggregator(iap.Choice([0, 50]),\n                                             iterations=100,\n                                             aggregation_method=\"max\")\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((2, 4))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (2, 4)\n        assert sample == 50\n        assert np.all(samples == 50)\n\n    def test_value_is_stochastic_min_100_iter(self):\n        param = iap.IterativeNoiseAggregator(iap.Choice([0, 50]),\n                                             iterations=100,\n                                             aggregation_method=\"min\")\n\n        sample = param.draw_sample()\n        samples = param.draw_samples((2, 4))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (2, 4)\n        assert sample == 0\n        assert np.all(samples == 0)\n\n    def test_value_is_stochastic_avg_or_max_100_iter_evaluate_counts(self):\n        seen = [0, 0, 0, 0]\n        for _ in sm.xrange(100):\n            param = iap.IterativeNoiseAggregator(\n                iap.Choice([0, 50]),\n                iterations=100,\n                aggregation_method=[\"avg\", \"max\"])\n            samples = param.draw_samples((1, 1))\n            diff_0 = abs(0 - samples[0, 0])\n            diff_25 = abs(25 - samples[0, 0])\n            diff_50 = abs(50 - samples[0, 0])\n            if diff_25 < 10.0:\n                seen[0] += 1\n            elif diff_50 < _eps(samples):\n                seen[1] += 1\n            elif diff_0 < _eps(samples):\n                seen[2] += 1\n            else:\n                seen[3] += 1\n\n        assert seen[2] <= 2  # around 0.0\n        assert seen[3] <= 2  # 0.0+eps <= x < 15.0 or 35.0 < x < 50.0 or >50.0\n        assert 50 - 20 < seen[0] < 50 + 20\n        assert 50 - 20 < seen[1] < 50 + 20\n\n    def test_value_is_stochastic_avg_tuple_as_iter_evaluate_histograms(self):\n        # iterations as tuple\n        param = iap.IterativeNoiseAggregator(\n            iap.Uniform(-1.0, 1.0),\n            iterations=(1, 100),\n            aggregation_method=\"avg\")\n\n        diffs = []\n        for _ in sm.xrange(100):\n            samples = param.draw_samples((1, 1))\n            diff = abs(samples[0, 0] - 0.0)\n            diffs.append(diff)\n\n        nb_bins = 3\n        hist, _ = np.histogram(diffs, bins=nb_bins, range=(-1.0, 1.0),\n                               density=False)\n\n        assert hist[1] > hist[0]\n        assert hist[1] > hist[2]\n\n    def test_value_is_stochastic_max_list_as_iter_evaluate_counts(self):\n        # iterations as list\n        seen = [0, 0]\n        for _ in sm.xrange(400):\n            param = iap.IterativeNoiseAggregator(\n                iap.Choice([0, 50]),\n                iterations=[1, 100],\n                aggregation_method=[\"max\"])\n            samples = param.draw_samples((1, 1))\n            diff_0 = abs(0 - samples[0, 0])\n            diff_50 = abs(50 - samples[0, 0])\n            if diff_50 < _eps(samples):\n                seen[0] += 1\n            elif diff_0 < _eps(samples):\n                seen[1] += 1\n            else:\n                assert False\n\n        assert 300 - 50 < seen[0] < 300 + 50\n        assert 100 - 50 < seen[1] < 100 + 50\n\n    def test_value_is_stochastic_all_100_iter(self):\n        # test ia.ALL as aggregation_method\n        # note that each method individually and list of methods are already\n        # tested, so no in depth test is needed here\n        param = iap.IterativeNoiseAggregator(\n            iap.Choice([0, 50]), iterations=100, aggregation_method=ia.ALL)\n\n        assert isinstance(param.aggregation_method, iap.Choice)\n        assert len(param.aggregation_method.a) == 3\n        assert [v in param.aggregation_method.a for v in [\"min\", \"avg\", \"max\"]]\n\n    def test_value_is_stochastic_max_2_iter(self):\n        param = iap.IterativeNoiseAggregator(\n            iap.Choice([0, 50]), iterations=2, aggregation_method=\"max\")\n\n        samples = param.draw_samples((2, 1000))\n        nb_0 = np.sum(samples == 0)\n        nb_50 = np.sum(samples == 50)\n\n        assert nb_0 + nb_50 == 2 * 1000\n        assert 0.25 - 0.05 < nb_0 / (2 * 1000) < 0.25 + 0.05\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.IterativeNoiseAggregator(\n            iap.Choice([0, 50]), iterations=5, aggregation_method=\"avg\")\n\n        samples1 = param.draw_samples((100, 10),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((100, 10),\n                                      random_state=iarandom.RNG(1234))\n\n        assert samples1.shape == (100, 10)\n        assert samples2.shape == (100, 10)\n        assert np.allclose(samples1, samples2)\n\n    def test_stochastic_param_as_aggregation_method(self):\n        param = iap.IterativeNoiseAggregator(\n            iap.Choice([0, 50]),\n            iterations=5,\n            aggregation_method=iap.Deterministic(\"max\"))\n\n        assert isinstance(param.aggregation_method, iap.Deterministic)\n        assert param.aggregation_method.value == \"max\"\n\n    def test_bad_datatype_for_aggregation_method(self):\n        with self.assertRaises(Exception) as context:\n            _ = iap.IterativeNoiseAggregator(\n                iap.Choice([0, 50]), iterations=5, aggregation_method=False)\n\n        self.assertTrue(\n            \"Expected aggregation_method to be\" in str(context.exception))\n\n    def test_bad_datatype_for_iterations(self):\n        with self.assertRaises(Exception) as context:\n            _ = iap.IterativeNoiseAggregator(\n                iap.Choice([0, 50]),\n                iterations=False,\n                aggregation_method=\"max\")\n\n        self.assertTrue(\"Expected iterations to be\" in str(context.exception))\n\n\nclass TestSigmoid(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n    def test___init__(self):\n        param = iap.Sigmoid(\n            iap.Deterministic(0),\n            threshold=(-10, 10),\n            activated=True,\n            mul=1,\n            add=0\n        )\n        expected = \"Sigmoid(%s, %s, %s, 1, 0)\" % (\n            str(param.other_param),\n            str(param.threshold),\n            str(param.activated)\n        )\n        assert \"Deterministic(int 0)\" in str(param)\n        assert \"Deterministic(int -10)\" in str(param)\n        assert \"Deterministic(int 1)\" in str(param)\n        assert (\n            param.__str__()\n            == param.__repr__()\n            == expected\n        )\n\n    def test_activated_is_true(self):\n        param = iap.Sigmoid(\n            iap.Deterministic(5),\n            add=0,\n            mul=1,\n            threshold=0.5,\n            activated=True)\n\n        expected = 1 / (1 + np.exp(-(5 * 1 + 0 - 0.5)))\n        sample = param.draw_sample()\n        samples = param.draw_samples((5, 10))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (5, 10)\n        assert expected - _eps(sample) < sample < expected + _eps(sample)\n        assert np.all(\n            np.logical_and(\n                expected - _eps(samples) < samples,\n                samples < expected + _eps(samples)\n            )\n        )\n\n    def test_activated_is_false(self):\n        param = iap.Sigmoid(\n            iap.Deterministic(5),\n            add=0,\n            mul=1,\n            threshold=0.5,\n            activated=False)\n\n        expected = 5\n        sample = param.draw_sample()\n        samples = param.draw_samples((5, 10))\n\n        assert sample.shape == tuple()\n        assert samples.shape == (5, 10)\n        assert expected - _eps(sample) < sample < expected + _eps(sample)\n        assert np.all(\n            np.logical_and(\n                expected - _eps(sample) < samples,\n                samples < expected + _eps(sample)\n            )\n        )\n\n    def test_activated_is_probabilistic(self):\n        param = iap.Sigmoid(\n            iap.Deterministic(5),\n            add=0,\n            mul=1,\n            threshold=0.5,\n            activated=0.5)\n\n        expected_first = 5\n        expected_second = 1 / (1 + np.exp(-(5 * 1 + 0 - 0.5)))\n        seen = [0, 0]\n        for _ in sm.xrange(1000):\n            sample = param.draw_sample()\n            diff_first = abs(sample - expected_first)\n            diff_second = abs(sample - expected_second)\n            if diff_first < _eps(sample):\n                seen[0] += 1\n            elif diff_second < _eps(sample):\n                seen[1] += 1\n            else:\n                assert False\n\n        assert 500 - 150 < seen[0] < 500 + 150\n        assert 500 - 150 < seen[1] < 500 + 150\n\n    def test_value_is_stochastic_param(self):\n        param = iap.Sigmoid(\n            iap.Choice([1, 10]),\n            add=0,\n            mul=1,\n            threshold=0.5,\n            activated=True)\n\n        expected_first = 1 / (1 + np.exp(-(1 * 1 + 0 - 0.5)))\n        expected_second = 1 / (1 + np.exp(-(10 * 1 + 0 - 0.5)))\n        seen = [0, 0]\n        for _ in sm.xrange(1000):\n            sample = param.draw_sample()\n            diff_first = abs(sample - expected_first)\n            diff_second = abs(sample - expected_second)\n            if diff_first < _eps(sample):\n                seen[0] += 1\n            elif diff_second < _eps(sample):\n                seen[1] += 1\n            else:\n                assert False\n\n        assert 500 - 150 < seen[0] < 500 + 150\n        assert 500 - 150 < seen[1] < 500 + 150\n\n    def test_mul_add_threshold_with_various_fixed_values(self):\n        muls = [0.1, 1, 10.3]\n        adds = [-5.7, -0.0734, 0, 0.0734, 5.7]\n        vals = [-1, -0.7, 0, 0.7, 1]\n        threshs = [-5.7, -0.0734, 0, 0.0734, 5.7]\n        for mul, add, val, thresh in itertools.product(muls, adds, vals,\n                                                       threshs):\n            with self.subTest(mul=mul, add=add, val=val, threshold=thresh):\n                param = iap.Sigmoid(\n                    iap.Deterministic(val),\n                    add=add,\n                    mul=mul,\n                    threshold=thresh)\n\n                sample = param.draw_sample()\n                samples = param.draw_samples((2, 3))\n                dt = sample.dtype\n                val_ = np.array([val], dtype=dt)\n                mul_ = np.array([mul], dtype=dt)\n                add_ = np.array([add], dtype=dt)\n                thresh_ = np.array([thresh], dtype=dt)\n                expected = (\n                    1 / (\n                        1 + np.exp(\n                            -(val_ * mul_ + add_ - thresh_)\n                        )\n                    )\n                )\n\n                assert sample.shape == tuple()\n                assert samples.shape == (2, 3)\n                assert (\n                    expected - 5*_eps(sample)\n                    < sample <\n                    expected + 5*_eps(sample)\n                )\n                assert np.all(\n                    np.logical_and(\n                        expected - 5*_eps(sample) < samples,\n                        samples < expected + 5*_eps(sample)\n                    )\n                )\n\n    def test_samples_same_values_for_same_seeds(self):\n        param = iap.Sigmoid(\n            iap.Choice([1, 10]),\n            add=0,\n            mul=1,\n            threshold=0.5,\n            activated=True)\n\n        samples1 = param.draw_samples((100, 10),\n                                      random_state=iarandom.RNG(1234))\n        samples2 = param.draw_samples((100, 10),\n                                      random_state=iarandom.RNG(1234))\n\n        assert samples1.shape == (100, 10)\n        assert samples2.shape == (100, 10)\n        assert np.array_equal(samples1, samples2)\n"
  },
  {
    "path": "test/test_random.py",
    "content": "from __future__ import print_function, division, absolute_import\n\nimport copy as copylib\nimport sys\n# unittest only added in 3.4 self.subTest()\nif sys.version_info[0] < 3 or sys.version_info[1] < 4:\n    import unittest2 as unittest\nelse:\n    import unittest\n# unittest.mock is not available in 2.7 (though unittest2 might contain it?)\ntry:\n    import unittest.mock as mock\nexcept ImportError:\n    import mock\n\nimport numpy as np\n\nimport imgaug as ia\nimport imgaug.augmenters as iaa\nfrom imgaug.testutils import reseed\nimport imgaug.random as iarandom\n\nNP_VERSION = np.__version__\nIS_NP_117_OR_HIGHER = (\n    NP_VERSION.startswith(\"2.\")\n    or NP_VERSION.startswith(\"1.25\")\n    or NP_VERSION.startswith(\"1.24\")\n    or NP_VERSION.startswith(\"1.23\")\n    or NP_VERSION.startswith(\"1.22\")\n    or NP_VERSION.startswith(\"1.21\")\n    or NP_VERSION.startswith(\"1.20\")\n    or NP_VERSION.startswith(\"1.19\")\n    or NP_VERSION.startswith(\"1.18\")\n    or NP_VERSION.startswith(\"1.17\")\n)\n\n\nclass _Base(unittest.TestCase):\n    def setUp(self):\n        reseed()\n\n\nclass TestConstants(_Base):\n    def test_supports_new_np_rng_style_is_true(self):\n        assert iarandom.SUPPORTS_NEW_NP_RNG_STYLE is IS_NP_117_OR_HIGHER\n\n    def test_global_rng(self):\n        iarandom.get_global_rng()  # creates global RNG upon first call\n        assert iarandom.GLOBAL_RNG is not None\n\n\nclass TestRNG(_Base):\n    @mock.patch(\"imgaug.random.normalize_generator_\")\n    def test___init___calls_normalize_mocked(self, mock_norm):\n        _ = iarandom.RNG(0)\n        mock_norm.assert_called_once_with(0)\n\n    def test___init___with_rng(self):\n        rng1 = iarandom.RNG(0)\n        rng2 = iarandom.RNG(rng1)\n\n        assert rng2.generator is rng1.generator\n\n    @mock.patch(\"imgaug.random.get_generator_state\")\n    def test_state_getter_mocked(self, mock_get):\n        mock_get.return_value = \"mock\"\n        rng = iarandom.RNG(0)\n        result = rng.state\n        assert result == \"mock\"\n        mock_get.assert_called_once_with(rng.generator)\n\n    @mock.patch(\"imgaug.random.RNG.set_state_\")\n    def test_state_setter_mocked(self, mock_set):\n        rng = iarandom.RNG(0)\n        state = {\"foo\"}\n        rng.state = state\n        mock_set.assert_called_once_with(state)\n\n    @mock.patch(\"imgaug.random.set_generator_state_\")\n    def test_set_state__mocked(self, mock_set):\n        rng = iarandom.RNG(0)\n        state = {\"foo\"}\n        result = rng.set_state_(state)\n        assert result is rng\n        mock_set.assert_called_once_with(rng.generator, state)\n\n    @mock.patch(\"imgaug.random.set_generator_state_\")\n    def test_use_state_of__mocked(self, mock_set):\n        rng1 = iarandom.RNG(0)\n        rng2 = mock.MagicMock()\n        state = {\"foo\"}\n        rng2.state = state\n        result = rng1.use_state_of_(rng2)\n        assert result == rng1\n        mock_set.assert_called_once_with(rng1.generator, state)\n\n    @mock.patch(\"imgaug.random.get_global_rng\")\n    def test_is_global__is_global__rng_mocked(self, mock_get):\n        rng1 = iarandom.RNG(0)\n        rng2 = iarandom.RNG(rng1.generator)\n        mock_get.return_value = rng2\n        assert rng1.is_global_rng() is True\n\n    @mock.patch(\"imgaug.random.get_global_rng\")\n    def test_is_global_rng__is_not_global__mocked(self, mock_get):\n        rng1 = iarandom.RNG(0)\n        # different instance with same state/seed should still be viewed as\n        # different by the method\n        rng2 = iarandom.RNG(0)\n        mock_get.return_value = rng2\n        assert rng1.is_global_rng() is False\n\n    @mock.patch(\"imgaug.random.get_global_rng\")\n    def test_equals_global_rng__is_global__mocked(self, mock_get):\n        rng1 = iarandom.RNG(0)\n        rng2 = iarandom.RNG(0)\n        mock_get.return_value = rng2\n        assert rng1.equals_global_rng() is True\n\n    @mock.patch(\"imgaug.random.get_global_rng\")\n    def test_equals_global_rng__is_not_global__mocked(self, mock_get):\n        rng1 = iarandom.RNG(0)\n        rng2 = iarandom.RNG(1)\n        mock_get.return_value = rng2\n        assert rng1.equals_global_rng() is False\n\n    @mock.patch(\"imgaug.random.generate_seed_\")\n    def test_generate_seed__mocked(self, mock_gen):\n        rng = iarandom.RNG(0)\n        mock_gen.return_value = -1\n        seed = rng.generate_seed_()\n        assert seed == -1\n        mock_gen.assert_called_once_with(rng.generator)\n\n    @mock.patch(\"imgaug.random.generate_seeds_\")\n    def test_generate_seeds__mocked(self, mock_gen):\n        rng = iarandom.RNG(0)\n        mock_gen.return_value = [-1, -2]\n        seeds = rng.generate_seeds_(2)\n        assert seeds == [-1, -2]\n        mock_gen.assert_called_once_with(rng.generator, 2)\n\n    @mock.patch(\"imgaug.random.reset_generator_cache_\")\n    def test_reset_cache__mocked(self, mock_reset):\n        rng = iarandom.RNG(0)\n        result = rng.reset_cache_()\n        assert result is rng\n        mock_reset.assert_called_once_with(rng.generator)\n\n    @mock.patch(\"imgaug.random.derive_generators_\")\n    def test_derive_rng__mocked(self, mock_derive):\n        gen = iarandom.convert_seed_to_generator(0)\n        mock_derive.return_value = [gen]\n        rng = iarandom.RNG(0)\n        result = rng.derive_rng_()\n        assert result.generator is gen\n        mock_derive.assert_called_once_with(rng.generator, 1)\n\n    @mock.patch(\"imgaug.random.derive_generators_\")\n    def test_derive_rngs__mocked(self, mock_derive):\n        gen1 = iarandom.convert_seed_to_generator(0)\n        gen2 = iarandom.convert_seed_to_generator(1)\n        mock_derive.return_value = [gen1, gen2]\n        rng = iarandom.RNG(0)\n        result = rng.derive_rngs_(2)\n        assert result[0].generator is gen1\n        assert result[1].generator is gen2\n        mock_derive.assert_called_once_with(rng.generator, 2)\n\n    @mock.patch(\"imgaug.random.is_generator_equal_to\")\n    def test_equals_mocked(self, mock_equal):\n        mock_equal.return_value = \"foo\"\n        rng1 = iarandom.RNG(0)\n        rng2 = iarandom.RNG(1)\n        result = rng1.equals(rng2)\n        assert result == \"foo\"\n        mock_equal.assert_called_once_with(rng1.generator, rng2.generator)\n\n    def test_equals_identical_generators(self):\n        rng1 = iarandom.RNG(0)\n        rng2 = iarandom.RNG(rng1)\n        assert rng1.equals(rng2)\n\n    def test_equals_with_similar_generators(self):\n        rng1 = iarandom.RNG(0)\n        rng2 = iarandom.RNG(0)\n        assert rng1.equals(rng2)\n\n    def test_equals_with_different_generators(self):\n        rng1 = iarandom.RNG(0)\n        rng2 = iarandom.RNG(1)\n        assert not rng1.equals(rng2)\n\n    def test_equals_with_advanced_generator(self):\n        rng1 = iarandom.RNG(0)\n        rng2 = iarandom.RNG(0)\n        rng2.advance_()\n        assert not rng1.equals(rng2)\n\n    @mock.patch(\"imgaug.random.advance_generator_\")\n    def test_advance__mocked(self, mock_advance):\n        rng = iarandom.RNG(0)\n        result = rng.advance_()\n        assert result is rng\n        mock_advance.assert_called_once_with(rng.generator)\n\n    @mock.patch(\"imgaug.random.copy_generator\")\n    def test_copy_mocked(self, mock_copy):\n        rng1 = iarandom.RNG(0)\n        rng2 = iarandom.RNG(1)\n        mock_copy.return_value = rng2.generator\n        result = rng1.copy()\n        assert result.generator is rng2.generator\n        mock_copy.assert_called_once_with(rng1.generator)\n\n    @mock.patch(\"imgaug.random.RNG.copy\")\n    @mock.patch(\"imgaug.random.RNG.is_global_rng\")\n    def test_copy_unless_global_rng__is_global__mocked(self, mock_is_global,\n                                                       mock_copy):\n        rng = iarandom.RNG(0)\n        mock_is_global.return_value = True\n        mock_copy.return_value = \"foo\"\n        result = rng.copy_unless_global_rng()\n        assert result is rng\n        mock_is_global.assert_called_once_with()\n        assert mock_copy.call_count == 0\n\n    @mock.patch(\"imgaug.random.RNG.copy\")\n    @mock.patch(\"imgaug.random.RNG.is_global_rng\")\n    def test_copy_unless_global_rng__is_not_global__mocked(self, mock_is_global,\n                                                           mock_copy):\n        rng = iarandom.RNG(0)\n        mock_is_global.return_value = False\n        mock_copy.return_value = \"foo\"\n        result = rng.copy_unless_global_rng()\n        assert result is \"foo\"\n        mock_is_global.assert_called_once_with()\n        mock_copy.assert_called_once_with()\n\n    def test_duplicate(self):\n        rng = iarandom.RNG(0)\n        rngs = rng.duplicate(1)\n        assert rngs == [rng]\n\n    def test_duplicate_two_entries(self):\n        rng = iarandom.RNG(0)\n        rngs = rng.duplicate(2)\n        assert rngs == [rng, rng]\n\n    @mock.patch(\"imgaug.random.create_fully_random_generator\")\n    def test_create_fully_random_mocked(self, mock_create):\n        gen = iarandom.convert_seed_to_generator(0)\n        mock_create.return_value = gen\n        rng = iarandom.RNG.create_fully_random()\n        mock_create.assert_called_once_with()\n        assert rng.generator is gen\n\n    @mock.patch(\"imgaug.random.derive_generators_\")\n    def test_create_pseudo_random__mocked(self, mock_get):\n        rng_glob = iarandom.get_global_rng()\n        rng = iarandom.RNG(0)\n        mock_get.return_value = [rng.generator]\n        result = iarandom.RNG.create_pseudo_random_()\n        assert result.generator is rng.generator\n        mock_get.assert_called_once_with(rng_glob.generator, 1)\n\n    @mock.patch(\"imgaug.random.polyfill_integers\")\n    def test_integers_mocked(self, mock_func):\n        mock_func.return_value = \"foo\"\n        rng = iarandom.RNG(0)\n\n        result = rng.integers(low=0, high=1, size=(1,), dtype=\"int64\",\n                              endpoint=True)\n\n        assert result == \"foo\"\n        mock_func.assert_called_once_with(\n            rng.generator, low=0, high=1, size=(1,), dtype=\"int64\",\n            endpoint=True)\n\n    @mock.patch(\"imgaug.random.polyfill_random\")\n    def test_random_mocked(self, mock_func):\n        mock_func.return_value = \"foo\"\n        rng = iarandom.RNG(0)\n        out = np.zeros((1,), dtype=\"float64\")\n\n        result = rng.random(size=(1,), dtype=\"float64\", out=out)\n\n        assert result == \"foo\"\n        mock_func.assert_called_once_with(\n            rng.generator, size=(1,), dtype=\"float64\", out=out)\n\n    # TODO below test for generator methods are all just mock-based, add\n    #      non-mocked versions\n\n    def test_choice_mocked(self):\n        self._test_sampling_func(\"choice\", a=[1, 2, 3], size=(1,),\n                                 replace=False, p=[0.1, 0.2, 0.7])\n\n    def test_bytes_mocked(self):\n        self._test_sampling_func(\"bytes\", length=[10])\n\n    def test_shuffle_mocked(self):\n        mock_gen = mock.MagicMock()\n        rng = iarandom.RNG(0)\n        rng.generator = mock_gen\n\n        rng.shuffle([1, 2, 3])\n\n        mock_gen.shuffle.assert_called_once_with([1, 2, 3])\n\n    def test_permutation_mocked(self):\n        mock_gen = mock.MagicMock()\n        rng = iarandom.RNG(0)\n        rng.generator = mock_gen\n        mock_gen.permutation.return_value = \"foo\"\n\n        result = rng.permutation([1, 2, 3])\n\n        assert result == \"foo\"\n        mock_gen.permutation.assert_called_once_with([1, 2, 3])\n\n    def test_beta_mocked(self):\n        self._test_sampling_func(\"beta\", a=1.0, b=2.0, size=(1,))\n\n    def test_binomial_mocked(self):\n        self._test_sampling_func(\"binomial\", n=10, p=0.1, size=(1,))\n\n    def test_chisquare_mocked(self):\n        self._test_sampling_func(\"chisquare\", df=2, size=(1,))\n\n    def test_dirichlet_mocked(self):\n        self._test_sampling_func(\"dirichlet\", alpha=0.1, size=(1,))\n\n    def test_exponential_mocked(self):\n        self._test_sampling_func(\"exponential\", scale=1.1, size=(1,))\n\n    def test_f_mocked(self):\n        self._test_sampling_func(\"f\", dfnum=1, dfden=2, size=(1,))\n\n    def test_gamma_mocked(self):\n        self._test_sampling_func(\"gamma\", shape=1, scale=1.2, size=(1,))\n\n    def test_geometric_mocked(self):\n        self._test_sampling_func(\"geometric\", p=0.5, size=(1,))\n\n    def test_gumbel_mocked(self):\n        self._test_sampling_func(\"gumbel\", loc=0.1, scale=1.1, size=(1,))\n\n    def test_hypergeometric_mocked(self):\n        self._test_sampling_func(\"hypergeometric\", ngood=2, nbad=4, nsample=6,\n                                 size=(1,))\n\n    def test_laplace_mocked(self):\n        self._test_sampling_func(\"laplace\", loc=0.5, scale=1.5, size=(1,))\n\n    def test_logistic_mocked(self):\n        self._test_sampling_func(\"logistic\", loc=0.5, scale=1.5, size=(1,))\n\n    def test_lognormal_mocked(self):\n        self._test_sampling_func(\"lognormal\", mean=0.5, sigma=1.5, size=(1,))\n\n    def test_logseries_mocked(self):\n        self._test_sampling_func(\"logseries\", p=0.5, size=(1,))\n\n    def test_multinomial_mocked(self):\n        self._test_sampling_func(\"multinomial\", n=5, pvals=0.5, size=(1,))\n\n    def test_multivariate_normal_mocked(self):\n        self._test_sampling_func(\"multivariate_normal\", mean=0.5, cov=1.0,\n                                 size=(1,), check_valid=\"foo\", tol=1e-2)\n\n    def test_negative_binomial_mocked(self):\n        self._test_sampling_func(\"negative_binomial\", n=10, p=0.5, size=(1,))\n\n    def test_noncentral_chisquare_mocked(self):\n        self._test_sampling_func(\"noncentral_chisquare\", df=0.5, nonc=1.0,\n                                 size=(1,))\n\n    def test_noncentral_f_mocked(self):\n        self._test_sampling_func(\"noncentral_f\", dfnum=0.5, dfden=1.5,\n                                 nonc=2.0, size=(1,))\n\n    def test_normal_mocked(self):\n        self._test_sampling_func(\"normal\", loc=0.5, scale=1.0, size=(1,))\n\n    def test_pareto_mocked(self):\n        self._test_sampling_func(\"pareto\", a=0.5, size=(1,))\n\n    def test_poisson_mocked(self):\n        self._test_sampling_func(\"poisson\", lam=1.5, size=(1,))\n\n    def test_power_mocked(self):\n        self._test_sampling_func(\"power\", a=0.5, size=(1,))\n\n    def test_rayleigh_mocked(self):\n        self._test_sampling_func(\"rayleigh\", scale=1.5, size=(1,))\n\n    def test_standard_cauchy_mocked(self):\n        self._test_sampling_func(\"standard_cauchy\", size=(1,))\n\n    def test_standard_exponential_np117_mocked(self):\n        fname = \"standard_exponential\"\n\n        arr = np.zeros((1,), dtype=\"float16\")\n        args = []\n        kwargs = {\"size\": (1,), \"dtype\": \"float16\", \"method\": \"foo\",\n                  \"out\": arr}\n\n        mock_gen = mock.MagicMock()\n        getattr(mock_gen, fname).return_value = \"foo\"\n        rng = iarandom.RNG(0)\n        rng.generator = mock_gen\n        rng._is_new_rng_style = True\n\n        result = getattr(rng, fname)(*args, **kwargs)\n\n        assert result == \"foo\"\n        getattr(mock_gen, fname).assert_called_once_with(*args, **kwargs)\n\n    def test_standard_exponential_np116_mocked(self):\n        fname = \"standard_exponential\"\n\n        arr_out = np.zeros((1,), dtype=\"float16\")\n        arr_result = np.ones((1,), dtype=\"float16\")\n\n        def _side_effect(x):\n            return arr_result\n\n        args = []\n        kwargs = {\"size\": (1,), \"dtype\": \"float16\", \"method\": \"foo\",\n                  \"out\": arr_out}\n        kwargs_subcall = {\"size\": (1,)}\n\n        mock_gen = mock.MagicMock()\n        mock_gen.astype.side_effect = _side_effect\n        getattr(mock_gen, fname).return_value = mock_gen\n        rng = iarandom.RNG(0)\n        rng.generator = mock_gen\n        rng._is_new_rng_style = False\n\n        result = getattr(rng, fname)(*args, **kwargs)\n\n        getattr(mock_gen, fname).assert_called_once_with(*args,\n                                                         **kwargs_subcall)\n        mock_gen.astype.assert_called_once_with(\"float16\")\n        assert np.allclose(result, arr_result)\n        assert np.allclose(arr_out, arr_result)\n\n    def test_standard_gamma_np117_mocked(self):\n        fname = \"standard_gamma\"\n\n        arr = np.zeros((1,), dtype=\"float16\")\n        args = []\n        kwargs = {\"shape\": 1.0, \"size\": (1,), \"dtype\": \"float16\", \"out\": arr}\n\n        mock_gen = mock.MagicMock()\n        getattr(mock_gen, fname).return_value = \"foo\"\n        rng = iarandom.RNG(0)\n        rng.generator = mock_gen\n        rng._is_new_rng_style = True\n\n        result = getattr(rng, fname)(*args, **kwargs)\n\n        assert result == \"foo\"\n        getattr(mock_gen, fname).assert_called_once_with(*args, **kwargs)\n\n    def test_standard_gamma_np116_mocked(self):\n        fname = \"standard_gamma\"\n\n        arr_out = np.zeros((1,), dtype=\"float16\")\n        arr_result = np.ones((1,), dtype=\"float16\")\n\n        def _side_effect(x):\n            return arr_result\n\n        args = []\n        kwargs = {\"shape\": 1.0, \"size\": (1,), \"dtype\": \"float16\",\n                  \"out\": arr_out}\n        kwargs_subcall = {\"shape\": 1.0, \"size\": (1,)}\n\n        mock_gen = mock.MagicMock()\n        mock_gen.astype.side_effect = _side_effect\n        getattr(mock_gen, fname).return_value = mock_gen\n        rng = iarandom.RNG(0)\n        rng.generator = mock_gen\n        rng._is_new_rng_style = False\n\n        result = getattr(rng, fname)(*args, **kwargs)\n\n        getattr(mock_gen, fname).assert_called_once_with(*args,\n                                                         **kwargs_subcall)\n        mock_gen.astype.assert_called_once_with(\"float16\")\n        assert np.allclose(result, arr_result)\n        assert np.allclose(arr_out, arr_result)\n\n    def test_standard_normal_np117_mocked(self):\n        fname = \"standard_normal\"\n\n        arr = np.zeros((1,), dtype=\"float16\")\n        args = []\n        kwargs = {\"size\": (1,), \"dtype\": \"float16\", \"out\": arr}\n\n        mock_gen = mock.MagicMock()\n        getattr(mock_gen, fname).return_value = \"foo\"\n        rng = iarandom.RNG(0)\n        rng.generator = mock_gen\n        rng._is_new_rng_style = True\n\n        result = getattr(rng, fname)(*args, **kwargs)\n\n        assert result == \"foo\"\n        getattr(mock_gen, fname).assert_called_once_with(*args, **kwargs)\n\n    def test_standard_normal_np116_mocked(self):\n        fname = \"standard_normal\"\n\n        arr_out = np.zeros((1,), dtype=\"float16\")\n        arr_result = np.ones((1,), dtype=\"float16\")\n\n        def _side_effect(x):\n            return arr_result\n\n        args = []\n        kwargs = {\"size\": (1,), \"dtype\": \"float16\", \"out\": arr_out}\n        kwargs_subcall = {\"size\": (1,)}\n\n        mock_gen = mock.MagicMock()\n        mock_gen.astype.side_effect = _side_effect\n        getattr(mock_gen, fname).return_value = mock_gen\n        rng = iarandom.RNG(0)\n        rng.generator = mock_gen\n        rng._is_new_rng_style = False\n\n        result = getattr(rng, fname)(*args, **kwargs)\n\n        getattr(mock_gen, fname).assert_called_once_with(*args,\n                                                         **kwargs_subcall)\n        mock_gen.astype.assert_called_once_with(\"float16\")\n        assert np.allclose(result, arr_result)\n        assert np.allclose(arr_out, arr_result)\n\n    def test_standard_t_mocked(self):\n        self._test_sampling_func(\"standard_t\", df=1.5, size=(1,))\n\n    def test_triangular_mocked(self):\n        self._test_sampling_func(\"triangular\", left=1.0, mode=1.5, right=2.0,\n                                 size=(1,))\n\n    def test_uniform_mocked(self):\n        self._test_sampling_func(\"uniform\", low=0.5, high=1.5, size=(1,))\n\n    def test_vonmises_mocked(self):\n        self._test_sampling_func(\"vonmises\", mu=1.0, kappa=1.5, size=(1,))\n\n    def test_wald_mocked(self):\n        self._test_sampling_func(\"wald\", mean=0.5, scale=1.0, size=(1,))\n\n    def test_weibull_mocked(self):\n        self._test_sampling_func(\"weibull\", a=1.0, size=(1,))\n\n    def test_zipf_mocked(self):\n        self._test_sampling_func(\"zipf\", a=1.0, size=(1,))\n\n    @classmethod\n    def _test_sampling_func(cls, fname, *args, **kwargs):\n        mock_gen = mock.MagicMock()\n        getattr(mock_gen, fname).return_value = \"foo\"\n        rng = iarandom.RNG(0)\n        rng.generator = mock_gen\n\n        result = getattr(rng, fname)(*args, **kwargs)\n\n        assert result == \"foo\"\n        getattr(mock_gen, fname).assert_called_once_with(*args, **kwargs)\n\n    #\n    # outdated methods from RandomState\n    #\n\n    def test_rand_mocked(self):\n        self._test_sampling_func_alias(\"rand\", \"random\", 1, 2, 3)\n\n    def test_randint_mocked(self):\n        self._test_sampling_func_alias(\"randint\", \"integers\", 0, 100)\n\n    def randn(self):\n        self._test_sampling_func_alias(\"randn\", \"standard_normal\", 1, 2, 3)\n\n    def random_integers(self):\n        self._test_sampling_func_alias(\"random_integers\", \"integers\", 1, 2)\n\n    def random_sample(self):\n        self._test_sampling_func_alias(\"random_sample\", \"uniform\", (1, 2, 3))\n\n    def tomaxint(self):\n        self._test_sampling_func_alias(\"tomaxint\", \"integers\", (1, 2, 3))\n\n    def test_rand(self):\n        result = iarandom.RNG(0).rand(10, 20, 3)\n        assert result.dtype.name == \"float32\"\n        assert result.shape == (10, 20, 3)\n        assert np.all(result >= 0.0)\n        assert np.all(result <= 1.0)\n        assert np.any(result > 0.0)\n        assert np.any(result < 1.0)\n\n    def test_randint(self):\n        result = iarandom.RNG(0).randint(10, 100, size=(10, 20, 3))\n        assert result.dtype.name == \"int32\"\n        assert result.shape == (10, 20, 3)\n        assert np.all(result >= 10)\n        assert np.all(result <= 99)\n        assert np.any(result > 10)\n        assert np.any(result < 99)\n\n    def test_randn(self):\n        result = iarandom.RNG(0).randn(10, 50, 3)\n        assert result.dtype.name == \"float32\"\n        assert result.shape == (10, 50, 3)\n        assert np.any(result > 0.5)\n        assert np.any(result < -0.5)\n        assert np.average(np.logical_or(result < 2.0, result > -2.0)) > 0.5\n\n    def test_random_integers(self):\n        result = iarandom.RNG(0).random_integers(10, 100, size=(10, 20, 3))\n        assert result.dtype.name == \"int32\"\n        assert result.shape == (10, 20, 3)\n        assert np.all(result >= 10)\n        assert np.all(result <= 100)\n        assert np.any(result > 10)\n        assert np.any(result < 100)\n\n    def test_random_integers__no_high(self):\n        result = iarandom.RNG(0).random_integers(100, size=(10, 20, 3))\n        assert result.dtype.name == \"int32\"\n        assert result.shape == (10, 20, 3)\n        assert np.all(result >= 1)\n        assert np.all(result <= 100)\n        assert np.any(result > 1)\n        assert np.any(result < 100)\n\n    def test_random_sample(self):\n        result = iarandom.RNG(0).random_sample((10, 20, 3))\n        assert result.dtype.name == \"float64\"\n        assert result.shape == (10, 20, 3)\n        assert np.all(result >= 0.0)\n        assert np.all(result <= 1.0)\n        assert np.any(result > 0.0)\n        assert np.any(result < 1.0)\n\n    def test_tomaxint(self):\n        result = iarandom.RNG(0).tomaxint((10, 200, 3))\n        assert result.dtype.name == \"int32\"\n        assert result.shape == (10, 200, 3)\n        assert np.all(result >= 0)\n        assert np.any(result > 10000)\n\n    @classmethod\n    def _test_sampling_func_alias(cls, fname_alias, fname_subcall, *args,\n                                  **kwargs):\n\n        rng = iarandom.RNG(0)\n        mock_func = mock.Mock()\n        mock_func.return_value = \"foo\"\n        setattr(rng, fname_subcall, mock_func)\n\n        result = getattr(rng, fname_alias)(*args, **kwargs)\n\n        assert result == \"foo\"\n        assert mock_func.call_count == 1\n\n\nclass Test_supports_new_numpy_rng_style(_Base):\n    def test_call(self):\n        assert iarandom.supports_new_numpy_rng_style() is IS_NP_117_OR_HIGHER\n\n\nclass Test_get_global_rng(_Base):\n    def test_call(self):\n        iarandom.seed(0)\n\n        rng = iarandom.get_global_rng()\n\n        expected = iarandom.RNG(0)\n        assert rng is not None\n        assert rng.equals(expected)\n\n\nclass Test_seed(_Base):\n    @mock.patch(\"imgaug.random._seed_np117_\")\n    @mock.patch(\"imgaug.random._seed_np116_\")\n    def test_mocked_call(self, mock_np116, mock_np117):\n        iarandom.seed(1)\n\n        if IS_NP_117_OR_HIGHER:\n            mock_np117.assert_called_once_with(1)\n            assert mock_np116.call_count == 0\n        else:\n            mock_np116.assert_called_once_with(1)\n            assert mock_np117.call_count == 0\n\n    def test_integrationtest(self):\n        iarandom.seed(1)\n        assert iarandom.GLOBAL_RNG.equals(iarandom.RNG(1))\n\n    def test_seed_affects_augmenters_created_after_its_call(self):\n        image = np.full((50, 50, 3), 128, dtype=np.uint8)\n\n        images_aug = []\n        for _ in np.arange(5):\n            iarandom.seed(100)\n            aug = iaa.AdditiveGaussianNoise(scale=50, per_channel=True)\n            images_aug.append(aug(image=image))\n\n        # assert all images identical\n        for other_image_aug in images_aug[1:]:\n            assert np.array_equal(images_aug[0], other_image_aug)\n\n        # but different seed must lead to different image\n        iarandom.seed(101)\n        aug = iaa.AdditiveGaussianNoise(scale=50, per_channel=True)\n        image_aug = aug(image=image)\n        assert not np.array_equal(images_aug[0], image_aug)\n\n    def test_seed_affects_augmenters_created_before_its_call(self):\n        image = np.full((50, 50, 3), 128, dtype=np.uint8)\n\n        images_aug = []\n        for _ in np.arange(5):\n            aug = iaa.AdditiveGaussianNoise(scale=50, per_channel=True)\n            iarandom.seed(100)\n            images_aug.append(aug(image=image))\n\n        # assert all images identical\n        for other_image_aug in images_aug[1:]:\n            assert np.array_equal(images_aug[0], other_image_aug)\n\n        # but different seed must lead to different image\n        aug = iaa.AdditiveGaussianNoise(scale=50, per_channel=True)\n        iarandom.seed(101)\n        image_aug = aug(image=image)\n        assert not np.array_equal(images_aug[0], image_aug)\n\n\nclass Test_normalize_generator(_Base):\n    @mock.patch(\"imgaug.random.normalize_generator_\")\n    def test_mocked_call(self, mock_subfunc):\n        mock_subfunc.return_value = \"foo\"\n        inputs = [\"bar\"]\n\n        result = iarandom.normalize_generator(inputs)\n\n        assert mock_subfunc.call_count == 1\n        assert mock_subfunc.call_args[0][0] is not inputs\n        assert mock_subfunc.call_args[0][0] == inputs\n        assert result == \"foo\"\n\n\nclass Test_normalize_generator_(_Base):\n    @mock.patch(\"imgaug.random._normalize_generator_np117_\")\n    @mock.patch(\"imgaug.random._normalize_generator_np116_\")\n    def test_mocked_call(self, mock_np116, mock_np117):\n        mock_np116.return_value = \"np116\"\n        mock_np117.return_value = \"np117\"\n\n        result = iarandom.normalize_generator_(None)\n\n        if IS_NP_117_OR_HIGHER:\n            assert result == \"np117\"\n            mock_np117.assert_called_once_with(None)\n            assert mock_np116.call_count == 0\n        else:\n            assert result == \"np116\"\n            mock_np116.assert_called_once_with(None)\n            assert mock_np117.call_count == 0\n\n    def test_called_with_none(self):\n        result = iarandom.normalize_generator_(None)\n        assert result is iarandom.get_global_rng().generator\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"SeedSequence does not exist in numpy <=1.16\")\n    def test_called_with_seed_sequence(self):\n        seedseq = np.random.SeedSequence(0)\n\n        result = iarandom.normalize_generator_(seedseq)\n\n        expected = np.random.Generator(\n            iarandom.BIT_GENERATOR(np.random.SeedSequence(0)))\n        assert iarandom.is_generator_equal_to(result, expected)\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"BitGenerator does not exist in numpy <=1.16\")\n    def test_called_with_bit_generator(self):\n        bgen = iarandom.BIT_GENERATOR(np.random.SeedSequence(0))\n\n        result = iarandom.normalize_generator_(bgen)\n\n        assert result.bit_generator is bgen\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Generator does not exist in numpy <=1.16\")\n    def test_called_with_generator(self):\n        gen = np.random.Generator(\n            iarandom.BIT_GENERATOR(np.random.SeedSequence(0))\n        )\n\n        result = iarandom.normalize_generator_(gen)\n\n        assert result is gen\n\n    def test_called_with_random_state(self):\n        rs = np.random.RandomState(0)\n\n        result = iarandom.normalize_generator_(rs)\n\n        if IS_NP_117_OR_HIGHER:\n            seed = iarandom.generate_seed_(np.random.RandomState(0))\n            expected = iarandom.convert_seed_to_generator(seed)\n            assert iarandom.is_generator_equal_to(result, expected)\n        else:\n            assert result is rs\n\n    def test_called_int(self):\n        seed = 0\n\n        result = iarandom.normalize_generator_(seed)\n\n        expected = iarandom.convert_seed_to_generator(seed)\n        assert iarandom.is_generator_equal_to(result, expected)\n\n\nclass Test_convert_seed_to_generator(_Base):\n    @mock.patch(\"imgaug.random._convert_seed_to_generator_np117\")\n    @mock.patch(\"imgaug.random._convert_seed_to_generator_np116\")\n    def test_mocked_call(self, mock_np116, mock_np117):\n        mock_np116.return_value = \"np116\"\n        mock_np117.return_value = \"np117\"\n\n        result = iarandom.convert_seed_to_generator(1)\n\n        if IS_NP_117_OR_HIGHER:\n            assert result == \"np117\"\n            mock_np117.assert_called_once_with(1)\n            assert mock_np116.call_count == 0\n        else:\n            assert result == \"np116\"\n            mock_np116.assert_called_once_with(1)\n            assert mock_np117.call_count == 0\n\n    def test_call(self):\n        gen = iarandom.convert_seed_to_generator(1)\n\n        if IS_NP_117_OR_HIGHER:\n            expected = np.random.Generator(\n                iarandom.BIT_GENERATOR(np.random.SeedSequence(1)))\n\n            assert iarandom.is_generator_equal_to(gen, expected)\n        else:\n            expected = np.random.RandomState(1)\n            assert iarandom.is_generator_equal_to(gen, expected)\n\n\nclass Test_convert_seed_sequence_to_generator(_Base):\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"SeedSequence does not exist in numpy <=1.16\")\n    def test_call(self):\n        seedseq = np.random.SeedSequence(1)\n\n        gen = iarandom.convert_seed_sequence_to_generator(seedseq)\n\n        expected = np.random.Generator(\n            iarandom.BIT_GENERATOR(np.random.SeedSequence(1)))\n        assert iarandom.is_generator_equal_to(gen, expected)\n\n\nclass Test_create_pseudo_random_generator_(_Base):\n    def test_call(self):\n        global_gen = copylib.deepcopy(iarandom.get_global_rng().generator)\n\n        gen = iarandom.create_pseudo_random_generator_()\n\n        expected = iarandom.convert_seed_to_generator(\n            iarandom.generate_seed_(global_gen))\n        assert iarandom.is_generator_equal_to(gen, expected)\n\n\nclass Test_create_fully_random_generator(_Base):\n    @mock.patch(\"imgaug.random._create_fully_random_generator_np117\")\n    @mock.patch(\"imgaug.random._create_fully_random_generator_np116\")\n    def test_mocked_call(self, mock_np116, mock_np117):\n        mock_np116.return_value = \"np116\"\n        mock_np117.return_value = \"np117\"\n\n        result = iarandom.create_fully_random_generator()\n\n        if IS_NP_117_OR_HIGHER:\n            assert result == \"np117\"\n            mock_np117.assert_called_once_with()\n            assert mock_np116.call_count == 0\n        else:\n            assert result == \"np116\"\n            mock_np116.assert_called_once_with()\n            assert mock_np117.call_count == 0\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_np117_mocked(self):\n        dummy_bitgen = np.random.SFC64(1)\n\n        with mock.patch(\"numpy.random.SFC64\") as mock_bitgen:\n            mock_bitgen.return_value = dummy_bitgen\n\n            result = iarandom._create_fully_random_generator_np117()\n\n        assert mock_bitgen.call_count == 1\n        assert iarandom.is_generator_equal_to(\n            result, np.random.Generator(dummy_bitgen))\n\n    def test_np116_mocked(self):\n        dummy_rs = np.random.RandomState(1)\n\n        with mock.patch(\"numpy.random.RandomState\") as mock_rs:\n            mock_rs.return_value = dummy_rs\n\n            result = iarandom._create_fully_random_generator_np116()\n\n        assert mock_rs.call_count == 1\n        assert iarandom.is_generator_equal_to(result, np.random.RandomState(1))\n\n\nclass Test_generate_seed_(_Base):\n    @mock.patch(\"imgaug.random.generate_seeds_\")\n    def test_mocked_call(self, mock_seeds):\n        gen = iarandom.convert_seed_to_generator(0)\n\n        _ = iarandom.generate_seed_(gen)\n\n        mock_seeds.assert_called_once_with(gen, 1)\n\n\nclass Test_generate_seeds_(_Base):\n    @mock.patch(\"imgaug.random.polyfill_integers\")\n    def test_mocked_call(self, mock_integers):\n        gen = iarandom.convert_seed_to_generator(0)\n\n        _ = iarandom.generate_seeds_(gen, 10)\n\n        mock_integers.assert_called_once_with(\n            gen, iarandom.SEED_MIN_VALUE, iarandom.SEED_MAX_VALUE, size=(10,))\n\n    def test_call(self):\n        gen = iarandom.convert_seed_to_generator(0)\n\n        seeds = iarandom.generate_seeds_(gen, 2)\n\n        assert len(seeds) == 2\n        assert ia.is_np_array(seeds)\n        assert seeds.dtype.name == \"int32\"\n\n\nclass Test_copy_generator(_Base):\n    @mock.patch(\"imgaug.random._copy_generator_np116\")\n    def test_mocked_call_with_random_state(self, mock_np116):\n        mock_np116.return_value = \"np116\"\n        gen = np.random.RandomState(1)\n\n        gen_copy = iarandom.copy_generator(gen)\n\n        assert gen_copy == \"np116\"\n        mock_np116.assert_called_once_with(gen)\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    @mock.patch(\"imgaug.random._copy_generator_np117\")\n    def test_mocked_call_with_generator(self, mock_np117):\n        mock_np117.return_value = \"np117\"\n        gen = np.random.Generator(iarandom.BIT_GENERATOR(1))\n\n        gen_copy = iarandom.copy_generator(gen)\n\n        assert gen_copy == \"np117\"\n        mock_np117.assert_called_once_with(gen)\n\n    def test_call_with_random_state(self):\n        gen = np.random.RandomState(1)\n\n        gen_copy = iarandom._copy_generator_np116(gen)\n\n        assert gen is not gen_copy\n        assert iarandom.is_generator_equal_to(gen, gen_copy)\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_call_with_generator(self):\n        gen = np.random.Generator(iarandom.BIT_GENERATOR(1))\n\n        gen_copy = iarandom._copy_generator_np117(gen)\n\n        assert gen is not gen_copy\n        assert iarandom.is_generator_equal_to(gen, gen_copy)\n\n\nclass Test_copy_generator_unless_global_generator(_Base):\n    @mock.patch(\"imgaug.random.get_global_rng\")\n    @mock.patch(\"imgaug.random.copy_generator\")\n    def test_mocked_gen_is_global(self, mock_copy, mock_get_global_rng):\n        gen = iarandom.convert_seed_to_generator(1)\n        mock_copy.return_value = \"foo\"\n        mock_get_global_rng.return_value = iarandom.RNG(gen)\n\n        result = iarandom.copy_generator_unless_global_generator(gen)\n\n        assert mock_get_global_rng.call_count == 1\n        assert mock_copy.call_count == 0\n        assert result is gen\n\n    @mock.patch(\"imgaug.random.get_global_rng\")\n    @mock.patch(\"imgaug.random.copy_generator\")\n    def test_mocked_gen_is_not_global(self, mock_copy, mock_get_global_rng):\n        gen1 = iarandom.convert_seed_to_generator(1)\n        gen2 = iarandom.convert_seed_to_generator(2)\n        mock_copy.return_value = \"foo\"\n        mock_get_global_rng.return_value = iarandom.RNG(gen2)\n\n        result = iarandom.copy_generator_unless_global_generator(gen1)\n\n        assert mock_get_global_rng.call_count == 1\n        mock_copy.assert_called_once_with(gen1)\n        assert result == \"foo\"\n\n\nclass Test_reset_generator_cache_(_Base):\n    @mock.patch(\"imgaug.random._reset_generator_cache_np117_\")\n    @mock.patch(\"imgaug.random._reset_generator_cache_np116_\")\n    def test_mocked_call(self, mock_np116, mock_np117):\n        mock_np116.return_value = \"np116\"\n        mock_np117.return_value = \"np117\"\n        gen = iarandom.convert_seed_to_generator(1)\n\n        result = iarandom.reset_generator_cache_(gen)\n\n        if IS_NP_117_OR_HIGHER:\n            assert result == \"np117\"\n            mock_np117.assert_called_once_with(gen)\n            assert mock_np116.call_count == 0\n        else:\n            assert result == \"np116\"\n            mock_np116.assert_called_once_with(gen)\n            assert mock_np117.call_count == 0\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_call_np117(self):\n        gen = iarandom.convert_seed_to_generator(1)\n        gen_without_cache_copy = copylib.deepcopy(gen)\n\n        state = iarandom._get_generator_state_np117(gen)\n        state[\"has_uint32\"] = 1\n        gen_with_cache = copylib.deepcopy(gen)\n        iarandom.set_generator_state_(gen_with_cache, state)\n        gen_with_cache_copy = copylib.deepcopy(gen_with_cache)\n\n        gen_cache_reset = iarandom.reset_generator_cache_(gen_with_cache)\n\n        assert iarandom.is_generator_equal_to(gen_cache_reset,\n                                              gen_without_cache_copy)\n        assert not iarandom.is_generator_equal_to(gen_cache_reset,\n                                                  gen_with_cache_copy)\n\n    def test_call_np116(self):\n        gen = np.random.RandomState(1)\n        gen_without_cache_copy = copylib.deepcopy(gen)\n\n        state = iarandom._get_generator_state_np116(gen)\n        state = list(state)\n        state[-2] = 1\n        gen_with_cache = copylib.deepcopy(gen)\n        iarandom.set_generator_state_(gen_with_cache, tuple(state))\n        gen_with_cache_copy = copylib.deepcopy(gen_with_cache)\n\n        gen_cache_reset = iarandom.reset_generator_cache_(gen_with_cache)\n\n        assert iarandom.is_generator_equal_to(gen_cache_reset,\n                                              gen_without_cache_copy)\n        assert not iarandom.is_generator_equal_to(gen_cache_reset,\n                                                  gen_with_cache_copy)\n\n\nclass Test_derive_generator_(_Base):\n    @mock.patch(\"imgaug.random.derive_generators_\")\n    def test_mocked_call(self, mock_derive_gens):\n        mock_derive_gens.return_value = [\"foo\"]\n        gen = iarandom.convert_seed_to_generator(1)\n\n        gen_derived = iarandom.derive_generator_(gen)\n\n        mock_derive_gens.assert_called_once_with(gen, n=1)\n        assert gen_derived == \"foo\"\n\n    def test_integration(self):\n        gen = iarandom.convert_seed_to_generator(1)\n        gen_copy = copylib.deepcopy(gen)\n\n        gen_derived = iarandom.derive_generator_(gen)\n\n        assert not iarandom.is_generator_equal_to(gen_derived, gen_copy)\n        # should have advanced the state\n        assert not iarandom.is_generator_equal_to(gen_copy, gen)\n\n\nclass Test_derive_generators_(_Base):\n    @mock.patch(\"imgaug.random._derive_generators_np117_\")\n    @mock.patch(\"imgaug.random._derive_generators_np116_\")\n    def test_mocked_call(self, mock_np116, mock_np117):\n        mock_np116.return_value = \"np116\"\n        mock_np117.return_value = \"np117\"\n        gen = iarandom.convert_seed_to_generator(1)\n\n        result = iarandom.derive_generators_(gen, 1)\n\n        if isinstance(gen, np.random.RandomState):\n            assert result == \"np116\"\n            mock_np116.assert_called_once_with(gen, n=1)\n            assert mock_np117.call_count == 0\n        else:\n            assert result == \"np117\"\n            mock_np117.assert_called_once_with(gen, n=1)\n            assert mock_np116.call_count == 0\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_call_np117(self):\n        gen = iarandom.convert_seed_to_generator(1)\n        gen_copy = copylib.deepcopy(gen)\n\n        result = iarandom.derive_generators_(gen, 2)\n\n        assert len(result) == 2\n        assert np.all([isinstance(gen, np.random.Generator)\n                       for gen in result])\n        assert not iarandom.is_generator_equal_to(result[0], gen_copy)\n        assert not iarandom.is_generator_equal_to(result[1], gen_copy)\n        assert not iarandom.is_generator_equal_to(result[0], result[1])\n        # derive should advance state\n        assert not iarandom.is_generator_equal_to(gen, gen_copy)\n\n    def test_call_np116(self):\n        gen = np.random.RandomState(1)\n        gen_copy = copylib.deepcopy(gen)\n\n        result = iarandom.derive_generators_(gen, 2)\n\n        assert len(result) == 2\n        assert np.all([isinstance(gen, np.random.RandomState)\n                       for gen in result])\n        assert not iarandom.is_generator_equal_to(result[0], gen_copy)\n        assert not iarandom.is_generator_equal_to(result[1], gen_copy)\n        assert not iarandom.is_generator_equal_to(result[0], result[1])\n        # derive should advance state\n        assert not iarandom.is_generator_equal_to(gen, gen_copy)\n\n\nclass Test_get_generator_state(_Base):\n    @mock.patch(\"imgaug.random._get_generator_state_np117\")\n    @mock.patch(\"imgaug.random._get_generator_state_np116\")\n    def test_mocked_call(self, mock_np116, mock_np117):\n        mock_np116.return_value = \"np116\"\n        mock_np117.return_value = \"np117\"\n        gen = iarandom.convert_seed_to_generator(1)\n\n        result = iarandom.get_generator_state(gen)\n\n        if isinstance(gen, np.random.RandomState):\n            assert result == \"np116\"\n            mock_np116.assert_called_once_with(gen)\n            assert mock_np117.call_count == 0\n        else:\n            assert result == \"np117\"\n            mock_np117.assert_called_once_with(gen)\n            assert mock_np116.call_count == 0\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_call_np117(self):\n        gen = iarandom.convert_seed_to_generator(1)\n        state = iarandom.get_generator_state(gen)\n        assert str(state) == str(gen.bit_generator.state)\n\n    def test_call_np116(self):\n        gen = np.random.RandomState(1)\n        state = iarandom.get_generator_state(gen)\n        assert str(state) == str(gen.get_state())\n\n\nclass Test_set_generator_state_(_Base):\n    @mock.patch(\"imgaug.random._set_generator_state_np117_\")\n    @mock.patch(\"imgaug.random._set_generator_state_np116_\")\n    def test_mocked_call(self, mock_np116, mock_np117):\n        gen = iarandom.convert_seed_to_generator(1)\n        state = {\"state\": 0}\n\n        iarandom.set_generator_state_(gen, state)\n\n        if isinstance(gen, np.random.RandomState):\n            mock_np116.assert_called_once_with(gen, state)\n            assert mock_np117.call_count == 0\n        else:\n            mock_np117.assert_called_once_with(gen, state)\n            assert mock_np116.call_count == 0\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_call_np117(self):\n        gen1 = np.random.Generator(iarandom.BIT_GENERATOR(1))\n        gen2 = np.random.Generator(iarandom.BIT_GENERATOR(2))\n        gen1_copy = copylib.deepcopy(gen1)\n        gen2_copy = copylib.deepcopy(gen2)\n\n        iarandom._set_generator_state_np117_(\n            gen2, iarandom.get_generator_state(gen1))\n\n        assert iarandom.is_generator_equal_to(gen2, gen1)\n        assert iarandom.is_generator_equal_to(gen1, gen1_copy)\n        assert not iarandom.is_generator_equal_to(gen2, gen2_copy)\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_call_np117_via_samples(self):\n        gen1 = np.random.Generator(iarandom.BIT_GENERATOR(1))\n        gen2 = np.random.Generator(iarandom.BIT_GENERATOR(2))\n        gen1_copy = copylib.deepcopy(gen1)\n        gen2_copy = copylib.deepcopy(gen2)\n\n        iarandom._set_generator_state_np117_(\n            gen2, iarandom.get_generator_state(gen1))\n\n        samples1 = gen1.random(size=(100,))\n        samples2 = gen2.random(size=(100,))\n        samples1_copy = gen1_copy.random(size=(100,))\n        samples2_copy = gen2_copy.random(size=(100,))\n\n        assert np.allclose(samples1, samples2)\n        assert np.allclose(samples1, samples1_copy)\n        assert not np.allclose(samples2, samples2_copy)\n\n    def test_call_np116(self):\n        gen1 = np.random.RandomState(1)\n        gen2 = np.random.RandomState(2)\n        gen1_copy = copylib.deepcopy(gen1)\n        gen2_copy = copylib.deepcopy(gen2)\n\n        iarandom._set_generator_state_np116_(\n            gen2, iarandom.get_generator_state(gen1))\n\n        assert iarandom.is_generator_equal_to(gen2, gen1)\n        assert iarandom.is_generator_equal_to(gen1, gen1_copy)\n        assert not iarandom.is_generator_equal_to(gen2, gen2_copy)\n\n    def test_call_np116_via_samples(self):\n        gen1 = np.random.RandomState(1)\n        gen2 = np.random.RandomState(2)\n        gen1_copy = copylib.deepcopy(gen1)\n        gen2_copy = copylib.deepcopy(gen2)\n\n        iarandom._set_generator_state_np116_(\n            gen2, iarandom.get_generator_state(gen1))\n\n        samples1 = gen1.uniform(0.0, 1.0, size=(100,))\n        samples2 = gen2.uniform(0.0, 1.0, size=(100,))\n        samples1_copy = gen1_copy.uniform(0.0, 1.0, size=(100,))\n        samples2_copy = gen2_copy.uniform(0.0, 1.0, size=(100,))\n\n        assert np.allclose(samples1, samples2)\n        assert np.allclose(samples1, samples1_copy)\n        assert not np.allclose(samples2, samples2_copy)\n\n\nclass Test_is_generator_equal_to(_Base):\n    @mock.patch(\"imgaug.random._is_generator_equal_to_np117\")\n    @mock.patch(\"imgaug.random._is_generator_equal_to_np116\")\n    def test_mocked_call(self, mock_np116, mock_np117):\n        mock_np116.return_value = \"np116\"\n        mock_np117.return_value = \"np117\"\n        gen = iarandom.convert_seed_to_generator(1)\n\n        result = iarandom.is_generator_equal_to(gen, gen)\n\n        if isinstance(gen, np.random.RandomState):\n            assert result == \"np116\"\n            mock_np116.assert_called_once_with(gen, gen)\n            assert mock_np117.call_count == 0\n        else:\n            assert result == \"np117\"\n            mock_np117.assert_called_once_with(gen, gen)\n            assert mock_np116.call_count == 0\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_generator_is_identical_np117(self):\n        gen = np.random.Generator(iarandom.BIT_GENERATOR(1))\n\n        result = iarandom._is_generator_equal_to_np117(gen, gen)\n\n        assert result is True\n\n    def test_generator_is_identical_np116(self):\n        gen = np.random.RandomState(1)\n\n        result = iarandom._is_generator_equal_to_np116(gen, gen)\n\n        assert result is True\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_generator_created_with_same_seed_np117(self):\n        gen1 = np.random.Generator(iarandom.BIT_GENERATOR(1))\n        gen2 = np.random.Generator(iarandom.BIT_GENERATOR(1))\n\n        result = iarandom._is_generator_equal_to_np117(gen1, gen2)\n\n        assert result is True\n\n    def test_generator_created_with_same_seed_np116(self):\n        gen1 = np.random.RandomState(1)\n        gen2 = np.random.RandomState(1)\n\n        result = iarandom._is_generator_equal_to_np116(gen1, gen2)\n\n        assert result is True\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_generator_is_copy_of_itself_np117(self):\n        gen1 = np.random.Generator(iarandom.BIT_GENERATOR(1))\n\n        result = iarandom._is_generator_equal_to_np117(gen1,\n                                                       copylib.deepcopy(gen1))\n\n        assert result is True\n\n    def test_generator_is_copy_of_itself_np116(self):\n        gen1 = np.random.RandomState(1)\n\n        result = iarandom._is_generator_equal_to_np116(gen1,\n                                                       copylib.deepcopy(gen1))\n\n        assert result is True\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_generator_created_with_different_seed_np117(self):\n        gen1 = np.random.Generator(iarandom.BIT_GENERATOR(1))\n        gen2 = np.random.Generator(iarandom.BIT_GENERATOR(2))\n\n        result = iarandom._is_generator_equal_to_np117(gen1, gen2)\n\n        assert result is False\n\n    def test_generator_created_with_different_seed_np116(self):\n        gen1 = np.random.RandomState(1)\n        gen2 = np.random.RandomState(2)\n\n        result = iarandom._is_generator_equal_to_np116(gen1, gen2)\n\n        assert result is False\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_generator_modified_to_have_same_state_np117(self):\n        gen1 = np.random.Generator(iarandom.BIT_GENERATOR(1))\n        gen2 = np.random.Generator(iarandom.BIT_GENERATOR(2))\n        iarandom.set_generator_state_(gen2, iarandom.get_generator_state(gen1))\n\n        result = iarandom._is_generator_equal_to_np117(gen1, gen2)\n\n        assert result is True\n\n    def test_generator_modified_to_have_same_state_np116(self):\n        gen1 = np.random.RandomState(1)\n        gen2 = np.random.RandomState(2)\n        iarandom.set_generator_state_(gen2, iarandom.get_generator_state(gen1))\n\n        result = iarandom._is_generator_equal_to_np116(gen1, gen2)\n\n        assert result is True\n\n\nclass Test_advance_generator_(_Base):\n    @mock.patch(\"imgaug.random._advance_generator_np117_\")\n    @mock.patch(\"imgaug.random._advance_generator_np116_\")\n    def test_mocked_call(self, mock_np116, mock_np117):\n        gen = iarandom.convert_seed_to_generator(1)\n\n        iarandom.advance_generator_(gen)\n\n        if isinstance(gen, np.random.RandomState):\n            mock_np116.assert_called_once_with(gen)\n            assert mock_np117.call_count == 0\n        else:\n            mock_np117.assert_called_once_with(gen)\n            assert mock_np116.call_count == 0\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_call_np117(self):\n        gen = np.random.Generator(iarandom.BIT_GENERATOR(1))\n        gen_copy1 = copylib.deepcopy(gen)\n\n        iarandom._advance_generator_np117_(gen)\n        gen_copy2 = copylib.deepcopy(gen)\n\n        iarandom._advance_generator_np117_(gen)\n\n        assert iarandom.is_generator_equal_to(gen, copylib.deepcopy(gen))\n        assert not iarandom.is_generator_equal_to(gen_copy1, gen_copy2)\n        assert not iarandom.is_generator_equal_to(gen_copy2, gen)\n        assert not iarandom.is_generator_equal_to(gen_copy1, gen)\n\n    def test_call_np116(self):\n        gen = np.random.RandomState(1)\n        gen_copy1 = copylib.deepcopy(gen)\n\n        iarandom._advance_generator_np116_(gen)\n        gen_copy2 = copylib.deepcopy(gen)\n\n        iarandom._advance_generator_np116_(gen)\n\n        assert iarandom.is_generator_equal_to(gen, copylib.deepcopy(gen))\n        assert not iarandom.is_generator_equal_to(gen_copy1, gen_copy2)\n        assert not iarandom.is_generator_equal_to(gen_copy2, gen)\n        assert not iarandom.is_generator_equal_to(gen_copy1, gen)\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_samples_different_after_advance_np117(self):\n        gen = np.random.Generator(iarandom.BIT_GENERATOR(1))\n        gen_copy1 = copylib.deepcopy(gen)\n\n        # first advance\n        iarandom._advance_generator_np117_(gen)\n        gen_copy2 = copylib.deepcopy(gen)\n\n        # second advance\n        iarandom._advance_generator_np117_(gen)\n\n        sample_before = gen_copy1.uniform(0.0, 1.0)\n        sample_after = gen_copy2.uniform(0.0, 1.0)\n        sample_after_after = gen.uniform(0.0, 1.0)\n        assert not np.isclose(sample_after, sample_before, rtol=0)\n        assert not np.isclose(sample_after_after, sample_after, rtol=0)\n        assert not np.isclose(sample_after_after, sample_before, rtol=0)\n\n    def test_samples_different_after_advance_np116(self):\n        gen = np.random.RandomState(1)\n        gen_copy1 = copylib.deepcopy(gen)\n\n        # first advance\n        iarandom._advance_generator_np116_(gen)\n        gen_copy2 = copylib.deepcopy(gen)\n\n        # second advance\n        iarandom._advance_generator_np116_(gen)\n\n        sample_before = gen_copy1.uniform(0.0, 1.0)\n        sample_after = gen_copy2.uniform(0.0, 1.0)\n        sample_after_after = gen.uniform(0.0, 1.0)\n        assert not np.isclose(sample_after, sample_before, rtol=0)\n        assert not np.isclose(sample_after_after, sample_after, rtol=0)\n        assert not np.isclose(sample_after_after, sample_before, rtol=0)\n\n\nclass Test_polyfill_integers(_Base):\n    def test_mocked_standard_call_np116(self):\n        def side_effect(low, high=None, size=None, dtype='l'):\n            return \"np116\"\n\n        gen = mock.MagicMock()\n        gen.randint.side_effect = side_effect\n\n        result = iarandom.polyfill_integers(gen, 2, 2000, size=(10,),\n                                            dtype=\"int8\")\n\n        assert result == \"np116\"\n        gen.randint.assert_called_once_with(low=2, high=2000, size=(10,),\n                                            dtype=\"int8\")\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_mocked_standard_call_np117(self):\n        def side_effect(low, high=None, size=None, dtype='int64',\n                        endpoint=False):\n            return \"np117\"\n\n        gen = mock.MagicMock()\n        gen.integers.side_effect = side_effect\n        del gen.randint\n\n        result = iarandom.polyfill_integers(gen, 2, 2000, size=(10,),\n                                            dtype=\"int8\", endpoint=True)\n\n        assert result == \"np117\"\n        gen.integers.assert_called_once_with(low=2, high=2000, size=(10,),\n                                             dtype=\"int8\", endpoint=True)\n\n    def test_mocked_call_with_default_values_np116(self):\n        def side_effect(low, high=None, size=None, dtype='l'):\n            return \"np116\"\n\n        gen = mock.MagicMock()\n        gen.randint.side_effect = side_effect\n\n        result = iarandom.polyfill_integers(gen, 2)\n\n        assert result == \"np116\"\n        gen.randint.assert_called_once_with(low=2, high=None, size=None,\n                                            dtype=\"int32\")\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_mocked_call_with_default_values_np117(self):\n        def side_effect(low, high=None, size=None, dtype='int64',\n                        endpoint=False):\n            return \"np117\"\n\n        gen = mock.MagicMock()\n        gen.integers.side_effect = side_effect\n        del gen.randint\n\n        result = iarandom.polyfill_integers(gen, 2)\n\n        assert result == \"np117\"\n        gen.integers.assert_called_once_with(low=2, high=None, size=None,\n                                             dtype=\"int32\", endpoint=False)\n\n    def test_mocked_call_with_default_values_and_endpoint_np116(self):\n        def side_effect(low, high=None, size=None, dtype='l'):\n            return \"np116\"\n\n        gen = mock.MagicMock()\n        gen.randint.side_effect = side_effect\n\n        result = iarandom.polyfill_integers(gen, 2, endpoint=True)\n\n        assert result == \"np116\"\n        gen.randint.assert_called_once_with(low=0, high=3, size=None,\n                                            dtype=\"int32\")\n\n    def test_mocked_call_with_low_high_and_endpoint_np116(self):\n        def side_effect(low, high=None, size=None, dtype='l'):\n            return \"np116\"\n\n        gen = mock.MagicMock()\n        gen.randint.side_effect = side_effect\n\n        result = iarandom.polyfill_integers(gen, 2, 5, endpoint=True)\n\n        assert result == \"np116\"\n        gen.randint.assert_called_once_with(low=2, high=6, size=None,\n                                            dtype=\"int32\")\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_sampled_values_np117(self):\n        gen = np.random.Generator(iarandom.BIT_GENERATOR(1))\n\n        result = iarandom.polyfill_integers(gen, 1, 10, size=(1000,),\n                                            endpoint=False)\n\n        assert result.dtype.name == \"int32\"\n        assert np.all(result >= 1)\n        assert np.all(result < 10)\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_sampled_values_with_endpoint_np117(self):\n        gen = np.random.Generator(iarandom.BIT_GENERATOR(1))\n\n        result = iarandom.polyfill_integers(gen, 1, 10, size=(1000,),\n                                            endpoint=True)\n\n        assert result.dtype.name == \"int32\"\n        assert np.all(result >= 1)\n        assert np.all(result <= 10)\n\n    def test_sampled_values_np116(self):\n        gen = np.random.RandomState(1)\n\n        result = iarandom.polyfill_integers(gen, 1, 10, size=(1000,),\n                                            endpoint=False)\n\n        assert result.dtype.name == \"int32\"\n        assert np.all(result >= 1)\n        assert np.all(result < 10)\n\n    def test_sampled_values_with_endpoint_np116(self):\n        gen = np.random.RandomState(1)\n\n        result = iarandom.polyfill_integers(gen, 1, 10, size=(1000,),\n                                            endpoint=True)\n\n        assert result.dtype.name == \"int32\"\n        assert np.all(result >= 1)\n        assert np.all(result <= 10)\n\n\nclass Test_polyfill_random(_Base):\n    def test_mocked_standard_call_np116(self):\n        def side_effect(size=None):\n            return np.zeros((1,), dtype=\"float64\")\n\n        gen = mock.MagicMock()\n        gen.random_sample.side_effect = side_effect\n\n        result = iarandom.polyfill_random(gen, size=(10,), dtype=\"float16\")\n\n        assert result.dtype.name == \"float16\"\n        gen.random_sample.assert_called_once_with(\n            size=(10,))\n\n    def test_mocked_standard_call_np117(self):\n        def side_effect(size=None, dtype='d', out=None):\n            return \"np117\"\n\n        gen = mock.MagicMock()\n        gen.random.side_effect = side_effect\n        del gen.random_sample\n\n        result = iarandom.polyfill_random(gen, size=(10,), dtype=\"float16\")\n\n        assert result == \"np117\"\n        gen.random.assert_called_once_with(\n            size=(10,), dtype=\"float16\", out=None)\n\n    def test_mocked_call_with_out_arg_np116(self):\n        def side_effect(size=None):\n            return np.zeros((1,), dtype=\"float64\")\n\n        gen = mock.MagicMock()\n        gen.random_sample.side_effect = side_effect\n\n        out = np.empty((10,), dtype=\"float16\")\n        result = iarandom.polyfill_random(gen, size=(10,), dtype=\"float16\",\n                                          out=out)\n\n        assert result.dtype.name == \"float16\"\n        # np1.16 does not support an out arg, hence it is not part of the\n        # expected call\n        gen.random_sample.assert_called_once_with(size=(10,))\n\n    def test_mocked_call_with_out_arg_np117(self):\n        def side_effect(size=None, dtype='d', out=None):\n            return \"np117\"\n\n        gen = mock.MagicMock()\n        gen.random.side_effect = side_effect\n        del gen.random_sample\n\n        out = np.empty((10,), dtype=\"float16\")\n        result = iarandom.polyfill_random(gen, size=(10,), dtype=\"float16\",\n                                          out=out)\n\n        assert result == \"np117\"\n        gen.random.assert_called_once_with(size=(10,), dtype=\"float16\",\n                                           out=out)\n\n    def test_sampled_values_np116(self):\n        gen = np.random.RandomState(1)\n\n        result = iarandom.polyfill_random(gen, size=(1000,))\n\n        assert result.dtype.name == \"float32\"\n        assert np.all(result >= 0)\n        assert np.all(result < 1.0)\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_sampled_values_np117(self):\n        gen = np.random.Generator(iarandom.BIT_GENERATOR(1))\n\n        result = iarandom.polyfill_random(gen, size=(1000,))\n\n        assert result.dtype.name == \"float32\"\n        assert np.all(result >= 0)\n        assert np.all(result < 1.0)\n\n    def test_sampled_values_with_out_arg_np116(self):\n        gen = np.random.RandomState(1)\n\n        out = np.zeros((1000,), dtype=\"float32\")\n        result = iarandom.polyfill_random(gen, size=(1000,), out=out)\n\n        assert result.dtype.name == \"float32\"\n        assert np.all(result >= 0)\n        assert np.all(result < 1.0)\n\n        assert np.any(out > 0.9)\n        assert np.all(out >= 0)\n        assert np.all(out < 1.0)\n\n    @unittest.skipIf(not IS_NP_117_OR_HIGHER,\n                     \"Function uses classes from numpy 1.17+\")\n    def test_sampled_values_with_out_arg_np117(self):\n        gen = np.random.Generator(iarandom.BIT_GENERATOR(1))\n\n        out = np.zeros((1000,), dtype=\"float32\")\n        result = iarandom.polyfill_random(gen, size=(1000,), out=out)\n\n        assert result.dtype.name == \"float32\"\n        assert np.all(result >= 0)\n        assert np.all(result < 1.0)\n\n        assert np.any(out > 0.9)\n        assert np.all(out >= 0)\n        assert np.all(out < 1.0)\n"
  }
]