ImageAsset#

class rapidtools.core.ImageAsset(*, path: Path, id: str | None = None, properties: dict[str, ~typing.Any]=<factory>, semantic_map: dict[int, str] | None=None, instance_map: dict[int, ~typing.Any] | None=None, allow_missing_file: bool = False)[source]#

Bases: object

A single image asset, its location, metadata, and segmentation info.

Variables:
  • path (Path) – Absolute path of the image file on disk.

  • id (str | None) – Unique identifier. Defaults to the file stem when omitted.

  • properties (dict[str, Any]) – Free-form metadata (e.g. latitude, longitude, url).

  • semantic_map (dict[int, str] | None) – Pixel value to class name mapping for the semantic mask.

  • instance_map (dict[int, Any] | None) – Instance ID to metadata mapping for the instance mask.

  • allow_missing_file (bool) – If True, the file does not need to exist yet (deferred download).

Example

>>> from rapidtools.core import ImageAsset
>>>
>>> img = ImageAsset(
...     id='img_001',
...     path='images/img_001.jpg',
...     properties={'url': 'https://example.com/img_001.jpg'},
...     allow_missing_file=True,
... )
>>> img.is_downloaded
False
>>> img.filename
'img_001.jpg'

Methods

download

Download the image file to self.path.

from_dict

Rebuild an asset from to_dict() output or a parsed JSON object.

get_mask_path

Determine the filename for the mask.

get_property

Safely retrieve one or more metadata properties from the asset.

load_image_from_disk

Load the image from disk using Pillow and caches it.

load_image_from_url

Download the image into memory and cache it.

load_mask

Lazy-load the segmentation mask from disk into a NumPy array.

merge

Merge data from another ImageAsset.

save_image

Saves the cached PIL image to disk.

save_interactive_html

Create an interactive HMTL file to view segmentation masks.

save_mask

Save the specified image mask to disk as a PNG image.

set_mask

Manually attaches a segmentation mask from memory.

show

Show the asset in the system's default image viewer.

summary

Print a human-readable summary of the image asset.

to_dict

Serialize the asset to a JSON-friendly dictionary.

to_dict() → dict[str, Any][source]#

Serialize the asset to a JSON-friendly dictionary.

Only the constructor arguments are included; the lazily loaded image and mask caches are not, and path is written as a string. The result round-trips through from_dict() and json.dumps().

Returns:

dict[str, Any] – Keyword arguments accepted by from_dict().

Example

>>> from rapidtools.core import ImageAsset
>>> img = ImageAsset(id='a', path='/tmp/a.jpg', allow_missing_file=True)
>>> sorted(img.to_dict())
['allow_missing_file', 'id', 'instance_map', 'path',
 'properties', 'semantic_map']
classmethod from_dict(data: Mapping[str, Any], allow_missing_file: bool | None = None) → ImageAsset[source]#

Rebuild an asset from to_dict() output or a parsed JSON object.

Unknown keys (including the private cache fields that older exports contained) are ignored, and mask-map keys that JSON turned into strings are converted back to integers.

Parameters:
  • data (Mapping[str, Any]) – The serialized asset. 'path' is required.

  • allow_missing_file (bool | None) – Whether the file may be absent on disk. None uses the stored value, defaulting to True when the dictionary has none: deserialized assets usually describe imagery that lives elsewhere.

Returns:

ImageAsset – The rebuilt asset.

Raises:

ValueError – If 'path' is missing, or the file is missing and allow_missing_file is False.

Example

>>> from rapidtools.core import ImageAsset
>>> img = ImageAsset.from_dict(
...     {'id': 'a', 'path': '/tmp/a.jpg', 'semantic_map': {'1': 'road'}}
... )
>>> img.semantic_map
{1: 'road'}
property directory: Path#

The parent directory of the image file.

Returns:

Path – The directory containing the image as a Path object.

Example

In the example below, we set allow_missing_file=True. This is useful for lazy-loading assets from an API or database where the local file may not yet exist, allowing for metadata manipulation without disk overhead.

