{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Sign Language Data Exploration Insights and fun 3D","metadata":{}},{"cell_type":"markdown","source":"## Why this notebook ?\n* My purpose in this notebook is to dive into the data of the competition to explore and visualize it.\n* It helps me understand and uncover some insights or find some patterns.\n* I share it as my contribution to the community and in the hope that it will helpful for someone.","metadata":{}},{"cell_type":"markdown","source":"### Imports","metadata":{}},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport seaborn as sns\nfrom mpl_toolkits.mplot3d import Axes3D\nimport matplotlib.pyplot as plt\nimport os","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-03-25T20:44:02.394123Z","iopub.execute_input":"2023-03-25T20:44:02.394514Z","iopub.status.idle":"2023-03-25T20:44:03.133557Z","shell.execute_reply.started":"2023-03-25T20:44:02.394479Z","shell.execute_reply":"2023-03-25T20:44:03.131874Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Load Train csv","metadata":{}},{"cell_type":"code","source":"train_df = pd.read_csv('/kaggle/input/asl-signs/train.csv')\nprint(\"Lenght of train set: \",len(train_df))\ntrain_df","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:03.136059Z","iopub.execute_input":"2023-03-25T20:44:03.136567Z","iopub.status.idle":"2023-03-25T20:44:03.403022Z","shell.execute_reply.started":"2023-03-25T20:44:03.136484Z","shell.execute_reply":"2023-03-25T20:44:03.401732Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.info()","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:03.404513Z","iopub.execute_input":"2023-03-25T20:44:03.405351Z","iopub.status.idle":"2023-03-25T20:44:03.435865Z","shell.execute_reply.started":"2023-03-25T20:44:03.405310Z","shell.execute_reply":"2023-03-25T20:44:03.434572Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.describe()","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:03.439287Z","iopub.execute_input":"2023-03-25T20:44:03.439753Z","iopub.status.idle":"2023-03-25T20:44:03.468849Z","shell.execute_reply.started":"2023-03-25T20:44:03.439716Z","shell.execute_reply":"2023-03-25T20:44:03.467578Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### In the train data\nThey are 21 unique particpants who realised the sequences.\n\n94477 sequences in total in the train data and 250 unique sign present in the sequences.","metadata":{}},{"cell_type":"code","source":"print(\"Number of participant: \",train_df['participant_id'].nunique())\nprint(\"Number of sequence: \",train_df['sequence_id'].nunique())\nprint(\"Number of sign: \",train_df['sign'].nunique())","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:03.470077Z","iopub.execute_input":"2023-03-25T20:44:03.470396Z","iopub.status.idle":"2023-03-25T20:44:03.490711Z","shell.execute_reply.started":"2023-03-25T20:44:03.470365Z","shell.execute_reply":"2023-03-25T20:44:03.489540Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Participant\nA participant made at min 3338 sequences and at most 4968 sequences.","metadata":{}},{"cell_type":"code","source":"print(train_df['participant_id'].value_counts(normalize=True))\nprint(train_df['participant_id'].value_counts())\n\n# setting the dimensions of the plot\nfig, ax = plt.subplots(figsize=(17, 5))\n \n# drawing the plot\nsns.countplot(data=train_df, x='participant_id')\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:03.492061Z","iopub.execute_input":"2023-03-25T20:44:03.492378Z","iopub.status.idle":"2023-03-25T20:44:03.888624Z","shell.execute_reply.started":"2023-03-25T20:44:03.492347Z","shell.execute_reply":"2023-03-25T20:44:03.887808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Sign","metadata":{}},{"cell_type":"code","source":"sign_count = train_df['sign'].value_counts()\n\nprint(\"Minimun number of row for a sign: \",min(sign_count))\nprint(\"Maximum number of row for a sign: \",max(sign_count))","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:03.890079Z","iopub.execute_input":"2023-03-25T20:44:03.890826Z","iopub.status.idle":"2023-03-25T20:44:03.904536Z","shell.execute_reply.started":"2023-03-25T20:44:03.890777Z","shell.execute_reply":"2023-03-25T20:44:03.903117Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(train_df['sign'].value_counts(normalize=True))\nprint(sign_count)\n\n# setting the dimensions of the plot\nfig, ax = plt.subplots(figsize=(40, 5))\n \n# drawing the plot\nsns.countplot(data=train_df, x='sign')\nplt.xticks(rotation = 'vertical')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:03.906532Z","iopub.execute_input":"2023-03-25T20:44:03.907260Z","iopub.status.idle":"2023-03-25T20:44:07.089466Z","shell.execute_reply.started":"2023-03-25T20:44:03.907196Z","shell.execute_reply":"2023-03-25T20:44:07.088159Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Let's plot in as a lucky wheel (just for fun!)","metadata":{}},{"cell_type":"code","source":"# setting the dimensions of the plot\nfig, ax = plt.subplots(figsize=(50, 5))\n\nplt.pie(sign_count.values, labels=sign_count.index,radius=30,rotatelabels=True)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:07.091527Z","iopub.execute_input":"2023-03-25T20:44:07.092433Z","iopub.status.idle":"2023-03-25T20:44:21.755503Z","shell.execute_reply.started":"2023-03-25T20:44:07.092387Z","shell.execute_reply":"2023-03-25T20:44:21.753323Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Read and plot the parquet file","metadata":{}},{"cell_type":"markdown","source":"The parquet file at index idx in the train dataframe is read and a specific frame index is ploted in 3D.","metadata":{}},{"cell_type":"code","source":"idx = 10 # choose any index of row to read the parquet file\nparquet_file = os.path.join('/kaggle/input/asl-signs/',train_df['path'][idx])\n# parquet_df = pd.read_parquet('/kaggle/input/asl-signs/train_landmark_files/16069/100015657.parquet')\nparquet_df = pd.read_parquet(parquet_file)\nparquet_df.y = parquet_df.y * -1\nparquet_df","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:21.763478Z","iopub.execute_input":"2023-03-25T20:44:21.764473Z","iopub.status.idle":"2023-03-25T20:44:21.927863Z","shell.execute_reply.started":"2023-03-25T20:44:21.764401Z","shell.execute_reply":"2023-03-25T20:44:21.926803Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Frame contains 543 landmarks but not always the same number of x,y,z.","metadata":{}},{"cell_type":"code","source":"parquet_df.groupby(['frame']).count()","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:21.929377Z","iopub.execute_input":"2023-03-25T20:44:21.930027Z","iopub.status.idle":"2023-03-25T20:44:21.958155Z","shell.execute_reply.started":"2023-03-25T20:44:21.929986Z","shell.execute_reply":"2023-03-25T20:44:21.956619Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(parquet_df['landmark_index'].unique()))\nprint(len(parquet_df['type'].unique()))\nprint(len(parquet_df['frame'].unique()))\nprint(len(parquet_df['row_id'].unique()))","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:21.960374Z","iopub.execute_input":"2023-03-25T20:44:21.961290Z","iopub.status.idle":"2023-03-25T20:44:21.975226Z","shell.execute_reply.started":"2023-03-25T20:44:21.961231Z","shell.execute_reply":"2023-03-25T20:44:21.974055Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"# Get the type list\ntype_list = parquet_df['type'].unique()\nprint(type_list)\n# Get the frame list\nframe_list = parquet_df['frame'].unique()\nprint(frame_list)\nplot_frameID = frame_list[24] # select a frame to plot","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:21.976767Z","iopub.execute_input":"2023-03-25T20:44:21.977412Z","iopub.status.idle":"2023-03-25T20:44:21.986916Z","shell.execute_reply.started":"2023-03-25T20:44:21.977370Z","shell.execute_reply":"2023-03-25T20:44:21.985889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Download and install mediapipe to plot the landmark in better way","metadata":{}},{"cell_type":"code","source":"!pip install mediapipe\nimport mediapipe as mp\nfrom mediapipe.tasks.python.components.containers import Landmark, NormalizedLandmark","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:21.988440Z","iopub.execute_input":"2023-03-25T20:44:21.989112Z","iopub.status.idle":"2023-03-25T20:44:37.555359Z","shell.execute_reply.started":"2023-03-25T20:44:21.989072Z","shell.execute_reply":"2023-03-25T20:44:37.554196Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Landmark plotting functions copy and adjust from Mediapipe repository\nLink : https://github.com/google/mediapipe/blob/master/mediapipe/python/solutions/drawing_utils.py","metadata":{}},{"cell_type":"code","source":"import math\nfrom typing import List, Mapping, Optional, Tuple, Union\n\n_PRESENCE_THRESHOLD = 0.5\n_VISIBILITY_THRESHOLD = 0.5\n_BGR_CHANNELS = 3\n\nWHITE_COLOR = (224, 224, 224)\nBLACK_COLOR = (0, 0, 0)\nRED_COLOR = (0, 0, 255)\nGREEN_COLOR = (0, 128, 0)\nBLUE_COLOR = (255, 0, 0)\n\ndef _normalize_color(color):\n    return tuple(v / 255. for v in color)\n\nclass DrawingSpec:\n    # Color for drawing the annotation. Default to the white color.\n    color: Tuple[int, int, int] = WHITE_COLOR\n    # Thickness for drawing the annotation. Default to 2 pixels.\n    thickness: int = 2\n    # Circle radius. Default to 2 pixels.\n    circle_radius: int = 2\n        \ndef plot_landmarks(landmark_list,\n                   connections: Optional[List[Tuple[int, int]]] = None,\n                   elevation: int = 10,\n                   azimuth: int = 10, big=False):\n    \"\"\"Plot the landmarks and the connections in matplotlib 3d.\n        Args:\n        landmark_list: A normalized landmark list proto message to be plotted.\n        connections: A list of landmark index tuples that specifies how landmarks to\n          be connected.\n        landmark_drawing_spec: A DrawingSpec object that specifies the landmarks'\n          drawing settings such as color and line thickness.\n        connection_drawing_spec: A DrawingSpec object that specifies the\n          connections' drawing settings such as color and line thickness.\n        elevation: The elevation from which to view the plot.\n        azimuth: the azimuth angle to rotate the plot.\n        Raises:\n        ValueError: If any connection contains an invalid landmark index.\n    \"\"\"\n    if not landmark_list:\n        return\n    if big:\n        plt.figure(figsize=(20, 20))\n    else:\n        plt.figure(figsize=(10, 10))\n    ax = plt.axes(projection='3d')\n    ax.view_init(elev=elevation, azim=azimuth)\n    plotted_landmarks = {}\n    for idx, landmark in enumerate(landmark_list):\n        ax.scatter3D(\n            xs=[-landmark.z],\n            ys=[landmark.x],\n            zs=[-landmark.y],\n            color=_normalize_color(GREEN_COLOR),\n            linewidth=5)\n        plotted_landmarks[idx] = (-landmark.z, landmark.x, -landmark.y)\n    if connections:\n        num_landmarks = len(landmark_list)\n    # Draws the connections if the start and end landmarks are both visible.\n    for connection in connections:\n        start_idx = connection[0]\n        end_idx = connection[1]\n        if not (0 <= start_idx < num_landmarks and 0 <= end_idx < num_landmarks):\n            raise ValueError(f'Landmark index is out of range. Invalid connection '\n                         f'from landmark #{start_idx} to landmark #{end_idx}.')\n        if start_idx in plotted_landmarks and end_idx in plotted_landmarks:\n            landmark_pair = [\n                plotted_landmarks[start_idx], plotted_landmarks[end_idx]\n            ]\n        ax.plot3D(\n            xs=[landmark_pair[0][0], landmark_pair[1][0]],\n            ys=[landmark_pair[0][1], landmark_pair[1][1]],\n            zs=[landmark_pair[0][2], landmark_pair[1][2]],\n            color=_normalize_color(GREEN_COLOR),\n            linewidth=5)\n    plt.show()\n    \n    # custom for 2D projection\ndef plot_landmarks2D(landmark_list,connections: Optional[List[Tuple[int, int]]] = None, big=False):\n    \"\"\"Plot the landmarks and the connections in matplotlib 2d.\n        Args:\n        landmark_list: A normalized landmark list proto message to be plotted.\n        connections: A list of landmark index tuples that specifies how landmarks to\n          be connected.\n        landmark_drawing_spec: A DrawingSpec object that specifies the landmarks'\n          drawing settings such as color and line thickness.\n        connection_drawing_spec: A DrawingSpec object that specifies the\n          connections' drawing settings such as color and line thickness.\n        Raises:\n        ValueError: If any connection contains an invalid landmark index.\n    \"\"\"\n    if not landmark_list:\n        return\n    \n    if big:\n        plt.figure(figsize=(20, 20))\n    else:\n        plt.figure(figsize=(6, 6))\n        \n    ax = plt.axes()\n    plotted_landmarks = {}\n    for idx, landmark in enumerate(landmark_list):\n        ax.scatter([landmark.x],[-landmark.y],\n            color=_normalize_color(GREEN_COLOR),\n            linewidth=5)\n        plotted_landmarks[idx] = (landmark.x, -landmark.y)\n    if connections:\n        num_landmarks = len(landmark_list)\n    # Draws the connections if the start and end landmarks are both visible.\n    for connection in connections:\n        start_idx = connection[0]\n        end_idx = connection[1]\n        if not (0 <= start_idx < num_landmarks and 0 <= end_idx < num_landmarks):\n            raise ValueError(f'Landmark index is out of range. Invalid connection '\n                         f'from landmark #{start_idx} to landmark #{end_idx}.')\n        if start_idx in plotted_landmarks and end_idx in plotted_landmarks:\n            landmark_pair = [\n                plotted_landmarks[start_idx], plotted_landmarks[end_idx]\n            ]\n        ax.plot(\n            [landmark_pair[0][0], landmark_pair[1][0]],\n            [landmark_pair[0][1], landmark_pair[1][1]],\n            color=_normalize_color(GREEN_COLOR),\n            linewidth=5)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2023-03-25T21:41:49.275316Z","iopub.execute_input":"2023-03-25T21:41:49.275732Z","iopub.status.idle":"2023-03-25T21:41:49.300160Z","shell.execute_reply.started":"2023-03-25T21:41:49.275697Z","shell.execute_reply":"2023-03-25T21:41:49.298788Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Plot the face\n![](https://learn.microsoft.com/en-us/azure/cognitive-services/computer-vision/media/landmarks.1.jpg)\n\nSource: https://learn.microsoft.com/en-us/azure/cognitive-services/computer-vision/concept-face-detection","metadata":{}},{"cell_type":"code","source":"mp_face_mesh = mp.solutions.face_mesh\nrslt = parquet_df[parquet_df['type'].isin(['face'])]\nlandmark_point = rslt[rslt['frame']==plot_frameID]\nnbX = landmark_point['x'].isnull().sum()\nprint(\"Number of NaN values: \", nbX)\nif nbX==0:\n    landmark_list = []\n    for index, row in landmark_point.iterrows():\n        landmark_list.append(Landmark(row.x,row.y*-1,row.z))\n    #     print(landmark_list)\n    #     break\n    plot_landmarks(landmark_list, mp_face_mesh.FACEMESH_CONTOURS, azimuth=5) #3D plot\n    plot_landmarks2D(landmark_list, mp_face_mesh.FACEMESH_CONTOURS) #2D plot\nelse:\n    print(\"No right hand landmark data\")\n","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:37.585362Z","iopub.execute_input":"2023-03-25T20:44:37.585708Z","iopub.status.idle":"2023-03-25T20:44:47.998995Z","shell.execute_reply.started":"2023-03-25T20:44:37.585676Z","shell.execute_reply":"2023-03-25T20:44:47.997729Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Plot the right hand\n![](https://mediapipe.dev/images/mobile/hand_landmarks.png)\n\nSource: https://google.github.io/mediapipe/solutions/hands.html","metadata":{}},{"cell_type":"code","source":"mp_hands = mp.solutions.hands\nrslt = parquet_df[parquet_df['type'].isin(['right_hand'])]\nlandmark_point = rslt[rslt['frame']==plot_frameID]\nnbX = landmark_point['x'].isnull().sum()\nprint(\"Number of NaN values: \", nbX)\nif nbX==0:\n    landmark_list = []\n    for index, row in landmark_point.iterrows():\n        landmark_list.append(Landmark(row.x,row.y*-1,row.z))\n    #     print(landmark_list)\n    #     break\n    plot_landmarks(landmark_list, mp_hands.HAND_CONNECTIONS, azimuth=5) #3D plot\n    plot_landmarks2D(landmark_list, mp_hands.HAND_CONNECTIONS) #2D plot\nelse:\n    print(\"No right hand landmark data\")","metadata":{"execution":{"iopub.status.busy":"2023-03-25T21:41:54.650022Z","iopub.execute_input":"2023-03-25T21:41:54.650468Z","iopub.status.idle":"2023-03-25T21:41:55.683989Z","shell.execute_reply.started":"2023-03-25T21:41:54.650424Z","shell.execute_reply":"2023-03-25T21:41:55.682691Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Plot the left hand","metadata":{}},{"cell_type":"code","source":"mp_hands = mp.solutions.hands\nrslt = parquet_df[parquet_df['type'].isin(['left_hand'])]\nlandmark_point = rslt[rslt['frame']==plot_frameID]\nnbX = landmark_point['x'].isnull().sum()\nprint(\"Number of NaN values: \", nbX)\nif nbX==0:\n    landmark_list = []\n    for index, row in landmark_point.iterrows():\n        landmark_list.append(Landmark(row.x,row.y,row.z))\n    #     print(landmark_list)\n    #     break\n    plot_landmarks(landmark_list, mp_hands.HAND_CONNECTIONS, azimuth=5) #3D plot\n    plot_landmarks2D(landmark_list, mp_hands.HAND_CONNECTIONS) #2D plot\nelse:\n    print(\"No left hand landmark data\")","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:49.253278Z","iopub.execute_input":"2023-03-25T20:44:49.253653Z","iopub.status.idle":"2023-03-25T20:44:49.264201Z","shell.execute_reply.started":"2023-03-25T20:44:49.253597Z","shell.execute_reply":"2023-03-25T20:44:49.263083Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Plot the pose\n![](https://mediapipe.dev/images/mobile/pose_tracking_full_body_landmarks.png)\n\nSource: https://google.github.io/mediapipe/solutions/pose.html","metadata":{}},{"cell_type":"code","source":"mp_pose = mp.solutions.pose\nrslt = parquet_df[parquet_df['type'].isin(['pose'])]\nlandmark_point = rslt[rslt['frame']==plot_frameID]\nnbX = landmark_point['x'].isnull().sum()\nprint(\"Number of NaN values: \", nbX)\nif nbX==0:\n    landmark_list = []\n    for index, row in landmark_point.iterrows():\n        landmark_list.append(Landmark(row.x,row.y*-1,row.z))\n    #     print(landmark_list)\n    #     break\n    plot_landmarks(landmark_list, mp_pose.POSE_CONNECTIONS, azimuth=5) #3D plot\n    plot_landmarks2D(landmark_list, mp_pose.POSE_CONNECTIONS) #2D plot\nelse:\n    print(\"No pose landmark data\")","metadata":{"execution":{"iopub.status.busy":"2023-03-25T20:44:49.265585Z","iopub.execute_input":"2023-03-25T20:44:49.266013Z","iopub.status.idle":"2023-03-25T20:44:50.535991Z","shell.execute_reply.started":"2023-03-25T20:44:49.265957Z","shell.execute_reply":"2023-03-25T20:44:50.534688Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Plot pose, left and right hands, and face landmarks sumiltaneously.\n","metadata":{}},{"cell_type":"code","source":"mp_holistic = mp.solutions.holistic\nrslt = parquet_df\n# print(parquet_df)\nlandmark_point = rslt[rslt['frame']==46]\n# print(landmark_point)\n\nlandmark_list = []\nfor index, row in landmark_point.iterrows():\n    landmark_list.append(Landmark(row.x,row.y*-1,row.z))\n#     print(landmark_list)\n#     break\n# plot_landmarks(landmark_list, [mp_holistic.FACEMESH_CONTOURS,mp_holistic.HAND_CONNECTIONS,mp_holistic.POSE_CONNECTIONS], azimuth=5, big=True)\nplot_landmarks2D(landmark_list, mp_holistic.POSE_CONNECTIONS, big=True)\n# TO-DO fixes connectors plotting","metadata":{"execution":{"iopub.status.busy":"2023-03-25T21:42:05.626905Z","iopub.execute_input":"2023-03-25T21:42:05.627340Z","iopub.status.idle":"2023-03-25T21:42:11.058696Z","shell.execute_reply.started":"2023-03-25T21:42:05.627302Z","shell.execute_reply":"2023-03-25T21:42:11.057493Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### We can also print the face in each frame as a 3D mesh\nIt works less for hand and pose as they are much less points to make a mesh.","metadata":{}},{"cell_type":"code","source":"import plotly.graph_objects as go\nimport numpy as np\n\nrslt = parquet_df[parquet_df['type'].isin(['face'])]\nlandmark_point = rslt[rslt['frame']==14]\nfig = go.Figure(data=[go.Mesh3d(x=landmark_point.x, y=landmark_point.y*-1, z=landmark_point.z,\n                   opacity=0.6,\n                   color='gray')])\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2023-03-25T21:42:34.975053Z","iopub.execute_input":"2023-03-25T21:42:34.975521Z","iopub.status.idle":"2023-03-25T21:42:35.265496Z","shell.execute_reply.started":"2023-03-25T21:42:34.975478Z","shell.execute_reply":"2023-03-25T21:42:35.264241Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"THANK YOU !\n\nI hope you like it.\n\nIf you have any suggestions please feel free to comment. I will keep improving.","metadata":{}}]}