Skip to main content

Quick start

Two parts. Part 1 gets the inference API answering. Part 2 gets a first curation result from a public sample. Not sure which you need? Read Choose your path first.

1. Core inference API​

Install​

curl -fsSL https://raw.githubusercontent.com/davidamacey/OpenProcessor/main/setup-openprocessor.sh | bash

This installs the latest published release into ./openprocessor/: no git clone, no image build, no host Python. It asks which tiers you want, picks GPUs, pulls digest-pinned images, exports the TensorRT engines inside the containers, starts everything and runs a health check. First install takes 30-60 minutes, mostly image pulls and TensorRT export.

TierWhat you get
coreTriton, the API (port 4603), OpenSearch
curationcuration workers, PE-Core embeddings, evaluator (implies core)
segmenterSAM 3 segmenter (gated HuggingFace token)
vlmlocal vLLM serving a catalog model
trainertraining service + MLflow
cropwrightthe Cropwright web UI

Monitoring is a flag (--with-monitoring), not a tier. For no prompts:

curl -fsSL https://raw.githubusercontent.com/davidamacey/OpenProcessor/main/setup-openprocessor.sh \
| bash -s -- --tiers core,curation,cropwright --unattended

The installer checks every file against the release's SHA256SUMS (integrity, not authenticity) and sizes the OpenSearch heap from your RAM (half of it, 2 to 30 GB). Cropwright is reachable on your LAN by default, for homelab or small-business use. The API itself stays bound to 127.0.0.1. There is no login on Cropwright — a warning is shown. Pass --local-only to opt out and keep everything on 127.0.0.1.

Tier sizes, every flag, verifying the download by hand, network access, heap sizing, upgrade, rollback and troubleshooting: One-line installer.

Check that it works​

cd openprocessor
./openprocessor health

Expected: the pretty-printed API /health JSON. status is ready and both triton and opensearch report "ok": true:

{
"status": "ready",
"version": "0.6.0",
"api_version": "v1",
"services": {
"triton": {"ok": true, "detail": "is_server_live OK", ...},
"opensearch": {"ok": true, "detail": "HTTP 200", ...}
},
...
}

Then detect objects in any image:

curl -X POST http://127.0.0.1:4603/detect -F "image=@your-image.jpg"

Expected: JSON with a detections list. Coordinates are normalized to 0.0-1.0; multiply by the image width and height for pixels.

Day-to-day commands, all run from the install directory:

./openprocessor status # containers, health, GPU memory
./openprocessor logs api -f # live logs
./openprocessor sample coco # public COCO sample (200 images), used in part 2

Install from source​

For development, or to build the images yourself:

git clone https://github.com/davidamacey/OpenProcessor.git && cd OpenProcessor
./scripts/setup.sh

scripts/setup.sh pulls the pre-built images, detects your GPU and picks a profile (minimal, standard, full), writes .env (it never overwrites an existing .env unless you pass --force, and a forced rewrite keeps your OPENSEARCH_HEAP), downloads the base weights, runs the one-time TensorRT export, starts the core services and runs smoke tests.

Useful flags (all from ./scripts/setup.sh --help):

FlagEffect
--yes, -yAccept defaults, no prompts
--profile=NAMEForce minimal, standard or full
--gpu=IDGPU id to use (default 0)
--skip-download / --skip-export / --skip-startSkip that step
--forceOverwrite an existing .env
--curationPrint the curation next steps at the end

Manage a checkout with make:

make up # core stack (Triton + API + OpenSearch)
make curation-up # the curation workers (compose profile "curation")
make status # health of all services
make down
./openprocessor in a checkout

In a checkout the openprocessor CLI uses the compose project openprocessor when .env does not name one (the .env that scripts/setup.sh writes does not) and prints a warning; add COMPOSE_PROJECT_NAME=<name> to .env to choose another. An install directory still refuses to run without it (exit code 3). When you run docker compose by hand, always pass -p <project> so you never act on another stack that uses the default name.

Prefer to do it by hand? See Model export and Compose profiles.

2. Curation with public sample data​

