@rig-c/pose 0.7.0 → 0.8.1
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.
- package/README.md +82 -1
- package/cli.ts +67 -4
- package/package.json +1 -1
- package/src/compare.ts +1046 -0
- package/src/pose.ts +17 -4
- package/src/rigc.ts +13 -4
- package/src/run.ts +45 -5
- package/src/runcompare.ts +119 -0
- package/src/sheet.ts +106 -0
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ Nothing is written until every check passes:
|
|
|
38
38
|
|
|
39
39
|
and what the run cannot decide for you it lists:
|
|
40
40
|
|
|
41
|
+
- `RIGC` — the rig-c version and entry that gated and posed the run (also `rigc` in the report): a later rig-c can change the bytes.
|
|
41
42
|
- `HOLD` — a joint missing, or scored under `--min-score`, holds its last good value; one line per span, with the frames and the reason.
|
|
42
43
|
- `SHORT` — a segment shorter in the picture than `--short` of its median length (with `--short <fraction>`; off by default) has its angle there interpolated, not read; one line per span, with the chain, the segment, the frames and the lowest fraction. Below.
|
|
43
44
|
- `END-ON` — a `SHORT` run lasting at least 0.15 s: the limb points at the camera, a pose the rig cannot draw.
|
|
@@ -109,6 +110,8 @@ Exit codes: `0` written, `1` refused (every reason printed, nothing written), `2
|
|
|
109
110
|
|
|
110
111
|
**From COCO-17** — the layout most pose models and datasets use — `rig-pose convert --coco in.json --out pose.json`, or `retarget --coco`. The input is `{ "format": "coco17-sequence/1", "width", "height", "source", "frames": [{ "t", "keypoints" }] }`, keypoints in COCO's order as 17 `[x, y, score]` or 51 numbers, or `null` for a frame with nobody. The mapping: COCO's `left_*`/`right_*` → body-18's `l_*`/`r_*` (both are the subject's sides), a score of 0 → `missing`, and body-18's `neck`, which COCO lacks, is the shoulders' midpoint scored the lower of the two (OpenPose's definition).
|
|
111
112
|
|
|
113
|
+
|
|
114
|
+
**The older name.** `rig-c-pose-frames/0`, written by the extraction jobs this package's demos were made from, is the same file under another spec string. It has `space.units` "painting-px" and an `fps` key; each frame's `t` is the time read. It is read as it is.
|
|
112
115
|
## The map: `rig-pose-map/1`
|
|
113
116
|
|
|
114
117
|
Which bones carry which joints. You write it for your rig:
|
|
@@ -178,9 +181,87 @@ Either way the `SWITCH` line ends with `moved on …, off …`: each boundary's
|
|
|
178
181
|
**TURN.** The cutoff was measured on the three lines across the body before it was fixed ([#6](https://github.com/rig-c/pose/issues/6)): on the 13 s front-facing dance their picture length never drops under 0.81 of its median and none reverses; on a second sequence the hip line falls to 0.17 at frames 465–491 (side-on) and the eye line to 0.22 at 26–58 (head in profile), and 3 frames reverse. 0.5 lies between. A line is reversed when it points more than 90° from the subject's right joint to the left (front-facing, it points at 0°). The lines are read after holds and smoothing, held and smoothed as the map's points are even where the map does not use them; a line whose points are good on no frame is named as not measured.
|
|
179
182
|
|
|
180
183
|
|
|
181
|
-
**A stopgap window: `frames`.** A set may state `"frames": [[first, last], …]`: it shows only inside those windows, and on them it is chosen over any set within tolerance that has no window. It is for two sets whose key skeletons the angles cannot tell apart. Measured on HQ's dancer, the hands clasped behind the back and the hands low in front differ by only 2–21°, and the wrist and hand-point scores do not separate them either; the cue that might is #33's depth state. It is recorded in the map as a stopgap, not a selection rule.
|
|
184
|
+
**A stopgap window: `frames`.** A set may state `"frames": [[first, last], …]`: it shows only inside those windows, and on them it is chosen over any set within tolerance that has no window. It is for two sets whose key skeletons the angles cannot tell apart. Measured on HQ's dancer, the hands clasped behind the back and the hands low in front differ by only 2–21°, and the wrist and hand-point scores do not separate them either; the cue that might is #33's depth state. It is recorded in the map as a stopgap, not a selection rule. A window **restricts** a set; it never forces one. Where the set does not show inside its window, the run says why in a `FRAMES` line: the stretch, and whether the pose there was past the tolerance (with the largest distance), or within it but the run was dropped.
|
|
182
185
|
|
|
183
186
|
**How far a set's art may turn: `turn`.** A drawing reads right only near the angle it was painted at. A set may state, per bone with a `drawn` angle, how far its art may turn from it, `"turn": { "sleeve_r0": [-20, 20] }`. While the set shows, the bone is held within it: its target becomes drawn + clamp(wrap(target − drawn), min, max), each bone on its own world angle, so a forearm under a held upper arm keeps its own target. The landing check measures against the held angle. The overflow the pose asked for is listed as `ART-RANGE`. A `turn` on a bone the set has no `drawn` angle for is refused (`MAP_ALT_TURN`).
|
|
187
|
+
## Compare: the rig's render against the video
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
rig-pose compare --reference ref.pose.json --render render.pose.json --map map.json \
|
|
191
|
+
[--retarget-report report.json] [--reference-hands ref.wholebody.json --render-hands render.wholebody.json] \
|
|
192
|
+
[--cloth-reference ref.cloth.json --cloth-render render.cloth.json --cloth-windows 3.9-7.4,7.6-11.1] \
|
|
193
|
+
[--report compare.json] [--sheet compare.png]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Polishing a retarget used to mean rendering it, having a person look, and hearing what was wrong. Most of what a person names can be measured with the same pose detector on both sides ([#38](https://github.com/rig-c/pose/issues/38)). `compare` reads two `rig-pose-sequence/1` files on one time base: the **reference** (the source video's keypoints) and the **render** (the same detector run on `rigc render` frames of the retargeted motion). It lists the differences per body unit, worst first, each with the fix it suggests. The person is then needed only for taste. The detector stays outside rig-pose, as it does for `retarget`.
|
|
197
|
+
|
|
198
|
+
The figures and bars below are fixed. They were set in [#38](https://github.com/rig-c/pose/issues/38) before any render was measured, and they are grading lines for [rig-c/exam](https://github.com/rig-c/exam). A change to one is a new comment on that issue first.
|
|
199
|
+
|
|
200
|
+
**One time base.** Both sequences have the same number of frames, and each frame's times differ by at most **1 ms**. Render the motion at the reference's rate, frame for frame. Anything else is refused (`COMPARE_FRAMES`, `COMPARE_TIMES`, with the first frames that differ). The hands files and cloth tracks are held to the same rule against the reference (`COMPARE_HANDS_FRAMES`/`_TIMES`, `COMPARE_CLOTH_FRAMES`/`_TIMES`).
|
|
201
|
+
|
|
202
|
+
**Positions are normalised per side.** A position is divided by that side's own **hip width**: the median over its frames of |r_hip − l_hip|, in picture px. Directions are compared as Spine angles of the picture segment, |wrap(render − reference)|. Every joint with a position counts, whatever its score; only hand visibility reads scores. A frame where either side lacks a point the measure needs is not measured, and it is never over the bar.
|
|
203
|
+
|
|
204
|
+
**A finding** is a run of frames over a measure's bar lasting **≥ 0.15 s**. A run is timed from the first frame's time to the end of the last frame, as `END-ON` is: 4 frames at 24 fps (0.167 s) are a finding, 3 (0.125 s) are not. Runs **one frame apart** merge, and the merged gap is not counted as over the bar. A finding's worst frame is the frame of its largest figure. Within a measure, findings are listed worst first.
|
|
205
|
+
|
|
206
|
+
| unit | measure | figure, per frame | bar | fix class |
|
|
207
|
+
| --- | --- | --- | --- | --- |
|
|
208
|
+
| head | eye-line tilt | \|direction difference\| of r_eye → l_eye | **> 15°** | not derivable |
|
|
209
|
+
| arms | direction, per mapped arm segment | \|direction difference\| | **> 20°** | **paint** inside a set's span, **rig** outside |
|
|
210
|
+
| arms | hand visibility, per hand | mean score of the hand's 21 points (or the wrist's score) on each side | reference **≥ 0.6** while render **< 0.4** | **depth** (#33) |
|
|
211
|
+
| body | torso length | \|mid_shoulder − mid_hip\| ÷ that side's median | the render's range **< 0.5 ×** the reference's, over the whole run | **motion** |
|
|
212
|
+
| body | hip vertical travel | mid_hip's y in hip widths | the same rule as torso length | **motion** |
|
|
213
|
+
| legs | direction, per mapped leg segment | \|direction difference\| | **> 20°** | **paint** inside a set's span, **rig** outside |
|
|
214
|
+
| legs | foot plant, per ankle | the ankle's speed in hip widths per frame (from the frame before) | render **> 0.05** while reference **< 0.02** | **motion** |
|
|
215
|
+
| cloth | lag, swing ratio, overshoot, per window | #34's three figures, on each side | outside #34's bands around the reference's figure | **rig** (physics) |
|
|
216
|
+
|
|
217
|
+
Why these bars ([#38](https://github.com/rig-c/pose/issues/38)):
|
|
218
|
+
|
|
219
|
+
- **20° for a segment's direction.** HQ's rule for a set's art is `drawn` ± 20°: the art may turn up to 20° from the angle it was painted at and still read. A bigger difference is an arm that looks different.
|
|
220
|
+
- **0.6 and 0.4 for a hand.** On the 13 s dance, the hands in front (S4) score 0.53–0.88 and the hands hidden behind the back (S1) score 0.41–0.57. 0.6 and 0.4 sit outside that overlap, one on each side.
|
|
221
|
+
- **15°** for the head's tilt, **0.05 / 0.02** hip widths per frame for a sliding foot, and **0.5 ×** of the reference's range for a missing bounce were fixed with the others before the dancer was measured.
|
|
222
|
+
|
|
223
|
+
**Units and segments.** The map says which segments are compared. An **arm** segment joins two of one side's shoulder, elbow and wrist, and a **leg** segment two of one side's hip, knee and ankle. A segment's name is `chain/bone`. Other chains, such as the trunk, the pelvis or the head, are not compared as segments. The head is measured on its eye line whatever the map says, and the body on its torso and hip. A mapped arm or leg segment whose point has a position on no frame of one side is refused (`COMPARE_JOINT_MISSING`, naming the chain, the bone, the point and the side).
|
|
224
|
+
|
|
225
|
+
**The fix class** is given only when it can be derived:
|
|
226
|
+
|
|
227
|
+
- **An arm or leg direction.** The finding's worst frame is checked against the spans in `--retarget-report` (its `switches`: each set's frames and the chains it depicts). Inside a span of a set that depicts the segment's chain, the class is **paint**: the drawing shown there is not this pose. Outside every such span it is **rig**: the art does not show the bone's direction. Without a report, a chain no set of the map depicts is **rig**. A chain some set depicts is not derivable, and the line names those sets.
|
|
228
|
+
- **Hand visibility** is **depth** ([#33](https://github.com/rig-c/pose/issues/33)): the hand is drawn behind something.
|
|
229
|
+
- **A foot slide or no bounce** is **motion**.
|
|
230
|
+
- **Cloth** is **rig**: the cloth is the rig's physics.
|
|
231
|
+
- **A head tilt** has no class in #38, so the line says it is not derivable.
|
|
232
|
+
|
|
233
|
+
**Hand visibility.** `rig-pose-sequence/1` carries body-18 only, so the hand points come from COCO-WholeBody files, one per side: `--reference-hands` and `--render-hands`. The shape is `{ "spec"?: "coco-wholebody-133", "fps"?, "width"?, "height"?, "source"?, "frames": [{ "t", "points": [[x, y, score] × 133] | null }] }`. The subject's left hand is points 91–111 and the right hand 112–132. A frame's figure is the mean score of the hand's 21 points. A `null` frame (nobody found) is not measured. Without the files, the wrist's score is read instead: an absent or `missing` wrist reads 0, as COCO's v = 0 does. The summary line then says `from wrist scores`. One of the two files without the other is refused with exit 2, because the two sides must be measured the same way.
|
|
234
|
+
|
|
235
|
+
**Body.** Each figure is read over the whole run, so the unit gives at most one finding per measure, `no bounce`, spanning every frame. Its figure is the ratio of the ranges, render ÷ reference. Its worst frame, the one the sheet draws, is the reference's frame farthest from its median. The summary line always gives both ranges.
|
|
236
|
+
|
|
237
|
+
**Cloth.** Cloth is measured only when both cloth tracks are given, with `--cloth-windows`. Without them the unit reads `not measured`. A cloth track is HQ's `rig-c-cloth-track/0`: `{ "spec": "rig-c-cloth-track/0", "subject"?, "method"?, "source"?, "fps"?, "frames_count"?, "units"?, "hipWidth": { "median", "unit": "picture px" | "rig world units" }, "origin"?, "axes"?, "skirtSwing"?, "frames": [{ "i", "t", "hip_x", "hip_y", "hip_w", "tilt_deg", "hem_mid", "hem_l", "hem_r", "hem_l_y", "hem_r_y", "hem_band_cx", "skirt_cx", "skirt_top", "skirt_bottom" }] }`, the frame keys all numbers. Every position is already divided by the subject's own median hip width, with x to the right. `hip_x` is the hips' midpoint and `hem_mid` the midpoint of the hem's leftmost and rightmost points in the lower half of the skirt ([#34](https://github.com/rig-c/pose/issues/34)'s hem); only those two and `t` are read. Any other spec, or a key outside that list, is refused (`COMPARE_CLOTH`). The three figures are #34's, as HQ's skirt criteria define them, ported from HQ's measuring script. On HQ's per-frame record of the dancer they reproduce its numbers exactly (reference A 0.958 / 2.634 / 0.061, B 0.696 / 1.409 / 0.000). Read through `rig-c-cloth-track/0`, whose values are rounded to 5 decimals, the reference's lag in A reads 0.959.
|
|
238
|
+
|
|
239
|
+
- Both signals are smoothed with a Gaussian, σ = 1 frame over ± 4 frames, ends padded, before differentiating (numpy's `gradient`).
|
|
240
|
+
- **lag**, in frames, hem after hip positive: the peak of the normalised cross-correlation of hip-x velocity with hem-x velocity over the window, searched over −6…+12 frames and refined by a parabola.
|
|
241
|
+
- **swing ratio**: per hip stroke, the hem's excursion over the hip's. A stroke runs from one hip reversal (a zero crossing of hip velocity) to the next and moves at least the stroke threshold. A wiggle under the threshold is dropped as a pair. The hem is read `round(lag)` frames after each reversal. The figure is the median over the window's strokes.
|
|
242
|
+
- **overshoot**: per stroke, how far the hem keeps travelling in the stroke's direction after the hips turn, within half of the next stroke (or 12 frames after the last), as a fraction of the hip stroke. The figure is the median.
|
|
243
|
+
- **The stroke threshold** is 0.2 *reference* hip widths: one physical size on both sides (the criteria's Revision 1), expressed in the render's own hip widths. A render track in rig world units is carried by the retarget's scale, `scale.factor` in `--retarget-report` (rig units per reference px): 0.2 × reference hip width × factor ÷ render hip width. On the dancer that is 0.2 × 128.1 × 0.54152 ÷ 92.202 = 0.1505, the criteria's own figure. Without the report it is refused (`COMPARE_CLOTH_SCALE`). A render track in picture px is carried by the torso, as the retarget scales the rig: 0.2 × (reference hip width ÷ reference torso) ÷ (render hip width ÷ render torso), each torso the sequence's median mid_hip → mid_shoulder. A reference track not in picture px is refused (`COMPARE_CLOTH_UNITS`). The `CLOTH` lines print both thresholds and how the render's was carried.
|
|
244
|
+
- **The bands**, per window, around the reference's own figure: lag ± 1.0 frame; swing ratio ± 25 %; overshoot ± 0.10, absolute, floored at 0. A render figure outside its band is a finding over the window's frames. A window with no stroke on one side is listed as not measured.
|
|
245
|
+
- A window is `t0-t1` in seconds, and windows are named A, B, … in the order given. A window's frames are `round((t − t₀) × fps)` up to the end, exclusive. A window of 12 frames or fewer is refused, since the lag search reaches 12.
|
|
246
|
+
|
|
247
|
+
**Output.** The first line gives the frames, the duration, both hip widths, and the report the spans came from. Then come one summary line per unit (`HEAD`, `ARMS`, `BODY`, `LEGS`, `CLOTH`) and one `FIND` line per finding. A summary line gives the unit's findings and, per measure, its findings, its worst figure with the subject and frame, the frames over the bar (in runs of any length), and the bar. A `FIND` line gives the unit, the measure, the subject and its points, the worst figure with both sides' readings and its frame, the frames and seconds, the frames over the bar, the bar, and the fix class with the reason. With cloth, a `CLOTH` line per window gives both sides' figures. The run ends with the number of findings.
|
|
248
|
+
|
|
249
|
+
`--report <file>` writes the same as JSON (`rig-pose-compare/1`):
|
|
250
|
+
|
|
251
|
+
- `inputs`, `frames`, `duration`, `hipWidth: { reference, render }`, `torso: { reference, render }` (medians, px);
|
|
252
|
+
- `rule: { findingSeconds, findingGap, timeTolerance }` and `bars` (every bar above, by name);
|
|
253
|
+
- `units: [{ unit, findings, notMeasured, measures: [{ measure, subjects, source, findings, overBar, notMeasured, bar, ranges? }] }]`;
|
|
254
|
+
- `findings: [{ unit, measure, subject, points, chain, frames: [first, last], seconds, over, worst: { frame, t, figure, reference, render }, figureUnit, bar, fix, fixWhy }]`;
|
|
255
|
+
- `cloth`: `null`, or `{ windows: [{ name, t, frames, reference, render, band }], stroke: { reference, render, carried } }`.
|
|
256
|
+
|
|
257
|
+
Numbers are rounded to 4 decimals, once, at the writer.
|
|
258
|
+
|
|
259
|
+
`--sheet <png>` draws the reference skeleton beside the render skeleton at each finding's worst frame, one row per finding, with the finding's own points marked in yellow. It never draws a video frame. A cloth finding is a figure over a window, with no single frame, so it has no row. When no finding has a frame, no sheet is written and the run says so.
|
|
260
|
+
|
|
261
|
+
**The control** is the reference compared with itself. It must report no finding in any unit, and every summary line says `0 findings`. On HQ's dancer (313 frames, RTMPose, with its whole-body hand points and the retarget's report) it does.
|
|
262
|
+
|
|
263
|
+
Exit codes: `0` compared, whether it found anything or not, since a finding is not a failure; `1` refused, with every reason printed; `2` bad invocation.
|
|
264
|
+
|
|
184
265
|
## Requirements
|
|
185
266
|
|
|
186
267
|
[Bun](https://bun.sh) ≥ 1.2 on `PATH` — rig-pose, like rig-c, runs its TypeScript sources directly. `rig-c` comes as a dependency. With `@esotericsoftware/spine-core` installed beside it, rig-c's gate runs the spine-core round trip; without it, rig-c's own gate, and the gate's report says which.
|
package/cli.ts
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
10
10
|
import pkg from './package.json' with { type: 'json' };
|
|
11
11
|
import { convertCoco17, writePoseSequence } from './src/pose.ts';
|
|
12
|
+
import { parseWindows } from './src/compare.ts';
|
|
12
13
|
import { lineOf, Refusal } from './src/problems.ts';
|
|
14
|
+
import { runCompare, type CompareArgs } from './src/runcompare.ts';
|
|
13
15
|
import { runRetarget, type RetargetArgs } from './src/run.ts';
|
|
14
16
|
|
|
15
17
|
const HELP = `rig-pose ${pkg.version} — motion from a pose sequence onto a rig-c rig
|
|
@@ -17,6 +19,7 @@ const HELP = `rig-pose ${pkg.version} — motion from a pose sequence onto a rig
|
|
|
17
19
|
Usage:
|
|
18
20
|
rig-pose retarget --rig <rig> --map <map.json> --pose <pose.json> --out <motion.json> [--name dance] [flags]
|
|
19
21
|
rig-pose convert --coco <coco17.json> --out <pose.json>
|
|
22
|
+
rig-pose compare --reference <pose.json> --render <pose.json> --map <map.json> [flags]
|
|
20
23
|
rig-pose --version | --help
|
|
21
24
|
|
|
22
25
|
retarget
|
|
@@ -30,6 +33,8 @@ retarget
|
|
|
30
33
|
--coco read --pose as coco17-sequence/1 and convert it first
|
|
31
34
|
--person <id> the person to follow when the sequence holds more than one
|
|
32
35
|
--base-motion <path> the rig's motion spec, when it is not motion.json beside rig.json
|
|
36
|
+
--images <dir> passed to rigc build: where the rig spec's images are, when its own "images" path
|
|
37
|
+
does not reach them (a rig.json copied away from its images)
|
|
33
38
|
--min-score <s> a joint scored below s is not good on that frame and holds its last good
|
|
34
39
|
value (default: 0.3); a joint with no score is good
|
|
35
40
|
--smooth <n> Savitzky–Golay over n frames of joint positions: a centred least-squares cubic,
|
|
@@ -73,7 +78,30 @@ convert
|
|
|
73
78
|
keypoints in COCO's order as 17 [x, y, score] or 51 numbers, or null
|
|
74
79
|
--out <file> the rig-pose-sequence/1 it becomes; neck = the shoulders' midpoint
|
|
75
80
|
|
|
76
|
-
|
|
81
|
+
compare
|
|
82
|
+
--reference <file> rig-pose-sequence/1: the source video's keypoints
|
|
83
|
+
--render <file> rig-pose-sequence/1: the same detector on rigc's render of the retargeted motion,
|
|
84
|
+
rendered at the reference's rate — same frame count, times within 1 ms
|
|
85
|
+
--map <map.json> rig-pose-map/1: its arm and leg segments are compared
|
|
86
|
+
--retarget-report <f> the retarget's --report: a limb finding inside a set's span suggests paint, outside rig
|
|
87
|
+
--reference-hands <f> --render-hands <f>
|
|
88
|
+
COCO-WholeBody keypoints ({ frames: [{ t, points: [[x, y, score] x 133] }] }) for
|
|
89
|
+
hand visibility from the 21 hand points; without them, from the wrist scores
|
|
90
|
+
--cloth-reference <f> --cloth-render <f> --cloth-windows <t0-t1,...>
|
|
91
|
+
cloth tracks (rig-c-cloth-track/0: hip_x and hem_mid per frame, in hip widths) and
|
|
92
|
+
the windows, in seconds, #34's lag, swing ratio and overshoot are read over; a render
|
|
93
|
+
track in rig world units needs --retarget-report (its scale); without the tracks,
|
|
94
|
+
cloth reads "not measured"
|
|
95
|
+
--report <file> write the comparison as JSON (rig-pose-compare/1)
|
|
96
|
+
--sheet <png> draw the reference and render skeletons side by side at each finding's worst frame
|
|
97
|
+
|
|
98
|
+
A finding is a run of frames over a bar lasting >= 0.15 s (gaps of one frame merged), worst first.
|
|
99
|
+
The bars (README, "Compare"): arm and leg segment direction > 20 deg; hand visibility reference >= 0.6
|
|
100
|
+
while render < 0.4; foot plant render > 0.05 while reference < 0.02 hip widths/frame; eye-line tilt
|
|
101
|
+
> 15 deg; torso length and hip height range under 0.5 x the reference's; cloth outside #34's bands.
|
|
102
|
+
Exit 0 whenever it compared, findings or not.
|
|
103
|
+
|
|
104
|
+
Exit codes: 0 written (compare: compared), 1 refused (every reason printed), 2 bad invocation.
|
|
77
105
|
`;
|
|
78
106
|
|
|
79
107
|
class Usage extends Error {}
|
|
@@ -128,7 +156,7 @@ function shortOf(f: Map<string, string>): number | 'off' {
|
|
|
128
156
|
}
|
|
129
157
|
|
|
130
158
|
function retargetArgs(argv: string[]): RetargetArgs {
|
|
131
|
-
const f = parseFlags(argv, ['rig', 'map', 'pose', 'out', 'name', 'person', 'base-motion', 'min-score', 'smooth', 'flip', 'short', 'bends', 'bend-tolerance', 'switch-tolerance', 'switch-min', 'switch-at', 'profile', 'build', 'render', 'report'], ['coco']);
|
|
159
|
+
const f = parseFlags(argv, ['rig', 'map', 'pose', 'out', 'name', 'person', 'base-motion', 'images', 'min-score', 'smooth', 'flip', 'short', 'bends', 'bend-tolerance', 'switch-tolerance', 'switch-min', 'switch-at', 'profile', 'build', 'render', 'report'], ['coco']);
|
|
132
160
|
const bends = f.get('bends') ?? 'refuse';
|
|
133
161
|
if (bends !== 'refuse' && bends !== 'clamp') throw new Usage(`--bends is "${bends}"; it must be refuse or clamp`);
|
|
134
162
|
const switchAt = f.get('switch-at') ?? 'first';
|
|
@@ -146,6 +174,7 @@ function retargetArgs(argv: string[]): RetargetArgs {
|
|
|
146
174
|
coco: f.has('coco'),
|
|
147
175
|
person: f.get('person'),
|
|
148
176
|
baseMotion: f.get('base-motion'),
|
|
177
|
+
images: f.get('images'),
|
|
149
178
|
minScore: num(f, 'min-score', 0.3, (n) => n >= 0, 'a number ≥ 0'),
|
|
150
179
|
smooth: smoothOf(f),
|
|
151
180
|
flip: flipOf(f),
|
|
@@ -162,6 +191,36 @@ function retargetArgs(argv: string[]): RetargetArgs {
|
|
|
162
191
|
};
|
|
163
192
|
}
|
|
164
193
|
|
|
194
|
+
function compareArgs(argv: string[]): CompareArgs {
|
|
195
|
+
const f = parseFlags(argv, ['reference', 'render', 'map', 'retarget-report', 'reference-hands', 'render-hands', 'cloth-reference', 'cloth-render', 'cloth-windows', 'report', 'sheet'], []);
|
|
196
|
+
const pair = (x: string, y: string) => {
|
|
197
|
+
if (f.has(x) !== f.has(y)) throw new Usage(`--${f.has(x) ? x : y} is given without --${f.has(x) ? y : x}; the two are read together, so both sides are measured the same way`);
|
|
198
|
+
};
|
|
199
|
+
pair('reference-hands', 'render-hands');
|
|
200
|
+
pair('cloth-reference', 'cloth-render');
|
|
201
|
+
let clothWindows: CompareArgs['clothWindows'];
|
|
202
|
+
if (f.has('cloth-reference') && !f.has('cloth-windows')) throw new Usage('--cloth-reference and --cloth-render need --cloth-windows <t0-t1,...>: the figures are read per window');
|
|
203
|
+
if (f.has('cloth-windows')) {
|
|
204
|
+
if (!f.has('cloth-reference')) throw new Usage('--cloth-windows is given without --cloth-reference and --cloth-render');
|
|
205
|
+
const w = parseWindows(f.get('cloth-windows')!);
|
|
206
|
+
if (typeof w === 'string') throw new Usage(`--cloth-windows is "${f.get('cloth-windows')}": ${w}; it must be t0-t1[,t0-t1...] in seconds`);
|
|
207
|
+
clothWindows = w;
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
reference: need(f, 'reference'),
|
|
211
|
+
render: need(f, 'render'),
|
|
212
|
+
map: need(f, 'map'),
|
|
213
|
+
retargetReport: f.get('retarget-report'),
|
|
214
|
+
referenceHands: f.get('reference-hands'),
|
|
215
|
+
renderHands: f.get('render-hands'),
|
|
216
|
+
clothReference: f.get('cloth-reference'),
|
|
217
|
+
clothRender: f.get('cloth-render'),
|
|
218
|
+
clothWindows,
|
|
219
|
+
report: f.get('report'),
|
|
220
|
+
sheet: f.get('sheet'),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
165
224
|
export function main(argv: string[]): number {
|
|
166
225
|
const [cmd, ...rest] = argv;
|
|
167
226
|
if (cmd === undefined || cmd === '--help' || cmd === '-h' || cmd === 'help') {
|
|
@@ -181,6 +240,10 @@ export function main(argv: string[]): number {
|
|
|
181
240
|
runRetarget(retargetArgs(rest), (l) => process.stdout.write(`${l}\n`));
|
|
182
241
|
return 0;
|
|
183
242
|
}
|
|
243
|
+
if (cmd === 'compare') {
|
|
244
|
+
runCompare(compareArgs(rest), (l) => process.stdout.write(`${l}\n`));
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
184
247
|
if (cmd === 'convert') {
|
|
185
248
|
const f = parseFlags(rest, ['coco', 'out'], []);
|
|
186
249
|
const input = need(f, 'coco');
|
|
@@ -190,7 +253,7 @@ export function main(argv: string[]): number {
|
|
|
190
253
|
process.stdout.write(`rig-pose convert: ${seq.frames.length} frame(s) of COCO-17 → body-18; wrote ${out}\n`);
|
|
191
254
|
return 0;
|
|
192
255
|
}
|
|
193
|
-
process.stderr.write(`rig-pose: unknown command "${cmd}" — the commands are: retarget, convert\n`);
|
|
256
|
+
process.stderr.write(`rig-pose: unknown command "${cmd}" — the commands are: retarget, convert, compare\n`);
|
|
194
257
|
return 2;
|
|
195
258
|
} catch (e) {
|
|
196
259
|
if (e instanceof Usage) {
|
|
@@ -199,7 +262,7 @@ export function main(argv: string[]): number {
|
|
|
199
262
|
}
|
|
200
263
|
if (e instanceof Refusal) {
|
|
201
264
|
for (const p of e.problems) process.stderr.write(`FAIL ${lineOf(p)}\n`);
|
|
202
|
-
process.stderr.write(`rig-pose ${cmd}: ${e.problems.length} problem(s) — nothing written\n`);
|
|
265
|
+
process.stderr.write(`rig-pose ${cmd}: ${e.problems.length} problem(s) — ${cmd === 'compare' ? 'not compared' : 'nothing written'}\n`);
|
|
203
266
|
return 1;
|
|
204
267
|
}
|
|
205
268
|
throw e;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rig-c/pose",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Puts motion from a body-18 pose sequence onto a rig built with rig-c: bone keys in rig-c's motion format, checked through rig-c's gate before anything is written. A CLI for agents that cannot see the rig.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|