Skip to content
AaronEliasZachariahPublic

About

Pull the best frame from a video clip and AI-upscale it into a post-ready, LinkedIn-sized still. HDR tone-mapping, face-protected upscaling (PyTorch+spandrel), modern PySide6 desktop app + CLI.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

FrameLift

Pull the best frame from a video clip and lift it to a post-ready, high-resolution still.

Built for the exact job: you shot a clip on your phone, and you want one crisp, well-composed, correctly-coloured image — sized for a LinkedIn post — without hand-scrubbing the timeline or fighting washed-out HDR colours.

FrameLift desktop app


What it does

video ──► probe ──► score every frame ──► pick best, diverse candidates
                                              │
                            contact sheet ◄───┤
                                              ▼
                       chosen frame ──► AI upscale ──► reframe + sharpen ──► LinkedIn JPEG
  1. Probe the clip and detect HDR (iPhone clips are usually HLG / BT.2020).
  2. Tone-map HDR → SDR correctly so the still isn't grey/washed-out (libplacebo BT.2390 for the final frame; fast zscale+Hable for the scoring sweep).
  3. Score every frame for sharpness (Tenengrad + FFT + Laplacian), exposure, contrast and faces, then pick the top diverse candidates (spread out in time).
  4. Contact sheet so you can eyeball the finalists and choose.
  5. AI upscale the chosen frame ~4× (GPU via PyTorch+spandrel, or CPU via ONNX, or Lanczos as a no-model fallback).
  6. Reframe to a LinkedIn aspect ratio (face-aware crop), sharpen gently, and export an sRGB JPEG.

Why these choices (June 2026 research)

  • Tone-mapping: HLG footage must be tone-mapped to BT.709/sRGB or it looks desaturated on LinkedIn. libplacebo=tonemapping=bt.2390 (GPU/Vulkan) gives the most natural colour; the CPU zscale + tonemap=hable chain is the robust fallback.
  • Upscaler model: for compressed phone footage, a model trained on JPEG/WEBP degradation beats a generic one. Default is 4xRealWebPhoto_v4_dat2 (DAT-2). realesrgan-x4plus is the fast, faithful alternative.
  • Runtime: the modern, maintained path is PyTorch (CUDA) + spandrel — it works on Python 3.13 / NumPy 2.x, unlike the abandoned basicsr/realesrgan packages (which import a torchvision module that no longer exists).
  • Faces: face restoration is opt-in / selective, not default — tiny crowd faces (20–60 px) can be hallucinated into a different person. Use it only on 1–3 foreground subjects, conservatively (CodeFormer w≈0.7).
  • LinkedIn: 1080×1350 (4:5 portrait) wins the most mobile feed space; export sRGB JPEG q≈88, under 3 MB. A 9:16 phone frame is reframed to 4:5, not letterboxed.
  • Caveat: no upscaler reconstructs unreadable on-screen slide text at 480p — it invents plausible-but-wrong glyphs. Crop/retype if legibility matters.

Install

Core (frame selection, tone-mapping, Lanczos/ONNX upscale) needs only:

pip install -r requirements.txt
# and FFmpeg on PATH (https://www.gyan.dev/ffmpeg/builds/)

GPU AI upscaling (recommended on an NVIDIA card) — use an isolated venv:

python -m venv .venv
.\.venv\Scripts\python -m pip install torch torchvision --index-url https://download.pytorch.org/whl/cu128
.\.venv\Scripts\python -m pip install -r requirements.txt spandrel "PySide6-Essentials==6.8.1.1"

PySide6 6.8.1.1 is pinned because newer builds are binary-incompatible with the Anaconda CPython used here ("procedure not found" on import). On a standard python.org install, the latest PySide6 is fine.

Usage

# Just inspect
python -m framelift probe "clip.mov"

# Score + build a contact sheet of the best candidates
python -m framelift analyze "clip.mov" --out output

# One-shot: pick a frame, upscale, export a LinkedIn-ready still
python -m framelift run "clip.mov" --pick 1 --preset portrait
python -m framelift run "clip.mov" --pick 7 --backend spandrel --model realweb-dat2
python -m framelift run "clip.mov" --no-upscale          # frame only
python -m framelift run "clip.mov" --backend lanczos      # no model download

Outputs land in output/: <stem>_contacts.png, <stem>_frame.png, <stem>_upscaled.png, <stem>_linkedin_<preset>.jpg.

