Turn a landscape video into a vertical one that follows a person you pick.
A native macOS app. Import a clip, let it scan for everyone who appears, choose your subject, and it renders a 9:16 crop that tracks them — smoothed like a camera operator rather than snapped to a bounding box.
decode ─► detect ─► describe ─► associate ─► smooth ─► compose ─► encode
12fps Vision feature identity virtual transform H.264
sample humans prints tracking camera ramps / HEVC
Detection — DetectHumanRectanglesRequest on frames sampled at 12fps, capped at
9000 frames so a long clip lowers its rate rather than scanning forever.
Appearance — GenerateImageFeaturePrintRequest over the head-and-torso crop,
computed only on frames where identity is actually contested.
Association — IdentityTracker builds
one cost matrix per frame blending predicted motion with appearance and solves it
optimally with the Hungarian method. Appearance's weight rises from 0.35 to 0.8 when
people are in contact, which is when motion alone flips identities. Tracks coast on
prediction through occlusions, and only learn appearance from uncontested crops.
Framing — CropPath turns the subject's
sightings into a virtual camera: resample onto a uniform grid, smooth, then drive with a
deadzone and a speed cap so the shot locks off when the subject is still and pans when
they move.
Render — the moving crop becomes affine transform ramps on a video composition layer
instruction, so the preview player and the exporter produce identical frames on the
hardware path. Export goes through AVAssetReader/AVAssetWriter rather than
AVAssetExportSession, so the output is exactly the requested size instead of whatever a
preset decides.
No third-party dependencies and no bundled ML models — Apple frameworks only.
9:16 (1080×1920), 4:5, or 1:1. H.264 or HEVC, in .mp4, with the source audio
re-encoded to AAC.
| Smoothing | Seconds of temporal averaging on the subject's position |
| Deadzone | How far the subject drifts before the camera moves at all |
| Pan speed | Ceiling on how fast the frame can travel |
| Zoom | 1.0× uses the full source height |
| Vertical bias | Nudges the subject up or down in frame when zoomed in |
The transport bar also carries playback speed (0.25×–2×, pitch-corrected) and volume. Both affect the preview only — the export always renders at the source's own timing and audio level.
macOS 26.5+, Xcode 26+.
git clone https://github.com/meteoroh/verticalize.git
cd verticalize
open verticalize.xcodeprojOr from the command line:
xcodebuild -project verticalize.xcodeproj -scheme verticalize -configuration Release buildNo DEVELOPMENT_TEAM is set, so a fresh clone builds and runs straight away under
"Sign to Run Locally" — the app is sandboxed, but ad-hoc signing satisfies that for local
use. Set your own team in Signing & Capabilities only if you want to distribute a build.
- No shot-cut detection. On edited footage the tracker will happily associate across a hard cut, and the camera pans across it instead of jumping. Fine for single takes.
- Appearance uses a general-purpose embedding. Vision's
FeaturePrintis not trained for person re-identification, so two people dressed alike can still be confused. A dedicated ReID Core ML model is the biggest available quality lever. - Automated tests use synthetic input. The tracking, geometry, and export layers are covered by scripted scenarios with known ground truth. Vision's detector and feature prints themselves are exercised only by running the app on real footage.
Most behaviour lives in two option structs, both worth adjusting against your own footage:
PersonScanner.Options— sample rate, analysis resolution, confidence floor, merge threshold.IdentityTracker.Options— motion gating, appearance weights and ceiling, coast window.
If one person gets split into two cards, raise appearanceCeiling. If the crop follows
the wrong person after an overlap, raise appearanceWeightAmbiguous.
MIT — see LICENSE.