@searpent/react-image-annotate 2.0.2 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/.babelrc +6 -0
  2. package/.env +1 -0
  3. package/.flowconfig +2 -0
  4. package/.github/workflows/release-on-master.yml +32 -0
  5. package/.github/workflows/test.yml +16 -0
  6. package/.prettierrc +3 -0
  7. package/.releaserc.js +18 -0
  8. package/.storybook/addons.js +2 -0
  9. package/.storybook/config.js +16 -0
  10. package/LICENSE +21 -0
  11. package/package.json +4 -1
  12. package/public/favicon.ico +0 -0
  13. package/public/index.html +38 -0
  14. package/src/Annotator/bike-pic.png +0 -0
  15. package/src/Annotator/bike-pic2.png +0 -0
  16. package/src/Annotator/dab-keyframes.story.json +1 -0
  17. package/src/Annotator/examplePhotos.js +7601 -0
  18. package/src/Annotator/index.js +291 -0
  19. package/src/Annotator/index.story.js +807 -0
  20. package/src/Annotator/poses.story.js +150 -0
  21. package/src/Annotator/reducers/combine-reducers.js +7 -0
  22. package/src/Annotator/reducers/convert-expanding-line-to-polygon.js +53 -0
  23. package/{Annotator → src/Annotator}/reducers/fix-twisted.js +5 -3
  24. package/src/Annotator/reducers/general-reducer.js +996 -0
  25. package/src/Annotator/reducers/get-active-image.js +21 -0
  26. package/src/Annotator/reducers/get-implied-video-regions.js +115 -0
  27. package/src/Annotator/reducers/history-handler.js +60 -0
  28. package/src/Annotator/reducers/image-reducer.js +23 -0
  29. package/src/Annotator/reducers/video-reducer.js +85 -0
  30. package/src/Annotator/video.story.js +51 -0
  31. package/src/ClassSelectionMenu/index.js +108 -0
  32. package/src/Crosshairs/index.js +64 -0
  33. package/src/DebugSidebarBox/index.js +36 -0
  34. package/src/DemoSite/Editor.js +235 -0
  35. package/src/DemoSite/ErrorBoundaryDialog.js +34 -0
  36. package/src/DemoSite/index.js +41 -0
  37. package/src/DemoSite/index.story.js +10 -0
  38. package/src/DemoSite/simple-segmentation-example.json +572 -0
  39. package/src/Editor/annotation-plugin/annotation.css +46 -0
  40. package/src/Editor/annotation-plugin/annotation.js +515 -0
  41. package/src/Editor/index.js +24 -0
  42. package/src/Editor/tools.js +6 -0
  43. package/src/FullImageSegmentationAnnotator/hard1.story.jpg +0 -0
  44. package/src/FullImageSegmentationAnnotator/hard2.story.jpg +0 -0
  45. package/src/FullImageSegmentationAnnotator/hard3.story.jpg +0 -0
  46. package/src/FullImageSegmentationAnnotator/index.js +7 -0
  47. package/src/FullImageSegmentationAnnotator/index.story.js +177 -0
  48. package/src/FullImageSegmentationAnnotator/orange.story.png +0 -0
  49. package/src/GroupSelectorSidebarBox/index.js +48 -0
  50. package/src/HighlightBox/index.js +143 -0
  51. package/src/HistorySidebarBox/index.js +78 -0
  52. package/src/ImageCanvas/dancing-man.story.jpg +0 -0
  53. package/src/ImageCanvas/index.js +494 -0
  54. package/src/ImageCanvas/index.story.js +314 -0
  55. package/src/ImageCanvas/mouse_mask.story.png +0 -0
  56. package/src/ImageCanvas/region-tools.js +171 -0
  57. package/src/ImageCanvas/seves_desk.story.jpg +0 -0
  58. package/{ImageCanvas → src/ImageCanvas}/styles.js +8 -12
  59. package/src/ImageCanvas/use-mouse.js +168 -0
  60. package/src/ImageCanvas/use-project-box.js +23 -0
  61. package/src/ImageCanvas/use-wasd-mode.js +50 -0
  62. package/src/ImageMask/index.js +127 -0
  63. package/src/ImageMask/load-image.js +32 -0
  64. package/src/ImageSelectorSidebarBox/index.js +54 -0
  65. package/src/KeyframeTimeline/get-time-string.js +25 -0
  66. package/src/KeyframeTimeline/index.js +223 -0
  67. package/src/KeyframesSelectorSidebarBox/index.js +93 -0
  68. package/src/LandingPage/content.md +57 -0
  69. package/src/LandingPage/github-markdown.css +964 -0
  70. package/src/LandingPage/index.js +147 -0
  71. package/src/MainLayout/icon-dictionary.js +79 -0
  72. package/src/MainLayout/index.js +510 -0
  73. package/src/MainLayout/index.story.js +240 -0
  74. package/{MainLayout → src/MainLayout}/styles.js +7 -6
  75. package/src/MainLayout/types.js +164 -0
  76. package/src/MainLayout/use-implied-video-regions.js +17 -0
  77. package/src/MetadataEditorSidebarBox/index.js +98 -0
  78. package/src/PageSelector/index.js +76 -0
  79. package/src/PageSelector/page-selector.css +69 -0
  80. package/src/PointDistances/index.js +90 -0
  81. package/src/PreventScrollToParents/index.js +48 -0
  82. package/src/PreventScrollToParents/index.story.js +23 -0
  83. package/src/RegionLabel/index.js +222 -0
  84. package/{RegionLabel → src/RegionLabel}/styles.js +15 -12
  85. package/src/RegionSelectAndTransformBoxes/index.js +234 -0
  86. package/src/RegionSelectorSidebarBox/index.js +216 -0
  87. package/{RegionSelectorSidebarBox → src/RegionSelectorSidebarBox}/styles.js +14 -13
  88. package/src/RegionShapes/index.js +254 -0
  89. package/src/RegionTags/index.js +134 -0
  90. package/src/SettingsDialog/index.js +58 -0
  91. package/src/SettingsProvider/index.js +48 -0
  92. package/src/Shortcuts/ShortcutField.js +44 -0
  93. package/src/Shortcuts/index.js +129 -0
  94. package/src/ShortcutsManager/index.js +162 -0
  95. package/src/Sidebar/index.js +117 -0
  96. package/src/SidebarBoxContainer/index.js +93 -0
  97. package/src/SmallToolButton/index.js +57 -0
  98. package/src/TagsSidebarBox/index.js +93 -0
  99. package/src/TaskDescriptionSidebarBox/index.js +43 -0
  100. package/src/Theme/index.js +36 -0
  101. package/src/VideoOrImageCanvasBackground/index.js +170 -0
  102. package/src/colors.js +32 -0
  103. package/src/hooks/use-colors.js +74 -0
  104. package/src/hooks/use-event-callback.js +11 -0
  105. package/src/hooks/use-exclude-pattern.js +27 -0
  106. package/src/hooks/use-load-image.js +21 -0
  107. package/src/hooks/use-window-size.js +46 -0
  108. package/{hooks → src/hooks}/xpattern.js +1 -1
  109. package/src/hooks/xpattern.png +0 -0
  110. package/src/index.js +18 -0
  111. package/src/lib.js +7 -0
  112. package/src/screenshot.png +0 -0
  113. package/src/site.css +5 -0
  114. package/src/stories.js +2 -0
  115. package/src/utils/filter-only-unique.js +5 -0
  116. package/src/utils/get-from-local-storage.js +7 -0
  117. package/src/utils/get-hotkey-help-text.js +11 -0
  118. package/src/utils/get-landmarks-with-transform.js +23 -0
  119. package/src/utils/photosToImages.js +40 -0
  120. package/src/utils/regions-to-blocks.js +14 -0
  121. package/src/utils/set-in-local-storage.js +6 -0
  122. package/Annotator/index.js +0 -161
  123. package/Annotator/reducers/combine-reducers.js +0 -14
  124. package/Annotator/reducers/convert-expanding-line-to-polygon.js +0 -73
  125. package/Annotator/reducers/general-reducer.js +0 -1058
  126. package/Annotator/reducers/get-active-image.js +0 -27
  127. package/Annotator/reducers/get-implied-video-regions.js +0 -180
  128. package/Annotator/reducers/history-handler.js +0 -38
  129. package/Annotator/reducers/image-reducer.js +0 -20
  130. package/Annotator/reducers/video-reducer.js +0 -88
  131. package/ClassSelectionMenu/index.js +0 -135
  132. package/Crosshairs/index.js +0 -53
  133. package/DebugSidebarBox/index.js +0 -20
  134. package/DemoSite/Editor.js +0 -194
  135. package/DemoSite/ErrorBoundaryDialog.js +0 -64
  136. package/DemoSite/index.js +0 -40
  137. package/FullImageSegmentationAnnotator/index.js +0 -7
  138. package/HighlightBox/index.js +0 -99
  139. package/HistorySidebarBox/index.js +0 -71
  140. package/ImageCanvas/index.js +0 -424
  141. package/ImageCanvas/region-tools.js +0 -165
  142. package/ImageCanvas/use-mouse.js +0 -180
  143. package/ImageCanvas/use-project-box.js +0 -27
  144. package/ImageCanvas/use-wasd-mode.js +0 -62
  145. package/ImageMask/index.js +0 -133
  146. package/ImageMask/load-image.js +0 -25
  147. package/ImageSelectorSidebarBox/index.js +0 -60
  148. package/KeyframeTimeline/get-time-string.js +0 -27
  149. package/KeyframeTimeline/index.js +0 -233
  150. package/KeyframesSelectorSidebarBox/index.js +0 -93
  151. package/LandingPage/index.js +0 -159
  152. package/MainLayout/icon-dictionary.js +0 -104
  153. package/MainLayout/index.js +0 -352
  154. package/MainLayout/types.js +0 -0
  155. package/MainLayout/use-implied-video-regions.js +0 -13
  156. package/PointDistances/index.js +0 -73
  157. package/PreventScrollToParents/index.js +0 -51
  158. package/RegionLabel/index.js +0 -191
  159. package/RegionSelectAndTransformBoxes/index.js +0 -167
  160. package/RegionSelectorSidebarBox/index.js +0 -248
  161. package/RegionShapes/index.js +0 -274
  162. package/RegionTags/index.js +0 -138
  163. package/SettingsDialog/index.js +0 -52
  164. package/SettingsProvider/index.js +0 -53
  165. package/Shortcuts/ShortcutField.js +0 -46
  166. package/Shortcuts/index.js +0 -133
  167. package/ShortcutsManager/index.js +0 -155
  168. package/Sidebar/index.js +0 -69
  169. package/SidebarBoxContainer/index.js +0 -93
  170. package/SmallToolButton/index.js +0 -42
  171. package/TagsSidebarBox/index.js +0 -105
  172. package/TaskDescriptionSidebarBox/index.js +0 -58
  173. package/Theme/index.js +0 -30
  174. package/VideoOrImageCanvasBackground/index.js +0 -151
  175. package/colors.js +0 -14
  176. package/hooks/use-event-callback.js +0 -10
  177. package/hooks/use-exclude-pattern.js +0 -24
  178. package/hooks/use-load-image.js +0 -26
  179. package/hooks/use-window-size.js +0 -46
  180. package/index.js +0 -3
  181. package/lib.js +0 -3
  182. package/stories.js +0 -5
  183. package/utils/get-from-local-storage.js +0 -7
  184. package/utils/get-hotkey-help-text.js +0 -9
  185. package/utils/get-landmarks-with-transform.js +0 -40
  186. package/utils/set-in-local-storage.js +0 -3
