Known rig layouts on omnifin0

What do the real canvas files say about where each camera, arena, and experimental area sits, and how does a camera pixel land on the canvas?

Everything below is quoted verbatim from a source repository at a pinned commit. Section headings are the curator's only words; diagrams are parsed from the quoted text.

  1. Who owns rig, canvas, and arena geometry
  2. What a canvas file contains
  3. Canvas casper
  4. Canvas shadow
  5. Canvas shadow as Citrus recorded it (session H5, 2026-08-25)
  6. Canvas blindfish

Who owns rig, canvas, and arena geometry

agent-contractspancake-orange-citrus/rig_canvas_inventory_contract.mdL27–L33heading: ## Canonical Ownersview at e648029cc8

Canonical Owners

Concern Canonical location Stable IDs Notes
Static machine and camera inventory pancakebatter/system_config.yml host_id, camera_serial Hardware facts that change when the host is rewired, rebuilt, or different hardware is attached.
Per-camera runtime capture presets ~/orange_data/config/local/<preset>/<camera_serial>.json and ~/orange_data/config/network/... camera_serial Startup/runtime camera behavior for Orange. Not a hardware inventory or calibration source of truth.
Rig/canvas/arena geometry and calibration citrus/targets/rigs/<rig_id>/<canvas_name>/ rig_id, canvas_name, arena_id, camera_serial Canvas JSON, arena geometry, per-camera calibration, calibration artifacts, and arena-level IPC binding.
citrussrc/core/rig_config.hL33–L42lines: [33, 42]view at 48ad93e4d2
struct RigConfig {
    std::string schema_version = "rig_config_v1";
    std::string rig_id;
    int rig_geometry_revision = 0;
    std::string stimulus_display_output_name;
    int projector_mount_rotation_deg_about_optical_axis = 0;
    std::string projector_mount_notes;
    nlohmann::json display_signal_chain = nlohmann::json::object();
    PhotodiodeMarkerConfig photodiode_marker;
};
data · omnifin0-rig/home/jeremy/citrus/targets/rigs/omnifin0/omnifin0_config.jsonL1–L7lines: [1, 7]snapshot sha256 d53ccc76ec
{
    "schema_version": "rig_config_v1",
    "rig_id": "omnifin0",
    "rig_geometry_revision": 3,
    "stimulus_display_output_name": "DP-3",
    "projector_mount_rotation_deg_about_optical_axis": 180,
    "projector_mount_notes": "Projector body is rotated 180 degrees about its optical axis on this rig.",
citrusdocs/coordinate_systems.mdL198–L210heading: ### `projector_body_frame`view at 48ad93e4d2

projector_body_frame

The physical coordinate frame attached to the projector chassis.

  • Status: Manual rig metadata
  • Units: degrees about the optical axis
  • Current field:
    • projector_mount_rotation_deg_about_optical_axis
  • Evidence:

This frame is not automatically inferred from homography or xrandr.

citrusdocs/coordinate_systems.mdL183–L196heading: ### `display_output_transform`view at 48ad93e4d2

display_output_transform

The runtime desktop/session transform for the configured stimulus display output.

  • Status: Code-defined runtime snapshot
  • Source: xrandr --query
  • Examples:
    • output name such as DP-3
    • transform token such as normal or inverted
  • Evidence:

This is not the same thing as physical projector-body orientation.

What a canvas file contains

citrussrc/core/Canvas.cppL17–L22lines: [17, 22]view at 48ad93e4d2
        m_canvas_name = data.value("canvas_name", "Unnamed Canvas");

        // Load canvas dimensions with backward compatibility
        // Try new field names first, fallback to old names
        m_canvas_width_px = data.value("canvas_width_px", 1920);
        m_canvas_height_px = data.value("canvas_height_px", 1080);
citrussrc/calibration/arena_config.hL110–L131lines: [110, 131]view at 48ad93e4d2
struct CameraCalibration {
    std::string camera_id = "DefaultCamera";

    int native_width_px = 0;
    int native_height_px = 0;
    
    // Arena definition for this camera.
    // These fields define the active arena placement within the final display
    // canvas for this camera calibration.
    int arena_center_x_px = 960;     // Was: sub_arena_x_px
    int arena_center_y_px = 540;     // Was: sub_arena_y_px
    int arena_width_px = 1920;       // Was: sub_arena_width_px
    int arena_height_px = 980;       // Was: sub_arena_height_px

    // Calibration image paths
    std::string homography_image_path;
    std::string scale_image_path;

    // Per-camera rig calibration
    float real_world_ref_mm = 10.0f; // Reference size in mm for calibration
    float pixels_per_mm_camera = 0.0f;
    float pixels_per_mm_projector = 0.0f; // Pixels per mm in display/canvas space
citrussrc/calibration/arena_config.hL308–L350lines: [308, 350]view at 48ad93e4d2
    int GetArenaCenterXInCanvasPx() const {
        if (const auto* active_cam = GetActiveCameraCalibration()) {
            if (active_cam->arena_width_px > 0 || active_cam->arena_height_px > 0) {
                return active_cam->arena_center_x_px + runtime_arena_translation_x_px;
            }
        }
        return sub_arena_x_px + runtime_arena_translation_x_px;
    }

    int GetArenaCenterYInCanvasPx() const {
        if (const auto* active_cam = GetActiveCameraCalibration()) {
            if (active_cam->arena_width_px > 0 || active_cam->arena_height_px > 0) {
                return active_cam->arena_center_y_px + runtime_arena_translation_y_px;
            }
        }
        return sub_arena_y_px + runtime_arena_translation_y_px;
    }

    int GetArenaWidthPx() const {
        if (const auto* active_cam = GetActiveCameraCalibration()) {
            if (active_cam->arena_width_px > 0) {
                return active_cam->arena_width_px;
            }
        }
        return sub_arena_width_px;
    }

    int GetArenaHeightPx() const {
        if (const auto* active_cam = GetActiveCameraCalibration()) {
            if (active_cam->arena_height_px > 0) {
                return active_cam->arena_height_px;
            }
        }
        return sub_arena_height_px;
    }

    int GetArenaOriginXInCanvasPx() const {
        return GetArenaCenterXInCanvasPx() - (GetArenaWidthPx() / 2);
    }

    int GetArenaOriginYInCanvasPx() const {
        return GetArenaCenterYInCanvasPx() - (GetArenaHeightPx() / 2);
    }

The accessors below are the rule the diagrams use to place each arena: origin = center − size / 2.

citrussrc/ui/arena_view_ui.cppL783–L798lines: [783, 798]view at 48ad93e4d2

        const std::string homography_filename =
            "homography_" + arena.config.config_name + "_" + safe_cam_id + ".yml";
        const std::string full_homography_path = FileUtils::resolve_calibration_artifact_path(
            arena.rig_name,
            canvas_name,
            homography_filename);

        if (!std::filesystem::exists(full_homography_path)) {
            return;
        }

        try {
            cv::FileStorage fs(full_homography_path, cv::FileStorage::READ);
            if (fs.isOpened()) {
                fs["homography_matrix"] >> arena.config.homography_matrix;
citrusdocs/coordinate_systems.mdL155–L181heading: ### `camera_view_to_canvas_mapping`view at 48ad93e4d2

camera_view_to_canvas_mapping

The effective mapping from camera_view_px into the stimulus canvas, computed from the active homography.

  • Status: Calibration-derived
  • Nature:
    • point mapping via homography
    • local direction mapping via finite differences around the active arena center
  • Evidence:

The OpenCV sidecar homography_<arena>_<camera>.yml and the H5 /calibration_snapshot/<camera_id>/homography_matrix represent the same direction:

camera_view_px -> final_display_canvas_px

The homography source points are camera-image pixels from the calibration image. For current Orange/Citrus workflows these should be full raw camera-frame pixels when the source image is the native camera frame. If a future calibration is derived from a crop or downsampled image, that source image shape and crop transform must be documented in the artifact before Orange can consume it safely.

Canvas casper

data · omnifin0-casper/home/jeremy/citrus/targets/rigs/omnifin0/casper/casper.jsonL232–L234lines: [232, 234]snapshot sha256 0779e3751a
    "canvas_height_px": 980,
    "canvas_name": "casper",
    "canvas_width_px": 1920,
data · omnifin0-casper/home/jeremy/citrus/targets/rigs/omnifin0/casper/casper.jsonL3–L59lines: [3, 59]snapshot sha256 0779e3751a
        "arena_1": {
            "calculated_z_eff_mm": 8.600000381469727,
            "camera_calibrations": [
                {
                    "arena_center_x_px": 233,
                    "arena_center_y_px": 518,
                    "arena_height_px": 358,
                    "arena_width_px": 358,
                    "camera_id": "2010093",
                    "homography_image_path": "Cam2010093_arena1_homography.png",
                    "native_height_px": 4512,
                    "native_width_px": 4512,
                    "pixels_per_mm_camera": 50.13677978515625,
                    "pixels_per_mm_projector": 4.13323974609375,
                    "real_world_ref_mm": 10.0,
                    "scale_image_path": "Cam2010093_arena1_scale.png",
                    "sub_arena_height_px": 358,
                    "sub_arena_width_px": 358,
                    "sub_arena_x_px": 233,
                    "sub_arena_y_px": 518
                }
            ],
            "config_name": "arena_1",
            "dot_radius_px": 5.0,
            "experimental_area_center_x_px": 179.0,
            "experimental_area_center_y_px": 179.0,
            "experimental_area_corner_radius_mm": 12.097047805786133,
            "experimental_area_corner_radius_px": 50.0,
            "experimental_area_height_mm": 261.2962341308594,
            "experimental_area_height_px": 1080.0,
            "experimental_area_radius_mm": 39.0,
            "experimental_area_radius_px": 161.19635009765625,
            "experimental_area_shape": "CIRCLE",
            "experimental_area_width_mm": 464.5266418457031,
            "experimental_area_width_px": 1920.0,
            "grid_cols": 10,
            "grid_rows": 10,
            "selected_dish_type": 0,
            "selected_dish_type_name": "alpine",
            "sub_arena_height_mm": 86.6148681640625,
            "sub_arena_height_px": 358,
            "sub_arena_width_mm": 86.6148681640625,
            "sub_arena_width_px": 358,
            "sub_arena_x_px": 233,
            "sub_arena_y_px": 518,
            "swimmable_area_center_x_px": 179.0,
            "swimmable_area_center_y_px": 179.0,
            "swimmable_area_corner_radius_mm": 12.097047805786133,
            "swimmable_area_corner_radius_px": 50.0,
            "swimmable_area_height_mm": 261.2962341308594,
            "swimmable_area_height_px": 1080.0,
            "swimmable_area_radius_mm": 39.0,
            "swimmable_area_radius_px": 161.19635009765625,
            "swimmable_area_shape": "CIRCLE",
            "swimmable_area_width_mm": 464.5266418457031,
            "swimmable_area_width_px": 1920.0
        },

Arenas 2 to 4 follow the same shape; the diagram reads all four.

data · omnifin0-casper/home/jeremy/citrus/targets/rigs/omnifin0/casper/calibration_artifacts/homography_arena_1_2010093.ymlL1–L11lines: allsnapshot sha256 fdcc3fc145
%YAML:1.0
---
calibration_timestamp_utc: "2025-09-17T20:18:21Z"
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.082207453082140877, -0.00066412810328021418,
       47.420417721206299, -8.9454253274016412e-05, 0.083756452591587066,
       329.69881415866479, -1.3409654806698208e-06,
       7.8935385992712704e-07, 1. ]
data · omnifin0-casper/home/jeremy/citrus/targets/rigs/omnifin0/casper/calibration_artifacts/homography_arena_2_2010094.ymlL1–L11lines: allsnapshot sha256 da86a65a8c
%YAML:1.0
---
calibration_timestamp_utc: "2025-09-17T20:02:13Z"
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.081886033441020245, -0.0020771447404461254,
       505.58800694720321, 0.0001926936601442456, 0.081828041416371677,
       328.02427490300983, -9.6997948595970616e-07,
       -2.5839676833045028e-06, 1. ]
data · omnifin0-casper/home/jeremy/citrus/targets/rigs/omnifin0/casper/calibration_artifacts/homography_arena_3_2010095.ymlL1–L11lines: allsnapshot sha256 78d940e9c3
%YAML:1.0
---
calibration_timestamp_utc: "2025-09-17T19:40:14Z"
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.082909630001767279, -0.0002622864390851616,
       950.81092323202608, 0.00022825197426609283, 0.083712022286087387,
       329.89189456156413, -6.2063443322505952e-07,
       -5.2083469382779818e-08, 0.99999999999999989 ]
data · omnifin0-casper/home/jeremy/citrus/targets/rigs/omnifin0/casper/calibration_artifacts/homography_arena_4_2010096.ymlL1–L11lines: allsnapshot sha256 78287f4af0
%YAML:1.0
---
calibration_timestamp_utc: "2025-08-07T18:13:24Z"
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.081147981086110682, -0.0020955747136053512,
       1408.219892517151, 4.4540080357911953e-06, 0.08250310332982029,
       317.8566782440501, -1.1406447654227366e-06,
       -1.2853170879874564e-06, 0.99999999999999989 ]