Presets

preset size use
portrait 1080×1350 4:5 — max mobile feed real estate (default)
square 1080×1080 1:1 — safe all-rounder
landscape 1200×627 1.91:1 — link/landscape card
banner 1584×396 profile background banner
original as-is keep source aspect ratio

Desktop app (GUI)

A modern dark PySide6 (Qt) app — scrub to any exact frame, see the LinkedIn crop live, and export. (Install once: pip install "PySide6-Essentials==6.8.1.1".)

.\.venv\Scripts\python -m framelift gui "clip.mov"   # or just: python -m framelift gui
  • Slider + filmstrip scrub every frame instantly (frames are pre-decoded once, then swapped on the fly — snappy); ◀ ▶ step one frame at a time.
  • The dimmed preview shows the exact crop rectangle for the chosen format.
  • Custom toggle for AI upscale; the engine badge shows what's in use (auto: GPU spandrel here, CPU/Lanczos elsewhere).
  • Model selector: Detailed (DAT-2, crisp) vs Smooth (Real-ESRGAN, fewer artifacts) — switch if a frame looks over-processed.
  • Format pills include Orig — exports the exact frame at native resolution (no crop, no enlargement) as a lossless PNG. With AI upscale off the button reads Export frame (native); the LinkedIn presets export an sRGB JPEG.
  • Export opens a Save As dialog (name + folder, pre-filled to output/), runs on a worker thread (UI never blocks), then reveals the file.

Avoiding upscale artifacts

AI upscalers amplify whatever they're fed, so:

  • Protect faces (on by default). Far-away / small faces are the worst case — the model invents texture and they come out smeared or blended into the background. With protection on, faces are detected (YuNet) and rebuilt from a clean, lightly sharpened resize, then feathered over the AI result — the scene still gets crisp, faces come out a little better and natural instead of hallucinated. Turn it off to let the model fully resynthesize faces (only worth it for large, sharp close-ups).
  • Pick a sharp frame. Motion-blurred frames upscale into directional streaks — use the slider/★ and watch the quality readout.
  • Post-sharpening is kept very low on AI output (the model is already sharp); a strong UnsharpMask on top causes halo/colour-fringe streaks.
  • For the cleanest faces/edges try the Smooth model; Detailed maximises crispness.
  • No upscaler can make unreadable on-screen slide text legible at 480p.

Falls back to a basic Tkinter UI automatically if PySide6 isn't available — which is what the portable build below uses.

Ship it

On your machine (GPU, one-click):

powershell -ExecutionPolicy Bypass -File .\make_shortcut.ps1

Creates a Desktop FrameLift shortcut that launches the GUI with the GPU venv (no console window). Or pin FrameLift.pyw.

Portable build to hand to others (no Python, no GPU):

.\.venv\Scripts\python -m pip install pyinstaller onnxruntime
.\.venv\Scripts\python -m PyInstaller packaging\framelift.spec --noconfirm
# -> dist\FrameLift\FrameLift.exe  (zip the folder and share)

The portable build excludes PyTorch to stay ~150 MB and upscales via ONNX/Lanczos. Bundling CUDA PyTorch is multi-GB and fragile — for full GPU quality, run from the venv.

Layout

framelift/
  probe.py     ffprobe wrapper + HDR detection
  extract.py   HDR→SDR tone-mapping + frame streaming/re-extraction
  score.py     sharpness / exposure / contrast / face scoring
  select.py    full analysis + diverse top-N selection
  contact.py   labelled contact sheet
  upscale.py   pluggable backends: spandrel(GPU) / onnx / lanczos
  models.py    model registry + lazy download
  linkedin.py  reframe / sharpen / export to LinkedIn specs
  gui_qt.py    modern PySide6 desktop app (filmstrip + scrubber)  ← default
  gui.py       Tkinter fallback app (used by the portable build)
  cli.py       command-line interface
FrameLift.pyw      double-clickable GUI launcher
make_shortcut.ps1  creates a Desktop shortcut (GPU venv)
packaging/         PyInstaller spec for a portable build

About

Pull the best frame from a video clip and AI-upscale it into a post-ready, LinkedIn-sized still. HDR tone-mapping, face-protected upscaling (PyTorch+spandrel), modern PySide6 desktop app + CLI.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages