{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":59156,"databundleVersionId":13719397,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"# Mouse Action Animation","metadata":{}},{"cell_type":"markdown","source":"**train_annotation**\n\n* agent_id - ID of the mouse performing the behavior.\n* target_id - ID of the mouse targeted by the behavior. Some behaviors, such as a mouse grooming itself, have the same agent and target ID.\n* action - What behavior is occurring (e.g., grooming, chasing). Different labs annotated different behaviors.\n* [start/stop]_frame - The first/last frame of the action.","metadata":{}},{"cell_type":"code","source":"\nimport os\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.animation import FuncAnimation\nfrom IPython.display import Image","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-22T13:16:54.073280Z","iopub.execute_input":"2025-09-22T13:16:54.073637Z","iopub.status.idle":"2025-09-22T13:16:54.078435Z","shell.execute_reply.started":"2025-09-22T13:16:54.073613Z","shell.execute_reply":"2025-09-22T13:16:54.077541Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filename='AdaptableSnail/1212811043.parquet'\n\nanno=pd.read_parquet(f'/kaggle/input/MABe-mouse-behavior-detection/train_annotation/{filename}')\ntrack=pd.read_parquet(f'/kaggle/input/MABe-mouse-behavior-detection/train_tracking/{filename}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-22T13:16:54.079754Z","iopub.execute_input":"2025-09-22T13:16:54.080153Z","iopub.status.idle":"2025-09-22T13:16:54.253403Z","shell.execute_reply.started":"2025-09-22T13:16:54.080125Z","shell.execute_reply":"2025-09-22T13:16:54.252600Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"display(anno)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-22T13:16:54.254727Z","iopub.execute_input":"2025-09-22T13:16:54.254979Z","iopub.status.idle":"2025-09-22T13:16:54.266031Z","shell.execute_reply.started":"2025-09-22T13:16:54.254958Z","shell.execute_reply":"2025-09-22T13:16:54.264830Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"display(anno.iloc[367:368])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-22T13:33:05.437872Z","iopub.execute_input":"2025-09-22T13:33:05.438199Z","iopub.status.idle":"2025-09-22T13:33:05.448435Z","shell.execute_reply.started":"2025-09-22T13:33:05.438174Z","shell.execute_reply":"2025-09-22T13:33:05.447385Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"track2 = track[\n    (track['video_frame'] >= 89171) &\n    (track['video_frame'] <= 89194) &\n    (track['mouse_id'].isin([1, 3]))\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-22T13:34:47.056162Z","iopub.execute_input":"2025-09-22T13:34:47.056463Z","iopub.status.idle":"2025-09-22T13:34:47.089382Z","shell.execute_reply.started":"2025-09-22T13:34:47.056440Z","shell.execute_reply":"2025-09-22T13:34:47.088423Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"display(track2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-22T13:35:04.166163Z","iopub.execute_input":"2025-09-22T13:35:04.166469Z","iopub.status.idle":"2025-09-22T13:35:04.178665Z","shell.execute_reply.started":"2025-09-22T13:35:04.166446Z","shell.execute_reply":"2025-09-22T13:35:04.177559Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.animation import FuncAnimation\nfrom IPython.display import Image\n\n# Prepare data - using only mouse1\nmouse1 = track2[track2['mouse_id'] == 1].copy()\n\n# Handle infinite values and NaN values\nmouse1['x'] = mouse1['x'].replace([np.inf, -np.inf], np.nan)\nmouse1['y'] = mouse1['y'].replace([np.inf, -np.inf], np.nan)\nmouse1.dropna(subset=['x', 'y'], inplace=True)\n\n# Coordinate ranges\nx_min = mouse1['x'].min()\nx_max = mouse1['x'].max()\ny_min = mouse1['y'].min()\ny_max = mouse1['y'].max()\n\n# Get a list of unique body parts\nbodyparts = mouse1['bodypart'].unique()\nprint(\"Bodyparts found:\", bodyparts)\n\n# Assign a color to each body part\ncolors = ['red', 'blue', 'green', 'orange', 'purple', 'brown', 'pink', 'gray', 'olive', 'cyan']\nif len(bodyparts) > len(colors):\n    # If there are too many body parts and not enough colors, generate additional colors\n    import matplotlib.colors as mcolors\n    colors = list(mcolors.TABLEAU_COLORS.values()) + list(mcolors.CSS4_COLORS.values())[:len(bodyparts)-len(colors)]\n\n# Create animation\nfig, ax = plt.subplots(figsize=(12, 10))\nax.set_xlim(x_min - 10, x_max + 10)\nax.set_ylim(y_min - 10, y_max + 10)\nax.set_xlabel('X coordinate')\nax.set_ylabel('Y coordinate')\nax.set_title('Mouse 1 Body Parts Movement Animation')\n\n# Lists to store the trajectory and current position of each body part\nlines = []\npoints = []\n\n# Create a trajectory line and a point for each body part\nfor i, bodypart in enumerate(bodyparts):\n    color = colors[i % len(colors)]\n    line, = ax.plot([], [], color=color, alpha=0.5, linewidth=1, label=f'{bodypart}')\n    point, = ax.plot([], [], 'o', color=color, markersize=8, markeredgecolor='black', markeredgewidth=0.5)\n    lines.append(line)\n    points.append(point)\n\nframe_text = ax.text(0.02, 0.95, '', transform=ax.transAxes, fontsize=12, bbox=dict(facecolor='white', alpha=0.7))\nax.legend(loc='upper right')\n\ndef init():\n    for line, point in zip(lines, points):\n        line.set_data([], [])\n        point.set_data([], [])\n    frame_text.set_text('')\n    return lines + points + [frame_text]\n\ndef update(frame):\n    # Update the data for each body part\n    for i, bodypart in enumerate(bodyparts):\n        # Filter the data for the current body part\n        bodypart_data = mouse1[mouse1['bodypart'] == bodypart]\n        data = bodypart_data[bodypart_data['video_frame'] <= frame]\n        \n        if len(data) > 0:\n            lines[i].set_data(data['x'], data['y'])\n            current = data.iloc[-1]\n            points[i].set_data([current['x']], [current['y']])\n        else:\n            lines[i].set_data([], [])\n            points[i].set_data([], [])\n    \n    frame_text.set_text(f'Frame: {frame}')\n    return lines + points + [frame_text]\n\n# Frames (using only mouse1)\nframes = mouse1['video_frame'].unique()\nframes.sort()\n\nprint(f\"Total frames: {len(frames)}\")\n\nif len(frames) > 500:\n    step = max(1, len(frames) // 500)\n    frames = frames[::step]\n    print(f\"Sampled frames: {len(frames)}\")\n\n# Animation\nani = FuncAnimation(fig, update, frames=frames,\n                    init_func=init, blit=True, interval=100)\n\n# Save as GIF\noutput_path = \"mouse1_bodyparts_movement.gif\"\nani.save(output_path, writer='pillow', fps=10, dpi=100)\nplt.close()\nprint(\"Animation GIF saved:\", output_path)\n\n# Display in notebook\nImage(open(output_path, 'rb').read())","metadata":{"trusted":true,"_kg_hide-input":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from matplotlib.lines import Line2D\nimport numpy as np\n\n# Create Line2D objects for the full mouse body lines\nmouse_lines = {}\n\n# Initialization (body, tail, head)\ntail_line, = ax.plot([], [], 'brown', linewidth=3, alpha=0.7)\nbody_line, = ax.plot([], [], 'gray', linewidth=4, alpha=0.7)\nhead_line, = ax.plot([], [], 'gray', linewidth=3, alpha=0.7)\n\nmouse_lines['tail'] = tail_line\nmouse_lines['body'] = body_line\nmouse_lines['head'] = head_line\n\n# Modify the initialization function\ndef init():\n    for i, bodypart in enumerate(bodyparts):\n        lines[i].set_data([], [])\n        points[i].set_data([], [])\n    \n    # Initialize mouse lines as well\n    for line in mouse_lines.values():\n        line.set_data([], [])\n    \n    frame_text.set_text('')\n    return lines + points + [frame_text] + list(mouse_lines.values())\n\n# Modify the update function\ndef update2(frame):\n    # Original body part animation update\n    for i, bodypart in enumerate(bodyparts):\n        bodypart_data = mouse1[mouse1['bodypart'] == bodypart]\n        data = bodypart_data[bodypart_data['video_frame'] <= frame]\n        \n        if len(data) > 0:\n            lines[i].set_data(data['x'], data['y'])\n            current = data.iloc[-1]\n            points[i].set_data([current['x']], [current['y']])\n        else:\n            lines[i].set_data([], [])\n            points[i].set_data([], [])\n    \n    # Update full mouse body lines\n    current_points = {}\n    for bp in bodyparts:\n        d = mouse1[(mouse1['bodypart']==bp) & (mouse1['video_frame']==frame)]\n        if len(d) > 0:\n            current_points[bp] = np.array([d['x'].values[0], d['y'].values[0]])\n    \n    # Draw the tail (connecting 3 points smoothly)\n    if all(k in current_points for k in ['tail_base','tail_midpoint','tail_tip']):\n        tail_x = [current_points['tail_base'][0], current_points['tail_midpoint'][0], current_points['tail_tip'][0]]\n        tail_y = [current_points['tail_base'][1], current_points['tail_midpoint'][1], current_points['tail_tip'][1]]\n        mouse_lines['tail'].set_data(tail_x, tail_y)\n    else:\n        mouse_lines['tail'].set_data([], [])\n    \n    # Draw the body (a more natural shape)\n    if all(k in current_points for k in ['lateral_left','lateral_right','body_center']):\n        # Draw a shape similar to an ellipse\n        body_x = [\n            current_points['lateral_left'][0],\n            current_points['body_center'][0],\n            current_points['lateral_right'][0],\n            current_points['body_center'][0],\n            current_points['lateral_left'][0]  # close the shape\n        ]\n        body_y = [\n            current_points['lateral_left'][1],\n            current_points['body_center'][1] + 5,  # slightly up\n            current_points['lateral_right'][1],\n            current_points['body_center'][1] - 5,  # slightly down\n            current_points['lateral_left'][1]  # close the shape\n        ]\n        mouse_lines['body'].set_data(body_x, body_y)\n    else:\n        mouse_lines['body'].set_data([], [])\n    \n    # Draw the head (a shape similar to a triangle)\n    if all(k in current_points for k in ['neck','nose','ear_left','ear_right']):\n        head_x = [\n            current_points['neck'][0],\n            current_points['ear_left'][0],\n            current_points['nose'][0],\n            current_points['ear_right'][0],\n            current_points['neck'][0]  # close the shape\n        ]\n        head_y = [\n            current_points['neck'][1],\n            current_points['ear_left'][1],\n            current_points['nose'][1],\n            current_points['ear_right'][1],\n            current_points['neck'][1]  # close the shape\n        ]\n        mouse_lines['head'].set_data(head_x, head_y)\n    else:\n        mouse_lines['head'].set_data([], [])\n    \n    frame_text.set_text(f'Frame: {frame}')\n    \n    # Modify the return value\n    return_lines = lines + points + [frame_text] + [mouse_lines['tail'], mouse_lines['body'], mouse_lines['head']]\n    return return_lines\n\n# Create the animation (with the correct variable name)\nani2 = FuncAnimation(fig, update2, frames=frames,\n                    init_func=init, blit=True, interval=100)\n\n# Save using ani2 (important!)\noutput_path2 = \"mouse1_bodyparts_movement2.gif\"\nani2.save(output_path2, writer='pillow', fps=10, dpi=100)\nplt.close()\nprint(\"Animation GIF saved:\", output_path2)\n\n# Display in notebook\nImage(open(output_path2, 'rb').read())","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from matplotlib.patches import Polygon, Ellipse\nimport numpy as np\nfrom scipy.interpolate import interp1d\n\n# Create patch objects for the silhouettes\nmouse_patches = {}\n\n# Patch for the overall body silhouette\nsilhouette_patch = Polygon([[0,0]], closed=True, color='darkgray', alpha=0.6, edgecolor='black', linewidth=2)\nax.add_patch(silhouette_patch)\nmouse_patches['silhouette'] = silhouette_patch\n\n# Patches for individual parts (optional)\nbody_patch = Ellipse((0,0), width=10, height=5, color='gray', alpha=0.5)\nhead_patch = Ellipse((0,0), width=8, height=6, color='gray', alpha=0.5)\nax.add_patch(body_patch)\nax.add_patch(head_patch)\nmouse_patches['body'] = body_patch\nmouse_patches['head'] = head_patch\n\n# Update the initialization function\ndef init():\n    for i, bodypart in enumerate(bodyparts):\n        lines[i].set_data([], [])\n        points[i].set_data([], [])\n    \n    # Initialize mouse lines\n    for line in mouse_lines.values():\n        line.set_data([], [])\n    \n    # Initialize patches\n    for patch in mouse_patches.values():\n        if isinstance(patch, Polygon):\n            patch.set_xy(np.array([[0,0]]))\n        elif isinstance(patch, Ellipse):\n            patch.set_center((0,0))\n            patch.set_width(0)\n            patch.set_height(0)\n    \n    frame_text.set_text('')\n    return_lines = lines + points + [frame_text] + list(mouse_lines.values())\n    return_patches = list(mouse_patches.values())\n    return return_lines + return_patches\n\n# Improve the update function to add a silhouette\ndef update3(frame):\n    # Update original body part animation\n    for i, bodypart in enumerate(bodyparts):\n        bodypart_data = mouse1[mouse1['bodypart'] == bodypart]\n        data = bodypart_data[bodypart_data['video_frame'] <= frame]\n        \n        if len(data) > 0:\n            lines[i].set_data(data['x'], data['y'])\n            current = data.iloc[-1]\n            points[i].set_data([current['x']], [current['y']])\n        else:\n            lines[i].set_data([], [])\n            points[i].set_data([], [])\n    \n    # Update full mouse body lines\n    current_points = {}\n    for bp in bodyparts:\n        d = mouse1[(mouse1['bodypart']==bp) & (mouse1['video_frame']==frame)]\n        if len(d) > 0:\n            current_points[bp] = np.array([d['x'].values[0], d['y'].values[0]])\n    \n    # Create the full body silhouette\n    if all(k in current_points for k in ['tail_base', 'lateral_left', 'lateral_right', 'neck', 'ear_left', 'ear_right', 'nose']):\n        # Define the contour points of the mouse (clockwise or counter-clockwise)\n        silhouette_points = [\n            current_points['tail_base'],      # Base of the tail\n            current_points['lateral_left'],   # Left side\n            current_points['ear_left'],       # Left ear\n            current_points['nose'],           # Tip of the nose\n            current_points['ear_right'],      # Right ear\n            current_points['lateral_right'],  # Right side\n            current_points['tail_base']       # Close the shape\n        ]\n        \n        # Extract the coordinates of the points\n        silhouette_xy = np.array(silhouette_points)\n        mouse_patches['silhouette'].set_xy(silhouette_xy)\n    else:\n        mouse_patches['silhouette'].set_xy(np.array([[0,0]]))\n    \n    # Draw the tail\n    if all(k in current_points for k in ['tail_base','tail_midpoint','tail_tip']):\n        tail_x = [current_points['tail_base'][0], current_points['tail_midpoint'][0], current_points['tail_tip'][0]]\n        tail_y = [current_points['tail_base'][1], current_points['tail_midpoint'][1], current_points['tail_tip'][1]]\n        mouse_lines['tail'].set_data(tail_x, tail_y)\n    else:\n        mouse_lines['tail'].set_data([], [])\n    \n    # Body silhouette (represented by an ellipse)\n    if all(k in current_points for k in ['lateral_left','lateral_right','body_center']):\n        # Calculate the center and size of the body\n        body_center_x = current_points['body_center'][0]\n        body_center_y = current_points['body_center'][1]\n        body_width = abs(current_points['lateral_left'][0] - current_points['lateral_right'][0]) * 1.2\n        body_height = body_width * 0.6\n        \n        mouse_patches['body'].set_center((body_center_x, body_center_y))\n        mouse_patches['body'].set_width(body_width)\n        mouse_patches['body'].set_height(body_height)\n    else:\n        mouse_patches['body'].set_width(0)\n        mouse_patches['body'].set_height(0)\n    \n    # Head silhouette\n    if all(k in current_points for k in ['neck','nose','ear_left','ear_right']):\n        head_center_x = (current_points['neck'][0] + current_points['nose'][0]) / 2\n        head_center_y = (current_points['neck'][1] + current_points['nose'][1]) / 2\n        head_width = abs(current_points['ear_left'][0] - current_points['ear_right'][0]) * 1.1\n        head_height = abs(current_points['neck'][1] - current_points['nose'][1]) * 1.3\n        \n        mouse_patches['head'].set_center((head_center_x, head_center_y))\n        mouse_patches['head'].set_width(head_width)\n        mouse_patches['head'].set_height(head_height)\n        \n        # Also update the head line\n        head_x = [\n            current_points['neck'][0],\n            current_points['ear_left'][0],\n            current_points['nose'][0],\n            current_points['ear_right'][0],\n            current_points['neck'][0]\n        ]\n        head_y = [\n            current_points['neck'][1],\n            current_points['ear_left'][1],\n            current_points['nose'][1],\n            current_points['ear_right'][1],\n            current_points['neck'][1]\n        ]\n        mouse_lines['head'].set_data(head_x, head_y)\n    else:\n        mouse_patches['head'].set_width(0)\n        mouse_patches['head'].set_height(0)\n        mouse_lines['head'].set_data([], [])\n    \n    frame_text.set_text(f'Frame: {frame}')\n    \n    # Add patches to the return value\n    return_lines = lines + points + [frame_text] + list(mouse_lines.values())\n    return_patches = list(mouse_patches.values())\n    return return_lines + return_patches\n\n# Create the animation\nani3 = FuncAnimation(fig, update3, frames=frames,\n                    init_func=init, blit=True, interval=100)\n\n# Save the GIF\noutput_path3 = \"mouse1_silhouette_animation3.gif\"\nani3.save(output_path3, writer='pillow', fps=10, dpi=100)\nplt.close()\nprint(\"Silhouette Animation GIF saved:\", output_path3)\n\n# Display\nImage(open(output_path3, 'rb').read())","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}