poseorbit 0.1.2__tar.gz → 0.2.0__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.
- {poseorbit-0.1.2 → poseorbit-0.2.0}/CHANGELOG.md +13 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/PKG-INFO +9 -7
- {poseorbit-0.1.2 → poseorbit-0.2.0}/README.md +8 -6
- {poseorbit-0.1.2 → poseorbit-0.2.0}/poseorbit/__init__.py +32 -14
- {poseorbit-0.1.2 → poseorbit-0.2.0}/poseorbit/__main__.py +9 -2
- {poseorbit-0.1.2 → poseorbit-0.2.0}/poseorbit/api.py +14 -4
- {poseorbit-0.1.2 → poseorbit-0.2.0}/pyproject.toml +1 -1
- {poseorbit-0.1.2 → poseorbit-0.2.0}/tests/test_api.py +23 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/tests/test_geometry.py +11 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/.gitattributes +0 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/.gitignore +0 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/LICENSE +0 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/NOTICE +0 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/SECURITY.md +0 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/poseorbit/detect.py +0 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/poseorbit/draw.py +0 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/poseorbit/geometry.py +0 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/poseorbit/server.py +0 -0
- {poseorbit-0.1.2 → poseorbit-0.2.0}/tests/test_downloads.py +0 -0
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 (2026-10-11)
|
|
4
|
+
|
|
5
|
+
- **Choose some of the people**: `pose(..., person=[0, 2])` draws just those
|
|
6
|
+
(turned together around their shared centre), and `/api/pose` takes
|
|
7
|
+
`"person": [0, 2]` (up to 64 indices). The list comes back sorted and
|
|
8
|
+
without repeats; an empty list or an index past the end is refused.
|
|
9
|
+
`python -m poseorbit draw --person 0,2` does the same.
|
|
10
|
+
- `/api/pose` answers `"person_subsets": true`, so a client knows it may
|
|
11
|
+
send a list.
|
|
12
|
+
- **Changed:** `PoseResult.person` (and the answer's `person`) is a list when
|
|
13
|
+
a list was asked for; code that only passes an int or `ALL_PEOPLE` still
|
|
14
|
+
gets an int.
|
|
15
|
+
|
|
3
16
|
## 0.1.2 (2026-10-11)
|
|
4
17
|
|
|
5
18
|
- **Pinned model downloads**: the three ONNX files come from a fixed commit
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: poseorbit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Find the people in a picture, turn their pose in 3D, and draw the skeleton a pose-conditioned image model follows.
|
|
5
5
|
Project-URL: Homepage, https://github.com/33rd-kk/poseorbit
|
|
6
6
|
Project-URL: Documentation, https://33rd-kk.github.io/poseorbit/
|
|
@@ -122,9 +122,9 @@ Turning a pose in 3D for ControlNet is not new (see
|
|
|
122
122
|
person boxes. Hands and face turn with the body instead of staying flat or
|
|
123
123
|
being dropped, and the front view is pixel-identical to plain 2D detection.
|
|
124
124
|
- **Choose a person in a group.** Everyone found comes back left to right
|
|
125
|
-
with a box, so index 1 is the same person on every call. Draw one of them
|
|
126
|
-
or all of them, turned together around a
|
|
127
|
-
its layout.
|
|
125
|
+
with a box, so index 1 is the same person on every call. Draw one of them,
|
|
126
|
+
any few of them (`person=[0, 2]`) or all of them, turned together around a
|
|
127
|
+
shared centre so the group keeps its layout.
|
|
128
128
|
- **Drawn for the model that will read it.** `dwpose` is rtmlib's
|
|
129
129
|
COCO-WholeBody drawing (thin lines, hands and face), the usual input of
|
|
130
130
|
DWPose-trained adapters. `openpose` is the thick, body-only drawing
|
|
@@ -251,7 +251,7 @@ curl -s localhost:7870/api/pose -H 'Content-Type: application/json' -d '{
|
|
|
251
251
|
```
|
|
252
252
|
|
|
253
253
|
```json
|
|
254
|
-
{ "skeleton_base64": "…png…", "width": 832, "height": 1216, "person": 0,
|
|
254
|
+
{ "skeleton_base64": "…png…", "width": 832, "height": 1216, "person": 0, "person_subsets": true,
|
|
255
255
|
"camera": { "yaw": 30, "pitch": 10, "framing": { "zoom": 2, "x": 0.5, "y": 0.35 } },
|
|
256
256
|
"style": "openpose", "limits": { "yaw": 90, "pitch": 45, "zoom": [0.5, 6] },
|
|
257
257
|
"joints_in_frame": 13,
|
|
@@ -260,8 +260,10 @@ curl -s localhost:7870/api/pose -H 'Content-Type: application/json' -d '{
|
|
|
260
260
|
```
|
|
261
261
|
|
|
262
262
|
Everything but `image_base64` is optional. `person` is an index into
|
|
263
|
-
`people` (left to right),
|
|
264
|
-
|
|
263
|
+
`people` (left to right), a list of indices for some of them (`[0, 2]`, up to
|
|
264
|
+
64; answered sorted), `-1` for everyone, or absent for the most confident.
|
|
265
|
+
`person_subsets: true` in the answer tells a client it may send a list (0.2.0
|
|
266
|
+
and later). `want_3d` adds each person's 133 points as `[x, y, z]` in units of
|
|
265
267
|
the picture's width, for a 3D viewer. Out-of-range angles and zoom are
|
|
266
268
|
clamped; a picture with nobody in it, or a person index past the end, gets a
|
|
267
269
|
400 with a `detail` message. Server and API are 127.0.0.1-only by default.
|
|
@@ -85,9 +85,9 @@ Turning a pose in 3D for ControlNet is not new (see
|
|
|
85
85
|
person boxes. Hands and face turn with the body instead of staying flat or
|
|
86
86
|
being dropped, and the front view is pixel-identical to plain 2D detection.
|
|
87
87
|
- **Choose a person in a group.** Everyone found comes back left to right
|
|
88
|
-
with a box, so index 1 is the same person on every call. Draw one of them
|
|
89
|
-
or all of them, turned together around a
|
|
90
|
-
its layout.
|
|
88
|
+
with a box, so index 1 is the same person on every call. Draw one of them,
|
|
89
|
+
any few of them (`person=[0, 2]`) or all of them, turned together around a
|
|
90
|
+
shared centre so the group keeps its layout.
|
|
91
91
|
- **Drawn for the model that will read it.** `dwpose` is rtmlib's
|
|
92
92
|
COCO-WholeBody drawing (thin lines, hands and face), the usual input of
|
|
93
93
|
DWPose-trained adapters. `openpose` is the thick, body-only drawing
|
|
@@ -214,7 +214,7 @@ curl -s localhost:7870/api/pose -H 'Content-Type: application/json' -d '{
|
|
|
214
214
|
```
|
|
215
215
|
|
|
216
216
|
```json
|
|
217
|
-
{ "skeleton_base64": "…png…", "width": 832, "height": 1216, "person": 0,
|
|
217
|
+
{ "skeleton_base64": "…png…", "width": 832, "height": 1216, "person": 0, "person_subsets": true,
|
|
218
218
|
"camera": { "yaw": 30, "pitch": 10, "framing": { "zoom": 2, "x": 0.5, "y": 0.35 } },
|
|
219
219
|
"style": "openpose", "limits": { "yaw": 90, "pitch": 45, "zoom": [0.5, 6] },
|
|
220
220
|
"joints_in_frame": 13,
|
|
@@ -223,8 +223,10 @@ curl -s localhost:7870/api/pose -H 'Content-Type: application/json' -d '{
|
|
|
223
223
|
```
|
|
224
224
|
|
|
225
225
|
Everything but `image_base64` is optional. `person` is an index into
|
|
226
|
-
`people` (left to right),
|
|
227
|
-
|
|
226
|
+
`people` (left to right), a list of indices for some of them (`[0, 2]`, up to
|
|
227
|
+
64; answered sorted), `-1` for everyone, or absent for the most confident.
|
|
228
|
+
`person_subsets: true` in the answer tells a client it may send a list (0.2.0
|
|
229
|
+
and later). `want_3d` adds each person's 133 points as `[x, y, z]` in units of
|
|
228
230
|
the picture's width, for a 3D viewer. Out-of-range angles and zoom are
|
|
229
231
|
clamped; a picture with nobody in it, or a person index past the end, gets a
|
|
230
232
|
400 with a `detail` message. Server and API are 127.0.0.1-only by default.
|
|
@@ -11,6 +11,7 @@ pose in 3D, and draw the skeleton a pose-conditioned image model follows.
|
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
+
from collections.abc import Sequence
|
|
14
15
|
from dataclasses import dataclass
|
|
15
16
|
|
|
16
17
|
import numpy as np
|
|
@@ -20,7 +21,7 @@ from .detect import KEYPOINT_THRESHOLD, Detector, NoPersonError, Person, most_co
|
|
|
20
21
|
from .draw import STYLES, Style, letterbox, render
|
|
21
22
|
from .geometry import MAX_PITCH, MAX_YAW, MAX_ZOOM, MIN_ZOOM, Camera, Framing, fit, frame, scene_centre, view
|
|
22
23
|
|
|
23
|
-
__version__ = "0.
|
|
24
|
+
__version__ = "0.2.0"
|
|
24
25
|
|
|
25
26
|
# Person index meaning "everyone detected", for a group pose.
|
|
26
27
|
ALL_PEOPLE = -1
|
|
@@ -53,7 +54,8 @@ class PoseResult:
|
|
|
53
54
|
Attributes:
|
|
54
55
|
skeleton: The skeleton on black, at the requested size.
|
|
55
56
|
people: Everyone detected, left to right.
|
|
56
|
-
person: Who was drawn: an index into `people`, or
|
|
57
|
+
person: Who was drawn: an index into `people`, `ALL_PEOPLE`, or the
|
|
58
|
+
sorted indices when several were chosen by a list.
|
|
57
59
|
camera: The camera actually used, after clamping.
|
|
58
60
|
framing: The framing actually used, after clamping.
|
|
59
61
|
joints_in_frame: Body joints (of 17) seen and inside the canvas, for
|
|
@@ -62,9 +64,10 @@ class PoseResult:
|
|
|
62
64
|
"""
|
|
63
65
|
|
|
64
66
|
skeleton: Image.Image
|
|
65
|
-
# Everyone detected, left to right, and which of them was drawn
|
|
67
|
+
# Everyone detected, left to right, and which of them was drawn: an index,
|
|
68
|
+
# ALL_PEOPLE for all, or the sorted indices of the ones chosen.
|
|
66
69
|
people: list[Person]
|
|
67
|
-
person: int
|
|
70
|
+
person: int | list[int]
|
|
68
71
|
# The camera and framing actually used, after clamping.
|
|
69
72
|
camera: Camera
|
|
70
73
|
framing: Framing
|
|
@@ -74,26 +77,38 @@ class PoseResult:
|
|
|
74
77
|
joints_in_frame: int
|
|
75
78
|
|
|
76
79
|
|
|
77
|
-
def chosen(people: list[Person], person: int | None) -> tuple[int, list[Person]]:
|
|
80
|
+
def chosen(people: list[Person], person: int | Sequence[int] | None) -> tuple[int | list[int], list[Person]]:
|
|
78
81
|
"""`person` resolved (None = the most confident) and the people it means.
|
|
79
82
|
|
|
83
|
+
A sequence chooses any of them: the indices come back sorted, without
|
|
84
|
+
repeats, and the people in that (left-to-right) order.
|
|
85
|
+
|
|
80
86
|
Raises:
|
|
81
|
-
ValueError: `person` is past the end of
|
|
87
|
+
ValueError: `person` (or one of a sequence) is past the end of
|
|
88
|
+
`people`, or the sequence is empty.
|
|
82
89
|
"""
|
|
83
90
|
if person is None:
|
|
84
91
|
person = most_confident(people)
|
|
85
|
-
if person
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
if isinstance(person, int):
|
|
93
|
+
if person == ALL_PEOPLE:
|
|
94
|
+
return person, people
|
|
95
|
+
if 0 <= person < len(people):
|
|
96
|
+
return person, [people[person]]
|
|
97
|
+
raise ValueError(f"No person {person}: {len(people)} found.")
|
|
98
|
+
indices = sorted(set(person))
|
|
99
|
+
if not indices:
|
|
100
|
+
raise ValueError("Choose at least one person.")
|
|
101
|
+
for index in indices:
|
|
102
|
+
if not 0 <= index < len(people):
|
|
103
|
+
raise ValueError(f"No person {index}: {len(people)} found.")
|
|
104
|
+
return indices, [people[index] for index in indices]
|
|
90
105
|
|
|
91
106
|
|
|
92
107
|
def pose(
|
|
93
108
|
detector: Detector,
|
|
94
109
|
image: Image.Image,
|
|
95
110
|
size: tuple[int, int],
|
|
96
|
-
person: int | None = None,
|
|
111
|
+
person: int | Sequence[int] | None = None,
|
|
97
112
|
camera: Camera | None = None,
|
|
98
113
|
style: Style = "dwpose",
|
|
99
114
|
depth: bool = False,
|
|
@@ -110,7 +125,9 @@ def pose(
|
|
|
110
125
|
size: The output's (width, height). The skeleton is drawn at it, the
|
|
111
126
|
picture letterboxed onto it.
|
|
112
127
|
person: An index into the left-to-right order, `ALL_PEOPLE` (-1) for
|
|
113
|
-
everyone,
|
|
128
|
+
everyone, a sequence of indices for some of them (drawn together
|
|
129
|
+
and turned around their shared centre), or None for the most
|
|
130
|
+
confident.
|
|
114
131
|
camera: Where the camera stands; None for the front view.
|
|
115
132
|
style: `"dwpose"` or `"openpose"`; see [render][poseorbit.render].
|
|
116
133
|
depth: Detect depth even for the front view (to read
|
|
@@ -122,7 +139,8 @@ def pose(
|
|
|
122
139
|
|
|
123
140
|
Raises:
|
|
124
141
|
NoPersonError: Nobody in the picture is seen well enough.
|
|
125
|
-
ValueError: `person` is past the end of the
|
|
142
|
+
ValueError: `person` (or one of a sequence) is past the end of the
|
|
143
|
+
people found, or the sequence is empty.
|
|
126
144
|
"""
|
|
127
145
|
camera = (camera or Camera()).clamped()
|
|
128
146
|
framing = (framing or Framing()).clamped()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""python -m poseorbit serve [--host 127.0.0.1] [--port 7870] [--weights-dir DIR]
|
|
2
|
-
python -m poseorbit draw PICTURE OUT.png [--size 832x1216] [--person N] [--yaw D] [--pitch D]
|
|
2
|
+
python -m poseorbit draw PICTURE OUT.png [--size 832x1216] [--person N|N,M,...] [--yaw D] [--pitch D]
|
|
3
3
|
[--zoom Z --centre X,Y] [--style S]"""
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
@@ -8,6 +8,13 @@ import argparse
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
def _person(text: str) -> int | list[int]:
|
|
12
|
+
"""`--person`: one index (-1 for everyone), or several as "0,2"."""
|
|
13
|
+
if "," in text:
|
|
14
|
+
return [int(part) for part in text.split(",") if part.strip()]
|
|
15
|
+
return int(text)
|
|
16
|
+
|
|
17
|
+
|
|
11
18
|
def main() -> None:
|
|
12
19
|
"""The `poseorbit` command (`python -m poseorbit`)."""
|
|
13
20
|
parser = argparse.ArgumentParser(prog="poseorbit")
|
|
@@ -22,7 +29,7 @@ def main() -> None:
|
|
|
22
29
|
draw.add_argument("picture", type=Path)
|
|
23
30
|
draw.add_argument("out", type=Path)
|
|
24
31
|
draw.add_argument("--size", default=None, help="WIDTHxHEIGHT (default: the picture's)")
|
|
25
|
-
draw.add_argument("--person", type=
|
|
32
|
+
draw.add_argument("--person", type=_person, default=None, help="index, several as 0,2, or -1 for everyone")
|
|
26
33
|
draw.add_argument("--yaw", type=float, default=0.0)
|
|
27
34
|
draw.add_argument("--pitch", type=float, default=0.0)
|
|
28
35
|
draw.add_argument("--zoom", type=float, default=1.0)
|
|
@@ -4,14 +4,15 @@ exactly the same JSON.
|
|
|
4
4
|
|
|
5
5
|
Request:
|
|
6
6
|
{ "image_base64": "...", "width": 832, "height": 1216,
|
|
7
|
-
"person": 0, # optional; -1 = everyone, absent = most confident
|
|
7
|
+
"person": 0, # optional; -1 = everyone, [0, 2] = those, absent = most confident
|
|
8
8
|
"want_3d": true, # optional; answer with each person's 3D points
|
|
9
9
|
"camera": { "yaw": 30, "pitch": 10, # optional; degrees, see geometry.py
|
|
10
10
|
"framing": { "zoom": 3, "x": 0.5, "y": 0.2 } }, # optional; the canvas point to centre, and zoom
|
|
11
11
|
"style": "openpose" } # optional; "dwpose" (default) or "openpose"
|
|
12
12
|
|
|
13
13
|
Answer:
|
|
14
|
-
{ "skeleton_base64": "...png...", "width": 832, "height": 1216, "person": 0,
|
|
14
|
+
{ "skeleton_base64": "...png...", "width": 832, "height": 1216, "person": 0, # or the sorted list asked for
|
|
15
|
+
"person_subsets": true, # "person" may be a list: a client may offer picking any of them
|
|
15
16
|
"camera": { "yaw": 30, "pitch": 10, "framing": { "zoom": 3, "x": 0.5, "y": 0.2 } },
|
|
16
17
|
"style": "openpose",
|
|
17
18
|
"limits": { "yaw": 90, "pitch": 45, "zoom": [0.5, 6] },
|
|
@@ -33,6 +34,10 @@ from PIL import Image
|
|
|
33
34
|
from . import ALL_PEOPLE, MAX_PITCH, MAX_YAW, MAX_ZOOM, MIN_ZOOM, STYLES, Camera, Detector, Framing, pose
|
|
34
35
|
from .draw import Style
|
|
35
36
|
|
|
37
|
+
# The most people one request may choose by a list: far more than a picture
|
|
38
|
+
# usefully holds, and it bounds the list before anything is detected.
|
|
39
|
+
MAX_CHOSEN = 64
|
|
40
|
+
|
|
36
41
|
|
|
37
42
|
class BadRequest(ValueError):
|
|
38
43
|
"""The request is wrong; answer 400 with the message."""
|
|
@@ -119,8 +124,12 @@ def handle(detector: Detector, body: dict[str, Any], default_style: Style = "dwp
|
|
|
119
124
|
if not (64 <= width <= 4096 and 64 <= height <= 4096):
|
|
120
125
|
raise BadRequest("width and height must be 64-4096")
|
|
121
126
|
person = body.get("person")
|
|
122
|
-
if
|
|
123
|
-
|
|
127
|
+
if isinstance(person, list):
|
|
128
|
+
# Bounded before anything is detected; past the people found is a ValueError later.
|
|
129
|
+
if not 1 <= len(person) <= MAX_CHOSEN or any(isinstance(index, bool) or not isinstance(index, int) or index < 0 for index in person):
|
|
130
|
+
raise BadRequest(f"person as a list must hold 1-{MAX_CHOSEN} indices")
|
|
131
|
+
elif person is not None and (isinstance(person, bool) or not isinstance(person, int) or person < ALL_PEOPLE):
|
|
132
|
+
raise BadRequest("person must be an index, a list of indices, or -1 for everyone")
|
|
124
133
|
style = body.get("style") or default_style
|
|
125
134
|
if style not in STYLES:
|
|
126
135
|
raise BadRequest(f"style must be one of {', '.join(STYLES)}")
|
|
@@ -146,6 +155,7 @@ def handle(detector: Detector, body: dict[str, Any], default_style: Style = "dwp
|
|
|
146
155
|
"width": width,
|
|
147
156
|
"height": height,
|
|
148
157
|
"person": result.person,
|
|
158
|
+
"person_subsets": True,
|
|
149
159
|
"camera": {
|
|
150
160
|
"yaw": result.camera.yaw,
|
|
151
161
|
"pitch": result.camera.pitch,
|
|
@@ -44,6 +44,7 @@ def test_an_answer_has_everything_a_client_needs():
|
|
|
44
44
|
answer = handle(StubDetector([_person(100), _person(300)]), {"image_base64": _picture(), "width": 832, "height": 1216})
|
|
45
45
|
assert answer["width"] == 832 and answer["height"] == 1216
|
|
46
46
|
assert answer["person"] in (0, 1)
|
|
47
|
+
assert answer["person_subsets"] is True
|
|
47
48
|
assert answer["camera"] == {"yaw": 0.0, "pitch": 0.0, "framing": {"zoom": 1.0, "x": 0.5, "y": 0.5}}
|
|
48
49
|
assert answer["limits"] == {"yaw": 90.0, "pitch": 45.0, "zoom": [0.5, 6.0]}
|
|
49
50
|
assert answer["style"] == "dwpose"
|
|
@@ -93,6 +94,11 @@ def test_zooming_in_leaves_fewer_joints_in_frame():
|
|
|
93
94
|
{"image_base64": "PIC", "width": 10},
|
|
94
95
|
{"image_base64": "PIC", "person": -2},
|
|
95
96
|
{"image_base64": "PIC", "person": True},
|
|
97
|
+
{"image_base64": "PIC", "person": []},
|
|
98
|
+
{"image_base64": "PIC", "person": [0, -1]},
|
|
99
|
+
{"image_base64": "PIC", "person": [0, True]},
|
|
100
|
+
{"image_base64": "PIC", "person": ["0"]},
|
|
101
|
+
{"image_base64": "PIC", "person": list(range(65))},
|
|
96
102
|
{"image_base64": "PIC", "style": "sketch"},
|
|
97
103
|
{"image_base64": "PIC", "camera": 5},
|
|
98
104
|
{"image_base64": "PIC", "camera": {"yaw": "left"}},
|
|
@@ -111,3 +117,20 @@ def test_nobody_and_a_person_past_the_end_are_refused():
|
|
|
111
117
|
handle(StubDetector([]), {"image_base64": _picture()})
|
|
112
118
|
with pytest.raises(ValueError):
|
|
113
119
|
handle(StubDetector([_person(200)]), {"image_base64": _picture(), "person": 3})
|
|
120
|
+
with pytest.raises(ValueError):
|
|
121
|
+
handle(StubDetector([_person(200)]), {"image_base64": _picture(), "person": [0, 3]})
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_some_people_are_drawn_together():
|
|
125
|
+
people = [_person(80), _person(200), _person(320)]
|
|
126
|
+
body = {"image_base64": _picture(), "width": 400, "height": 600}
|
|
127
|
+
some = handle(StubDetector(people), {**body, "person": [2, 0]})
|
|
128
|
+
assert some["person"] == [0, 2]
|
|
129
|
+
assert len(some["people"]) == 3
|
|
130
|
+
# Two figures draw differently from one, and from all three.
|
|
131
|
+
one = handle(StubDetector(people), {**body, "person": 0})
|
|
132
|
+
everyone = handle(StubDetector(people), {**body, "person": -1})
|
|
133
|
+
assert len({some["skeleton_base64"], one["skeleton_base64"], everyone["skeleton_base64"]}) == 3
|
|
134
|
+
# Turned, the chosen ones still turn together.
|
|
135
|
+
turned = handle(StubDetector(people), {**body, "person": [0, 2], "camera": {"yaw": 30}})
|
|
136
|
+
assert turned["person"] == [0, 2] and turned["camera"]["yaw"] == 30
|
|
@@ -82,6 +82,17 @@ def test_choosing_people():
|
|
|
82
82
|
chosen(people, 2)
|
|
83
83
|
|
|
84
84
|
|
|
85
|
+
def test_choosing_some_people_by_a_list():
|
|
86
|
+
people = [_person(10), _person(20), _person(30)]
|
|
87
|
+
# Sorted, without repeats, in left-to-right order.
|
|
88
|
+
assert chosen(people, [2, 0, 2]) == ([0, 2], [people[0], people[2]])
|
|
89
|
+
assert chosen(people, (1,)) == ([1], [people[1]])
|
|
90
|
+
with pytest.raises(ValueError):
|
|
91
|
+
chosen(people, [])
|
|
92
|
+
with pytest.raises(ValueError):
|
|
93
|
+
chosen(people, [0, 3])
|
|
94
|
+
|
|
95
|
+
|
|
85
96
|
def test_whole_framing_changes_nothing():
|
|
86
97
|
points = np.array([[10.0, 20.0], [300.0, 400.0]])
|
|
87
98
|
assert np.allclose(frame(points, (832, 1216), Framing()), points)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|