final_display_canvas_px — canvas_name=casper, canvas_width_px=1920, canvas_height_px=980solid = arena region (arena_center ± arena_size/2) · filled = experimental area · + = experimental_area_centerdashed = camera native frame corners pushed through homography_matrix+x+y(0,0)arena_1 origin (54, 339)camera_id=2010093arena_2 origin (513, 340)camera_id=2010094arena_3 origin (961, 340)camera_id=2010095arena_4 origin (1390, 347)camera_id=2010096camera_view_px — arena region and experimental area mapped through inverse(H_camera_to_display)2010093 · arena_1 · 4512×4512exp. center → (2272.0, 2242.7)2010094 · arena_2 · 4512×4512exp. center → (2265.9, 2277.3)2010095 · arena_3 · 4512×4512exp. center → (2268.0, 2243.4)2010096 · arena_4 · 4512×4512exp. center → (1940.9, 2488.2)
arena_idcamera_idarena_center_x_px, arena_center_y_pxarena_width_px × arena_height_pxarena origin
center − size/2
experimental_area_center_x_px, _y_px
arena_relative_canvas_px
same point in final_display_canvas_px
origin + center
same point in camera_view_px
inverse(H) · p_display
pixels_per_mm_camerapixels_per_mm_projector
arena_12010093(233, 518)358 × 358(54, 339)(179, 179)(233, 518)(2272.0, 2242.7)50.144.133
arena_22010094(692, 519)358 × 358(513, 340)(179, 179)(692, 519)(2265.9, 2277.3)50.204.187
arena_32010095(1140, 519)358 × 358(961, 340)(179, 179)(1140, 519)(2268.0, 2243.4)50.994.269
arena_42010096(1569, 526)358 × 358(1390, 347)(179, 179)(1569, 526)(1940.9, 2488.2)51.014.259
Derived automatically from casper-arena-1 (omnifin0-casper: casper.json), casper-h-2010093 (omnifin0-casper: calibration_artifacts/homography_arena_1_2010093.yml), casper-h-2010094 (omnifin0-casper: calibration_artifacts/homography_arena_2_2010094.yml), casper-h-2010095 (omnifin0-casper: calibration_artifacts/homography_arena_3_2010095.yml), casper-h-2010096 (omnifin0-casper: calibration_artifacts/homography_arena_4_2010096.yml).

Canvas shadow

data · omnifin0-shadow/home/jeremy/citrus/targets/rigs/omnifin0/shadow/shadow.jsonL568–L570lines: [568, 570]snapshot sha256 aa36e307fe
    "canvas_height_px": 1080,
    "canvas_name": "shadow",
    "canvas_width_px": 1920