>>> from rapidtools.core import ImageAsset
>>> image = ImageAsset(
...     path='/projects/data/image.jpg',
...     allow_missing_file=True
... )

The property returns a standard pathlib.Path object:

>>> image.directory
PosixPath('/projects/data')

This allows for seamless path manipulation, such as joining related files in the same folder:

>>> image.directory / 'metadata.json'
PosixPath('/projects/data/metadata.json')
property filename: str#

The filename of the image, including its extension.

Returns:

str – The image filename as a string.

Example

In the example below, we set allow_missing_file=True. This allows the asset to be initialized and its naming metadata to be accessed even if the file has not yet been downloaded to the local file system.

>>> from rapidtools.core import ImageAsset
>>> image = ImageAsset(
...     path='/data/projects/panoramas/image_001.jpg',
...     allow_missing_file=True
... )

The filename property extracts only the final component of the path:

>>> image.filename
'image_001.jpg'

This property is useful for generating logs, status reports, or matching image data with external database records:

>>> print(f'Processing asset: {image.filename}')
Processing asset: image_001.jpg
property is_downloaded: bool#

Check whether the image file exists on disk and is non-empty.

This property verifies that:

  • The path exists.

  • The path points to a regular file (not a directory).

  • The file size is greater than zero bytes.

Returns:

bool – True if a non-empty image file exists at image path, otherwise False.

Examples

is_downloaded is essential for workflows where assets are initialized lazily. Here, by using allow_missing_file=True, we can work with an asset’s metadata and only trigger a download if this property returns False.

Case 1: The file exists on the local file system:

>>> from rapidtools.core import ImageAsset
>>> image = ImageAsset('exists.jpg')
>>> image.is_downloaded
True

Case 2: The file is missing or has not been downloaded yet. We use allow_missing_file=True to avoid an initialization error:

>>> image = ImageAsset(path='missing.jpg', allow_missing_file=True)
>>> image.is_downloaded
False

This allows for simple conditional logic in processing pipelines.

>>> if not image.is_downloaded:
...     image.download(
...         url=r'https://upload.wikimedia.org/wikipedia/commons/1'
...             r'/1e/Suzzallo_Reading_Room%2C_May_2016.jpg'
...     )
...     print(f'Downloaded: {image.is_downloaded}')
INFO: Downloading missing...
INFO: Successfully downloaded: missing.jpg
Downloaded: True
property stem: str#

The filename of the image without its extension.

Returns:

str – The filename stem as a string.

Examples

>>> from rapidtools.core import ImageAsset
>>>
>>> asset = ImageAsset(
...     id='scene',
...     path='/data/images/scene_01.tif',
...     allow_missing_file=True
... )
>>> asset.stem
'scene_01'
>>>
>>> asset = ImageAsset(
...     id='photo',
...     path='photo.jpg',
...     allow_missing_file=True
... )
>>> asset.stem
'photo'
download(url: str | None = None, url_key: str = 'thumb_original_url', overwrite: bool = False, session: Session | None = None)[source]#

Download the image file to self.path.

Parameters:
  • url (str, optional) – The direct download link. If provided, this takes precedence over properties.

  • url_key (str) – The key in self.properties to look for if 'url' is None. Defaults to 'thumb_original_url'. Useful for downloading specific sizes (e.g. 'thumb_2048_url').

  • overwrite (bool) – If True, forces download even if file exists.

  • session (requests.Session, optional) – An existing requests session to speed up connections.

Raises:
  • ValueError – If no URL is provided or found in properties.

  • IOError – If the download fails.

Examples

Basic usage through automatic URL Lookup:

>>> from rapidtools.core import ImageAsset
>>>
>>> asset = ImageAsset(
...     id='img_01',
...     path='data/img_01.jpg',
...     properties={
...         'thumb_original_url':
...             r'https://upload.wikimedia.org/wikipedia/commons/1'
...             r'/1e/Suzzallo_Reading_Room%2C_May_2016.jpg'
...             },
...     allow_missing_file=True
... )
>>> asset.download()
INFO: Downloading 'img_01' from https://upload.wikimedia.org/
wikipedia/commons/1/1e/Suzzallo_Reading_Room%2C_May_2016.jpg...
INFO: Successfully downloaded: img_01.jpg

Usage through providing explicit URL:

>>> asset.download(
...     url=r'https://upload.wikimedia.org/wikipedia/commons/1'
...     r'/1e/Suzzallo_Reading_Room%2C_May_2016.jpg'
... )
INFO: Downloading 'img_01' from https://upload.wikimedia.org/
wikipedia/commons/1/1e/Suzzallo_Reading_Room%2C_May_2016.jpg...
INFO: Successfully downloaded: img_01.jpg

Download using a session (recommended for batch operations):

>>> import requests
>>>
>>> assets = [
...     ImageAsset(
...         id='wiki_A',
...         path='data/wiki_a.jpg',
...         allow_missing_file=True,
...         properties={
...             'src': (
...                 'https://upload.wikimedia.org/wikipedia/commons/'
...                 'a/a9/Example.jpg'
...             ),
...         },
...     ),
...     ImageAsset(
...         id='wiki_B',
...         path='data/wiki_b.jpg',
...         allow_missing_file=True,
...         properties={
...             'src': (
...                 'https://upload.wikimedia.org/wikipedia/commons/5'
...                 '/50/Smile_Image.png'
...             ),
...         },
...     )
... ]
>>>
>>> with requests.Session() as s:
...     s.headers.update({'User-Agent': 'RapidTools/1.0'})
...     for asset in assets:
...         asset.download(url_key='src', session=s)
INFO: Downloading 'wiki_A' from https://upload.wikimedia.org/
wikipedia/commons/a/a9/Example.jpg...
INFO: Successfully downloaded: wiki_a.jpg
INFO: Downloading 'wiki_B' from https://upload.wikimedia.org/
wikipedia/commons/5/50/Smile_Image.png...
INFO: Successfully downloaded: wiki_b.jpg
get_mask_path(mask_type: MaskType, override_path: Path | str | None = None) → Path[source]#

Determine the filename for the mask.

Default Convention:
  • Image: photo.jpg

  • Semantic: photo_semantic.png

  • Instance: photo_instance.png

Parameters:
  • mask_type – The type of mask ('semantic' or 'instance').

  • override_path – If provided, this specific path is used instead of the automatic naming convention.

Returns:

Path – The resolved path object for the mask.

Raises:

ValueError – If mask_type is not a valid MaskType.

Examples

Create an ImageAsset:

>>> from rapidtools.core import ImageAsset
>>> from rapidtools.config import MaskType
>>> asset = ImageAsset(
...     id='A1',
...     path='/tmp/scene_01.jpg',
...     allow_missing_file=True
... )

Getting mask path by providing a mask type:

>>> path = asset.get_mask_path('instance')
>>> print(path)
/tmp/scene_01_instance.png

Getting mask path using a manual override. This option is useful when masks are stored in a completely different folder:

>>> path = asset.get_mask_path(
...     'semantic',
...     override_path='/masks/custom_01.png'
... )
>>> print(path)
/masks/custom_01.png
get_property(key: str | Iterable[str], default: Any = None) → Any | dict[str, Any][source]#

Safely retrieve one or more metadata properties from the asset.

This method is polymorphic:

  1. If key is a str, it returns the single value.

  2. If key is a list/tuple, it returns a dictionary of {key: value}.

Parameters:
  • key (str or Iterable[str]) – A single property name or a list of names to retrieve.

  • default (Any, optional) – The value to return for any key that is not found. Defaults to None.

Returns:

Any or dict[str, Any] – - The value (if input was a single string). - A dictionary of values (if input was an iterable).

Examples

Set up an ImageAsset

>>> from rapidtools.core import ImageAsset
>>> asset = ImageAsset(
...     id='img_01',
...     path='photo.jpg',
...     properties={'width': 1920, 'height': 1080, 'iso': 100},
...     allow_missing_file=True
... )

Request the values of individual properties:

>>> asset.get_property('width')
1920
>>> asset.get_property('shutter_speed', default='Auto')
'Auto'

Request the values of multiple properties at once:

>>> asset.get_property(['width', 'height'])
{'width': 1920, 'height': 1080}

Request the values of multiple properties, including some that exist and some that are missing:

>>> asset.get_property(['iso', 'aperture'], default='Unknown')
{'iso': 100, 'aperture': 'Unknown'}
load_image_from_disk(force_reload: bool = False, convert_mode: str | None = None, apply_exif_orientation: bool = True) → Image[source]#

Load the image from disk using Pillow and caches it.

Behavior:

  • Lazy-caches by default (returns cached image if present).

  • Always fully decodes and then closes the underlying file handle. Optionally converts to a target mode (e.g., 'RGB').

  • Optional force reload to bypass cache.

Parameters:
  • force_reload – If True, re-reads the file from disk even if an image is currently cached. Defaults to False.

  • convert_mode – If provided, converts image to the specified PIL mode. Some options are L (Grayscale), 1 (Black and White), CMYK (Cyan, Magenta, Yellow, Key/Black), I (32-bit signed integer pixels), and HSV (Hue, Saturation, Value color space).

  • apply_exif_orientation (bool) – If True, reads the EXIF tags and rotates the image to appear upright (e.g., fixing sideways smartphone photos). Defaults to True.

Returns:

PillowImage.Image – The loaded PIL image.

Raises:
  • FileNotFoundError – If the file does not exist or has not been downloaded.

  • OSError – If the file exists but cannot be opened (corruption/permissions).

Examples

Create a dummy red 100x100 RGB image for testing:

>>> import os
>>> from PIL import Image
>>> from rapidtools.core import ImageAsset
>>>
>>> temp_path = 'temp_example.jpg'
>>> Image.new('RGB', (100, 100), color='red').save(temp_path)
>>>
>>> asset = ImageAsset(id='img_01', path=temp_path)

Basic loading from a file. The first call reads the file from disk, subsequent calls return the cached result:

>>> img1 = asset.load_image_from_disk()
>>> print(f'{img1.format} {img1.size} {img1.mode}')
INFO: Loading image data from: temp_example.jpg
None (100, 100) RGB
>>>
>>> img2 = asset.load_image_from_disk()
>>> print(img1 is img2)
True

Loading an image with mode conversion and force reload. Here force reload is enabled to ignore the cache, and the image is converted to Grayscale ('L'):

>>> img_gray = asset.load_image_from_disk(
...     force_reload=True,
...     convert_mode='L'
... )
INFO: Loading image data from: temp_example.jpg
>>> print(img_gray.mode)
L

load_image_from_disk raises a FileNotFoundError if the file does not exist locally (or has not yet been downloaded) or raises an OSError if the file exists but cannot be opened (e.g., due to corruption or insufficient permissions). The following example shows a sample error handling situation:

>>> missing_asset = ImageAsset(
...     id=1,
...     path='ghost.jpg',
...     allow_missing_file=True,
... )
>>> try:
...     missing_asset.load_image_from_disk()
... except FileNotFoundError as e:
...     print('Caught error')
Caught error

Finally, run the following line to delete the temporary image file created for this example:

>>> os.remove(temp_path)
load_image_from_url(url: str | None = None, url_key: str = 'thumb_original_url', convert_mode: str | None = None, force_reload: bool = False, apply_exif_orientation: bool = True, verbose: bool = True, session: Session | None = None) → Image[source]#

Download the image into memory and cache it.

This method bypasses path for the image entirely. Note that the is_downloaded property of the image will remain False after this method completes execution (unless the file happened to exist previously).

