Open-source Python toolkit

Computer vision that keeps your code visible.

CVGO simplifies repetitive OpenCV and MediaPipe setup while keeping the familiar while True flow easy to read, edit, and customize.

CVGO — Simple Computer Vision for Python

Simple code, without hiding the process.

Start with useful defaults. Keep control of frames, detections, conditions, output, and cleanup when your project grows.

Readable camera loops

Read a frame, inspect a result, make a decision, and display it yourself.

Computer vision building blocks

Face recognition and model training, hand, pose, holistic, gesture, object detection, and segmentation.

Ready for real projects

FPS, timers, alarms, Arduino serial, Telegram photos, and editable thresholds.

Headless web monitoring

Publish annotated frames to a browser while the main CLI process stays independent.

Install CVGO.

Use Python 3.9–3.12 in a dedicated virtual environment. Python 3.11 is recommended.

Quick install
python -m pip install "cvgo==1.0.1"
Windows PowerShell
py -3.11 -m venv .venv-cvgo
.venv-cvgo\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install "cvgo==1.0.1"
Optional face recognition (fresh environment)
python -m pip install "cvgo[recognition]==1.0.1"
python -m pip check
Armbian AArch64 · Python 3.11
sudo apt update
sudo apt install -y python3-venv libgl1 libglib2.0-0 v4l-utils
python3.11 -m venv .venv-cvgo
source .venv-cvgo/bin/activate
python -m ensurepip --upgrade
python -m pip install --upgrade pip setuptools wheel
python -m pip install "cvgo==1.0.1"
python -m cvgo check --camera auto
Armbian with uv
uv pip install "mediapipe==0.10.9" "cvgo==1.0.1"
Check installation and find a camera
python -m cvgo check
python -m cvgo check --camera auto
python -m cvgo check --recognition

Core CVGO uses only opencv-contrib-python. Face recognition is intentionally optional: install its constrained extra in a fresh environment, not with an unconstrained pip install deepface. It keeps NumPy 1.26.4 and selects a MediaPipe-compatible protobuf, and pins DeepFace's OpenCV distribution to the same 4.11 release. Linux AArch64 uses MediaPipe 0.10.9; desktop uses 0.10.21. PySerial is already included, and pygame is not required by Alarm. TensorFlow/DeepFace availability on AArch64 still depends on compatible wheels for the exact operating system and Python version.

Defaults that stay customizable.

Constructors work with no arguments for beginners. Optional parameters remain available when a camera or project needs different behavior.

Main defaults

Camera()
Camera 0, OpenCV CAP_ANY
FaceDetector()
Fast engine, maximum one face
FaceLandmarks()
Maximum one face
FaceRecognizer("faces")
Optional DeepFace, video mode
train_faces("faces")
Reusable checksummed embedding model
HandTracker()
Maximum two hands
PoseTracker()
One main pose, complexity 1
GestureRecognizer()
Video mode, two hands
ObjectDetector()
Video mode, 10 objects
Serial()
Automatic port, 9600 baud
WebViewer()
All interfaces, port 8000, 15 FPS
Telegram()
Environment config, 30 s cooldown
Timer()
One-second duration
Smoother()
Alpha 0.45

Python naming style

  • Classes use PascalCase: PoseTracker.
  • Methods use snake_case: put_text().
  • Constants use UPPER_CASE: BIT_DROWSY.
  • Use fps.read() for the short, readable FPS API.

MediaPipe task modes

ObjectDetector, GestureRecognizer, and FaceRecognizer support three modes. Video remains the compatible default; live keeps camera loops responsive.

mode="image"
Independent still images
mode="video"
Synchronous sequential frames
mode="live"
Latest asynchronous camera result
result_ready
First result has completed

Consistent bounding boxes

Face, hand, pose, and object boxes share one predictable API. Pose boxes ignore low-visibility landmarks by default.

box.xyxy
Left, top, right, bottom
box.center
Center pixel coordinates
box.area
Box area in pixels
box.draw()
Rectangle and optional label

Advanced parameters, without losing simplicity.

Keep the basic examples unchanged. Add only the named parameters needed by a camera, model, output, or calibrated project.

Start small, then customize.

Every omitted parameter keeps its documented default. Parameters after an asterisk are keyword-only, so their names stay visible.

Adding only the parameters you need
camera = go.Camera()
camera = go.Camera(4)
camera = go.Camera(4, width=1280, height=720, fps=30)
  • Confidence values use 0.0 to 1.0.
  • OpenCV colors use BGR, not RGB.
  • None keeps the system or library default.
  • static=True is for unrelated still images.
  • mode="live" keeps task-based camera loops responsive.
Camera, drawing, and web monitor Choose a source and publish annotated frames to desktop or headless systems.

go.Camera(source=0, *, width=None, height=None, fps=None, backend=None)

ParameterDefaultWhat it changes
source0Camera index, video path, or stream URL.
widthNoneRequested capture width in pixels.
heightNoneRequested capture height in pixels.
fpsNoneRequested camera frame rate.
backendNoneOpenCV backend; None uses CAP_ANY.

Width, height, and FPS are requests; a camera driver may choose the nearest supported value. Read camera.size after opening, or use camera.capture for raw OpenCV settings.

Display and text

API / parameterDefaultWhat it changes
show.title"CVGO"Window title.
show.delay1Keyboard polling delay in milliseconds.
show.quit_key"q"One character that closes the loop.
close.windowsTrueDestroy OpenCV GUI windows; use False for a headless check.
put_text.position(20, 35)Text origin in pixels.
put_text.color(0, 255, 0)Text color in BGR.
put_text.scale0.7OpenCV font scale.
put_text.thickness2Text stroke width.
put_text.backgroundFalseAdd a black text background.
box.draw.color(0, 255, 0)Shared bounding-box color.
box.draw.thickness2Shared bounding-box thickness.
box.draw.labelNoneOptional shared bounding-box label.

Headless web monitor

go.WebViewer(title="CVGO Web Monitor", *, host="0.0.0.0", port=8000, jpeg_quality=80, stream_fps=15.0, start=True, log_requests=False)

ParameterDefaultWhat it changes
title"CVGO Web Monitor"Browser page title.
host"0.0.0.0"Listen on all network interfaces; use 127.0.0.1 for local-only access.
port8000HTTP port; 0 selects a free port.
jpeg_quality80JPEG quality from 1 to 100.
stream_fps15.0Maximum browser stream frame rate.
startTrueStart the server during construction.
log_requestsFalsePrint HTTP requests in the terminal.

Call viewer.update(frame, status={...}) after drawing boxes and labels. It never waits for the browser. No DISPLAY, Flask, FastAPI, or Uvicorn is required; closing the browser does not stop the main CLI loop. JPEG encoding pauses when no stream viewer is connected. This monitor is read-only by default and is for a trusted local network.

Headless browser monitor
viewer = go.WebViewer(
    "CVGO Security",
    host="0.0.0.0",
    port=8000,
    jpeg_quality=75,
    stream_fps=10,
)