data · omnifin0-shadow/home/jeremy/citrus/targets/rigs/omnifin0/shadow/shadow.jsonL3–L81lines: [3, 81]snapshot sha256 aa36e307fe
        "arena_1": {
            "active_camera_id": "2010093",
            "recording_context": {
                "schema_id": "citrus.parent_recording_context",
                "schema_version": 2,
                "recording_type": "behavior",
                "behavior_mode": "free",
                "recording_intent": "stimulus_experiment",
                "data_origin": "acquired"
            },
            "arena_region_height_mm": 82.82921600341797,
            "arena_region_width_mm": 82.82921600341797,
            "calculated_z_eff_mm": 14.94233512878418,
            "calibration_pattern_mask_policy": "experimental_area",
            "calibration_pattern_mode": "circular_rings",
            "calibration_ring_chirality_marker_radius_scale": 1.2999999523162842,
            "calibration_ring_count": 4,
            "calibration_ring_dots_inner": 8,
            "calibration_ring_dots_outer": 32,
            "calibration_ring_include_center_dot": true,
            "calibration_ring_inner_radius_px": 40.0,
            "calibration_ring_orientation_marker_angle_deg": 0.0,
            "calibration_ring_orientation_marker_radius_scale": 1.600000023841858,
            "calibration_ring_outer_radius_px": 140.0,
            "calibration_ring_show_chirality_marker": false,
            "calibration_ring_show_orientation_marker": true,
            "calibration_verification_include_center_dot": true,
            "calibration_verification_inner_angle_offset_deg": 45.0,
            "calibration_verification_inner_point_count": 4,
            "calibration_verification_inner_radius_fraction": 0.3799999952316284,
            "calibration_verification_outer_angle_offset_deg": 22.5,
            "calibration_verification_outer_point_count": 8,
            "calibration_verification_outer_radius_fraction": 0.7799999713897705,
            "camera_calibrations": [
                {
                    "arena_center_x_px": 257,
                    "arena_center_y_px": 330,
                    "arena_height_px": 348,
                    "arena_width_px": 348,
                    "camera_id": "2010093",
                    "homography_detector_strategy": "",
                    "homography_fit_inlier_count": 0,
                    "homography_fit_max_reprojection_error_px": 0.0,
                    "homography_fit_method": "",
                    "homography_fit_point_count": 0,
                    "homography_fit_quality_available": false,
                    "homography_fit_rms_reprojection_error_px": 0.0,
                    "homography_image_path": "Cam2010093_arena1_homography.png",
                    "native_height_px": 4512,
                    "native_width_px": 4512,
                    "pixels_per_mm_camera": 52.731327056884766,
                    "pixels_per_mm_projector": 4.201416015625,
                    "real_world_ref_mm": 5.0,
                    "scale_image_path": "/home/jeremy/orange_data/calibrations/sessions/calsess_2026_07_20T16_16_39Z_shadow/artifacts/Cam2010093_arena_1/scale_observations/calgrp_2026_07_20T16_16_38Z_shadow_projected_surface_scale_calibration_projected_surface_g1/observation.json",
                    "scale_models": [
                        {
                            "clicked_points_camera_px": [
                                {
                                    "x": -1.0,
                                    "y": -1.0
                                },
                                {
                                    "x": -1.0,
                                    "y": -1.0
                                }
                            ],
                            "kind": "projective_local_jacobian_at_target_origin",
                            "notes": "Loaded from immutable active projected-surface scale pointer",
                            "pixels_per_mm_camera": 52.731327056884766,
                            "pixels_per_mm_projector": 4.201416015625,
                            "real_world_ref_mm": 5.0,
                            "scale_image_path": "/home/jeremy/orange_data/calibrations/sessions/calsess_2026_07_20T16_16_39Z_shadow/artifacts/Cam2010093_arena_1/scale_observations/calgrp_2026_07_20T16_16_38Z_shadow_projected_surface_scale_calibration_projected_surface_g1/observation.json",
                            "status": "accepted",
                            "target_plane": "projected_surface"
                        }
                    ]
                }
            ],
            "config_name": "arena_1",
data · omnifin0-shadow/home/jeremy/citrus/targets/rigs/omnifin0/shadow/calibration_artifacts/homography_arena_1_2010093.ymlL1–L28lines: allsnapshot sha256 64389adc05
%YAML:1.0
---
artifact_schema_id: "citrus.calibration.homography_candidate"
artifact_schema_version: 1
candidate_id: homography_arena_1_2010093_holder_operational_calgrp_2026_09_14T21_14_03Z_shadow_homography_grid_projected_surface_g4
candidate_set_id: homography_set_holder_operational_calgrp_2026_09_14T21_14_03Z_shadow_homography_grid_projected_surface_g4
homography_direction: camera_native_px_to_final_display_canvas_px
target_plane: projected_surface
pattern_mode: circular_rings
orientation_validation_status: passed
orientation_normalization_transform: ""
configuration_fingerprint: "sha256:acdf5e997e9534a5aacf1a5918dcdd5c39b82083723a1c97e9f8c797d7b590dd"
source_capture_checksum: "fnv1a64:59a824924a20c5b8"
fit_quality_schema: "citrus.homography_fit_quality.v2"
point_count: 81
inlier_count: 81
rms_reprojection_error_px: 0.15760693143058349
max_reprojection_error_px: 0.55931097552812092
holdout_rms_reprojection_error_px: 0.26288468800299092
holdout_max_reprojection_error_px: 0.8113809254153006
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.079491864932271408, -0.00030229498036896943,
       77.133992465809925, -0.0002757502180579138, -0.080094974712873052,
       510.53996688386758, -5.5941326802569046e-07,
       -1.5604039838712963e-07, 1. ]
data · omnifin0-shadow/home/jeremy/citrus/targets/rigs/omnifin0/shadow/calibration_artifacts/homography_arena_2_2010094.ymlL1–L28lines: allsnapshot sha256 9ecc6fd22e
%YAML:1.0
---
artifact_schema_id: "citrus.calibration.homography_candidate"
artifact_schema_version: 1
candidate_id: homography_arena_2_2010094_holder_operational_calgrp_2026_09_14T21_14_03Z_shadow_homography_grid_projected_surface_g4
candidate_set_id: homography_set_holder_operational_calgrp_2026_09_14T21_14_03Z_shadow_homography_grid_projected_surface_g4
homography_direction: camera_native_px_to_final_display_canvas_px
target_plane: projected_surface
pattern_mode: circular_rings
orientation_validation_status: passed
orientation_normalization_transform: ""
configuration_fingerprint: "sha256:d72e9f92a659d5a82300fd5f459901f57b66bb32c3f10e9c0dff90868635445f"
source_capture_checksum: "fnv1a64:0ba1ed8aac5fa422"
fit_quality_schema: "citrus.homography_fit_quality.v2"
point_count: 81
inlier_count: 81
rms_reprojection_error_px: 0.10770023906032811
max_reprojection_error_px: 0.19808838955150102
holdout_rms_reprojection_error_px: 0.097546878939541851
holdout_max_reprojection_error_px: 0.21875675225054581
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.079749699673856339, -0.00016696639198430951,
       534.10987430089563, 9.7135909416466784e-07, -0.080221795348528954,
       515.65136969047126, -2.4060414289172904e-08,
       -2.0730202530434087e-07, 1. ]
data · omnifin0-shadow/home/jeremy/citrus/targets/rigs/omnifin0/shadow/calibration_artifacts/homography_arena_3_2010095.ymlL1–L28lines: allsnapshot sha256 5a34ec42fa
%YAML:1.0
---
artifact_schema_id: "citrus.calibration.homography_candidate"
artifact_schema_version: 1
candidate_id: homography_arena_3_2010095_holder_operational_calgrp_2026_09_14T21_14_03Z_shadow_homography_grid_projected_surface_g4
candidate_set_id: homography_set_holder_operational_calgrp_2026_09_14T21_14_03Z_shadow_homography_grid_projected_surface_g4
homography_direction: camera_native_px_to_final_display_canvas_px
target_plane: projected_surface
pattern_mode: circular_rings
orientation_validation_status: passed
orientation_normalization_transform: ""
configuration_fingerprint: "sha256:eb537d7ccafe30c060a47dbb873a30f5e163df957bc999549bbd65e8764efdb9"
source_capture_checksum: "fnv1a64:4e6556a50bcffc03"
fit_quality_schema: "citrus.homography_fit_quality.v2"
point_count: 81
inlier_count: 81
rms_reprojection_error_px: 0.10510913896910379
max_reprojection_error_px: 0.18766646396702313
holdout_rms_reprojection_error_px: 0.089778931591189701
holdout_max_reprojection_error_px: 0.19379527155274262
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.079776648055879154, -0.00035172875892869603,
       992.89076791707544, -0.0001315853717091643, -0.080414900406512849,
       508.13242693376446, -8.1480313487639167e-08,
       -4.1264831497215999e-07, 1. ]
data · omnifin0-shadow/home/jeremy/citrus/targets/rigs/omnifin0/shadow/calibration_artifacts/homography_arena_4_2010096.ymlL1–L28lines: allsnapshot sha256 50f5ad5c3b
%YAML:1.0
---
artifact_schema_id: "citrus.calibration.homography_candidate"
artifact_schema_version: 1
candidate_id: homography_arena_4_2010096_holder_operational_calgrp_2026_09_14T21_14_03Z_shadow_homography_grid_projected_surface_g4
candidate_set_id: homography_set_holder_operational_calgrp_2026_09_14T21_14_03Z_shadow_homography_grid_projected_surface_g4
homography_direction: camera_native_px_to_final_display_canvas_px
target_plane: projected_surface
pattern_mode: circular_rings
orientation_validation_status: passed
orientation_normalization_transform: ""
configuration_fingerprint: "sha256:dbf5a368656f5693824d54474d0d44c757d57dbe35e18fd0ed7604f1f021c19a"
source_capture_checksum: "fnv1a64:83be12be94f9afe9"
fit_quality_schema: "citrus.homography_fit_quality.v2"
point_count: 81
inlier_count: 81
rms_reprojection_error_px: 0.10346316182563985
max_reprojection_error_px: 0.18949248947106911
holdout_rms_reprojection_error_px: 0.10203517579609787
holdout_max_reprojection_error_px: 0.18449438895003067
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.079789382117857285, -0.00050997723109478654,
       1471.539452875699, -0.00038137965481465632, -0.080710715149257969,
       501.65602085052916, -2.8975815431741569e-07,
       -4.6056798093723558e-07, 1. ]
