The CameraCapture class is module M1 of the Anaquel Inteligente 3B pipeline. It wraps OpenCV’sDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/elzackarias/Hackaton3B-Reto1/llms.txt
Use this file to discover all available pages before exploring further.
VideoCapture and abstracts two camera source types: USB cameras (identified by an integer index such as 0) and IP/NVR cameras via RTSP URLs. The module spawns a dedicated background reader thread that continuously flushes the RTSP buffer, ensuring the detection engine always receives the most recent frame rather than a stale one queued in the codec’s internal buffer.
FrameData
Every frame produced byget_frames() is wrapped in a FrameData dataclass:
BGR image array with shape
(H, W, 3) and dtype uint8.time.time() captured immediately after the frame was read.Monotonically increasing frame counter, initialised to
0 when the
CameraCapture object is constructed. It is not reset on reconnection.(width, height) of the raw frame as reported by OpenCV.Constructor
Camera source. Pass an
int (e.g. 0) for a USB/built-in camera, or
a string RTSP URL (e.g.
"rtsp://admin:admin1234@192.168.1.10:554/cam/realmonitor?channel=1&subtype=0")
for a network camera.The built-in default URL (DEFAULT_RTSP) points to the development NVR
recorded in assets/cadenadeconexion.md.Core Methods
start() -> bool
Opens the video source and starts the background reader thread. Returns True on success, False if the source cannot be opened after exhausting all reconnection attempts.
Reconnection behaviour on RTSP failure:
Initial open attempt
Calls
cv2.VideoCapture(source, cv2.CAP_FFMPEG) with CAP_PROP_BUFFERSIZE=1 and 5-second open/read timeouts.Retry loop (up to MAX_RECONNECT=5)
If the initial open fails, retries up to 5 times with a 2-second delay between attempts.
stop()
Signals the background reader thread to terminate, waits up to 3 seconds for it to join, then releases the VideoCapture object. Safe to call multiple times.
get_frames()
A generator that yields FrameData objects continuously. Each iteration waits up to 2 seconds for the background reader to produce a new frame. If no frame arrives within the timeout the generator checks the connection status and triggers _reconnect() if needed.
Only the most recently captured frame is yielded — older buffered frames
are discarded by the reader thread. This prevents the detection pipeline from
falling behind on a fast RTSP feed.
stream_loop(det_engine, overlay, callback, max_fps=5, stream_width=640, detect_every=2)
The high-level loop called from main.py. It combines frame acquisition, optional resizing, detection, overlay rendering, and callback dispatch in a single blocking call designed to be run inside a thread executor.
Detection engine instance. Pass
None to stream frames without running
inference (e.g. for a raw video preview).VideoOverlay instance used to draw bounding boxes and encode the frame
to base-64 JPEG. Pass None to skip overlay rendering.Called after each frame with the signature
callback(frame_b64: str | None, detection_result: DetectionResult | None, events: list[DetectionEvent]) -> bool.
Return True to stop the loop.Maximum processed frames per second. Frames that arrive faster are skipped
to stay within the budget.
Frames wider than this value are down-scaled (maintaining aspect ratio)
before detection and encoding, reducing CPU load and payload size.
Run YOLO inference only every
N frames. On alternating frames the
previous DetectionResult is reused for the overlay, halving inference
load at minimal accuracy cost.Auto-Reconnect with Exponential Backoff
Inmain.py, stream_loop is executed inside a dedicated thread via loop.run_in_executor(None, _run_camera_loop). The wrapper function _run_camera_loop implements infinite retries with exponential backoff:
min(base_delay × retry_count, 30):
| Retry | Delay |
|---|---|
| 1 | 3 s |
| 2 | 6 s |
| 3 | 9 s |
| 10+ | 30 s (capped) |
retry_count resets to 0 so the next disconnection starts the backoff from the beginning.
Configuration
The camera source is determined by thesource argument passed to CameraCapture(). In main.py the constructor is called without arguments, which means it uses the DEFAULT_RTSP constant defined at the top of camera_capture.py:
USB Camera
Pass an integer index:Use
0 for the first USB/built-in camera, 1 for the second, and so on.RTSP Stream
Pass the full RTSP URL:Edit
DEFAULT_RTSP in camera_capture.py to update the default.