@searpent/react-image-annotate 2.0.3 → 2.0.4

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 (187) 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 -169
  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/GroupSelectorSidebarBox/index.js +0 -63
  139. package/HighlightBox/index.js +0 -99
  140. package/HistorySidebarBox/index.js +0 -71
  141. package/ImageCanvas/index.js +0 -424
  142. package/ImageCanvas/region-tools.js +0 -165
  143. package/ImageCanvas/use-mouse.js +0 -180
  144. package/ImageCanvas/use-project-box.js +0 -27
  145. package/ImageCanvas/use-wasd-mode.js +0 -62
  146. package/ImageMask/index.js +0 -133
  147. package/ImageMask/load-image.js +0 -25
  148. package/ImageSelectorSidebarBox/index.js +0 -60
  149. package/KeyframeTimeline/get-time-string.js +0 -27
  150. package/KeyframeTimeline/index.js +0 -233
  151. package/KeyframesSelectorSidebarBox/index.js +0 -93
  152. package/LandingPage/index.js +0 -159
  153. package/MainLayout/icon-dictionary.js +0 -104
  154. package/MainLayout/index.js +0 -366
  155. package/MainLayout/types.js +0 -0
  156. package/MainLayout/use-implied-video-regions.js +0 -13
  157. package/PointDistances/index.js +0 -73
  158. package/PreventScrollToParents/index.js +0 -51
  159. package/RegionLabel/index.js +0 -191
  160. package/RegionSelectAndTransformBoxes/index.js +0 -167
  161. package/RegionSelectorSidebarBox/index.js +0 -248
  162. package/RegionShapes/index.js +0 -274
  163. package/RegionTags/index.js +0 -138
  164. package/SettingsDialog/index.js +0 -52
  165. package/SettingsProvider/index.js +0 -53
  166. package/Shortcuts/ShortcutField.js +0 -46
  167. package/Shortcuts/index.js +0 -133
  168. package/ShortcutsManager/index.js +0 -155
  169. package/Sidebar/index.js +0 -69
  170. package/SidebarBoxContainer/index.js +0 -93
  171. package/SmallToolButton/index.js +0 -42
  172. package/TagsSidebarBox/index.js +0 -105
  173. package/TaskDescriptionSidebarBox/index.js +0 -58
  174. package/Theme/index.js +0 -30
  175. package/VideoOrImageCanvasBackground/index.js +0 -151
  176. package/colors.js +0 -14
  177. package/hooks/use-event-callback.js +0 -10
  178. package/hooks/use-exclude-pattern.js +0 -24
  179. package/hooks/use-load-image.js +0 -26
  180. package/hooks/use-window-size.js +0 -46
  181. package/index.js +0 -3
  182. package/lib.js +0 -3
  183. package/stories.js +0 -5
  184. package/utils/get-from-local-storage.js +0 -7
  185. package/utils/get-hotkey-help-text.js +0 -9
  186. package/utils/get-landmarks-with-transform.js +0 -40
  187. 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