Skip to content

pixano.features.utils.image

base64_to_image(base64_image)

Decode image from base64 to Pillow.

Expect the image to be formatted as "data:image/{image_format};base64,{base64}".

Parameters:

Name Type Description Default
base64_image str

Image as base64.

required

Returns:

Type Description
Image

Pillow image.

Source code in pixano/features/utils/image.py
def base64_to_image(base64_image: str) -> Image.Image:
    """Decode image from base64 to Pillow.

    Expect the image to be formatted as "data:image/{image_format};base64,{base64}".

    Args:
        base64_image: Image as base64.

    Returns:
        Pillow image.
    """
    image_data = base64.b64decode(base64_image.split(",", maxsplit=1)[1].encode("utf-8"))
    return Image.open(io.BytesIO(image_data))

binary_to_url(im_bytes)

Encode image from binary to base 64 URL.

Parameters:

Name Type Description Default
im_bytes bytes

Image as binary.

required

Returns:

Type Description
str

Image base 64 URL.

Source code in pixano/features/utils/image.py
def binary_to_url(im_bytes: bytes) -> str:
    """Encode image from binary to base 64 URL.

    Args:
        im_bytes: Image as binary.

    Returns:
        Image base 64 URL.
    """
    encoded = base64.b64encode(im_bytes).decode("utf-8")
    return f"data:image;base64,{encoded}"

depth_array_to_gray(depth, valid_start=0.2, valid_end=1, scale=1.0)

Encode depth array to gray levels.

Parameters:

Name Type Description Default
depth ndarray

Depth array

required
valid_start float

Valid start.

0.2
valid_end float

Valid end.

1
scale float

Scale.

1.0

Returns:

Type Description
ndarray

Depth array in gray levels.

Source code in pixano/features/utils/image.py
def depth_array_to_gray(
    depth: np.ndarray,
    valid_start: float = 0.2,
    valid_end: float = 1,
    scale: float = 1.0,
) -> np.ndarray:
    """Encode depth array to gray levels.

    Args:
        depth: Depth array
        valid_start: Valid start.
        valid_end: Valid end.
        scale: Scale.

    Returns:
        Depth array in gray levels.
    """
    mask = depth > 1

    # Scale gives depth in mm
    depth_n = depth * scale * 0.001

    if mask.sum() > 0:
        depth_n[mask] -= depth_n[mask].min()
        depth_n[mask] /= depth_n[mask].max() / (valid_end - valid_start)
        depth_n[mask] += valid_start

    depth_n *= 255
    depth_n = cv2.applyColorMap(depth_n[:, :, np.newaxis].astype(np.uint8), cv2.COLORMAP_PLASMA)

    return depth_n

depth_file_to_binary(depth_path)

Encode depth file to RGB image in binary.

Parameters:

Name Type Description Default
depth_path str

Depth file path.

required

Returns:

Type Description
bytes

Depth file as RGB image in binary.

Source code in pixano/features/utils/image.py
def depth_file_to_binary(depth_path: str) -> bytes:
    """Encode depth file to RGB image in binary.

    Args:
        depth_path: Depth file path.

    Returns:
        Depth file as RGB image in binary.
    """
    depth = cv2.imread(depth_path, cv2.IMREAD_ANYDEPTH).astype(np.float32)
    depth = depth_array_to_gray(depth)
    depth_rgb = Image.fromarray(depth)

    return image_to_binary(depth_rgb)

encode_rle(mask, height, width)

Encode mask from polygons / uncompressed RLE / RLE to RLE.

Parameters:

Name Type Description Default
mask list[list] | dict

Mask as polygons / uncompressed RLE / RLE.

required
height int

Image height.

required
width int

Image width.

required

Returns:

Type Description
dict

Mask as RLE.

Source code in pixano/features/utils/image.py
def encode_rle(mask: list[list] | dict, height: int, width: int) -> dict:
    """Encode mask from polygons / uncompressed RLE / RLE to RLE.

    Args:
        mask: Mask as polygons / uncompressed RLE / RLE.
        height: Image height.
        width: Image width.

    Returns:
        Mask as RLE.
    """
    if isinstance(mask, list):
        return polygons_to_rle(mask, height, width)
    elif isinstance(mask, dict):
        if isinstance(mask["counts"], list):
            return urle_to_rle(mask)
        return mask
    raise ValueError("Mask must be a list of polygons or an uncompressed RLE")

