lab-camera-optimizer 1.0.0__tar.gz → 1.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lab_camera_optimizer-1.0.2/MANIFEST.in +4 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/PKG-INFO +17 -29
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/README.md +15 -4
- lab_camera_optimizer-1.0.2/configs/T_zone_direction_change.yaml +200 -0
- lab_camera_optimizer-1.0.2/configs/example_real_world.yaml +270 -0
- lab_camera_optimizer-1.0.2/configs/example_simple.yaml +180 -0
- lab_camera_optimizer-1.0.2/configs/labo_CHU.yaml +268 -0
- lab_camera_optimizer-1.0.2/init_project.py +87 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/lab_camera_optimizer.egg-info/PKG-INFO +17 -29
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/lab_camera_optimizer.egg-info/SOURCES.txt +8 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/lab_camera_optimizer.egg-info/entry_points.txt +1 -0
- lab_camera_optimizer-1.0.2/lab_camera_optimizer.egg-info/top_level.txt +4 -0
- lab_camera_optimizer-1.0.2/optimize.py +510 -0
- lab_camera_optimizer-1.0.2/preview_room.py +472 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/pyproject.toml +7 -3
- lab_camera_optimizer-1.0.0/lab_camera_optimizer.egg-info/top_level.txt +0 -1
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/LICENSE +0 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/core/__init__.py +0 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/core/candidates.py +0 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/core/config_loader.py +0 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/core/greedy.py +0 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/core/room.py +0 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/core/scoring.py +0 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/core/visualize.py +0 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/lab_camera_optimizer.egg-info/dependency_links.txt +0 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/lab_camera_optimizer.egg-info/requires.txt +0 -0
- {lab_camera_optimizer-1.0.0 → lab_camera_optimizer-1.0.2}/setup.cfg +0 -0
|
@@ -1,31 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lab-camera-optimizer
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Automated camera placement optimiser for markerless biomechanics motion capture labs
|
|
5
5
|
Author: Florian Delaplace
|
|
6
|
-
License: MIT
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2026 Florian Delaplace
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
-
in the Software without restriction, including without limitation the rights
|
|
13
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
-
furnished to do so, subject to the following conditions:
|
|
16
|
-
|
|
17
|
-
The above copyright notice and this permission notice shall be included in all
|
|
18
|
-
copies or substantial portions of the Software.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
-
SOFTWARE.
|
|
27
|
-
|
|
28
|
-
|
|
6
|
+
License-Expression: MIT
|
|
29
7
|
Project-URL: Homepage, https://github.com/flodelaplace/lab-camera-optimizer
|
|
30
8
|
Project-URL: Repository, https://github.com/flodelaplace/lab-camera-optimizer
|
|
31
9
|
Project-URL: Documentation, https://github.com/flodelaplace/lab-camera-optimizer/blob/main/ALGORITHM.md
|
|
@@ -35,7 +13,6 @@ Classifier: Programming Language :: Python :: 3
|
|
|
35
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
36
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
37
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
38
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
39
16
|
Classifier: Operating System :: OS Independent
|
|
40
17
|
Classifier: Topic :: Scientific/Engineering
|
|
41
18
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -56,6 +33,8 @@ Given a room layout, a set of cameras and a capture zone, this tool finds the
|
|
|
56
33
|
configuration that maximises 3D body coverage (head-to-toe visibility) across
|
|
57
34
|
all evaluation points, with optional bilateral coverage constraints.
|
|
58
35
|
|
|
36
|
+