No dataset ships in the repository and nothing proprietary is bundled. The sample fetchers download small, license-filtered, deterministic subsets of public datasets into the gitignored data/samples/ directory, from pinned manifests under scripts/datasets/manifests/.

Make targetWhat it fetchesOutput
make sample-coco-readme200 COCO 2017 images, 20 per class (about 1-2 minutes)data/samples/coco_va_readme/
make sample-coco800 COCO 2017 images (10 classes, 80 per class) plus 12-image upload and 24-image post_promote side setsdata/samples/coco_va/
make sample-plates300 Open Images V7 "Vehicle registration plate" images with YOLO-format region labelsdata/samples/oi_plates/
make sample-cleanRemoves everything under data/samples/

COCO images are kept only under Attribution, Attribution-ShareAlike, "No known copyright restrictions" or "United States Government Work" Flickr licenses; Open Images images only under CC BY 2.0. Each fetch writes an ATTRIBUTION.csv next to the images. Run python3 scripts/datasets/fetch_coco_subset.py --help or python3 scripts/datasets/fetch_openimages_plates.py --help for every flag.

Walkthrough​

Curation data lives in projects: isolated workspaces with their own classes, items, settings and jobs. A fresh stack has one project, default. The walkthrough makes its own so nothing is shared.

Docker commands below use -p openprocessor, the default compose project name. If you set another COMPOSE_PROJECT_NAME in .env, use that name.

Step 1: make sure the encoders and workers exist​

Curation needs the PE-Core encoders and the curation compose profile.

  • Installed directory. Nothing to do if you installed the curation tier. Otherwise re-run the installer with --tiers core,curation.

  • Source checkout. scripts/setup.sh does not build the PE-Core encoders:

    make export-pe

    Expected: pe_image_encoder and pe_text_encoder are READY in make models-list.

Step 2: fetch a sample​

make sample-coco-readme # source checkout
# or, from an installed directory:
./openprocessor sample coco

Expected: 200 images and an ATTRIBUTION.csv under data/samples/coco_va_readme/. Both commands write there.

Step 3: let the containers see the sample​

The API and the workers read images from one source root, mounted read-only at /data/source. The host side is OP_SOURCE_ROOT_HOST in .env. Three services mount it: api, curation-detection-worker and curation-auto-label-worker. If one of them lacks the mount, thumbnails 404 and the VLM stage labels nothing.

  • Installed directory. The installer already set OP_SOURCE_ROOT_HOST=./data, so the sample is at /data/source/samples/coco_va_readme/images. Skip to step 4.

  • Source checkout. The default root is ./data/source. Point it at the samples directory, set the ingest detector, and recreate the services:

    cat >> .env <<'EOF'
    OP_INGEST_PRIMARY_DETECTOR_MODEL=yolov11_small_trt_end2end
    OP_SOURCE_ROOT_HOST=./data/samples
    EOF
    make up && make curation-up

    Expected: docker compose -p openprocessor ps lists api, curation-detection-worker and curation-auto-label-worker as recently created. The sample is at /data/source/coco_va_readme/images.

restart does not re-read .env, so a plain restart keeps the old mount. ./openprocessor start and make up / make curation-up recreate the services whose configuration changed.

Step 4: create a project and its classes​

API=http://127.0.0.1:4603
P=coco-sample
curl -s -X POST "$API/curation/projects" \
-H 'content-type: application/json' \
-d "{\"slug\": \"$P\", \"display_name\": \"COCO sample\"}"

# Seed the classes from the detector's own labels. The default is a dry run;
# "dry_run": false writes. Or create your own with POST .../classes.
curl -s -X POST "$API/curation/projects/$P/classes/seed_from_detector" \
-H 'content-type: application/json' \
-d '{"dry_run": false}'

Expected: the project record, then a created list with one entry per detector label (person, bicycle, car, and so on for all 80 COCO classes).

Step 5: ingest​

--folder is a path inside the API container, under /data/source. Use the path from step 3.

SRC=/data/source/samples/coco_va_readme/images # installed directory
# SRC=/data/source/coco_va_readme/images # source checkout

