Class AnalysisManager#

Inheritance Relationships#

Base Type#

  • public QObject

Class Documentation#

class AnalysisManager : public QObject

Manages the Python analysis subprocess(es) for post-recording plugins (pose estimation, face masking, speaker diarization/transcription, facial expression).

analyze_session() / run_face_mask() / run_diarization() / run_expression_analysis() always run when called directly (e.g. a UI “Run” button). auto_analyze is a plain flag callers can check to decide whether to also trigger analysis automatically when a recording session ends — AnalysisManager itself does not gate on it. Only one script runs at a time regardless of which plugin queued it; a second call while one is running queues behind it.

The Python script is found relative to the application’s executable directory (for installed builds) or the project root (during development).

See also

RecordManager

Usage
AnalysisManager mgr;
connect(&mgr, &AnalysisManager::output_received, this, [](const QString& line) {
    log_info("[Analysis] " + line);
});
connect(recordMgr, &RecordManager::recording_stopped, &mgr,
        [&](const QString& path, int) {
            if (mgr.auto_analyze()) { mgr.analyze_session(path); }
        });
mgr.set_auto_analyze(true);

Public Functions

explicit AnalysisManager(QObject *parent = nullptr)
~AnalysisManager() override
void set_auto_analyze(bool enabled)

Enable / disable automatic post-recording analysis.

When true, analyze_session() is triggered automatically by the recording_stopped connection in Application.

bool auto_analyze() const
Returns:

true if auto-analysis is enabled.

void set_python_path(const QString &path)

Override the Python interpreter path.

By default, the manager searches for a virtual-environment interpreter at analysis/.venv/bin/python (macOS/Linux) or analysis.venv\Scripts\python.exe (Windows), then falls back to the system python3 / python.

Parameters:

path – Absolute path to the Python executable.

void set_model(const QString &modelName)

Set the YOLOv8 model variant.

Parameters:

modelName – e.g. "yolov8n-pose.pt" (default) or "yolov8s-pose.pt".

void set_frame_skip(int skip)

Set how many frames to skip between pose estimates.

1 = every frame (slowest, most detailed). 5 = every 5th frame (≈6 fps at 30 fps recording).

bool is_running() const
Returns:

true while the analysis subprocess is running.

void analyze_session(const QString &sessionPath)

Analyse all .mp4 files in sessionPath asynchronously.

Always runs when called directly (e.g. from a UI “Run” action). auto_analyze() only gates whether the caller triggers this automatically after a recording stops — it is not checked here. If a previous analysis is still running, this queues the new session.

Parameters:

sessionPath – Absolute path to the recorded session directory.

void run_face_mask(const QString &sessionPath, const QString &backend, const QString &style, int frameSkip)

Anonymize (blur/box) faces in all .mp4 files in sessionPath, writing output into a sibling “anonymized/” folder — originals are never modified.

Always runs when called directly, exactly like analyze_session(). If a previous analysis is still running, this queues the new job.

Parameters:
  • sessionPath – Absolute path to the recorded session directory.

  • backend – “mediapipe” (default), “yolov8”, or “opencv”.

  • style – “blur” (default) or “box”.

  • frameSkip – Run the detector every Nth frame, reusing the last detected boxes on skipped frames. Defaults to 1 (every frame) at the call site — raising this risks a skipped frame’s fast head motion going unmasked.

void run_diarization(const QString &sessionPath, const QString &modelSize, const QString &language, const QString &hfToken, int minSpeakers, int maxSpeakers, bool skipDiarization)

Transcribe (and, when possible, diarize) all .wav files in sessionPath, writing a “<name>.transcript.json” sidecar next to each one — originals are never modified.

Always runs when called directly, exactly like analyze_session(). If a previous analysis is still running, this queues the new job.

Parameters:
  • sessionPath – Absolute path to the recorded session directory.

  • modelSize – faster-whisper model size, e.g. “small” (default).

  • language – Force a language code (e.g. “en”), or empty to auto-detect.

  • hfToken – Hugging Face access token for the gated pyannote diarization models, or empty to skip diarization (transcript-only output).

  • minSpeakers – Optional pyannote hint, 0 = unset.

  • maxSpeakers – Optional pyannote hint, 0 = unset.

  • skipDiarization – Force transcript-only even if hfToken is set.

void run_expression_analysis(const QString &sessionPath, const QString &backend, int maxFaces, double minConfidence, int frameSkip)

Detect faces and classify a dominant basic-emotion label per face in all .mp4 files in sessionPath, writing a “<name>.expression.json” file per camera into the session’s own expression/ subfolder — originals are never modified.

