Record and reconstruct photonic lantern taper images from diameter scan videos.
- Taper Recorder — System tray app that records screen regions to MP4: a diameter scan for reconstruction (tagged with its scan speed) or the taper being drawn (a plain video), each from its own region, outlined on screen while it runs, and logged. Includes one-click reconstruction.
- Taper Reconstruct — CLI tool that stitches video frames into a single composite image of the taper using phase correlation alignment and linear blending.
Requires Python ≥ 3.10.
# Clone and install
git clone https://github.com/your-org/taper_reconstruct.git
cd taper_reconstruct
pip install -e .mamba env create -f environment.yml
mamba activate taper_reconstruct
pip install -e .Download TaperRecorder.exe from the latest release. No Python installation required.
taper-recorder- A system tray icon (blue "T") appears in the taskbar
- Right-click → Select Scan Region... to draw the capture area for diameter scans; Select Process Region... for the view of the taper being drawn (the two are independent and remembered separately)
- Right-click → Show Region Overlays to confirm both are on the right spot (scan in cyan, process in amber)
- Right-click → Start Scan Recording... — asks for the scan speed, scan axis, an optional sample label and notes, a time limit, and the output folder — or Start Process Recording..., which asks only for a label, notes, frame rate and time limit
- Stop (or Pause) from the buttons beside the overlay, or right-click → Stop … Recording / Pause Recording
- Right-click → Reconstruct Last Scan... — shows the scan speed, axis and frame rate it is about to use, and where each came from, then builds the composite image
| Scan | Process | |
|---|---|---|
| What it captures | The diameter scan sweeping along the taper | The taper being drawn, as it happens |
| Region | Scan region | Process region |
| Asks for | Scan speed, axis, label, notes | Label, notes |
| Default frame rate | 15 fps | 5 fps (a draw runs for minutes) |
| File name | taper_scan_<time>_<speed>umps[_label].mp4 |
taper_process_<time>[_label].mp4 |
| Afterwards | Reconstructed into one image | Kept as a video |
Only one recording runs at a time. Both write a .meta.json sidecar and a
capture log; a process sidecar carries "kind": "process", and if such a
video is ever sent to Reconstruct from File... the confirmation dialog
says so in red.
A device usually gets its data folder only once the taper is finished, which
is after its process recording has to start. So the setup dialog has a
checkbox, Choose the folder when the recording finishes — on by default
for process recordings, off for scans, remembered per mode. A deferred
recording is written to <output folder>/unfiled/, and when it stops a
prompt asks where it goes: Choose Folder... opens a picker (create the
device folder right there) and moves the video with its sidecar and logs;
Later leaves it in unfiled/.
Anything left behind is filed from File Held Recordings... in the tray
menu: tick the recordings that belong together — typically the draw and the
scan that followed it — and pick one destination. The video and every file
sharing its name (.meta.json, .log, .reconstruct.log,
.reconstructed.png) always move as a set, nothing is ever overwritten, and
a video that is still being reconstructed is held back until that finishes.
The tray mentions how many recordings are waiting when the app starts.
Settings hold the defaults (scan speed, axis, frame rate, compression, etc.) and persist between sessions; the recording dialog starts from them and updates them with whatever you last used.
Every recording prompts for the scan speed, which goes straight into the file name:
taper_scan_20260822_143000_1000umps_lantern7.mp4
│ │ └── optional sample label
│ └── 1000 µm/s
└── start time
Speeds with a fraction use p for the decimal point (12p5umps = 12.5 µm/s).
While the overlays are on, each configured region is outlined on screen (cyan for scan and amber for process when idle, red while recording, yellow while paused) with a status panel showing elapsed time, frame count, measured frame rate, scan speed, when the time limit will stop it, and a live thumbnail of what is being captured. During a recording a strip with Pause/Resume and Stop buttons sits beside the panel. Everything is drawn outside the captured region, so none of it appears in the recording. If the region fills the screen and the panel has nowhere to go, only the outline is drawn.
The recording overlay comes down with the recording. Whether the idle outlines stay up afterwards is the Keep the region overlays on screen after the recording setting (off by default), in both Settings and the setup dialog.
Pause stops frames being written; the file stays open and the elapsed clock freezes, so the measured frame rate and the sidecar's frame timestamps are unaffected. Each pause is recorded in the sidecar. Pausing a scan is allowed but flagged, since frames either side of the gap will not line up: the capture log says so, and the reconstruction dialog warns in red.
Every recording has a time limit — Stop after in the setup dialog, 5 min by default for both modes, remembered per mode, 0 for none — as a safety net for one left running. Paused time does not count. The overlay shows "stops at MM:SS", switching to a countdown in the last minute, and when it fires the recording ends exactly as Stop would, with the usual filing prompt if the folder was deferred.
| File | Contents |
|---|---|
<name>.mp4 |
The recording |
<name>.meta.json |
Scan speed, label, region, target vs. measured frame rate, frame timestamps, reconstruction settings |
<name>.log |
Capture log: environment, frame size, progress, capture statistics |
<name>.reconstruct.log |
Written when that video is reconstructed — inputs, alignment diagnostics, and the traceback if it fails |
<name>.reconstructed.png |
The composite image |
These always travel together: filing a held recording moves the whole set.
Reconstruction reads <name>.meta.json for the scan speed, scan axis and the
frame rate actually achieved during capture, so a recording that ran slower
than the requested rate still gets the right pixel scale.
Choosing Reconstruct Last Recording... or Reconstruct from File... opens a confirmation dialog listing the scan speed, scan axis and frame rate, each tagged with its source: the recording's sidecar, the speed in the file name, or the video container. A value that had to fall back to a Settings default — a video from before this version, or one recorded elsewhere — is flagged in red so it gets checked rather than trusted. Edit anything that is wrong and press Enter. Automatic reconstruction after a recording skips the dialog, since those values were just entered.
Everything is also appended to a rolling application log at
~/.taper_tools/logs/taper_tools.log (2 MB × 5 files; override the location with
the TAPER_TOOLS_LOG_DIR environment variable). Reach it from the tray menu via
Logs → View Last Log... or Logs → Open Log Folder. When a reconstruction
fails, the error dialog names the log holding the traceback.
# Recording made by the taper recorder — speed, frame rate and axis come
# from the .meta.json sidecar (or the speed in the file name)
taper-reconstruct taper_scan_20260822_143000_1000umps.mp4 -o taper.png
# Auto mode (recommended) — derives pixel scale from scan speed
taper-reconstruct video.mp4 --speed 1000 -o taper.png
# Constant mode — uses known pixel scale
taper-reconstruct video.mp4 --mode constant --speed 1000 --pixel_scale 1.5
# With compression along scan axis
taper-reconstruct video.mp4 --speed 1000 --compress 10
# Full options
taper-reconstruct --helpEach run writes <output>.log with the inputs, the alignment diagnostics and,
if the run fails, the traceback (--log PATH to move it, --no-log to skip it,
--verbose for debug detail on the console). --ignore-sidecar disables the
metadata lookup and --fps overrides the frame rate used for the pixel scale.
| Mode | Description | Required args |
|---|---|---|
auto (default) |
Phase correlation + outlier replacement using known scan speed | --speed |
correlation |
Phase correlation only | — |
constant |
Fixed shift from known speed and pixel scale | --speed, --pixel_scale |
pip install pyinstaller
pyinstaller --onefile --windowed \
--name TaperRecorder \
--hidden-import=taper_tools.reconstruct \
--hidden-import=scipy.stats \
taper_tools/recorder.pyThe executable will be in dist/TaperRecorder.exe.
Push a version tag to trigger an automated build:
git tag v0.1.0
git push --tagsThis runs the workflow in .github/workflows/build-executables.yml, which builds the Windows executable and creates a GitHub Release with the .exe.
You can also trigger a build manually from the Actions tab using "workflow_dispatch".
- numpy — array operations
- opencv-python-headless — video I/O and phase correlation
- PyQt6 — system tray GUI
- mss — screen capture
- tqdm — progress bars
- scipy — KDE for shift peak detection (optional, falls back to histogram)
MIT