viewer.update(
    frame,
    status={"Security": "SAFE", "People": 0},
)

viewer.set_form(fields, *, submit_label="Kirim", description="")

An optional form can collect setup values without Flask or FastAPI. The HTTP thread validates and queues the submission; poll_form() or wait_form(timeout) returns it to the main CLI loop. Use clear_form() after setup. Closing the browser never stops capture, training, alarms, or device logic.

Optional headless setup form
viewer.set_form(
    {
        "person_name": {
            "label": "Person name",
            "value": "Ajang",
            "required": True,
            "max_length": 60,
        },
        "photo_count": {
            "label": "Number of photos",
            "type": "number",
            "value": 5,
            "min": 3,
            "max": 30,
            "step": 1,
            "required": True,
        },
    },
    submit_label="Start face training",
)

settings = viewer.wait_form()

Diagnostics

go.system_info()

go.check_camera(source=0, *, backend=None, max_index=9)

Use these from a custom support tool, or run python -m cvgo check --camera auto. Auto mode scans camera indexes 0–9, reports the first source that returns a frame, and closes without opening a GUI window. Use --max-camera-index to extend the scan.

Custom camera and GUI
camera = go.Camera(1, width=1280, height=720, fps=30)

go.put_text(
    frame,
    "Security active",
    color=(0, 255, 255),
    background=True,
)
camera.show(frame, title="Security Camera", quit_key="x")
Face detection, landmarks, and recognition Control face geometry and optional DeepFace identity matching.

go.FaceDetector(*, max_faces=1, padding=10, model=0, detection_confidence=0.5, engine="auto", refine=False, tracking_confidence=0.5)

go.FaceLandmarks(*, max_faces=1, refine=False, detection_confidence=0.5, tracking_confidence=0.5)

ParameterDefaultWhat it changes
max_faces1Maximum faces returned per frame.
padding10Extra pixels around a detector box.
model0Fast model: 0 near-range or 1 full-range.
engine"auto"Select auto, fast, or Face Mesh-compatible mesh.
refineFalseMesh only: refine eyes and lips and add iris landmarks.
detection_confidence0.5Minimum initial face detection confidence.
tracking_confidence0.5Mesh only: minimum landmark tracking confidence.

The default auto engine uses lightweight MediaPipe Face Detection. It keeps the compatible mesh engine when refine=True or a custom tracking_confidence is supplied. Every fast FaceBox also exposes confidence. raw_result remains available in both modes; detector.faces contains landmarks only in mesh mode.

Face result methods

API / parameterDefaultWhat it changes
face.box.padding10Extra pixels around landmark bounds.
face.draw.style"contours"contours, tesselation, iris, or all.
face.draw.color(0, 255, 0)Landmark and connection color.
face.draw.thickness1Connection thickness.
face.draw.radius1Landmark radius.
FaceBox.draw.color(0, 255, 0)Box and label color.
FaceBox.draw.thickness2Box and label thickness.
FaceBox.draw.label"Face"Box label; None hides it.
Custom face landmarks
detector = go.FaceDetector(
    max_faces=2,
    detection_confidence=0.5,
    model=0,
)

landmarker = go.FaceLandmarks(
    max_faces=2,
    refine=True,
    detection_confidence=0.5,
)

faces = landmarker.detect(frame)

for face in faces:
    face.draw(frame, style="tesselation", color=(255, 180, 0))
    face.box(padding=20).draw(frame, label="Tracked face")

Optional DeepFace recognition

go.FaceRecognizer(db_path, *, model_name="Facenet512", detector_backend="mediapipe", distance_metric="cosine", threshold=None, max_faces=1, mode="video", stream=None, align=True, expand_percentage=10, anti_spoofing=False, refresh_database=True)

go.FaceRecognizer.from_model(model_path, *, max_faces=1, mode="video", stream=None, threshold=None, anti_spoofing=False)

ParameterDefaultWhat it changes
db_pathRequiredFolder of reference images, such as faces/Ajang/01.jpg.
model_name"Facenet512"Recognition model; SFace is a lighter alternative.
detector_backend"mediapipe"Corrected RGB MediaPipe detector used to crop and align faces.
distance_metric"cosine"cosine, euclidean, euclidean_l2, or angular.
thresholdNoneUse the model's tuned threshold or set a custom positive value.
max_faces1Maximum identities processed per recognition frame.
mode"video"Synchronous image/video, or background live.
alignTrueAlign each detected face before embedding.
expand_percentage10Add context around each face before recognition.
anti_spoofingFalseEnable DeepFace presentation-attack checking.
refresh_databaseTrueScan reference images before the first search.

Install cvgo[recognition]==1.0.1 in a fresh virtual environment. This constrained extra keeps NumPy 1.26.4, pins both OpenCV distributions to version 4.11, and selects protobuf 3.x for MediaPipe 0.10.9 on Linux ARM64 or protobuf 4.x for MediaPipe 0.10.21 on desktop. DeepFace stays lazily imported, so normal CVGO installations do not load TensorFlow.

CVGO defaults the process-wide DeepFace MediaPipe confidence to 0.5. Dark-camera deployments may set MEDIAPIPE_MIN_DETECTION_CONFIDENCE before importing CVGO and before the first recognition call. The foreground FaceDetector uses its normal detection_confidence parameter.

Result / stateUseMeaning
match.nameDisplay labelPerson folder name, or Unknown.
match.recognizedDecisionTrue only for a known, non-spoofed identity.
match.boxGeometryShared FaceBox API.
match.confidenceQualityDeepFace confidence normalized to 0-1 when available.
match.draw(frame)GUIGreen known box or red unknown/spoof box.
match.to_dict()OutputSerializable data for logs, MQTT, or WebSocket.
result_ready / busyLive stateFirst result completed / worker currently processing.
last_errorDiagnosticsLatest background error in live mode.
refresh()DatabaseRescan after reference images change.
Non-blocking face recognition
recognizer = go.FaceRecognizer(
    "faces",
    mode="live",
    model_name="Facenet512",
    detector_backend="mediapipe",
    max_faces=2,
)
detector = go.FaceDetector(engine="fast", detection_confidence=0.5)

faces = detector.detect(frame)
matches = recognizer.recognize(frame) if faces else []

for index, face in enumerate(faces):
    label = matches[index].name if index < len(matches) else "RECOGNIZING"
    face.draw(frame, label=label)

Reusable face model training

go.train_faces(dataset_path, model_path=None, *, model_name="Facenet512", detector_backend="mediapipe", distance_metric="cosine", threshold=None, align=True, expand_percentage=10, min_images=1, strict=False, progress=None)

ParameterDefaultWhat it changes
dataset_pathRequiredFolder containing one subfolder per identity.
model_pathNoneOutput path; defaults to the dataset name with .pkl.
model_name"Facenet512"Embedding model stored for matching inference.
detector_backend"mediapipe"Corrected RGB MediaPipe detector used on each training photo.
distance_metric"cosine"Distance calculation stored in the model.
thresholdNoneUse the model-specific default or a positive override.
alignTrueAlign the detected training face.
expand_percentage10Add context around the training crop.
min_images1Minimum valid photos required to retain each identity.
strictFalseSkip invalid photos; True stops on the first error.
progressNoneOptional callback receiving (done, total, image_path).
Train and load face_model.pkl
result = go.train_faces(
    "faces",
    "face_model.pkl",
    detector_backend="mediapipe",
    min_images=3,
)

print(result.to_dict())
print(go.face_model_info("face_model.pkl"))

recognizer = go.FaceRecognizer.from_model(
    "face_model.pkl",
    mode="live",
)

CLI Web example 22 uses the built-in browser form to choose the person name, photo count, and camera index. After submission, the CLI process owns capture and training, so closing the browser does not stop model creation.

This training step builds an embedding index; it does not fine-tune a neural network. FaceTrainingResult reports retained identities, valid images, skipped files, configuration, and output path. CVGO writes models atomically and validates their format, checksum, dimensions, and primitive-only pickle contents when loading.

Camera examples run a lightweight FaceDetector first. It immediately draws the current face box and gates the heavier background recognition, so Busy=true is displayed as RECOGNIZING instead of the misleading NO FACE state.

Reference photos and face_model.pkl embeddings are biometric data. Obtain consent, restrict access, keep them out of public repositories, and load only files controlled by your project.

Hand tracking Balance speed, accuracy, hand count, handedness, and drawing.

go.HandTracker(*, max_hands=2, model_complexity=1, detection_confidence=0.5, tracking_confidence=0.5, static=False, mirrored=False)

ParameterDefaultWhat it changes
max_hands2Maximum hands returned per frame.
model_complexity10 is lighter; 1 is more accurate.
detection_confidence0.5Minimum hand detection confidence.
tracking_confidence0.5Minimum landmark tracking confidence.
staticFalseUse True for independent still images.
mirroredFalseUse True if the input was already flipped horizontally.

Hand result methods

API / parameterDefaultWhat it changes
hand.box.padding10Extra pixels around the hand.
hand.draw.color(0, 255, 0)Connection color.
hand.draw.point_color(255, 0, 255)Landmark color.
hand.draw.thickness2Connection thickness.
hand.draw.radius2Landmark radius.
HandBox.draw.label"Hand"Box label; None hides it.
Lightweight single-hand tracking
tracker = go.HandTracker(
    max_hands=1,
    model_complexity=0,
    detection_confidence=0.7,
)

hands = tracker.detect(frame)

for hand in hands:
    hand.draw(frame, color=(255, 200, 0), point_color=(0, 0, 255))
Pose tracking Select Lite, Full, or Heavy and tune visibility-based boxes.

go.PoseTracker(*, model_complexity=1, detection_confidence=0.5, tracking_confidence=0.5, smooth=True, segmentation=False, static=False)

ParameterDefaultWhat it changes
model_complexity10 Lite, 1 Full, or 2 Heavy.
detection_confidence0.5Minimum pose detection confidence.
tracking_confidence0.5Minimum landmark tracking confidence.
smoothTrueSmooth landmarks and an optional segmentation mask.
segmentationFalseAlso produce pose.mask.
staticFalseUse True for independent still images.

Pose result methods

API / parameterDefaultWhat it changes
pose.visible.confidence0.5Required landmark visibility.
pose.box.padding20Extra pixels around the visible body.
pose.box.min_visibility0.5Ignore weaker landmarks when building the box.
pose.draw.color(0, 255, 0)Skeleton connection color.
pose.draw.point_color(255, 0, 255)Landmark color.
pose.draw.thickness2Connection thickness.
pose.draw.radius2Landmark radius.
PoseBox.draw.label"Person"Box label; None hides it.

Use model_complexity=0 for an STB or low-power board.

Lightweight pose box
tracker = go.PoseTracker(
    model_complexity=0,
    detection_confidence=0.6,
    segmentation=True,
)

pose = tracker.detect(frame)

if pose:
    pose.box(padding=30, min_visibility=0.6).draw(
        frame,
        label="Person",
    )
Holistic tracking Configure the combined face, pose, hands, and segmentation pipeline.

go.HolisticTracker(*, model_complexity=1, detection_confidence=0.5, tracking_confidence=0.5, smooth=True, refine_face=False, segmentation=False, static=False)

ParameterDefaultWhat it changes
model_complexity1Pose model complexity: 0, 1, or 2.
detection_confidence0.5Minimum initial detection confidence.
tracking_confidence0.5Minimum landmark tracking confidence.
smoothTrueSmooth landmarks and an optional mask.
refine_faceFalseRefine landmarks around the eyes and lips.
segmentationFalseAlso produce result.mask.
staticFalseUse True for independent still images.

result.draw(frame, face=True, pose=True, hands=True) can show or hide each landmark group independently. All three Boolean parameters default to True.

Choose holistic result parts
tracker = go.HolisticTracker(
    model_complexity=0,
    refine_face=True,
)
result = tracker.detect(frame)

if result:
    result.draw(frame, face=False, pose=True, hands=True)
Object detection Filter labels, tune confidence, select a task mode, or load a model.

go.ObjectDetector(model_path=None, *, confidence=0.5, max_objects=10, allow=None, deny=None, locale="en", mode="video", stream=None, download=True)

ParameterDefaultWhat it changes
model_pathNoneCompatible custom .tflite model path.
confidence0.5Minimum object score.
max_objects10Maximum results per frame.
allowNoneReturn only these labels, such as ["person"].
denyNoneExclude these labels.
locale"en"Preferred display-name locale in model metadata.
mode"video"image, video, or asynchronous live.
streamNoneLegacy option; new code should use mode.
downloadTrueDownload the default model when not cached.

allow and deny cannot be combined. In live mode, detect() returns the latest completed result; result_ready identifies the first completion.

Detected object drawing

ParameterDefaultWhat it changes
color(0, 255, 0)Box and label color.
thickness2Box and label thickness.
show_scoreTrueInclude confidence in the label.
Filtered live object detection
detector = go.ObjectDetector(
    confidence=0.65,
    max_objects=3,
    allow=["person", "car"],
)

objects = detector.detect(frame)

for item in objects:
    item.draw(frame, color=(0, 200, 255), show_score=False)
Gesture recognition and task models Tune gesture stages, use asynchronous results, and prepare offline models.

go.GestureRecognizer(model_path=None, *, max_hands=2, gesture_confidence=0.5, detection_confidence=0.5, presence_confidence=0.5, tracking_confidence=0.5, mirrored=False, mode="video", stream=None, download=True)

ParameterDefaultWhat it changes
model_pathNoneCompatible custom .task model path.
max_hands2Maximum hands recognized per frame.
gesture_confidence0.5Minimum score for a recognized gesture.
detection_confidence0.5Minimum hand detection confidence.
presence_confidence0.5Minimum hand presence confidence.
tracking_confidence0.5Minimum landmark tracking confidence.
mirroredFalseUse True if input was already flipped.
mode"video"image, video, or asynchronous live.
streamNoneLegacy option; new code should use mode.
downloadTrueDownload the default model when not cached.