docker compose -p openprocessor exec api python scripts/curation/bulk_ingest.py \
--project $P --folder $SRC

The script shows live progress and exits 0 when the run completes, 1 when some images failed, 2 when the run failed.

Check the counts:

curl -s "$API/curation/projects/$P/stats"

Expected: counts.images reaches 200 and counts.items is higher (one item per detection). counts.pending_detection drops to 0 once the detection worker has finished.

To ingest a large folder from a NAS, see Ingest a large folder.

Step 6 (optional): cluster and label​

The curation profile is already up. Start an auto-label run:

curl -s -X POST "$API/curation/projects/$P/pipeline/auto_label/start?run_vlm=true&train_clusters=true"
curl -s "$API/curation/projects/$P/pipeline/auto_label/status"

Expected: a job id, then status moving to completed. auto_label clusters only by default (run_vlm=false). The VLM labels nothing unless you pass run_vlm=true and the project has activated a VLM endpoint, or you name one with vlm=<name> (see VLM selection). A new project has none, so the request above is refused with 409 vlm_not_configured until you activate one; drop run_vlm=true to cluster without a VLM.

Things that trip people up​

  • Every curation data route is under /curation/projects/{project}/. An unscoped path such as /curation/classes is a 404.
  • The detector stores everything it sees. A stock detector's whole label space (all 80 COCO classes) becomes unlabeled item proposals, each carrying the detector's own label (person, dog, pizza and so on, not only vehicles). To store or embed less, set the per-project ingest policy (PUT /curation/projects/{project}/ingest/policy) before a big ingest; the old OP_INGEST_PRIMARY_CLASS_IDS env var is retired and ignored. See Ingest policy.
  • Cost of the full vocabulary. Each stored detection also gets an embedding by default, so the full COCO vocabulary stores about 3.6x the data of a vehicle-only run (the "Ingest cost per image" section of docs/PERFORMANCE.md).
  • Switching detectors. Set OP_INGEST_PRIMARY_DETECTOR_MODEL (and OP_INGEST_PRIMARY_LABELS_PATH if the model directory has no labels.txt) in .env, then recreate api. The model must be loaded in Triton and serve the end2end four-tensor output. The detector is deployment-wide.
  • Ingest returns 503 until OP_INGEST_PRIMARY_DETECTOR_MODEL is set and that model is loaded in Triton. The installer sets it; a checkout does not.
  • OP_SOURCE_ROOT_HOST is the host side of a fixed /data/source mount. The walker's --folder must be a container path under it, otherwise every image fails as unservable_path.
  • OP_* variables are read at process start. Recreate the services after changing them (./openprocessor start or make up); a restart is not enough.
  • GET /curation/projects/{project}/health reports degraded until at least one class exists and Triton and OpenSearch are reachable.
The project switcher (captured from Cropwright against the public COCO sample projects)
A new project's empty cluster view (captured from Cropwright against the public COCO sample projects)

Bring a labeled dataset instead​

If you already have a YOLO or COCO dataset, import it rather than re-labeling. Classes are matched to the project's classes by name, and the import can be resumed or undone:

make sample-coco-import # public COCO fixture, four layouts under data/samples/import_fixture
docker compose -p openprocessor exec api python scripts/curation/import_labeled_dataset.py \
--project $P --dataset /data/source/import_fixture/yolo \
--accept-suggestions --dry-run

The dataset path is a container path under the source root, as in step 3 (/data/source/samples/import_fixture/yolo for an installed directory). Drop --dry-run to import. See Dataset import.

Regions: the car-to-wheel example​

A region is a sub-object found inside each item, such as a wheel inside a car, with its own boxes, states and (optionally) text. The public car-to-wheel example walks through a region profile, a prompt pack, VLM verification and multi-box review on public COCO images; see the wheel example.

The Open Images plates set (make sample-plates) is region ground truth (whole frames with a single region class), not item classes. Index its images with scripts/curation/import_labeled_dataset.py --images-only so the labels never touch the item registry, activate a region profile (see Region profiles), let the detection worker's cascade run, then score it with scripts/curation/eval_regions_vs_gt.py.

Continue with the end-to-end curation workflow.