Skip to content

Repository files navigation

Sea Turtle Behavior Pipeline

End-to-end pipeline from raw rehabilitation-tank video to:

  • Pool cell occupancy — heatmaps, transition matrices, run-length distributions
  • Breathing events — per-frame classification, event timeline, CSV export

Which script to use

Situation Script
One camera, one recording run_pipeline.py
One camera, multiple days run_batch.py
Two cameras (comparison) run_batch.py

Pipeline steps

Video filename format: CAMERA_YYYYMMDDHHMMSS.mp4 (e.g. D01_20250808060000.mp4)

Raw video
    │
    ▼ Step 1 — Calculate working-hours window (default 06:00–18:00)
    │
    ▼ Step 2 — Pool ellipse mask
              First run: interactive GUI → drag dots to pool boundary → confirm
              Saved to configs/<CAMERA>_pool_config.json, reused on future runs
    │
    ▼ Step 3 — Frame extraction at target FPS
    │
    ▼ Step 4 — YOLOv8m-seg detection  (auto-downloaded from HuggingFace)
    │
    ├─────────────────────────────────────────┐
    ▼                                         ▼
Step 5 — Cell assignment                  Step 6 — Breathing classification
Heatmaps, transition matrices,            ResNet50  (auto-downloaded from HuggingFace)
run-length plots                          → breathing_events.csv + timeline plot

Both models are downloaded automatically on first run and cached locally.

Setup

Unzip the archive and run setup from inside the folder:

cd sea-turtle-behavior-pipeline-main
bash setup.sh
source .venv/bin/activate

setup.sh installs uv if not present, creates a .venv, and installs all dependencies. No sudo required.

Quick start — sample clips

setup.sh downloads 1-minute sample clips (2 cameras, 2 days) automatically. Run the full pipeline on them to verify everything works:

python run_batch.py \
    --input-dir samples/1min_sample/ \
    --duration  1

Output lands under results/ with a timestamped name, e.g.: results/20250808_143022_D01-D03_20250808-09/

Completes in a few minutes and exercises the full pipeline end-to-end. This is also the recommended first step when setting up pool configs for new cameras — the boundary picker will open automatically on first run.


run_pipeline.py — single recording

python run_pipeline.py --video /path/to/D01_20250808060000.mp4

--camera is inferred from the filename automatically (D01_… → camera D01). Pass --camera <ID> explicitly only if your filename doesn't follow the standard format.

Option Description
--video Path to raw tank video (required)
--camera Camera ID — inferred from filename if omitted
--output Output directory (default: results/<video-stem>/)
--start Recording window start, HH:MM (default: 06:00)
--end Recording window end, HH:MM (default: 18:00)
--processing-fps FPS for frame extraction (default: 8)
--yolo-fps FPS for YOLO (default: same as --processing-fps)
--breathing-fps FPS for breathing classifier (default: same as --processing-fps)
--verify-trim Interactively verify trim window frame-by-frame
--keep-frames Keep extracted JPEG frames after processing (deleted by default)
--skip-cells Skip cell assignment / heatmaps
--skip-breathing Skip breathing classification

Output

results/D01_20250808060000/
├── D01_20250808060000_detections.csv
├── cells/
│   ├── D01_20250808060000_cells.csv
│   └── plots/
└── breathing/
    ├── breathing_predictions.csv
    ├── breathing_events.csv
    └── breathing_timeline.png

run_batch.py — multiple days / two cameras

python run_batch.py \
    --input-dir /path/to/videos/ \
    --duration  60          # optional: cap to 60 min per day

Input directory structure

Videos are found by recursive scan. Camera ID and timestamp are parsed from the filename — the folder layout does not need to follow a fixed pattern.

Required filename format: CAMERA_YYYYMMDDHHMMSS.mp4

D01_20250808060000.mp4   ← camera D01, started 2025-08-08 at 06:00:00
D03_20250809132619.mp4

The camera ID is everything before the first _. Any alphanumeric string works (D01, D03, cam1, turtle1 …). Only 1 or 2 cameras per run are supported.

Supported folder layouts (all handled automatically):

# A — camera / day / videos  (recommended)
input_dir/
├── D01/
│   ├── 8_8_2025/
│   │   ├── D01_20250808060000.mp4
│   │   └── D01_20250808071746.mp4
│   └── 9_8_2025/
│       └── D01_20250809060000.mp4
└── D03/
    └── 8_8_2025/
        └── D03_20250808060000.mp4

# B — camera / videos  (no day subfolder)
input_dir/
├── D01/
│   ├── D01_20250808060000.mp4
│   └── D01_20250809060000.mp4
└── D03/
    └── D03_20250808060000.mp4

# C — camera / subtype / videos  (use --subdir to filter)
input_dir/
├── D01/
│   ├── daytime_synchronized/
│   │   └── D01_20250808060000.mp4
│   └── daytime_not_synchronized/
│       └── D01_20250808043608.mp4
└── D03/
    └── daytime_synchronized/
        └── D03_20250808060000.mp4

Mix of the above also works — the scan is recursive and groups by camera and calendar day from the filename regardless of folder depth. Use --subdir <name> to restrict to a specific subdirectory name anywhere in the path (e.g. --subdir daytime_synchronized).

Option Description
--input-dir Root directory to scan for MP4 videos (required)
--subdir Only include videos inside this subdirectory name
--output Exact output directory (default: auto-named under results/)
--start Recording window start, HH:MM (default: 06:00)
--end Recording window end, HH:MM (default: 18:00)
--duration Cap each day to this many minutes from start
--processing-fps FPS for frame extraction (default: 8)
--yolo-fps FPS for YOLO (default: same as --processing-fps)
--breathing-fps FPS for breathing classifier (default: same as --processing-fps)
--verify-trim Interactively verify trim window frame-by-frame
--verify-config Re-verify pool boundary config even if previously approved
--keep-frames Keep extracted JPEG frames after processing (deleted by default)
--skip-cells Skip cell assignment / heatmaps
--skip-breathing Skip breathing classification

All defaults can be set in pipeline_config.yaml and overridden per-run via CLI.

Output

results/20250808_143022_D01-D03_20250808-09/
├── D01/
│   ├── 2025-08-08/
│   │   ├── D01_2025-08-08.csv          ← merged day detections
│   │   ├── cells/
│   │   └── breathing/
│   ├── 2025-08-09/
│   └── combined/                       ← all days merged
├── D03/
└── comparison_D01_D03/                 ← cross-camera comparison (2-camera runs only)

Notes

  • Supports 1 or 2 cameras. Cross-camera comparison is automatic when 2 cameras are present, clipped to the shared coverage window per day.
  • Pool configs are saved to configs/ and reused across runs. The boundary picker opens automatically on first run for each camera.
  • Intermediate JPEG frames are deleted after processing by default. Pass --keep-frames to retain them (needed if you plan to re-run breathing without re-extracting).

Pool config

On first run for a camera, a fullscreen interactive window opens showing a frame from the source video. Drag the 10 green dots to the pool boundary — the fitted ellipse and cell grid update live. Press Enter to confirm.

The config is saved to configs/<CAMERA>_pool_config.json and reused on future runs. Use --verify-config to force re-verification.

Authors

  • Marko Barišić — University of Zagreb, FER, Laboratory for Underwater Systems and Technologies (LABUST)
  • Roee Diamant — University of Haifa

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages