Skip to content

DetectionLogger ​

DetectionLogger writes image frames and their bounding-box annotations to a YOLO or RF-DETR/COCO dataset on disk. Frames are split automatically across train, val, and test according to configurable ratios.

Import ​

python
from telekinesis.dataengine import DetectionLogger

Creating a Logger ​

Use the DetectionLogger.create() factory to get a format-specific logger.

python
logger = DetectionLogger.create(
    dataset_format,     # "yolo" | "rfdetr"
    dataset_dir,        # output directory
    categories=None,    # see Categories below
    *,
    task="detect",      # "detect" | "segment"
    mode="create",      # "create" | "overwrite" | "append"
    split_ratios=None,  # see Split Ratios below
    color_order="rgb",  # "rgb" | "bgr"
    image_ext=".jpg",
)

Parameters ​

ParameterTypeDescription
dataset_formatstr"yolo" or "rfdetr".
dataset_dirstr | PathRoot directory for the dataset. Created if it does not exist.
categorieslist[dict] | NoneCategory definitions (see below). None enables dynamic registration.
taskstr"detect" for bounding boxes, "segment" for instance segmentation masks.
modestrHow to handle an existing dataset at dataset_dir (see Modes).
split_ratiosdict | NonePer-split fractions, e.g. {"train": 0.8, "val": 0.1, "test": 0.1}. Default 80/10/10.
color_orderstrChannel order of input numpy arrays. Converted to RGB before writing.
image_extstrOutput image extension. Default .jpg.

Categories ​

Pass a list of COCO-style category dicts:

python
categories = [
    {"id": 1, "name": "box",    "supercategory": "object"},
    {"id": 2, "name": "carton", "supercategory": "object"},
]

Passing categories=None enables dynamic registration: unknown category ids are registered automatically on first use. Useful when the full class list is not known upfront.

Modes ​

ModeBehavior
"create"Fails if dataset_dir already contains dataset files. Safe default.
"overwrite"Deletes existing dataset files in dataset_dir and starts fresh.
"append"Resumes an existing dataset, continuing image and annotation counters.

Split Ratios ​

Splits are assigned using a largest-deficit algorithm so that the running per-split proportions track the configured ratios at every step — no batching required.

python
# Custom split — e.g. 90% train, 5% val, 5% test
logger = DetectionLogger.create(
    "yolo", "results/dataset", categories,
    split_ratios={"train": 0.9, "val": 0.05, "test": 0.05}
)

To assign a split explicitly when logging, pass split= to log().

Logging Frames ​

python
logger.log(
    image,              # np.ndarray | Image datatype
    annotations=None,   # list[dict] | ObjectDetectionAnnotations | None
    split=None,         # "train" | "val" | "test" | None (auto-assigned)
    file_name=None,     # optional filename stem override
)

Annotation Format ​

Annotations are COCO-style dicts, one per object:

python
annotations = [
    {
        "id": 1,
        "image_id": 0,          # filled automatically if omitted
        "category_id": 1,
        "bbox": [x, y, w, h],   # absolute pixel coordinates
        "area": w * h,
        "iscrowd": 0,
    },
    ...
]

The logger also accepts ObjectDetectionAnnotations datatypes directly, which is the native output format of Telekinesis detection Skills.

Closing ​

Always call close() when done. It flushes the final dataset manifest (data.yaml for YOLO, _annotations.coco.json for RF-DETR).

python
logger.close()

The logger is also a context manager:

python
with DetectionLogger.create("yolo", "results/dataset", categories) as logger:
    for image, annotations in detections:
        logger.log(image, annotations)
# close() called automatically

Example ​

python
import numpy as np
from telekinesis.dataengine import DetectionLogger

# ------------------------------------------------
# 1. Define categories and create logger
# ------------------------------------------------
categories = [
    {"id": 1, "name": "box",    "supercategory": "object"},
    {"id": 2, "name": "carton", "supercategory": "object"},
]

logger = DetectionLogger.create(
    "yolo",
    "results/warehouse_dataset",
    categories,
    mode="create",
    split_ratios={"train": 0.8, "val": 0.1, "test": 0.1},
)

# ------------------------------------------------
# 2. Simulate detections and log frames
# ------------------------------------------------
for i in range(20):
    image = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)
    annotations = [
        {
            "id": i * 2,
            "category_id": 1,
            "bbox": [100, 80, 200, 150],
            "area": 200 * 150,
            "iscrowd": 0,
        }
    ]
    logger.log(image, annotations)

# ------------------------------------------------
# 3. Close — writes data.yaml
# ------------------------------------------------
logger.close()

Output Layout ​

YOLO ​

results/warehouse_dataset/
├── images/
│   ├── train/   frame_0000.jpg ...
│   ├── val/     frame_0016.jpg ...
│   └── test/    frame_0018.jpg ...
├── labels/
│   ├── train/   frame_0000.txt ...
│   ├── val/     frame_0016.txt ...
│   └── test/    frame_0018.txt ...
└── data.yaml

RF-DETR / COCO ​

results/warehouse_dataset/
├── train/
│   ├── frame_0000.jpg ...
│   └── _annotations.coco.json
├── valid/
│   ├── frame_0016.jpg ...
│   └── _annotations.coco.json
└── test/
    ├── frame_0018.jpg ...
    └── _annotations.coco.json
Publisher / Subscriber
Decouple disk writes from detection so I/O never stalls the capture loop.
Read more →