Skip to content

Commit 91f2e9c

Browse files
authored
Keypoints (#122)
* Keypoints visual circles * Keypoints paperjs object * Add keypoints to models * Created Keypoints object * Keypoint visualization logic * Fixed saving keypoint logic * Create Keypoints panel * Setting keypoint edges * Fixed model * Export keypoints and bug fixes * Simplified clamp function * Fixed js lint * Keypoint delete using delete key * Keypoints shortcut * Fixed keypoint line error
1 parent 9cfb516 commit 91f2e9c

File tree

20 files changed

+1087
-59
lines changed

20 files changed

+1087
-59
lines changed

app/api/annotator.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ def post(self):
4646
if db_category is None:
4747
continue
4848

49-
db_category.update(set__color=category.get('color'))
49+
db_category.update(
50+
set__color=category.get('color'),
51+
set__keypoint_edges=category.get('keypoint_edges', []),
52+
set__keypoint_labels=category.get('keypoint_labels', [])
53+
)
5054

5155
# Iterate every annotation from the data annotations
5256
for annotation in category.get('annotations', []):
@@ -63,6 +67,7 @@ def post(self):
6367

6468
# Update annotation in database
6569
db_annotation.update(
70+
set__keypoints=annotation.get('keypoints', []),
6671
set__metadata=annotation.get('metadata'),
6772
set__color=annotation.get('color')
6873
)

app/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ class AnnotationModel(db.DynamicDocument):
221221
height = db.IntField()
222222

223223
color = db.StringField()
224+
225+
keypoints = db.ListField(default=[])
224226

225227
metadata = db.DictField(default={})
226228
paper_object = db.ListField(default=[])
@@ -311,6 +313,10 @@ class CategoryModel(db.DynamicDocument):
311313
deleted = db.BooleanField(default=False)
312314
deleted_date = db.DateTimeField()
313315

316+
keypoint_edges = db.ListField(default=[])
317+
keypoint_labels = db.ListField(default=[])
318+
319+
314320
@classmethod
315321
def bulk_create(cls, categories):
316322

@@ -462,6 +468,7 @@ def api_json(self):
462468
"name": self.name
463469
}
464470

471+
465472
class CocoImportModel(db.DynamicDocument):
466473
id = db.SequenceField(primary_key=True)
467474
creator = db.StringField(required=True)

app/util/coco_util.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -114,24 +114,40 @@ def get_image_coco(image):
114114
annotations = []
115115

116116
for category in bulk_categories:
117-
117+
category = category[1]
118118
category_annotations = AnnotationModel.objects(
119-
deleted=False, category_id=category[1].id, image_id=image.get('id')
119+
deleted=False, category_id=category.id, image_id=image.get('id')
120120
).exclude('paper_object', 'deleted_date').all()
121-
121+
122122
if len(category_annotations) == 0:
123123
continue
124+
125+
has_keypoints = len(category.keypoint_labels) > 0
124126

125127
for annotation in category_annotations:
126128
annotation = fix_ids(annotation)
127129

128-
if len(annotation.get('segmentation')) != 0:
130+
if len(annotation.get('segmentation', [])) != 0 or \
131+
len(annotation.get('keypoints', [])) != 0:
129132
del annotation['deleted']
130-
del annotation['paper_object']
133+
134+
if not has_keypoints:
135+
del annotation['keypoints']
136+
else:
137+
arr = np.array(annotation.get('keypoints', []))
138+
arr = arr[2::3]
139+
annotation['num_keypoints'] = len(arr[arr > 0])
140+
131141
annotations.append(annotation)
132142

133-
category = fix_ids(category[1])
143+
category = fix_ids(category)
134144
del category['deleted']
145+
if has_keypoints:
146+
category['keypoints'] = category.pop('keypoint_labels')
147+
category['skeleton'] = category.pop('keypoint_edges')
148+
else:
149+
del category['keypoint_edges']
150+
del category['keypoint_labels']
135151
categories.append(category)
136152

137153
del image['deleted']
@@ -169,7 +185,15 @@ def get_dataset_coco(dataset):
169185