@@ -0,0 +1,216 @@
1
+ // @flow
2
+
3
+ import React, { Fragment, useState, memo } from "react"
4
+ import SidebarBoxContainer from "../SidebarBoxContainer"
5
+ import { makeStyles } from "@mui/styles"
6
+ import { createTheme, ThemeProvider } from "@mui/material/styles"
7
+ import { styled } from "@mui/material/styles"
8
+ import { grey } from "@mui/material/colors"
9
+ import RegionIcon from "@mui/icons-material/PictureInPicture"
10
+ import Grid from "@mui/material/Grid"
11
+ import ReorderIcon from "@mui/icons-material/SwapVert"
12
+ import PieChartIcon from "@mui/icons-material/PieChart"
13
+ import TrashIcon from "@mui/icons-material/Delete"
14
+ import LockIcon from "@mui/icons-material/Lock"
15
+ import UnlockIcon from "@mui/icons-material/LockOpen"
16
+ import VisibleIcon from "@mui/icons-material/Visibility"
17
+ import VisibleOffIcon from "@mui/icons-material/VisibilityOff"
18
+ import styles from "./styles"
19
+ import classnames from "classnames"
20
+ import isEqual from "lodash/isEqual"
21
+
22
+ const theme = createTheme()
23
+ const useStyles = makeStyles((theme) => styles)
24
+
25
+ const HeaderSep = styled("div")(({ theme }) => ({
26
+ borderTop: `1px solid ${grey[200]}`,
27
+ marginTop: 2,
28
+ marginBottom: 2,
29
+ }))
30
+
31
+ const Chip = ({ color, text }) => {
32
+ const classes = useStyles()
33
+ return (
34
+ <span className={classes.chip}>
35
+ <div className="color" style={{ backgroundColor: color }} />
36
+ <div className="text">{text}</div>
37
+ </span>
38
+ )
39
+ }
40
+
41
+ const RowLayout = ({
42
+ header,
43
+ highlighted,
44
+ order,
45
+ classification,
46
+ area,
47
+ tags,
48
+ trash,
49
+ lock,
50
+ visible,
51
+ onClick,
52
+ }) => {
53
+ const classes = useStyles()
54
+ const [mouseOver, changeMouseOver] = useState(false)
55
+ return (
56
+ <div
57
+ onClick={onClick}
58
+ onMouseEnter={() => changeMouseOver(true)}
59
+ onMouseLeave={() => changeMouseOver(false)}
60
+ className={classnames(classes.row, { header, highlighted })}
61
+ >
62
+ <Grid container alignItems="center">
63
+ <Grid item xs={2}>
64
+ <div style={{ textAlign: "right", paddingRight: 10 }}>{order}</div>
65
+ </Grid>
66
+ <Grid item xs={5}>
67
+ {classification}
68
+ </Grid>
69
+ <Grid item xs={2}>
70
+ <div style={{ textAlign: "right", paddingRight: 6 }}>{area}</div>
71
+ </Grid>
72
+ <Grid item xs={1}>
73
+ {trash}
74
+ </Grid>
75
+ <Grid item xs={1}>
76
+ {lock}
77
+ </Grid>
78
+ <Grid item xs={1}>
79
+ {visible}
80
+ </Grid>
81
+ </Grid>
82
+ </div>
83
+ )
84
+ }
85
+
86
+ const RowHeader = () => {
87
+ return (
88
+ <RowLayout
89
+ header
90
+ highlighted={false}
91
+ order={<ReorderIcon className="icon" />}
92
+ classification={<div style={{ paddingLeft: 10 }}>Class</div>}
93
+ area={<PieChartIcon className="icon" />}
94
+ trash={<TrashIcon className="icon" />}
95
+ lock={<LockIcon className="icon" />}
96
+ visible={<VisibleIcon className="icon" />}
97
+ />
98
+ )
99
+ }
100
+
101
+ const MemoRowHeader = memo(RowHeader)
102
+
103
+ const Row = ({
104
+ region: r,
105
+ highlighted,
106
+ onSelectRegion,
107
+ onDeleteRegion,
108
+ onChangeRegion,
109
+ visible,
110
+ locked,
111
+ color,
112
+ cls,
113
+ index,
114
+ }) => {
115
+ return (
116
+ <RowLayout
117
+ header={false}
118
+ highlighted={highlighted}
119
+ onClick={() => onSelectRegion(r)}
120
+ order={`#${index + 1}`}
121
+ classification={<Chip text={cls || ""} color={color || "#ddd"} />}
122
+ area=""
123
+ trash={<TrashIcon onClick={() => onDeleteRegion(r)} className="icon2" />}
124
+ lock={
125
+ r.locked ? (
126
+ <LockIcon
127
+ onClick={() => onChangeRegion({ ...r, locked: false })}
128
+ className="icon2"
129
+ />
130
+ ) : (
131
+ <UnlockIcon
132
+ onClick={() => onChangeRegion({ ...r, locked: true })}
133
+ className="icon2"
134
+ />
135
+ )
136
+ }
137
+ visible={
138
+ r.visible || r.visible === undefined ? (
139
+ <VisibleIcon
140
+ onClick={() => onChangeRegion({ ...r, visible: false })}
141
+ className="icon2"
142
+ />
143
+ ) : (
144
+ <VisibleOffIcon
145
+ onClick={() => onChangeRegion({ ...r, visible: true })}
146
+ className="icon2"
147
+ />
148
+ )
149
+ }
150
+ />
151
+ )
152
+ }
153
+
154
+ const MemoRow = memo(
155
+ Row,
156
+ (prevProps, nextProps) =>
157
+ prevProps.highlighted === nextProps.highlighted &&
158
+ prevProps.visible === nextProps.visible &&
159
+ prevProps.locked === nextProps.locked &&
160
+ prevProps.id === nextProps.id &&
161
+ prevProps.index === nextProps.index &&
162
+ prevProps.cls === nextProps.cls &&
163
+ prevProps.color === nextProps.color
164
+ )
165
+
166
+ const emptyArr = []
167
+
168
+ export const RegionSelectorSidebarBox = ({
169
+ regions = emptyArr,
170
+ onDeleteRegion,
171
+ onChangeRegion,
172
+ onSelectRegion,
173
+ }) => {
174
+ const classes = useStyles()
175
+ return (
176
+ <ThemeProvider theme={theme}>
177
+ <SidebarBoxContainer
178
+ title="Regions"
179
+ subTitle=""
180
+ icon={<RegionIcon style={{ color: grey[700] }} />}
181
+ expandedByDefault
182
+ >
183
+ <div className={classes.container}>
184
+ <MemoRowHeader />
185
+ <HeaderSep />
186
+ {regions.map((r, i) => (
187
+ <MemoRow
188
+ key={r.id}
189
+ {...r}
190
+ region={r}
191
+ index={i}
192
+ onSelectRegion={onSelectRegion}
193
+ onDeleteRegion={onDeleteRegion}
194
+ onChangeRegion={onChangeRegion}
195
+ />
196
+ ))}
197
+ </div>
198
+ </SidebarBoxContainer>
199
+ </ThemeProvider>
200
+ )
201
+ }
202
+
203
+ const mapUsedRegionProperties = (r) => [
204
+ r.id,
205
+ r.color,
206
+ r.locked,
207
+ r.visible,
208
+ r.highlighted,
209
+ ]
210
+
211
+ export default memo(RegionSelectorSidebarBox, (prevProps, nextProps) =>
212
+ isEqual(
213
+ (prevProps.regions || emptyArr).map(mapUsedRegionProperties),
214
+ (nextProps.regions || emptyArr).map(mapUsedRegionProperties)
215
+ )
216
+ )
@@ -1,4 +1,5 @@
1
- import { grey, blue, orange, purple } from "@mui/material/colors";
1
+ import { grey, blue, orange, purple } from "@mui/material/colors"
2
+
2
3
  export default {
3
4
  container: {
4
5
  fontSize: 11,
@@ -7,7 +8,7 @@ export default {
7
8
  "& .icon": {
8
9
  marginTop: 4,
9
10
  width: 16,
10
- height: 16
11
+ height: 16,
11
12
  },
12
13
  "& .icon2": {
13
14
  opacity: 0.5,
@@ -16,23 +17,23 @@ export default {
16
17
  transition: "200ms opacity",
17
18
  "&:hover": {
18
19
  cursor: "pointer",
19
- opacity: 1
20
- }
21
- }
20
+ opacity: 1,
21
+ },
22
+ },
22
23
  },
23
24
  row: {
24
25
  padding: 4,
25
26
  cursor: "pointer",
26
27
  "&.header:hover": {
27
- backgroundColor: "#fff"
28
+ backgroundColor: "#fff",
28
29
  },
29
30
  "&.highlighted": {
30
- backgroundColor: blue[100]
31
+ backgroundColor: blue[100],
31
32
  },
32
33
  "&:hover": {
33
34
  backgroundColor: blue[50],
34
- color: grey[800]
35
- }
35
+ color: grey[800],
36
+ },
36
37
  },