Always runs when called directly, exactly like analyze_session(). If a previous analysis is still running, this queues the new job.

Parameters:
  • sessionPath – Absolute path to the recorded session directory.

  • backend – “heuristic” (default, rule-based blendshapes) or “ferplus” (pretrained FER+ ONNX model).

  • maxFaces – Maximum simultaneous faces to detect per frame.

  • minConfidence – Face detection/presence confidence threshold (0-1).

  • frameSkip – Process every Nth frame (1 = every frame).

void run_gaze_fusion(const QString &sessionPath, int minCameras, double minConfidence, int frameSkip)

Fuse per-camera 3D gaze rays (from every camera with both intrinsic and extrinsic calibration) into a triangulated room-space gaze origin/direction, plus (when the room plane is defined) a target point, per synchronized master tick. Writes a session-root “gaze_fusion.json” sidecar — originals are never modified.

Always runs when called directly, exactly like analyze_session(). If a previous analysis is still running, this queues the new job.

Proactively generates+saves sync_manifest.json first if the session doesn’t already have one (mirrors SessionPlayerW’s own “generate if

missing” pattern) — the fusion script requires it and shouldn’t have to duplicate that C++-side generation logic in Python.

Parameters:
  • sessionPath – Absolute path to the recorded session directory.

  • minCameras – Minimum simultaneous cameras required to compute a target point (rays are still recorded below this).

  • minConfidence – Face detection/presence confidence threshold (0-1).

  • frameSkip – Process every Nth frame per camera (1 = every frame).

void run_pose3d_reconstruction(const QString &sessionPath, int minCameras, double maxReprojectionErrorPx, int frameSkip, int smoothingWindow)

Triangulate each camera’s already-computed 2D pose keypoints (analyze_session()’s “.pose.json” sidecars — must already exist for at least 2 cameras) into 3D room-space skeletons, using the room extrinsic calibration and real cross-camera person association (multi-person capable, unlike run_gaze_fusion()’s single-subject design). Writes a session-root “skeleton3d.json” sidecar — originals are never modified.

Always runs when called directly, exactly like analyze_session(). If a previous analysis is still running, this queues the new job.

Proactively generates+saves sync_manifest.json first if the session doesn’t already have one, same as run_gaze_fusion().

Parameters:
  • sessionPath – Absolute path to the recorded session directory.

  • minCameras – Minimum cameras a person cluster must span to be reconstructed at all (>=2, the mathematical minimum for triangulation).

  • maxReprojectionErrorPx – Per-view reprojection error threshold (px) for outlier-view rejection during triangulation.

  • frameSkip – Process every Nth master tick (1 = every tick).

  • smoothingWindow – Centered per-track median filter width (in valid ticks) for the output’s “keypoints_room_smoothed” field (1 = off, the default — raw “keypoints_room” is always written regardless).

void run_rppg_analysis(const QString &sessionPath, const QString &backend, double windowSec, double hopSec, int smoothingWindows)

Estimate a remote (camera-based) heart rate over the course of a recorded session’s video, using classical (non-deep-learning) signal-processing algorithms. Writes one “<video_stem>.<backend>.rppg.json” per camera into the session’s own rppg/ subfolder — originals are never modified.

EXPERIMENTAL — research-grade heart-rate estimate only, not a medical device and not clinically validated. No blood-pressure or heart-rate-variability estimate is attempted (see item 21’s plan section for why both were deliberately descoped).

Always runs when called directly, exactly like analyze_session(). If a previous analysis is still running, this queues the new job. No sync_manifest.json dependency, unlike run_gaze_fusion()/ run_pose3d_reconstruction() — this is a single-camera analysis with no cross-camera synchronization need.

Parameters:
  • sessionPath – Absolute path to the recorded session directory.

  • backend – “green” (naive baseline), “chrom”, or “pos” (default, generally the most robust classical method).

  • windowSec – HR-analysis window length, in seconds.

  • hopSec – Sliding-window hop length, in seconds.

  • smoothingWindows – Centered median-filter width, in windows, for the smoothed_bpm series (1 = no smoothing).

void stop()

Stop the currently running analysis process immediately.

Signals

void output_received(QString line)

Emitted for every line of stdout / stderr from the Python subprocess.

void analysis_started(QString sessionPath)

Emitted when the subprocess starts.

void analysis_finished(QString sessionPath, bool success)

Emitted when the subprocess exits.

void setup_error(QString message)

Emitted if the Python interpreter or script cannot be found.