nishantkaushik20 commited on
Commit
bc3ec38
·
1 Parent(s): c732d93

Upload 15 files

Browse files
.gitignore ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/#use-with-ide
110
+ .pdm.toml
111
+
112
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
+ __pypackages__/
114
+
115
+ # Celery stuff
116
+ celerybeat-schedule
117
+ celerybeat.pid
118
+
119
+ # SageMath parsed files
120
+ *.sage.py
121
+
122
+ # Environments
123
+ .env
124
+ .venv
125
+ env/
126
+ venv/
127
+ ENV/
128
+ env.bak/
129
+ venv.bak/
130
+
131
+ # Spyder project settings
132
+ .spyderproject
133
+ .spyproject
134
+
135
+ # Rope project settings
136
+ .ropeproject
137
+
138
+ # mkdocs documentation
139
+ /site
140
+
141
+ # mypy
142
+ .mypy_cache/
143
+ .dmypy.json
144
+ dmypy.json
145
+
146
+ # Pyre type checker
147
+ .pyre/
148
+
149
+ # pytype static type analyzer
150
+ .pytype/
151
+
152
+ # Cython debug symbols
153
+ cython_debug/
154
+
155
+ # PyCharm
156
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
159
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
+ #.idea/
161
+
162
+ *.jpg
163
+ *.pt
164
+ huggingface
README.md CHANGED
@@ -1,10 +1,10 @@
1
  ---
2
- title: PoseEstimationYOLOv8
3
- emoji: 🚀
4
- colorFrom: blue
5
- colorTo: gray
6
  sdk: streamlit
7
- sdk_version: 1.27.0
8
  app_file: app.py
9
  pinned: false
10
  ---
 
1
  ---
2
+ title: YoloV8 Pose Keypoint Classification
3
+ emoji: 👀
4
+ colorFrom: purple
5
+ colorTo: purple
6
  sdk: streamlit
7
+ sdk_version: 1.21.0
8
  app_file: app.py
9
  pinned: false
10
  ---