Gesture result methods

API / parameterDefaultWhat it changes
gesture.box.padding10Extra pixels around the gesture hand.
gesture.draw.color(0, 255, 0)Connections and box color.
gesture.draw.point_color(255, 0, 255)Hand landmark color.
Custom gesture and offline model setup
model = go.download_model(
    "gesture_recognizer",
    directory="models",
    timeout=180,
)
recognizer = go.GestureRecognizer(
    model,
    max_hands=1,
    gesture_confidence=0.7,
)

Model download parameters

ParameterDefaultWhat it changes
nameRequiredobject_detection or gesture_recognizer.
directoryNoneCustom download folder.
forceFalseDownload again when a valid model exists.
timeout120.0Download timeout in seconds.

Set CVGO_MODEL_DIR to change the shared model cache. CVGO verifies each pinned model's header and SHA-256 checksum and downloads a damaged or incomplete cache file again.

Segmentation and timing Choose a segmentation model and tune masks, timers, smoothing, and FPS.
API / parameterDefaultWhat it changes
SelfieSegmenter.model10 general; 1 landscape/webcam.
foreground.threshold0.5Minimum mask value treated as foreground.
apply.background(0, 0, 0)BGR color or image matching the frame size.
apply.threshold0.5Foreground cutoff.
blur.amount35Blur kernel; an even value is raised to the next odd value.
blur.threshold0.5Foreground cutoff.
Timer.seconds1.0Time a condition must remain true.
Smoother.alpha0.45Lower is smoother; higher reacts faster.
FPS.update_every1.0Seconds between displayed FPS updates.
Custom segmentation and timing
segmenter = go.SelfieSegmenter(model=0)
timer = go.Timer(1.5)
smoother = go.Smoother(alpha=0.3)
fps = go.FPS(update_every=0.5)

result = segmenter.segment(frame)
frame = result.blur(frame, amount=51, threshold=0.6)
Output and connectivity Configure Serial, Telegram, MQTT, WebSocket, and sound.

go.Serial(port=None, *, baud=9600, timeout=1.0, reconnect_after=5.0, settle_time=2.0, newline=False, connect=True)

Serial parameterDefaultWhat it changes
portNoneAuto-detect, or use a path such as COM5 or /dev/ttyUSB0.
baud9600Baud rate; it must match the board.
timeout1.0Read timeout in seconds.
reconnect_after5.0Minimum delay between reconnect attempts.
settle_time2.0Wait after a board resets on connect; use 0 when unnecessary.
newlineFalseAppend a newline to outgoing values.
connectTrueConnect during construction.

send() waits for its result and flushes successful writes before returning, so immediate cleanup does not leave bytes buffered. send_async() uses one ordered worker so a serial reconnect or write does not hold the camera loop. Both return a boolean result; the asynchronous form wraps it in a Future. Many Arduino-compatible boards reset when the port opens, so increase settle_time for a one-shot script when needed and give the sketch a brief moment to act before closing. A real GUI should create one Serial object at startup and close it only when the window exits.

go.Telegram(token=None, chat_id=None, *, cooldown=30.0, timeout=15.0, silent=False, protect=False)

Telegram parameterDefaultWhat it changes
tokenEnvironmentBot token or CVGO_TELEGRAM_TOKEN.
chat_idEnvironmentTarget ID or CVGO_TELEGRAM_CHAT_ID.
cooldown30.0Seconds between successful sends using the same key.
timeout15.0HTTP request timeout in seconds.
silentFalseSend without notification sound.
protectFalseAsk Telegram to protect message content.

send_message() parameters

ParameterDefaultWhat it changes
textRequiredMessage text, from 1 to 4096 characters.
key"message"Independent cooldown name.
forceFalseBypass cooldown intentionally.
silentNoneUse or override the constructor setting.
protectNoneUse or override the constructor setting.
parse_modeNoneFormatting mode such as HTML.

send_photo() parameters

ParameterDefaultWhat it changes
photoRequiredOpenCV frame, image bytes, or image path.
caption""Caption up to 1024 characters.
key"photo"Independent cooldown name.
forceFalseBypass cooldown intentionally.
filenameNoneOptional uploaded filename.
quality85JPEG quality from 1 to 100 for OpenCV frames.
silentNoneUse or override the constructor setting.
protectNoneUse or override the constructor setting.
parse_modeNoneCaption formatting mode.

send_message_async() and send_photo_async() accept the same parameters and use one background queue. Camera frames are copied before queueing. Call telegram.close() after the loop; its wait=True default finishes queued sends.

go.MqttClient(host="localhost", port=1883, *, client_id="", username=None, password=None, keepalive=60, reconnect_after=5.0, connect_timeout=5.0, tls=False, connect=False)

MQTT parameterDefaultWhat it changes
host"localhost"Broker hostname or IP address.
port1883Broker TCP port; commonly 8883 with TLS.
client_id""Stable MQTT client identifier.
username, passwordNoneOptional broker credentials.
keepalive60Keepalive interval in seconds.
reconnect_after5.0Minimum delay between reconnect attempts.
connect_timeout5.0Maximum wait for the broker's CONNACK.
tlsFalseEnable the default TLS configuration.
connectFalseConnect during construction.

go.WebSocketClient(url, *, timeout=10.0, reconnect_after=5.0, connect=False)

WebSocket parameterDefaultWhat it changes
urlRequiredws:// or wss:// server URL.
timeout10.0Connect, send, and receive timeout in seconds.
reconnect_after5.0Minimum delay between reconnect attempts.
connectFalseConnect during construction.

MQTT publishes and subscriptions use JSON for structured values; WebSocket send() and receive() do the same. Both clients support connect(), reconnect(), close(), context managers, and asynchronous sending. MQTT waits for CONNACK and restores subscriptions after reconnect. Failed WebSocket objects are closed before a replacement is created. Install these optional integrations with pip install "cvgo[robotics]".

MQTT robot state
import cvgo as go


with go.MqttClient(host="localhost", client_id="cvgo-camera") as mqtt:
    mqtt.publish("robot/camera/state", {"person_detected": True})
WebSocket robot event
import cvgo as go


with go.WebSocketClient("ws://localhost:8080") as websocket:
    websocket.send({"type": "person_detected", "confidence": 0.94})
    message = websocket.receive()

go.Alarm(*, frequency=1500, duration=180, repeat=3, cooldown=0.8)

Alarm parameterDefaultWhat it changes
frequency1500Beep frequency in Hz on Windows.
duration180Length of each beep in milliseconds.
repeat3Number of beeps per trigger.
cooldown0.8Minimum seconds between alarm starts.
Custom output settings
arduino = go.Serial(
    "/dev/ttyUSB0",
    baud=115200,
    newline=True,
)
telegram = go.Telegram(cooldown=60, silent=True)
alarm = go.Alarm(frequency=1800, repeat=2, cooldown=1.0)

