lab-camera-optimizer 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,416 @@
1
+ Metadata-Version: 2.4
2
+ Name: lab-camera-optimizer
3
+ Version: 1.0.0
4
+ Summary: Automated camera placement optimiser for markerless biomechanics motion capture labs
5
+ Author: Florian Delaplace
6
+ License: MIT License
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
+
29
+ Project-URL: Homepage, https://github.com/flodelaplace/lab-camera-optimizer
30
+ Project-URL: Repository, https://github.com/flodelaplace/lab-camera-optimizer
31
+ Project-URL: Documentation, https://github.com/flodelaplace/lab-camera-optimizer/blob/main/ALGORITHM.md
32
+ Project-URL: Bug Tracker, https://github.com/flodelaplace/lab-camera-optimizer/issues
33
+ Keywords: biomechanics,motion-capture,camera-placement,optimisation,markerless,line-of-sight
34
+ Classifier: Programming Language :: Python :: 3
35
+ Classifier: Programming Language :: Python :: 3.10
36
+ Classifier: Programming Language :: Python :: 3.11
37
+ Classifier: Programming Language :: Python :: 3.12
38
+ Classifier: License :: OSI Approved :: MIT License
39
+ Classifier: Operating System :: OS Independent
40
+ Classifier: Topic :: Scientific/Engineering
41
+ Classifier: Intended Audience :: Science/Research
42
+ Requires-Python: >=3.10
43
+ Description-Content-Type: text/markdown
44
+ License-File: LICENSE
45
+ Requires-Dist: numpy>=1.24
46
+ Requires-Dist: matplotlib>=3.7
47
+ Requires-Dist: pyyaml>=6.0
48
+ Requires-Dist: tqdm>=4.65
49
+ Dynamic: license-file
50
+
51
+ # Lab Camera Optimizer
52
+
53
+ **Optimal camera placement for markerless biomechanics motion capture labs.**
54
+
55
+ Given a room layout, a set of cameras and a capture zone, this tool finds the
56
+ configuration that maximises 3D body coverage (head-to-toe visibility) across
57
+ all evaluation points, with optional bilateral coverage constraints.
58
+
59
+ ---
60
+
61
+ ## Features
62
+
63
+ - **Any room shape** — define any polygon floor plan (L-shaped, rectangular, etc.)
64
+ - **Obstacles & walls** — pillars, partial-height furniture, irregular wall segments
65
+ - **Multiple camera sets** — wall-mounted cameras + optional tripod cameras
66
+ - **Per-camera height variation** — each camera in the same configuration can be at a different height
67
+ - **3D visibility** — checks both horizontal FOV and vertical body coverage (0 → subject height)
68
+ - **Line-of-sight** — occlusion by walls and floor-to-ceiling obstacles
69
+ - **Bilateral constraint** — ensures coverage from both sides of the capture axis (configurable weight)
70
+ - **Zone sweep** — automatically tests multiple corridor/polygon zone positions and finds the best layout
71
+ - **Room preview** — top-down visualisation of your room before running optimisation
72
+ - **YAML configuration** — no code editing required to adapt to your lab
73
+
74
+ > **Want to understand how it works?**
75
+ > See [ALGORITHM.md](ALGORITHM.md) for a full explanation of the scoring
76
+ > function, the greedy optimisation, the combo sweep and all tuning parameters.
77
+
78
+ ---
79
+
80
+ ## Preview
81
+
82
+ ### Room layout (`preview_room.py`)
83
+ ![Room preview](docs/example_preview.png)
84
+
85
+ ### Optimisation result
86
+ ![Optimisation result](docs/example_result.png)
87
+
88
+ ---
89
+
90
+ ## Installation
91
+
92
+ ### Option A — pip install (recommended)
93
+
94
+ ```bash
95
+ pip install git+https://github.com/flodelaplace/lab-camera-optimizer.git
96
+ ```
97
+
98
+ Once installed, two commands are available anywhere in your terminal:
99
+
100
+ ```bash
101
+ lab-camera-optimizer --config path/to/my_lab.yaml
102
+ lab-camera-preview --config path/to/my_lab.yaml
103
+ ```
104
+
105
+ ### Option B — Clone and run locally
106
+
107
+ ```bash
108
+ git clone https://github.com/flodelaplace/lab-camera-optimizer.git
109
+ cd lab-camera-optimizer
110
+ pip install -r requirements.txt
111
+ python optimize.py --config configs/example_simple.yaml
112
+ python preview_room.py --config configs/example_simple.yaml
113
+ ```
114
+
115
+ Python ≥ 3.10 recommended.
116
+
117
+ ---
118
+
119
+ ## Quick start
120
+
121
+ ### 1. Preview your room layout
122
+
123
+ Before running the (potentially long) optimisation, always verify your room
124
+ geometry, obstacles and capture zones visually:
125
+
126
+ ```bash
127
+ python preview_room.py --config configs/example_simple.yaml
128
+ ```
129
+
130
+ This saves a top-down PNG to `outputs/preview_room/` and opens an interactive
131
+ window. Use it every time you modify your config to catch geometry errors early.
132
+
133
+ > **Tip:** `preview_room.py` can be run standalone at any time — it does not
134
+ > require the optimiser to have been run first.
135
+
136
+ ### 2. Run the optimiser
137
+
138
+ ```bash
139
+ python optimize.py --config configs/example_simple.yaml
140
+ ```
141
+
142
+ The room preview is shown automatically at startup. Close the window to start
143
+ the optimisation. To skip the preview (e.g. for batch runs):
144
+
145
+ ```bash
146
+ python optimize.py --config configs/example_simple.yaml --no-preview
147
+ ```
148
+
149
+ Results are saved in `outputs/`:
150
+ - `FINAL_RESULT_*.png` — 4-panel figure (top view, heatmap, side view, coverage bar chart)
151
+ - `graphs/` — intermediate graphs for each optimisation attempt
152
+ - `graphs_optimal/` — best result per zone combination, ranked by score
153
+ - `log_*.txt` — full optimisation log
154
+ - `preview_room/` — room layout previews
155
+
156
+ ---
157
+
158
+ ## Adapting to your lab
159
+
160
+ ### Step 1 — Choose a starting config
161
+
162
+ | File | Description |
163
+ |---|---|
164
+ | `configs/example_simple.yaml` | **Start here** — 10×6 m rectangle, one camera set, no obstacles |
165
+ | `configs/example_real_world.yaml` | Full real-world example — L-shaped room, obstacles, two camera sets, corridor |
166
+ | `configs/T_zone_direction_change.yaml` | T-shaped capture zone for direction-change analysis |
167
+
168
+ ```bash
169
+ cp configs/example_simple.yaml configs/my_lab.yaml
170
+ ```
171
+
172
+ ### Step 2 — Edit `my_lab.yaml`
173
+
174
+ The YAML file has six sections:
175
+
176
+ #### `room` — room geometry
177
+ ```yaml
178
+ room:
179
+ corners: [[0,0],[10,0],[10,5],[0,5]] # (X,Y) vertices in metres, in order
180
+ height: 3.0 # floor-to-ceiling height (metres)
181
+ ```
182
+
183
+ #### `obstacles` — walls, pillars, furniture
184
+ ```yaml
185
+ obstacles:
186
+ - type: polygon
187
+ vertices: [[1.0,0.0],[1.2,0.0],[1.2,0.5],[1.0,0.5]]
188
+ height: 3.0 # = room height → fully blocks line-of-sight
189
+ label: "Pillar A"
190
+ can_mount_camera: false
191
+
192
+ - type: polygon
193
+ vertices: [[2,0],[2,1],[3,1],[3,0]]
194
+ height: 1.2 # partial height → cameras can see over it
195
+ label: "Table"
196
+ can_mount_camera: false
197
+ ```
198
+
199
+ > **Tip:** run `python preview_room.py --config configs/my_lab.yaml` after every
200
+ > obstacle addition to verify the geometry looks right before optimising.
201
+
202
+ #### `subject` — person being recorded
203
+ ```yaml
204
+ subject:
205
+ height: 1.9 # metres
206
+ foot_z: 0.0
207
+ ```
208
+
209
+ #### `camera_sets` — define your cameras
210
+ ```yaml
211
+ camera_sets:
212
+ - id: "cam_A"
213
+ name: "My Camera"
214
+ mounting: "wall" # wall | tripod
215
+ fov_h_landscape: 110.0
216
+ fov_v_landscape: 70.0
217
+ fov_h_portrait: 70.0
218
+ fov_v_portrait: 110.0
219
+ height_options: [2.0, 2.2] # tested heights (metres)
220
+ max_range: 10.0
221
+ min_range: 0.5
222
+ max_count: 8
223
+ min_spacing: 1.2
224
+ score_weight: 1.0
225
+ color: "#1f77b4"
226
+ ```
227
+
228
+ #### `capture_zones` — where coverage matters
229
+
230
+ **Corridor-based** (walking path):
231
+ ```yaml
232
+ capture_zones:
233
+ - id: "full_corridor"
234
+ type: "corridor"
235
+ priority: 0.5
236
+ length: 10.0
237
+ width: 1.0
238
+ placement:
239
+ x_start_options: [1.0, 2.0]
240
+ y_options: [1.5, 2.0]
241
+
242
+ - id: "analysis_zone"
243
+ type: "sub_zone"
244
+ priority: 1.0
245
+ length: 6.0
246
+ contained_in: "full_corridor"
247
+ offset_options: [1.0, 2.0, 3.0]
248
+
249
+ - id: "key_point"
250
+ type: "point"
251
+ priority: 2.0
252
+ radius: 0.5
253
+ contained_in: "analysis_zone"
254
+ auto_optimize: true
255
+ ```
256
+
257
+ **Polygon-based** (arbitrary shape — L, T, cross…):
258
+ ```yaml
259
+ capture_zones:
260
+ - id: "approach"
261
+ type: "polygon"
262
+ priority: 1.0
263
+ grid_step: 0.30
264
+ vertices:
265
+ - [0.0, 0.0]
266
+ - [6.0, 0.0]
267
+ - [6.0, 1.0]
268
+ - [0.0, 1.0]
269
+ placement:
270
+ x_offsets: [0.0, 0.5]
271
+ y_offsets: [2.0, 2.5]
272
+ ```
273
+
274
+ #### `optimization` — tuning parameters
275
+ ```yaml
276
+ optimization:
277
+ target_coverage: 4 # cameras per evaluation point
278
+ bilateral_weight: 0.8 # 0 = disabled, 1 = fully enforced
279
+ vertical_coverage_threshold: 0.9
280
+ restarts_per_combo: 15
281
+ algo: "greedy_1opt" # greedy | greedy_1opt
282
+ early_stop: 5 # stop after N restarts with no improvement
283
+ graph_mode: "best_per_combo" # all | records_only | best_per_combo
284
+ wall_step: 0.35
285
+ angle_steps: 24
286
+ tripod_grid_step: 0.70
287
+ distance_quality_factor: 0.001
288
+ ```
289
+
290
+ ### Step 3 — Preview, then run
291
+
292
+ ```bash
293
+ # Verify geometry
294
+ python preview_room.py --config configs/my_lab.yaml
295
+
296
+ # Run optimisation
297
+ python optimize.py --config configs/my_lab.yaml
298
+ ```
299
+
300
+ ---
301
+
302
+ ## Output explained
303
+
304
+ | Panel | Description |
305
+ |---|---|
306
+ | Top-left: **Top view** | Room plan + camera cones. Grey = blind zone. Coloured = useful coverage. |
307
+ | Top-right: **XY heatmap** | Number of cameras covering each floor position in 3D. |
308
+ | Bottom-left: **XZ side view** | Number of cameras covering each height slice along the room length. |
309
+ | Bottom-right: **Coverage bar chart** | Camera count per X position along the corridor. Green = target reached. |
310
+
311
+ ### Example terminal output
312
+
313
+ ```
314
+ =================================================================
315
+ OPTIMAL CONFIGURATION
316
+ =================================================================
317
+ Score: 829.08
318
+ Bilateral: SOUTH=939.9 (49%) NORTH=981.1 (51%) balance=96% OK
319
+
320
+ Wall cameras (cam_A):
321
+ A 1 [L][S] pos=(0.00m, 0.36m) h=2.2m
322
+ Pan: 10deg to the RIGHT | Tilt: 50.3deg downward
323
+ A 2 [P][S] pos=(8.78m, 0.00m) h=2.0m
324
+ Pan: 55deg to the RIGHT | Tilt: 36.9deg downward
325
+ A 3 [P][S] pos=(4.22m, 0.00m) h=2.0m
326
+ Pan: 55deg to the LEFT | Tilt: 36.9deg downward
327
+ A 4 [P][S] pos=(11.95m, 0.00m) h=2.2m
328
+ Pan: 55deg to the RIGHT | Tilt: 41.8deg downward
329
+ A 5 [P][N] pos=(8.00m, 4.70m) h=2.2m
330
+ Pan: 55deg to the RIGHT | Tilt: 20.7deg downward
331
+ A 6 [L][N] pos=(0.00m, 2.53m) h=2.0m
332
+ Pan: 10deg to the LEFT | Tilt: 42.9deg downward
333
+ A 7 [P][S] pos=(2.04m, 0.00m) h=2.2m
334
+ Pan: 55deg to the LEFT | Tilt: 41.8deg downward
335
+ A 8 [L][N] pos=(2.04m, 4.07m) h=2.2m
336
+ Pan: 35deg to the LEFT | Tilt: 25.0deg downward
337
+ A 9 [P][N] pos=(0.00m, 4.34m) h=2.2m
338
+ Pan: 25deg to the LEFT | Tilt: 23.0deg downward
339
+ A10 [L][N] pos=(13.00m, 3.00m) h=2.2m
340
+ Pan: 35deg to the RIGHT | Tilt: 38.0deg downward
341
+
342
+ Tripod cameras (cam_B):
343
+ B1 [P] pos=(1.10m, 0.40m) h=1.5m angle=20deg tilt=28.8deg
344
+ B2 [L] pos=(10.90m, 0.40m) h=1.5m angle=180deg tilt=28.8deg
345
+ =================================================================
346
+ ```
347
+
348
+ Each camera line gives:
349
+ - `[L]`/`[P]` — landscape or portrait orientation
350
+ - `[S]`/`[N]` — south or north side of the capture axis
351
+ - `pos` — XY position on the wall (metres)
352
+ - `h` — mounting height (metres)
353
+ - **Pan** — horizontal angle left/right from the wall normal
354
+ - **Tilt** — vertical angle downward toward the subject
355
+
356
+ ---
357
+
358
+ ## Room coordinate system
359
+
360
+ ```
361
+ Y
362
+ ^
363
+ |
364
+ | (room interior)
365
+ |
366
+ +-----------> X
367
+ (0,0)
368
+ ```
369
+
370
+ - **X axis**: room length (left → right)
371
+ - **Y axis**: room width (bottom → top)
372
+ - **Z axis**: height (floor = 0)
373
+ - **Angles**: 0° = East (+X), 90° = North (+Y), 180° = West (−X)
374
+
375
+ ---
376
+
377
+ ## How it works
378
+
379
+ The full technical documentation is in **[ALGORITHM.md](ALGORITHM.md)**. Here is a short summary:
380
+
381
+ | Component | Description |
382
+ |---|---|
383
+ | **Sample grid** | Evaluation points distributed across capture zones, weighted by `priority` |
384
+ | **Score per point** | `v²` (vertical body coverage) × `dist_quality` × bilateral factor × angular diversity |
385
+ | **Bilateral factor** | Rewards cameras on both sides of the capture axis (configurable weight) |
386
+ | **Greedy** | Cameras added one by one to maximise score; fast but local optima |
387
+ | **Greedy + 1-opt** | Greedy init with diverse spatial spread, then 1-opt local search; recommended |
388
+ | **Coverage ratio** | Penalises candidates that see only a tiny slice of the zone, regardless of proximity |
389
+ | **Combo sweep** | Tests all combinations of zone positions (X offset, Y position, run-up distance) |
390
+ | **walk_y** | Bilateral axis auto-derived from the highest-priority zone centroid each combo |
391
+
392
+ ---
393
+
394
+ ## Citing this tool
395
+
396
+ If you use this tool in a research publication, please cite it using the
397
+ `CITATION.cff` file at the root of this repository (GitHub shows a
398
+ *"Cite this repository"* button automatically).
399
+
400
+ > Delaplace F. — *Lab Camera Optimizer: automated camera placement for
401
+ > markerless biomechanics motion capture laboratories* — 2026.
402
+
403
+ ---
404
+
405
+ ## License
406
+
407
+ This project is licensed under the **MIT License** — see the
408
+ [LICENSE](LICENSE) file for details.
409
+
410
+ ---
411
+
412
+ ## Contributing
413
+
414
+ Pull requests and issues are welcome.
415
+ Please open an issue before submitting large changes.
416
+
@@ -0,0 +1,13 @@
1
+ core/__init__.py,sha256=01uRez0vw2wd7X8LOgdwgc3ZiGXN4rjNM5NOtwr5ZKg,39
2
+ core/candidates.py,sha256=3f9W6g3sbX1VmZMKP04KyMMEHL89Xy4Neh1mr02H1n0,21813
3
+ core/config_loader.py,sha256=vn2UeiD5LhD4tFSkYaoHoJWU5J4JD03simSGTx9vDMg,14783
4
+ core/greedy.py,sha256=C9EUNpDFNaPLbjaLmjUGzMM1Ry0d9kX8HBseD3tazuk,21860
5
+ core/room.py,sha256=6tWSKCiC4lR9GjRc-xjZ5kIfihIr18ODXOTwi80Iezo,16940
6
+ core/scoring.py,sha256=VW5I08BPBZm2n9L8l9EqQLXLseS2hBLm2W8G_NSHxHg,11613
7
+ core/visualize.py,sha256=95AdIBCl3gjABqxh3fH_EHQV-5Yg8-WmpUrLuabA-5g,27943
8
+ lab_camera_optimizer-1.0.0.dist-info/licenses/LICENSE,sha256=123NfbIUIZEal5ig9wgzzA8ALqMR46suQx-EURcJjOQ,1097
9
+ lab_camera_optimizer-1.0.0.dist-info/METADATA,sha256=TwJY7DHf1wk-7QLglrHLy1SD-c5soD2dzy4I3GyHSZ0,14070
10
+ lab_camera_optimizer-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
11
+ lab_camera_optimizer-1.0.0.dist-info/entry_points.txt,sha256=E3P_GYmtMjYz9BXFmYqglOAdirrPH-120IA_w_6TR5I,94
12
+ lab_camera_optimizer-1.0.0.dist-info/top_level.txt,sha256=0ebp8k35dOTHVwbY3UcokRsjC7cWLN_iaJ70tIcmODU,5
13
+ lab_camera_optimizer-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ lab-camera-optimizer = optimize:main
3
+ lab-camera-preview = preview_room:main
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Florian Delaplace
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+