Skip to main content

horde-image-utilities

Isolated heavy image capabilities for the AI Horde worker ecosystem.

Some image utilities a worker needs (background removal via rembg/onnxruntime, and later controlnet annotators and layer diffusion) carry heavy, conflict-prone native dependencies. Pulling those into the worker's main environment bloats it and risks dependency clashes. This package keeps them out of the way: it runs each capability as a small FastAPI service in its own virtual environment, and the worker talks to it over loopback HTTP using a dependency-free client.

It ships four cooperating pieces:

  • Service: a FastAPI application exposing capability endpoints (for example POST /rembg/remove-background) plus an always-on operational surface under /ops.
  • Client: a standard-library-only HTTP client (HordeImageUtilitiesClient) that the lean consumer uses to call the service. Its only runtime dependency is Pillow, already a base dependency.
  • Launcher: CapabilityServerProcess starts, health-checks, and tears down the service subprocess, optionally under a different interpreter so the heavy dependencies live in a separate venv.
  • Provisioning: build_provision_commands produces the uv commands that create a capability venv and install this package with the right extras.

Two-mode model

  • Lean consumer environment: installs the base package only (Pillow, pydantic). It imports horde_image_utilities.client and horde_image_utilities.launcher to drive a capability process. No FastAPI, rembg, torch, or onnxruntime enter this environment.
  • Capability environment: a separate venv that installs the server extra plus exactly one backend extra. It runs the actual model inference. Keeping this environment separate is what makes the dependency isolation real.

Install matrix

The base install has no heavy backends. Capabilities are opt-in extras:

Extra Provides
server FastAPI transport for running the service (fastapi[standard]).
rembg_cpu Background removal on CPU (rembg[cpu]).
rembg_cuda Background removal on NVIDIA CUDA (rembg[gpu] + onnxruntime-gpu).
rembg_rocm Background removal on AMD ROCm, Linux only (rembg[rocm] + onnxruntime-rocm).
annotators ControlNet preprocessors (canny, hed, depth, normal, openpose, seg, ...) via torch. A single extra serves every accelerator backend; the torch wheel index is the installer's concern.
mediapipe MediaPipe face-mesh detector (mediapipe_face).

The three rembg_* extras are mutually exclusive: exactly one accelerator backend per environment. This is enforced by a [tool.uv].conflicts declaration, so uv refuses to install more than one. The annotators extra has no accelerator split (no detector in scope uses onnxruntime), so no conflict entry is needed.

Do not co-install mediapipe with annotators in the same environment. mediapipe pulls in opencv-contrib-python (the GUI wheel) while annotators pulls in opencv-contrib-python-headless. Two opencv wheels sharing the cv2 namespace break cv2 (it imports as an empty namespace). Until this is resolved via a dependency override at deployment-pin compile time, keep mediapipe in its own environment. The mediapipe_face detector is future work and is not required by any worker capability.

Provision a capability venv (CUDA background removal shown):

uv venv .venv-rembg
uv pip install --python .venv-rembg "horde-image-utilities[server,rembg_cuda]"

Quickstart

Run the service:

# In the capability environment
python -m horde_image_utilities
# or the console script:
horde-image-utilities

Call it from the lean consumer environment:

from PIL import Image

from horde_image_utilities.client import HordeImageUtilitiesClient

client = HordeImageUtilitiesClient("http://127.0.0.1:7860")
if client.health(timeout=5.0):
    result = client.remove_background(Image.open("input.png"))
    result.save("output.png")

health() and remove_background() accept a per-call timeout that overrides the client-wide one, so a liveness probe can use a short budget while the work request keeps the long one. A failed call reports whether the service could not be reached at all or was reached but did not answer within the timeout.

Or let the launcher own the subprocess lifecycle (point python_executable at the capability venv):

from horde_image_utilities.launcher import CapabilityServerProcess

with CapabilityServerProcess(python_executable="/path/to/.venv-rembg/bin/python") as server:
    memory_report = server.client.get_memory_report()

Configuration

