01 / START HERE
What this version does
Camera Studio is a working local setup tool. It captures camera frames, straightens the cloth, records labelled ball boxes, trains a small camera-specific appearance model, tests its detections and exports reusable data. It does not yet score games, infer fouls, identify players, create a public camera relay or upload a model into the older Umbrel service.
The proposed public address is livestream.cuetracker.app. This package is a local development version. Publishing the subdomain and integrating an inference worker into the existing stream are separate rollout steps.
Install the local connector
Download the Camera Studio connector source ZIP. On a local connector instance, use the copy you already extracted.
- Install Python 3.13 on the computer that can reach your camera. Download and extract the Camera Studio source package.
- Open a terminal in the extracted
cuetracker-camera-studiofolder. - Create an isolated environment and install the two required packages:
python -m venv .venv # Windows PowerShell .venv\Scripts\python.exe -m pip install -r requirements.txt .venv\Scripts\python.exe server.py # macOS / Linux (use python3 for the first command if needed) .venv/bin/python -m pip install -r requirements.txt .venv/bin/python server.py
Open http://127.0.0.1:8765 on that computer. The server listens only on loopback. Stop it with Ctrl+C. Your settings, training images and models persist under data/; source URLs and viewing passwords are memory-only and must be re-entered after a restart.
Use one data folder per camera and fixed angle. For another camera, launch a second instance on another port with its own STUDIO_DATA environment variable. Do not mix crops from two cameras into one profile unless you are deliberately building and evaluating a general model.
# PowerShell: a second camera workspace $env:STUDIO_DATA = 'C:\CueTracker\camera-two' .venv\Scripts\python.exe server.py --port 8766
The published photo workspace can work without the connector for corner marking and JSON export. Live camera access, perspective correction and training run in the local connector. A public website cannot directly reach everyone's private cameras.
02 / THE CONNECTION
Keep the camera input separate from the display output.
| Part | What it does |
|---|---|
| Camera or recorder | Produces a raw image or video: JPEG, MJPEG, RTSP, a USB device, or a recording. |
| Local Camera Studio | Reads the input on the venue network. Calibrates, collects examples and tests the local model. |
| Presentation relay | Provides the browser-compatible feed, optionally rectified or with an overlay. The existing Umbrel service already does this. |
| 8 Ball / KellyPool | Displays a compatible camera URL in Venue Display. It does not need the recorder's administration interface. |
Analysis input: use clean camera frames. Do not train on an AI overlay, painted ball numbers, a score graphic or a computer-generated table. An analysis loop that reads its own output can learn its own mistakes.
Display output: choose real, straightened, AI or hybrid presentation independently. Calibration belongs to a specific source crop, aspect ratio and angle. Changing those invalidates it.
RTSP is useful between camera and local software; browsers do not play it directly. ONVIF helps discover camera streams; an ONVIF address is not itself a video feed. HLS and WebRTC need suitable players and gateways. This connector accepts JPEG/MJPEG over HTTP, RTSP via OpenCV, and local USB camera indices; it does not decode a vendor's cloud dashboard or YouTube watch page.
03 / YOUR EQUIPMENT
Choose a route that matches what you own.
Blue Iris
- Add the physical camera in Blue Iris and confirm its raw view works.
- Find the camera's short name, not just its display title. Check the configured web-server port.
- Create or use a restricted viewing account that can access only the table camera.
- In Camera Studio choose Blue Iris and replace the host, port and short name in the example.
http://BLUE-IRIS-IP:PORT/image/CAMERA-SHORT-NAME http://BLUE-IRIS-IP:PORT/mjpg/CAMERA-SHORT-NAME/video.mjpg
Use the separate username/password fields when the endpoint accepts HTTP Basic or Digest. Blue Iris's secure JSON-session login is a different authentication mechanism: this new generic connector does not implement that login handshake. If you receive a login page or 401, keep authentication enabled and use a restricted local relay or the existing vision service's raw-snapshot endpoint. Do not disable Blue Iris security to make a URL work. Camera Studio's snapshot test is the final check; URL patterns alone do not establish compatibility.
If you already have a local vision service, use its documented unannotated snapshot endpoint. Verify that it shows the raw camera, not an AI overlay.
Agent DVR
- Add the camera through Agent DVR's network camera or ONVIF setup.
- Find its numeric object ID.
- Use the local MJPEG endpoint below, replacing host, port and ID.
- If the API is protected, use the configured viewing credentials. Confirm the endpoint returns images on the connector computer.
http://AGENT-IP:8090/video.mjpg?oid=CAMERA-ID
See the official Agent DVR API reference for the installed version's supported endpoints and authentication.
Direct IP camera / NVR
- Find the manufacturer's local snapshot or RTSP stream URL. Use a read-only camera account.
- Choose a main stream with enough detail and a stable aspect ratio. A high-quality 1080p image is a useful starting point; the actual ball size in pixels matters more than the advertised resolution.
- Paste the HTTP snapshot/MJPEG address or RTSP address into Camera Studio. RTSP decoding uses the local OpenCV/FFmpeg build; codec support varies.
- For RTSP with authentication, use the camera's documented authenticated URL and percent-encode reserved characters in the password. Keep that URL local. It is not written into setup exports.
Reolink, Hikvision, Dahua, Amcrest and other vendors have different paths and settings. The app deliberately does not guess credentials or scan the network. If a vendor only offers a cloud app, export snapshots or recordings, or use an NVR that exposes a documented local feed.
USB webcam / HDMI capture card
Select USB and enter 0, then try 1 if needed. Run Camera Studio on the computer physically connected to the camera. Close software using exclusive camera access. A USB device attached to a different machine is not accessible by entering its device number here.
Phone camera
Use a camera app that explicitly offers a local JPEG/MJPEG or RTSP feed. Connect the phone and connector computer to the same network, copy the app's exact local address and keep the phone awake, powered and fixed in place. If it only offers cloud streaming, capture stills with the phone in the final position and upload them instead.
OBS / recordings / unsupported software
For initial training, export full-resolution stills from recorded video and upload them. For a continuous feed, use an appropriate local relay that converts your source to JPEG/MJPEG or RTSP. OBS Virtual Camera can act as a local webcam where the OS and capture backend support it. This release does not install an OBS plugin or start a stream on your behalf.
04 / PICTURE QUALITY
Fix the image before teaching the model.
- Mount securely. An overhead angle reduces occlusion. An angled view can work, but a player's arm or body can hide several balls. Perspective correction cannot reveal hidden balls.
- Show the entire playing surface. Include all six pockets and a little cushion. Avoid changing optical zoom, aspect ratio, stabilisation or recorder crop after calibration.
- Count pixels, not megapixels. Zoom the still to 100%. Each ball should have a clear edge and visible colour. Aim for at least roughly 20–30 source pixels across a ball as an initial practical target, then measure performance. Numbered-ball reading needs substantially more detail.
- Stabilise lighting. Use even table lighting. Reduce blown-out highlights on yellow and white balls. If the camera allows it, fix focus and white balance after testing. Choose exposure that limits motion blur without making the image too noisy.
- Test the weak areas. Put a red, yellow, black and cue ball near the far rail, near rail, each pocket and the brightest patch. The black ball must be distinguishable from its shadow and the pocket.
Use an uncovered table for calibration and scatter the balls before collecting training examples. A tightly packed rack is a difficult case and should be tested separately.
A 1000 × 500 rectified view is used by this baseline. The 2:1 assumption suits a standard pool playing surface. A nonstandard table ratio or strong fisheye lens requires additional calibration before relying on geometry. OpenCV's camera-calibration documentation explains the distinction between lens distortion and perspective.
05 / FOUR CORNERS
Mark the cloth, then check the result.
- Capture a fresh frame in step 1. Do not move the camera afterwards.
- Open Mark your table and choose Mark four corners.
- Click the inside cushion intersections in order: top left, top right, bottom right, bottom left. Estimate the cloth-plane corner where the pocket cuts it away.
- Use Undo corner or the numeric coordinate fields to correct a point. Coordinates are fractions of the original frame, so 0.5 means halfway across or down.
- Select Straighten table. The cloth should form a rectangle, with straight opposite rails. Balls should look approximately circular; excessive distortion suggests a poor corner choice, fisheye distortion or a very oblique angle.
- Keep these corners for repeated captures from the same feed. A fresh source connection or uploaded photo clears the points to avoid accidentally using an old geometry. Import a saved setup after loading an image from the same fixed camera.
Do not use the table's wooden outer corners as the playing-surface corners. If you also want a display frame with cushions visible, treat that as a separate presentation crop. The old Umbrel service has separate camera-frame, playfield and outline settings; the new exported JSON is not a drop-in replacement for its config file.
06 / REDS & YELLOWS
Teach four classes, without inventing ball numbers.
Select Reds & yellows. The classes are red, yellow, black and cue. All seven reds share one class; all seven yellows share one class. Each detected ball can receive the same colour as another ball. The expected count is a sanity check, never a reason to force a label.
- Remove the triangle and scatter the balls so their edges are separated. Capture and straighten the frame.
- Give the capture a meaningful session name, such as
daylight-layout-1. - Select red, choose Draw ball boxes and drag a tight rectangle around every red ball. Include its full visible outline, avoiding a large border of cloth.
- Repeat for yellow, black and cue. Review the boxes; undo mistakes before saving. For YOLO detector export, every visible ball must be labelled. An unlabelled ball becomes misleading background during detector training.
- Select Save labelled capture. The app stores the frame, boxes, class labels and session name locally.
- Move and rotate the balls. Make another capture under a new session name. Include the far and near ends, corners, rails, highlights and shadows.
- Repeat with realistic changes of lighting. Collect at least six examples of every class across three independent sessions to unlock the local baseline. This is a minimum workflow gate, not an accuracy guarantee; aim for dozens of varied examples per class and collect more where testing fails.
- Select Train local candidate. Training produces a separate candidate, without replacing the existing Umbrel model.
Use the same session name for near-duplicate frames from one arrangement. Renaming every near-identical frame as a new session defeats the validation split. A useful session differs in arrangement, position or conditions, not merely filename.
Numbered balls: this profile keeps cue + 1–15 labels and data separate. The lightweight classifier uses colour and appearance, not optical character recognition. Stripes, number patches, rotation and glare make this harder. If it cannot distinguish 4 from 12 reliably, collect better images or train a detector; do not hide the ambiguity.
Adapting a numbered model: change both its class labels and its identity/count handling. A one-to-one numbered-ball assignment cannot represent seven separate reds or yellows.
07 / VALIDATION
A training score is only one part of the test.
The local model compares camera-specific colour/appearance features against labelled examples. It evaluates each capture session using the other sessions, and reports correct and unknown crops. This prevents same-session examples leaking into that evaluation. It does not measure whether the full-frame detector found every ball.
- Make an entirely new arrangement after training, without saving it to the training set.
- Capture and straighten it. Open Test the result.
- Set the approximate ball radius in the 1000-pixel-wide rectified image. For a ball measuring 20 pixels across, start with radius 10.
- Select Detect this frame. Compare all boxes and counts against the actual image.
- Check black-ball misses, red/yellow confusion, cue-ball glare and false detections on pockets or rails. A low-distance match is a similarity measurement, not a calibrated probability.
- Test separated balls, touching pairs, the rack, rolling balls and a player leaning over the table. This baseline deliberately skips large merged regions; the rack must not be reported as several confidently identified balls without supporting evidence.
- Use live test to capture every two seconds after each completed request. It is a setup diagnostic, not high-frame-rate shot tracking.
- Only select Use candidate locally once the observations are useful. A previous local model is retained for rollback. Test the active model via the model selector.
Keep an independent, untouched test session for final evaluation. Review a per-class confusion matrix and detection precision/recall before any scoring integration. A ball disappearing can mean occlusion, camera motion, blur or a failed detection. Confirm pots using temporal tracking and pocket evidence; never equate a single missed detection with a scored pot.
The local segmentation assumes the cloth occupies most of the calibrated image and has a reasonably consistent colour. Patterned cloth, very low contrast, severe shadows and touching clusters may require a learned detector. These limitations are visible in the test output.
08 / TAKE THE DATA FURTHER
Export a real training dataset.
Download YOLO dataset writes a ZIP with images, normalised bounding-box labels, data.yaml and a manifest. Entire sessions stay together in train or validation. Review the images before training, especially missing boxes and labels on reflections. Save a separate test session that was not used to tune the model.
dataset/ data.yaml manifest.json images/train/…jpg images/val/…jpg labels/train/…txt labels/val/…txt
Each label line is class_id centre_x centre_y width height, with geometry normalised to 0–1. Reds/yellows uses IDs 0:red, 1:yellow, 2:black, 3:cue. The numbered profile has its own mapping. Never reuse a model with a different class order.
The following is an optional external training workflow, based on the official Ultralytics training interface. It is not executed by Camera Studio. Review the framework/model licence for your intended distribution and service. Use a separate environment and record the exact package and model versions you choose.
python -m venv training-env # Activate this environment using your operating system's instructions. python -m pip install ultralytics python -m pip freeze > training-requirements.lock.txt # Run from inside the extracted dataset directory: yolo detect train model=yolo26n.pt data=data.yaml imgsz=1280 epochs=100 yolo detect val model=runs/detect/train/weights/best.pt data=data.yaml yolo export model=runs/detect/train/weights/best.pt format=onnx
These are starting settings, not a promised training time or accuracy. Choose image size and batch size to suit the available memory; a supported GPU is generally useful. The baseline in Camera Studio does not load ONNX/YOLO models yet. An inference adapter must be built and tested against the exported model before inserting it into a live stream.
What the production worker needs
Version the detector together with class order, source dimensions, camera identity, calibration and preprocessing. Run candidate inference beside the current worker, compare observations, then promote it explicitly. Output per-frame timestamp, geometry version, class, box and confidence plus unknown/occlusion status. Track objects across time before proposing game events. Keep model activation and scoring activation separate.
For general use across many venues, gather consented examples from different cameras and evaluate on entire held-out venues. A model that works on one green table is not established as universal. Local fine-tuning or per-camera colour calibration can then adapt the general detector.
09 / YOUR DISPLAY
Give CueTracker the output it can display.
- Keep the raw feed as the analysis input.
- Choose a browser-compatible playback output from your recorder or relay, commonly MJPEG over HTTP/HTTPS.
- On the actual display device, open that exact URL. It should show the image/video, not a recorder login screen.
- In 8 Ball or KellyPool, open your club's Settings → Venue Display and configure the camera source and supported authentication settings.
- Test both locally and from any intended remote display. A private IP works only where that network is reachable.
An HTTPS app can be blocked from loading an HTTP camera by browser mixed-content or local-network protections. CORS alone does not solve mixed content, authentication or network reachability. Use a supported venue-local display arrangement or an authenticated HTTPS relay with a compatible media player. Do not expose the recorder's administration UI to the internet.
Output from the new local connector
In Use & export, choose real, AI or hybrid and apply it. http://127.0.0.1:8765/table.mjpg remains the same URL while the selected view changes. This is a low-rate local diagnostic output, limited by capture and inference speed. It is not a remote public feed or a replacement for the existing hardware-accelerated Umbrel stream. An active local model is used; without one, detections stay unknown.
Send observations to your own software
The package includes forward_observations.py. With no destination configured it prints JSON locally. Optionally set DESTINATION_URL to an HTTPS endpoint you control and DESTINATION_TOKEN to its token, then run it with Python. It sends positions, labels, counts, uncertainty and a timestamp every five seconds; it does not send images or credentials. The endpoint is your own adapter, not a built-in CueTracker scoring API. Account for stale timestamps and retries in the receiving application. This is explicit outbound sharing: leave the destination unset when you want data to stay local.
An existing relay may already offer separate raw, AI and hybrid feeds plus a single URL that follows the selected view. Confirm its documented endpoints and coordinate system before integrating a new worker.
The new Camera Studio export describes its own normalised geometry. Do not overwrite the old service's config with it. An adapter must convert coordinates and preserve the old service's distinct camera-frame/playfield settings.
10 / OPTIONAL CONTROL
One URL, with a selectable view.
An MQTT-backed view selector can keep one playback URL stable while switching the rendered view. Use your relay’s documented command topic, supported view names and state feedback. This local connector offers real, AI and hybrid output selection in its own interface.
- Confirm the local stream/relay works independently of Home Assistant.
- Use the stream service's supported MQTT discovery or integration to create its view selector.
- In Home Assistant's entity UI, select a view and verify the actual playback image changes.
- Only then add any desired dashboard control or automation through the Home Assistant UI.
- Keep camera availability, table visibility and presentation mode as separate states. A camera can be connected while no table is visible; an idle feed may have no viewers.
Preserve existing camera policies tied to lighting, occupancy or table visibility. Check that a feed-health sensor reflects actual current frames rather than a retained or stale state before using it as an alarm.
Camera Studio does not need Home Assistant. Its generic local workflow is useful to users with a camera and a computer alone. MQTT discovery and automated output switching in the new connector are future integration work.
11 / LIVESTREAM.CUETRACKER.APP
Publish the setup site; keep processing beside the cameras.
The public site should provide this wizard, the tutorial and a downloadable local connector. The guide and photo-only workflow can be served as static assets. The Python connector is intentionally loopback-only and must not be exposed directly through a public tunnel.
- Review the source package and passing checks. Commit the reviewed application source to its repository's main branch, without
data/, credentials or local camera images. - Deploy a nonproduction static preview of
public/. Verify phone/desktop layout, tutorial links, offline-photo flow and local connector download instructions. Supply an actual versioned connector download before a public launch. - For production, configure a dedicated hosting service and add
livestream.cuetracker.appas its custom domain. Use the exact DNS target provided by that host and verify TLS. - Test on a clean machine. A visitor needs understandable installation instructions and must be able to export a setup without access to the owner's LAN.
- For cloud-connected inference later, design an authenticated outbound connector with per-venue credentials, revocation and isolation. Keep camera credentials on the venue machine. Do not add an unrestricted cloud URL-fetch endpoint.
- Deploy the integration worker to staging with its old-model rollback path. Verify reds/yellows and numbered profiles independently before an explicit production promotion.
No DNS change, public publication, production stream change or existing-model replacement is included in this local build. The subdomain is chosen; the hosting/DNS release remains to be performed after the reviewable build is approved.
12 / WHEN SOMETHING DOESN'T WORK
Follow the symptom to the right layer.
| Symptom | What to check |
|---|---|
| No image / timeout | Open the source from the connector computer; verify network, port, exact endpoint, camera enabled state and stream connection limits. |
| 401 or a login page | Check the viewing account and whether the endpoint uses Basic/Digest or a separate session login. Use a supported authenticated relay for session-only sources. |
| RTSP fails | Check the vendor path and credentials, codec/backend support and simultaneous connections. Try a JPEG endpoint or a recorder-generated MJPEG feed. |
| Preview works, export doesn't | Setup export requires valid corners. Dataset export needs three sessions. Training additionally needs six examples of every class across three sessions. |
| Reds turn yellow / cue | Check exposure, white balance, coloured lighting and overly loose boxes. Add correctly labelled examples from those positions and lighting conditions. |
| Black ball missing | Check shadows, pocket overlap and background contrast. Move it to several rail positions and collect examples; a better camera angle or detector may be necessary. |
| Rack is missing balls | Touching balls merge into one region in this baseline. Scatter them for training and evaluate a learned detector for clustered-ball use. |
| Only one red detected | Ensure the worker uses the colour profile and permits repeated labels. The old numbered model's one-to-one identity assignment is unsuitable. |
| High validation score, poor live result | Look for near-duplicate sessions, missing difficult conditions and detection failures. Crop classification accuracy does not measure full-frame recall. |
| Calibration drifts | Check camera movement, recorder crop, rotation, aspect ratio and zoom. Recalibrate and keep the model paired with the new geometry. |
| Remote display cannot see a private URL | Private addresses are local to a venue. Configure a suitable authenticated relay or local display arrangement; DNS alone does not relay video. |
Keep a known-good setup export and a backup of the local data/ folder. Stop the connector before copying it. Do not share that folder unintentionally: it contains your camera images.