Readme copy.md ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### Introduction
2
+
3
+ Pose estimation is a task that involves identifying the location of specific points in an image, usually referred to as keypoints. The keypoints can represent various parts of the object such as joints, landmarks, or other distinctive features. The locations of the keypoints are usually represented as a set of 2D** **`[x, y]` **or 3D*`[x, y, visible]` coordinates.
4
+
5
+ The output of a pose estimation model is a set of points that represent the keypoints on an object in the image, usually along with the confidence scores for each point. Pose estimation is a good choice when you need to identify specific parts of an object in a scene, and their location in relation to each other.
6
+
7
+ ### YOLOV8 Pose
8
+
9
+ How to use YOLOv8 pretrained Pose models?
10
+
11
+ ```python
12
+ from ultralytics import YOLO
13
+
14
+ # Load a model
15
+ model = YOLO('yolov8n-pose.pt')
16
+
17
+ # Predict with the model
18
+ results = model('https://ultralytics.com/images/bus.jpg')
19
+
20
+ # Extract keypoint
21
+ result_keypoint = results.keypoints.xyn.cpu().numpy()[0]
22
+ ```
23
+
24
+
25
+ ### Exploring Ouput Keypoint![](https://cdn-images-1.medium.com/max/800/1*PM5Q-58eNOWdoLogVKCGnQ.png)
26
+
27
+ source : https://learnopencv.com/wp-content/uploads/2021/05/fix-overlay-issue.jpg
28
+
29
+ In the output of YOLOv8 pose estimation, there are no keypoint names. Here’s sample output
30
+
31
+ ![](https://cdn-images-1.medium.com/max/800/1*Om_wkVg8tv0ou1BN1tQl_Q.png)
32
+
33
+ To obtain the x, y coordinates by calling the keypoint name, you can create a Pydantic class with a “keypoint” attribute where the keys represent the keypoint names, and the values indicate the index of the keypoint in the YOLOv8 output.
34
+
35
+ ```python
36
+ from pydantic import BaseModel
37
+
38
+ class GetKeypoint(BaseModel):
39
+ NOSE: int = 0
40
+ LEFT_EYE: int = 1
41
+ RIGHT_EYE: int = 2
42
+ LEFT_EAR: int = 3
43
+ RIGHT_EAR: int = 4
44
+ LEFT_SHOULDER: int = 5
45
+ RIGHT_SHOULDER: int = 6
46
+ LEFT_ELBOW: int = 7
47
+ RIGHT_ELBOW: int = 8
48
+ LEFT_WRIST: int = 9
49
+ RIGHT_WRIST: int = 10
50
+ LEFT_HIP: int = 11
51
+ RIGHT_HIP: int = 12
52
+ LEFT_KNEE: int = 13
53
+ RIGHT_KNEE: int = 14
54
+ LEFT_ANKLE: int = 15
55
+ RIGHT_ANKLE: int = 16
56
+
57
+ # example
58
+ get_keypoint = GetKeypoint()
59
+ nose_x, nose_y = result_yolov8[get_keypoint.NOSE]
60
+ left_eye_x, left_eye_y = keypoint[get_keypoint.LEFT_EYE]
61
+ ```
62
+
63
+
64
+ ### Generate Dataset Keypoint
65
+
66
+ To classify keypoints, you need to create a keypoint dataset. If you are using images from a public dataset on Kaggle [yoga-pose-classification](https://www.kaggle.com/datasets/ujjwalchowdhury/yoga-pose-classification). This dataset have 5 classes Downdog, Goddess, Plank, Tree, Warrior2. I will run pose estimation YoloV8 on each image and extract the output. I extracted the keypoints for each body part to obtain the x, y coordinates, and then I saved them in CSV format.
67
+
68
+ ![](https://cdn-images-1.medium.com/max/800/1*SBXgggGPWHPnoVCz9pLV6Q.png)
69
+ column of dataset
70
+
71
+ ![](https://cdn-images-1.medium.com/max/800/1*KMwo_Htgmmi0DAJFLRZY3g.png)
72
+ sample dataset
73
+
74
+
75
+ ### Train Classification
76
+
77
+ Let’s proceed with training a multi-class classification model for keypoints using the PyTorch library for neural networks.
78
+
79
+ ```python
80
+ class NeuralNet(nn.Module):
81
+ def __init__(self, input_size, hidden_size, num_classes):
82
+ super(NeuralNet, self).__init__()
83
+ self.l1 = nn.Linear(input_size, hidden_size)
84
+ self.relu = nn.ReLU()
85
+ self.l2 = nn.Linear(hidden_size, num_classes)
86
+
87
+ def forward(self, x):
88
+ out = self.l1(x)
89
+ out = self.relu(out)
90
+ out = self.l2(out)
91
+ return out
92
+
93
+ hidden_size = 256
94
+ model = NeuralNet(X_train.shape[1], hidden_size, len(class_weights))
95
+ ```
96
+
97
+
98
+ The neural network architecture consists of two linear layers and a ReLU activation function:
99
+
100
+ * `self.l1 = nn.Linear(input_size, hidden_size)`: The first linear layer, which takes the input features and maps them to the hidden layer.
101
+ * `self.relu = nn.ReLU()`: The activation function, which applies element-wise rectified linear unit (ReLU) activation to introduce non-linearity.
102
+ * `self.l2 = nn.Linear(hidden_size, num_classes)`: The second linear layer, which maps the hidden layer to the output classes.
103
+
104
+ `forward(self, x)` This method defines the forward pass of the neural network. It takes an input tensor** **`x` and returns the output tensor. The forward pass involves passing the input through the defined layers in sequence and returning the final output.
105
+
106
+ ```python
107
+ learning_rate = 0.01
108
+ criterion = nn.CrossEntropyLoss(weight=torch.from_numpy(class_weights.astype(np.float32)))
109
+ optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
110
+ ```
111
+
112
+
113
+ In this code,`learning_rate` is set to 0.01, which controls the step size during optimization. The `CrossEntropyLoss`criterion is used for multi-class classification, and the `weight` parameter is set to the class weights converted to a PyTorch tensor. This allows for handling class imbalance if present in the dataset.
114
+
115
+ The optimizer is defined as the Adam optimizer, which is a popular optimization algorithm for neural networks. It takes** `model.parameters()` as the input, which specifies the parameters of the model to be optimized. The **`lr` parameter sets the learning rate for the optimizer.
116
+
117
+
118
+ ### Training Keypoint Result
119
+
120
+ The results are quite good for a simple Neural Network and the given dataset size, with an accuracy above 90%.
121
+
122
+ ![](https://cdn-images-1.medium.com/max/800/1*2cgx6lQExwpRBL8FuWknoQ.png)
app.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Import library
2
+ import cv2
3
+ import glob
4
+ import numpy as np
5
+ from PIL import Image
6
+ import streamlit as st
7
+
8
+ from src.detection_keypoint import DetectKeypoint
9
+ from src.classification_keypoint import KeypointClassification
10
+
11
+ detection_keypoint = DetectKeypoint()
12
+ classification_keypoint = KeypointClassification(
13
+ './models/pose_classification.pth'
14
+ )
15
+
16
+ def pose_classification(img, col=None):
17
+ image = Image.open(img)
18
+ image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
19
+
20
+ image_rgb = cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB)
21
+ # show image col 1
22
+ col1.write("Original Image :")
23
+ col1.image(image_rgb)
24
+
25
+ # detection keypoint
26
+ results = detection_keypoint(image_cv)
27
+ results_keypoint = detection_keypoint.get_xy_keypoint(results)
28
+
29
+ # classification keypoint
30
+ input_classification = results_keypoint[10:]
31
+ results_classification = classification_keypoint(input_classification)
32
+
33
+ # visualize result
34
+ image_draw = results.plot(boxes=False)
35
+ x_min, y_min, x_max, y_max = results.boxes.xyxy[0].numpy()
36
+ image_draw = cv2.rectangle(
37
+ image_draw,
38
+ (int(x_min), int(y_min)),(int(x_max), int(y_max)),
39
+ (0,0,255), 2
40
+ )
41
+ (w, h), _ = cv2.getTextSize(
42
+ results_classification.upper(),
43
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2
44
+ )
45
+ image_draw = cv2.rectangle(
46
+ image_draw,
47
+ (int(x_min), int(y_min)-20),(int(x_min)+w, int(y_min)),
48
+ (0,0,255), -1
49
+ )
50
+ image_draw = cv2.putText(image_draw,
51
+ f'{results_classification.upper()}',
52
+ (int(x_min), int(y_min)-4),
53
+ cv2.FONT_HERSHEY_SIMPLEX,
54
+ 0.5, (255, 255, 255),
55
+ thickness=2
56
+ )
57
+ image_draw = cv2.cvtColor(image_draw, cv2.COLOR_BGR2RGB)
58
+ col2.write("Keypoint Result :wrench:")
59
+ col2.image(image_draw)
60
+ col2.text(f'Pose Classification : {results_classification}')
61
+ return image_draw, results_classification
62
+
63
+ st.set_page_config(
64
+ layout="wide",
65
+ page_title="YoloV8 Keypoint Classification"
66
+ )
67
+ st.write(
68
+ "## YoloV8 Keypoint Yoga Pose Classification"
69
+ )
70
+ st.write(
71
+ ":dog: Try uploading an image to Classification Yoga Basic Pose like a Downdog, Goddess, Plank, Tree, Warrior2 :grin:"
72
+ )
73
+ st.sidebar.write(
74
+ "## Upload Image :gear:"
75
+ )
76
+
77
+ col1, col2 = st.columns(2)
78
+ img_upload = st.sidebar.file_uploader("Upload an image", type=["png", "jpg", "jpeg"])
79
+
80
+ if img_upload is not None:
81
+ pose_classification(img=img_upload)
82
+
83
+ # show sample image
84
+ st.write('## Sample Image')
85
+ images = glob.glob('./images/*.jpeg')
86
+ row_size = len(images)
87
+ grid = st.columns(row_size)
88
+ col = 0
89
+ for image in images:
90
+ with grid[col]:
91
+ st.image(f'{image}')
92
+ st.button(label='RUN', key=f'run_{image}',
93
+ on_click=pose_classification, args=(image, 'run'))
94
+ col = (col + 1) % row_size
95
+
images/1.jpeg ADDED
images/2.jpeg ADDED
images/3.jpeg ADDED
images/4.jpeg ADDED
images/5.jpeg ADDED
models/pose_classification.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c8c94aa11d6fb05e0b351fa8cb3f705ee7dc4c8ba6cc2f0c87df5a97ea55e2ea
3
+ size 32159
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ opencv-python
2
+ torch
3
+ streamlit
4
+ pydantic
5
+ ultralytics
6
+ numpy
src/__init__.py ADDED
File without changes
src/classification_keypoint.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+
4
+ class NeuralNet(nn.Module):
5
+ def __init__(
6
+ self,
7
+ input_size = 24,
8
+ hidden_size = 256,
9
+ num_classes = 5
10
+ ):
11
+ super(NeuralNet, self).__init__()
12
+ self.l1 = nn.Linear(input_size, hidden_size)
13
+ self.relu = nn.ReLU()
14
+ self.l2 = nn.Linear(hidden_size, num_classes)
15
+
16
+ def forward(self, x):
17
+ out = self.l1(x)
18
+ out = self.relu(out)
19
+ out = self.l2(out)
20
+ return out
21
+
22
+ class KeypointClassification:
23
+ def __init__(self, path_model):
24
+ self.path_model = path_model
25
+ self.classes = ['Downdog', 'Goddess', 'Plank', 'Tree', 'Warrior2']
26
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
27
+ self.load_model()
28
+
29
+ def load_model(self):
30
+ self.model = NeuralNet()
31
+ self.model.load_state_dict(
32
+ torch.load(self.path_model, map_location=self.device)
33
+ )
34
+ def __call__(self, input_keypoint):
35
+ if not type(input_keypoint) == torch.Tensor:
36
+ input_keypoint = torch.tensor(
37
+ input_keypoint, dtype=torch.float32
38
+ )
39
+ out = self.model(input_keypoint)
40
+ _, predict = torch.max(out, -1)
41
+ label_predict = self.classes[predict]
42
+ return label_predict
43
+
44
+ if __name__ == '__main__':
45
+ keypoint_classification = KeypointClassification(
46
+ path_model='/Users/alimustofa/Me/source-code/AI/YoloV8_Pose_Classification/models/pose_classification.pt'
47
+ )
48
+ dummy_input = torch.randn(23)
49
+ classification = keypoint_classification(dummy_input)
50
+ print(classification)
51
+
52
+
53
+
54
+
src/detection_keypoint.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import cv2
3
+ import numpy as np
4
+ from pydantic import BaseModel
5
+
6
+ import ultralytics
7
+ from ultralytics.yolo.engine.results import Results
8
+
9
+ # Define keypoint
10
+ class GetKeypoint(BaseModel):
11
+ NOSE: int = 0
12
+ LEFT_EYE: int = 1
13
+ RIGHT_EYE: int = 2
14
+ LEFT_EAR: int = 3
15
+ RIGHT_EAR: int = 4
16
+ LEFT_SHOULDER: int = 5
17
+ RIGHT_SHOULDER: int = 6
18
+ LEFT_ELBOW: int = 7
19
+ RIGHT_ELBOW: int = 8
20
+ LEFT_WRIST: int = 9
21
+ RIGHT_WRIST: int = 10
22
+ LEFT_HIP: int = 11
23
+ RIGHT_HIP: int = 12
24
+ LEFT_KNEE: int = 13
25
+ RIGHT_KNEE: int = 14
26
+ LEFT_ANKLE: int = 15
27
+ RIGHT_ANKLE: int = 16
28
+
29
+ class DetectKeypoint:
30
+ def __init__(self, yolov8_model='yolov8m-pose'):
31
+ self.yolov8_model = yolov8_model
32
+ self.get_keypoint = GetKeypoint()
33
+ self.__load_model()
34
+
35
+ def __load_model(self):
36
+ if not self.yolov8_model.split('-')[-1] == 'pose':
37
+ sys.exit('Model not yolov8 pose')
38
+ self.model = ultralytics.YOLO(model=self.yolov8_model)
39
+
40
+ # extract function keypoint
41
+ def extract_keypoint(self, keypoint: np.ndarray) -> list:
42
+ # nose
43
+ nose_x, nose_y = keypoint[self.get_keypoint.NOSE]
44
+ # eye
45
+ left_eye_x, left_eye_y = keypoint[self.get_keypoint.LEFT_EYE]
46
+ right_eye_x, right_eye_y = keypoint[self.get_keypoint.RIGHT_EYE]
47
+ # ear
48
+ left_ear_x, left_ear_y = keypoint[self.get_keypoint.LEFT_EAR]
49
+ right_ear_x, right_ear_y = keypoint[self.get_keypoint.RIGHT_EAR]
50
+ # shoulder
51
+ left_shoulder_x, left_shoulder_y = keypoint[self.get_keypoint.LEFT_SHOULDER]
52
+ right_shoulder_x, right_shoulder_y = keypoint[self.get_keypoint.RIGHT_SHOULDER]
53
+ # elbow
54
+ left_elbow_x, left_elbow_y = keypoint[self.get_keypoint.LEFT_ELBOW]
55
+ right_elbow_x, right_elbow_y = keypoint[self.get_keypoint.RIGHT_ELBOW]
56
+ # wrist
57
+ left_wrist_x, left_wrist_y = keypoint[self.get_keypoint.LEFT_WRIST]
58
+ right_wrist_x, right_wrist_y = keypoint[self.get_keypoint.RIGHT_WRIST]
59
+ # hip
60
+ left_hip_x, left_hip_y = keypoint[self.get_keypoint.LEFT_HIP]
61
+ right_hip_x, right_hip_y = keypoint[self.get_keypoint.RIGHT_HIP]
62
+ # knee
63
+ left_knee_x, left_knee_y = keypoint[self.get_keypoint.LEFT_KNEE]
64
+ right_knee_x, right_knee_y = keypoint[self.get_keypoint.RIGHT_KNEE]
65
+ # ankle
66
+ left_ankle_x, left_ankle_y = keypoint[self.get_keypoint.LEFT_ANKLE]
67
+ right_ankle_x, right_ankle_y = keypoint[self.get_keypoint.RIGHT_ANKLE]
68
+
69
+ return [
70
+ nose_x, nose_y, left_eye_x, left_eye_y, right_eye_x, right_eye_y,
71
+ left_ear_x, left_ear_y, right_ear_x, right_ear_y, left_shoulder_x, left_shoulder_y,
72
+ right_shoulder_x, right_shoulder_y, left_elbow_x, left_elbow_y, right_elbow_x, right_elbow_y,
73
+ left_wrist_x, left_wrist_y, right_wrist_x, right_wrist_y, left_hip_x, left_hip_y,
74
+ right_hip_x, right_hip_y, left_knee_x, left_knee_y, right_knee_x, right_knee_y,
75
+ left_ankle_x, left_ankle_y,right_ankle_x, right_ankle_y
76
+ ]
77
+
78
+ def get_xy_keypoint(self, results: Results) -> list:
79
+ result_keypoint = results.keypoints.xyn.cpu().numpy()[0]
80
+ keypoint_data = self.extract_keypoint(result_keypoint)
81
+ return keypoint_data
82
+
83
+ def __call__(self, image: np.array) -> Results:
84
+ results = self.model.predict(image, save=False)[0]
85
+ return results
86
+