arduino.send_async("1")
telegram.send_message_async("CVGO active")
Advanced Driver Monitor Calibrate eye, head, missing-face, serial, and event behavior.

Driver Monitor groups related thresholds into small configuration objects, so each part can be calibrated without a crowded constructor.

EyeConfig parameterDefaultWhat it changes
closed_threshold0.20Enter closed-eye state below this EAR.
open_threshold0.24Leave closed-eye state above this EAR.
alert_after1.5Closed-eye seconds before drowsiness is active.
smoothing0.45EAR smoother alpha.
HeadConfig parameterDefaultWhat it changes
yaw_normal0.50Calibrated straight-ahead yaw ratio.
turn_threshold0.12Enter looking-away state beyond this offset.
turn_release0.07Leave looking-away state below this offset.
turn_alert_after0.7Looking-away seconds before an alert.
pitch_normal0.50Calibrated upright pitch ratio.
down_threshold0.055Enter head-down state beyond this offset.
down_release0.030Leave head-down state below this offset.
down_alert_after0.7Head-down seconds before an alert.
Other parameterDefaultWhat it changes
FaceConfig.missing_alert_after2.0Missing-face seconds before an alert.
DriverMonitor.camera0Camera source or configured Camera.
DriverMonitor.serialFalseTrue for auto serial or a Serial object.
DriverMonitor.soundFalseEnable its built-in alarm.
serial_repeat_after0.5Seconds between repeated mask transmissions.
Calibrated Driver Monitor
eyes = go.EyeConfig(
    closed_threshold=0.22,
    open_threshold=0.26,
    alert_after=1.2,
)
head = go.HeadConfig(turn_threshold=0.10, down_threshold=0.05)
face = go.FaceConfig(missing_alert_after=3.0)

monitor = go.DriverMonitor(
    camera=go.Camera(4, width=640, height=480),
    serial=go.Serial("/dev/ttyUSB0", baud=115200),
    sound=True,
    eyes=eyes,
    head=head,
    face=face,
)
monitor.serial_repeat_after = 1.0

Events: drowsy, looking_away, looking_left, looking_right, head_down, face_missing, and normal. The result keeps measurements, durations, landmarks, alert flags, FPS, mask, and mask_hex available for custom logic.

Driver Monitor display and shortcut mode

Method parameterDefaultWhat it changes
show.title"CVGO Driver Monitor"GUI window title.
show.draw_landmarksTrueDraw face landmarks before showing.
show.landmark_style"contours"Face drawing style.
show.quit_key"q"GUI quit key.
run.showFalseEnable a GUI window in shortcut mode.
run.draw_landmarksFalseDraw landmarks in shortcut mode.
run.print_statusTruePrint status twice per second.
run.quit_key"q"GUI quit key when show=True.

Raw access remains available

AccessRaw or editable valueUse
camera.capturecv2.VideoCaptureAdditional OpenCV camera properties.
tracker.raw_resultMediaPipe resultFeatures not wrapped by CVGO.
face.raw / hand.raw / pose.rawMediaPipe landmarksDirect MediaPipe interoperability.
item.raw / gesture.rawTask result or categoryModel-specific metadata.
face.points / hand.points / pose.pointsCVGO pointsReadable custom calculations.

69 complete, copy-ready examples.

Every topic includes complete GUI, CLI, and CLI Web programs, including imports, loops, annotated output, and cleanup.

CLI Web runs without DISPLAY. Closing the browser does not stop detection; use Ctrl+C in the terminal.

01 Camera and GUIOpen a camera and publish its frames in GUI, terminal, or browser mode.
examples/01_camera.py
"""Example 1: open and display the camera."""

import cvgo as go


camera = go.Camera()

while True:
    frame = camera.read()

    if frame is None:
        break

    if not camera.show(frame):
        break

camera.close()
02 Face DetectionDetect faces, draw the boxes, or print the live face count.
examples/02_face_detection.py
"""Example 2: detect faces."""

import cvgo as go


camera = go.Camera()
detector = go.FaceDetector()

while True:
    frame = camera.read()

    if frame is None:
        break

    faces = detector.detect(frame)

    for face in faces:
        face.draw(frame)

    if not camera.show(frame):
        break

camera.close()
detector.close()
03 Face LandmarksRead and draw detailed face landmarks for custom measurements.
examples/03_face_landmarks.py
"""Example 3: display face landmarks."""

import cvgo as go


camera = go.Camera()
landmarker = go.FaceLandmarks()

while True:
    frame = camera.read()

    if frame is None:
        break

    faces = landmarker.detect(frame)

    for face in faces:
        face.draw(frame)

    if not camera.show(frame):
        break

camera.close()
landmarker.close()
04 Face MetricsCalculate EAR, yaw, and pitch while keeping thresholds editable.
examples/04_face_metrics.py
"""Example 4: read EAR, yaw, and pitch."""

import cvgo as go


camera = go.Camera()
landmarker = go.FaceLandmarks()

while True:
    frame = camera.read()

    if frame is None:
        break

    faces = landmarker.detect(frame)

    if faces:
        face = faces[0]

        ear = go.eye_ratio(face)
        yaw = go.yaw_ratio(face)
        pitch = go.pitch_ratio(face)

        go.put_text(frame, f"EAR: {ear:.3f}")
        go.put_text(frame, f"Yaw: {yaw:.3f}", (20, 70))
        go.put_text(frame, f"Pitch: {pitch:.3f}", (20, 105))

        face.draw(frame)

    if not camera.show(frame):
        break

camera.close()
landmarker.close()
05 Serial ArduinoConnect automatically and send values to an Arduino.
examples/05_serial_arduino.py
"""Example 5: send one value to Arduino and report the result."""

import time
import cvgo as go


arduino = go.Serial(
    settle_time=4.0,
)

try:
    if not arduino.connected:
        print("Arduino: NOT CONNECTED")
    else:
        print(f"Arduino: CONNECTED | Port: {arduino.port}")
        sent = arduino.send("1")
        status = "SENT" if sent else "FAILED"
        print(f"{status}: 1")

        if sent:
            # Give the sketch time to process a one-shot command before close.
            time.sleep(0.5)
finally:
    arduino.close()
06 Face to ArduinoSend face-presence status without repeating unchanged serial data.
examples/06_face_to_arduino.py
"""Example 6: send face detection status to Arduino."""

import cvgo as go


camera = go.Camera()
detector = go.FaceDetector()
arduino = go.Serial()
last_status = None

while True:
    frame = camera.read()

    if frame is None:
        break

    faces = detector.detect(frame)
    status = 1 if faces else 0

    if status != last_status:
        if arduino.send(status):
            last_status = status

    for face in faces:
        face.draw(frame)

    if not camera.show(frame):
        break

camera.close()
detector.close()
arduino.close()
07 Drowsiness DetectionCombine EAR, smoothing, a timer, and an alarm.
examples/07_drowsiness.py
"""Example 7: detect drowsiness based on EAR and duration."""