|
|
37
|
+
|
|
59
38
|
---
|
|
60
39
|
|
|
61
40
|
## Features
|
|
@@ -92,14 +71,23 @@ all evaluation points, with optional bilateral coverage constraints.
|
|
|
92
71
|
### Option A — pip install (recommended)
|
|
93
72
|
|
|
94
73
|
```bash
|
|
95
|
-
pip install
|
|
74
|
+
pip install lab-camera-optimizer
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then initialise a working directory with the example configs:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
mkdir my-lab && cd my-lab
|
|
81
|
+
lab-camera-init
|
|
96
82
|
```
|
|
97
83
|
|
|
98
|
-
|
|
84
|
+
This copies the example YAML configs into `configs/` and creates the `outputs/`
|
|
85
|
+
folder in your current directory. Three commands are then available:
|
|
99
86
|
|
|
100
87
|
```bash
|
|
101
|
-
lab-camera-
|
|
102
|
-
lab-camera-preview --config
|
|
88
|
+
lab-camera-init # copy example configs (run once)
|
|
89
|
+
lab-camera-preview --config configs/example_simple.yaml
|
|
90
|
+
lab-camera-optimizer --config configs/example_simple.yaml
|
|
103
91
|
```
|
|
104
92
|
|
|
105
93
|
### Option B — Clone and run locally
|
|
@@ -6,6 +6,8 @@ Given a room layout, a set of cameras and a capture zone, this tool finds the
|
|
|
6
6
|
configuration that maximises 3D body coverage (head-to-toe visibility) across
|
|
7
7
|
all evaluation points, with optional bilateral coverage constraints.
|
|
8
8
|
|
|
9
|
+