Settings are read from the environment with the HIU_ prefix (see horde_image_utilities.config.ServiceSettings):

Variable Default Purpose
HIU_HOST 127.0.0.1 Bind host for the service.
HIU_PORT 7860 Bind port for the service.
HIU_MAX_REQUEST_SIZE 67108864 Maximum accepted request size in bytes.
HIU_CONTROLNET_MAX_CONCURRENT 1 Concurrent controlnet processing slots.
HIU_CONTROLNET_QUEUE_TIMEOUT 30.0 Seconds to wait for a controlnet slot.
HIU_ISOLATE_MODEL_CACHE true Store model files under AIWORKER_CACHE_HOME when set.
HIU_ALLOW_DOWNLOADS false Allow runtime model downloads when missing from cache.
HIU_ANNOTATOR_MODEL_DIR unset Base directory holding annotator checkpoints. Defaults to the annotators/ cache directory below.
HIU_VENDOR_DIR unset Base directory for the cloned upstream annotator node. Defaults to a vendor/ sibling of the cache directories below.

HIU_ALLOW_DOWNLOADS defaults to false: the service will not fetch missing models at runtime unless you opt in. Pre-download models, or set it to true.

Cache layout

When HIU_ISOLATE_MODEL_CACHE is enabled and AIWORKER_CACHE_HOME is set, model files are stored under an isolated, per-capability directory:

$AIWORKER_CACHE_HOME/horde/image-utilities/rembg/                     # rembg ONNX models
$AIWORKER_CACHE_HOME/horde/image-utilities/controlnet_aux/            # controlnet_aux base
$AIWORKER_CACHE_HOME/horde/image-utilities/controlnet_aux/annotators/ # annotator checkpoints (default)
$AIWORKER_CACHE_HOME/horde/image-utilities/vendor/                    # cloned upstream annotator node (default)

If AIWORKER_CACHE_HOME is not set, or isolation is disabled, each backend falls back to its own default (U2NET_HOME / ~/.u2net for rembg, the Hugging Face cache for controlnet). The service logs a warning when conflicting cache environment variables are detected.

Annotator checkpoints

The annotators capability resolves weight files under HIU_ANNOTATOR_MODEL_DIR (defaulting to the annotators/ directory shown above), laid out as <repo_id>/<filename> exactly as the worker pre-populates them (for example lllyasviel/Annotators/ControlNetHED.pth). Missing weights return HTTP 409 rather than triggering a download (downloads are off unless HIU_ALLOW_DOWNLOADS=true).

The transformers-hub control types are the exception: their weights load through the transformers library from the standard Hugging Face hub cache, so the deployment must point HF_HOME (or HUGGINGFACE_HUB_CACHE) at a cache pre-populated with the relevant model. These are normal and midas_depth (Intel/dpt-hybrid-midas), zoe_depth (Intel/zoedepth-nyu-kitti), depth_anything (LiheYoung/depth-anything-large-hf), and oneformer_ade20k / oneformer_coco (shi-labs/oneformer_*_swin_large).

Implemented control types span the classic set and the extended surface the AI-Horde annotation form and worker pre-annotation use:

  • Classic: canny, scribble, hed, fakescribbles, mlsd, depth (LeReS), normal (MiDaS), openpose, seg (UniFormer).
  • Weightless algorithmic: binary, standard_lineart, scribble_xdog, pyracanny, color, shuffle, recolor_luminance, recolor_intensity, tile, tile_ttplanet_guided, tile_ttplanet_simple.
  • Annotator-layout weighted: lineart, lineart_anime, lineart_anime_denoise, pidinet, scribble_pidinet, teed, normal_bae, depth_anything_v2.
  • Transformers-hub weighted: midas_depth, zoe_depth, depth_anything, oneformer_ade20k, oneformer_coco.

Per-detector parameters match the defaults hordelib passes through the comfyui_controlnet_aux AIO preprocessor (only the resolution knob is set; every other widget stays at its node default), so pre-annotation here is interchangeable with hordelib's in-graph annotation. Notably lineart runs with coarse disabled, mirroring hordelib, which drives it through the LineArtPreprocessor node without setting the coarse widget.