import cvgo as go


EAR_THRESHOLD = 0.20

camera = go.Camera()
landmarker = go.FaceLandmarks()
eye_timer = go.Timer(1.5)
ear_smoother = go.Smoother()
alarm = go.Alarm()

while True:
    frame = camera.read()

    if frame is None:
        break

    faces = landmarker.detect(frame)
    drowsy = False

    if faces:
        face = faces[0]
        ear = ear_smoother.update(go.eye_ratio(face))
        eyes_closed = ear < EAR_THRESHOLD
        drowsy = eye_timer.check(eyes_closed)

        status = "DROWSY" if drowsy else "NORMAL"
        color = (0, 0, 255) if drowsy else (0, 255, 0)

        go.put_text(frame, f"Status: {status}", color=color)
        go.put_text(frame, f"EAR: {ear:.3f}", (20, 70))
        face.draw(frame, color=color)
    else:
        eye_timer.reset()
        ear_smoother.reset()

    alarm.trigger(drowsy)

    if not camera.show(frame):
        break

camera.close()
landmarker.close()
08 Driver MonitorMonitor eyes, head direction, serial output, alarms, and FPS.
examples/08_driver_monitor.py
"""Example 8: final driver monitor project that is still easy to study."""

import cvgo as go


# Detection thresholds
EAR_THRESHOLD = 0.20
YAW_NORMAL = 0.50
YAW_LIMIT = 0.12
PITCH_NORMAL = 0.50
PITCH_LIMIT = 0.055

# Components
camera = go.Camera()
landmarker = go.FaceLandmarks()
arduino = go.Serial()
alarm = go.Alarm()
fps_counter = go.FPS()

# Condition timers
eye_timer = go.Timer(1.5)
turn_timer = go.Timer(0.7)
down_timer = go.Timer(0.7)
missing_timer = go.Timer(2.0)

# Eye value smoother
ear_smoother = go.Smoother()

# Last serial status
last_mask = None

while True:
    frame = camera.read()

    if frame is None:
        break

    faces = landmarker.detect(frame)
    fps = fps_counter.read()

    ear = None
    yaw = None
    pitch = None

    drowsy = False
    looking_away = False
    head_down = False
    face_missing = False

    if faces:
        face = faces[0]

        ear = ear_smoother.update(go.eye_ratio(face))
        yaw = go.yaw_ratio(face)
        pitch = go.pitch_ratio(face)

        eyes_closed = ear < EAR_THRESHOLD
        turn_condition = abs(yaw - YAW_NORMAL) > YAW_LIMIT
        down_condition = pitch - PITCH_NORMAL > PITCH_LIMIT

        drowsy = eye_timer.check(eyes_closed)
        looking_away = turn_timer.check(turn_condition)
        head_down = down_timer.check(down_condition)

        missing_timer.reset()
        face.draw(frame)
    else:
        eye_timer.reset()
        turn_timer.reset()
        down_timer.reset()
        ear_smoother.reset()

        face_missing = missing_timer.check(True)

    mask = 0

    if drowsy:
        mask |= go.BIT_DROWSY

    if looking_away:
        mask |= go.BIT_LOOKING_AWAY

    if head_down:
        mask |= go.BIT_HEAD_DOWN

    if face_missing:
        mask |= go.BIT_FACE_MISSING

    if mask != last_mask:
        if arduino.send(f"{mask:X}"):
            last_mask = mask

    alerts = []

    if drowsy:
        alerts.append("DROWSY")

    if looking_away:
        alerts.append("LOOKING_AWAY")

    if head_down:
        alerts.append("HEAD_DOWN")

    if face_missing:
        alerts.append("FACE_MISSING")

    status = " | ".join(alerts) if alerts else "NORMAL"
    color = (0, 0, 255) if alerts else (0, 255, 0)

    ear_text = "-" if ear is None else f"{ear:.3f}"
    yaw_text = "-" if yaw is None else f"{yaw:.3f}"
    pitch_text = "-" if pitch is None else f"{pitch:.3f}"

    go.put_text(
        frame,
        f"Status: {status}",
        (20, 35),
        color=color,
        background=True,
    )
    go.put_text(frame, f"EAR: {ear_text}", (20, 70))
    go.put_text(frame, f"Yaw: {yaw_text}", (20, 105))
    go.put_text(frame, f"Pitch: {pitch_text}", (20, 140))
    go.put_text(frame, f"FPS: {fps:.1f} | Mask: {mask:X}", (20, 175))

    alarm.trigger(mask != 0)

    if not camera.show(frame, title="CVGO Driver Monitor"):
        break

camera.close()
landmarker.close()
arduino.close()
09 Hand TrackingTrack 21 landmarks, handedness, and confidence for each hand.
examples/09_hand_tracking.py
"""Example 9: hand tracking, hand labels, and FPS."""

import cvgo as go


camera = go.Camera()
tracker = go.HandTracker()
fps = go.FPS()

while True:
    frame = camera.read()

    if frame is None:
        break

    hands = tracker.detect(frame)

    for hand in hands:
        hand.draw(frame)
        label = f"{hand.handedness}: {hand.confidence:.2f}"
        hand.box().draw(frame, label=label)

    go.put_text(frame, f"Hands: {len(hands)}")
    go.put_text(frame, f"FPS: {fps.read():.1f}", (20, 70))

    if not camera.show(frame, title="CVGO Hand Tracking"):
        break

camera.close()
tracker.close()
10 Pose TrackingDetect one main body pose with 33 landmarks.
examples/10_pose_tracking.py
"""Example 10: body pose tracking."""

import cvgo as go


camera = go.Camera()
tracker = go.PoseTracker()
fps = go.FPS()

while True:
    frame = camera.read()

    if frame is None:
        break

    pose = tracker.detect(frame)
    person_detected = pose is not None

    if pose:
        pose.draw(frame)

    status = "POSE DETECTED" if person_detected else "NO POSE"
    color = (0, 255, 0) if person_detected else (0, 0, 255)

    go.put_text(frame, status, color=color)
    go.put_text(frame, f"FPS: {fps.read():.1f}", (20, 70))

    if not camera.show(frame, title="CVGO Pose Tracking"):
        break

camera.close()
tracker.close()
11 Pose SecurityUse Pose Lite as a lightweight person box without a skeleton.
examples/11_security_pose.py
"""Example 11: lightweight person security with a pose bounding box."""

import cvgo as go


camera = go.Camera()
tracker = go.PoseTracker(model_complexity=0)
presence_timer = go.Timer(0.5)
alarm = go.Alarm()
fps = go.FPS()

while True:
    frame = camera.read()

    if frame is None:
        break

    pose = tracker.detect(frame)
    person_detected = pose is not None
    alert = presence_timer.check(person_detected)

    status = "ALERT" if alert else "SAFE"
    color = (0, 0, 255) if alert else (0, 255, 0)

    if pose:
        pose.box(
            padding=30,
        ).draw(
            frame,
            color=color,
            label="Person",
        )

    go.put_text(frame, f"Status: {status}", color=color)
    go.put_text(frame, f"FPS: {fps.read():.1f}", (20, 70))
    alarm.trigger(alert)

    if not camera.show(frame, title="CVGO Person Security Lite"):
        break

