Introduction
In this tutorial we will show you how to use sly.GraphNodes class to create data annotation for pose estimation / keypoints detection task. The tutorial illustrates basic upload-download scenario:
create project and dataset on server
programmatically create annotation and upload it to image
download image and annotation
ℹ️ Everything you need to reproduce this tutorial is on GitHub : source code, Visual Studio Code configuration, and a shell script for creating virtual env.
How to debug this tutorial
Step 1. Prepare ~/supervisely.env
file with credentials. Learn more here.
Step 2. Clone repository with source code and demo data and create Virtual Environment .
Copy git clone https://github.com/supervisely-ecosystem/keypoints-labeling-example
cd keypoints-labeling-example
./create_venv.sh
Step 3. Open repository directory in Visual Studio Code
Step 4. Start debugging src/main.py
Python Code
Importing Necessary Libraries
Import necessary libraries:
Copy import supervisely as sly
from supervisely.geometry.graph import Node, KeypointsTemplate
import os
import json
from dotenv import load_dotenv
Before we will start creating our project, let's learn how to create keypoints template - we are going to use it in our project.
Working With Keypoints Template
We will need an image to create and visualize our keypoints template.
Image for building keypoints template:
Create keypoints template:
Copy # initialize template
template = KeypointsTemplate()
# add nodes
template.add_point(label="nose", row=635, col=427)
template.add_point(label="left_eye", row=597, col=404)
template.add_point(label="right_eye", row=685, col=401)
template.add_point(label="left_ear", row=575, col=431)
template.add_point(label="right_ear", row=723, col=425)
template.add_point(label="left_shoulder", row=502, col=614)
template.add_point(label="right_shoulder", row=794, col=621)
template.add_point(label="left_elbow", row=456, col=867)
template.add_point(label="right_elbow", row=837, col=874)
template.add_point(label="left_wrist", row=446, col=1066)
template.add_point(label="right_wrist", row=845, col=1073)
template.add_point(label="left_hip", row=557, col=1035)
template.add_point(label="right_hip", row=743, col=1043)
template.add_point(label="left_knee", row=541, col=1406)
template.add_point(label="right_knee", row=751, col=1421)
template.add_point(label="left_ankle", row=501, col=1760)
template.add_point(label="right_ankle", row=774, col=1765)
# add edges
template.add_edge(src="left_ankle", dst="left_knee")
template.add_edge(src="left_knee", dst="left_hip")
template.add_edge(src="right_ankle", dst="right_knee")
template.add_edge(src="right_knee", dst="right_hip")
template.add_edge(src="left_hip", dst="right_hip")
template.add_edge(src="left_shoulder", dst="left_hip")
template.add_edge(src="right_shoulder", dst="right_hip")
template.add_edge(src="left_shoulder", dst="right_shoulder")
template.add_edge(src="left_shoulder", dst="left_elbow")
template.add_edge(src="right_shoulder", dst="right_elbow")
template.add_edge(src="left_elbow", dst="left_wrist")
template.add_edge(src="right_elbow", dst="right_wrist")
template.add_edge(src="left_eye", dst="right_eye")
template.add_edge(src="nose", dst="left_eye")
template.add_edge(src="nose", dst="right_eye")
template.add_edge(src="left_eye", dst="left_ear")
template.add_edge(src="right_eye", dst="right_ear")
template.add_edge(src="left_ear", dst="left_shoulder")
template.add_edge(src="right_ear", dst="right_shoulder")
Visualize your keypoints template:
Copy template_img = sly.image.read("images/girl.jpg")
template.draw(image=template_img, thickness=7)
sly.image.write("images/template.jpg", template_img)
Explore Keypoints Template in JSON Format
You can also transfer your template to json:
Copy template_json = template.to_json()
Click to see the example of template in json format
Copy {
"nodes": {
"nose": {
"label": "nose",
"loc": [635, 427],
"color": "#0000FF"
},
"left_eye": {
"label": "left_eye",
"loc": [597, 404],
"color": "#0000FF"
},
"right_eye": {
"label": "right_eye",
"loc": [685, 401],
"color": "#0000FF"
},
"left_ear": {
"label": "left_ear",
"loc": [575, 431],
"color": "#0000FF"
},
"right_ear": {
"label": "right_ear",
"loc": [723, 425],
"color": "#0000FF"
},
"left_shoulder": {
"label": "left_shoulder",
"loc": [502, 614],
"color": "#0000FF"
},
"right_shoulder": {
"label": "right_shoulder",
"loc": [794, 621],
"color": "#0000FF"
},
"left_elbow": {
"label": "left_elbow",
"loc": [456, 867],
"color": "#0000FF"
},
"right_elbow": {
"label": "right_elbow",
"loc": [837, 874],
"color": "#0000FF"
},
"left_wrist": {
"label": "left_wrist",
"loc": [446, 1066],
"color": "#0000FF"
},
"right_wrist": {
"label": "right_wrist",
"loc": [845, 1073],
"color": "#0000FF"
},
"left_hip": {
"label": "left_hip",
"loc": [557, 1035],
"color": "#0000FF"
},
"right_hip": {
"label": "right_hip",
"loc": [743, 1043],
"color": "#0000FF"
},
"left_knee": {
"label": "left_knee",
"loc": [541, 1406],
"color": "#0000FF"
},
"right_knee": {
"label": "right_knee",
"loc": [751, 1421],
"color": "#0000FF"
},
"left_ankle": {
"label": "left_ankle",
"loc": [501, 1760],
"color": "#0000FF"
},
"right_ankle": {
"label": "right_ankle",
"loc": [774, 1765],
"color": "#0000FF"
}
},
"edges": [
{
"src": "left_ankle",
"dst": "left_knee",
"color": "#00FF00"
},
{
"src": "left_knee",
"dst": "left_hip",
"color": "#00FF00"
},
{
"src": "right_ankle",
"dst": "right_knee",
"color": "#00FF00"
},
{
"src": "right_knee",
"dst": "right_hip",
"color": "#00FF00"
},
{
"src": "left_hip",
"dst": "right_hip",
"color": "#00FF00"
},
{
"src": "left_shoulder",
"dst": "left_hip",
"color": "#00FF00"
},
{
"src": "right_shoulder",
"dst": "right_hip",
"color": "#00FF00"
},
{
"src": "left_shoulder",
"dst": "right_shoulder",
"color": "#00FF00"
},
{
"src": "left_shoulder",
"dst": "left_elbow",
"color": "#00FF00"
},
{
"src": "right_shoulder",
"dst": "right_elbow",
"color": "#00FF00"
},
{
"src": "left_elbow",
"dst": "left_wrist",
"color": "#00FF00"
},
{
"src": "right_elbow",
"dst": "right_wrist",
"color": "#00FF00"
},
{
"src": "left_eye",
"dst": "right_eye",
"color": "#00FF00"
},
{
"src": "nose",
"dst": "left_eye",
"color": "#00FF00"
},
{
"src": "nose",
"dst": "right_eye",
"color": "#00FF00"
},
{
"src": "left_eye",
"dst": "left_ear",
"color": "#00FF00"
},
{
"src": "right_eye",
"dst": "right_ear",
"color": "#00FF00"
},
{
"src": "left_ear",
"dst": "left_shoulder",
"color": "#00FF00"
},
{
"src": "right_ear",
"dst": "right_shoulder",
"color": "#00FF00"
}
]
}
Now, when we have successfully created keypoints template, we can start creating keypoints annotation for our project.
Programmatically Create Keypoints Annotation
Authenticate (learn more here ):
Copy load_dotenv(os.path.expanduser('~/supervisely.env'))
api = sly.Api.from_env()
my_teams = api.team.get_list()
team = my_teams[0]
workspace = api.workspace.get_list(team.id)[0]
Input image:
Create project and dataset:
Copy project = api.project.create(workspace.id, "Human Pose Estimation", change_name_if_conflict=True)
dataset = api.dataset.create(project.id, "Person with dog", change_name_if_conflict=True)
print(f"Project {project.id} with dataset {dataset.id} are created")
Now let's create annotation class using our keypoints template as a geometry config (unlike other supervisely geometry classes, sly.GraphNodes requires geometry config to be passed - it is necessary for object class initialization):
Copy person = sly.ObjClass("person", geometry_type=sly.GraphNodes, geometry_config=template)
project_meta = sly.ProjectMeta(obj_classes=[person])
api.project.update_meta(project.id, project_meta.to_json())
You can also go to Supervisely platform and check that class with shape "Keypoints" was successfully added to your project:
Upload image:
Copy image_info = api.image.upload_path(
dataset.id, name="person_with_dog.jpg", path="images/person_with_dog.jpg"
)
Build keypoints graph:
Copy nodes = [
sly.Node(label="nose", row=146, col=670),
sly.Node(label="left_eye", row=130, col=644),
sly.Node(label="right_eye", row=135, col=701),
sly.Node(label="left_ear", row=137, col=642),
sly.Node(label="right_ear", row=142, col=705),
sly.Node(label="left_shoulder", row=221, col=595),
sly.Node(label="right_shoulder", row=226, col=738),
sly.Node(label="left_elbow", row=335, col=564),
sly.Node(label="right_elbow", row=342, col=765),
sly.Node(label="left_wrist", row=429, col=555),
sly.Node(label="right_wrist", row=438, col=784),
sly.Node(label="left_hip", row=448, col=620),
sly.Node(label="right_hip", row=451, col=713),
sly.Node(label="left_knee", row=598, col=591),
sly.Node(label="right_knee", row=602, col=715),
sly.Node(label="left_ankle", row=761, col=573),
sly.Node(label="right_ankle", row=766, col=709),
]
Label the image:
Copy input_image = sly.image.read("images/person_with_dog.jpg")
img_height, img_width = input_image.shape[:2]
label = sly.Label(sly.GraphNodes(nodes), person)
ann = sly.Annotation(img_size=[img_height, img_width], labels=[label])
api.annotation.upload_ann(image_info.id, ann)
You can check that keypoints annotation was successfully created in Annotation Tool:
Download data:
Copy image = api.image.download_np(image_info.id)
ann_json = api.annotation.download_json(image_info.id)
Draw annotation:
Copy ann = sly.Annotation.from_json(ann_json, project_meta)
output_path = "images/person_with_dog_labelled.jpg"
ann.draw_pretty(image, output_path=output_path, thickness=7)
Result: