FALCON / artifacts.py
JonathanJMK's picture
Upload complete FALCON model and project documentation
c645013 verified
Raw
History Blame Contribute Delete
2.59 kB
"""File output and panoptic image utilities."""
from __future__ import annotations
import json
import os
import tempfile
from collections.abc import Iterable
from pathlib import Path
from typing import Any
def panoptic_ids_from_image(image):
"""Decode integer or RGB(A) panoptic IDs without rescaling."""
import numpy as np
array = np.asarray(image)
if array.ndim == 2:
if not np.issubdtype(array.dtype, np.integer):
raise ValueError("panoptic map must contain integer IDs")
return array.astype(np.int64)
if array.ndim == 3 and array.shape[2] in (3, 4):
rgb = array[..., :3].astype(np.int64)
return rgb[..., 0] + 256 * rgb[..., 1] + 65536 * rgb[..., 2]
raise ValueError("panoptic PNG must be integer or RGB")
def external_output(
path: str | Path,
dataset_root: str | Path,
*,
protected_roots: Iterable[str | Path] = (),
) -> Path:
"""Keep generated files outside dataset and annotation directories."""
output = Path(path).expanduser().resolve()
requested_roots = (dataset_root, *tuple(protected_roots))
immutable_roots: list[Path] = []
for raw_root in requested_roots:
root = Path(raw_root).expanduser().resolve(strict=True)
if not root.is_dir():
raise ValueError(f"Protected input root is not a directory: {root}")
if root not in immutable_roots:
immutable_roots.append(root)
for root in immutable_roots:
if output == root or root in output.parents or output in root.parents:
raise ValueError(
"Derived output must be outside immutable input roots and not an ancestor of one"
)
return output
def atomic_json(path: str | Path, value: Any) -> None:
"""Write a complete UTF-8 JSON document, then atomically replace its target."""
path = Path(path)
encoded = (
json.dumps(value, sort_keys=True, indent=2, ensure_ascii=False, allow_nan=False)
+ "\n"
)
path.parent.mkdir(parents=True, exist_ok=True)
temporary = None
try:
with tempfile.NamedTemporaryFile(
mode="w",
encoding="utf-8",
dir=path.parent,
prefix=f".{path.name}.",
suffix=".tmp",
delete=False,
) as stream:
temporary = Path(stream.name)
stream.write(encoded)
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary, path)
finally:
if temporary is not None and temporary.exists():
temporary.unlink()