Skip to content

Commit 1807d09

Browse files
committed
finish initial keypoint pose interface
1 parent ff20e29 commit 1807d09

File tree

13 files changed

+301
-29
lines changed

13 files changed

+301
-29
lines changed

src/Annotator/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
MainLayoutState,
1111
Action,
1212
} from "../MainLayout/types"
13+
import type { KeypointsDefinition } from "../ImageCanvas/region-tools"
1314
import SettingsProvider from "../SettingsProvider"
1415

1516
import combineReducers from "./reducers/combine-reducers.js"
@@ -41,6 +42,7 @@ type Props = {
4142
videoSrc?: string,
4243
keyframes?: Object,
4344
videoName?: string,
45+
keypointDefinitions: KeypointsDefinition,
4446
fullImageSegmentationMode?: boolean,
4547
autoSegmentationOptions?:
4648
| {| type: "simple" |}
@@ -77,6 +79,7 @@ export const Annotator = ({
7779
onExit,
7880
onNextImage,
7981
onPrevImage,
82+
keypointDefinitions,
8083
autoSegmentationOptions = { type: "autoseg" },
8184
}: Props) => {
8285
if (typeof selectedImage === "string") {
@@ -112,6 +115,7 @@ export const Annotator = ({
112115
enabledTools,
113116
history: [],
114117
videoName,
118+
keypointDefinitions,
115119
...(annotationType === "image"
116120
? {
117121
selectedImage,

src/Annotator/poses.story.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// @flow
2+
3+
import React, { useState } from "react"
4+
5+
import { storiesOf } from "@storybook/react"
6+
import { action as actionAddon } from "@storybook/addon-actions"
7+
import dancingManImage from "../ImageCanvas/dancing-man.story.jpg"
8+
import Annotator from "./"
9+
10+
const middlewares = [
11+
(store) => (next) => (action) => {
12+
actionAddon(action.type)(action)
13+
return next(action)
14+
},
15+
]
16+
17+
storiesOf("Annotator (Poses)", module).add("Basic", () => (
18+
<Annotator
19+
onExit={actionAddon("onExit")}
20+
middlewares={middlewares}
21+
labelImages
22+
enabledTools={["create-keypoints"]}
23+
keypointDefinitions={{
24+
human: {
25+
connections: [
26+
["head", "sternum"],
27+
["sternum", "leftElbow"],
28+
["sternum", "rightElbow"],
29+
],
30+
landmarks: {
31+
head: {
32+
label: "Head",
33+
color: "#f00",
34+
defaultPosition: [0, -0.05],
35+
},
36+
sternum: {
37+
label: "Torso",
38+
color: "#0f0",
39+
defaultPosition: [0, 0],
40+
},
41+
leftElbow: {
42+
label: "Left Elbow",
43+
color: "#00f",
44+
defaultPosition: [-0.05, 0],
45+
},
46+
rightElbow: {
47+
label: "Right Elbow",
48+
color: "#00f",
49+
defaultPosition: [0.05, 0],
50+
},
51+
},
52+
},
53+
}}
54+
images={[
55+
{
56+
src: dancingManImage,
57+
name: "Dancing Man",
58+
regions: [
59+
{
60+
type: "keypoints",
61+
id: "keypoints1",
62+
keypointsDefinitionId: "human",
63+
highlighted: true,
64+
points: {
65+
head: { x: 0.54, y: 0.2 },
66+
sternum: { x: 0.57, y: 0.3 },
67+
leftElbow: { x: 0.4, y: 0.39 },
68+
rightElbow: { x: 0.7, y: 0.32 },
69+
},
70+
visible: true,
71+
},
72+
],
73+
},
74+
]}
75+
/>
76+
))

src/Annotator/reducers/general-reducer.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import colors from "../../colors"
1010
import fixTwisted from "./fix-twisted"
1111
import convertExpandingLineToPolygon from "./convert-expanding-line-to-polygon"
1212
import clamp from "clamp"
13+
import getLandmarksWithTransform from "../../utils/get-landmarks-with-transform"
1314

1415
const getRandomId = () => Math.random().toString().split(".")[1]
1516

@@ -206,6 +207,16 @@ export default (state: MainLayoutState, action: Action) => {
206207
pointIndex,
207208
})
208209
}
210+
case "BEGIN_MOVE_KEYPOINT": {
211+
const { region, keypointId } = action
212+
state = closeEditors(state)
213+
state = saveToHistory(state, "Move Keypoint")
214+
return setIn(state, ["mode"], {
215+
mode: "MOVE_KEYPOINT",
216+
regionId: region.id,
217+
keypointId,
218+
})
219+
}
209220
case "ADD_POLYGON_POINT": {
210221
const { polygon, point, pointIndex } = action
211222
const regionIndex = getRegionIndex(polygon)
@@ -240,6 +251,22 @@ export default (state: MainLayoutState, action: Action) => {
240251
[x, y]
241252
)
242253
}
254+
case "MOVE_KEYPOINT": {
255+
const { keypointId, regionId } = state.mode
256+
const [region, regionIndex] = getRegion(regionId)
257+
if (regionIndex === null) return state
258+
return setIn(
259+
state,
260+
[
261+
...pathToActiveImage,
262+
"regions",
263+
regionIndex,
264+
"points",
265+
keypointId,
266+
],
267+
{ ...(region: any).points[keypointId], x, y }
268+
)
269+
}
243270
case "MOVE_REGION": {
244271
const { regionId } = state.mode
245272
if (regionId === "$$allowed_area") {
@@ -312,6 +339,20 @@ export default (state: MainLayoutState, action: Action) => {
312339
h: dh,
313340
})
314341
}
342+
case "RESIZE_KEYPOINTS": {
343+
const { regionId, landmarks, centerX, centerY } = state.mode
344+
const distFromCenter = Math.sqrt(
345+
(centerX - x) ** 2 + (centerY - y) ** 2
346+
)
347+
const scale = distFromCenter / 0.15
348+
return modifyRegion(regionId, {
349+
points: getLandmarksWithTransform({
350+
landmarks,
351+
center: { x: centerX, y: centerY },
352+
scale,
353+
}),
354+
})
355+
}
315356
case "DRAW_POLYGON": {
316357
const { regionId } = state.mode
317358
const [region, regionIndex] = getRegion(regionId)
@@ -541,6 +582,34 @@ export default (state: MainLayoutState, action: Action) => {
541582
})
542583
break
543584
}
585+
case "create-keypoints": {
586+
state = saveToHistory(state, "Create Keypoints")
587+
const [
588+
[keypointsDefinitionId, { landmarks, connections }],
589+
] = (Object.entries(state.keypointDefinitions): any)
590+
591+
newRegion = {
592+
type: "keypoints",
593+
keypointsDefinitionId,
594+
points: getLandmarksWithTransform({
595+
landmarks,
596+
center: { x, y },
597+
scale: 1,
598+
}),
599+
highlighted: true,
600+
editingLabels: false,
601+
id: getRandomId(),
602+
}
603+
state = setIn(state, ["mode"], {
604+
mode: "RESIZE_KEYPOINTS",
605+
landmarks,
606+
centerX: x,
607+
centerY: y,
608+
regionId: newRegion.id,
609+
isNew: true,
610+
})
611+
break
612+
}
544613
default:
545614
break
546615
}
@@ -581,9 +650,13 @@ export default (state: MainLayoutState, action: Action) => {
581650
}
582651
}
583652
case "MOVE_REGION":
653+
case "RESIZE_KEYPOINTS":
584654
case "MOVE_POLYGON_POINT": {
585655
return { ...state, mode: null }
586656
}
657+
case "MOVE_KEYPOINT": {
658+
return { ...state, mode: null }
659+
}
587660
case "CREATE_POINT_LINE": {
588661
return state
589662
}

src/ImageCanvas/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {
1515
Point,
1616
Polygon,
1717
Box,
18+
Keypoints,
1819
KeypointsDefinition,
1920
} from "./region-tools.js"
2021
import { makeStyles } from "@material-ui/core/styles"
@@ -72,6 +73,7 @@ type Props = {
7273
onDeleteRegion: (Region) => any,
7374
onBeginBoxTransform: (Box, [number, number]) => any,
7475
onBeginMovePolygonPoint: (Polygon, index: number) => any,
76+
onBeginMoveKeypoint: (Keypoints, index: number) => any,
7577
onAddPolygonPoint: (Polygon, point: [number, number], index: number) => any,
7678
onSelectRegion: (Region) => any,
7779
onBeginMovePoint: (Point) => any,
@@ -127,6 +129,7 @@ export const ImageCanvas = ({
127129
onBeginBoxTransform,
128130
onBeginMovePolygonPoint,
129131
onAddPolygonPoint,
132+
onBeginMoveKeypoint,
130133
onSelectRegion,
131134
onBeginMovePoint,
132135
onDeleteRegion,
@@ -364,6 +367,7 @@ export const ImageCanvas = ({
364367
mat={mat}
365368
onBeginBoxTransform={onBeginBoxTransform}
366369
onBeginMovePolygonPoint={onBeginMovePolygonPoint}
370+
onBeginMoveKeypoint={onBeginMoveKeypoint}
367371
onAddPolygonPoint={onAddPolygonPoint}
368372
showHighlightBox={showHighlightBox}
369373
/>

src/ImageCanvas/index.story.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ const events = {
105105
onAddPolygonPoint: action("onAddPolygonPoint"),
106106
onClosePolygon: action("onClosePolygon"),
107107

108+
onBeginMoveKeypoint: action("onBeginMoveKeypoint"),
109+
108110
onBeginMovePoint: action("onBeginMovePoint"),
109111
onDeleteRegion: action("onDeleteRegion"),
110112
}
@@ -162,17 +164,17 @@ storiesOf("ImageCanvas", module)
162164
keypointDefinitions={{
163165
human: {
164166
connections: [
165-
["head", "torso"],
166-
["torso", "leftElbow"],
167-
["torso", "rightElbow"],
167+
["head", "sternum"],
168+
["sternum", "leftElbow"],
169+
["sternum", "rightElbow"],
168170
],
169171
landmarks: {
170172
head: {
171173
label: "Head",
172174
color: "#f00",
173175
defaultPosition: [0, -0.05],
174176
},
175-
torso: {
177+
sternum: {
176178
label: "Torso",
177179
color: "#0f0",
178180
defaultPosition: [0, 0],
@@ -194,13 +196,13 @@ storiesOf("ImageCanvas", module)
194196
{
195197
type: "keypoints",
196198
id: "keypoints1",
197-
keypointDefinitionId: "human",
199+
keypointsDefinitionId: "human",
198200
highlighted: true,
199201
points: {
200-
head: [0.5, 0.4],
201-
torso: [0.5, 0.5],
202-
leftElbow: [0.4, 0.5],
203-
rightElbow: [0.6, 0.5],
202+
head: { x: 0.54, y: 0.2 },
203+
sternum: { x: 0.57, y: 0.3 },
204+
leftElbow: { x: 0.4, y: 0.39 },
205+
rightElbow: { x: 0.7, y: 0.32 },
204206
},
205207
visible: true,
206208
},

src/ImageCanvas/region-tools.js

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ export type ExpandingLine = {|
5454
points: Array<{ x: number, y: number, angle: number, width: number }>,
5555
|}
5656

57-
export type Keypoint = {|
57+
export type KeypointDefinition = {|
5858
label: string,
59+
color: string,
5960
defaultPosition: [number, number],
6061
|}
6162

@@ -65,7 +66,7 @@ export type KeypointsDefinition = {|
6566
[id: string]: {
6667
connections: Array<[KeypointId, KeypointId]>,
6768
landmarks: {
68-
[KeypointId]: Keypoint,
69+
[KeypointId]: KeypointDefinition,
6970
},
7071
},
7172
|}
@@ -75,7 +76,7 @@ export type Keypoints = {|
7576
type: "keypoints",
7677
keypointsDefinitionId: string,
7778
points: {
78-
[string]: [number, number],
79+
[string]: { x: number, y: number },
7980
},
8081
|}
8182

@@ -100,6 +101,26 @@ export const getEnclosingBox = (region: Region) => {
100101
box.h = Math.max(...region.points.map(([x, y]) => y)) - box.y
101102
return box
102103
}
104+
case "keypoints": {
105+
const minX = Math.min(
106+
...Object.values(region.points).map(({ x, y }) => x)
107+
)
108+
const minY = Math.min(
109+
...Object.values(region.points).map(({ x, y }) => y)
110+
)
111+
const maxX = Math.max(
112+
...Object.values(region.points).map(({ x, y }) => x)
113+
)
114+
const maxY = Math.max(
115+
...Object.values(region.points).map(({ x, y }) => y)
116+
)
117+
return {
118+
x: minX,
119+
y: minY,
120+
w: maxX - minX,
121+
h: maxY - minY,
122+
}
123+
}
103124
case "expanding-line": {
104125
const box = {
105126
x: Math.min(...region.points.map(({ x, y }) => x)),

src/MainLayout/icon-dictionary.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
faEdit,
1919
} from "@fortawesome/free-solid-svg-icons"
2020
import FullscreenIcon from "@material-ui/icons/Fullscreen"
21+
import AccessibilityNewIcon from "@material-ui/icons/AccessibilityNew"
2122

2223
const faStyle = { marginTop: 4, width: 16, height: 16, marginBottom: 4 }
2324

@@ -67,6 +68,7 @@ export const iconDictionary = {
6768
"modify-allowed-area": () => (
6869
<FontAwesomeIcon style={faStyle} size="xs" fixedWidth icon={faEdit} />
6970
),
71+
"create-keypoints": AccessibilityNewIcon,
7072
window: FullscreenIcon,
7173
}
7274

0 commit comments

Comments
 (0)