Parameters:
  • url (str, optional) – Direct URL to download from. If None, checks image properties to find the URL.

  • url_key (str) – Property key to use for URL if direct url is None. Defaults to ‘thumb_original_url’.

  • convert_mode (str, optional) – Mode to convert the loaded image to (e.g., 'RGB').

  • force_reload (bool) – If True, re-downloads even if _pil_image is already set.

  • apply_exif_orientation (bool) – If True, rotates the image based on EXIF data (e.g., fixing sideways phone photos). Defaults to True.

  • verbose (bool) – If True, logs standard info messages. Set to False to suppress the “Downloading into memory” log. Defaults to True.

  • session (requests.Session, optional) – Session for efficient connection pooling during download.

Returns:

PillowImage.Image – The loaded PIL image object.

Raises:
  • ValueError – If the URL cannot be determined.

  • requests.RequestException – If the network request fails.

  • IOError – If the bytes cannot be decoded as an image.

Examples

Setup an asset defined only by metadata (no local file yet):

>>> from rapidtools.core import ImageAsset
>>> asset = ImageAsset(
...     id='wiki_cat',
...     path='virtual_cat.jpg',
...     allow_missing_file=True,
...     properties={
...         'thumb_original_url': (
...             'https://upload.wikimedia.org/wikipedia/commons/'
...             '3/3a/Cat03.jpg'
...         ),
...         'backup_url': (
...             'https://upload.wikimedia.org/wikipedia/commons/'
...             '4/47/PNG_transparency_demonstration_1.png'
...         ),
...     },
... )

Loading image from the URL in properties (loads from 'thumb_original_url' by default) into memory:

>>> img = asset.load_image_from_url()
INFO: Downloading wiki_cat into memory...
>>> print(img.size)
(1600, 1598)
>>> print(asset.is_downloaded)
False

Load from a specific key, convert to Grayscale ('L'), force a re-download, and suppress the log message:

>>> img_small = asset.load_image_from_url(
...     url_key='backup_url',
...     convert_mode='L',
...     force_reload=True,
...     verbose=False
... )
>>> print(img_small.mode)
L