170186
for category in categories:
171187
category = fix_ids(category[1])
188+
172189
del category['deleted']
190+
if len(category.keypoint_labels) > 0:
191+
category['keypoints'] = category.pop('keypoint_labels')
192+
category['skeleton'] = category.pop('keypoint_edges')
193+
else:
194+
del category['keypoint_edges']
195+
del category['keypoint_labels']
196+
173197
coco.get('categories').append(category)
174198

175199
for image in images:
@@ -180,8 +204,19 @@ def get_dataset_coco(dataset):
180204
annotations = fix_ids(annotations.all())
181205

182206
for annotation in annotations:
183-
if len(annotation.get('segmentation', [])) != 0:
207+
208+
has_keypoints = len(annotation.get('keypoints', [])) > 0
209+
has_segmentation = len(annotation.get('segmentation', [])) > 0
210+
211+
if has_keypoints or has_keypoints:
184212
del annotation['deleted']
213+
214+
if not has_keypoints:
215+
del annotation['keypoints']
216+
else:
217+
arr = np.array(annotation.get('keypoints', []))
218+
arr = arr[2::3]
219+
annotation['num_keypoints'] = len(arr[arr > 0])
185220
coco.get('annotations').append(annotation)
186221

187222
image = fix_ids(image)
@@ -192,11 +227,4 @@ def get_dataset_coco(dataset):
192227

193228

194229
def _fit(value, max_value, min_value):
195-
196-
if value > max_value:
197-
return max_value
198-
199-
if value < min_value:
200-
return min_value
201-
202-
return value
230+
return max(min(value, max_value), min_value)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<template>
2+
<div class="input-group tool-input-group">
3+
<div class="input-group-prepend tool-option-pre">
4+
<span class="input-group-text tool-option-font">{{ name }}</span>
5+
</div>
6+
<select v-model="localValue" class="form-control tool-option-input">
7+
<option :key="option.key" v-for="option in options" :value="option.key" :selected="option.selected">{{ option.value }}</option>
8+
</select>
9+
</div>
10+
</template>
11+
12+
<script>
13+
export default {
14+
name: "PanelInputString",
15+
model: {
16+
prop: "value",
17+
event: "update"
18+
},
19+
props: {
20+
name: {
21+
type: String,
22+
required: true
23+
},
24+
value: {
25+
type: [Number, Array, Object, String],
26+
required: true
27+
},
28+
values: {
29+
type: Object,
30+
required: true
31+
}
32+
},
33+
data() {
34+
return {
35+
localValue: this.value
36+
};
37+
},
38+
watch: {
39+
localValue() {
40+
this.$emit("update", this.localValue);
41+
},
42+
value(newValue) {
43+
this.localValue = newValue;
44+
}
45+
},
46+
computed: {
47+
options() {
48+
let array = [];
49+
Object.keys(this.values).forEach(k => {
50+
array.push({
51+
key: k,
52+
value: this.values[k],
53+
selected: this.value == k
54+
});
55+
});
56+
return array;
57+
}
58+
}
59+
};
60+
</script>
61+
62+
<style scoped>
63+
.tool-input-group {
64+
padding-top: 3px;
65+
}
66+
67+
.tool-option-pre {
68+
background-color: #383c4a;
69+
}
70+
71+
.tool-option-font {
72+
border-color: #4b5162;
73+
background-color: #383c4a;
74+
color: white;
75+
font-size: 12px;
76+
height: 20px;
77+
}
78+
79+
.tool-option-input {
80+
display: table-cell;
81+
border-color: #4b5162;
82+
color: white;
83+
padding: 0 0 0 3px;
84+
background-color: #383c4a;
85+
font-size: 12px;
86+
height: 20px;
87+
}
88+
</style>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<template>
2+
<div class="tool-option-input tool-option-font">
3+
<p>{{ name }}</p>
4+
</div>
5+
</template>
6+
7+
<script>
8+
export default {
9+
name: "PanelButton",
10+
props: {
11+
name: {
12+
type: String,
13+
required: true
14+
}
15+
}
16+
};
17+
</script>
18+
19+
<style scoped>
20+
.tool-option-font {
21+
border-color: #4b5162;
22+
background-color: #383c4a;
23+
color: white;
24+
font-size: 12px;
25+
height: 20px;
26+
padding: 2px 0 0 0;
27+
}
28+
</style>

0 commit comments

Comments
 (0)