|
|
10
|
+
|
|
9
11
|
---
|
|
10
12
|
|
|
11
13
|
## Features
|
|
@@ -42,14 +44,23 @@ all evaluation points, with optional bilateral coverage constraints.
|
|
|
42
44
|
### Option A — pip install (recommended)
|
|
43
45
|
|
|
44
46
|
```bash
|
|
45
|
-
pip install
|
|
47
|
+
pip install lab-camera-optimizer
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then initialise a working directory with the example configs:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
mkdir my-lab && cd my-lab
|
|
54
|
+
lab-camera-init
|
|
46
55
|
```
|
|
47
56
|
|
|
48
|
-
|
|
57
|
+
This copies the example YAML configs into `configs/` and creates the `outputs/`
|
|
58
|
+
folder in your current directory. Three commands are then available:
|
|
49
59
|
|
|
50
60
|
```bash
|
|
51
|
-
lab-camera-
|
|
52
|
-
lab-camera-preview --config
|
|
61
|
+
lab-camera-init # copy example configs (run once)
|
|
62
|
+
lab-camera-preview --config configs/example_simple.yaml
|
|
63
|
+
lab-camera-optimizer --config configs/example_simple.yaml
|
|
53
64
|
```
|
|
54
65
|
|
|
55
66
|
### Option B — Clone and run locally
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# =============================================================
|
|
2
|
+
# Lab Camera Optimizer — T-shaped capture zone
|
|
3
|
+
# Use case: approach run + 90° direction change at the END
|
|
4
|
+
#
|
|
5
|
+
# Room : 12m × 7m rectangle with a small structural pillar
|
|
6
|
+
# Cameras: 10 wall-mounted cameras at 2.0m or 2.2m height
|
|
7
|
+
#
|
|
8
|
+
# T-shape (RELATIVE coordinates, junction at the end of the run):
|
|
9
|
+
#
|
|
10
|
+
# [8,-1.5]──[10,-1.5]
|
|
11
|
+
# │ left │ ← turn left priority 1.8
|
|
12
|
+
# │ turn │
|
|
13
|
+
# [0,0]──────────────────[8, 0]──[10, 0]
|
|
14
|
+
# │ approach (8m×1m) │ inter │ priority 1.0 / 2.5
|
|
15
|
+
# [0,1]──────────────────[8, 1]──[10, 1]
|
|
16
|
+
# │ right │ ← turn right priority 1.8
|
|
17
|
+
# │ turn │
|
|
18
|
+
# [8, 2.5]──[10,2.5]
|
|
19
|
+
#
|
|
20
|
+
# Approach arm : X=0→8, Y=0→1 (8m long × 1m wide)
|
|
21
|
+
# Turn arm : X=8→10, Y=-1.5→2.5 (2m wide × 4m total)
|
|
22
|
+
# Intersection : X=8→10, Y=0→1 (2m × 1m — end of run)
|
|
23
|
+
#
|
|
24
|
+
# The whole T translates via x_offsets / y_offsets.
|
|
25
|
+
# =============================================================
|
|
26
|
+
|
|
27
|
+
# ─────────────────────────────────────────────────────────────
|
|
28
|
+
# 1. ROOM
|
|
29
|
+
# ─────────────────────────────────────────────────────────────
|
|
30
|
+
room:
|
|
31
|
+
corners:
|
|
32
|
+
- [0, 0]
|
|
33
|
+
- [12, 0]
|
|
34
|
+
- [12, 7]
|
|
35
|
+
- [0, 7]
|
|
36
|
+
height: 3.0
|
|
37
|
+
|
|
38
|
+
# ─────────────────────────────────────────────────────────────
|
|
39
|
+
# 2. OBSTACLES
|
|
40
|
+
# ─────────────────────────────────────────────────────────────
|
|
41
|
+
obstacles:
|
|
42
|
+
- type: rect
|
|
43
|
+
bounds: [5.8, 0.0, 6.1, 0.3]
|
|
44
|
+
height: 3.0
|
|
45
|
+
label: "Pillar"
|
|
46
|
+
can_mount_camera: false
|
|
47
|
+
|
|
48
|
+
# ─────────────────────────────────────────────────────────────
|
|
49
|
+
# 3. SUBJECT
|
|
50
|
+
# ─────────────────────────────────────────────────────────────
|
|
51
|
+
subject:
|
|
52
|
+
height: 1.9
|
|
53
|
+
foot_z: 0.0
|
|
54
|
+
|
|
55
|
+
# ─────────────────────────────────────────────────────────────
|
|
56
|
+
# 4. CAMERA SETS
|
|
57
|
+
# ─────────────────────────────────────────────────────────────
|
|
58
|
+
camera_sets:
|
|
59
|
+
|
|
60
|
+
- id: "cam_A"
|
|
61
|
+
name: "Wall camera"
|
|
62
|
+
mounting: "wall"
|
|
63
|
+
optional: false
|
|
64
|
+
fov_h_landscape: 110.0
|
|
65
|
+
fov_v_landscape: 70.0
|
|
66
|
+
fov_h_portrait: 70.0
|
|
67
|
+
fov_v_portrait: 110.0
|
|
68
|
+
max_range: 12.0
|
|
69
|
+
min_range: 0.5
|
|
70
|
+
height_options: [2.0, 2.2]
|
|
71
|
+
max_count: 10
|
|
72
|
+
min_spacing: 1.2
|
|
73
|
+
score_weight: 1.0
|
|
74
|
+
color: "#1f77b4"
|
|
75
|
+
|
|
76
|
+
- id: "cam_B"
|
|
77
|
+
name: "Tripod camera"
|
|
78
|
+
mounting: "tripod"
|
|
79
|
+
optional: true
|
|
80
|
+
fov_h_landscape: 80.0
|
|
81
|
+
fov_v_landscape: 58.0
|
|
82
|
+
fov_h_portrait: 58.0
|
|
83
|
+
fov_v_portrait: 80.0
|
|
84
|
+
max_range: 8.0
|
|
85
|
+
min_range: 0.5
|
|
86
|
+
height_options: [1.5]
|
|
87
|
+
max_count: 0 # disabled — set > 0 to re-enable
|
|
88
|
+
min_spacing: 1.5
|
|
89
|
+
walk_axis_margin: 0.7
|
|
90
|
+
score_weight: 0.6
|
|
91
|
+
color: "#d62728"
|
|
92
|
+
|
|
93
|
+
# ─────────────────────────────────────────────────────────────
|
|
94
|
+
# 5. CAPTURE ZONES
|
|
95
|
+
# ─────────────────────────────────────────────────────────────
|
|
96
|
+
#
|
|
97
|
+
# All coordinates are RELATIVE.
|
|
98
|
+
# The entire T moves together via the shared x_offsets / y_offsets.
|
|
99
|
+
#
|
|
100
|
+
# Visual (relative coords):
|
|
101
|
+
#
|
|
102
|
+
# [8,-1.5]──[10,-1.5]
|
|
103
|
+
# │ LEFT │
|
|
104
|
+
# │ TURN │ priority 1.8
|
|
105
|
+
# [0,0]──────────────────[8, 0 ]──[10, 0]
|
|
106
|
+
# │ ║ INTER ║
|
|
107
|
+
# │ APPROACH (8m×1m) ║ 2.5 ║ priority 1.0
|
|
108
|
+
# │ ║ ║
|
|
109
|
+
# [0,1]──────────────────[8, 1 ]──[10, 1]
|
|
110
|
+
# │ RIGHT │
|
|
111
|
+
# │ TURN │ priority 1.8
|
|
112
|
+
# [8, 2.5]──[10,2.5]
|
|
113
|
+
#
|
|
114
|
+
capture_zones:
|
|
115
|
+
|
|
116
|
+
# ── Level 1 : approach corridor (8m × 1m) ────────────────────────────
|
|
117
|
+
# The full run-up. Covered but not the priority focus.
|
|
118
|
+
- id: "approach"
|
|
119
|
+
type: "polygon"
|
|
120
|
+
priority: 1.0
|
|
121
|
+
grid_step: 0.30
|
|
122
|
+
vertices:
|
|
123
|
+
- [1.0, 0.0] # start bottom-left
|
|
124
|
+
- [7.0, 0.0] # end bottom-right (junction with turn arm)
|
|
125
|
+
- [7.0, 1.0] # end top-right
|
|
126
|
+
- [1.0, 1.0] # start top-left
|
|
127
|
+
placement:
|
|
128
|
+
# 3 X positions × 2 Y positions = 6 combos
|
|
129
|
+
# x_offsets : left end of approach at X = 0, 0.5, 1.0
|
|
130
|
+
# y_offsets : bottom of approach at Y = 2.5, 3.0
|
|
131
|
+
x_offsets: [0.0, 0.5, 1.0]
|
|
132
|
+
y_offsets: [2.5, 3.0]
|
|
133
|
+
|
|
134
|
+
# ── Level 2 : turn arm — left side (toward +Y, 2m × 1.5m) ───────────
|
|
135
|
+
# The portion of the perpendicular corridor ABOVE the approach axis.
|
|
136
|
+
# Subject turns left here.
|
|
137
|
+
- id: "turn_left"
|
|
138
|
+
type: "polygon"
|
|
139
|
+
priority: 1.8
|
|
140
|
+
grid_step: 0.25
|
|
141
|
+
vertices:
|
|
142
|
+
- [8.0, -1.5] # far end top-left
|
|
143
|
+
- [10.0, -1.5] # far end top-right
|
|
144
|
+
- [10.0, 0.0] # meets approach top (intersection boundary)
|
|
145
|
+
- [8.0, 0.0] # meets approach top (intersection boundary)
|
|
146
|
+
placement:
|
|
147
|
+
x_offsets: [0.0, 0.5, 1.0]
|
|
148
|
+
y_offsets: [2.5, 3.0]
|
|
149
|
+
|
|
150
|
+
# ── Level 2 : turn arm — right side (toward -Y, 2m × 1.5m) ──────────
|
|
151
|
+
# The portion of the perpendicular corridor BELOW the approach axis.
|
|
152
|
+
# Subject turns right here.
|
|
153
|
+
- id: "turn_right"
|
|
154
|
+
type: "polygon"
|
|
155
|
+
priority: 1.8
|
|
156
|
+
grid_step: 0.25
|
|
157
|
+
vertices:
|
|
158
|
+
- [8.0, 1.0] # meets approach bottom (intersection boundary)
|
|
159
|
+
- [10.0, 1.0] # meets approach bottom (intersection boundary)
|
|
160
|
+
- [10.0, 2.5] # far end bottom-right
|
|
161
|
+
- [8.0, 2.5] # far end bottom-left
|
|
162
|
+
placement:
|
|
163
|
+
x_offsets: [0.0, 0.5, 1.0]
|
|
164
|
+
y_offsets: [2.5, 3.0]
|
|
165
|
+
|
|
166
|
+
# ── Level 3 : intersection (2m × 1m) — maximum priority ──────────────
|
|
167
|
+
# Where the approach meets the turn arm. The direction change happens
|
|
168
|
+
# here — maximise camera coverage at all costs.
|
|
169
|
+
- id: "intersection"
|
|
170
|
+
type: "polygon"
|
|
171
|
+
priority: 2.5
|
|
172
|
+
grid_step: 0.15
|
|
173
|
+
vertices:
|
|
174
|
+
- [7.0, 0.0] # bottom-left (end of approach / top of right turn)
|
|
175
|
+
- [10.0, 0.0] # bottom-right
|
|
176
|
+
- [10.0, 1.0] # top-right
|
|
177
|
+
- [7.0, 1.0] # top-left
|
|
178
|
+
placement:
|
|
179
|
+
x_offsets: [0.0, 0.5, 1.0]
|
|
180
|
+
y_offsets: [2.5, 3.0]
|
|
181
|
+
|
|
182
|
+
# ─────────────────────────────────────────────────────────────
|
|
183
|
+
# 6. OPTIMISATION PARAMETERS
|
|
184
|
+
# ─────────────────────────────────────────────────────────────
|
|
185
|
+
optimization:
|
|
186
|
+
target_coverage: 5
|
|
187
|
+
bilateral_weight: 0.9
|
|
188
|
+
vertical_coverage_threshold: 0.9
|
|
189
|
+
restarts_per_combo: 15
|
|
190
|
+
wall_step: 0.35
|
|
191
|
+
angle_steps: 24
|
|
192
|
+
tripod_grid_step: 0.80
|
|
193
|
+
distance_quality_factor: 0.005
|
|
194
|
+
# algo options:
|
|
195
|
+
# greedy → pure greedy (fast, original behaviour)
|
|
196
|
+
# greedy_1opt → greedy init + 1-opt local search (better quality, recommended)
|
|
197
|
+
algo: "greedy_1opt"
|
|
198
|
+
early_stop: 5 # stop if no improvement after 5 consecutive restarts (0 = auto)
|
|
199
|
+
graph_mode: "all"
|
|
200
|
+
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# =============================================================
|
|
2
|
+
# Lab Camera Optimizer — Real-world example configuration
|
|
3
|
+
# L-shaped room with obstacles, two camera sets, corridor zones.
|
|
4
|
+
# Based on an actual biomechanics lab (13m × 4.7m, L-shaped).
|
|
5
|
+
# Use this as a starting point for complex lab geometries.
|
|
6
|
+
# =============================================================
|
|
7
|
+
# Units : metres for all distances, degrees for all angles.
|
|
8
|
+
# Angles follow the standard mathematical convention:
|
|
9
|
+
# 0° = East (+X), 90° = North (+Y), 180°/-180° = West (-X)
|
|
10
|
+
# =============================================================
|
|
11
|
+
|
|
12
|
+
# ─────────────────────────────────────────────────────────────
|
|
13
|
+
# 1. ROOM GEOMETRY
|
|
14
|
+
# ─────────────────────────────────────────────────────────────
|
|
15
|
+
room:
|
|
16
|
+
# List of (X, Y) vertices of the room polygon, in order (CW or CCW).
|
|
17
|
+
# The room is assumed to be a flat floor plan.
|
|
18
|
+
corners:
|
|
19
|
+
- [0, 0 ]
|
|
20
|
+
- [13, 0 ]
|
|
21
|
+
- [13, 3.0]
|
|
22
|
+
- [8, 3.0]
|
|
23
|
+
- [8, 4.7]
|
|
24
|
+
- [0, 4.7]
|
|
25
|
+
|
|
26
|
+
# Floor-to-ceiling height (metres)
|
|
27
|
+
height: 3.0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ─────────────────────────────────────────────────────────────
|
|
31
|
+
# 2. OBSTACLES
|
|
32
|
+
# ─────────────────────────────────────────────────────────────
|
|
33
|
+
# Two types supported:
|
|
34
|
+
#
|
|
35
|
+
# type: rect → axis-aligned rectangle
|
|
36
|
+
# bounds: [x_min, y_min, x_max, y_max]
|
|
37
|
+
#
|
|
38
|
+
# type: polygon → arbitrary polygon
|
|
39
|
+
# vertices: [[x0,y0],[x1,y1],...] (in order)
|
|
40
|
+
#
|
|
41
|
+
# height: floor-to-ceiling height of the obstacle (metres).
|
|
42
|
+
# = room.height → fully blocks line-of-sight (wall/pillar)
|
|
43
|
+
# < room.height → partial height (object, furniture)
|
|
44
|
+
# does NOT block line-of-sight above it
|
|
45
|
+
#
|
|
46
|
+
# can_mount_camera: true → wall cameras can be placed on this surface
|
|
47
|
+
# false (default) → no camera on this obstacle
|
|
48
|
+
|
|
49
|
+
obstacles:
|
|
50
|
+
- type: rect
|
|
51
|
+
bounds: [1.70, 0.00, 2.04, 0.34]
|
|
52
|
+
height: 3.0
|
|
53
|
+
label: "Stub 1"
|
|
54
|
+
can_mount_camera: true
|
|
55
|
+
|
|
56
|
+
- type: rect
|
|
57
|
+
bounds: [5.39, 0.00, 5.59, 0.42]
|
|
58
|
+
height: 3.0
|
|
59
|
+
label: "Stub 2"
|
|
60
|
+
can_mount_camera: true
|
|
61
|
+
|
|
62
|
+
- type: rect
|
|
63
|
+
bounds: [8.94, 0.00, 9.14, 0.42]
|
|
64
|
+
height: 3.0
|
|
65
|
+
label: "Stub 3"
|
|
66
|
+
can_mount_camera: true
|
|
67
|
+
|
|
68
|
+
- type: rect
|
|
69
|
+
bounds: [5.65, 3.85, 5.85, 4.25]
|
|
70
|
+
height: 3.0
|
|
71
|
+
label: "Stub 4"
|
|
72
|
+
can_mount_camera: true
|
|
73
|
+
|
|
74
|
+
- type: rect
|
|
75
|
+
bounds: [5.65, 2.85, 6.35, 3.55]
|
|
76
|
+
height: 1.4
|
|
77
|
+
label: "Object 1"
|
|
78
|
+
can_mount_camera: false # partial-height object, no camera mount
|
|
79
|
+
|
|
80
|
+
- type: polygon
|
|
81
|
+
vertices:
|
|
82
|
+
- [1.47, 4.70]
|
|
83
|
+
- [1.47, 4.38]
|
|
84
|
+
- [1.71, 4.38]
|
|
85
|
+
- [1.71, 3.88]
|
|
86
|
+
- [2.04, 3.88]
|
|
87
|
+
- [2.04, 4.27]
|
|
88
|
+
- [3.02, 4.27]
|
|
89
|
+
- [3.02, 4.70]
|
|
90
|
+
height: 3.0
|
|
91
|
+
label: "Wall N"
|
|
92
|
+
can_mount_camera: true
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ─────────────────────────────────────────────────────────────
|
|
96
|
+
# 3. SUBJECT (person being recorded)
|
|
97
|
+
# ─────────────────────────────────────────────────────────────
|
|
98
|
+
subject:
|
|
99
|
+
height: 1.9 # total height feet-to-head (metres)
|
|
100
|
+
foot_z: 0.0 # height of feet above floor (metres, usually 0)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# ─────────────────────────────────────────────────────────────
|
|
104
|
+
# 4. CAMERA SETS
|
|
105
|
+
# ─────────────────────────────────────────────────────────────
|
|
106
|
+
# Define as many camera sets as needed.
|
|
107
|
+
# Each set is an independent group of cameras with its own FOV,
|
|
108
|
+
# mounting type, placement constraints and score weight.
|
|
109
|
+
#
|
|
110
|
+
# mounting:
|
|
111
|
+
# wall → cameras placed on room walls and floor-to-ceiling obstacles
|
|
112
|
+
# tripod → cameras placed freely inside the room on a tripod stand
|
|
113
|
+
# ceiling → (future) cameras hung from the ceiling
|
|
114
|
+
#
|
|
115
|
+
# optional: true → this entire set is skipped if max_count is 0
|
|
116
|
+
|
|
117
|
+
camera_sets:
|
|
118
|
+
|
|
119
|
+
- id: "cam_A"
|
|
120
|
+
name: "ZED2i"
|
|
121
|
+
mounting: "wall"
|
|
122
|
+
optional: false
|
|
123
|
+
|
|
124
|
+
# Field of view (degrees) — landscape and portrait orientations
|
|
125
|
+
fov_h_landscape: 110.0
|
|
126
|
+
fov_v_landscape: 70.0
|
|
127
|
+
fov_h_portrait: 70.0
|
|
128
|
+
fov_v_portrait: 110.0
|
|
129
|
+
|
|
130
|
+
# Detection range (metres)
|
|
131
|
+
max_range: 15.0
|
|
132
|
+
min_range: 0.5 # blind zone closer than this
|
|
133
|
+
|
|
134
|
+
# Camera heights to evaluate (metres).
|
|
135
|
+
# Each candidate position is tested at ALL heights listed here.
|
|
136
|
+
# A single optimised configuration can mix different heights.
|
|
137
|
+
height_options: [2.0, 2.2]
|
|
138
|
+
|
|
139
|
+
# Placement constraints
|
|
140
|
+
max_count: 10 # maximum number of cameras to place
|
|
141
|
+
min_spacing: 1.5 # minimum distance between two cameras of this set (m)
|
|
142
|
+
|
|
143
|
+
# Score contribution
|
|
144
|
+
score_weight: 1.0 # relative weight vs other camera sets
|
|
145
|
+
|
|
146
|
+
# Visualisation
|
|
147
|
+
color: "#1f77b4" # hex colour for graphs
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
- id: "cam_B"
|
|
151
|
+
name: "iPad"
|
|
152
|
+
mounting: "tripod"
|
|
153
|
+
optional: true # set max_count: 0 to fully disable
|
|
154
|
+
|
|
155
|
+
fov_h_landscape: 80.0
|
|
156
|
+
fov_v_landscape: 58.0
|
|
157
|
+
fov_h_portrait: 58.0
|
|
158
|
+
fov_v_portrait: 80.0
|
|
159
|
+
|
|
160
|
+
max_range: 10.0
|
|
161
|
+
min_range: 0.5
|
|
162
|
+
|
|
163
|
+
height_options: [1.5]
|
|
164
|
+
|
|
165
|
+
max_count: 2
|
|
166
|
+
min_spacing: 1.5
|
|
167
|
+
|
|
168
|
+
# Tripod-specific: minimum distance from the capture axis (metres).
|
|
169
|
+
# Prevents the tripod from blocking the walking path.
|
|
170
|
+
walk_axis_margin: 0.7
|
|
171
|
+
|
|
172
|
+
score_weight: 0.6
|
|
173
|
+
|
|
174
|
+
color: "#d62728"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# ─────────────────────────────────────────────────────────────
|
|
178
|
+
# 5. CAPTURE ZONES
|
|
179
|
+
# ─────────────────────────────────────────────────────────────
|
|
180
|
+
# Zones define WHERE the cameras must provide coverage and HOW MUCH
|
|
181
|
+
# each zone matters (priority weight in the global score).
|
|
182
|
+
#
|
|
183
|
+
# Zone types:
|
|
184
|
+
# corridor → rectangular strip, explored at several X/Y positions
|
|
185
|
+
# sub_zone → rectangular strip contained within a parent corridor
|
|
186
|
+
# point → circular zone (e.g. a chair test)
|
|
187
|
+
#
|
|
188
|
+
# The optimiser tests all combinations of (x_start, y_position, sub_offset)
|
|
189
|
+
# and keeps the globally best placement.
|
|
190
|
+
|
|
191
|
+
capture_zones:
|
|
192
|
+
|
|
193
|
+
# ── Primary corridor (total walking path) ─────────────────
|
|
194
|
+
- id: "full_corridor"
|
|
195
|
+
type: "corridor"
|
|
196
|
+
priority: 0.5 # lower weight: coverage outside analysis is a bonus
|
|
197
|
+
|
|
198
|
+
length: 10.0 # length of the corridor (metres, along main axis)
|
|
199
|
+
width: 1.0 # transverse width of the evaluation strip (metres)
|
|
200
|
+
|
|
201
|
+
# Positions to explore during optimisation
|
|
202
|
+
placement:
|
|
203
|
+
# X start positions of the corridor to test (metres from room origin)
|
|
204
|
+
x_start_options: [1.0, 2.0]
|
|
205
|
+
# Transverse (Y) axis positions of the corridor centre to test
|
|
206
|
+
y_options: [1.0, 1.4, 1.8]
|
|
207
|
+
|
|
208
|
+
# ── Analysis zone (priority sub-zone inside the corridor) ──
|
|
209
|
+
- id: "analysis_zone"
|
|
210
|
+
type: "sub_zone"
|
|
211
|
+
priority: 1.0 # main zone: highest weight on coverage score
|
|
212
|
+
|
|
213
|
+
length: 6.0 # length of the priority sub-zone (metres)
|
|
214
|
+
contained_in: "full_corridor"
|
|
215
|
+
|
|
216
|
+
# Offsets (metres) from the corridor start where the sub-zone can begin.
|
|
217
|
+
# e.g. offset 1.0 → sub-zone starts 1m after corridor start (1m run-up)
|
|
218
|
+
# offset 3.0 → sub-zone starts 3m in (3m run-up, 1m deceleration)
|
|
219
|
+
offset_options: [1.0, 2.0, 3.0]
|
|
220
|
+
|
|
221
|
+
# ── STS point (sit-to-stand chair test — secondary priority zone) ──
|
|
222
|
+
- id: "sts_point"
|
|
223
|
+
type: "point"
|
|
224
|
+
priority: 2.0 # highest weight: this spot must be very well covered
|
|
225
|
+
|
|
226
|
+
radius: 0.6 # coverage circle radius (metres)
|
|
227
|
+
contained_in: "analysis_zone"
|
|
228
|
+
|
|
229
|
+
# If true, the optimiser searches automatically for the best position
|
|
230
|
+
# of this point inside the parent zone (maximises camera coverage score).
|
|
231
|
+
auto_optimize: true
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# ─────────────────────────────────────────────────────────────
|
|
235
|
+
# 6. OPTIMISATION PARAMETERS
|
|
236
|
+
# ─────────────────────────────────────────────────────────────
|
|
237
|
+
optimization:
|
|
238
|
+
|
|
239
|
+
# Number of cameras required per evaluation point to consider it "well covered"
|
|
240
|
+
target_coverage: 6
|
|
241
|
+
|
|
242
|
+
# Bilateral constraint weight [0.0 – 1.0]
|
|
243
|
+
# 0.0 = disabled (pure coverage maximisation)
|
|
244
|
+
# 1.0 = fully enforced (score 0 if only one side covered)
|
|
245
|
+
# Recommended: 0.6 – 0.8 for biomechanics labs
|
|
246
|
+
bilateral_weight: 0.9
|
|
247
|
+
|
|
248
|
+
# Fraction of the subject body (feet→head) that must be visible
|
|
249
|
+
# for a camera to "count" at an evaluation point.
|
|
250
|
+
vertical_coverage_threshold: 0.9
|
|
251
|
+
|
|
252
|
+
# Greedy optimisation restarts per combination of zone positions
|
|
253
|
+
restarts_per_combo: 10
|
|
254
|
+
|
|
255
|
+
# Candidate generation resolution
|
|
256
|
+
wall_step: 0.35 # spacing between camera positions along walls (m)
|
|
257
|
+
angle_steps: 24 # number of yaw angles tested per wall position
|
|
258
|
+
tripod_grid_step: 0.70 # spacing of the interior tripod grid (m)
|
|
259
|
+
distance_quality_factor: 0.001
|
|
260
|
+
|
|
261
|
+
# Distance quality decay: score multiplier = 1 / (1 + k * d²)
|
|
262
|
+
# Higher k → strong penalty for distant cameras
|
|
263
|
+
# k=0.01: 0.96 @ 2m | 0.80 @ 5m | 0.50 @ 10m
|
|
264
|
+
# Graph generation mode:
|
|
265
|
+
# all → save a graph for every optimisation attempt (verbose)
|
|
266
|
+
# records_only → save only when a new global record is set
|
|
267
|
+
# best_per_combo → save only the best result of each zone combination
|
|
268
|
+
algo: "greedy_1opt" # greedy | greedy_1opt
|
|
269
|
+
graph_mode: "all"
|
|
270
|
+
|