{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cab2d0f1",
   "metadata": {},
   "source": [
    "# **Trajectory** back on original video\n",
    "\n",
    "IDEA:\n",
    "- take the processed positions\n",
    "- apply the inverse of the homography\n",
    "- print on the video frame by frame all points before the current frame"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "id": "4cd7f188",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "import cv2\n",
    "import csv\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "id": "060e2535",
   "metadata": {},
   "outputs": [],
   "source": [
    "VIDEO_NUMBER = \"2\"\n",
    "PROJECT_ROOT = Path().resolve().parent.parent\n",
    "INPUT_VIDEO_PATH = str(\n",
    "    PROJECT_ROOT\n",
    "    / \"data\"\n",
    "    / f\"recording_{VIDEO_NUMBER}\"\n",
    "    / f\"Recording_{VIDEO_NUMBER}.mp4\"\n",
    ")\n",
    "TRASFORMED_CSV_PATH = str(\n",
    "    PROJECT_ROOT\n",
    "    / \"data\"\n",
    "    / \"auxiliary_data\"\n",
    "    / \"reconstructed_positions\"\n",
    "    / f\"Transformed_positions_processed_TEST_{VIDEO_NUMBER}.csv\"\n",
    ")\n",
    "LANE_CSV_PATH = str(\n",
    "    PROJECT_ROOT\n",
    "    / \"data\"\n",
    "    / \"auxiliary_data\"\n",
    "    / \"lane_points\"\n",
    "    / f\"Lane_points_{VIDEO_NUMBER}.csv\"\n",
    ")\n",
    "\n",
    "OUTPUT_VIDEO_PATH = str(\n",
    "    PROJECT_ROOT\n",
    "    / \"data\"\n",
    "    / f\"recording_{VIDEO_NUMBER}\"\n",
    "    / f\"Tracked_output_TEST_{VIDEO_NUMBER}.mp4\"\n",
    ")\n",
    "OUTPUT_CSV_PATH = str(\n",
    "    PROJECT_ROOT\n",
    "    / \"data\"\n",
    "    / \"auxiliary_data\"\n",
    "    / \"circle_positions\"\n",
    "    / f\"Ball_lower_point_raw_TEST_{VIDEO_NUMBER}.csv\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "72b93c45",
   "metadata": {},
   "outputs": [],
   "source": [
    "lane_df = pd.read_csv(LANE_CSV_PATH)\n",
    "homographies = {}\n",
    "width_px, height_px = 106, 1829  # your real-world-to-pixel scale\n",
    "dst = np.array(\n",
    "    [[0, height_px], [width_px, height_px], [width_px, 0], [0, 0]], dtype=np.float32\n",
    ")\n",
    "\n",
    "for _, r in lane_df.iterrows():\n",
    "    frame_id = int(r[\"Frame\"])\n",
    "    src = np.array(\n",
    "        [\n",
    "            [r[\"bottom_left_x\"], r[\"bottom_left_y\"]],\n",
    "            [r[\"bottom_right_x\"], r[\"bottom_right_y\"]],\n",
    "            [r[\"up_right_x\"], r[\"up_right_y\"]],\n",
    "            [r[\"up_left_x\"], r[\"up_left_y\"]],\n",
    "        ],\n",
    "        dtype=np.float32,\n",
    "    )\n",
    "    H, _ = cv2.findHomography(dst, src)\n",
    "    if H is not None:\n",
    "        homographies[frame_id] = H"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "id": "47f2a59d",
   "metadata": {},
   "outputs": [],
   "source": [
    "positions = {}\n",
    "with open(TRASFORMED_CSV_PATH, newline=\"\") as f:\n",
    "    reader = csv.reader(f)\n",
    "    next(reader)  # skip header\n",
    "    for frame_str, x_str, y_str in reader:\n",
    "        fid = int(frame_str)\n",
    "        if x_str and y_str:\n",
    "            positions[fid] = (float(x_str), float(y_str))\n",
    "        else:\n",
    "            positions[fid] = None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "id": "dab28bc3",
   "metadata": {},
   "outputs": [],
   "source": [
    "cap = cv2.VideoCapture(str(INPUT_VIDEO_PATH))\n",
    "if not cap.isOpened():\n",
    "    raise RuntimeError(f\"Cannot open {INPUT_VIDEO_PATH!r}\")\n",
    "\n",
    "w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))\n",
    "h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))\n",
    "fps = cap.get(cv2.CAP_PROP_FPS)\n",
    "\n",
    "fourcc = cv2.VideoWriter_fourcc(*\"mp4v\")\n",
    "out = cv2.VideoWriter(str(OUTPUT_VIDEO_PATH), fourcc, fps, (w, h))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "id": "27d15335",
   "metadata": {},
   "outputs": [],
   "source": [
    "trajectory = []  # list of (x,y) in real-world space\n",
    "last_H = None\n",
    "frame_idx = 0\n",
    "saved_pts = []\n",
    "\n",
    "while True:\n",
    "    ret, frame = cap.read()\n",
    "    if not ret:\n",
    "        break\n",
    "\n",
    "    # Update homography if available\n",
    "    if frame_idx in homographies:\n",
    "        last_H = homographies[frame_idx]\n",
    "\n",
    "    # Append new real-world point\n",
    "    pt = positions.get(frame_idx)\n",
    "    if pt is not None:\n",
    "        trajectory.append(pt)\n",
    "\n",
    "    # If we have a homography and trajectory, project & draw\n",
    "    if last_H is not None and trajectory:\n",
    "        # Prepare points in Nx1x2 shape\n",
    "        pts_world = np.array(trajectory, dtype=np.float32).reshape(-1, 1, 2)\n",
    "        pts_img = cv2.perspectiveTransform(pts_world, last_H).reshape(-1, 2)\n",
    "        pts_int = np.round(pts_img).astype(np.int32)\n",
    "\n",
    "        # Draw full trajectory polyline\n",
    "        cv2.polylines(frame, [pts_int], isClosed=False, color=(0, 0, 255), thickness=2)\n",
    "\n",
    "        # Draw most recent point as a filled circle\n",
    "        x0, y0 = tuple(pts_int[-1])\n",
    "        cv2.circle(frame, (x0, y0), radius=4, color=(0, 0, 255), thickness=-1)\n",
    "        saved_pts.append([frame_idx, x0, y0])\n",
    "\n",
    "    out.write(frame)\n",
    "    frame_idx += 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "e63549c3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tracking video saved to: /home/davic/projects/IACV_project/bowling-analysis/data/recording_2/Tracked_output_TEST_2.mp4\n"
     ]
    }
   ],
   "source": [
    "cap.release()\n",
    "out.release()\n",
    "print(f\"Tracking video saved to: {OUTPUT_VIDEO_PATH}\")\n",
    "\n",
    "# Save the raw projected points\n",
    "df_out = pd.DataFrame(saved_pts, columns=[\"frame\", \"x\", \"y\"])\n",
    "# df_out.to_csv(OUTPUT_CSV_PATH, index=False)\n",
    "# print(f\"Projected points saved to: {OUTPUT_CSV_PATH}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
