Simple utility library for file management, image processing, 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 |
crop_bboxes |
Crop bounding boxes from an image, returns crops and relative labels |
viz_bboxes |
Draw bounding boxes onto an image and 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 — files in each matched folder only
files = simply.get_files("data/202601*/images")
# Wildcard path — also search subfolders of each matched directory
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"])
# 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,
)
crop_bboxes
Crop bounding boxes from an image and return paired crops and labels.
Returned labels are relative to each crop's top-left corner (0, 0).
Confidence is forwarded to the returned label if present in the input detection.
# Basic crop — all detections
crops, labels = simply.crop_bboxes("image.jpg", detections)
# With padding and class filter
crops, labels = simply.crop_bboxes(
"image.jpg",
detections,
class_filter=["car", "person"],
pad=0.1, # expand bbox by 10% on each side, clipped to image bounds
)
# Iterate crops and labels together
for crop, label in zip(crops, labels):
print(label) # [class_name, x1, y1, x2, y2] relative to crop
# Full example — read labels then crop
detections = simply.read_label(
"image.txt",
fmt_in="norm",
fmt_out="pixel",
class_map=CLASS_MAP,
image_path="image.jpg",
)
crops, labels = simply.crop_bboxes("image.jpg", detections, class_filter=["car"], pad=0.1)
viz_bboxes
Draw bounding boxes onto an image and save as JPEG. Accepts detections directly —
use read_label beforehand to load from a label file.
Output suffix is always .jpg regardless of input format.
# Basic usage — pixel format detections (default)
detections = simply.read_label("image.txt")
simply.viz_bboxes("image.jpg", detections, "output/image_viz")
# Norm format labels — read and convert first
detections = simply.read_label(
"image.txt",
fmt_in="norm",
fmt_out="pixel",
class_map=CLASS_MAP,
image_path="image.jpg",
)
simply.viz_bboxes("image.jpg", detections, "output/image_viz")
# Filter classes and custom colors
simply.viz_bboxes(
"image.jpg",
detections,
"output/image_viz",
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
crop_utils.py # crop_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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file simply_utils-0.1.4.tar.gz.
File metadata
- Download URL: simply_utils-0.1.4.tar.gz
- Upload date:
- Size: 399.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0f617bba7e06bd35065f34050b3fa1c9545d8cb3e36f38578342bbf6a7e58863
|
|
| MD5 |
4aed4d30c68e5de44bfc990b35da2694
|
|
| BLAKE2b-256 |
653ac97309d010c6cf6994e3733630d80b145f37f82748816aae2d9c800cfe53
|
File details
Details for the file simply_utils-0.1.4-py3-none-any.whl.
File metadata
- Download URL: simply_utils-0.1.4-py3-none-any.whl
- Upload date:
- Size: 397.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
296634ee8c20640ae24c811fe08c3f40a58d17ee3616aaf450afc00ed3a7cb2a
|
|
| MD5 |
2c97516fdec25763f20e7c4e69db97a0
|
|
| BLAKE2b-256 |
f019de055530dd4c3e6aef2a1466506c867940e73bee3bc60080b0f3f650c847
|