A temporal meteor detection module for Allsky.
Unlike single-frame streak detectors, this module tells meteors apart from aircraft and satellites — the thing single-frame detectors fundamentally cannot do.
Detection mask built by tools/build_mask.py: green = usable sky, red = ignored
(trees, horizon, lens vignette).
The detector bundled with Allsky — and the one in indi-allsky — look for straight lines in a single image (Canny → Hough). That approach cannot distinguish a meteor from an aircraft, a satellite, a cloud edge or a power line, because in one frame they all look like a bright streak. The result is either constant false alarms or a threshold so high that real meteors are missed.
The physical thing that makes a meteor a meteor is transience: it is present in one frame. A satellite or aircraft moves across several consecutive frames. This module uses that.
frame N-1, frame N ─► difference ─► threshold ─► soft mask
└► connected components + PCA ─► streaks (length + elongation)
│
▼ classify against neighbouring frames
streak continues a PROGRESSING track → satellite / aircraft (rejected)
streak repeats at the SAME location → disappearance of a meteor (de-duped)
isolated, transient streak → meteor candidate → confirmed next frame → saved
Key points:
- Frame differencing removes stars and static clouds, so — unlike single-frame detection — the sky background does not generate lines.
- PCA streaks (connected components + principal-axis length/elongation) are robust against gaps and reject blobby cloud brightening.
- Deferred confirmation: a candidate is held for one frame and only saved if the next frame shows no progressing continuation — so a satellite is rejected even on its first appearance (a live detector has no future frames).
- Soft, feathered mask edge (a trick borrowed from indi-allsky) so the mask boundary itself is never detected as a streak.
- Cloud and twilight gates skip frames that are too bright or changing too much.
- Allsky
v2023.05.01_04or later (module system). - Python packages already present in the Allsky virtualenv:
opencv-python,numpy.
Copy the module into your Allsky modules folder:
cp allsky_meteordetect.py ~/allsky/scripts/modules/or drop it into a clone of allsky-modules and run its installer. Then enable “Meteor Detection (temporal)” in the Allsky WebUI under Module Settings for the night flow.
Trees, buildings and the lens vignette should be excluded, or wind-blown leaves
produce endless false positives. tools/build_mask.py builds the mask
automatically from your own daytime images:
python3 tools/build_mask.py \
--images ~/allsky/images \
--nights 20260703 20260704 20260705 \
--out meteor_mask.png \
--preview preview.jpgCopy meteor_mask.png into ~/allsky/config/overlay/images/ and select it as the
module's Detection Mask.
Method. Obstructions are persistently dark silhouettes. For every pixel the tool measures, across many daytime frames, how often it is markedly darker than the sky (referenced to the bright image centre). Sky is rarely dark, trees almost always are — a far more robust separator than brightness or texture, both of which fail because tree interiors are smooth and averaging washes out their texture.
| Setting | Default | Meaning |
|---|---|---|
| Detection Mask | meteor_mask.png |
White = sky to analyse, black = ignore |
| Min Streak Length | 50 px |
Minimum streak length — the main lever against short star artifacts |
| Difference Threshold | 22 |
Brightness increase over previous frame to count as “new” |
| Min Elongation | 5.0 |
Length/width ratio (rejects round star blobs and clouds); real meteors here measured ≥7 |
| Max Streak Area | 6000 px |
Larger regions = cloud brightening |
| Cloud Skip | 2.0 % |
Skip frame if more than this share of sky changed |
| Mask Edge Feather | 35 px |
Soft mask fade so the edge is not detected |
| Reject Dashed Trails | on | Reject a long streak broken into many bright/dark segments — a tumbling satellite or strobing aircraft |
| Dash Segments | 10 |
Segment count that marks a streak as dashed (real meteor ≤5, a dashed satellite scored 19) |
| Dash Min Length | 120 px |
Only test streaks at least this long for a dashed pattern; short meteors are exempt |
| Reject Fragmented Trails (arm) | off | Arm the fragmented-trail veto. Off = shadow mode: the collinear-fragment metric is measured and logged (frag_n/frag_ext, frag-shadow) but nothing is vetoed. Turn on only after real meteors confirm they score 0 |
| Fragment Segments | 3 |
Collinear diff fragments beyond a streak's ends that mark it as the head of a fragmented dashed trail (real meteor 0, validated glint 3) |
| Fragment Min Length | 120 px |
Only test streaks at least this long for a collinear fragmented tail |
| Reject Satellites/Aircraft | on | Discard progressing tracks |
| Scintillation Guard | on | On very clear nights, if a frame has more than Scintillation Max streaks keep only a clearly dominant one |
| Scintillation Max | 8 |
Streak count that marks a scintillation-dominated frame |
| Reject Recurring Positions | on | Reject a spot that keeps firing across frames (scintillation/bloom/trailed star/reflection); a real meteor appears once |
| Recurrence Frames | 3 |
Earlier frames at the same spot (~55 px, ~25 min) needed to call it recurring — keep ≥3 |
| Reject Star-Trail Orientation | on | Reject a streak parallel to the local diurnal star-trail direction (needs the fisheye calibration); fireballs >130 px exempt |
| Star-Trail Tolerance | 12° |
How close to the trail direction counts as a trailed star |
| Upload to Remote Website | on | Upload each hit via Allsky's upload.sh |
| Save Marked Copy | off | Extra copy with brackets around the streak |
Clear nights are the hard case. Star scintillation and slight frame shake make bright stars flicker into short streaks that share a meteor's appear-then-disappear signature. The defenses are, in order of impact: geometry (a real meteor is long and thin — raise Min Streak Length / Min Elongation if a clear night still produces false positives), the scintillation guard, and same-location confirmation. There is no single perfect filter; tune the geometry to your sky.
With a calibrated fisheye projection the module can attribute each meteor to the shower whose radiant its streak actually points back to — real geometry, not just "which showers are active tonight".
tools/calibrate_fisheye.py fits the camera model (optical centre, radial
distortion, rotation, handedness) from a plate-solved night frame: bright stars are
detected, their true alt/az computed (Hipparcos + sidereal time), matched and
least-squares fitted. Blind matching is unreliable on a rich Milky-Way sky, so the
robust path is to plate-solve a small zenith crop with astrometry.net (which is
robust to star density) and bootstrap the full-frame fit from it. The result is a
calibration.json (verified here to 0.11° RMS over 228 stars).
allsky_fisheye.py then provides pixel_to_altaz / altaz_to_pixel and
match_radiant: a meteor travels along a great circle whose backward extension
passes through its radiant, so the module tests which active shower's radiant lies
on that circle (and above the horizon).
Drop allsky_fisheye.py + calibration.json next to the module; if either is
missing, radiant matching is silently skipped. The calibration is per-camera —
regenerate it for your own site with tools/calibrate_fisheye.py.
meteors-<timestamp>.jpgin the websitemeteors/folder, plus a thumbnail inmeteors/thumbnails/— picked up automatically by Allsky's meteor gallery page. The gallery image keeps the meteor's true colours, untouched.meteors.json— a rolling log of{time, file, length, angle, elong, peak, frag_n, frag_ext, showers, radiant}for later statistics (showers= active by date,radiant= geometric attribution if calibrated,frag_n= collinear-fragment shadow metric).- Optional remote-website upload of each hit.
Meteor colour encodes composition — green from magnesium/oxygen, yellow/orange from sodium/iron, blue-white for fast trails. The gallery image is therefore never painted over; the optional marked copy draws brackets around the streak, never on it.
- Keogram markers for detected meteors.
- Sky Quality Meter (SQM) in mag/arcsec², following indi-allsky:
mag = offset − 2.5·log10(mean_ADU)over a masked ROI (offset calibrated against a real SQM device). - Time-series dashboard charts (SQM, star count, temperature) in the style
of indi-allsky's
webui_chart01, fed from a rollingchart.jsonand drawn with Chart.js — no backend required. - Meteor-shower radiant awareness (Perseids, Geminids, …) — date-based context and geometric radiant matching via a plate-solved fisheye calibration.
- Optional pull request to
AllskyTeam/allsky-modules.
- Allsky by Thomas Jacquin and the Allsky team.
- indi-allsky by Aaron Morris — the feathered-mask trick and the SQM/chart approach on the roadmap are inspired by it.
- Built for astronomy.garden.
MIT — see LICENSE.
