Skip to main content

Simple utility library for file management and object detection workflows.

Project description

simply-utils

Simple utility library for file management, image processing, and object detection workflows.

pip install simply-utils
import simply

Table of Contents

general

Function Description
mkdir Join path parts and create directories
get_files Find files by extension in a directory or wildcard path
read_data Read a text file into a list of lines
write_data Write a flat or nested list to a text file
consolidate_files Consolidate files from multiple sources into one directory
load_image Load an image as RGB, handling PNG transparency
load_image_resized Load and resize an image with aspect, pad, pad_only, or stretch modes

detection

Function Description
read_label Parse a YOLO-format label file into structured detections
write_label Write structured detections to a label file
draw_bboxes Draw bounding boxes onto an image
viz_bboxes End-to-end: load image + label, draw boxes, save as JPEG

general

mkdir

Join path parts and create all directories. If the path looks like a file path (has a suffix), the parent directory is created instead.

simply.mkdir("outputs", "images")       # creates outputs/images
simply.mkdir("/a/b", "c/d")             # creates /a/b/c/d
simply.mkdir("outputs/result.jpg")      # creates outputs/

get_files

Find files by extension inside a directory, a single file path, or a wildcard path. Defaults to {".jpg", ".jpeg", ".png", ".bmp"} when exts is not provided.

# Search a directory recursively
files = simply.get_files("data/images", recursive=True)

# Filter by extension — string or list, with or without dot
files = simply.get_files("data", recursive=True, exts=".txt")
files = simply.get_files("data", recursive=True, exts=["jpg", ".png"])

# Single file
files = simply.get_files("image.jpg")

# Wildcard path — recursive=True required
files = simply.get_files("data/202601*/images", recursive=True)

read_data

Read a text file and return a list of stripped, non-empty lines.

lines = simply.read_data("labels.txt")
# ["line one", "line two", "line three"]

write_data

Write a flat or nested list to a text file.

# Flat list — one item per line
simply.write_data("out.txt", ["a", "b", "c"])

# Flat list — joined into a single line
simply.write_data("out.txt", ["a", "b", "c"], sep=",")
# a,b,c

# Nested list — one inner list per line, joined by sep
simply.write_data("out.txt", [["a", "b"], ["c", "d"]], sep=",")
# a,b
# c,d

# Nested list — default space separator
simply.write_data("out.txt", [["a", "b"], ["c", "d"]])
# a b
# c d

consolidate_files

Consolidate files from one or more source directories into a single destination. Source symlinks are always resolved before transfer.

# Flat consolidation via symlink (default)
simply.consolidate_files(
    src="data/batch01",
    dst_dir="data/consolidated",
    recursive=True,
)

# Multiple sources, mirror structure, copy mode, auto-suffix on conflict
simply.consolidate_files(
    src=["data/batch01", "data/batch02"],
    dst_dir="data/consolidated",
    recursive=True,
    mode="copy",               # "symlink" (default) | "copy"
    structure="mirror",        # "flat" (default) | "mirror"
    on_conflict="auto_suffix", # "skip" (default) | "overwrite" | "auto_suffix"
)

# Wildcard source
simply.consolidate_files(
    src="data/202601*/images",
    dst_dir="data/consolidated",
    recursive=True,
    exts=[".jpg", ".png"],
)

load_image

Load an image as RGB. Handles PNG transparency by compositing onto a background color.

img = simply.load_image("photo.jpg")

# Custom background for transparent PNGs
img = simply.load_image("photo.png", bg_color=(0, 0, 0))

# Also accepts a PIL Image
from PIL import Image
pil_img = Image.open("photo.png")
img = simply.load_image(pil_img)

load_image_resized

Load and resize an image using one of four strategies. Internally calls load_image so transparency is always handled. All resizing uses the Lanczos filter.

# "aspect" — scale longest side to max_len, preserve aspect ratio (default)
img = simply.load_image_resized("photo.jpg", 640)

# "pad" — resize then pad to square (YOLO letterbox)
img = simply.load_image_resized("photo.jpg", 640, mode="pad")
img = simply.load_image_resized("photo.jpg", 640, mode="pad", pad_color=(114, 114, 114))

# "pad" + downscale_only — skip resize if fits, still pad to square
img = simply.load_image_resized("photo.jpg", 640, mode="pad", downscale_only=True)

# "pad_only" — no resize, always pad to max_len x max_len
img = simply.load_image_resized("photo.jpg", 640, mode="pad_only")

# "stretch" — hard resize to max_len x max_len
img = simply.load_image_resized("photo.jpg", 640, mode="stretch")

# downscale_only — no-op if image already fits within max_len
img = simply.load_image_resized("photo.jpg", 640, downscale_only=True)
mode Resize Output size downscale_only effect
"aspect" Longest side → max_len Variable No-op if longest side ≤ max_len
"pad" Longest side → max_len max_len × max_len No resize, pad to longest side²
"pad_only" None max_len × max_len Ignored
"stretch" Both sides → max_len max_len × max_len No-op if both sides ≤ max_len

detection

read_label

Parse a detection label .txt file into a list of detections. Supports two formats:

  • "norm" — YOLO normalised: class_id cx cy w h (values in [0.0, 1.0])
  • "pixel" — absolute pixel corners: class_name x1 y1 x2 y2

Confidence is optional, detected automatically by field count (5 = no conf, 6 = conf present).

CLASS_MAP = ["car", "person", "bike"]

# Read pixel format (default)
detections = simply.read_label("image.txt")
# [["car", 100.0, 200.0, 400.0, 600.0], ...]

# Read norm format, convert to pixel
detections = simply.read_label(
    "image.txt",
    fmt_in="norm",
    fmt_out="pixel",
    class_map=CLASS_MAP,
    image_path="image.jpg",
)

# Skip malformed lines instead of raising
detections = simply.read_label("image.txt", skip_malformed=True)

write_label

Write a list of detections to a label .txt file. Mirrors read_label — accepts detections in fmt_in format and writes in fmt_out format.

# Write pixel format (default)
simply.write_label("image.txt", detections)

# Convert pixel → norm on write
simply.write_label(
    "image.txt",
    detections,
    fmt_in="pixel",
    fmt_out="norm",
    class_map=CLASS_MAP,
    image_path="image.jpg",
)

draw_bboxes

Draw bounding boxes onto an image and return the annotated copy. The original is not modified. Colors are deterministically auto-assigned per class name, or overridden via class_colors.

from PIL import Image

img = Image.open("image.jpg")
annotated = simply.draw_bboxes(img, detections)
annotated.save("annotated.jpg")

# Filter to specific classes
annotated = simply.draw_bboxes(img, detections, class_filter=["car", "person"])

# Custom colors
annotated = simply.draw_bboxes(
    img,
    detections,
    class_colors={"car": (255, 100, 100), "person": (100, 255, 100)},
)

# Draw from norm format directly
annotated = simply.draw_bboxes(
    "image.jpg",
    detections,
    fmt_in="norm",
    class_map=CLASS_MAP,
)

viz_bboxes

End-to-end visualisation — loads image and label, draws boxes, saves as JPEG. Output suffix is always .jpg regardless of input format.

# Pixel format labels (default)
simply.viz_bboxes("image.jpg", "image.txt", "output/image_viz")

# Norm format labels
simply.viz_bboxes(
    "image.jpg",
    "image.txt",
    "output/image_viz",
    fmt_in="norm",
    class_map=CLASS_MAP,
)

# Filter classes and custom colors
simply.viz_bboxes(
    "image.jpg",
    "image.txt",
    "output/image_viz",
    fmt_in="norm",
    class_map=CLASS_MAP,
    class_filter=["car"],
    class_colors={"car": (255, 180, 0)},
)

Package Structure

simply/
    general/
        file_utils.py     # get_files, read_data, write_data, consolidate_files
        image_utils.py    # load_image, load_image_resized
    detection/
        label_utils.py    # read_label, write_label
        bbox_utils.py     # draw_bboxes, viz_bboxes
        fonts/
            DejaVuSans.ttf  # place font here for anti-aliased label text

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

simply_utils-0.1.1.tar.gz (397.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

simply_utils-0.1.1-py3-none-any.whl (394.7 kB view details)

Uploaded Python 3

File details

Details for the file simply_utils-0.1.1.tar.gz.

File metadata

  • Download URL: simply_utils-0.1.1.tar.gz
  • Upload date:
  • Size: 397.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for simply_utils-0.1.1.tar.gz
Algorithm Hash digest
SHA256 a90dc6aff0f8733b27f92a1c849aec28befb3dc6dd3fa85b4b441a646f2157dc
MD5 804987528b2b3fd8a79cd4fbc72b3924
BLAKE2b-256 77b5957829e576bf20bf8747f86d2933822d0cf22fba6e3660b7b8fb3ee20877

See more details on using hashes here.

File details

Details for the file simply_utils-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: simply_utils-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 394.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for simply_utils-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a56a4ab991b7ef5bf3a7b24ba11fab48fc0394502dc8fdfc4f1fa96d64badb58
MD5 6bfd3c87687d24e471535993cad1a987
BLAKE2b-256 9dbaa669bd3f136fcb6891e7041e13c0da89d798c8c8d2eb90919f92c4e6b2c3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page