Video stabilization looks like a visual editing task.
With Gyroflow, much of it can also become a repeatable code pipeline.
The application can read motion data embedded by GoPro, Sony, Insta360, DJI, Blackmagic, RED, and Canon cameras. It also accepts external data from Betaflight Blackbox, ArduPilot, phone sensors, and the open .gcsv format.
The interesting question is not only whether Gyroflow works from a desktop UI.
It is whether we can give it a video, motion data, lens profile, stabilization preset, codec settings, and destination entirely from code.
The short answer is mostly yes.
Gyroflow is a cross-platform, GPL-licensed video stabilizer built around recorded gyroscope data, lens correction, motion synchronization, rolling-shutter correction, adaptive zoom, and GPU-accelerated rendering.
Gyroflow GitHub Source Code Gyroflow Website Gyroflow Documentation Gyroflow CLI Documentation Gyroflow Releases License: GPL-3.0 with additional permissions
What is Gyroflow?
Gyroflow uses the camera’s motion over time to calculate how each video frame should be transformed.
That is different from asking an editor to infer every movement only from image features. If the camera recorded accurate IMU data, Gyroflow already knows how it rotated. It can combine that signal with a lens model, timing information, smoothing, horizon control, rolling-shutter correction, and adaptive cropping.
For FPV footage, an external Betaflight Blackbox log can provide the motion source. That connects naturally with the Betaflight Blackbox Explorer post, although the tools solve different problems: Blackbox Explorer helps inspect a flight log, while Gyroflow uses motion data to transform the resulting video.
Repository and Architecture
I cloned and inspected this revision without installing or running the application:
repo: https://github.com/gyroflow/gyroflow
branch: master
commit: d918ab3594e539f25a67a9f1d2b8e042798f61f7
application version: 1.6.3
license: GPL-3.0-or-later with additional permissions
The main layers are easy to identify:
| Layer | Implementation | Role |
|---|---|---|
| User interface | Qt 6 and QML | Desktop and mobile editing workflow |
| CLI | Rust argh parser plus Qt core event loop |
Headless batch control |
| Stabilization engine | gyroflow-core Rust crate |
Telemetry, sync, lens, smoothing, transforms, GPU kernels |
| Media rendering | Rust and FFmpeg | Decode, process, encode, audio, metadata |
| Project data | JSON .gyroflow files |
Presets, projects, settings, optional embedded motion data |
| Motion interchange | GCSV and supported camera/log formats | IMU samples and timestamps |
The split between core and rendering is important.
gyroflow-core does not depend on Qt or FFmpeg. The complete application does, because somebody still needs to decode compressed video, feed frames into the stabilizer, encode the result, carry audio, and manage hardware codecs.
Can We Do Everything as Code?
For a normal repeated render workflow, yes:
- select input videos or project files
- attach an external gyro log
- load a settings preset
- set synchronization parameters
- set codec, bitrate, GPU use, output size, and output path
- render multiple videos in parallel
- watch a folder for new footage
- export Gyroflow projects
- export parsed metadata or per-frame camera motion
- export STMaps for another compositor
- print progress to standard output
But there is no single official “Gyroflow API” covering every layer.
| If you write… | Recommended interface |
|---|---|
| Shell scripts | Gyroflow CLI plus .gyroflow preset files |
| Python | subprocess plus JSON generated with json.dumps() |
| Rust batch orchestration | Start with the CLI; use gyroflow-core only when you need native frame-level integration |
| A custom sensor logger | Generate GCSV, then pass it through --gyro-file |
| A web service | Build a queue around the CLI; Gyroflow does not ship a REST API |
There are no official Python bindings for the current Rust implementation.
The separate gyroflow-python repository is the legacy pre-1.0 application, not a Python SDK for modern Gyroflow. New Python automation should call the current executable.
Using the Gyroflow CLI
The CLI is built into the normal application binary. On Linux that is normally ./Gyroflow; on Windows it is Gyroflow.exe.
The simplest route is to prepare a .gyroflow project in the GUI, then render it headlessly:
./Gyroflow flight.gyroflow \
--stdout-progress \
--overwrite
This is a good automation boundary because the project already records the video reference, lens calibration, sync offsets, smoothing, crop, keyframes, and output settings.
Render a Video with a Preset and JSON
You can also provide the pieces separately:
./Gyroflow flight.mp4 \
--preset fpv.gyroflow \
--out-params '{"codec":"H.265/HEVC","bitrate":80,"use_gpu":true,"audio":true,"output_folder":"./renders","output_filename":"flight-stabilized.mp4"}' \
--sync-params '{"search_size":5,"processing_resolution":720,"max_sync_points":5,"auto_sync_points":true}' \
--stdout-progress \
--overwrite
That JSON is parsed and merged into Gyroflow’s defaults.
The output object can also control dimensions, pixel format, codec options, audio codec, interpolation, keyframe distance, FFmpeg encoder options, metadata comments, track preservation, padding, and separate trim exports.
If motion data lives in a separate file:
./Gyroflow flight.mp4 \
--gyro-file flight.bbl \
--preset fpv.gyroflow \
--stdout-progress
Gyroflow supports Betaflight .bbl and .bfl, ArduPilot logs, GCSV, and multiple vendor-specific formats.
Automate an Incoming Folder
The watcher turns Gyroflow into a small local media worker:
./Gyroflow --watch /srv/incoming-footage \
--preset fpv.gyroflow \
--out-params '{"output_folder":"../stabilized","codec":"H.265/HEVC","bitrate":80}' \
--stdout-progress
The code waits until a new file has stopped changing before adding it to the queue. That reduces the chance of opening a video while another machine is still copying it.
This is local process automation, not self-hosting in the usual web-app sense. There is no dashboard server, database, login, or HTTP endpoint to expose.
Python Wrapper for Gyroflow
Python is useful for discovering files, pairing videos with logs, generating JSON, launching jobs, and validating the results.
from __future__ import annotations
import json
import subprocess
from pathlib import Path
def stabilize(
gyroflow: Path,
source: Path,
destination: Path,
preset: Path,
) -> Path:
destination.parent.mkdir(parents=True, exist_ok=True)
output = {
"codec": "H.265/HEVC",
"bitrate": 80,
"use_gpu": True,
"audio": True,
"output_folder": str(destination.parent.resolve()),
"output_filename": destination.name,
}
sync = {
"search_size": 5,
"processing_resolution": 720,
"max_sync_points": 5,
"auto_sync_points": True,
}
command = [
str(gyroflow),
str(source.resolve()),
"--preset", str(preset.resolve()),
"--out-params", json.dumps(output, separators=(",", ":")),
"--sync-params", json.dumps(sync, separators=(",", ":")),
"--stdout-progress",
"--overwrite",
]
completed = subprocess.run(
command,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
log = completed.stdout
if completed.returncode != 0:
raise RuntimeError(f"Gyroflow exited with {completed.returncode}\n{log}")
if "Rendering failed:" in log or not destination.is_file():
raise RuntimeError(f"Expected output was not produced\n{log}")
return destination
Passing a list instead of shell=True keeps file paths and JSON from being interpreted again by a shell.
The file and log checks are intentional. In the source revision I inspected, render failures are printed from queue callbacks, but the top-level CLI does not explicitly set a non-zero process exit code for each job failure. A robust pipeline should not treat return code as its only success signal.
JSON Projects Are the Real Automation Format
Gyroflow projects and presets use the .gyroflow extension, but their outer format is JSON.
A project can contain:
- video path and video dimensions
- lens calibration data
- gyro source, orientation, filtering, and bias
- synchronization offsets
- smoothing method and parameters
- horizon lock and rolling-shutter settings
- adaptive zoom and lens correction
- keyframes and trim ranges
- rendering options
- optionally compressed raw or processed motion data
The current source exports project format version 4.
Export mode determines how self-contained the file is:
# Simple project
./Gyroflow flight.mp4 --export-project 1
# Include gyro data
./Gyroflow flight.mp4 --export-project 2
# Include processed gyro data
./Gyroflow flight.mp4 --export-project 3
# Render video and save a project
./Gyroflow flight.mp4 --export-project 4
Modes with embedded motion data remain JSON files, but some large values are compressed as base91-encoded CBOR strings. They are designed for Gyroflow interchange, not pleasant manual editing.
Presets are also JSON .gyroflow files. The main difference is that they do not contain a videofile, so the CLI recognizes them as settings to apply.
You can pass a small preset inline:
./Gyroflow flight.mp4 \
--preset '{"version":2,"stabilization":{"fov":1.2,"lens_correction_amount":1.0}}'
For serious work, I would create the preset in Gyroflow, inspect the resulting JSON, keep it in version control, and pin the Gyroflow release used by the automation.
Export Metadata Instead of Video
Gyroflow can be useful as a telemetry conversion tool even when you do not want a stabilized render.
Export parsed metadata:
./Gyroflow flight.mp4 --export-metadata 2:parsed.json
Export selected original and stabilized motion values:
./Gyroflow flight.mp4 \
--export-metadata 3:camera-motion.json \
--export-metadata-fields '{"original":{"gyroscope":true,"accelerometer":true,"quaternion":true,"euler_angles":true},"stabilized":{"quaternion":true,"euler_angles":true},"zooming":{"minimal_fovs":true,"fovs":true,"focal_length":true}}'
The output extension for type 3 selects CSV, JSON, USD, or JSX. That opens useful code workflows for data analysis, Blender/USD pipelines, and After Effects camera data.
Gyroflow can also generate EXR STMaps:
./Gyroflow flight.gyroflow --export-stmap 1:./stmaps
Mode 1 writes a single map pair; mode 2 generates per-frame maps.
What About Rust?
The current engine is Rust, and gyroflow-core is a real reusable crate inside the repository.
A Rust project can pin it directly:
[dependencies]
gyroflow-core = { git = "https://github.com/gyroflow/gyroflow.git", rev = "d918ab3594e539f25a67a9f1d2b8e042798f61f7", package = "gyroflow-core" }
That gives native access to telemetry parsing, IMU integration, lens models, synchronization, smoothing, transforms, zooming, keyframes, STMaps, and GPU processing.
It does not immediately give you this:
gyroflow::stabilize_video("in.mp4", "out.mp4")?;
The full video path still needs probing, decoding, pixel formats, timestamps, frame transforms, encoding, audio, and metadata. Gyroflow implements those pieces in its application rendering modules, while its RenderQueue is tied to Qt.
That is why I would use the CLI from Rust too unless the project genuinely needs frame-level integration. The separate OpenFX plugin proves that core integration is possible, but it is application-development work, not a tiny SDK call.
Installation Options, Without Installing It Here
For normal use, upstream offers Windows and Apple store packages, Android and iOS apps, macOS Homebrew, and downloadable archives for Windows, macOS, and Linux.
Linux users can download the release archive and invoke its Gyroflow binary from a terminal. Developers can build from source with Rust and just after installing the platform dependencies documented upstream.
I did not install packages, download a release binary, build the source, or run Gyroflow for this review. The conclusions here come from the official documentation and direct inspection of the cloned Rust/QML repository.
The Most Reliable Code-First Workflow
I would structure a production pipeline like this:
camera video + embedded IMU, or video + external GCSV/Blackbox log
-> version-controlled .gyroflow preset/project
-> Python/Rust/shell job runner
-> official Gyroflow CLI
-> progress log + explicit output validation
-> stabilized video, project, metadata, or STMaps
The GUI becomes a visual configuration editor, not a mandatory step for every clip.
Tune a representative clip once. Export the settings. Review the JSON. Pin a release. Then run new footage through the same configuration as code.
Conclusion
Gyroflow is more automatable than its polished desktop interface first suggests.
The official CLI covers rendering, batch inputs, watch folders, external motion logs, JSON overrides, presets, projects, metadata, STMaps, GPU selection, and progress output. Python can orchestrate that cleanly without a dedicated binding.
Rust offers a deeper route through gyroflow-core, but the full media pipeline is not packaged as a simple headless SDK. Use it when you need custom frame processing or a plugin; use the CLI when you need dependable application-level rendering.
So, can we do everything as code?
We can automate almost everything in a repeatable render pipeline. The remaining boundary is authoring and validating good stabilization choices—and the lack of one stable public API for every control exposed by the GUI.
FAQ
Does modern Gyroflow have a Python API?
No official Python binding was present in the current Rust repository.
Use Python’s subprocess module to call the Gyroflow executable and generate --out-params, --sync-params, and inline presets with json.dumps().
The gyroflow-python repository is the legacy implementation replaced by Gyroflow 1.0, not a binding for the current app.
Are .gyroflow files JSON?
Yes. Projects and presets have a JSON outer structure.
Projects with embedded gyro or processed data can contain compressed base91/CBOR strings, so they are not entirely convenient to edit manually. Exported presets are the friendliest configuration artifacts for version control.
Can Gyroflow process a whole folder automatically?
Yes. --watch PATH recursively watches a folder and queues supported new files after their sizes stop changing.
Combine it with a preset, JSON output options, and --stdout-progress for an unattended local workflow.
Can I use Betaflight Blackbox data from code?
Yes. Pass the .bbl or .bfl log using --gyro-file, or generate the documented GCSV interchange format from your own logger or script.
The video, motion log, lens profile, synchronization settings, and output options still need to describe the same recording correctly.
Does Gyroflow expose a REST API or run as a server?
No. Gyroflow is a desktop/mobile media application with a headless CLI, not a network service.
If remote jobs are needed, place your own authenticated queue or worker around the CLI. Do not expose an unrestricted command runner or watch folder directly to the internet.
Can gyroflow-core render an entire video by itself?
Not as a simple public one-call API.
The core crate provides stabilization calculations and frame-processing machinery. Full video rendering also needs the application’s FFmpeg decoding/encoding and queue layers, some of which are coupled to Qt.
Comments