Skip to main content

PixelFlow

PyPI version CI Python 3.9+ License: MIT

The computer vision library that gets out of your way.

PixelFlow provides a unified, intuitive API for object detection, tracking, annotation, and video processing. Write clean, readable pipelines that work with any ML framework.

import pixelflow as pf
from ultralytics import YOLO

model = YOLO("yolo11n.pt")
video = pf.VideoReader("traffic.mp4", width=640)
tracker = pf.tracker.ByteTracker()
zones = pf.Zones()
zones.add_zone([(100, 400), (500, 400), (500, 600), (100, 600)], zone_id="entrance")

for frame in video:
    detections = pf.from_ultralytics(model.predict(frame))
    detections = tracker.update(detections)
    zones.update(detections)

    frame = pf.annotate.box(frame, detections)
    frame = pf.annotate.label(frame, detections)
    frame = pf.annotate.zones(frame, zones)

    if pf.show_frame("Live", frame) == ord('q'):
        break

Installation

pip install pixelflow

Core Concepts

Two Result Types

Every model output becomes one of two things. Detections for anything that localises — detection, segmentation, keypoints, OCR. Classifications for anything that only names.

They are peers, not variants of each other: three detections are three objects, while three classifications are three competing answers about one image.

Detections - One Format, Every Framework

Convert outputs from any ML framework into a unified format:

import pixelflow as pf

# Ultralytics YOLO
from ultralytics import YOLO
model = YOLO("yolo11n.pt")
detections = pf.from_ultralytics(model.predict(image))

# Detectron2
from detectron2.engine import DefaultPredictor
predictor = DefaultPredictor(cfg)
detections = pf.from_detectron2(predictor(image), labels=["person", "car"])

# Mayaku (returns Instances directly; take labels from the checkpoint)
from mayaku import from_pretrained
predictor = from_pretrained("mayaku-n-det")
detections = pf.from_mayaku(predictor("photo.jpg"), labels=predictor.class_names)

# HuggingFace Transformers
from transformers import pipeline
detector = pipeline("object-detection")
detections = pf.from_transformers(detector(image))

# Florence-2 - captions and OCR reads land in det.text, <OD> classes in det.class_name
detections = pf.from_florence2(model_output, task_prompt="<OD>")

# SAM (Segment Anything)
detections = pf.from_sam(masks, scores)

# RF-DETR
detections = pf.from_rfdetr(model_output)

# Supervision
detections = pf.from_supervision(sv_detections)

# EasyOCR - text lands in det.text, the quad in det.segments
import easyocr
reader = easyocr.Reader(['en'])
detections = pf.from_easyocr(reader.readtext("sign.jpg"))

# Plain arrays (numpy or torch) - no framework container to convert from
detections = pf.from_arrays(boxes, scores, class_ids, labels=model.class_names)

Classifications - When the Model Only Names

A classifier localises nothing, so its result carries no geometry at all:

import pixelflow as pf

# Ultralytics -cls checkpoints
from ultralytics import YOLO
model = YOLO("yolo11n-cls.pt")
result = pf.from_ultralytics_classification(model.predict("dog.jpg"))

result.top1.class_name              # 'golden retriever'
result.top_k(5)                     # -> Classifications, best first
result.to_json()

# Plain scores - CLIP zero-shot, a vendored ResNet, anything with a score vector
prompts = ["a photo of a cat", "a photo of a dog"]
result = pf.from_scores(similarities, labels=prompts)

# Multi-label: however many labels genuinely match, not a fixed count
matches = result.filter_by_confidence(0.3)

# Draw it
frame = pf.annotate.classification(frame, result, top_k=3)

Scores are reported exactly as the model emitted them. PixelFlow does not normalise, re-softmax, or assume they sum to 1, so a softmax over a fixed class list and an independent per-label similarity both survive intact. A model emitting logits should be converted by the caller, who is the only one who knows which convention applies.

Per-frame paths can skip building a row per class:

result = pf.from_ultralytics_classification(model.predict(frame), top_k=5)

Powerful Filtering

Chain filters for complex queries with zero overhead:

# Get high-confidence people in the parking zone, tracked for 5+ seconds
results = (detections
    .filter_by_confidence(min_confidence=0.7)
    .filter_by_class_id("person")
    .filter_by_zones(["parking_lot"])
    .filter_by_tracking_duration(min_seconds=5.0))