37
38
  chip: {
38
39
  display: "flex",
@@ -46,8 +47,8 @@ export default {
46
47
  borderRadius: 5,
47
48
  width: 10,
48
49
  height: 10,
49
- marginRight: 4
50
+ marginRight: 4,
50
51
  },
51
- "& .text": {}
52
- }
53
- };
52
+ "& .text": {},
53
+ },
54
+ }
@@ -0,0 +1,254 @@
1
+ // @flow
2
+
3
+ import React, { memo } from "react"
4
+ import colorAlpha from "color-alpha"
5
+ import useColors from '../hooks/use-colors';
6
+
7
+
8
+ function clamp(num, min, max) {
9
+ return num <= min ? min : num >= max ? max : num
10
+ }
11
+
12
+ const RegionComponents = {
13
+ point: memo(({ region, iw, ih }) => (
14
+ <g transform={`translate(${region.x * iw} ${region.y * ih})`}>
15
+ <path
16
+ d={"M0 8L8 0L0 -8L-8 0Z"}
17
+ strokeWidth={2}
18
+ stroke={region.color}
19
+ fill="transparent"
20
+ />
21
+ </g>
22
+ )),
23
+ line: memo(({ region, iw, ih }) => (
24
+ <g transform={`translate(${region.x1 * iw} ${region.y1 * ih})`}>
25
+ <line
26
+ strokeWidth={2}
27
+ x1={0}
28
+ y1={0}
29
+ x2={(region.x2 - region.x1) * iw}
30
+ y2={(region.y2 - region.y1) * ih}
31
+ stroke={colorAlpha(region.color, 0.75)}
32
+ fill={colorAlpha(region.color, 0.25)}
33
+ />
34
+ </g>
35
+ )),
36
+ box: memo(({ region, iw, ih }) => {
37
+ const { clsColor, groupColor } = useColors();
38
+
39
+ if (region.groupId !== undefined) {
40
+ return <g transform={`translate(${region.x * iw} ${region.y * ih})`}>
41
+ <rect
42
+ strokeWidth={(region.groupHighlighted) ? 3 : 0}
43
+ x={0}
44
+ y={0}
45
+ width={Math.max(region.w * iw, 0)}
46
+ height={Math.max(region.h * ih, 0)}
47
+ stroke={colorAlpha(clsColor(region.cls), 0.85)}
48
+ fill={(region.groupHighlighted) ? colorAlpha(groupColor(region.groupId), 0.5) : colorAlpha(groupColor(region.groupId), 0.25)}
49
+ />
50
+ </g>
51
+ } else {
52
+ return <g transform={`translate(${region.x * iw} ${region.y * ih})`}>
53
+ <rect
54
+ strokeWidth={2}
55
+ x={0}
56
+ y={0}
57
+ width={Math.max(region.w * iw, 0)}
58
+ height={Math.max(region.h * ih, 0)}
59
+ stroke={colorAlpha(clsColor(region.cls), 0.85)}
60
+ fill={colorAlpha(clsColor(region.cls), 0.25)}
61
+ />
62
+ </g>
63
+ }
64
+ }
65
+ ),
66
+ polygon: memo(({ region, iw, ih, fullSegmentationMode }) => {
67
+ const Component = region.open ? "polyline" : "polygon"
68
+ const alphaBase = fullSegmentationMode ? 0.5 : 1
69
+ return (
70
+ <Component
71
+ points={region.points
72
+ .map(([x, y]) => [x * iw, y * ih])
73
+ .map((a) => a.join(" "))
74
+ .join(" ")}
75
+ strokeWidth={2}
76
+ stroke={colorAlpha(region.color, 0.75)}
77
+ fill={colorAlpha(region.color, 0.25)}
78
+ />
79
+ )
80
+ }),
81
+ keypoints: ({ region, iw, ih, keypointDefinitions }) => {
82
+ const { points, keypointsDefinitionId } = region
83
+ if (!keypointDefinitions[keypointsDefinitionId]) {
84
+ throw new Error(
85
+ `No definition for keypoint configuration "${keypointsDefinitionId}"`
86
+ )
87
+ }
88
+ const { landmarks, connections } =
89
+ keypointDefinitions[keypointsDefinitionId]
90
+ return (
91
+ <g>
92
+ {Object.entries(points).map(([keypointId, { x, y }], i) => (
93
+ <g key={i} transform={`translate(${x * iw} ${y * ih})`}>
94
+ <path
95
+ d={"M0 8L8 0L0 -8L-8 0Z"}
96
+ strokeWidth={2}
97
+ stroke={landmarks[keypointId].color}
98
+ fill="transparent"
99
+ />
100
+ </g>
101
+ ))}
102
+ {connections.map(([kp1Id, kp2Id]) => {
103
+ const kp1 = points[kp1Id]
104
+ const kp2 = points[kp2Id]
105
+ const midPoint = { x: (kp1.x + kp2.x) / 2, y: (kp1.y + kp2.y) / 2 }
106
+
107
+ return (
108
+ <g key={`${kp1.x},${kp1.y}.${kp2.x},${kp2.y}`}>
109
+ <line
110
+ x1={kp1.x * iw}
111
+ y1={kp1.y * ih}
112
+ x2={midPoint.x * iw}
113
+ y2={midPoint.y * ih}
114
+ strokeWidth={2}
115
+ stroke={landmarks[kp1Id].color}
116
+ />
117
+ <line
118
+ x1={kp2.x * iw}
119
+ y1={kp2.y * ih}
120
+ x2={midPoint.x * iw}
121
+ y2={midPoint.y * ih}
122
+ strokeWidth={2}
123
+ stroke={landmarks[kp2Id].color}
124
+ />
125
+ </g>
126
+ )
127
+ })}
128
+ </g>
129
+ )
130
+ },
131
+ "expanding-line": memo(({ region, iw, ih }) => {
132
+ let { expandingWidth = 0.005, points } = region
133
+ expandingWidth = points.slice(-1)[0].width || expandingWidth
134
+ const pointPairs = points.map(({ x, y, angle, width }, i) => {
135
+ if (!angle) {
136
+ const n = points[clamp(i + 1, 0, points.length - 1)]
137
+ const p = points[clamp(i - 1, 0, points.length - 1)]
138
+ angle = Math.atan2(p.x - n.x, p.y - n.y) + Math.PI / 2
139
+ }
140
+ const dx = (Math.sin(angle) * (width || expandingWidth)) / 2
141
+ const dy = (Math.cos(angle) * (width || expandingWidth)) / 2
142
+ return [
143
+ { x: x + dx, y: y + dy },
144
+ { x: x - dx, y: y - dy },
145
+ ]
146
+ })
147
+ const firstSection = pointPairs.map(([p1, p2]) => p1)
148
+ const secondSection = pointPairs.map(([p1, p2]) => p2).asMutable()
149
+ secondSection.reverse()
150
+ const lastPoint = points.slice(-1)[0]
151
+ return (
152
+ <>
153
+ <polygon
154
+ points={firstSection
155
+ .concat(region.candidatePoint ? [region.candidatePoint] : [])
156
+ .concat(secondSection)
157
+ .map((p) => `${p.x * iw} ${p.y * ih}`)
158
+ .join(" ")}
159
+ strokeWidth={2}
160
+ stroke={colorAlpha(region.color, 0.75)}
161
+ fill={colorAlpha(region.color, 0.25)}
162
+ />
163
+ {points.map(({ x, y, angle }, i) => (
164
+ <g
165
+ key={i}
166
+ transform={`translate(${x * iw} ${y * ih}) rotate(${(-(angle || 0) * 180) / Math.PI
167
+ })`}
168
+ >
169
+ <g>
170
+ <rect
171
+ x={-5}
172
+ y={-5}
173
+ width={10}
174
+ height={10}
175
+ strokeWidth={2}
176
+ stroke={colorAlpha(region.color, 0.75)}
177
+ fill={colorAlpha(region.color, 0.25)}
178
+ />
179
+ </g>
180
+ </g>
181
+ ))}
182
+ <rect
183
+ x={lastPoint.x * iw - 8}
184
+ y={lastPoint.y * ih - 8}
185
+ width={16}
186
+ height={16}
187
+ strokeWidth={4}
188
+ stroke={colorAlpha(region.color, 0.5)}
189
+ fill={"transparent"}
190
+ />
191
+ </>
192
+ )
193
+ }),
194
+ pixel: () => null,
195
+ }
196
+
197
+ export const WrappedRegionList = memo(
198
+ ({ regions, keypointDefinitions, iw, ih, fullSegmentationMode }) => {
199
+ return regions
200
+ .filter((r) => r.visible !== false)
201
+ .map((r) => {
202
+ const Component = RegionComponents[r.type]
203
+ return (
204
+ <Component
205
+ key={r.regionId}
206
+ region={r}
207
+ iw={iw}
208
+ ih={ih}
209
+ keypointDefinitions={keypointDefinitions}
210
+ fullSegmentationMode={fullSegmentationMode}
211
+ />
212
+ )
213
+ })
214
+ },
215
+ (n, p) => n.regions === p.regions && n.iw === p.iw && n.ih === p.ih
216
+ )
217
+
218
+ export const RegionShapes = ({
219
+ mat,
220
+ imagePosition,
221
+ regions = [],
222
+ keypointDefinitions,
223
+ fullSegmentationMode,
224
+ }) => {
225
+ const iw = imagePosition.bottomRight.x - imagePosition.topLeft.x
226
+ const ih = imagePosition.bottomRight.y - imagePosition.topLeft.y
227
+ if (isNaN(iw) || isNaN(ih)) return null
228
+ return (
229
+ <svg
230
+ width={iw}
231
+ height={ih}
232
+ style={{
233
+ position: "absolute",
234
+ zIndex: 2,
235
+ left: imagePosition.topLeft.x,
236
+ top: imagePosition.topLeft.y,
237
+ pointerEvents: "none",
238
+ width: iw,
239
+ height: ih,
240
+ }}
241
+ >
242
+ <WrappedRegionList
243
+ key="wrapped-region-list"
244
+ regions={regions}
245
+ iw={iw}
246
+ ih={ih}
247
+ keypointDefinitions={keypointDefinitions}
248
+ fullSegmentationMode={fullSegmentationMode}
249
+ />
250
+ </svg>
251
+ )
252
+ }
253
+
254
+ export default RegionShapes
@@ -0,0 +1,134 @@
1
+ // @flow weak
2
+
3
+ import React from "react"
4
+ import Paper from "@mui/material/Paper"
5
+ import DefaultRegionLabel from "../RegionLabel"
6
+ import LockIcon from "@mui/icons-material/Lock"
7
+
8
+ const copyWithout = (obj, ...args) => {
9
+ const newObj = { ...obj }
10
+ for (const arg of args) {
11
+ delete newObj[arg]
12
+ }
13
+ return newObj
14
+ }
15
+
16
+ export const RegionTags = ({
17
+ regions,
18
+ projectRegionBox,
19
+ mouseEvents,
20
+ regionClsList,
21
+ regionTagList,
22
+ onBeginRegionEdit,
23
+ onChangeRegion,
24
+ onCloseRegionEdit,
25
+ onDeleteRegion,
26
+ layoutParams,
27
+ imageSrc,
28
+ RegionEditLabel,
29
+ onRegionClassAdded,
30
+ allowComments,
31
+ hideNotEditingLabel,
32
+ allowedGroups
33
+ }) => {
34
+ const RegionLabel =
35
+ RegionEditLabel != null ? RegionEditLabel : DefaultRegionLabel
36
+ return regions
37
+ .filter((r) => r.visible || r.visible === undefined)
38
+ .map((region) => {
39
+ const pbox = projectRegionBox(region)
40
+ const { iw, ih } = layoutParams.current
41
+ let margin = 8
42
+ if (region.highlighted && region.type === "box") margin += 6
43
+ const labelBoxHeight =
44
+ region.editingLabels && !region.locked ? 170 : region.tags ? 60 : 50
45
+ const displayOnTop = pbox.y > labelBoxHeight
46
+
47
+ const coords = displayOnTop
48
+ ? {
49
+ left: pbox.x,
50
+ top: pbox.y - margin / 2,
51
+ }
52
+ : { left: pbox.x, top: pbox.y + pbox.h + margin / 2 }
53
+ if (region.locked) {
54
+ return (
55
+ <div
56
+ key={region.id}
57
+ style={{
58
+ position: "absolute",
59
+ ...coords,
60
+ zIndex: 10 + (region.editingLabels ? 5 : 0),
61
+ }}
62
+ >
63
+ <Paper
64
+ style={{
65
+ position: "absolute",
66
+ left: 0,
67
+ ...(displayOnTop ? { bottom: 0 } : { top: 0 }),
68
+ zIndex: 10,
69
+ backgroundColor: "#fff",
70
+ borderRadius: 4,
71
+ padding: 2,
72
+ paddingBottom: 0,
73
+ opacity: 0.5,
74
+ pointerEvents: "none",
75
+ }}
76
+ >
77
+ <LockIcon style={{ width: 16, height: 16, color: "#333" }} />
78
+ </Paper>
79
+ </div>
80
+ )
81
+ }
82
+ return (
83
+ <div
84
+ key={region.id}
85
+ style={{
86
+ position: "absolute",
87
+ ...coords,
88
+ zIndex: 10 + (region.editingLabels ? 5 : 0),
89
+ width: 200,
90
+ }}
91
+ onMouseDown={(e) => e.preventDefault()}
92
+ onMouseUp={(e) => e.preventDefault()}
93
+ onMouseEnter={(e) => {
94
+ if (region.editingLabels) {
95
+ mouseEvents.onMouseUp(e)
96
+ e.button = 1
97
+ mouseEvents.onMouseUp(e)
98
+ }
99
+ }}
100
+ >
101
+ <div
102
+ style={{
103
+ position: "absolute",
104
+ zIndex: 20,
105
+ left: 0,
106
+ ...(displayOnTop ? { bottom: 0 } : { top: 0 }),
107
+ }}
108
+ {...(!region.editingLabels
109
+ ? copyWithout(mouseEvents, "onMouseDown", "onMouseUp")
110
+ : {})}
111
+ >
112
+ <RegionLabel
113
+ allowedClasses={regionClsList}
114
+ allowedTags={regionTagList}
115
+ onOpen={onBeginRegionEdit}
116
+ onChange={onChangeRegion}
117
+ onClose={onCloseRegionEdit}
118
+ onDelete={onDeleteRegion}
119
+ editing={region.editingLabels}
120
+ region={region}
121
+ regions={regions}
122
+ imageSrc={imageSrc}
123
+ onRegionClassAdded={onRegionClassAdded}
124
+ allowComments={allowComments}
125
+ hideNotEditingLabel={hideNotEditingLabel}
126
+ allowedGroups={allowedGroups}
127
+ />
128
+ </div>
129
+ </div>
130
+ )
131
+ })
132
+ }
133
+
134
+ export default RegionTags