{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "8c7f10fa",
   "metadata": {},
   "source": [
    "# PostProcessing of the whole lane"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "0db8f073",
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "from pathlib import Path\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from scipy.signal import savgol_filter\n",
    "from scipy.signal import medfilt\n",
    "from scipy.spatial import procrustes\n",
    "from scipy.optimize import minimize"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0de0a5d1",
   "metadata": {},
   "source": [
    "Import video"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "id": "ed686058",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Project root: C:\\Users\\miche\\OneDrive\\Documenti\\GitHub\\bowling-analysis\n",
      "Opened: True, FPS: 59.94005994005994, Total Frames: 227.0\n"
     ]
    }
   ],
   "source": [
    "video_number = \"3\"\n",
    "# Define the relative path to the video file\n",
    "notebook_dir = Path().resolve()\n",
    "project_root = notebook_dir.parent.parent\n",
    "print(f\"Project root: {project_root}\")\n",
    "video_path = (\n",
    "    project_root\n",
    "    / \"data\"\n",
    "    / f\"recording_{video_number}\"\n",
    "    / f\"Recording_{video_number}.mp4\"\n",
    ")\n",
    "video_path = str(video_path)\n",
    "\n",
    "# Load the video\n",
    "cap = cv2.VideoCapture(video_path)\n",
    "\n",
    "# Check\n",
    "print(\n",
    "    f\"Opened: {cap.isOpened()}, FPS: {cap.get(cv2.CAP_PROP_FPS)}, Total Frames: {cap.get(cv2.CAP_PROP_FRAME_COUNT)}\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd8d6fa2",
   "metadata": {},
   "source": [
    "Import lane points"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "b3969453",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define the path to the CSV file\n",
    "input_data_path = (\n",
    "    project_root\n",
    "    / \"data\"\n",
    "    / \"auxiliary_data\"\n",
    "    / \"lane_points\"\n",
    "    / f\"lane_points_raw_{video_number}.csv\"\n",
    ")  # may change raw to processed\n",
    "\n",
    "# Load the CSV file into a DataFrame\n",
    "points_df = pd.read_csv(input_data_path)\n",
    "# Display the first few rows of the DataFrame\n",
    "# print(points_df.head())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4e2bf1c",
   "metadata": {},
   "source": [
    "Draw the lines on a frame"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "id": "7d69a333",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"Disegna la linea sul frame\"\"\"\n",
    "\n",
    "\n",
    "def write_line_on_frame(frame, line):\n",
    "    # Create a copy of the original frame to draw the first line\n",
    "    modified_frame = np.copy(frame)\n",
    "\n",
    "    # Extract the first line's rho and theta\n",
    "    if line is not None:\n",
    "        x1, y1, x2, y2 = line\n",
    "\n",
    "        # Draw the first line on the frame\n",
    "        cv2.line(modified_frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)\n",
    "\n",
    "    # return the modified frame\n",
    "    return modified_frame"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "539e8c46",
   "metadata": {},
   "outputs": [],
   "source": [
    "def write_lines_on_frame(frame, lines):\n",
    "    for i in range(len(lines)):\n",
    "        # print('linea', i, ':', lines[i])\n",
    "        frame = write_line_on_frame(frame, lines[i])\n",
    "    return frame"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ddea825e",
   "metadata": {},
   "source": [
    "From the 4 points get the 4 linies to draw"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "id": "5e8fdb7d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def lines_from_points(df):\n",
    "    lines = []\n",
    "    for i in range(len(df)):\n",
    "        bottom = [\n",
    "            df.iloc[i][\"bottom_right_x\"],\n",
    "            df.iloc[i][\"bottom_right_y\"],\n",
    "            df.iloc[i][\"bottom_left_x\"],\n",
    "            df.iloc[i][\"bottom_left_y\"],\n",
    "        ]\n",
    "        top = [\n",
    "            df.iloc[i][\"up_right_x\"],\n",
    "            df.iloc[i][\"up_right_y\"],\n",
    "            df.iloc[i][\"up_left_x\"],\n",
    "            df.iloc[i][\"up_left_y\"],\n",
    "        ]\n",
    "        left = [\n",
    "            df.iloc[i][\"bottom_left_x\"],\n",
    "            df.iloc[i][\"bottom_left_y\"],\n",
    "            df.iloc[i][\"up_left_x\"],\n",
    "            df.iloc[i][\"up_left_y\"],\n",
    "        ]\n",
    "        right = [\n",
    "            df.iloc[i][\"bottom_right_x\"],\n",
    "            df.iloc[i][\"bottom_right_y\"],\n",
    "            df.iloc[i][\"up_right_x\"],\n",
    "            df.iloc[i][\"up_right_y\"],\n",
    "        ]\n",
    "\n",
    "        # Append the line to the list\n",
    "        lines.append([bottom, top, left, right])\n",
    "    return lines"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f027d9bd",
   "metadata": {},
   "source": [
    "## Sav-Gol filter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "id": "d9492ccc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a new 8D array from your points\n",
    "quad_points = points_df[\n",
    "    [\n",
    "        \"bottom_left_x\",\n",
    "        \"bottom_left_y\",\n",
    "        \"bottom_right_x\",\n",
    "        \"bottom_right_y\",\n",
    "        \"up_left_x\",\n",
    "        \"up_left_y\",\n",
    "        \"up_right_x\",\n",
    "        \"up_right_y\",\n",
    "    ]\n",
    "].values\n",
    "\n",
    "med_points = medfilt(quad_points, kernel_size=(1, 1))\n",
    "\n",
    "# Apply savgol filter across the entire matrix (axis=0 means apply filter column-wise)\n",
    "smoothed_quad = savgol_filter(quad_points, window_length=5, polyorder=1, axis=0)\n",
    "\n",
    "smoothed_quad = smoothed_quad.astype(int)\n",
    "\n",
    "# Update DataFrame\n",
    "df_smoothed = points_df.copy()\n",
    "df_smoothed.loc[\n",
    "    :,\n",
    "    [\n",
    "        \"bottom_left_x\",\n",
    "        \"bottom_left_y\",\n",
    "        \"bottom_right_x\",\n",
    "        \"bottom_right_y\",\n",
    "        \"up_left_x\",\n",
    "        \"up_left_y\",\n",
    "        \"up_right_x\",\n",
    "        \"up_right_y\",\n",
    "    ],\n",
    "] = med_points"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "839a60e1",
   "metadata": {},
   "source": [
    "## Procrustes Analysis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "id": "6c3aa6ed",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Reference shape: [[ 454.85462555 1036.78414097]\n",
      " [1126.33039648 1049.10132159]\n",
      " [1106.17180617  402.40528634]\n",
      " [1367.17621145  404.03964758]]\n",
      "[[-0.59982921  0.33674757]\n",
      " [ 0.12097641  0.34996963]\n",
      " [ 0.09933687 -0.34423582]\n",
      " [ 0.37951592 -0.34248139]]\n",
      "[-0.59982921  0.33674757  0.12097641  0.34996963  0.09933687 -0.34423582\n",
      "  0.37951592 -0.34248139]\n"
     ]
    }
   ],
   "source": [
    "\"\"\" Align each frame quadrilateral to the mean quadrilateral using Procrustes analysis\n",
    "    It allpies a similarity transformation to minimize the disparity between two shapes. (rotation, translation, scaling)\n",
    "\"\"\"\n",
    "\n",
    "# Define the columns for the quadrilateral points\n",
    "\n",
    "point_cols = [\n",
    "    \"bottom_left_x\",\n",
    "    \"bottom_left_y\",\n",
    "    \"bottom_right_x\",\n",
    "    \"bottom_right_y\",\n",
    "    \"up_left_x\",\n",
    "    \"up_left_y\",\n",
    "    \"up_right_x\",\n",
    "    \"up_right_y\",\n",
    "]\n",
    "\n",
    "\n",
    "# Extract quadrilateral as array (N, 4, 2)\n",
    "def df_to_quads(points_df):\n",
    "    return points_df[point_cols].values.reshape((-1, 4, 2))\n",
    "\n",
    "\n",
    "quads = df_to_quads(points_df)\n",
    "\n",
    "# Choose reference shape: e.g., the first frame, or average shape\n",
    "reference_shape = np.mean(quads, axis=0)  # mean quadrilateral\n",
    "\n",
    "print(\"Reference shape:\", reference_shape)\n",
    "\n",
    "# Align each frame's quad to reference using Procrustes\n",
    "aligned_quads = []\n",
    "\n",
    "# with procrustes\n",
    "for quad in quads:\n",
    "    # Procrustes expects same shape and centered/normalized input\n",
    "    mtx1, mtx2, disparity = procrustes(reference_shape, quad)\n",
    "    aligned_quads.append(\n",
    "        mtx1\n",
    "    )  # you can use mtx1 to get the ideal shape, mtx2 is the aligned version\n",
    "\n",
    "\n",
    "aligned_quads = np.array(aligned_quads)  # shape (N, 4, 2)\n",
    "print(aligned_quads[0])\n",
    "# Convert back to DataFrame\n",
    "flattened = aligned_quads.reshape((aligned_quads.shape[0], -1))\n",
    "print(flattened[0])\n",
    "smoothed_df = points_df.copy()\n",
    "smoothed_df[point_cols] = flattened\n",
    "\n",
    "df_procrustes = smoothed_df.copy()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2403a0bf",
   "metadata": {},
   "source": [
    "Similarity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "id": "6264d95a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute_quadrilater_center(quad):\n",
    "    return np.mean(quad, axis=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "id": "31df788e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Reference shape: [[ 454.85462555 1036.78414097]\n",
      " [1126.33039648 1049.10132159]\n",
      " [1106.17180617  402.40528634]\n",
      " [1367.17621145  404.03964758]]\n",
      "[[ 608.25 1048.25]\n",
      " [1323.25 1058.25]\n",
      " [1244.25  436.25]\n",
      " [1548.25  439.25]]\n",
      "[ 608.25 1048.25 1323.25 1058.25 1244.25  436.25 1548.25  439.25]\n"
     ]
    }
   ],
   "source": [
    "\"\"\" Align each frame quadrilateral to the mean quadrilateral using similarity transform\n",
    "    It allpies a similarity transformation to minimize the disparity between two shapes. (rotation, translation, scaling)\n",
    "    Then it align the quadrilateral with one chosen reference point\n",
    "    It solves the problem of bottom line not being in the frame\n",
    "    Not very good for points processing, it still jitters\n",
    "\"\"\"\n",
    "\n",
    "# Define the columns for the quadrilateral points\n",
    "\n",
    "point_cols = [\n",
    "    \"bottom_left_x\",\n",
    "    \"bottom_left_y\",\n",
    "    \"bottom_right_x\",\n",
    "    \"bottom_right_y\",\n",
    "    \"up_left_x\",\n",
    "    \"up_left_y\",\n",
    "    \"up_right_x\",\n",
    "    \"up_right_y\",\n",
    "]\n",
    "\n",
    "\n",
    "# Extract quadrilateral as array (N, 4, 2)\n",
    "def df_to_quads(points_df):\n",
    "    return points_df[point_cols].values.reshape((-1, 4, 2))\n",
    "\n",
    "\n",
    "quads = df_to_quads(points_df)\n",
    "\n",
    "# Choose reference shape: e.g., the first frame, or average shape\n",
    "reference_shape = np.mean(quads, axis=0)  # mean quadrilateral\n",
    "\n",
    "\n",
    "print(\"Reference shape:\", reference_shape)\n",
    "\n",
    "# Align each frame's quad to reference using Procrustes\n",
    "aligned_quads = []\n",
    "\n",
    "\n",
    "# with similarity transform\n",
    "i = 0\n",
    "for quad in quads:\n",
    "    # Estimate similarity transform (rotation + translation + uniform scale)\n",
    "    transform_matrix, _ = cv2.estimateAffinePartial2D(\n",
    "        quad, reference_shape, method=cv2.LMEDS\n",
    "    )\n",
    "\n",
    "    # Apply transform to quad\n",
    "    aligned = cv2.transform(np.array([quad]), transform_matrix)[0]\n",
    "\n",
    "    # Shift the quadrilateral in a way that the reference point is aligned to the original reference point int the original image\n",
    "    reference_point_new = compute_quadrilater_center(aligned)\n",
    "    reference_point_old = compute_quadrilater_center(quad)\n",
    "\n",
    "    shift = [\n",
    "        reference_point_old[0] - reference_point_new[0],\n",
    "        reference_point_old[1] - reference_point_new[1],\n",
    "    ]\n",
    "\n",
    "    # Apply shift to entire aligned quadrilateral\n",
    "    shifted_quad = aligned + shift\n",
    "\n",
    "    aligned_quads.append(shifted_quad)\n",
    "\n",
    "    i += 1\n",
    "\n",
    "aligned_quads = np.array(aligned_quads)  # shape (N, 4, 2)\n",
    "print(aligned_quads[0])\n",
    "# Convert back to DataFrame\n",
    "flattened = aligned_quads.reshape((aligned_quads.shape[0], -1))\n",
    "print(flattened[0])\n",
    "smoothed_df = points_df.copy()\n",
    "smoothed_df[point_cols] = flattened\n",
    "\n",
    "df_similarity = smoothed_df.copy()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e0007bf",
   "metadata": {},
   "source": [
    "## With Optimization \n",
    "Preserve Lane Lengths in consecutive frames"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "e6e6120b",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\" It works well if the first frame is correct\"\"\"\n",
    "\n",
    "\n",
    "def edge_lengths(quad):\n",
    "    return np.linalg.norm(np.roll(quad, -1, axis=0) - quad, axis=1)\n",
    "\n",
    "\n",
    "def cost_function(x, prev_lengths):\n",
    "    # x: flattened coords of 4 points relative to center\n",
    "    rel_quad = x.reshape(4, 2)\n",
    "    lengths = edge_lengths(rel_quad)\n",
    "    return np.sum((lengths - prev_lengths) ** 2)\n",
    "\n",
    "\n",
    "# Initial data: quads (original), shape (N, 4, 2)\n",
    "centers = np.mean(quads, axis=1)  # shape (N, 2)\n",
    "smoothed_quads = []\n",
    "\n",
    "# First frame: use as is\n",
    "smoothed_quads.append(quads[0])\n",
    "prev_quad = quads[0]\n",
    "\n",
    "for t in range(1, len(quads)):\n",
    "    center = centers[t]  # fixed center for this frame\n",
    "    prev_lengths = edge_lengths(prev_quad)\n",
    "\n",
    "    # Use previous relative positions from center as initial guess\n",
    "    rel_prev = prev_quad - compute_quadrilater_center(prev_quad)\n",
    "    x0 = rel_prev.flatten()\n",
    "\n",
    "    # Optimize shape while preserving center\n",
    "    res = minimize(cost_function, x0, args=(prev_lengths,), method=\"L-BFGS-B\")\n",
    "    rel_quad = res.x.reshape(4, 2)\n",
    "    new_quad = rel_quad + center  # add back center\n",
    "\n",
    "    smoothed_quads.append(new_quad)\n",
    "    prev_quad = new_quad\n",
    "\n",
    "# Convert to array\n",
    "smoothed_quads = np.array(smoothed_quads)\n",
    "\n",
    "# Update DataFrame\n",
    "flattened = smoothed_quads.reshape((smoothed_quads.shape[0], -1))\n",
    "df_smoothed_center = points_df.copy()\n",
    "df_smoothed_center[point_cols] = flattened"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "206d79ec",
   "metadata": {},
   "source": [
    "Preserve lane lengths with respect the average one"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "id": "833e33a0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from scipy.optimize import minimize\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "def quad_center(quad):\n",
    "    return np.mean(quad, axis=0)\n",
    "\n",
    "\n",
    "# Step 1: Compute relative positions for all quads\n",
    "rel_quads = quads - np.mean(quads, axis=1, keepdims=True)  # (N, 4, 2)\n",
    "\n",
    "# Step 2: Compute the average shape (relative to center)\n",
    "average_rel_shape = np.mean(rel_quads, axis=0)  # (4, 2)\n",
    "\n",
    "\n",
    "# Step 3: Optimization function to minimize deviation from avg shape\n",
    "def cost_to_avg_shape(x, avg_shape):\n",
    "    rel_quad = x.reshape(4, 2)\n",
    "    return np.sum((rel_quad - avg_shape) ** 2)\n",
    "\n",
    "\n",
    "# Step 4: Optimize each frame\n",
    "smoothed_quads = []\n",
    "\n",
    "for t in range(len(quads)):\n",
    "    center = quad_center(quads[t])\n",
    "\n",
    "    # Initial guess: current relative shape\n",
    "    rel_init = quads[t] - center\n",
    "    x0 = rel_init.flatten()\n",
    "\n",
    "    # Minimize distance from average shape\n",
    "    res = minimize(cost_to_avg_shape, x0, args=(average_rel_shape,), method=\"L-BFGS-B\")\n",
    "\n",
    "    # Reconstruct quad\n",
    "    rel_quad = res.x.reshape(4, 2)\n",
    "    new_quad = rel_quad + center\n",
    "    smoothed_quads.append(new_quad)\n",
    "\n",
    "# Convert result\n",
    "smoothed_quads = np.array(smoothed_quads)\n",
    "\n",
    "# Update DataFrame\n",
    "flattened = smoothed_quads.reshape((smoothed_quads.shape[0], -1))\n",
    "df_smoothed_to_avg_shape = points_df.copy()\n",
    "df_smoothed_to_avg_shape[point_cols] = flattened"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "91746a90",
   "metadata": {},
   "source": [
    "Apply the average quadrilater in the center of each detection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "id": "83386e1d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# compute the average shape\n",
    "average_shape = np.mean(quads, axis=0)  # shape (4, 2)\n",
    "\n",
    "# compute the center of the average shape\n",
    "average_shape_center = np.mean(average_shape, axis=0)\n",
    "# compute the relative positions of the average shape points to its center\n",
    "relative_average_shape = average_shape - average_shape_center\n",
    "\n",
    "# in each frame compute the center of the quadrilateral and apply to it the relative average shape\n",
    "new_quads = []\n",
    "for t in range(len(quads)):\n",
    "    center = quad_center(quads[t])\n",
    "    new_quad = relative_average_shape + center\n",
    "    new_quads.append(new_quad)\n",
    "\n",
    "# Convert result\n",
    "new_quads = np.array(new_quads)\n",
    "# Update DataFrame\n",
    "flattened = new_quads.reshape((new_quads.shape[0], -1))\n",
    "df_avg_shape = points_df.copy()\n",
    "df_avg_shape[point_cols] = flattened\n",
    "\n",
    "# display the first few rows of the DataFrame\n",
    "# print(df_avg_shape.head())\n",
    "# print(points_df.head())\n",
    "\n",
    "# print the average of each row of df_avg_shape and points_df\n",
    "# print(df_avg_shape.mean(axis=1))\n",
    "# print(points_df.mean(axis=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e7b18878",
   "metadata": {},
   "source": [
    "Postprocessing on the points"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "688e6cba",
   "metadata": {},
   "outputs": [],
   "source": [
    "def Savitzky_Golay_filter(points, window_length=25, polyorder=3):\n",
    "    \"\"\"\n",
    "    Smooths the X and Y coordinates using Savitzky-Golay filter to reduce noise.\n",
    "    \"\"\"\n",
    "\n",
    "    points[:, 0] = savgol_filter(\n",
    "        points[:, 0], window_length=window_length, polyorder=polyorder\n",
    "    )\n",
    "    points[:, 1] = savgol_filter(\n",
    "        points[:, 1], window_length=window_length, polyorder=polyorder\n",
    "    )\n",
    "\n",
    "    return points"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "016851ef",
   "metadata": {},
   "outputs": [],
   "source": [
    "def process_point(points):\n",
    "    Savitzky_Golay_filter(points, window_length=5, polyorder=1)\n",
    "    # points = medfilt(points, kernel_size=(3, 1))\n",
    "    return points"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "194e28fb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'USELESS'"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bottom_left = points_df[[\"bottom_left_x\", \"bottom_left_y\"]].values\n",
    "bottom_right = points_df[[\"bottom_right_x\", \"bottom_right_y\"]].values\n",
    "up_left = points_df[[\"up_left_x\", \"up_left_y\"]].values\n",
    "up_right = points_df[[\"up_right_x\", \"up_right_y\"]].values\n",
    "\n",
    "bottom_left_processed = process_point(bottom_left)\n",
    "bottom_right_processed = process_point(bottom_right)\n",
    "up_left_processed = process_point(up_left)\n",
    "up_right_processed = process_point(up_right)\n",
    "\n",
    "\n",
    "# Create a new DataFrame with the processed points\n",
    "processed_df = pd.DataFrame(\n",
    "    {\n",
    "        \"bottom_left_x\": bottom_left_processed[:, 0],\n",
    "        \"bottom_left_y\": bottom_left_processed[:, 1],\n",
    "        \"bottom_right_x\": bottom_right_processed[:, 0],\n",
    "        \"bottom_right_y\": bottom_right_processed[:, 1],\n",
    "        \"up_left_x\": up_left_processed[:, 0],\n",
    "        \"up_left_y\": up_left_processed[:, 1],\n",
    "        \"up_right_x\": up_right_processed[:, 0],\n",
    "        \"up_right_y\": up_right_processed[:, 1],\n",
    "    }\n",
    ")\n",
    "\n",
    "\"\"\"USELESS\"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11e80f8e",
   "metadata": {},
   "source": [
    "PostProcessing on the distances between points"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "af3b4fe3",
   "metadata": {},
   "outputs": [],
   "source": [
    "left_line = points_df[\n",
    "    [\"bottom_left_x\", \"bottom_left_y\", \"up_left_x\", \"up_left_y\"]\n",
    "].values\n",
    "right_line = points_df[\n",
    "    [\"bottom_right_x\", \"bottom_right_y\", \"up_right_x\", \"up_right_y\"]\n",
    "].values\n",
    "bottom_line = points_df[\n",
    "    [\"bottom_left_x\", \"bottom_left_y\", \"bottom_right_x\", \"bottom_right_y\"]\n",
    "].values\n",
    "up_line = points_df[[\"up_left_x\", \"up_left_y\", \"up_right_x\", \"up_right_y\"]].values\n",
    "# compute the difference between the two endpoints of the line\n",
    "left_line_diff = left_line[:, 2:] - left_line[:, :2]\n",
    "right_line_diff = right_line[:, 2:] - right_line[:, :2]\n",
    "bottom_line_diff = bottom_line[:, 2:] - bottom_line[:, :2]\n",
    "up_line_diff = up_line[:, 2:] - up_line[:, :2]\n",
    "\n",
    "left_processed_diff = process_point(left_line_diff)\n",
    "right_processed_diff = process_point(right_line_diff)\n",
    "bottom_processed_diff = process_point(bottom_line_diff)\n",
    "up_processed_diff = process_point(up_line_diff)\n",
    "\n",
    "left_processed = left_processed_diff + left_line[:, :2]\n",
    "right_processed = right_processed_diff + right_line[:, :2]\n",
    "bottom_processed = bottom_processed_diff + bottom_line[:, :2]\n",
    "up_processed = up_processed_diff + up_line[:, :2]\n",
    "\n",
    "# Create a new DataFrame with the processed points\n",
    "# processed_df = pd.DataFrame({\n",
    "#     'bottom_left_x': bottom_line[:, 0],\n",
    "#     'bottom_left_y': bottom_line[:, 1],\n",
    "#     'bottom_right_x': bottom_processed[:, 0],\n",
    "#     'bottom_right_y': bottom_processed[:, 1],\n",
    "#     'up_left_x': up_line[:, 0],\n",
    "#     'up_left_y': up_line[:, 1],\n",
    "#     'up_right_x': up_processed[:, 0],\n",
    "#     'up_right_y': up_processed[:, 1]\n",
    "# })\n",
    "\n",
    "processed_df = pd.DataFrame(\n",
    "    {\n",
    "        \"bottom_left_x\": left_processed[:, 0],\n",
    "        \"bottom_left_y\": left_processed[:, 1],\n",
    "        \"bottom_right_x\": right_processed[:, 0],\n",
    "        \"bottom_right_y\": right_processed[:, 1],\n",
    "        \"up_left_x\": left_line[:, 0],\n",
    "        \"up_left_y\": left_line[:, 1],\n",
    "        \"up_right_x\": right_line[:, 0],\n",
    "        \"up_right_y\": right_line[:, 1],\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db7e8fbd",
   "metadata": {},
   "source": [
    "##  Modify the video"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "370b9dea",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adjusted video saved to C:\\Users\\miche\\OneDrive\\Documenti\\GitHub\\bowling-analysis\\data\\recording_3\\Lane_detection.mp4\n"
     ]
    }
   ],
   "source": [
    "# Reset the video to the beginning\n",
    "cap.set(cv2.CAP_PROP_POS_FRAMES, 0)\n",
    "\n",
    "# Define the codec and create a VideoWriter object to save the modified frames\n",
    "output_path = project_root / \"data\" / f\"recording_{video_number}\" / \"Lane_detection.mp4\"\n",
    "fourcc = cv2.VideoWriter_fourcc(*\"mp4v\")  # Use 'mp4v' codec for MP4 format\n",
    "fps = int(cap.get(cv2.CAP_PROP_FPS))\n",
    "frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))\n",
    "frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))\n",
    "out = cv2.VideoWriter(str(output_path), fourcc, fps, (frame_width, frame_height))\n",
    "\n",
    "# Process the lines\n",
    "lines = lines_from_points(points_df)\n",
    "# print('Processed lines:', lines)\n",
    "\n",
    "\n",
    "# Loop through each frame in the video\n",
    "frame_index = 0\n",
    "while frame_index < len(lines):\n",
    "    ret, video_frame = cap.read()\n",
    "    if not ret:\n",
    "        print(\"End of video or failed to read the frame at iteration\", frame_index)\n",
    "        break\n",
    "    # print(f\"Processing frame {frame_index}\")\n",
    "\n",
    "    # draw the lines on the frame\n",
    "    modified_frame = write_lines_on_frame(video_frame, lines[frame_index])\n",
    "\n",
    "    # Write the modified frame to the output video\n",
    "    out.write(modified_frame)\n",
    "\n",
    "    # Increment the frame index\n",
    "    frame_index += 1\n",
    "\n",
    "# Release the video capture and writer objects\n",
    "# cap.release()\n",
    "out.release()\n",
    "\n",
    "print(f\"Adjusted video saved to {output_path}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
