Instructions to use Viggle/Meridian with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use Viggle/Meridian with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("Viggle/Meridian", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
File size: 15,637 Bytes
9f57754 1f487cc 9f57754 1f487cc 9f57754 1f487cc 9f57754 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | # Inference: camera and time
[← Meridian](../README.md) · [Installation](installation.md) · [Method](../README.md#method) · [Studio demo](../README.md#self-hosting-the-demo)
Run the examples from the release directory after completing installation. The CLI selects one GPU
through `CUDA_VISIBLE_DEVICES`; it does not split a take across cards.
```bash
CUDA_VISIBLE_DEVICES=0 python inference/sample.py \
--video examples/media/sp_bouldering_hang.mp4 \
--yaw 15 --sweep --out out/orbit
```
The default is a 73-frame, 24 fps take using the distilled student: `--steps 4 --flow-shift 3`.
Use a fresh `--out` directory for each take; filenames are fixed rather than automatically versioned.
## Prepare the input
Use a single continuous shot. The **CLI does not normalize frame rate or detect cuts**: it reads
decoded frames by index and always writes at 24 fps. A 30 fps or variable-frame-rate input can
therefore change pace and lose audio alignment unless you normalize it first.
```bash
# Preserve playback duration while exporting a constant 24 fps input.
ffmpeg -i clip.mp4 -vf "setpts=PTS-STARTPTS,fps=24" \
-c:v libx264 -crf 18 -pix_fmt yuv420p -c:a aac clip_24fps.mp4
# Inspect the actual decoded frame count, not only the container's FPS label.
ffprobe -v error -select_streams v:0 -count_frames \
-show_entries stream=width,height,r_frame_rate,nb_read_frames \
-of default=noprint_wrappers=1 clip_24fps.mp4
```
For a normal take, the input must contain at least `start + frames` decoded frames. The two included
sample clips each contain exactly **73 frames at 24 fps** and no audio. A longer take needs a longer
input or an explicit hold; simply increasing `--frames` on those samples will not extend the action.
## Camera recipes
The following commands all work with the included 73-frame sample as an input, subject to the
hardware and model setup. Motion quality still depends on reconstruction and viewpoint coverage.
### Orbit with a gentle start and stop
```bash
python inference/sample.py --video examples/media/sp_bouldering_hang.mp4 \
--yaw 15 --sweep --ease --out out/eased_orbit
```
Positive yaw moves the camera **left** around the pivot. Without `--sweep`, the offset is applied
throughout the clip instead of ramping from the original view. It remains an offset from each source
camera, not necessarily a camera fixed in world space.
### Push in without changing the lens
```bash
python inference/sample.py --video examples/media/sp_bouldering_hang.mp4 \
--dolly 0.8 --zoom 1 --sweep --ease --out out/push_in
```
**`--dolly` alone performs a dolly zoom:** it changes both the camera radius and focal length to
approximately preserve the pivot plane's size. Add `--zoom 1` for a fixed-lens push-in, where the
subject grows in frame. `--zoom 1.5` without translation is an optical zoom; these pure-zoom takes
can be ignored by the model. Prefer moves with parallax.
### Slide or crane while keeping the subject framed
```bash
# Move right by 0.15 pivot-depth units and aim back toward the pivot.
python inference/sample.py --video examples/media/sp_bouldering_hang.mp4 \
--truck 0.15 --aim --sweep --ease --out out/slide
# Raise the camera by 0.15 pivot-depth units.
python inference/sample.py --video examples/media/sp_bouldering_hang.mp4 \
--boom 0.15 --aim --sweep --ease --out out/crane
```
### Choose the orbit center
```bash
python inference/sample.py --video examples/media/sp_bouldering_hang.mp4 \
--pivot 0.5,0.5 --pivot-lock --yaw 15 --sweep --out out/pivot_orbit
```
`--pivot fx,fy` uses fractions of the **center-cropped picture**, not the full letterbox or original
uncropped image. Choose a point on the subject, away from image boundaries, sky, and missing depth.
The example selects the crop center; adjust it for your footage. `--pivot` sets the depth scale;
`--pivot-lock` additionally moves the orbit center to the picked 3D point.
**Choose the point in the frame used for pivot depth.** Without `--freeze`, this is
the first selected source frame (`--start`). With `--freeze F:N`, it is frame `F`.
An athlete-centered point at the held apex can land on the distant audience in the
approach frame: do not reuse it unchanged when removing the hold. The depth is a
median over a neighborhood extending roughly 5% of the picture in each direction,
so check that neighborhood as well as the exact pixel. `--pivot-lock` is not dynamic
subject tracking; inspect the projected reference throughout the shot before
treating the requested trajectory as a successful composition.
## Timing
All CLI source indices are **zero-based absolute frame indices** in the supplied file.
### Select a passage
```bash
# On an input with at least 121 frames, use source frames 48 through 120 inclusive.
python inference/sample.py --video clip_24fps.mp4 \
--start 48 --frames 73 --yaw 15 --sweep --out out/later_moment
```
`--start 48` is two seconds into a 24 fps input. It is not a seek time in seconds.
### Hold a moment while moving the camera
```bash
# 24 live frames, then source frame 24 repeated for 49 output frames; no tail.
python inference/sample.py --video examples/media/sp_bouldering_reach.mp4 \
--yaw 35 --freeze 24:49 --out out/bullet
# Hold one instant for the entire take; the camera still sweeps through 20 degrees.
python inference/sample.py --video examples/media/sp_bouldering_reach.mp4 \
--yaw 20 --freeze 24:73 --start 24 --out out/held_moment
```
For `--freeze F:N`, the output consists of:
1. Source frames `start` through `F - 1`, once each.
2. Source frame `F`, repeated `N` times.
3. Source frames after `F`, once each, until the requested output length is reached.
The tail length is `frames - N - (F - start)`. Use a positive `N`, `F >= start`, and a non-negative
tail. The source must reach frame `F + tail`. With a hold, fewer distinct source frames can produce
a longer output, but the repeated interval contains no new event motion.
By default, the camera ramp progresses only during the held interval. Add `--sweep` to move during
the live lead-in and tail too; `--live-speed` sets their ramp speed relative to the held interval
(default `0.33`). It controls the **camera ramp**, not playback speed.
The CLI can also decode a still image, for example
`--video still.png --freeze 0:73 --yaw 15`. This is a camera move over a held image, not animation of
the subject. The studio's video upload workflow does not offer this short-input path.
### Slow motion and speed-ups
**Set the event's pace first, then author the camera.** Export the retimed source at a constant
24 fps and use that export as the input to either the CLI or the studio.
```bash
# 0.5x: twice the duration.
ffmpeg -i clip.mp4 -vf "setpts=2*(PTS-STARTPTS),fps=24" -an clip_slow.mp4
# 2x: half the duration.
ffmpeg -i clip.mp4 -vf "setpts=0.5*(PTS-STARTPTS),fps=24" -an clip_fast.mp4
python inference/sample.py --video clip_slow.mp4 \
--yaw 15 --sweep --out out/slow_orbit
python inference/sample.py --video clip_fast.mp4 \
--yaw 15 --sweep --out out/fast_orbit
```
Changing playback metadata alone is not enough for the CLI: timing must be baked into the **decoded
frame sequence**. `fps=24` duplicates or drops frames; it does not interpolate new motion. Slow-motion
smoothness depends on the source frame rate and any interpolation applied before inference.
Check the retimed file's frame count before rendering. In particular, acceleration shortens the input:
the included 73-frame samples become too short for an ordinary 73-frame take at 2x. Use longer footage
or explicitly hold a moment. The commands above omit audio; retime a soundtrack separately if needed.
In the studio, one source frame per output frame preserves the export's retimed pace. Stretching keys
across the unchanged source is a different workflow: it duplicates or skips reconstructed frames and
can trigger a speed warning.
## Output lengths and resolution
| `--frames` | Duration at 24 fps |
|---|---|
| 73 | 3.04 s |
| 90 | 3.75 s |
| 107 | 4.46 s |
| 124 | 5.17 s |
| 141 | 5.88 s |
| 158 | 6.58 s |
| 175 | 7.29 s |
| 243 | 10.13 s |
These are the lengths with shipped text and audio-layout assets. Other values are not accepted by
the CLI. This is a per-take limit, not a limit on the total duration of the input file.
Output uses an aspect-matched 768-class bucket, usually about 1.03 million pixels; 16:9 maps to
1344 × 768. Both references use the smaller 480-class bucket: 832 × 480 for a 16:9 input.
## Output files
| File in `--out` | Contents |
|---|---|
| `out.mp4` | Generated take, 24 fps, no generated audio. |
| `render.mp4` | Geometry reference at conditioning resolution, including grey holes. |
| `source.mp4` | Source images after the selected frame mapping and output crop. A hold is visible here too. |
| `grid.mp4` | Source, geometry reference, and generated take side by side. |
| `out_audio.mp4` | For non-freeze commands: source-window audio muxed onto the take, when the input has audio. A silent input remains silent. |
| `last.png` | Final generated frame. Reusing it is possible, but does not guarantee cross-take consistency. |
| `cams.npz` | Source/target camera matrices, intrinsics, pivot metadata, crop, canvas, FPS, and command arguments. |
`cams.npz` stores `c2w_src` and `c2w_dst` as camera-to-world matrices; `intr_src` and `intr_dst` are
in the 512-space geometry grid. The `*_px` arrays are exported for the output canvas. Translation
units are reconstruction-relative, not meters. The archive is diagnostic metadata, not a scene model.
For reproducibility, retain the input export, command, seed, checkpoint revisions, and environment.
Do not assume the CLI and studio, or different dependency/backend versions, produce bit-identical
results from the same seed.
## CLI reference
Run `python inference/sample.py --help` for the parser's complete help. The tables below group the
options by purpose; boolean flags are off unless stated otherwise.
### Input and model
| Option | Default | Meaning |
|---|---|---|
| `--video` | Required | Input video or decodable still image. |
| `--out` | Required | Output directory. |
| `--start` | `0` | First source-frame index. |
| `--frames` | `73` | Supported output length from the table above. |
| `--seed` | `1234` | Random seed. |
| `--ckpt` | `transformer/` inside `--model-dir` | A local Diffusers transformer directory. The default is the unmodified MiniMax-H3 transformer. |
| `--lora` | `<release>/teacher_lora <release>/turbo_lora` | One or more adapter directories, applied together at weight 1.0 in the order given. |
| `--no-lora` | Off | Load no adapter: stock MiniMax-H3, which cannot re-camera. Diagnostic only. |
| `--steps` | `4` | Scheduler grid points, including the terminal point. |
| `--flow-shift` | `3` | Video schedule shift; use `12` for the teacher. |
| `--model-dir` | `MiniMaxAI/MiniMax-H3` | Hub repo or local directory containing `vae/`. |
| `--vggt-repo`, `--vggt` | Environment-based | VGGT-Omega source checkout and checkpoint; see [Installation](installation.md#2-obtain-vggt-omega-separately). |
| `--attn-backend` | `_native_cudnn` | Diffusers attention backend. Alternatives are hardware- and version-dependent. |
To use the teacher alone — the re-camera adapter without the distillation, 50 steps instead of 3 forwards:
```bash
python inference/sample.py --video examples/media/sp_bouldering_hang.mp4 \
--yaw 15 --sweep --lora teacher_lora --steps 50 --flow-shift 12 --out out/teacher
```
### Camera and motion
| Option | Default | Meaning |
|---|---|---|
| `--yaw` | `0` | Orbit angle in degrees; positive moves left. |
| `--yaw-from` | `0` | Initial yaw when ramping; the live lead-in holds this value unless also swept. |
| `--truck` | `0` | Sideways shift in pivot-depth units; positive moves right. |
| `--boom` | `0` | Vertical shift in pivot-depth units; positive raises the camera. |
| `--dolly` | `1` | Orbit-radius scale; below one moves closer and, by default, widens the lens. |
| `--zoom` | `0` | Zero means automatic dolly-linked focal scaling; a positive value specifies the final focal multiplier. |
| `--pivot` | Unset | `fx,fy` in the crop; selects the depth-scale neighborhood. |
| `--pivot-lock` | Off | With `--pivot`, orbit about the selected 3D point. |
| `--aim` | Off | Reorient toward the pivot after translation. |
| `--pivot-to` | Unset | With `--aim`, a second `fx,fy` point toward which the aim transitions. |
| `--sweep` | Off | Ramp from the initial to the final offset over the take. |
| `--ease` | Off | Cosine ease-in/out applied to the ramp; it does not create a ramp by itself. |
| `--bounce` | Off | There-and-back ramp, `0 → 1 → 0`; implies a ramp even without `--sweep`. |
| `--swing` | Off | Sine ramp, `0 → 1 → 0 → −1 → 0`; implies a ramp. |
| `--freeze` | Unset | `F:N`: hold source frame `F` for `N` output frames. |
| `--live-speed` | `0.33` | With `--freeze --sweep`, relative camera-ramp speed outside the hold. |
Use one basic ramp shape at a time. Combining `--ease`, `--bounce`, and `--swing` composes their
functions in code order; it does not select between independent motion presets.
### Advanced and diagnostic controls
| Option | Default | Meaning and caveat |
|---|---|---|
| `--gauge-only` | Off | Reconstruct, warp, print geometry gauges, then stop before loading H3. No normal output artifacts are written. |
| `--follow` | Off | Replay estimated source cameras over the **first selected frame's fixed geometry and RGB**. Ignores the authored yaw/translation/lens controls; it does not retain the event's live motion. |
| `--smooth` | `8` | With `--follow`, Gaussian smoothing sigma in frames for estimated camera poses and intrinsics. `0` disables smoothing. |
| `--cull` | Off | Reject surfaces seen from behind according to estimated depth-map normals. This removes misleading splats; it does not reveal hidden surfaces. |
| `--fast-back` | `1` | Above one, compress the middle half of the camera ramp. Does not improve the reconstruction of an unseen back view. |
| `--canvas` | Automatic | Explicit `WxH`, with dimensions divisible by 32; advanced override outside the reported default benchmarks. |
| `--full` | `0` → 1280 | Override the square letterbox side. Higher values increase point-cloud sampling and memory, not VGGT's 512-pixel input resolution. |
The CLI prints `ahead`, coverage, and other geometry diagnostics but **does not reject a take using
the studio's clearance/motion thresholds**. Inspect the diagnostics and `render.mp4`; do not treat a
successful process exit as a quality check.
## Improving a take
1. **Inspect the geometry reference first.** A bent subject or unstable depth in `render.mp4` usually
needs a better source shot or a smaller move, not more denoising steps.
2. **Use modest viewpoint changes.** Large orbits reveal surfaces absent from the source. A plausible
completion can still be wrong; roughly 40° is a reported caution point, not a universal threshold.
3. **Check the depth scale.** If a small numerical move sends the camera through the scene, pick a
pivot on the subject and reduce the translation.
4. **Add parallax to lens changes.** Use a small dolly rather than relying on a pure optical zoom.
5. **Check timing before inference.** Stuttering from repeated input frames is not a geometry failure;
use higher-frame-rate footage or an interpolated export when smooth slow motion matters.
6. **Do not cross cuts.** Split the input into continuous shots yourself when using the CLI.
For dependency and memory errors, see [Setup problems](installation.md#setup-problems).
|