final_display_canvas_px — canvas_name=shadow, canvas_width_px=1920, canvas_height_px=1080solid = arena region (arena_center ± arena_size/2) · filled = experimental area · + = experimental_area_centerdashed = camera native frame corners pushed through homography_matrix+x+y(0,0)arena_1 origin (83, 156)camera_id=2010093arena_2 origin (540, 162)camera_id=2010094arena_3 origin (997, 153)camera_id=2010095arena_4 origin (1475, 143)camera_id=2010096camera_view_px — arena region and experimental area mapped through inverse(H_camera_to_display)2010093 · arena_1 · 4512×4512exp. center → (2253.5, 2265.4)2010094 · arena_2 · 4512×4512exp. center → (2255.7, 2241.6)2010095 · arena_3 · 4512×4512exp. center → (2213.7, 2265.8)2010096 · arena_4 · 4512×4512exp. center → (2216.2, 2271.7)
arena_idcamera_idarena_center_x_px, arena_center_y_pxarena_width_px × arena_height_pxarena origin
center − size/2
experimental_area_center_x_px, _y_px
arena_relative_canvas_px
same point in final_display_canvas_px
origin + center
same point in camera_view_px
inverse(H) · p_display
pixels_per_mm_camerapixels_per_mm_projector
arena_12010093(257, 330)348 × 348(83, 156)(173, 173)(256, 329)(2253.5, 2265.4)52.734.201
arena_22010094(715, 337)350 × 350(540, 162)(174, 174)(714, 336)(2255.7, 2241.6)52.604.209
arena_32010095(1173, 329)352 × 352(997, 153)(173, 173)(1170, 326)(2213.7, 2265.8)52.454.219
arena_42010096(1652, 320)354 × 354(1475, 143)(175, 175)(1650, 318)(2216.2, 2271.7)52.324.233
Derived automatically from shadow-arena-1 (omnifin0-shadow: shadow.json), shadow-h-2010093 (omnifin0-shadow: calibration_artifacts/homography_arena_1_2010093.yml), shadow-h-2010094 (omnifin0-shadow: calibration_artifacts/homography_arena_2_2010094.yml), shadow-h-2010095 (omnifin0-shadow: calibration_artifacts/homography_arena_3_2010095.yml), shadow-h-2010096 (omnifin0-shadow: calibration_artifacts/homography_arena_4_2010096.yml).

Canvas shadow as Citrus recorded it (session H5, 2026-08-25)

agent-contractscitrus-crimson/spatial_overlay_contract.mdL77–L96heading: ### Arena embedding into full display canvasview at e648029cc8

Arena embedding into full display canvas

Group:

  • /calibration_snapshot/arena_geometry

Authoritative attrs:

  • arena_region_center_in_canvas_x_px
  • arena_region_center_in_canvas_y_px
  • arena_region_width_px
  • arena_region_height_px
  • arena_origin_in_canvas_x_px
  • arena_origin_in_canvas_y_px

Meaning:

  • These place the active arena region inside final_display_canvas_px.
  • Overlay consumers should use arena_origin_in_canvas_x_px and arena_origin_in_canvas_y_px as the authoritative local-to-global embedding.
agent-contractscitrus-crimson/spatial_overlay_contract.mdL98–L129heading: ### Per-camera homographyview at e648029cc8

Per-camera homography

Per-camera group:

  • /calibration_snapshot/<camera_id>

Authoritative dataset:

  • homography_matrix (3x3, float64)

Compatibility fallback:

  • homography_matrix_yml

Required interpretation for both:

  • source_frame="camera_view_px"
  • dest_frame="final_display_canvas_px"
  • coordinate_origin="top_left"
  • axes="x_right_y_down"
  • image_space="raw"
  • camera_id=<camera_id> when present

Meaning:

  • The stored homography direction is:
camera_view_px -> final_display_canvas_px
  • Consumers that need camera overlay coordinates must invert it.
