!pip uninstall opencv_python_headless
!pip install opencv-python-headless==4.5.4.60
!pip install fiftyone
import fiftyone as fo
Golf Swing Part III- Using pre-trained models- FiftyOne and Streamlit App
Using Python and pre-trained to identify parts of the body and club during a golf swing. Uses both FiftyOne and creates an app using Streamlit
Overview
In a previous part Part 1 a neural network model was used to find positions on the body during a golf swing. The model was not particularly succesful, perhaps due to the lack of data (specific to the golf swing) that was used to train the model on.
This problem can be got around by using a model that has been pre-trained on human gestures. Several pre-trained models can be found here Pre-trained models. I tried a few and found the chose the model keypoint-rcnn-resnet50-fpn-coco-torch
worked well with this data. Link to model and Paper of model.
The input to the model is taken from Part 2 which separated a golf video into a series of images of the swing.
In this page I use the model with both fiftyOne and as a streamlit app.
youtube: https://youtu.be/Q0BB0huWb6s https://youtu.be/Q0BB0huWb6s
Code
The code can be run on google colab here COCO50_1 (works best on google chrome)
Installs and imports
Upoad some images to the workspace
Untar and create a dataset object from them
And look at them
The image files can be found here GC2.tgz
import tarfile
= tarfile.open('/content/GC2.tgz')
my_tar '/content/my_folder') # specify which folder to extract to
my_tar.extractall( my_tar.close()
import fiftyone as fo
import fiftyone.zoo as foz
import fiftyone as fo
= "my_folder"
name = "/content"
dataset_dir
# Create the dataset
= fo.Dataset.from_dir(
dataset =dataset_dir,
dataset_dir=fo.types.ImageDirectory,
dataset_type=name,
name
)
= fo.launch_app(dataset) session
This screen is interative and allows us to look at the images
Load the trained model
Apply the model to the dataset
View the results
= foz.load_zoo_model("keypoint-rcnn-resnet50-fpn-coco-torch")
model
# label_types=["classification", "classifications", "detections", "instances", "segmentations", "keypoints", "polylines", "polygons", "scalar"],
="predictions",label_types='predictions_keypoints')
dataset.apply_model(model, label_field
= fo.launch_app(dataset) session
youtube: https://youtu.be/dkxtOBWD7Vw
Extract data from the model
We might want to use the data from the model outside of fiftyOne.
In the following I extract the data so that it can be plotted.
def plotPredOne(i):
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
= mpimg.imread(i['filepath'])
img
#need to take account of more than one person in image
= np.array(i['predictions_keypoints']['keypoints'][0]['points'])
points1 = np.shape(img)[0]
adjPts = np.array(i['predictions_detections']['detections'][0]['bounding_box'])
box1 =box1*adjPts
box1# Bboxes are in [top-left-x, top-left-y, width, height] format
=np.array([
box20], box1[1]],
[box1[0] +box1[2] ,box1[1] ],
[box1[0] +box1[2] ,box1[1] +box1[3]] ,
[box1[0] ,box1[1] +box1[3]],
[box1[0], box1[1]]
[box1[
])
plt.figure()
plt.imshow(img)
0]*adjPts,points1[:,1]*adjPts, '+k',markersize=10,linewidth=3)
plt.plot(points1[:,0],box2[:,1], '--og',markersize=10,linewidth=3)
plt.plot(box2[:,
#back of body
=[4,6,12,14,16]
v0]*adjPts,points1[v,1]*adjPts, '-k<',markersize=10,linewidth=2)
plt.plot(points1[v,
#front of body
=[0,5,11,13,15]
v0]*adjPts,points1[v,1]*adjPts, '-w>',markersize=10,linewidth=2)
plt.plot(points1[v,
= np.array([[ 5,6],#shoulders also 4?
vects 11,12], #hips
[13,14], #knees
[15,16],#heels
[7,8],#elbows
[9,10],#hands
[
]) ='gcyrmb'
makfor iv,v in enumerate(vects):
0]*adjPts,points1[v,1]*adjPts, '-'+mak[iv],markersize=10,linewidth=3) plt.plot(points1[v,
for iii,i in enumerate(dataset):
plotPredOne(i)
Convert into a Streamlit App
To convert to a streamlit app I will use the PyTorch module rather than the fiftyOne.
I will also keep it simple by loading only 3 images- start of swing, top of backswing and at impact- and modeling these at the start of the load part of the app.
The app will then just plot the images as shown above.
Imports and give the app a title
import streamlit as st
import torch
import torchvision
from torchvision import transforms
from PIL import Image
import tarfile
import os
from torchvision.io import read_image
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
'Golf Swing') st.title(
Loading the data & applying the model
Create a function to load data and model the data
load_data(choi)
Images are loaded from Gc2.tgz
my_tar = tarfile.open(cda2+'/GC2.tgz')
The particular model to use is loaded
model = torchvision.models.detection.keypointrcnn_resnet50_fpn(pretrained=True)
model.eval()
Images are loaded and converted to a tensor
number_img = Image.open(cda2+'/images/'+image_filename)
convert_tensor = transforms.ToTensor()
And predictions are made
predictions=model(imgTens)
In the main body the function is called
data_load_state = st.text('Loading data...')
predictions,imgLocAll,cda2=load_data(1)
data_load_state.text("Loaded data (using st.cache)")
# So only have to do this when app launches
@st.cache()
# the function 'choi' is the video file to use
def load_data(choi):
# the images are in the GC2.tgz file- this needs to be untarred first
= os.getcwd()
cda =cda
cda2= tarfile.open(cda2+'/GC2.tgz')
my_tar # specify which folder to extract to
my_tar.extractall(cda2)
my_tar.close()
# Create a variable of the image names and which video they are part of
=[]
imgAll=[]
vidAll=0
i=' '
last1for xx in os.listdir(cda2+'/images/'):
if xx[-1]=='g':
= np.append(imgAll, xx)
imgAll if xx.split('_')[1]!=last1:
=i+1
i=np.append(vidAll,i)
vidAll=xx.split('_')[1]
last1
=np.unique(vidAll)
vidAllUnq
# Load the model to be used
= torchvision.models.detection.keypointrcnn_resnet50_fpn(pretrained=True)
model eval()
model.
# Select the images to be used
= imgAll[vidAll==vidAllUnq[choi]]
imgs
# make sure in correct order
=[int(xx.split('_')[-1].split('e')[1].split('.')[0]) for xx in imgs]
aa=sorted(range(len(aa)), key=lambda k: aa[k])
ind=imgs[ind]
imgs
# create tensor of images to be used- here 3 (images) X width X height
=[]
imgTens=[]
imgLocAll
# Just use the start, top and impact of swing
=[0,3,5]
iiUsefor ii,image_filename in enumerate(imgs):
# print(cda2+'images/'+image_filename)
if ii in iiUse:
= Image.open(cda2+'/images/'+image_filename)
number_img = transforms.ToTensor()
convert_tensor =convert_tensor(number_img)
number_img
imgTens.append(number_img)
imgLocAll.append(image_filename)
# Make the predictions
=model(imgTens)
predictions
return predictions,imgLocAll,cda2
# Outside the function, the load function is called
= st.text('Loading data...')
data_load_state =load_data(1)
predictions,imgLocAll,cda2"Loaded data (using st.cache)") data_load_state.text(
Streamlit user interface
User selects the images from this box:
choice=imgLocAll
imgSEL = st.sidebar.selectbox( 'Select how to search', choice)
Display to user what swing it is:
SwingPos=['Start','Back','Through']
SwingPos[numSEL]
And at the end of the file the figure is displayed in streamlit with the following command:
st.pyplot(fig)
The plot part
Extract the data from the model about different parts of the body:
points1=np.array([x.detach().numpy()[0:2] for x in predictions[numSEL]['keypoints'][0]])
The plot lines plot different parts of the body, the following plot the back of the body
v=[4,6,12,14,16]
plt.plot(points1[v,0]*adjPts,points1[v,1]*adjPts, '-w<',markersize=10,linewidth=2)
# load the images so can be plotted
= mpimg.imread(cda2+'/images/'+imgSEL)
img
# the image selected
=[oo for oo,x in enumerate(choice) if x==imgSEL][0]
numSEL
# get data from model as a numpy array - here want keypoints other info is also available
=np.array([x.detach().numpy()[0:2] for x in predictions[numSEL]['keypoints'][0]])
points1
# create a plot
=plt.figure(figsize=(7,7))
fig
plt.imshow(img)
# Plot across back and front of body
=1
adjPts#back of body
=[4,6,12,14,16]
v0]*adjPts,points1[v,1]*adjPts, '-w<',markersize=10,linewidth=2)
plt.plot(points1[v,
#front of body
=[0,5,11,13,15]
v0]*adjPts,points1[v,1]*adjPts, '-k>',markersize=10,linewidth=2)
plt.plot(points1[v,
# Plot over lines on body
= np.array([[ 5,6],#shoulders also 4?
vects 11,12], #hips
[13,14], #knees
[15,16],#heels
[7,8],#elbows
[9,10],#hands
[
]) ='gcyrmb'
makfor iv,v in enumerate(vects):
0]*adjPts,points1[v,1]*adjPts, '-'+mak[iv],markersize=10,linewidth=3)
plt.plot(points1[v,
=['Back','Front','Shoulders','Hips','Knees','Heels','Elbows','Hands']
LEG
plt.legend(LEG)for x in points1:
0],x[1],'+b') plt.plot(x[
Requirements.txt
Finally streamlit needs a requirements text in the GitHub repository
torch
torchvision
Pillow
matplotlib numpy
The Streamlit App
youtube: https://youtu.be/Q0BB0huWb6s