camera.close()
tracker.close()
12 Object DetectionDetect common objects and inspect labels, scores, and boxes.
examples/12_object_detection.py
"""Example 12: general object detection."""

import cvgo as go


camera = go.Camera()
detector = go.ObjectDetector()
fps = go.FPS()

while True:
    frame = camera.read()

    if frame is None:
        break

    objects = detector.detect(frame)

    for item in objects:
        item.draw(frame)

    go.put_text(frame, f"Objects: {len(objects)}")
    go.put_text(frame, f"Loop FPS: {fps.read():.1f}", (20, 70))

    if not camera.show(frame, title="CVGO Object Detection"):
        break

camera.close()
detector.close()
13 Person SecurityFilter object detection to people for a security monitor.
examples/13_person_security.py
"""Example 13: detect multiple people for simple security."""

import cvgo as go


camera = go.Camera()
detector = go.ObjectDetector(allow=["person"])
presence_timer = go.Timer(0.5)
alarm = go.Alarm()

while True:
    frame = camera.read()

    if frame is None:
        break

    people = detector.detect(frame)
    alert = presence_timer.check(bool(people))

    for person in people:
        person.draw(frame, color=(0, 0, 255))

    status = "ALERT" if alert else "SAFE"
    color = (0, 0, 255) if alert else (0, 255, 0)

    go.put_text(frame, f"Status: {status}", color=color)
    go.put_text(frame, f"Count: {len(people)}", (20, 70))
    alarm.trigger(alert)

    if not camera.show(frame, title="CVGO Person Security"):
        break

camera.close()
detector.close()
14 Gesture RecognitionRecognize supported hand gestures and their confidence.
examples/14_gesture_recognition.py
"""Example 14: hand gesture recognition."""

import cvgo as go


camera = go.Camera()
recognizer = go.GestureRecognizer()

while True:
    frame = camera.read()

    if frame is None:
        break

    gestures = recognizer.detect(frame)

    for gesture in gestures:
        gesture.draw(frame)

    if not camera.show(frame, title="CVGO Gesture Recognition"):
        break

camera.close()
recognizer.close()
15 Holistic TrackingTrack face, pose, and both hands through one result.
examples/15_holistic_tracking.py
"""Example 15: face, pose, and hands in one pipeline."""

import cvgo as go


camera = go.Camera()
tracker = go.HolisticTracker()

while True:
    frame = camera.read()

    if frame is None:
        break

    result = tracker.detect(frame)
    result.draw(frame)

    if not camera.show(frame, title="CVGO Holistic Tracking"):
        break

camera.close()
tracker.close()
16 Selfie SegmentationSeparate a person from the background or measure coverage.
examples/16_selfie_segmentation.py
"""Example 16: blur the webcam background."""

import cvgo as go


camera = go.Camera()
segmenter = go.SelfieSegmenter()

while True:
    frame = camera.read()

    if frame is None:
        break

    result = segmenter.segment(frame)
    frame = result.blur(frame)

    if not camera.show(frame, title="CVGO Selfie Segmentation"):
        break

camera.close()
segmenter.close()
17 Telegram SecuritySend a camera photo to Telegram when a person is detected.
examples/17_telegram_security.py
"""Example 17: send a Telegram photo when a pose is detected."""

import cvgo as go


camera = go.Camera()
tracker = go.PoseTracker(model_complexity=0)
telegram = go.Telegram()
presence_timer = go.Timer(0.5)
notified = False
pending = None

while True:
    frame = camera.read()

    if frame is None:
        break

    pose = tracker.detect(frame)
    alert = presence_timer.check(pose is not None)

    if pending is not None and pending.done():
        if not pending.result():
            print(f"Telegram: {telegram.last_error}")
        pending = None

    if pose:
        pose.box(padding=30).draw(frame, color=(0, 0, 255), label="Person")

    status = "PERSON DETECTED" if alert else "SAFE"
    color = (0, 0, 255) if alert else (0, 255, 0)
    go.put_text(frame, f"Status: {status}", color=color)

    if alert and not notified and pending is None:
        pending = telegram.send_photo_async(frame, "Warning: person detected.", key="security")

    notified = alert

    if not camera.show(frame, title="CVGO Telegram Security"):
        break

camera.close()
tracker.close()
telegram.close()
18 Telegram Person SecuritySend a camera photo when one or more people are detected.
examples/18_telegram_person_security.py
"""Example 18: send a Telegram photo when people are detected."""

import cvgo as go


camera = go.Camera()
detector = go.ObjectDetector(allow=["person"])
telegram = go.Telegram()
presence_timer = go.Timer(0.5)
notified = False
pending = None

while True:
    frame = camera.read()

    if frame is None:
        break

    people = detector.detect(frame)
    alert = presence_timer.check(bool(people))

    if pending is not None and pending.done():
        if not pending.result():
            print(f"Telegram: {telegram.last_error}")
        pending = None

    for person in people:
        person.draw(frame, color=(0, 0, 255))

    status = "PEOPLE DETECTED" if alert else "SAFE"
    color = (0, 0, 255) if alert else (0, 255, 0)
    go.put_text(frame, f"Status: {status}", color=color)
    go.put_text(frame, f"Count: {len(people)}", (20, 70))

    if alert and not notified and pending is None:
        pending = telegram.send_photo_async(
            frame,
            f"Warning: {len(people)} person(s) detected.",
            key="person-security",
        )

    notified = alert

    if not camera.show(frame, title="CVGO Telegram Person Security"):
        break

camera.close()
detector.close()
telegram.close()
19 MQTT Robot StatusPublish pose status as JSON to an MQTT robot topic.
examples/19_mqtt_robot.py
"""Example 19: publish pose status to an MQTT robot topic."""

import cvgo as go


camera = go.Camera()
tracker = go.PoseTracker(model_complexity=0)
mqtt = go.MqttClient(host="localhost", client_id="cvgo-camera", connect=True)
last_detected = None
pending = None

try:
    while True:
        frame = camera.read()

        if frame is None:
            break

        pose = tracker.detect(frame)
        detected = pose is not None

        if pending is not None and pending.done():
            if not pending.result():
                print(f"MQTT: {mqtt.last_error}")
            pending = None

        if detected != last_detected and pending is None:
            pending = mqtt.publish_async(
                "robot/camera/pose",
                {"person_detected": detected},
            )
            last_detected = detected

        if pose:
            pose.box(padding=30).draw(frame, label="Person")
        go.put_text(frame, f"Person detected: {detected}")

        if not camera.show(frame, title="CVGO MQTT Robot"):
            break
finally:
    camera.close()
    tracker.close()
    mqtt.close()
20 WebSocket Robot StatusSend pose status as JSON to a WebSocket robot service.
examples/20_websocket_robot.py
"""Example 20: send pose status to a WebSocket robot service."""

