Skip to content

Enobarbus

Enobarbus — Claude Context

Computer vision and tracking package. Depends only on Horatio. Provides camera management, all trackers (Vision + ARKit), processing utilities, and diagnostic overlay views. All tracking code is #if os(iOS).

Architecture

Session Layer

Two mutually exclusive session types — ARSession and AVCaptureSession cannot run simultaneously on iOS.

File Role
TrackingSession.swift TrackingSession protocol + TrackingBackend enum (.vision / .arkit)
VisionTrackingSession.swift Wraps CameraManager → conforms to TrackingSession
ARTrackingSession.swift Owns ARSession, two configs: .face (front/TrueDepth) or .body (rear/A12+). Distributes ARFrame.capturedImage to pixel buffer handlers + routes typed anchor callbacks

Camera

File Role
CameraManager.swift Owns AVCaptureSession, distributes frames via FrameHandler / PositionAwareFrameHandler / DepthHandler. Front/rear switching, orientation tracking, depth output config

Vision Trackers

All follow the same pattern: processFrame(_:orientation:)@Published [SourceType] on main thread. Also have processPixelBuffer(_:orientation:cameraPosition:) for ARKit interop.

File Sources Count
HandTracker.swift [HandInputSource] Up to 4 hands, chirality, finger count, identity resolution
BodyTracker.swift [BodyJointInputSource] 19 joints via VNDetectHumanBodyPoseRequest
FaceTracker.swift [FaceLandmarkInputSource] 11 sources: 3 positions + 5 expressions + 3 rotations
DepthTracker.swift [DepthEnrichedInputSource] TrueDepth / LiDAR depth enrichment

ARKit Trackers

Process ARKit anchors instead of Vision requests. Produce the same base source IDs as their Vision counterparts for compatibility.

File Sources Notes
ARKitFaceTracker.swift [FaceLandmarkInputSource] 11 base IDs (same as Vision) + 52 face.arkit.{blendShape} extended sources. Uses FaceTracker.normalizeAngle for head rotation
ARKitBodyTracker.swift [BodyJointInputSource] 19 base IDs (same as Vision) + extended body.arkit.{joint} sources. 3D→2D via ARCamera.projectPoint
ARKitCapabilities.swift Extension on TrackerCapabilities arkitCapabilities (runtime detection), allAvailable (Vision + ARKit)

Processing

File Role
PositionStabilizer.swift EMA smoothing (α=0.6), velocity clamping (0.15), stale-frame recovery (5 rejections)
RelationshipProcessor.swift Derives compound sources from joint pairs (distance, angle, deltas). 7 default definitions
BodyCalibration.swift T-pose calibration for body tracking normalization
TrackingMode.swift Enum: .hands, .body, .face, .relationships, .all

Views (Views/)

Diagnostic overlay views for PhotoLab. All #if os(iOS).

File Role
CameraPreviewView.swift UIViewRepresentable wrapping AVCaptureVideoPreviewLayer
TrackingSessionPreviewView.swift UIViewRepresentable wrapping any TrackingSession.makePreviewView()
HandOverlayView.swift Dot overlay for hand positions
BodyOverlayView.swift Joint dot + skeleton overlay
FaceOverlayView.swift Face landmark overlay
RelationshipOverlayView.swift Lines between related joints
ConfidenceIndicator.swift Visual confidence badge
SourceDataView.swift Scrollable data panel showing all active sources
CalibrationView.swift T-pose calibration UI

Key Implementation Details

  • Coordinate transforms: transformPosition(_:for:cameraPosition:) handles Vision→screen mapping with front-camera X mirror. All orientations supported.
  • Hand identity: resolveHandIdentities detects Vision chirality swaps between frames using position continuity (2-hand case, 2x threshold).
  • Finger counting: Compares fingertip vs PIP joint (not MCP) for deliberate extension detection. Holds last count when tips leave frame.
  • ARKit constraint: Face tracking = front camera only. Body tracking = rear camera only. Cannot run both ARKit configs simultaneously. In "all" mode: one ARKit config + Vision fallback for the other.
  • Frame skipping: ARTrackingSession skips every other frame for hand detection when ARKit is active (handDetectionFrameSkip).

Tests

cd Packages/Enobarbus && swift test
77 tests. ARKit-specific tests (ARKitFaceTrackerTests, ARKitBodyTrackerTests, ARKitCapabilitiesTests) are #if os(iOS) gated — they run on iOS Simulator builds but not macOS swift test.

Rules

  • All tracker @Published properties publish on main thread via DispatchQueue.main.async.
  • Source IDs are stable contracts — do not change body.{joint} or face.{landmark} names without updating all downstream consumers.
  • processPixelBuffer must stay in sync with processFrame — they share the same core logic.
  • Adding new trackers: follow the existing pattern (ObservableObject, @Published inputSources, processFrame/processPixelBuffer, allSourceIDs).