citrussrc/logging/session_logger.cppL5801–L5831lines: [5801, 5831]view at 48ad93e4d2

            const auto direction_mapping =
                BuildDirectionFrameMapping(cam_calib, arena_config.homography_matrix);
            if (direction_mapping.has_value()) {
                writeFloatAttr(camGroup, "direction_mapping_ref_camera_x",
                               direction_mapping->reference_camera_point_px.x);
                writeFloatAttr(camGroup, "direction_mapping_ref_camera_y",
                               direction_mapping->reference_camera_point_px.y);
                writeFloatAttr(camGroup, "direction_mapping_ref_canvas_x",
                               direction_mapping->reference_canvas_point_px.x);
                writeFloatAttr(camGroup, "direction_mapping_ref_canvas_y",
                               direction_mapping->reference_canvas_point_px.y);
                writeFloatAttr(camGroup, "camera_view_right_in_canvas_x",
                               direction_mapping->camera_view_right_in_canvas.x);
                writeFloatAttr(camGroup, "camera_view_right_in_canvas_y",
                               direction_mapping->camera_view_right_in_canvas.y);
                writeFloatAttr(camGroup, "camera_view_right_in_canvas_angle_deg",
                               direction_mapping->camera_view_right_angle_deg);
                writeFloatAttr(camGroup, "camera_view_down_in_canvas_x",
                               direction_mapping->camera_view_down_in_canvas.x);
                writeFloatAttr(camGroup, "camera_view_down_in_canvas_y",
                               direction_mapping->camera_view_down_in_canvas.y);
                writeFloatAttr(camGroup, "camera_view_down_in_canvas_angle_deg",
                               direction_mapping->camera_view_down_angle_deg);
                writeFloatAttr(camGroup, "direction_mapping_scale_x",
                               direction_mapping->canvas_px_per_camera_px_x);
                writeFloatAttr(camGroup, "direction_mapping_scale_y",
                               direction_mapping->canvas_px_per_camera_px_y);
                writeStrAttr(camGroup, "direction_mapping_handedness",
                             direction_mapping->is_mirrored ? "mirrored" : "preserved");
                writeStrAttr(camGroup, "direction_mapping_canvas_angle_convention",
citrussrc/logging/session_logger.cppL5835–L5849lines: [5835, 5849]view at 48ad93e4d2
            if (!arena_config.homography_matrix.empty() &&
                arena_config.homography_matrix.rows == 3 &&
                arena_config.homography_matrix.cols == 3) {
                cv::Mat homography_matrix_f64;
                arena_config.homography_matrix.convertTo(homography_matrix_f64, CV_64F);
                hsize_t matrix_dims[2] = {3, 3};
                H5::DataSpace matrix_dspace(2, matrix_dims);
                H5::DataSet matrix_dset = camGroup.createDataSet(
                    "homography_matrix",
                    H5::PredType::NATIVE_DOUBLE,
                    matrix_dspace);
                matrix_dset.write(homography_matrix_f64.ptr<double>(), H5::PredType::NATIVE_DOUBLE);
                WriteHomographyDatasetAttrs(matrix_dset, cam_calib.camera_id);
                WriteHomographyProvenanceAttrs(matrix_dset,
                                               homography_provenance,
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_45fac0f71f098cf00f44065893c773b7847ab224edba618cb2a1c9744731065e_redbat.calibration_snapshot.jsonL7–L52json_path: root_attrssnapshot sha256 0209571b3f
  "root_attrs": {
    "active_ipc_source": "SHAMAN",
    "arena_id": "arena_1",
    "artifact_schema_id": "behavior_v1",
    "behavior_mode": "free",
    "canvas_name": "shadow",
    "display_mode_height_reported_px": 1080,
    "display_mode_observation_source": "xrandr_selected_output_verbose_current_mode",
    "display_mode_pixel_clock_reported_mhz": 296.7,
    "display_mode_width_reported_px": 1920,
    "display_refresh_rate_reported_hz": 119.88,
    "display_refresh_rate_source": "xrandr_selected_output_current_mode",
    "display_submission_rate_source": "citrus_frame_rate_limiter_nominal",
    "display_submission_rate_target_hz": 120.0,
    "display_swap_interval": 0,
    "expected_bounding_boxes_rows": 173563,
    "expected_chaser_states_rows": 180232,
    "expected_display_frame_submission_rows": 215977,
    "expected_events_rows": 152,
    "expected_frame_metadata_rows": 216232,
    "expected_independent_motion_grid_states_rows": 0,
    "expected_moving_grating_states_rows": 0,
    "expected_runtime_trials_rows": 4,
    "experimental_chamber": "palm1",
    "hostname": "pancake0.hhmi.org",
    "ipc_first_nonzero_frame_id": 1008,
    "ipc_first_nonzero_frame_timestamp_ns_session": 1388876200,
    "ipc_reader_init_status": "initialized",
    "ipc_source_name": "/shm_cam_2010093",
    "loaded_protocol_filepath": "redbat.json",
    "protocol_name_from_definition": "redbat",
    "recording_subtype": "free",
    "recording_type": "behavior",
    "rig_id": "omnifin0",
    "session_output_dir": "/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus",
    "session_start_iso8601_utc": "2026-08-26T03-12-19Z",
    "session_start_ns_epoch": 1787713939114183878,
    "session_status": "COMPLETE",
    "session_uuid": "citsess_45fac0f71f098cf00f44065893c773b7847ab224edba618cb2a1c9744731065e",
    "software_version": "",
    "stimulus_frame_rate_nominal_hz": 120.0,
    "stimulus_output_height": 1080,
    "stimulus_output_width": 1920,
    "tracking_bounding_boxes_rows": 173563,
    "tracking_chaser_states_rows": 180232
  },

Session identity and the canvas size Citrus actually rendered (stimulus_output_width/height). The other three sessions share canvas_name and rig_id.

data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_45fac0f71f098cf00f44065893c773b7847ab224edba618cb2a1c9744731065e_redbat.calibration_snapshot.jsonL231–L254json_path: calibration_snapshot.groups.arena_geometry.attrssnapshot sha256 0209571b3f
        "attrs": {
          "arena_geometry_source": "active_camera_calibration",
          "arena_origin_in_canvas_x_px": 90.0,
          "arena_origin_in_canvas_y_px": 164.0,
          "arena_region_center_in_canvas_x_px": 264.0,
          "arena_region_center_in_canvas_y_px": 338.0,
          "arena_region_height_px": 348.0,
          "arena_region_width_px": 348.0,
          "coordinate_frame": "arena_relative_canvas_px",
          "coordinate_origin": "top_left_of_active_arena",
          "experimental_area_center_x_px": 173.0,
          "experimental_area_center_y_px": 173.0,
          "experimental_area_corner_radius_mm": 11.99231243133545,
          "experimental_area_corner_radius_px": 50.1770133972168,
          "experimental_area_height_mm": 80.0,
          "experimental_area_height_px": 334.72784423828125,
          "experimental_area_radius_mm": 40.0,
          "experimental_area_radius_px": 167.36392211914062,
          "experimental_area_shape": "CIRCLE",
          "experimental_area_width_mm": 80.0,
          "experimental_area_width_px": 334.72784423828125,
          "x_axis_direction": "right",
          "y_axis_direction": "down"
        }
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_45fac0f71f098cf00f44065893c773b7847ab224edba618cb2a1c9744731065e_redbat.calibration_snapshot.jsonL61–L79json_path: calibration_snapshot.groups.2010093.attrssnapshot sha256 0209571b3f
        "attrs": {
          "camera_view_down_in_canvas_angle_deg": -90.35123443603516,
          "camera_view_down_in_canvas_x": -0.006130152847617865,
          "camera_view_down_in_canvas_y": -0.999981164932251,
          "camera_view_right_in_canvas_angle_deg": -0.35421979427337646,
          "camera_view_right_in_canvas_x": 0.9999808073043823,
          "camera_view_right_in_canvas_y": -0.0061822617426514626,
          "direction_mapping_canvas_angle_convention": "0=right,90=down",
          "direction_mapping_handedness": "mirrored",
          "direction_mapping_ref_camera_x": 2229.577392578125,
          "direction_mapping_ref_camera_y": 2245.14892578125,
          "direction_mapping_ref_canvas_x": 259.0,
          "direction_mapping_ref_canvas_y": 332.0,
          "direction_mapping_scale_x": 0.07898100465536118,
          "direction_mapping_scale_y": 0.07965237647294998,
          "pixels_per_mm_camera": 52.75507354736328,
          "pixels_per_mm_projector": 4.184098243713379,
          "real_world_ref_mm": 5.0
        },
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_45fac0f71f098cf00f44065893c773b7847ab224edba618cb2a1c9744731065e_redbat.calibration_snapshot.jsonL132–L184json_path: calibration_snapshot.groups.2010093.datasets.homography_matrixsnapshot sha256 0209571b3f
          "homography_matrix": {
            "attrs": {
              "arena_config_name": "arena_1",
              "axes": "x_right_y_down",
              "camera_id": "2010093",
              "canvas_name": "shadow",
              "coordinate_origin": "top_left",
              "dest_frame": "final_display_canvas_px",
              "homography_artifact_checksum_algorithm": "fnv1a64",
              "homography_artifact_checksum_fnv1a64": "af351bdb2310837d",
              "homography_artifact_exists": "true",
              "homography_artifact_filename": "homography_arena_1_2010093.yml",
              "homography_artifact_mtime_unix_ns": 1786323758019198158,
              "homography_artifact_path": "/home/jeremy/citrus/targets/rigs/omnifin0/shadow/calibration_artifacts/homography_arena_1_2010093.yml",
              "homography_artifact_size_bytes": 1295,
              "homography_fit_inlier_count": 81,
              "homography_fit_max_reprojection_error_px": 0.2432277899465086,
              "homography_fit_point_count": 81,
              "homography_fit_quality_available": "true",
              "homography_fit_quality_schema": "citrus.homography_fit_quality.v1",
              "homography_fit_rms_reprojection_error_px": 0.12110689401871437,
              "homography_payload_source": "runtime_arena_config.homography_matrix",
              "homography_provenance_schema": "citrus.homography_provenance.v1",
              "image_space": "raw",
              "rig_id": "omnifin0",
              "runtime_matrix_checksum_algorithm": "sha256",
              "runtime_matrix_checksum_serialization": "json_row_major_float64_v1",
              "runtime_matrix_checksum_sha256": "sha256:cfdbbbf45b75226f42cdadeb77831a9cd5fa7b7d31834237f41e18d85c729112",
              "source_frame": "camera_view_px"
            },
            "dtype": "float64",
            "shape": [
              3,
              3
            ],
            "value": [
              [
                0.07874250445494245,
                -0.0005856985662148073,
                84.33456319725997
              ],
              [
                -0.0006186948810984491,
                -0.07962950658326917,
                511.62381365417457
              ],
              [
                -3.960271677654378e-07,
                -3.2541264852901244e-07,
                1.0
              ]
            ]
          },
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_dfa15a4b01f45041f60d8bc571389db77000fa9c8d030d300b76f4636f89d021_redbat.calibration_snapshot.jsonL231–L254json_path: calibration_snapshot.groups.arena_geometry.attrssnapshot sha256 75f3378881
        "attrs": {
          "arena_geometry_source": "active_camera_calibration",
          "arena_origin_in_canvas_x_px": 551.0,
          "arena_origin_in_canvas_y_px": 159.0,
          "arena_region_center_in_canvas_x_px": 726.0,
          "arena_region_center_in_canvas_y_px": 334.0,
          "arena_region_height_px": 350.0,
          "arena_region_width_px": 350.0,
          "coordinate_frame": "arena_relative_canvas_px",
          "coordinate_origin": "top_left_of_active_arena",
          "experimental_area_center_x_px": 174.0,
          "experimental_area_center_y_px": 174.0,
          "experimental_area_corner_radius_mm": 11.97751522064209,
          "experimental_area_corner_radius_px": 50.25548553466797,
          "experimental_area_height_mm": 80.0,
          "experimental_area_height_px": 335.66552734375,
          "experimental_area_radius_mm": 40.0,
          "experimental_area_radius_px": 167.832763671875,
          "experimental_area_shape": "CIRCLE",
          "experimental_area_width_mm": 80.0,
          "experimental_area_width_px": 335.66552734375,
          "x_axis_direction": "right",
          "y_axis_direction": "down"
        }
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_dfa15a4b01f45041f60d8bc571389db77000fa9c8d030d300b76f4636f89d021_redbat.calibration_snapshot.jsonL61–L79json_path: calibration_snapshot.groups.2010094.attrssnapshot sha256 75f3378881
        "attrs": {
          "camera_view_down_in_canvas_angle_deg": -90.131103515625,
          "camera_view_down_in_canvas_x": -0.002288323361426592,
          "camera_view_down_in_canvas_y": -0.9999973773956299,
          "camera_view_right_in_canvas_angle_deg": -0.2858169376850128,
          "camera_view_right_in_canvas_x": 0.9999875426292419,
          "camera_view_right_in_canvas_y": -0.004988425876945257,
          "direction_mapping_canvas_angle_convention": "0=right,90=down",
          "direction_mapping_handedness": "mirrored",
          "direction_mapping_ref_camera_x": 2247.4580078125,
          "direction_mapping_ref_camera_y": 2261.30322265625,
          "direction_mapping_ref_canvas_x": 721.0,
          "direction_mapping_ref_canvas_y": 337.0,
          "direction_mapping_scale_x": 0.07952979952096939,
          "direction_mapping_scale_y": 0.08001729846000671,
          "pixels_per_mm_camera": 52.58562088012695,
          "pixels_per_mm_projector": 4.195818901062012,
          "real_world_ref_mm": 5.0
        },
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_dfa15a4b01f45041f60d8bc571389db77000fa9c8d030d300b76f4636f89d021_redbat.calibration_snapshot.jsonL132–L184json_path: calibration_snapshot.groups.2010094.datasets.homography_matrixsnapshot sha256 75f3378881
          "homography_matrix": {
            "attrs": {
              "arena_config_name": "arena_2",
              "axes": "x_right_y_down",
              "camera_id": "2010094",
              "canvas_name": "shadow",
              "coordinate_origin": "top_left",
              "dest_frame": "final_display_canvas_px",
              "homography_artifact_checksum_algorithm": "fnv1a64",
              "homography_artifact_checksum_fnv1a64": "2106f36756e5fbd8",
              "homography_artifact_exists": "true",
              "homography_artifact_filename": "homography_arena_2_2010094.yml",
              "homography_artifact_mtime_unix_ns": 1786323758055198160,
              "homography_artifact_path": "/home/jeremy/citrus/targets/rigs/omnifin0/shadow/calibration_artifacts/homography_arena_2_2010094.yml",
              "homography_artifact_size_bytes": 1295,
              "homography_fit_inlier_count": 81,
              "homography_fit_max_reprojection_error_px": 0.2901524344662282,
              "homography_fit_point_count": 81,
              "homography_fit_quality_available": "true",
              "homography_fit_quality_schema": "citrus.homography_fit_quality.v1",
              "homography_fit_rms_reprojection_error_px": 0.1147780064519357,
              "homography_payload_source": "runtime_arena_config.homography_matrix",
              "homography_provenance_schema": "citrus.homography_provenance.v1",
              "image_space": "raw",
              "rig_id": "omnifin0",
              "runtime_matrix_checksum_algorithm": "sha256",
              "runtime_matrix_checksum_serialization": "json_row_major_float64_v1",
              "runtime_matrix_checksum_sha256": "sha256:ffdb4429ed3fe0c3a534960ff15e2a92ea8b57e7cf2fda316f306404867264b2",
              "source_frame": "camera_view_px"
            },
            "dtype": "float64",
            "shape": [
              3,
              3
            ],
            "value": [
              [
                0.07906860271482405,
                -0.00047793029202255766,
                542.9984779325481
              ],
              [
                -0.0005470654070846848,
                -0.07998867525696146,
                518.4636429271127
              ],
              [
                -4.5995586459547647e-07,
                -3.886052250950324e-07,
                1.0
              ]
            ]
          },
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_8b4b3225db87e46aed185e1358300c839157a13d2d87c6a2ee1d2eb88668221b_redbat.calibration_snapshot.jsonL231–L254json_path: calibration_snapshot.groups.arena_geometry.attrssnapshot sha256 36b589f184
        "attrs": {
          "arena_geometry_source": "active_camera_calibration",
          "arena_origin_in_canvas_x_px": 1012.0,
          "arena_origin_in_canvas_y_px": 156.0,
          "arena_region_center_in_canvas_x_px": 1188.0,
          "arena_region_center_in_canvas_y_px": 332.0,
          "arena_region_height_px": 352.0,
          "arena_region_width_px": 352.0,
          "coordinate_frame": "arena_relative_canvas_px",
          "coordinate_origin": "top_left_of_active_arena",
          "experimental_area_center_x_px": 173.0,
          "experimental_area_center_y_px": 173.0,
          "experimental_area_corner_radius_mm": 11.802724838256836,
          "experimental_area_corner_radius_px": 49.67599868774414,
          "experimental_area_height_mm": 80.0,
          "experimental_area_height_px": 336.70867919921875,
          "experimental_area_radius_mm": 40.0,
          "experimental_area_radius_px": 168.35433959960938,
          "experimental_area_shape": "CIRCLE",
          "experimental_area_width_mm": 80.0,
          "experimental_area_width_px": 336.70867919921875,
          "x_axis_direction": "right",
          "y_axis_direction": "down"
        }
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_8b4b3225db87e46aed185e1358300c839157a13d2d87c6a2ee1d2eb88668221b_redbat.calibration_snapshot.jsonL61–L79json_path: calibration_snapshot.groups.2010095.attrssnapshot sha256 36b589f184
        "attrs": {
          "camera_view_down_in_canvas_angle_deg": -90.0,
          "camera_view_down_in_canvas_x": 0.0,
          "camera_view_down_in_canvas_y": -1.0,
          "camera_view_right_in_canvas_angle_deg": -0.37119460105895996,
          "camera_view_right_in_canvas_x": 0.9999789595603943,
          "camera_view_right_in_canvas_y": -0.006478522438555956,
          "direction_mapping_canvas_angle_convention": "0=right,90=down",
          "direction_mapping_handedness": "mirrored",
          "direction_mapping_ref_camera_x": 2235.46533203125,
          "direction_mapping_ref_camera_y": 2230.24267578125,
          "direction_mapping_ref_canvas_x": 1178.0,
          "direction_mapping_ref_canvas_y": 335.0,
          "direction_mapping_scale_x": 0.0800798088312149,
          "direction_mapping_scale_y": 0.080413818359375,
          "pixels_per_mm_camera": 52.43170166015625,
          "pixels_per_mm_projector": 4.208858489990234,
          "real_world_ref_mm": 5.0
        },
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_8b4b3225db87e46aed185e1358300c839157a13d2d87c6a2ee1d2eb88668221b_redbat.calibration_snapshot.jsonL132–L184json_path: calibration_snapshot.groups.2010095.datasets.homography_matrixsnapshot sha256 36b589f184
          "homography_matrix": {
            "attrs": {
              "arena_config_name": "arena_3",
              "axes": "x_right_y_down",
              "camera_id": "2010095",
              "canvas_name": "shadow",
              "coordinate_origin": "top_left",
              "dest_frame": "final_display_canvas_px",
              "homography_artifact_checksum_algorithm": "fnv1a64",
              "homography_artifact_checksum_fnv1a64": "e033634141030c5d",
              "homography_artifact_exists": "true",
              "homography_artifact_filename": "homography_arena_3_2010095.yml",
              "homography_artifact_mtime_unix_ns": 1786323758071198160,
              "homography_artifact_path": "/home/jeremy/citrus/targets/rigs/omnifin0/shadow/calibration_artifacts/homography_arena_3_2010095.yml",
              "homography_artifact_size_bytes": 1296,
              "homography_fit_inlier_count": 81,
              "homography_fit_max_reprojection_error_px": 0.2105458119777816,
              "homography_fit_point_count": 81,
              "homography_fit_quality_available": "true",
              "homography_fit_quality_schema": "citrus.homography_fit_quality.v1",
              "homography_fit_rms_reprojection_error_px": 0.11368694460021456,
              "homography_payload_source": "runtime_arena_config.homography_matrix",
              "homography_provenance_schema": "citrus.homography_provenance.v1",
              "image_space": "raw",
              "rig_id": "omnifin0",
              "runtime_matrix_checksum_algorithm": "sha256",
              "runtime_matrix_checksum_serialization": "json_row_major_float64_v1",
              "runtime_matrix_checksum_sha256": "sha256:309025e02d4179828304bb2deb0f18c63cdf71c6839c2048cb7c458298bc187a",
              "source_frame": "camera_view_px"
            },
            "dtype": "float64",
            "shape": [
              3,
              3
            ],
            "value": [
              [
                0.0790050268403753,
                -0.0006674120271375568,
                999.5425773946706
              ],
              [
                -0.0007720151510413161,
                -0.0803815604980243,
                515.0483915388191
              ],
              [
                -7.216399587149153e-07,
                -5.452778809885962e-07,
                1.0
              ]
            ]
          },
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_59e779ca13bc9eaa4c11195d0b79ffe6fb3653e854a0d3bc93f95cd166b55b81_redbat.calibration_snapshot.jsonL231–L254json_path: calibration_snapshot.groups.arena_geometry.attrssnapshot sha256 816ed7c105
        "attrs": {
          "arena_geometry_source": "active_camera_calibration",
          "arena_origin_in_canvas_x_px": 1480.0,
          "arena_origin_in_canvas_y_px": 152.0,
          "arena_region_center_in_canvas_x_px": 1657.0,
          "arena_region_center_in_canvas_y_px": 329.0,
          "arena_region_height_px": 354.0,
          "arena_region_width_px": 354.0,
          "coordinate_frame": "arena_relative_canvas_px",
          "coordinate_origin": "top_left_of_active_arena",
          "experimental_area_center_x_px": 175.0,
          "experimental_area_center_y_px": 175.0,
          "experimental_area_corner_radius_mm": 11.901163101196289,
          "experimental_area_corner_radius_px": 50.36293411254883,
          "experimental_area_height_mm": 80.0,
          "experimental_area_height_px": 338.541259765625,
          "experimental_area_radius_mm": 40.0,
          "experimental_area_radius_px": 169.2706298828125,
          "experimental_area_shape": "CIRCLE",
          "experimental_area_width_mm": 80.0,
          "experimental_area_width_px": 338.541259765625,
          "x_axis_direction": "right",
          "y_axis_direction": "down"
        }
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_59e779ca13bc9eaa4c11195d0b79ffe6fb3653e854a0d3bc93f95cd166b55b81_redbat.calibration_snapshot.jsonL61–L79json_path: calibration_snapshot.groups.2010096.attrssnapshot sha256 816ed7c105
        "attrs": {
          "camera_view_down_in_canvas_angle_deg": -89.91357421875,
          "camera_view_down_in_canvas_x": 0.0015082939062267542,
          "camera_view_down_in_canvas_y": -0.9999988675117493,
          "camera_view_right_in_canvas_angle_deg": -0.47601136565208435,
          "camera_view_right_in_canvas_x": 0.9999655485153198,
          "camera_view_right_in_canvas_y": -0.008307870477437973,
          "direction_mapping_canvas_angle_convention": "0=right,90=down",
          "direction_mapping_handedness": "mirrored",
          "direction_mapping_ref_camera_x": 2251.96240234375,
          "direction_mapping_ref_camera_y": 2247.328369140625,
          "direction_mapping_ref_canvas_x": 1654.0,
          "direction_mapping_ref_canvas_y": 322.0,
          "direction_mapping_scale_x": 0.08081333339214325,
          "direction_mapping_scale_y": 0.08093270659446716,
          "pixels_per_mm_camera": 52.31583786010742,
          "pixels_per_mm_projector": 4.2317657470703125,
          "real_world_ref_mm": 5.0
        },
data · omnifin0-shadow-session-2026-08-25/home/jeremy/orange_data/exp/unsorted/2026_08_25_23_12_06/citrus/citsess_59e779ca13bc9eaa4c11195d0b79ffe6fb3653e854a0d3bc93f95cd166b55b81_redbat.calibration_snapshot.jsonL132–L184json_path: calibration_snapshot.groups.2010096.datasets.homography_matrixsnapshot sha256 816ed7c105
          "homography_matrix": {
            "attrs": {
              "arena_config_name": "arena_4",
              "axes": "x_right_y_down",
              "camera_id": "2010096",
              "canvas_name": "shadow",
              "coordinate_origin": "top_left",
              "dest_frame": "final_display_canvas_px",
              "homography_artifact_checksum_algorithm": "fnv1a64",
              "homography_artifact_checksum_fnv1a64": "f82c955660f61ee8",
              "homography_artifact_exists": "true",
              "homography_artifact_filename": "homography_arena_4_2010096.yml",
              "homography_artifact_mtime_unix_ns": 1786323758083198159,
              "homography_artifact_path": "/home/jeremy/citrus/targets/rigs/omnifin0/shadow/calibration_artifacts/homography_arena_4_2010096.yml",
              "homography_artifact_size_bytes": 1296,
              "homography_fit_inlier_count": 81,
              "homography_fit_max_reprojection_error_px": 0.26431522471725416,
              "homography_fit_point_count": 81,
              "homography_fit_quality_available": "true",
              "homography_fit_quality_schema": "citrus.homography_fit_quality.v1",
              "homography_fit_rms_reprojection_error_px": 0.12366124769422783,
              "homography_payload_source": "runtime_arena_config.homography_matrix",
              "homography_provenance_schema": "citrus.homography_provenance.v1",
              "image_space": "raw",
              "rig_id": "omnifin0",
              "runtime_matrix_checksum_algorithm": "sha256",
              "runtime_matrix_checksum_serialization": "json_row_major_float64_v1",
              "runtime_matrix_checksum_sha256": "sha256:8e4e0b0739547dc6901e0e6f5fe6757f65716e146f2644e660fb72de78e40637",
              "source_frame": "camera_view_px"
            },
            "dtype": "float64",
            "shape": [
              3,
              3
            ],
            "value": [
              [
                0.07941404014474829,
                -0.00035544630587712374,
                1472.08739971268
              ],
              [
                -0.0009073157076933863,
                -0.08084017897724494,
                504.9634861168206
              ],
              [
                -7.445148038954546e-07,
                -2.9615715082307834e-07,
                1.0
              ]
            ]
          },
final_display_canvas_px — canvas_name=shadow, canvas_width_px=1920, canvas_height_px=1080solid = arena region (arena_center ± arena_size/2) · filled = experimental area · + = experimental_area_centerdashed = camera native frame corners pushed through homography_matrix+x+y(0,0)arena_1 origin (90, 164)camera_id=2010093arena_2 origin (551, 159)camera_id=2010094arena_3 origin (1012, 156)camera_id=2010095arena_4 origin (1480, 152)camera_id=2010096camera axes in final_display_canvas_px — camera_view_right/down_in_canvas_* drawn at direction_mapping_ref_canvas_*camera +xcamera +y2010093 · handedness=mirroredcamera +xcamera +y2010094 · handedness=mirroredcamera +xcamera +y2010095 · handedness=mirroredcamera +xcamera +y2010096 · handedness=mirroredcamera_view_px — arena region and experimental area mapped through inverse(H_camera_to_display)2010093 · arena_1 · 4512×4512exp. center → (2279.8, 2182.1)2010094 · arena_2 · 4512×4512exp. center → (2297.9, 2311.0)2010095 · arena_3 · 4512×4512exp. center → (2322.9, 2304.3)2010096 · arena_4 · 4512×4512exp. center → (2264.4, 2185.4)
arena_idcamera_idarena_center_x_px, arena_center_y_pxarena_width_px × arena_height_pxarena origin
arena_origin_in_canvas_x_px, _y_px (as written)
experimental_area_center_x_px, _y_px
arena_relative_canvas_px
same point in final_display_canvas_px
origin + center
same point in camera_view_px
inverse(H) · p_display
pixels_per_mm_camerapixels_per_mm_projector
arena_12010093(264, 338)348 × 348(90, 164)(173, 173)(263, 337)(2279.8, 2182.1)52.764.184
arena_22010094(726, 334)350 × 350(551, 159)(174, 174)(725, 333)(2297.9, 2311.0)52.594.196
arena_32010095(1188, 332)352 × 352(1012, 156)(173, 173)(1185, 329)(2322.9, 2304.3)52.434.209
arena_42010096(1657, 329)354 × 354(1480, 152)(175, 175)(1655, 327)(2264.4, 2185.4)52.324.232
Derived automatically from shadow-session-arena-1-geometry (omnifin0-shadow-session-2026-08-25: citsess_45fac0f71f098cf00f44065893c773b7847ab224edba618cb2a1c9744731065e_redbat.calibration_snapshot.json), shadow-session-arena-2-geometry (omnifin0-shadow-session-2026-08-25: citsess_dfa15a4b01f45041f60d8bc571389db77000fa9c8d030d300b76f4636f89d021_redbat.calibration_snapshot.json), shadow-session-arena-3-geometry (omnifin0-shadow-session-2026-08-25: citsess_8b4b3225db87e46aed185e1358300c839157a13d2d87c6a2ee1d2eb88668221b_redbat.calibration_snapshot.json), shadow-session-arena-4-geometry (omnifin0-shadow-session-2026-08-25: citsess_59e779ca13bc9eaa4c11195d0b79ffe6fb3653e854a0d3bc93f95cd166b55b81_redbat.calibration_snapshot.json).

Canvas blindfish

data · omnifin0-blindfish/home/jeremy/citrus/targets/rigs/omnifin0/blindfish/blindfish.jsonL536–L538lines: [536, 538]snapshot sha256 1229038c83
    "canvas_height_px": 1080,
    "canvas_name": "blindfish",
    "canvas_width_px": 1920,
data · omnifin0-blindfish/home/jeremy/citrus/targets/rigs/omnifin0/blindfish/blindfish.jsonL3–L73lines: [3, 73]snapshot sha256 1229038c83
        "arena_1": {
            "active_camera_id": "2010093",
            "arena_region_height_mm": 83.17204284667969,
            "arena_region_width_mm": 83.17204284667969,
            "calculated_z_eff_mm": 16.731597900390625,
            "calibration_pattern_mask_policy": "full_arena_unmasked",
            "calibration_pattern_mode": "rectangular_grid",
            "calibration_ring_chirality_marker_radius_scale": 1.2999999523162842,
            "calibration_ring_count": 4,
            "calibration_ring_dots_inner": 8,
            "calibration_ring_dots_outer": 32,
            "calibration_ring_include_center_dot": true,
            "calibration_ring_inner_radius_px": 40.0,
            "calibration_ring_orientation_marker_angle_deg": 0.0,
            "calibration_ring_orientation_marker_radius_scale": 1.600000023841858,
            "calibration_ring_outer_radius_px": 140.0,
            "calibration_ring_show_chirality_marker": false,
            "calibration_ring_show_orientation_marker": true,
            "calibration_verification_include_center_dot": true,
            "calibration_verification_inner_angle_offset_deg": 45.0,
            "calibration_verification_inner_point_count": 4,
            "calibration_verification_inner_radius_fraction": 0.3799999952316284,
            "calibration_verification_outer_angle_offset_deg": 22.5,
            "calibration_verification_outer_point_count": 8,
            "calibration_verification_outer_radius_fraction": 0.7799999713897705,
            "camera_calibrations": [
                {
                    "arena_center_x_px": 259,
                    "arena_center_y_px": 332,
                    "arena_height_px": 348,
                    "arena_width_px": 348,
                    "camera_id": "2010093",
                    "homography_detector_strategy": "",
                    "homography_fit_inlier_count": 0,
                    "homography_fit_max_reprojection_error_px": 0.0,
                    "homography_fit_method": "",
                    "homography_fit_point_count": 0,
                    "homography_fit_quality_available": false,
                    "homography_fit_rms_reprojection_error_px": 0.0,
                    "homography_image_path": "Cam2010093_arena1_homography.png",
                    "native_height_px": 4512,
                    "native_width_px": 4512,
                    "pixels_per_mm_camera": 52.75507354736328,
                    "pixels_per_mm_projector": 4.184098243713379,
                    "real_world_ref_mm": 5.0,
                    "scale_image_path": "Cam2010093_arena1_scale.png",
                    "scale_models": [
                        {
                            "clicked_points_camera_px": [
                                {
                                    "x": -1.0,
                                    "y": -1.0
                                },
                                {
                                    "x": -1.0,
                                    "y": -1.0
                                }
                            ],
                            "kind": "projective_local_jacobian_at_target_origin",
                            "notes": "Loaded from immutable active projected-surface scale pointer",
                            "pixels_per_mm_camera": 52.75507354736328,
                            "pixels_per_mm_projector": 4.184098243713379,
                            "real_world_ref_mm": 5.0,
                            "scale_image_path": "/home/jeremy/orange_data/calibrations/sessions/calsess_2026_08_10T01_53_54Z_shadow/artifacts/Cam2010093_arena_1/scale_observations/calgrp_2026_08_10T01_53_53Z_shadow_projected_surface_scale_calibration_projected_surface_g1/observation.json",
                            "status": "accepted",
                            "target_plane": "projected_surface"
                        }
                    ]
                }
            ],
            "config_name": "arena_1",
data · omnifin0-blindfish/home/jeremy/citrus/targets/rigs/omnifin0/blindfish/calibration_artifacts/homography_arena_1_2010093.ymlL1–L11lines: allsnapshot sha256 0b1c0bccd1
%YAML:1.0
---
calibration_timestamp_utc: "2026-04-20T13:53:09Z"
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.081190495253022743, -5.8629227576639497e-05,
       78.016343251990492, -6.615283571743281e-05, 0.08208723961407946,
       145.61594704306023, -1.4179512772275522e-06,
       8.1150695283403544e-07, 1. ]
data · omnifin0-blindfish/home/jeremy/citrus/targets/rigs/omnifin0/blindfish/calibration_artifacts/homography_arena_2_2010094.ymlL1–L11lines: allsnapshot sha256 e00bab2d53
%YAML:1.0
---
calibration_timestamp_utc: "2026-04-20T13:41:24Z"
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.080939913970578259, -0.00058851917693874514,
       546.50709930182188, 7.4047431215621808e-05, 0.081426285214485819,
       150.95990421765154, -5.597081221294883e-07,
       -6.291259115207835e-07, 1. ]
data · omnifin0-blindfish/home/jeremy/citrus/targets/rigs/omnifin0/blindfish/calibration_artifacts/homography_arena_3_2010095.ymlL1–L11lines: allsnapshot sha256 cfaf7a2587
%YAML:1.0
---
calibration_timestamp_utc: "2026-04-20T13:31:54Z"
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.081830026421205487, 0.00027137249386961022,
       1000.2767635708324, 0.00030792815696202162, 0.08237628931575354,
       144.9714247530712, -3.0271908617221341e-07,
       3.5077112303254264e-07, 1. ]
data · omnifin0-blindfish/home/jeremy/citrus/targets/rigs/omnifin0/blindfish/calibration_artifacts/homography_arena_4_2010096.ymlL1–L11lines: allsnapshot sha256 5965502a4c
%YAML:1.0
---
calibration_timestamp_utc: "2026-04-20T13:22:29Z"
homography_matrix: !!opencv-matrix
   rows: 3
   cols: 3
   dt: d
   data: [ 0.082441037531989614, 0.0023408737657269587,
       1453.1024204077116, 0.00043275717954872499, 0.083561105698659363,
       136.43324401838655, -7.7015124750487782e-07,
       1.696994773321424e-06, 1. ]
final_display_canvas_px — canvas_name=blindfish, canvas_width_px=1920, canvas_height_px=1080solid = arena region (arena_center ± arena_size/2) · filled = experimental area · + = experimental_area_centerdashed = camera native frame corners pushed through homography_matrix+x+y(0,0)arena_1 origin (85, 158)camera_id=2010093arena_2 origin (546, 162)camera_id=2010094arena_3 origin (1002, 159)camera_id=2010095arena_4 origin (1477, 145)camera_id=2010096camera_view_px — arena region and experimental area mapped through inverse(H_camera_to_display)2010093 · arena_1 · 4512×4512exp. center → (2251.0, 2242.5)2010094 · arena_2 · 4512×4512exp. center → (2161.1, 2210.8)2010095 · arena_3 · 4512×4512exp. center → (2214.9, 2214.0)2010096 · arena_4 · 4512×4512exp. center → (2351.9, 2155.7)
arena_idcamera_idarena_center_x_px, arena_center_y_pxarena_width_px × arena_height_pxarena origin
center − size/2
experimental_area_center_x_px, _y_px
arena_relative_canvas_px
same point in final_display_canvas_px
origin + center
same point in camera_view_px
inverse(H) · p_display
pixels_per_mm_camerapixels_per_mm_projector
arena_12010093(259, 332)348 × 348(85, 158)(176, 172)(261, 330)(2251.0, 2242.5)52.764.184
arena_22010094(721, 337)350 × 350(546, 162)(176, 170)(722, 332)(2161.1, 2210.8)52.594.196
arena_32010095(1178, 335)352 × 352(1002, 159)(180, 169)(1182, 328)(2214.9, 2214.0)52.434.209
arena_42010096(1654, 322)354 × 354(1477, 145)(172, 172)(1649, 317)(2351.9, 2155.7)52.324.232
Derived automatically from blindfish-arena-1 (omnifin0-blindfish: blindfish.json), blindfish-h-2010093 (omnifin0-blindfish: calibration_artifacts/homography_arena_1_2010093.yml), blindfish-h-2010094 (omnifin0-blindfish: calibration_artifacts/homography_arena_2_2010094.yml), blindfish-h-2010095 (omnifin0-blindfish: calibration_artifacts/homography_arena_3_2010095.yml), blindfish-h-2010096 (omnifin0-blindfish: calibration_artifacts/homography_arena_4_2010096.yml).