import cvgo as go


camera = go.Camera()
tracker = go.PoseTracker(model_complexity=0)
websocket = go.WebSocketClient("ws://localhost:8080", connect=True)
last_detected = None
pending = None

try:
    while True:
        frame = camera.read()

        if frame is None:
            break

        pose = tracker.detect(frame)
        detected = pose is not None

        if pending is not None and pending.done():
            if not pending.result():
                print(f"WebSocket: {websocket.last_error}")
            pending = None

        if detected != last_detected and pending is None:
            pending = websocket.send_async({"person_detected": detected})
            last_detected = detected

        if pose:
            pose.box(padding=30).draw(frame, label="Person")
        go.put_text(frame, f"Person detected: {detected}")

        if not camera.show(frame, title="CVGO WebSocket Robot"):
            break
finally:
    camera.close()
    tracker.close()
    websocket.close()
21 Face RecognitionRecognize identities with optional DeepFace without blocking the camera.
examples/21_face_recognition.py
"""Example 21: recognize known faces without blocking the camera GUI."""

import cvgo as go


camera = go.Camera()
detector = go.FaceDetector(
    engine="fast",
    model=0,
    detection_confidence=0.5,
)
recognizer = go.FaceRecognizer(
    "faces",
    mode="live",
    model_name="Facenet512",
    detector_backend="mediapipe",
)
fps = go.FPS()

try:
    while True:
        frame = camera.read()

        if frame is None:
            break

        faces = detector.detect(frame)
        matches = recognizer.recognize(frame) if faces else []

        for index, face in enumerate(faces):
            if index < len(matches):
                match = matches[index]
                label = match.name
                if match.recognized and match.confidence is not None:
                    label = f"{label} {match.confidence:.0%}"
                color = (0, 255, 0) if match.recognized else (0, 0, 255)
            else:
                label = "Recognizing..." if recognizer.busy else "Face"
                color = (0, 200, 255)
            face.draw(frame, label=label, color=color)

        if recognizer.last_error:
            status = f"Error: {recognizer.last_error}"
        elif not faces:
            status = "No face"
        elif not recognizer.result_ready:
            status = "Loading face recognition..."
        elif recognizer.busy and not matches:
            status = "Recognizing..."
        elif matches:
            status = ", ".join(match.name for match in matches)
        else:
            status = "Face detected"

        go.put_text(frame, status)
        go.put_text(
            frame,
            f"Loop FPS: {fps.read():.1f}",
            (20, 70),
        )

        if not camera.show(
            frame,
            title="CVGO Face Recognition",
        ):
            break
finally:
    camera.close()
    detector.close()
    recognizer.close()
22 Face Model TrainingCapture labeled photos and build a reusable CVGO face model.
examples/22_face_training.py
"""Example 22: capture a face dataset and train a reusable model."""

from pathlib import Path

import cv2
import cvgo as go


PERSON_NAME = "Ajang"
PHOTO_COUNT = 5
MIN_IMAGES = 3
DATASET_PATH = Path("faces")
MODEL_PATH = Path("face_model.pkl")

person_path = DATASET_PATH / PERSON_NAME
person_path.mkdir(parents=True, exist_ok=True)
next_number = len(list(person_path.glob("*.jpg"))) + 1

camera = go.Camera()
detector = go.FaceDetector(
    engine="fast",
    model=0,
    detection_confidence=0.5,
)
capture_timer = go.Timer(0.7)
captured = 0

try:
    while captured < PHOTO_COUNT:
        frame = camera.read()

        if frame is None:
            break

        clean_frame = frame.copy()
        faces = detector.detect(frame)

        if capture_timer.check(len(faces) == 1):
            while (person_path / f"{next_number:03d}.jpg").exists():
                next_number += 1
            photo_path = person_path / f"{next_number:03d}.jpg"
            if not cv2.imwrite(str(photo_path), clean_frame):
                raise OSError(f"Failed to save {photo_path}")
            captured += 1
            next_number += 1
            capture_timer.reset()

        for face in faces:
            face.draw(frame, label=PERSON_NAME)

        go.put_text(frame, f"Hold one face still: {captured}/{PHOTO_COUNT}")

        if not camera.show(frame, title="CVGO Face Training"):
            break
finally:
    camera.close()
    detector.close()

if captured >= MIN_IMAGES:
    print("Training face model...")
    result = go.train_faces(
        DATASET_PATH,
        MODEL_PATH,
        detector_backend="mediapipe",
        min_images=MIN_IMAGES,
        progress=lambda done, total, image_path: print(
            f"Embedding: {done}/{total} | {image_path.name}"
        ),
    )
    print(
        f"MODEL READY: {result.model_path} | "
        f"{result.identity_count} people | {result.images} photos | "
        f"detector: {result.detector_backend}"
    )
    for message in result.skipped:
        print(f"SKIPPED: {message}")
else:
    print(f"Training canceled: capture at least {MIN_IMAGES} photos.")
23 Face Recognition ModelLoad a trained face model in GUI, terminal, or headless web mode.
examples/23_face_model.py
"""Example 23: recognize faces with a trained CVGO model."""

import cvgo as go


camera = go.Camera()
detector = go.FaceDetector(
    engine="fast",
    model=0,
    detection_confidence=0.5,
)
recognizer = go.FaceRecognizer.from_model(
    "face_model.pkl",
    mode="live",
)
fps = go.FPS()

try:
    while True:
        frame = camera.read()

        if frame is None:
            break

        faces = detector.detect(frame)
        matches = recognizer.recognize(frame) if faces else []

        for index, face in enumerate(faces):
            if index < len(matches):
                match = matches[index]
                label = match.name
                if match.recognized and match.confidence is not None:
                    label = f"{label} {match.confidence:.0%}"
                color = (0, 255, 0) if match.recognized else (0, 0, 255)
            else:
                label = "Recognizing..." if recognizer.busy else "Face"
                color = (0, 200, 255)
            face.draw(frame, label=label, color=color)

        if recognizer.last_error:
            status = f"Error: {recognizer.last_error}"
        elif not faces:
            status = "No face"
        elif not recognizer.result_ready:
            status = "Loading face model..."
        elif recognizer.busy and not matches:
            status = "Recognizing..."
        elif matches:
            status = ", ".join(match.name for match in matches)
        else:
            status = "Face detected"

        go.put_text(frame, f"Model: {status}")
        go.put_text(frame, f"Loop FPS: {fps.read():.1f}", (20, 70))

        if not camera.show(frame, title="CVGO Face Model"):
            break
finally:
    camera.close()
    detector.close()
    recognizer.close()

Built to be studied and changed.

CVGO is released under the MIT License. The visible camera loops and decisions can grow into learning projects, security tools, or a complete driver-monitoring final project. The bundle also includes README.md, ARMBIAN.md, FACE_RECOGNITION.md, PUBLISHING.md, and CHANGELOG.md; their installation and face-recognition essentials are mirrored on this page.

Back to top