The seg runtime comes from the clone's bundled MMSegmentation/MMCV subtree, which runs on CPU without compiled ops given the pure-python MMCV config packages the annotators extra pulls in. The mediapipe_face control type is registered but not implemented and returns HTTP 501.

At startup a best-effort prefetch (enabled only when HIU_ALLOW_DOWNLOADS=true) warms the classic control types' weights before the extended set, so a fresh install can serve the legacy surface quickly while the extended weights trickle in.

Annotator detector source (clone at a pin)

The annotator detector algorithms are not vendored into this repository. On first use (and, best-effort, at service startup) the capability clones comfyui_controlnet_aux at a commit pinned in horde_image_utilities/annotators/upstream_manifest.json, prepends the clone's src to sys.path, and imports custom_controlnet_aux directly (the clone is not pip-installed). This mirrors how hordelib pins ComfyUI: the acquisition is idempotent and self-healing, performing no network access once the clone is already at the pin. Two runtime monkeypatches redirect the upstream weight loading through this package's resolver (so pre-populated checkpoints are used and missing files return HTTP 409) and make the LeReS detector load only its depth model, never the pix2pix "boost" model. See the repository NOTICE for the attribution and the exact patch sites.

Pre-bake the clone for an offline or image-baking step (git only; needs neither torch nor the annotator extra):

python -m horde_image_utilities.annotators

The /ops surface

The ops endpoints mount in every install, including lean ones with no heavy backend. They assume a trusted loopback caller (the worker that launched the process) and carry no authentication.

Endpoint Purpose
GET /ops/memory Report process resident-set size, torch CUDA figures (null without torch), the onnxruntime session count, and loaded model names.
POST /ops/release-cache Release framework caches (torch's CUDA allocator when present) and return a fresh memory report.
POST /ops/unload Drop cached sessions for one capability ({"capability": "background_removal"}) or all ({"capability": null}).
POST /ops/shutdown Schedule a graceful process exit after the response flushes.

The client exposes typed methods for each: get_memory_report(), release_cache(), unload(capability=None), and shutdown().

A GET /health liveness endpoint is always available regardless of which backends are installed. It is answered on the event loop while capability work runs in worker threads, so it reports promptly even while a long background removal is in progress; background removals themselves are serialised, so a burst of them queues instead of running in parallel.

Development

uv sync
uv run ruff format . && uv run ruff check . --fix
uv run pytest

Tests that require a rembg backend are skipped automatically when it is not installed. The package version is derived from the release tag via hatch-vcs; there is no version string to bump in source.

Metadata

Release files for horde-image-utilities 0.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for horde-image-utilities 0.3.2
File Size Uploaded
horde_image_utilities-0.3.2.tar.gz 3.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for horde-image-utilities 0.3.2
File Interpreter ABI Platform
horde_image_utilities-0.3.2-py3-none-any.whl Python 3 none any Details

Total release size: 3.3 MB

Release files / horde_image_utilities-0.3.2.tar.gz

Download URL horde_image_utilities-0.3.2.tar.gz
Size 3.2 MB
Tags Source
SHA-256 checksum
How to use checksums
3fd2b617178e5191154adbd83b146299bbf5ca2aeaf23203433d43bd48d9dc7e
BLAKE2b-256 checksum
How to use checksums
01b0e40de4e04299e4b1b33ea961f9d867706be7ef21666f12a66fef297e3511
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / horde_image_utilities-0.3.2-py3-none-any.whl

Download URL horde_image_utilities-0.3.2-py3-none-any.whl
Size 87.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c4f5e25b9a8c8536cc298a5a0a6b905e6e0488b316420cf9d52340bc961e66a2
BLAKE2b-256 checksum
How to use checksums
832599671899adf6b76b3b13b16de2dfb2fef720cbe956da770e9d2eb06b705d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.2 This release

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release 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