diff --git a/docs/tracking/elm-rats/ELM_tracking.ipynb b/docs/tracking/elm-rats/ELM_tracking.ipynb new file mode 100644 index 0000000..703187f --- /dev/null +++ b/docs/tracking/elm-rats/ELM_tracking.ipynb @@ -0,0 +1,252 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fc7f8679", + "metadata": {}, + "source": [ + "Tracking some rats for now... (data from new session, not already in gcloud)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "50048038", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "48f7f39a", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from pathlib import Path\n", + "\n", + "# Import Environment Variables\n", + "from dotenv import load_dotenv\n", + "\n", + "# Import Utility Functions\n", + "from collab_env.data.file_utils import expand_path, get_project_root\n", + "from collab_env.data.gcs_utils import GCSClient\n", + "\n", + "# Import Custom Scripts\n", + "from collab_env.tracking.alignment_gui import align_videos\n", + "from collab_env.tracking.model.local_model_inference import infer_with_yolo\n", + "from collab_env.tracking.model.local_model_tracking import (\n", + " output_tracked_bboxes_csv,\n", + " overlay_tracks_on_video,\n", + " plot_tracks_at_frame_bbox_from_video,\n", + " run_tracking,\n", + " visualize_detections_from_video,\n", + ")\n", + "from collab_env.tracking.thermal_processing import (\n", + " process_directory,\n", + " validate_session_structure,\n", + ")\n", + "import shutil\n", + "import cv2\n", + "import numpy as np\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee16be3b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Session directory /Users/emily/Downloads/2025_07_17-session_0001 already exists. Skipping...\n", + "Session directory /Users/emily/Downloads/2025_07_17-session_0002 already exists. Skipping...\n", + "Session directory /Users/emily/Downloads/2025_07_17-session_0003 already exists. Skipping...\n", + "Moving /Users/emily/Downloads/2025_07_17/Thermal_1/20250717202910847.csq to /Users/emily/Downloads/2025_07_17-session_0004/thermal_1/20250717202910847.csq\n", + "Found 1 .csq files in /Users/emily/Downloads/2025_07_17-session_0004/thermal_1\n", + "\n", + "Processing [0] in thermal_1: 20250717202910847.csq\n", + "Auto-detecting vmin/vmax...\n", + "→ Using vmin=26.0, vmax=35.0\n", + "🖼️ Total frames in video: 2482\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "🔄 Writing frames: 100%|██████████| 2482/2482 [10:18<00:00, 4.01frame/s] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "✅ Exported 2482 frames to: /Users/emily/Downloads/2025_07_17-session_0004/processed/thermal_1/thermal_26_35.mp4\n", + "⏱ Approx. duration: 82.73 seconds at 30 fps\n", + "⚠️ Folder thermal_2 does not exist in /Users/emily/Downloads/2025_07_17-session_0004. Skipping...\n", + "Moving /Users/emily/Downloads/2025_07_17/Thermal_1/20250717210946836.csq to /Users/emily/Downloads/2025_07_17-session_0005/thermal_1/20250717210946836.csq\n", + "Found 1 .csq files in /Users/emily/Downloads/2025_07_17-session_0005/thermal_1\n", + "\n", + "Processing [0] in thermal_1: 20250717210946836.csq\n", + "Auto-detecting vmin/vmax...\n", + "→ Using vmin=26.0, vmax=36.0\n", + "🖼️ Total frames in video: 42989\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "🔄 Writing frames: 0%| | 92/42989 [00:26<5:50:32, 2.04frame/s]" + ] + } + ], + "source": [ + "# split raw folder into session folders, and convert to mp4, and compute max projection image\n", + "BASE_DIR = '/Users/emily/Downloads/'\n", + "DATE_STR = '2025_07_17'\n", + "RAW_DIR = os.path.join(BASE_DIR, DATE_STR)\n", + "csq_dir = os.path.join(RAW_DIR, \"Thermal_1\")\n", + "csq_files = list(Path(csq_dir).glob(\"*.csq\"))\n", + "for idx, csq in enumerate(csq_files, start=1):\n", + " # Create session directory\n", + " session_dir = os.path.join(BASE_DIR, f\"{DATE_STR}-session_{idx:04d}\")\n", + " # check if session directory already exists\n", + " if os.path.exists(session_dir):\n", + " print(f\"Session directory {session_dir} already exists. Skipping...\")\n", + " continue\n", + " os.makedirs(session_dir, exist_ok=True)\n", + " os.makedirs(os.path.join(session_dir, \"thermal_1\"), exist_ok=True)\n", + " print(f\"Moving {csq} to {os.path.join(session_dir, 'thermal_1', csq.name)}\")\n", + " shutil.copy(csq, os.path.join(session_dir, \"thermal_1\", csq.name))\n", + "\n", + " # Process each session directory\n", + " process_directory(\n", + " folder_path=os.path.join(session_dir),\n", + " out_path=os.path.join(session_dir, \"processed\"),\n", + " color=\"magma\", # 'Grays_r'\n", + " preview=False,\n", + " # max_frames=10,\n", + " fps=30,\n", + " )\n", + "\n", + "\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cfc1bddd", + "metadata": {}, + "outputs": [], + "source": [ + "def max_projection_from_video(video_path, out_path):\n", + " \"\"\"\n", + " Compute the maximum projection image from a video file.\n", + " \n", + " Args:\n", + " video_path (str): Path to the input video file.\n", + " out_path (str): Path to save the maximum projection image.\n", + " \"\"\"\n", + " cap = cv2.VideoCapture(video_path)\n", + " ok, frame = cap.read()\n", + " acc = frame.astype(np.uint16)\n", + " while ok:\n", + " acc = np.maximum(acc, frame)\n", + " ok, frame = cap.read()\n", + " cap.release()\n", + " cv2.imwrite(out_path, np.clip(acc, 0, 255).astype(np.uint8))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "10159f4f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0007-max_projection.png\n", + "No mp4 files found in /Users/emily/Downloads/2025_07_17-session_0007-max_projection.png/processed/thermal_1\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0004-max_projection.png\n", + "No mp4 files found in /Users/emily/Downloads/2025_07_17-session_0004-max_projection.png/processed/thermal_1\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0002-max_projection.png\n", + "No mp4 files found in /Users/emily/Downloads/2025_07_17-session_0002-max_projection.png/processed/thermal_1\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0001\n", + "Max projection image saved to /Users/emily/Downloads/2025_07_17-session_0001/processed/max_projection.png\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0001-max_projection.png\n", + "No mp4 files found in /Users/emily/Downloads/2025_07_17-session_0001-max_projection.png/processed/thermal_1\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0008\n", + "Max projection image saved to /Users/emily/Downloads/2025_07_17-session_0008/processed/max_projection.png\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0003-max_projection.png\n", + "No mp4 files found in /Users/emily/Downloads/2025_07_17-session_0003-max_projection.png/processed/thermal_1\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0008-max_projection.png\n", + "No mp4 files found in /Users/emily/Downloads/2025_07_17-session_0008-max_projection.png/processed/thermal_1\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0005-max_projection.png\n", + "No mp4 files found in /Users/emily/Downloads/2025_07_17-session_0005-max_projection.png/processed/thermal_1\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0003\n", + "No mp4 files found in /Users/emily/Downloads/2025_07_17-session_0003/processed/thermal_1\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0006-max_projection.png\n", + "No mp4 files found in /Users/emily/Downloads/2025_07_17-session_0006-max_projection.png/processed/thermal_1\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0004\n", + "Max projection image saved to /Users/emily/Downloads/2025_07_17-session_0004/processed/max_projection.png\n", + "Processing session directory: /Users/emily/Downloads/2025_07_17-session_0002\n", + "Max projection image saved to /Users/emily/Downloads/2025_07_17-session_0002/processed/max_projection.png\n" + ] + } + ], + "source": [ + "# compute max projection image for each session\n", + "\n", + "sessions_list = list(Path(BASE_DIR).glob(f\"{DATE_STR}-session_*\"))\n", + "for session_dir in sessions_list:\n", + " print(f\"Processing session directory: {session_dir}\")\n", + " max_proj_path = os.path.join(session_dir, \"processed\", \"max_projection.png\")\n", + " # find the video file in the processed directory\n", + " mp4_files = list(Path(os.path.join(session_dir, \"processed\", \"thermal_1\")).glob(\"*.mp4\"))\n", + " # check if there are any mp4 files\n", + " if not mp4_files:\n", + " print(f\"No mp4 files found in {os.path.join(session_dir, 'processed', 'thermal_1')}\")\n", + " continue\n", + " video_path = mp4_files[0] \n", + " max_projection_from_video(video_path, max_proj_path)\n", + " # get just the folder name from the session directory\n", + " session_str = os.path.basename(session_dir)\n", + " max_projection_from_video(video_path, os.path.join(BASE_DIR, f'{session_str}-max_projection.png'))\n", + " print(f\"Max projection image saved to {max_proj_path}\")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "collab-environment", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}