File size: 1,545 Bytes
c19ca42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from typing import Callable

import numpy as np
from typing_extensions import Concatenate, ParamSpec

ImageOp = Callable[[np.ndarray], np.ndarray]
"""
An image processing operation that takes an image and produces a new image.

The given image is guaranteed to *not* be modified.
"""


def clipped(op: ImageOp) -> ImageOp:
    """
    Ensures that all values in the returned image are between 0 and 1.
    """
    return lambda i: np.clip(op(i), 0, 1)


P = ParamSpec("P")


def to_op(fn: Callable[Concatenate[np.ndarray, P], np.ndarray]) -> Callable[P, ImageOp]:
    """
    Applies a form of currying to convert the given function into a constructor for an image operation.

    Example: Simple resize method could be defined as follows: `resize(np.ndarray, Size2D) -> np.ndarray`.
    It takes an image and its new size and returns the resized image.
    If we want to convert it to an image operation, we have to create a function with the following signature: `resize_op(Size2D) -> ImageOp`.
    The implementation of this function would be rather simple, it would simply take all arguments of `resize` except for the image like this:
    ```py
    def resize_op(size: Size2D) -> ImageOp:
        return lambda img: resize(img, size)
    ```
    `to_op` does exactly this transformation, but for any number of arguments.

    Note: This only works if the input image is the first argument of the given function.
    """

    def p(*args: P.args, **kwargs: P.kwargs) -> ImageOp:
        return lambda i: fn(i, *args, **kwargs)

    return p