get_image_thumbnail(image, size)

Get image thumbnail.

Parameters:

Name Type Description Default
image Image

Pillow Image.

required
size tuple[int, int]

Thumbnail size.

required

Returns:

Type Description
Image

Image thumbnail as Pillow.

Source code in pixano/features/utils/image.py
def get_image_thumbnail(image: Image.Image, size: tuple[int, int]) -> Image.Image:
    """Get image thumbnail.

    Args:
        image: Pillow Image.
        size: Thumbnail size.

    Returns:
        Image thumbnail as Pillow.
    """
    if (
        not isinstance(size, tuple)
        or len(size) != 2
        or not isinstance(size[0], int)
        or not isinstance(size[1], int)
        or size[0] <= 0
        or size[1] <= 0
    ):
        raise ValueError(f"Invalid thumbnail size: {size}")
    thumbnail = image.copy()
    thumbnail.thumbnail(size)
    return thumbnail

image_to_base64(image, format=None)

Encode image from Pillow to base64.

The image is returned as a base64 string formatted as "data:image/{image_format};base64,{base64}".

Parameters:

Name Type Description Default
image Image

Pillow image.

required
format str | None

Image format.

None

Returns:

Type Description
str

Image as base64.

Source code in pixano/features/utils/image.py
def image_to_base64(image: Image.Image, format: str | None = None) -> str:
    """Encode image from Pillow to base64.

    The image is returned as a base64 string formatted as
    "data:image/{image_format};base64,{base64}".

    Args:
        image: Pillow image.
        format: Image format.

    Returns:
        Image as base64.
    """
    if image.format is None and format is None:
        raise ValueError("Image format is not defined")

    buffered = io.BytesIO()
    out_format = format or image.format
    if out_format.upper() == "UNKNOWN":
        out_format = "JPEG"
    image.save(buffered, format=out_format)

    encoded = base64.b64encode(buffered.getvalue()).decode("utf-8")

    return f"data:image/{out_format.lower()};base64,{encoded}"

image_to_binary(image, im_format='PNG')

Encode an image from Pillow to binary.

Parameters:

Name Type Description Default
image Image

Pillow image.

required
im_format str

Image file extension.

'PNG'

Returns:

Type Description
bytes

Image as binary.

Source code in pixano/features/utils/image.py
def image_to_binary(image: Image.Image, im_format: str = "PNG") -> bytes:
    """Encode an image from Pillow to binary.

    Args:
        image: Pillow image.
        im_format: Image file extension.

    Returns:
        Image as binary.
    """
    with BytesIO() as output_bytes:
        image.save(output_bytes, im_format)
        im_bytes = output_bytes.getvalue()

    return im_bytes

mask_to_polygons(mask)

Encode mask from NumPy array to polygons.

Parameters:

Name Type Description Default
mask ndarray

Mask as NumPy array

required

Returns:

Type Description
Tuple
  • Mask as polygons
  • True if mask has holes