# Size-based filtering
large_objects = detections.filter_by_size(min_area=10000)
tall_objects = detections.filter_by_dimensions(min_height=200)
squares = detections.filter_by_aspect_ratio(min_ratio=0.9, max_ratio=1.1)

# Spatial filtering
left_side = detections.filter_by_position(max_x=frame_width // 2)

# Remove overlapping detections
unique = detections.remove_duplicates(iou_threshold=0.5)

# OCR-specific filters
titles = detections.filter_by_text_level("title")
english_text = detections.filter_by_text_language("en")

Media Handling

Purpose-built classes for videos, cameras, and images:

import pixelflow as pf

# Read a video file
video = pf.VideoReader("video.mp4", width=640)
print(f"{video.width}x{video.height}, {video.fps}fps, {len(video)} frames")

for frame in video:       # Replayable — resets on each iteration
    if pf.show_frame("Preview", frame) == ord('q'):
        break

# Load a single image
image = pf.read_image("photo.jpg", width=640)

# Webcam / network stream
cam = pf.CameraStream(0, width=640)
cam = pf.CameraStream("rtsp://camera.local/stream")

# Write processed video
writer = pf.VideoWriter("output.mp4", fps=video.fps)
for frame in video:
    processed = process(frame)
    writer.write(processed)
writer.close()

Zone-Based Analytics

Define spatial regions and track what enters them:

import pixelflow as pf

zones = pf.Zones()

# Define zones with different trigger strategies
zones.add_zone(
    polygon=[(100, 400), (300, 400), (300, 600), (100, 600)],
    zone_id="entrance",
    name="Main Entrance",
    trigger_strategy="bottom_center"  # Trigger when bottom-center of bbox enters
)

zones.add_zone(
    polygon=[(500, 200), (700, 200), (700, 500), (500, 500)],
    zone_id="restricted",
    trigger_strategy="percentage",
    overlap_threshold=0.3  # Trigger when 30% of bbox is inside
)

# Update detections with zone info
zones.update(detections)

# Access zone statistics
print(zones.get_zone_counts())  # {'entrance': 3, 'restricted': 1}
print(zones.get_zone_stats())   # Detailed stats including total_entered

# Filter by zone
entrance_only = zones.filter_by_zones(detections, ["entrance"])
not_restricted = zones.filter_by_zones(detections, ["restricted"], exclude=True)

Trigger Strategies:

  • center - Bounding box center point (default)
  • bottom_center - Bottom-center point (great for people/vehicles)
  • percentage - Percentage of bbox overlap
  • overlap - Any intersection
  • Multiple strategies with mode="any" or mode="all"

Object Tracking

Built-in ByteTrack for multi-object tracking:

import pixelflow as pf

tracker = pf.tracker.ByteTracker()

for frame in media.frames:
    detections = pf.from_ultralytics(model.predict(frame))
    detections = tracker.update(detections)

    for det in detections:
        print(f"Track ID: {det.tracker_id}, Class: {det.class_name}")
        print(f"First seen: {det.first_seen_time}, Duration: {det.total_time}s")

Rich Annotations

Beautiful, customizable visualizations:

import pixelflow as pf

# Bounding boxes and labels
frame = pf.annotate.box(frame, detections, thickness=2)
frame = pf.annotate.label(frame, detections, font_scale=0.6)

# Segmentation masks
frame = pf.annotate.mask(frame, detections, opacity=0.4)

# Keypoints and skeletons (pose estimation)
frame = pf.annotate.keypoint(frame, detections)
frame = pf.annotate.keypoint_skeleton(frame, detections)

# Zone visualization
frame = pf.annotate.zones(frame, zones)
frame = pf.annotate.crossings(frame, crossings)

# Privacy protection
frame = pf.annotate.blur(frame, detections, kernel_size=51)
frame = pf.annotate.pixelate(frame, detections, pixel_size=15)

# Shapes
frame = pf.annotate.oval(frame, detections)
frame = pf.annotate.polygon(frame, detections)

Image Transforms

Comprehensive image and detection-aware transformations:

import pixelflow as pf

# Image-only transforms
rotated = pf.transform.rotate(image, 45)
flipped = pf.transform.flip_horizontal(image)
cropped = pf.transform.crop(image, [100, 50, 500, 400])

# Enhancement
enhanced = pf.transform.clahe(image)
gray = pf.transform.to_grayscale(image)
corrected = pf.transform.gamma_correction(image, gamma=1.2)

# Detection-aware transforms (coordinates update automatically)
rotated_img, rotated_dets = pf.transform.rotate_detections(image, detections, 45)
flipped_img, flipped_dets = pf.transform.flip_horizontal_detections(image, detections)
cropped_img, cropped_dets = pf.transform.crop_detections(image, detections, bbox=[100, 50, 500, 400])

# Automatic inverse transforms (undo all transforms)
original_coords = pf.transform.inverse_transforms(transformed_detections)

Sliced Inference for Large Images

Detect small objects in high-resolution images:

import pixelflow as pf

slicer = pf.SlicedInference(
    slice_height=640,
    slice_width=640,
    overlap_ratio_h=0.2,
    overlap_ratio_w=0.2
)

def detector(image):
    return pf.from_ultralytics(model.predict(image))

# Run on 4K image - automatically slices, detects, and merges
large_image = cv2.imread("satellite.jpg")  # 4000x3000
detections = slicer.predict(large_image, detector)

Line Crossing Detection

Track objects crossing defined lines:

import pixelflow as pf

crossings = pf.Crossings()
crossings.add_line(
    start=(100, 300),
    end=(500, 300),
    line_id="entry_line",
    direction="down"  # Only count downward crossings
)

for frame in media.frames:
    detections = tracker.update(pf.from_ultralytics(model.predict(frame)))
    crossings.update(detections)

    print(f"Crossed: {crossings.get_counts()}")
    frame = pf.annotate.crossings(frame, crossings)

Serialization

Export and import detection data:

# To JSON
json_str = detections.to_json()

# To dictionary (for pandas, etc.)
data = detections.to_dict()
df = pd.DataFrame(data)

# With metrics
report = detections.to_json_with_metrics()

Supported Frameworks

Framework Converter Features
None (plain numpy/torch arrays) from_arrays() Boxes, masks, keypoints
Ultralytics (YOLO) from_ultralytics() Boxes, masks, keypoints
Detectron2 from_detectron2() Boxes, masks, keypoints
Mayaku from_mayaku() Boxes, masks, keypoints
HuggingFace Transformers from_transformers() Boxes, scores
Florence-2 from_florence2() Boxes, quads, polygons, captions
SAM from_sam() Masks, scores
EfficientTAM from_efficienttam() Masks, scores
RF-DETR from_rfdetr() Boxes, masks
Supervision from_supervision() Boxes, masks, keypoints
Falcon Perception from_falcon_perception() Boxes, masks
EasyOCR from_easyocr() Quads, text, scores
Datamarkin API from_datamarkin() Boxes, masks, keypoints

Documentation

Full documentation available at https://datamarkin.com/docs/pixelflow/

Contributing

Contributions welcome! Please read our contributing guidelines.

License

MIT License - see LICENSE file for details.


Built with love by Datamarkin

Download files

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

Source Distribution

pixelflow-0.3.2.tar.gz (169.8 kB view details)

Uploaded Source

Built Distribution

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

pixelflow-0.3.2-py3-none-any.whl (195.5 kB view details)

Uploaded Python 3

File details

Details for the file pixelflow-0.3.2.tar.gz.

File metadata

  • Download URL: pixelflow-0.3.2.tar.gz
  • Upload date:
  • Size: 169.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pixelflow-0.3.2.tar.gz
Algorithm Hash digest
SHA256 f12db0e38262a6df5220501b8444b202ff661b41d2b3e7122ef341a9f9cfb167
MD5 2771635ce567bdabc147b0dcd8d4363c
BLAKE2b-256 900bfd3e0a11dcf83d7abae50bec45b4ba07b68cef87f8676c66b74d529044ba

See more details on using hashes here.

Provenance

The following attestation bundles were made for pixelflow-0.3.2.tar.gz:

Publisher: publish.yml on datamarkin/pixelflow

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pixelflow-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: pixelflow-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 195.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pixelflow-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 b341cabcf6148c8a9a53b0cc2e3b2e0c4bb6f0c19c332e1e996ed23961b93b27
MD5 6c9b24e6c22b934bf392ecc7328d1d86
BLAKE2b-256 34405accb2026198b6e0fbf0fa28ae8c788e741b972f7c77b83b411b1cb32622

See more details on using hashes here.

Provenance

The following attestation bundles were made for pixelflow-0.3.2-py3-none-any.whl:

Publisher: publish.yml on datamarkin/pixelflow

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.5.0

2 files

This release

0.3.2 This release

2 files

0.3.1

2 files

0.3.0

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page