If you use this work for academic work please cite the following paper:
The code is open-source and free to use. It is aimed for, but not limited to, academic research. We welcome forking of this repository, pull requests, and any contributions in the spirit of open science and open-source code. For inquiries about collaboration, you may contact Md Shadab Alam (md_shadab_alam@outlook.com) or Pavlo Bazilinskyy (pavlo.bazilinskyy@gmail.com).
Tested with Python 3.11.9 and the uv package manager.
Follow these steps to set up the project.
Step 1: Install uv. uv is a fast Python package and environment manager. Install it using one of the following methods:
macOS / Linux (bash/zsh):
curl -LsSf https://astral.sh/uv/install.sh | shWindows (PowerShell):
irm https://astral.sh/uv/install.ps1 | iexAlternative (if you already have Python and pip):
pip install uvStep 2: Fix permissions (if needed):
Sometimes uv needs to create a folder under ~/.local/share/uv/python (macOS/Linux) or %LOCALAPPDATA%\uv\python (Windows).
If this folder was created by another tool (e.g. sudo), you may see an error like:
error: failed to create directory ... Permission denied (os error 13)To fix it, ensure you own the directory:
mkdir -p ~/.local/share/uv
chown -R "$(id -un)":"$(id -gn)" ~/.local/share/uv
chmod -R u+rwX ~/.local/share/uv# Create directory if it doesn't exist
New-Item -ItemType Directory -Force "$env:LOCALAPPDATA\uv"
# Ensure you (the current user) own it
# (usually not needed, but if permissions are broken)
icacls "$env:LOCALAPPDATA\uv" /grant "$($env:UserName):(OI)(CI)F"Step 3: After installing, verify:
uv --versionStep 4: Clone the repository:
git clone https://github.com/Shaadalam9/youtube-cycling.git
cd youtube-cyclingStep 5: Ensure correct Python version. If you don’t already have Python 3.11.9 installed, let uv fetch it:
uv python install 3.11.9The repo should contain a .python-version file so uv will automatically use this version.
Step 6: Create and sync the virtual environment. This will create .venv in the project folder and install dependencies exactly as locked in uv.lock:
uv sync --frozenStep 7: Activate the virtual environment:
macOS / Linux (bash/zsh):
source .venv/bin/activateWindows (PowerShell):
.\.venv\Scripts\Activate.ps1Windows (cmd.exe):
.\.venv\Scripts\activate.batStep 8: Ensure that dataset are present. Place required datasets (including mapping.csv) into the data/ directory:
Step 9: Run the code:
python3 analysis.pyIf the convoy CSVs already exist and only the video-context stage needs to be run or repeated, use:
python3 convoy_context_pipeline.pyThis reuses cyclist_crossing_sequences.csv and
cyclist_crossing_sequence_members.csv; it does not rerun cyclist detection.
It replaces only the two context sidecars, and only after configuration and all
sequence annotations complete successfully.
The analysis runs as a candidate-first convoy pipeline:
- Clean detector rows and skip empty or malformed detector CSVs safely.
- Find moving bicycle tracks that reach the road centre, then form candidate convoys from tracks travelling in the same direction with compatible passage time and lateral position.
- Run person/bicycle association, full road-crossing detection, and camera/static-reference false-positive filters only for the candidate convoy bicycles.
- Rebuild the convoy from the surviving confirmed crossings.
- For every confirmed convoy, sample the automatic traffic-light state at each member's exact passage frame and estimate ego motion once after the final road-exit frame. Context failures never remove confirmed convoys.
The pipeline writes:
data/cyclist_convoy_candidates.csvanddata/cyclist_convoy_candidate_members.csvfor the pre-crossing stage.data/cyclist_crossing_events.csvfor confirmed member crossings.data/cyclist_crossing_sequences.csvanddata/cyclist_crossing_sequence_members.csvfor final convoys.data/cyclist_crossing_sequence_context.csvfor convoy-level traffic-light consensus and post-convoy ego motion.data/cyclist_crossing_sequence_member_context.csvfor per-member automatic traffic-light results.data/cyclist_crossing_sequence_snippets/for optional confirmed-convoy video clips.
The batch context stage is non-interactive. Its automatic traffic-light result describes the signal selected as
relevant to the camera vehicle; it is not necessarily the cyclist-facing signal and is never used to accept or
reject a convoy. A mixed sequence state means the selected automatic states differ between members, not that the
same physical signal was proven to change. cyclist_crossing_validation_tool.py remains available for human review
and trusted traffic-light labels.
Context rows use explicit statuses such as known, no_detection, ambiguous, model_unavailable, and
video_unavailable. Missing source videos are downloaded automatically through the configured FTP/HTTP file server,
but only after a convoy is confirmed. Set convoy_context_download_missing_videos=false to disable this behavior.
Downloads use FTP_* environment variables first, then the project secret file, and are validated before being
atomically moved into the configured video directory.
Set convoy_snippets_enabled=true to create one review clip for every confirmed convoy. Each clip begins five
seconds before the first cyclist enters the road and ends five seconds after the last cyclist exits the road. The
padding and output folder are configurable, clips are written atomically, and clip failures never remove confirmed
crossing or context rows. The convoy context CSV records each clip's status, path, source-frame bounds, timestamps,
and duration. Snippet export can run even when traffic-light and ego-motion context are disabled, and it uses the
same local-video lookup and FTP fallback. If settings or confirmed convoys change between runs, treat the snippet
paths in the current context CSV as authoritative; older generated clips are not deleted automatically.
The sequence threshold is applied between neighbouring cyclists. For example, A→B and B→C can form one sequence even when the total A→C duration is longer than the threshold. By default, a change of direction breaks a sequence, and groups never span different detector CSV segments.
Configuration of the project needs to be defined in config. Please use the default.config file for the required structure of the file. If no custom config file is provided, default.config is used. The config file has the following parameters:
mapping: CSV file that contains mapping data for the cities referenced in the data.data: List of directories containing data (CSV output from YOLO).videos: Directory containing the videos used to generate the data.always_analyse: Always conduct analysis even when pickle files are present (good for testing).min_confidence: Sets the confidence threshold parameter for YOLO.countries_analyse: List of countries to include in the analysis. If empty, all countries are analysed.vehicles_analyse: List of YOLO class IDs to include in the analysis (e.g.,[0]). If empty, all configured classes are analysed.boundary_leftandboundary_right: Normalised horizontal boundaries of the road-crossing band.detector_frame_count_base: Set to1for one-based detector frame counts (the project default), or0for zero-based CSVs. This is explicit so sparse detections cannot shift video context by one frame.successive_crossing_min_cyclists: Minimum number of distinct confirmed cyclists required for a sequence.successive_crossing_max_headway_seconds: Maximum time between neighbouring cyclist passage moments.successive_crossing_same_direction_only: Iftrue, an opposite-direction crossing breaks the sequence.convoy_prefilter_enabled: Iftrue, run candidate convoy formation before strict road-crossing detection.convoy_candidate_max_lateral_offset: Maximum normalised vertical separation between neighbouring candidate bicycle tracks at their centre-line passage.convoy_context_enabled: Iftrue, run automatic traffic-light and post-convoy ego-motion context after each confirmed convoy is written.convoy_context_traffic_light_enabledandconvoy_context_ego_motion_enabled: Independently enable the two context estimators.convoy_snippets_enabled: Iftrue, create one video clip per confirmed convoy. The default isfalse.convoy_snippet_before_secondsandconvoy_snippet_after_seconds: Padding around the earliest cyclist road entry and latest cyclist road exit. Both default to5.0seconds.convoy_snippet_output_dir: Output directory for convoy clips. Relative paths are resolved from the repository root; the default isdata/cyclist_crossing_sequence_snippets.convoy_context_download_missing_videos: Iftrue, download missing source videos that contain confirmed convoys or requested snippets. The current default istrue.convoy_context_video_download_dir: Optional download destination; when empty, use the firstvideosroot.convoy_context_ftp_timeout_secondsandconvoy_context_ftp_max_pages: Timeout and crawl safety limit for FTP/HTTP file-server lookup.convoy_context_traffic_light_model_paths: Ordered primary/fallback traffic-light model paths.convoy_context_traffic_light_window_before_secondsandconvoy_context_traffic_light_window_after_seconds: Video window sampled around each cyclist passage.convoy_context_ego_lookahead_seconds: Video duration sampled after the last cyclist exits the road.logger_level: Level of console output. Can be: debug, info, warning, error.font_family: Specifies the font family to be used in outputs.font_size: Specifies the font size to be used in outputs.plotly_template: Plotly template to use for figures (e.g.,plotly_white).ftp_base_url: Base URL of the remote file server used to retrieve videos.