Source code in pixano/features/utils/image.py
def mask_to_polygons(mask: np.ndarray) -> tuple[list[list], bool]:
    """Encode mask from NumPy array to polygons.

    Args:
        mask: Mask as NumPy array

    Returns:
        Tuple:
            - Mask as polygons
            - True if mask has holes
    """
    # Some versions of cv2 does not support incontiguous arr
    mask = np.ascontiguousarray(mask)

    # cv2.RETR_CCOMP flag retrieves all the contours and arranges them
    # to a 2-level hierarchy.
    # External contours (boundary) of the object are placed in hierarchy-1.
    # Internal contours (holes) are placed in hierarchy-2.
    # cv2.CHAIN_APPROX_NONE flag gets vertices of polygons from contours.
    res = cv2.findContours(mask.astype("uint8"), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
    hierarchy = res[-1]

    # If mask is empty
    if hierarchy is None:
        return [], False

    # Check if mask has holes
    has_holes = (hierarchy.reshape(-1, 4)[:, 3] >= 0).sum() > 0

    res = res[-2]
    res = [x.flatten() for x in res]

    # The coordinates from OpenCV are integers in range [0, W-1 or H-1].
    # We add 0.5 to turn them into real-value coordinate space. A better solution
    # would be to first +0.5 and then dilate the returned polygon by 0.5.
    res = [x + 0.5 for x in res if len(x) >= 6]

    # Convert np.array to lists
    res = [x.tolist() for x in res]

    return res, has_holes

mask_to_rle(mask)

Encode mask from Pillow or NumPy array to RLE.

Parameters:

Name Type Description Default
mask Image | ndarray

Mask as Pillow or NumPy array.

required

Returns:

Type Description
dict

Mask as RLE.

Source code in pixano/features/utils/image.py
def mask_to_rle(mask: Image.Image | np.ndarray) -> dict:
    """Encode mask from Pillow or NumPy array to RLE.

    Args:
        mask: Mask as Pillow or NumPy array.

    Returns:
        Mask as RLE.
    """
    mask_array = np.asfortranarray(mask)
    return mask_api.encode(mask_array)

polygons_to_rle(polygons, height, width)

Encode mask from polygons to RLE.

Parameters:

Name Type Description Default
polygons list[list]

Mask as polygons.

required
height int

Image height.

required
width int

Image width.

required

Returns:

Type Description
dict

Mask as RLE.

Source code in pixano/features/utils/image.py
def polygons_to_rle(polygons: list[list], height: int, width: int) -> dict:
    """Encode mask from polygons to RLE.

    Args:
        polygons: Mask as polygons.
        height: Image height.
        width: Image width.

    Returns:
        Mask as RLE.
    """
    rles = mask_api.frPyObjects(polygons, height, width)
    return mask_api.merge(rles)

rle_to_mask(rle)

Decode mask from RLE to NumPy array.

Parameters:

Name Type Description Default
rle dict[str, list[int] | bytes]

Mask as RLE.

required

Returns:

Type Description
ndarray

Mask as NumPy array.

Source code in pixano/features/utils/image.py
def rle_to_mask(rle: dict[str, list[int] | bytes]) -> np.ndarray:
    """Decode mask from RLE to NumPy array.

    Args:
        rle: Mask as RLE.

    Returns:
        Mask as NumPy array.
    """
    return mask_api.decode(rle)

rle_to_polygons(rle)

Encode mask from RLE to polygons.

Parameters:

Name Type Description Default
rle dict[str, list[int] | bytes]

Mask as RLE.

required

Returns:

Type Description
list[list]

Mask as polygons.

Source code in pixano/features/utils/image.py
def rle_to_polygons(rle: dict[str, list[int] | bytes]) -> list[list]:
    """Encode mask from RLE to polygons.

    Args:
        rle: Mask as RLE.

    Returns:
        Mask as polygons.
    """
    if "size" not in rle:
        raise ValueError("RLE must have a size")
    h, w = rle["size"]
    polygons, _ = mask_to_polygons(rle_to_mask(rle))

    # Normalize point coordinates
    for p in polygons:
        p[::2] = [x / w for x in p[::2]]
        p[1::2] = [y / h for y in p[1::2]]

    return polygons

rle_to_urle(rle)

Encode mask from RLE to uncompressed RLE.

Parameters:

Name Type Description Default
rle dict[str, list[int] | bytes]

Mask as RLE.

required

Returns:

Type Description
dict[str, list[int]]

Mask as uncompressed RLE.

Source code in pixano/features/utils/image.py
def rle_to_urle(rle: dict[str, list[int] | bytes]) -> dict[str, list[int]]:
    """Encode mask from RLE to uncompressed RLE.

    Args:
        rle: Mask as RLE.

    Returns:
        Mask as uncompressed RLE.
    """
    if "counts" not in rle or rle["counts"] is None:
        raise ValueError("RLE must have counts")
    mask = rle_to_mask(rle)
    urle = {"counts": [], "size": list(mask.shape)}

    for i, (value, elements) in enumerate(groupby(mask.ravel(order="F"))):
        urle["counts"].append(0 if i == 0 and value == 1 else len(list(elements)))

    return urle

urle_to_rle(urle)

Encode mask from uncompressed RLE to RLE.

Parameters:

Name Type Description Default
urle dict[str, list[int]]

Mask as uncompressed RLE.

required

Returns:

Type Description
dict[str, list[int] | bytes]

Mask as RLE.

Source code in pixano/features/utils/image.py
def urle_to_rle(urle: dict[str, list[int]]) -> dict[str, list[int] | bytes]:
    """Encode mask from uncompressed RLE to RLE.

    Args:
        urle: Mask as uncompressed RLE.

    Returns:
        Mask as RLE.
    """
    height, width = urle["size"]
    return mask_api.frPyObjects(urle, height, width)