Load an image using a Session (recommended for batch processing. This example uses the TCP connection for speed:

>>> import requests
>>> with requests.Session() as session:
...     asset.load_image_from_url(session=session)
load_mask(mask_type: MaskType, force_reload: bool = False, custom_path: Path | str | None = None) → ndarray[source]#

Lazy-load the segmentation mask from disk into a NumPy array.

This method handles caching automatically. It checks if the mask is already loaded in memory (e.g. _semantic_mask) before attempting file I/O.

Parameters:
  • mask_type (str) – The type of mask to load. Standard values are 'semantic' or 'instance'. Defaults to 'semantic'.

  • force_reload (bool) – If True, ignores the internal cache and re-reads the file from disk. Defaults to False.

  • custom_path (Path, or str, or None) – If provided, loads the mask from this specific path instead of deriving the filename from the image asset’s path.

Returns:

np.ndarray – The segmentation mask data as a NumPy array.

Raises:
  • ValueError – If mask_type is invalid.

  • FileNotFoundError – If the mask file does not exist at the calculated or provided path.

  • OSError – If the file exists but cannot be opened or decoded (e.g. corruption).

Examples

Create a mock environment with a real mask file by creating a dummy asset and a semantic mask file matching the expected naming convention:

>>> import os
>>> import numpy as np
>>> from PIL import Image
>>> from rapidtools.config import MaskType
>>> from rapidtools.core import ImageAsset
>>>
>>> asset = ImageAsset(
...     id='scene_01',
...     path='test_scene.jpg',
...     allow_missing_file=True
... )
>>>
>>> mask_data = np.zeros((10, 10), dtype=np.uint8)
>>> mask_data[0:5, :] = 1  # Top half is class 1
>>> mask_path = 'test_scene_semantic.png'
>>> Image.fromarray(mask_data).save(mask_path)

Load the semantic segmentation mask from ‘test_scene_semantic.png’ Please note that the load_mask method infers this filename automatically from the image filename and the provided mask_type by appending _semantic before the file extension (e.g., test_scene.png turns into test_scene_semantic.png):

>>> loaded_mask = asset.load_mask(MaskType.SEMANTIC)
>>> print(loaded_mask.shape)
(10, 10)
>>> print(loaded_mask[0, 0])
1

To delete the generated mask file, please run:

>>> os.remove(mask_path)
merge(other: ImageAsset, ignore_id_mismatch: bool = False, overwrite_path: bool = False, overwrite_properties: bool = True) → None[source]#

Merge data from another ImageAsset.

By default, this method ensures IDs match before merging to prevent accidental data corruption.

Parameters:
  • other (ImageAsset) – The source asset to merge data from.

  • ignore_id_mismatch (bool) – If True, allows merging even if self.id != other.id. Defaults to False.

  • overwrite_path (bool) – If True, updates self.path with other.path and adopts its internal caches (images/masks). Defaults to False (preserves original path).

  • overwrite_properties (bool) – If True, other.properties, semantic_map, and instance_map will overwrite existing values in self. If False, only missing values are filled. Defaults to True.

Raises:
  • TypeError – If other is not an ImageAsset.

  • ValueError – If IDs do not match and ignore_id_mismatch is False.

Examples

Create two ImageAsset objects:

>>> from rapidtools.core import ImageAsset
>>>
>>> asset_a = ImageAsset(
...     id='1',
...     path='a.jpg',
...     properties={'temp': 20},
...     allow_missing_file=True,
... )
>>> asset_b = ImageAsset(
...     id='1',
...     path='b.jpg',
...     properties={'temp': 25, 'gps': [0, 0]},
...     allow_missing_file=True,
... )

Merge asset_b’s properties into asset_a using the default behavior that overwrites conflicts:

>>> asset_a.merge(asset_b, overwrite_properties=True)
>>> print(asset_a.properties)
{'temp': 25, 'gps': [0, 0]}

Merge without overwriting existing keys. Here, 'gps' is added (it was not present on asset_a), 'temp' is NOT overwritten (stays 20):

>>> asset_a = ImageAsset(
...     id='1',
...     path='a.jpg',
...     properties={'temp': 20},
...     allow_missing_file=True,
... )
>>> asset_a.merge(asset_b, overwrite_properties=False)
>>> print(asset_a.properties)
{'temp': 20, 'gps': [0, 0]}
save_interactive_html(output_path: Path | str | None = None, mask_type: MaskType = MaskType.SEMANTIC, opacity: float = 0.5, min_area: int = 10) → None[source]#

Create an interactive HMTL file to view segmentation masks.

Generates an interactive HTML file where masks are converted to SVG polygons. Hovering over a polygon shows its label from the asset’s mapping data.

This method converts the raster NumPy mask into vector polygons on the fly.

Args:
output_path (Path, or str, or None):

File path to save the HTML. Defaults to {filename}_{mask_type}.html.

mask_type (str):

Type of segmentation mask displayed, i.e., ‘semantic’ or ‘instance’.

opacity (float):

Opacity of the polygon fill (0.0 to 1.0).

min_area (int):

Minimum number of vertices to generate a polygon for. Helps reduce noise/dust in the SVG.

Examples

Create a dummy image and a corresponding mask:

>>> import os
>>> import numpy as np
>>> from PIL import Image
>>> from rapidtools.core import ImageAsset
>>> from rapidtools.config import MaskType
>>>
>>> img_path = 'html_test.jpg'
>>> Image.new('RGB', (100, 100), color='white').save(img_path)
>>> mask_data = np.zeros((100, 100), dtype=np.uint8)
>>> mask_data[10:50, 10:50] = 1 # Square object
>>>
>>> asset = ImageAsset(id='viz', path=img_path, allow_missing_file=True)
>>> asset.set_mask(mask_data, MaskType.SEMANTIC, map_data={1: 'Square'})

Create an interactive HTML:

>>> asset.save_interactive_html(mask_type=MaskType.SEMANTIC)
INFO: Loading image data from: html_test.jpg
INFO: Generating HTML polygons for 1 semantics...
INFO: Saved interactive HTML to: html_test_semantic.html
>>> print(os.path.exists('html_test_semantic.html'))
True

Please run the following lines to remove the created files:

>>> for f in [img_path, 'html_test_semantic.html']:
...     if os.path.exists(f): os.remove(f)
save_image(output_path: Path | str | None = None, format: str | None = None, quality: int = 100) → None[source]#

Saves the cached PIL image to disk.

This method writes the in-memory image data to a file. It automatically creates any necessary parent directories.

Parameters:
  • output_path (Path, or str, or None) – The file path where the image will be saved. If None, defaults to self.path, which will overwrite the original image file.

  • format (str, or None) – The image format to use (e.g., 'JPEG', 'PNG'). If None, the format is inferred from the file extension of the output path.

  • quality (int) – The compression quality (1-100) for lossy formats like JPEG. Higher numbers mean better quality but larger file size. Defaults to 100.

Raises:
  • ValueError – If no image is currently loaded in memory (i.e., load_image_from_disk(), load_image_from_url has not been called).

  • OSError – If the file cannot be written (e.g., permission errors).

Examples

Create an asset with a cached image:

>>> import os
>>> from PIL import Image
>>> from rapidtools.core import ImageAsset
>>>
>>> asset = ImageAsset(id='A1', path='input.jpg', allow_missing_file=True)
>>> asset._pil_image = Image.new('RGB', (100, 100), color='blue')

Example 1: Save to a new path

>>> asset.save_image(output_path='output.png')
INFO: Saving image to output.png
>>> print(os.path.exists('output.png'))
True

Example 2: Save as JPEG with specific quality

>>> asset.save_image(output_path='compressed.jpg', quality=50)
INFO: Saving image to compressed.jpg

Cleanup

>>> for f in ['output.png', 'compressed.jpg']:
...     if os.path.exists(f): os.remove(f)
save_mask(mask_type: MaskType, output_path: Path | str | None = None) → None[source]#

Save the specified image mask to disk as a PNG image.

This method retrieves the mask (from memory cache or by reloading the source file) and writes it to the specified location. It strictly enforces the PNG format to ensure lossless storage of integer class IDs.

Parameters:
  • mask_type (MaskType) – The type of mask to save (e.g., 'instance' or 'semantic').

  • output_path (Path, or str, or None) – The destination path. If None, the method calculates the default path (e.g., image_name_semantic.png) in the same directory as the source image.

Raises:
  • ValueError – If mask_type is invalid or if the mask data cannot be found (neither in memory nor on disk).

  • OSError – If the file cannot be written (e.g., permission denied).

Examples

Create an ImageAsset with an in-memory dummy mask (10x10 numpy array):

>>> import os
>>> import numpy as np
>>> from rapidtools.core import ImageAsset
>>> from rapidtools.config import MaskType
>>>
>>> asset = ImageAsset(
...     id='A1',
...     path='temp_asset.jpg',
...     allow_missing_file=True,
... )
>>>
>>> mask_data = np.zeros((10, 10), dtype=np.uint8)
>>> asset.set_mask(mask_data, MaskType.SEMANTIC)

Save the mask using the default naming convention:

>>> asset.save_mask('semantic')
INFO: Saving semantic mask to temp_asset_semantic.png
>>> print(os.path.exists('temp_asset_semantic.png'))
True

Save to a specific custom path:

>>> asset.save_mask('semantic', output_path='custom_mask.png')
INFO: Saving semantic mask to custom_mask.png

Remove the created files:

>>> for f in ['temp_asset_semantic.png', 'custom_mask.png']:
...     if os.path.exists(f): os.remove(f)
set_mask(data: ndarray, mask_type: MaskType, map_data: dict[int, Any] | None = None) → None[source]#

Manually attaches a segmentation mask from memory.

Use this when you have generated a mask in code (e.g., model inference) instead of loading it from a file.

Parameters:
  • data – The array containing the mask.

  • mask_type – Mask type, i.e., 'semantic' or 'instance'.

  • map_data – Optional dictionary to update the class/instance mapping. If provided, it replaces the existing map for this asset.

Raises:
  • TypeError – If data is not a NumPy array.

  • ValueError – If mask_type is not ‘semantic’ or ‘instance’.

Example

Setup a virtual asset and mock segmentation data (representing output from an AI model):

>>> import numpy as np
>>> from rapidtools.core import ImageAsset
>>>
>>> asset = ImageAsset(
...     id='img_01',
...     path='virtual.jpg',
...     allow_missing_file=True
... )

Create a dummy 10x10 mask where 1=Sky, 2=Grass:

>>> fake_mask = np.zeros((10, 10), dtype=np.uint8)
>>> fake_mask[:5, :] = 1  # Top half is Sky
>>> fake_mask[5:, :] = 2  # Bottom half is Grass

Define what the numbers mean (i.e., a segmentation map):

>>> labels = {1: 'Sky', 2: 'Grass'}

Attach the mask and the labels to the asset:

>>> asset.set_mask(fake_mask, 'semantic', map_data=labels)
>>> print(asset.semantic_map)
{1: 'Sky', 2: 'Grass'}

Verify the mask is cached and retrievable:

>>> retrieved = asset.load_mask('semantic')
>>> print(retrieved.shape)
(10, 10)
show(output_type: Literal['image', 'semantic', 'instance', 'overlay_semantic', 'overlay_instance'] = 'image', alpha: float = 0.5, cmap: str | None = None) → None[source]#

Show the asset in the system’s default image viewer.

Parameters:
  • output_type (str) –

    The type of visualization to generate. Options:

    • 'image': Show raw image only.

    • 'semantic': Show semantic mask only (colored).

    • 'instance': Show instance mask only.

    • 'overlay_semantic': Image + Semantic Mask.

    • 'overlay_instance': Image + Instance Mask.

  • alpha (float) – Transparency of the mask in overlay modes (0.0 to 1.0), where 0 is fully transparent, 0.5 is 50% Transparent (see-through), and 1 is a solid color (blocks anything underneath it)

  • cmap (str, optional) – Matplotlib colormap name (e.g., 'tab20', 'jet').

Raises:
  • ValueError – If the provided cmap is not a valid Matplotlib colormap name.

  • OSError – If a mask file exists but cannot be opened (e.g., file corruption), as this method only catches FileNotFoundError for masks.

Examples

Create a dummy base image (100x100 gray square) and dummy semantic mask (inner square is class 1) and initialize an ImageAsset with this image and its mask:

>>> import os
>>> import numpy as np
>>> from PIL import Image
>>> from rapidtools.core import ImageAsset
>>> from rapidtools.config import MaskType
>>>
>>> img_path = "temp_viz_test.jpg"
>>> Image.new('RGB', (100, 100), color='gray').save(img_path)
>>>
>>> mask_path = "temp_viz_test_semantic.png"
>>> mask_data = np.zeros((100, 100), dtype=np.uint8)
>>> mask_data[25:75, 25:75] = 1
>>> Image.fromarray(mask_data).save(mask_path)
>>>
>>> asset = ImageAsset(id='viz_test', path=img_path)

View base image:

>>> asset.show(output_type='image')
Loading image data from: temp_viz_test.jpg

View semantic mask:

>>> asset.show(output_type='semantic')

Overlay the semantic mask with custom settings:

>>> asset.show(
...     output_type='overlay_semantic',
...     alpha=0.3,
...     cmap='viridis'
... )

Remove dummy image and mask files:

>>> os.remove(img_path)
>>> os.remove(mask_path)
summary() → None[source]#

Print a human-readable summary of the image asset.

This method prints details including the asset’s ID, filename, path, status (downloaded/in-memory), file size (if available), and segmentation mask availability.

Example

>>> from rapidtools.core import ImageAsset
>>>
>>> img = ImageAsset(
...     id='img_01',
...     path='/tmp/photo.jpg',
...     allow_missing_file=True
... )
>>> img.summary()
--- ImageAsset Summary ---
ID:          img_01
Filename:    photo.jpg
Status:      Missing (Virtual)
Path:        /tmp/photo.jpg
Masks:       Semantic [Not Loaded], Instance [Not Loaded]
Properties (0):
  (No properties)
--------------------------