Tutorial: flag frames and export¶
This covers marking moments in a recording — a dropped tracking point, a bad frame, an event worth returning to — and getting them, or the media around them, out of AvialSync.
The frame-flagging workflow exists for a specific job: producing a corrections list for retraining a pose model. The exported CSV names the exact frame in each camera, which is what a tool like DeepLabCut or LightningPose needs to be told what to fix.
Flag a frame¶

Flag Frame (shortcut
M) records an annotation at the current time.Snapshot (
Ctrl+E) writes a composed figure of the current moment.
You can also click directly on a plot at the moment you care about, which adds a marker there without moving the playhead first.
What a flag actually stores. Not just a timestamp: for every video loaded at that moment, AvialSync records the file path, the exact frame index, and that frame’s own presentation timestamp. That is the point — “2.47 seconds” is ambiguous across four cameras with different offsets and frame rates, while “camera_2.mp4, frame 74” is not. This is why the frame the app displays and the frame number it reports are resolved by the same lookup: they cannot disagree.
Mark a range¶

[sets the start of a range at the current time.]sets the end.
Use × to clear it. A range is what the clip and data-slice exports operate on, and it also drives
A/B loop playback for reviewing the same span repeatedly.
Review and label what you flagged¶
The Changes tab in the left panel lists everything you have marked, in time order, alongside any
tracking corrections you have made. Double-click a row’s label to name it — occluded, bad_paw,
stim_onset, whatever your analysis needs. Delete removes the selected row, and Ctrl+Z puts
it back.
Export¶
The Exports inspector page shows every output as a visible button. The same commands are in File and the command palette; their labels and availability come from the same actions.

Export |
What you get |
Use it for |
|---|---|---|
Export Changes… |
Annotation rows, one edited pose copy per source with corrections or accepted identity swaps, and a DeepLabCut or single-view Lightning Pose retraining set when frames were hand-corrected |
Analysis and pose-model retraining |
Export Snapshot… |
A composed figure of the current moment |
Figures, notes, lab reports |
Export Trimmed Video Clip… |
The marked range, copied out of the source |
Sharing a moment without re-encoding it |
Export Stimulus Grid… |
Selected sensor-triggered windows: camera and imaging rows by event columns, above one full-width band per chosen sensor group |
Comparing repeated stimuli across cameras, imaging and sensors |
Export Data Slice… |
The marked range of the loaded signals |
Analysis in another tool |
Export Changes… is also the Export… button in the Changes tab; it is one action, so the two cannot offer different things. It lists only the recordings that actually have something to export, and nothing is written until you choose it. Correcting a tracked point covers the pose outputs in full.
An export that has nothing to work on is greyed out, and says why. Export Data Slice needs loaded signals, Export Trimmed Video Clip needs a video and a marked A/B range, Export Snapshot needs something on screen. Hover a greyed item and its tooltip names what is missing, so you find out before you commit to the gesture rather than after.
Exports run in the background and report when they finish. Each one appears under Tasks in the status bar while it runs, with a Cancel in the status area, and the result arrives as a line at the bottom of the window rather than a dialog you have to dismiss. If several finish at once they queue: one message shows, a count beside it says how many are waiting, and Dismiss brings up the next. A failure stays until you dismiss it and keeps the technical detail behind Show details, ready to paste into a bug report. Each successful export has a Reveal action that opens its containing folder. The save dialog remembers a separate last folder for each export kind. A failed or cancelled write leaves an existing output intact, and a destination that names a loaded source or the disposable cache is rejected before writing.
Exports carry an avialsync schema ID, program version, UTC write time, and source names and sizes
where their format permits it. Corrected pose and retraining CSVs keep their strict
three-row headers (four for multi-animal DeepLabCut) and get a sibling <name>_avialsync.json
provenance file. Annotation CSVs also
get a JSON companion. Data slice CSVs have a final provenance comment; Parquet embeds the record
in schema metadata; PNG snapshots carry PNG text fields; MP4 clips record their actual keyframe
start in the container comment. CSV files use UTF-8 without a BOM. For a retraining set, enter your
project scorer and select a new export folder. Copy its contents into your project root; the
CSV’s image paths then point to the included PNGs. Lightning Pose (single view) gets
CollectedData.csv at the root. DeepLabCut gets
labeled-data/<video>/CollectedData_<scorer>.csv and still needs convertcsv2h5 before training.
Unedited points on corrected frames remain model predictions in the label file; review them before
training. A missing frame or failed image leaves no incomplete training folder, and the JSON
companion lists exported frames and counts.
A snapshot is composed, not grabbed. Every displayed camera goes in at the resolution it decoded at, with the 3D pose and the whole channel stack including rows you would have to scroll to reach — so nothing is cut off at a pane edge, and each camera is captioned with its own frame number, timecode, and format.
A marker with no video loaded still exports, with the video columns left empty — so a flag is never silently dropped for lack of a camera.
Clips are copied, not re-encoded. The exported video holds the original pixels rather than a second generation of them, which matters when someone measures from it later. The cut is aligned to the nearest keyframe at or before your start point, because a clip beginning mid-GOP would have no frame to decode from.
Export an event-aligned video grid with its TTL trace¶
Run avialsync demo for four generated cameras and a channel named TTL, or open your own
aligned videos and stimulus channel. Choose File → Export Stimulus Grid…. In the dialog, select
the stimulus channel, set a rising threshold and minimum event spacing, then choose Scan events.
Select up to twelve events and set the time before and after each one. Cursor update rate
defaults to 10 fps and caps the composite output rate. Each camera is sampled at emitted output
times; source transitions between them are skipped. Playback speed stretches or compresses
the mapped timestamps for the whole grid. The dialog shows the resulting video length before you
export.

Choose Continue to export, then save the MP4. The default half-second before and 1.5 seconds after each event at 1x produce a two-second video. At the default 10 fps, each output frame samples every camera at that instant; a 230 fps source is therefore rate-limited instead of writing all 230 transitions per second. Custom… → 0.130435x stretches the same two-second source window to about 15.33 seconds. At 30 fps, that slow-motion rate can represent the 230 fps source’s transitions at about 33 ms each. Faster playback may skip more transitions. The shared TTL cursor follows the selected speed, and its labels remain relative to stimulus onset.
Saving over an existing MP4 replaces it when the export finishes. AvialSync confirms the
replacement in its notification strip. If that MP4 is already open in a video player, reopen it
to see the new version. Choose an output path separate from the camera source videos; AvialSync
rejects source/output collisions. A cancelled or failed export leaves the existing file intact.
While encoding, a temporary .tmp.mp4 file is written beside the target and renamed to the final
MP4 only after a successful encode.

Each camera becomes a row and each selected event becomes a column, so a three-camera, twelve-event export is a wide grid. Video tiles are separated by just one pixel. Camera names remain in the left gutter and event labels remain above their columns; frame-number badges are not burned into the video. Each camera row follows that camera’s aspect ratio, so mixed-aspect footage fills its tile without cropping, stretching, or letterboxing. Camera names and event labels remain aligned outside the tiles; the static trace is rendered once, while the cursor follows the output time.
Choosing the rows. The Rows list beside the event controls offers only what the session has: each camera, each imaging stack, and each sensor group (one data source – an accelerometer’s x/y/z, a probe’s channels) with its streams beneath it. Tick what the movie should carry; the cameras on screen, the imaging stack shown in the viewer and the trigger channel’s group are ticked to begin with, every stream of a ticked group included. Move up and Move down reorder rows (pictures always above sensor bands). Under the list, the dialog states how large each row’s cells will come out and warns, before anything is encoded, when one will be too small to read – a wide imaging mosaic across twelve events usually is; choose fewer events or High detail. Choices are kept for the next export in the same session.
Imaging rows show the stack exactly as the image viewer does – channels, colours, levels, frame averaging, the tree layout, and the viewer’s zoom and pan – with each frame held until the next, as a camera’s is. A column whose window has no imaging says No imaging data.
Sensor bands run the full width below the picture rows, one per ticked group, every selected event overlaid on a shared relative-time axis. Each stream has its own colour and is named in the band; each event is drawn thin and translucent, and the stream’s mean across events bold on top. Up to four streams share one y-axis; more are stacked in lanes on one shared amplitude scale, as a multichannel recording viewer shows them (Auto; choose Overlay or Stacked per group to override). The red line marks the trigger in every band and the light cursor moves in step with the pictures above.
All selected signal windows are overlaid in each full-width band below the video grid on a shared relative-time axis. The axis labels and moving light cursor show time relative to stimulus onset; the red line marks zero. The dashed horizontal line is the detection threshold, drawn for the trigger channel. Bands use each channel’s actual cached samples and accepted time mapping, with gaps left open. The generated example below uses synthetic footage of a three-prong marker from three camera angles on a muted gray background. The marker moves and an off-white point appears only around each trigger; those details are part of the generated source videos, not export graphics.

Missing video coverage is labeled in its tile. The MP4 uses each camera’s accepted time mapping and
display levels; the source recordings are never modified. The grid is resized and encoded as an
H.264 MP4, so it is a visual comparison rather than a pixel-identical copy of the source. Output
detail defaults to Standard, which keeps the existing compact bounds. High detail (UHD 4K)
allows a 3840×2160 composite and larger tiles; a three-camera, twelve-event grid gets about
307×173 pixels per tile instead of about 200×112. Larger output dimensions can increase file size,
so keep Standard for compact comparisons. H.264 uses the ultrafast preset at CRF 17 for higher visual
quality while retaining source frame timing. The screenshots and demo video above show the current
renderer and are captured through the app’s File → Export Stimulus Grid action, using synthetic data
generated by conda run -n avialsync python tools/generate_stimulus_grid_demo.py.
What is not exported¶
AvialSync does not write your analysis, and it never modifies a source recording. Offsets, drift,
accepted mappings, and annotations live in the session file (.avv) beside your data — so the
alignment a colleague sees is the one you accepted, with the evidence behind it.
Tracking corrections are the one thing kept outside the session, in pose_csv_avialfix.csv (for a pose.csv) next
to the pose file, so they travel with the recording rather than with the session. Your pose files
themselves are still never modified.