@searpent/react-image-annotate 2.0.0
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.
- package/.babelrc +6 -0
- package/.env +1 -0
- package/.flowconfig +2 -0
- package/.github/workflows/release-on-master.yml +32 -0
- package/.github/workflows/test.yml +16 -0
- package/.prettierrc +3 -0
- package/.releaserc.js +18 -0
- package/.storybook/addons.js +2 -0
- package/.storybook/config.js +16 -0
- package/LICENSE +21 -0
- package/README.md +101 -0
- package/package.json +93 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +38 -0
- package/src/Annotator/bike-pic.png +0 -0
- package/src/Annotator/bike-pic2.png +0 -0
- package/src/Annotator/dab-keyframes.story.json +1 -0
- package/src/Annotator/index.js +206 -0
- package/src/Annotator/index.story.js +727 -0
- package/src/Annotator/poses.story.js +150 -0
- package/src/Annotator/reducers/combine-reducers.js +7 -0
- package/src/Annotator/reducers/convert-expanding-line-to-polygon.js +53 -0
- package/src/Annotator/reducers/fix-twisted.js +6 -0
- package/src/Annotator/reducers/general-reducer.js +914 -0
- package/src/Annotator/reducers/get-active-image.js +21 -0
- package/src/Annotator/reducers/get-implied-video-regions.js +115 -0
- package/src/Annotator/reducers/history-handler.js +60 -0
- package/src/Annotator/reducers/image-reducer.js +23 -0
- package/src/Annotator/reducers/video-reducer.js +85 -0
- package/src/Annotator/video.story.js +51 -0
- package/src/ClassSelectionMenu/index.js +108 -0
- package/src/Crosshairs/index.js +64 -0
- package/src/DebugSidebarBox/index.js +36 -0
- package/src/DemoSite/Editor.js +235 -0
- package/src/DemoSite/ErrorBoundaryDialog.js +34 -0
- package/src/DemoSite/index.js +41 -0
- package/src/DemoSite/index.story.js +10 -0
- package/src/DemoSite/simple-segmentation-example.json +572 -0
- package/src/FullImageSegmentationAnnotator/hard1.story.jpg +0 -0
- package/src/FullImageSegmentationAnnotator/hard2.story.jpg +0 -0
- package/src/FullImageSegmentationAnnotator/hard3.story.jpg +0 -0
- package/src/FullImageSegmentationAnnotator/index.js +7 -0
- package/src/FullImageSegmentationAnnotator/index.story.js +177 -0
- package/src/FullImageSegmentationAnnotator/orange.story.png +0 -0
- package/src/HighlightBox/index.js +143 -0
- package/src/HistorySidebarBox/index.js +78 -0
- package/src/ImageCanvas/dancing-man.story.jpg +0 -0
- package/src/ImageCanvas/index.js +488 -0
- package/src/ImageCanvas/index.story.js +214 -0
- package/src/ImageCanvas/mouse_mask.story.png +0 -0
- package/src/ImageCanvas/region-tools.js +171 -0
- package/src/ImageCanvas/seves_desk.story.jpg +0 -0
- package/src/ImageCanvas/styles.js +27 -0
- package/src/ImageCanvas/use-mouse.js +168 -0
- package/src/ImageCanvas/use-project-box.js +23 -0
- package/src/ImageCanvas/use-wasd-mode.js +50 -0
- package/src/ImageMask/index.js +127 -0
- package/src/ImageMask/load-image.js +32 -0
- package/src/ImageSelectorSidebarBox/index.js +54 -0
- package/src/KeyframeTimeline/get-time-string.js +25 -0
- package/src/KeyframeTimeline/index.js +223 -0
- package/src/KeyframesSelectorSidebarBox/index.js +93 -0
- package/src/LandingPage/content.md +57 -0
- package/src/LandingPage/github-markdown.css +964 -0
- package/src/LandingPage/index.js +147 -0
- package/src/MainLayout/icon-dictionary.js +79 -0
- package/src/MainLayout/index.js +420 -0
- package/src/MainLayout/index.story.js +240 -0
- package/src/MainLayout/styles.js +26 -0
- package/src/MainLayout/types.js +156 -0
- package/src/MainLayout/use-implied-video-regions.js +17 -0
- package/src/PointDistances/index.js +90 -0
- package/src/PreventScrollToParents/index.js +48 -0
- package/src/PreventScrollToParents/index.story.js +23 -0
- package/src/RegionLabel/index.js +201 -0
- package/src/RegionLabel/styles.js +51 -0
- package/src/RegionSelectAndTransformBoxes/index.js +234 -0
- package/src/RegionSelectorSidebarBox/index.js +216 -0
- package/src/RegionSelectorSidebarBox/styles.js +54 -0
- package/src/RegionShapes/index.js +236 -0
- package/src/RegionTags/index.js +130 -0
- package/src/SettingsDialog/index.js +58 -0
- package/src/SettingsProvider/index.js +44 -0
- package/src/Shortcuts/ShortcutField.js +44 -0
- package/src/Shortcuts/index.js +129 -0
- package/src/ShortcutsManager/index.js +162 -0
- package/src/Sidebar/index.js +117 -0
- package/src/SidebarBoxContainer/index.js +93 -0
- package/src/SmallToolButton/index.js +57 -0
- package/src/TagsSidebarBox/index.js +93 -0
- package/src/TaskDescriptionSidebarBox/index.js +43 -0
- package/src/Theme/index.js +36 -0
- package/src/VideoOrImageCanvasBackground/index.js +170 -0
- package/src/colors.js +32 -0
- package/src/hooks/use-event-callback.js +11 -0
- package/src/hooks/use-exclude-pattern.js +27 -0
- package/src/hooks/use-load-image.js +21 -0
- package/src/hooks/use-window-size.js +46 -0
- package/src/hooks/xpattern.js +1 -0
- package/src/hooks/xpattern.png +0 -0
- package/src/index.js +18 -0
- package/src/lib.js +7 -0
- package/src/screenshot.png +0 -0
- package/src/site.css +5 -0
- package/src/stories.js +2 -0
- package/src/utils/get-from-local-storage.js +7 -0
- package/src/utils/get-hotkey-help-text.js +11 -0
- package/src/utils/get-landmarks-with-transform.js +23 -0
- package/src/utils/set-in-local-storage.js +6 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import React from "react"
|
|
4
|
+
import Button from "@mui/material/Button"
|
|
5
|
+
import { styled } from "@mui/material/styles"
|
|
6
|
+
import { createTheme, ThemeProvider } from "@mui/material/styles"
|
|
7
|
+
import * as colors from "@mui/material/colors"
|
|
8
|
+
import Grid from "@mui/material/Grid"
|
|
9
|
+
import Markdown from "react-markdown"
|
|
10
|
+
import GitHubButton from "react-github-btn"
|
|
11
|
+
import "./github-markdown.css"
|
|
12
|
+
import raw from "raw.macro"
|
|
13
|
+
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
|
|
14
|
+
import { docco } from "react-syntax-highlighter/dist/cjs/languages/hljs/javascript"
|
|
15
|
+
|
|
16
|
+
const contentMd = raw("./content.md")
|
|
17
|
+
const theme = createTheme()
|
|
18
|
+
const RootContainer = styled("div")(({ theme }) => ({
|
|
19
|
+
display: "flex",
|
|
20
|
+
flexDirection: "column",
|
|
21
|
+
alignItems: "center",
|
|
22
|
+
}))
|
|
23
|
+
const ContentContainer = styled("div")(({ theme }) => ({
|
|
24
|
+
width: "100%",
|
|
25
|
+
boxSizing: "border-box",
|
|
26
|
+
display: "flex",
|
|
27
|
+
flexDirection: "column",
|
|
28
|
+
maxWidth: 1200,
|
|
29
|
+
}))
|
|
30
|
+
const Header = styled("div")(({ theme }) => ({
|
|
31
|
+
width: "100%",
|
|
32
|
+
display: "flex",
|
|
33
|
+
justifyContent: "center",
|
|
34
|
+
backgroundColor: colors.blue[600],
|
|
35
|
+
padding: 8,
|
|
36
|
+
boxSizing: "border-box",
|
|
37
|
+
}))
|
|
38
|
+
const HeaderButton = styled(Button)(({ theme }) => ({
|
|
39
|
+
color: "white",
|
|
40
|
+
margin: 8,
|
|
41
|
+
padding: 16,
|
|
42
|
+
paddingLeft: 24,
|
|
43
|
+
paddingRight: 24,
|
|
44
|
+
}))
|
|
45
|
+
const Hero = styled("div")(({ theme }) => ({
|
|
46
|
+
display: "flex",
|
|
47
|
+
justifyContent: "center",
|
|
48
|
+
width: "100%",
|
|
49
|
+
backgroundColor: colors.blue[500],
|
|
50
|
+
padding: 16,
|
|
51
|
+
color: "white",
|
|
52
|
+
boxSizing: "border-box",
|
|
53
|
+
}))
|
|
54
|
+
const HeroMain = styled("div")(({ theme }) => ({
|
|
55
|
+
fontSize: 48,
|
|
56
|
+
fontWeight: "bold",
|
|
57
|
+
paddingTop: 64,
|
|
58
|
+
textShadow: "0px 1px 5px rgba(0,0,0,0.3)",
|
|
59
|
+
}))
|
|
60
|
+
const HeroSub = styled("div")(({ theme }) => ({
|
|
61
|
+
paddingTop: 32,
|
|
62
|
+
lineHeight: 1.5,
|
|
63
|
+
fontSize: 24,
|
|
64
|
+
textShadow: "0px 1px 3px rgba(0,0,0,0.2)",
|
|
65
|
+
}))
|
|
66
|
+
const HeroButtons = styled("div")(({ theme }) => ({
|
|
67
|
+
paddingTop: 32,
|
|
68
|
+
paddingBottom: 48,
|
|
69
|
+
}))
|
|
70
|
+
const Section = styled("div")(({ theme }) => ({
|
|
71
|
+
display: "flex",
|
|
72
|
+
padding: 16,
|
|
73
|
+
paddingTop: 32,
|
|
74
|
+
flexDirection: "column",
|
|
75
|
+
}))
|
|
76
|
+
|
|
77
|
+
const CodeBlock = ({ language, value }) => {
|
|
78
|
+
return (
|
|
79
|
+
<SyntaxHighlighter language={language} style={docco}>
|
|
80
|
+
{value}
|
|
81
|
+
</SyntaxHighlighter>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function flatten(text, child) {
|
|
86
|
+
return typeof child === "string"
|
|
87
|
+
? text + child
|
|
88
|
+
: React.Children.toArray(child.props.children).reduce(flatten, text)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function HeadingRenderer(props) {
|
|
92
|
+
var children = React.Children.toArray(props.children)
|
|
93
|
+
var text = children.reduce(flatten, "")
|
|
94
|
+
var slug = text.toLowerCase().replace(/\W/g, "-")
|
|
95
|
+
return React.createElement("h" + props.level, { id: slug }, props.children)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const LandingPage = () => {
|
|
99
|
+
return (
|
|
100
|
+
<ThemeProvider theme={theme}>
|
|
101
|
+
<RootContainer>
|
|
102
|
+
<Header id="about">
|
|
103
|
+
<ContentContainer style={{ flexDirection: "row", flexGrow: 1 }}>
|
|
104
|
+
<HeaderButton href="#features">Features</HeaderButton>
|
|
105
|
+
<HeaderButton href="#usage">Usage</HeaderButton>
|
|
106
|
+
<HeaderButton href="#props">Props</HeaderButton>
|
|
107
|
+
<HeaderButton href="./demo">Demo Playground</HeaderButton>
|
|
108
|
+
</ContentContainer>
|
|
109
|
+
</Header>
|
|
110
|
+
<Hero>
|
|
111
|
+
<ContentContainer>
|
|
112
|
+
<HeroMain>React Image Annotate</HeroMain>
|
|
113
|
+
<HeroSub>
|
|
114
|
+
Powerful React component for image annotations with bounding
|
|
115
|
+
boxes, tagging, classification, multiple images and polygon
|
|
116
|
+
segmentation.
|
|
117
|
+
</HeroSub>
|
|
118
|
+
<HeroButtons>
|
|
119
|
+
<GitHubButton
|
|
120
|
+
href="https://github.com/waoai/react-image-annotate"
|
|
121
|
+
data-size="large"
|
|
122
|
+
data-show-count="true"
|
|
123
|
+
aria-label="Star waoai/react-image-annotate on GitHub"
|
|
124
|
+
>
|
|
125
|
+
Star
|
|
126
|
+
</GitHubButton>
|
|
127
|
+
</HeroButtons>
|
|
128
|
+
</ContentContainer>
|
|
129
|
+
</Hero>
|
|
130
|
+
<ContentContainer className="markdown-body">
|
|
131
|
+
<Section className="markdown-body">
|
|
132
|
+
<Markdown
|
|
133
|
+
escapeHtml={false}
|
|
134
|
+
source={contentMd}
|
|
135
|
+
renderers={{
|
|
136
|
+
code: CodeBlock,
|
|
137
|
+
heading: HeadingRenderer,
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
140
|
+
</Section>
|
|
141
|
+
</ContentContainer>
|
|
142
|
+
</RootContainer>
|
|
143
|
+
</ThemeProvider>
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export default LandingPage
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import React from "react"
|
|
4
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
|
5
|
+
import {
|
|
6
|
+
faArrowsAlt,
|
|
7
|
+
faMousePointer,
|
|
8
|
+
faExpandArrowsAlt,
|
|
9
|
+
faGripLines,
|
|
10
|
+
faTag,
|
|
11
|
+
faPaintBrush,
|
|
12
|
+
faCrosshairs,
|
|
13
|
+
faDrawPolygon,
|
|
14
|
+
faVectorSquare,
|
|
15
|
+
faHandPaper,
|
|
16
|
+
faSearch,
|
|
17
|
+
faMask,
|
|
18
|
+
faEdit,
|
|
19
|
+
faChartLine,
|
|
20
|
+
} from "@fortawesome/free-solid-svg-icons"
|
|
21
|
+
import FullscreenIcon from "@mui/icons-material/Fullscreen"
|
|
22
|
+
import AccessibilityNewIcon from "@mui/icons-material/AccessibilityNew"
|
|
23
|
+
|
|
24
|
+
const faStyle = { marginTop: 4, width: 16, height: 16, marginBottom: 4 }
|
|
25
|
+
|
|
26
|
+
export const iconDictionary = {
|
|
27
|
+
select: () => (
|
|
28
|
+
<FontAwesomeIcon
|
|
29
|
+
style={faStyle}
|
|
30
|
+
size="xs"
|
|
31
|
+
fixedWidth
|
|
32
|
+
icon={faMousePointer}
|
|
33
|
+
/>
|
|
34
|
+
),
|
|
35
|
+
pan: () => (
|
|
36
|
+
<FontAwesomeIcon style={faStyle} size="xs" fixedWidth icon={faHandPaper} />
|
|
37
|
+
),
|
|
38
|
+
zoom: () => (
|
|
39
|
+
<FontAwesomeIcon style={faStyle} size="xs" fixedWidth icon={faSearch} />
|
|
40
|
+
),
|
|
41
|
+
"show-tags": () => (
|
|
42
|
+
<FontAwesomeIcon style={faStyle} size="xs" fixedWidth icon={faTag} />
|
|
43
|
+
),
|
|
44
|
+
"create-point": () => (
|
|
45
|
+
<FontAwesomeIcon style={faStyle} size="xs" fixedWidth icon={faCrosshairs} />
|
|
46
|
+
),
|
|
47
|
+
"create-box": () => (
|
|
48
|
+
<FontAwesomeIcon
|
|
49
|
+
style={faStyle}
|
|
50
|
+
size="xs"
|
|
51
|
+
fixedWidth
|
|
52
|
+
icon={faVectorSquare}
|
|
53
|
+
/>
|
|
54
|
+
),
|
|
55
|
+
"create-polygon": () => (
|
|
56
|
+
<FontAwesomeIcon
|
|
57
|
+
style={faStyle}
|
|
58
|
+
size="xs"
|
|
59
|
+
fixedWidth
|
|
60
|
+
icon={faDrawPolygon}
|
|
61
|
+
/>
|
|
62
|
+
),
|
|
63
|
+
"create-expanding-line": () => (
|
|
64
|
+
<FontAwesomeIcon style={faStyle} size="xs" fixedWidth icon={faGripLines} />
|
|
65
|
+
),
|
|
66
|
+
"create-line": () => (
|
|
67
|
+
<FontAwesomeIcon style={faStyle} size="xs" fixedWidth icon={faChartLine} />
|
|
68
|
+
),
|
|
69
|
+
"show-mask": () => (
|
|
70
|
+
<FontAwesomeIcon style={faStyle} size="xs" fixedWidth icon={faMask} />
|
|
71
|
+
),
|
|
72
|
+
"modify-allowed-area": () => (
|
|
73
|
+
<FontAwesomeIcon style={faStyle} size="xs" fixedWidth icon={faEdit} />
|
|
74
|
+
),
|
|
75
|
+
"create-keypoints": AccessibilityNewIcon,
|
|
76
|
+
window: FullscreenIcon,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default iconDictionary
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import type { Action, MainLayoutState } from "./types"
|
|
4
|
+
import { FullScreen, useFullScreenHandle } from "react-full-screen"
|
|
5
|
+
import React, { useCallback, useRef } from "react"
|
|
6
|
+
import { makeStyles } from "@mui/styles"
|
|
7
|
+
import { createTheme, ThemeProvider } from "@mui/material/styles"
|
|
8
|
+
import { styled } from "@mui/material/styles"
|
|
9
|
+
|
|
10
|
+
import ClassSelectionMenu from "../ClassSelectionMenu"
|
|
11
|
+
import DebugBox from "../DebugSidebarBox"
|
|
12
|
+
import HistorySidebarBox from "../HistorySidebarBox"
|
|
13
|
+
import ImageCanvas from "../ImageCanvas"
|
|
14
|
+
import ImageSelector from "../ImageSelectorSidebarBox"
|
|
15
|
+
import KeyframeTimeline from "../KeyframeTimeline"
|
|
16
|
+
import KeyframesSelector from "../KeyframesSelectorSidebarBox"
|
|
17
|
+
import type { Node } from "react"
|
|
18
|
+
import RegionSelector from "../RegionSelectorSidebarBox"
|
|
19
|
+
import SettingsDialog from "../SettingsDialog"
|
|
20
|
+
import TagsSidebarBox from "../TagsSidebarBox"
|
|
21
|
+
import TaskDescription from "../TaskDescriptionSidebarBox"
|
|
22
|
+
import Workspace from "react-material-workspace-layout/Workspace"
|
|
23
|
+
import classnames from "classnames"
|
|
24
|
+
import getActiveImage from "../Annotator/reducers/get-active-image"
|
|
25
|
+
import getHotkeyHelpText from "../utils/get-hotkey-help-text"
|
|
26
|
+
import iconDictionary from "./icon-dictionary"
|
|
27
|
+
import styles from "./styles"
|
|
28
|
+
import { useDispatchHotkeyHandlers } from "../ShortcutsManager"
|
|
29
|
+
import useEventCallback from "use-event-callback"
|
|
30
|
+
import useImpliedVideoRegions from "./use-implied-video-regions"
|
|
31
|
+
import useKey from "use-key-hook"
|
|
32
|
+
import { useSettings } from "../SettingsProvider"
|
|
33
|
+
import { withHotKeys } from "react-hotkeys"
|
|
34
|
+
|
|
35
|
+
// import Fullscreen from "../Fullscreen"
|
|
36
|
+
|
|
37
|
+
const emptyArr = []
|
|
38
|
+
const theme = createTheme()
|
|
39
|
+
const useStyles = makeStyles((theme) => styles)
|
|
40
|
+
|
|
41
|
+
const HotkeyDiv = withHotKeys(({ hotKeys, children, divRef, ...props }) => (
|
|
42
|
+
<div {...{ ...hotKeys, ...props }} ref={divRef}>
|
|
43
|
+
{children}
|
|
44
|
+
</div>
|
|
45
|
+
))
|
|
46
|
+
|
|
47
|
+
const FullScreenContainer = styled("div")(({ theme }) => ({
|
|
48
|
+
width: "100%",
|
|
49
|
+
height: "100%",
|
|
50
|
+
"& .fullscreen": {
|
|
51
|
+
width: "100%",
|
|
52
|
+
height: "100%",
|
|
53
|
+
},
|
|
54
|
+
}))
|
|
55
|
+
|
|
56
|
+
type Props = {
|
|
57
|
+
state: MainLayoutState,
|
|
58
|
+
RegionEditLabel?: Node,
|
|
59
|
+
dispatch: (Action) => any,
|
|
60
|
+
alwaysShowNextButton?: boolean,
|
|
61
|
+
alwaysShowPrevButton?: boolean,
|
|
62
|
+
onRegionClassAdded: () => {},
|
|
63
|
+
hideHeader?: boolean,
|
|
64
|
+
hideHeaderText?: boolean,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const MainLayout = ({
|
|
68
|
+
state,
|
|
69
|
+
dispatch,
|
|
70
|
+
alwaysShowNextButton = false,
|
|
71
|
+
alwaysShowPrevButton = false,
|
|
72
|
+
RegionEditLabel,
|
|
73
|
+
onRegionClassAdded,
|
|
74
|
+
hideHeader,
|
|
75
|
+
hideHeaderText,
|
|
76
|
+
hideNext = false,
|
|
77
|
+
hidePrev = false,
|
|
78
|
+
hideClone = false,
|
|
79
|
+
hideSettings = false,
|
|
80
|
+
hideFullScreen = false,
|
|
81
|
+
hideSave = false,
|
|
82
|
+
}: Props) => {
|
|
83
|
+
const classes = useStyles()
|
|
84
|
+
const settings = useSettings()
|
|
85
|
+
const fullScreenHandle = useFullScreenHandle()
|
|
86
|
+
|
|
87
|
+
const memoizedActionFns = useRef({})
|
|
88
|
+
const action = (type: string, ...params: Array<string>) => {
|
|
89
|
+
const fnKey = `${type}(${params.join(",")})`
|
|
90
|
+
if (memoizedActionFns.current[fnKey])
|
|
91
|
+
return memoizedActionFns.current[fnKey]
|
|
92
|
+
|
|
93
|
+
const fn = (...args: any) =>
|
|
94
|
+
params.length > 0
|
|
95
|
+
? dispatch(
|
|
96
|
+
({
|
|
97
|
+
type,
|
|
98
|
+
...params.reduce((acc, p, i) => ((acc[p] = args[i]), acc), {}),
|
|
99
|
+
}: any)
|
|
100
|
+
)
|
|
101
|
+
: dispatch({ type, ...args[0] })
|
|
102
|
+
memoizedActionFns.current[fnKey] = fn
|
|
103
|
+
return fn
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const { currentImageIndex, activeImage } = getActiveImage(state)
|
|
107
|
+
let nextImage
|
|
108
|
+
if (currentImageIndex !== null) {
|
|
109
|
+
nextImage = state.images[currentImageIndex + 1]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
useKey(() => dispatch({ type: "CANCEL" }), {
|
|
113
|
+
detectKeys: [27],
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const isAVideoFrame = activeImage && activeImage.frameTime !== undefined
|
|
117
|
+
const innerContainerRef = useRef()
|
|
118
|
+
const hotkeyHandlers = useDispatchHotkeyHandlers({ dispatch })
|
|
119
|
+
|
|
120
|
+
let impliedVideoRegions = useImpliedVideoRegions(state)
|
|
121
|
+
|
|
122
|
+
const refocusOnMouseEvent = useCallback((e) => {
|
|
123
|
+
if (!innerContainerRef.current) return
|
|
124
|
+
if (innerContainerRef.current.contains(document.activeElement)) return
|
|
125
|
+
if (innerContainerRef.current.contains(e.target)) {
|
|
126
|
+
innerContainerRef.current.focus()
|
|
127
|
+
e.target.focus()
|
|
128
|
+
}
|
|
129
|
+
}, [])
|
|
130
|
+
|
|
131
|
+
const canvas = (
|
|
132
|
+
<ImageCanvas
|
|
133
|
+
{...settings}
|
|
134
|
+
showCrosshairs={
|
|
135
|
+
settings.showCrosshairs &&
|
|
136
|
+
!["select", "pan", "zoom"].includes(state.selectedTool)
|
|
137
|
+
}
|
|
138
|
+
key={state.selectedImage}
|
|
139
|
+
showMask={state.showMask}
|
|
140
|
+
fullImageSegmentationMode={state.fullImageSegmentationMode}
|
|
141
|
+
autoSegmentationOptions={state.autoSegmentationOptions}
|
|
142
|
+
showTags={state.showTags}
|
|
143
|
+
allowedArea={state.allowedArea}
|
|
144
|
+
modifyingAllowedArea={state.selectedTool === "modify-allowed-area"}
|
|
145
|
+
regionClsList={state.regionClsList}
|
|
146
|
+
regionTagList={state.regionTagList}
|
|
147
|
+
regions={
|
|
148
|
+
state.annotationType === "image"
|
|
149
|
+
? activeImage.regions || []
|
|
150
|
+
: impliedVideoRegions
|
|
151
|
+
}
|
|
152
|
+
realSize={activeImage ? activeImage.realSize : undefined}
|
|
153
|
+
videoPlaying={state.videoPlaying}
|
|
154
|
+
imageSrc={state.annotationType === "image" ? activeImage.src : null}
|
|
155
|
+
videoSrc={state.annotationType === "video" ? state.videoSrc : null}
|
|
156
|
+
pointDistancePrecision={state.pointDistancePrecision}
|
|
157
|
+
createWithPrimary={state.selectedTool.includes("create")}
|
|
158
|
+
dragWithPrimary={state.selectedTool === "pan"}
|
|
159
|
+
zoomWithPrimary={state.selectedTool === "zoom"}
|
|
160
|
+
showPointDistances={state.showPointDistances}
|
|
161
|
+
videoTime={
|
|
162
|
+
state.annotationType === "image"
|
|
163
|
+
? state.selectedImageFrameTime
|
|
164
|
+
: state.currentVideoTime
|
|
165
|
+
}
|
|
166
|
+
keypointDefinitions={state.keypointDefinitions}
|
|
167
|
+
onMouseMove={action("MOUSE_MOVE")}
|
|
168
|
+
onMouseDown={action("MOUSE_DOWN")}
|
|
169
|
+
onMouseUp={action("MOUSE_UP")}
|
|
170
|
+
onChangeRegion={action("CHANGE_REGION", "region")}
|
|
171
|
+
onBeginRegionEdit={action("OPEN_REGION_EDITOR", "region")}
|
|
172
|
+
onCloseRegionEdit={action("CLOSE_REGION_EDITOR", "region")}
|
|
173
|
+
onDeleteRegion={action("DELETE_REGION", "region")}
|
|
174
|
+
onBeginBoxTransform={action("BEGIN_BOX_TRANSFORM", "box", "directions")}
|
|
175
|
+
onBeginMovePolygonPoint={action(
|
|
176
|
+
"BEGIN_MOVE_POLYGON_POINT",
|
|
177
|
+
"polygon",
|
|
178
|
+
"pointIndex"
|
|
179
|
+
)}
|
|
180
|
+
onBeginMoveKeypoint={action(
|
|
181
|
+
"BEGIN_MOVE_KEYPOINT",
|
|
182
|
+
"region",
|
|
183
|
+
"keypointId"
|
|
184
|
+
)}
|
|
185
|
+
onAddPolygonPoint={action(
|
|
186
|
+
"ADD_POLYGON_POINT",
|
|
187
|
+
"polygon",
|
|
188
|
+
"point",
|
|
189
|
+
"pointIndex"
|
|
190
|
+
)}
|
|
191
|
+
onSelectRegion={action("SELECT_REGION", "region")}
|
|
192
|
+
onBeginMovePoint={action("BEGIN_MOVE_POINT", "point")}
|
|
193
|
+
onImageLoaded={action("IMAGE_LOADED", "image")}
|
|
194
|
+
RegionEditLabel={RegionEditLabel}
|
|
195
|
+
onImageOrVideoLoaded={action("IMAGE_OR_VIDEO_LOADED", "metadata")}
|
|
196
|
+
onChangeVideoTime={action("CHANGE_VIDEO_TIME", "newTime")}
|
|
197
|
+
onChangeVideoPlaying={action("CHANGE_VIDEO_PLAYING", "isPlaying")}
|
|
198
|
+
onRegionClassAdded={onRegionClassAdded}
|
|
199
|
+
allowComments={state.allowComments}
|
|
200
|
+
/>
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
const onClickIconSidebarItem = useEventCallback((item) => {
|
|
204
|
+
dispatch({ type: "SELECT_TOOL", selectedTool: item.name })
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
const onClickHeaderItem = useEventCallback((item) => {
|
|
208
|
+
if (item.name === "Fullscreen") {
|
|
209
|
+
fullScreenHandle.enter()
|
|
210
|
+
} else if (item.name === "Window") {
|
|
211
|
+
fullScreenHandle.exit()
|
|
212
|
+
}
|
|
213
|
+
dispatch({ type: "HEADER_BUTTON_CLICKED", buttonName: item.name })
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
const debugModeOn = Boolean(window.localStorage.$ANNOTATE_DEBUG_MODE && state)
|
|
217
|
+
const nextImageHasRegions =
|
|
218
|
+
!nextImage || (nextImage.regions && nextImage.regions.length > 0)
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<ThemeProvider theme={theme}>
|
|
222
|
+
<FullScreenContainer>
|
|
223
|
+
<FullScreen
|
|
224
|
+
handle={fullScreenHandle}
|
|
225
|
+
onChange={(open) => {
|
|
226
|
+
if (!open) {
|
|
227
|
+
fullScreenHandle.exit()
|
|
228
|
+
action("HEADER_BUTTON_CLICKED", "buttonName")("Window")
|
|
229
|
+
}
|
|
230
|
+
}}
|
|
231
|
+
>
|
|
232
|
+
<HotkeyDiv
|
|
233
|
+
tabIndex={-1}
|
|
234
|
+
divRef={innerContainerRef}
|
|
235
|
+
onMouseDown={refocusOnMouseEvent}
|
|
236
|
+
onMouseOver={refocusOnMouseEvent}
|
|
237
|
+
allowChanges
|
|
238
|
+
handlers={hotkeyHandlers}
|
|
239
|
+
className={classnames(
|
|
240
|
+
classes.container,
|
|
241
|
+
state.fullScreen && "Fullscreen"
|
|
242
|
+
)}
|
|
243
|
+
>
|
|
244
|
+
<Workspace
|
|
245
|
+
allowFullscreen
|
|
246
|
+
iconDictionary={iconDictionary}
|
|
247
|
+
hideHeader={hideHeader}
|
|
248
|
+
hideHeaderText={hideHeaderText}
|
|
249
|
+
headerLeftSide={[
|
|
250
|
+
state.annotationType === "video" ? (
|
|
251
|
+
<KeyframeTimeline
|
|
252
|
+
currentTime={state.currentVideoTime}
|
|
253
|
+
duration={state.videoDuration}
|
|
254
|
+
onChangeCurrentTime={action("CHANGE_VIDEO_TIME", "newTime")}
|
|
255
|
+
keyframes={state.keyframes}
|
|
256
|
+
/>
|
|
257
|
+
) : activeImage ? (
|
|
258
|
+
<div className={classes.headerTitle}>{activeImage.name}</div>
|
|
259
|
+
) : null,
|
|
260
|
+
].filter(Boolean)}
|
|
261
|
+
headerItems={[
|
|
262
|
+
!hidePrev && { name: "Prev" },
|
|
263
|
+
!hideNext && { name: "Next" },
|
|
264
|
+
state.annotationType !== "video"
|
|
265
|
+
? null
|
|
266
|
+
: !state.videoPlaying
|
|
267
|
+
? { name: "Play" }
|
|
268
|
+
: { name: "Pause" },
|
|
269
|
+
!hideClone &&
|
|
270
|
+
!nextImageHasRegions &&
|
|
271
|
+
activeImage.regions && { name: "Clone" },
|
|
272
|
+
!hideSettings && { name: "Settings" },
|
|
273
|
+
!hideFullScreen &&
|
|
274
|
+
(state.fullScreen
|
|
275
|
+
? { name: "Window" }
|
|
276
|
+
: { name: "Fullscreen" }),
|
|
277
|
+
!hideSave && { name: "Save" },
|
|
278
|
+
].filter(Boolean)}
|
|
279
|
+
onClickHeaderItem={onClickHeaderItem}
|
|
280
|
+
onClickIconSidebarItem={onClickIconSidebarItem}
|
|
281
|
+
selectedTools={[
|
|
282
|
+
state.selectedTool,
|
|
283
|
+
state.showTags && "show-tags",
|
|
284
|
+
state.showMask && "show-mask",
|
|
285
|
+
].filter(Boolean)}
|
|
286
|
+
iconSidebarItems={[
|
|
287
|
+
{
|
|
288
|
+
name: "select",
|
|
289
|
+
helperText: "Select" + getHotkeyHelpText("select_tool"),
|
|
290
|
+
alwaysShowing: true,
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
name: "pan",
|
|
294
|
+
helperText:
|
|
295
|
+
"Drag/Pan (right or middle click)" +
|
|
296
|
+
getHotkeyHelpText("pan_tool"),
|
|
297
|
+
alwaysShowing: true,
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: "zoom",
|
|
301
|
+
helperText:
|
|
302
|
+
"Zoom In/Out (scroll)" + getHotkeyHelpText("zoom_tool"),
|
|
303
|
+
alwaysShowing: true,
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
name: "show-tags",
|
|
307
|
+
helperText: "Show / Hide Tags",
|
|
308
|
+
alwaysShowing: true,
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: "create-point",
|
|
312
|
+
helperText: "Add Point" + getHotkeyHelpText("create_point"),
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: "create-box",
|
|
316
|
+
helperText:
|
|
317
|
+
"Add Bounding Box" +
|
|
318
|
+
getHotkeyHelpText("create_bounding_box"),
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
name: "create-polygon",
|
|
322
|
+
helperText:
|
|
323
|
+
"Add Polygon" + getHotkeyHelpText("create_polygon"),
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: "create-line",
|
|
327
|
+
helperText: "Add Line",
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: "create-expanding-line",
|
|
331
|
+
helperText: "Add Expanding Line",
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: "create-keypoints",
|
|
335
|
+
helperText: "Add Keypoints (Pose)",
|
|
336
|
+
},
|
|
337
|
+
state.fullImageSegmentationMode && {
|
|
338
|
+
name: "show-mask",
|
|
339
|
+
alwaysShowing: true,
|
|
340
|
+
helperText: "Show / Hide Mask",
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "modify-allowed-area",
|
|
344
|
+
helperText: "Modify Allowed Area",
|
|
345
|
+
},
|
|
346
|
+
]
|
|
347
|
+
.filter(Boolean)
|
|
348
|
+
.filter(
|
|
349
|
+
(a) => a.alwaysShowing || state.enabledTools.includes(a.name)
|
|
350
|
+
)}
|
|
351
|
+
rightSidebarItems={[
|
|
352
|
+
debugModeOn && (
|
|
353
|
+
<DebugBox state={debugModeOn} lastAction={state.lastAction} />
|
|
354
|
+
),
|
|
355
|
+
state.taskDescription && (
|
|
356
|
+
<TaskDescription description={state.taskDescription} />
|
|
357
|
+
),
|
|
358
|
+
state.regionClsList && (
|
|
359
|
+
<ClassSelectionMenu
|
|
360
|
+
selectedCls={state.selectedCls}
|
|
361
|
+
regionClsList={state.regionClsList}
|
|
362
|
+
onSelectCls={action("SELECT_CLASSIFICATION", "cls")}
|
|
363
|
+
/>
|
|
364
|
+
),
|
|
365
|
+
state.labelImages && (
|
|
366
|
+
<TagsSidebarBox
|
|
367
|
+
currentImage={activeImage}
|
|
368
|
+
imageClsList={state.imageClsList}
|
|
369
|
+
imageTagList={state.imageTagList}
|
|
370
|
+
onChangeImage={action("CHANGE_IMAGE", "delta")}
|
|
371
|
+
expandedByDefault
|
|
372
|
+
/>
|
|
373
|
+
),
|
|
374
|
+
// (state.images?.length || 0) > 1 && (
|
|
375
|
+
// <ImageSelector
|
|
376
|
+
// onSelect={action("SELECT_REGION", "region")}
|
|
377
|
+
// images={state.images}
|
|
378
|
+
// />
|
|
379
|
+
// ),
|
|
380
|
+
<RegionSelector
|
|
381
|
+
regions={activeImage ? activeImage.regions : emptyArr}
|
|
382
|
+
onSelectRegion={action("SELECT_REGION", "region")}
|
|
383
|
+
onDeleteRegion={action("DELETE_REGION", "region")}
|
|
384
|
+
onChangeRegion={action("CHANGE_REGION", "region")}
|
|
385
|
+
/>,
|
|
386
|
+
state.keyframes && (
|
|
387
|
+
<KeyframesSelector
|
|
388
|
+
onChangeVideoTime={action("CHANGE_VIDEO_TIME", "newTime")}
|
|
389
|
+
onDeleteKeyframe={action("DELETE_KEYFRAME", "time")}
|
|
390
|
+
onChangeCurrentTime={action("CHANGE_VIDEO_TIME", "newTime")}
|
|
391
|
+
currentTime={state.currentVideoTime}
|
|
392
|
+
duration={state.videoDuration}
|
|
393
|
+
keyframes={state.keyframes}
|
|
394
|
+
/>
|
|
395
|
+
),
|
|
396
|
+
<HistorySidebarBox
|
|
397
|
+
history={state.history}
|
|
398
|
+
onRestoreHistory={action("RESTORE_HISTORY")}
|
|
399
|
+
/>,
|
|
400
|
+
].filter(Boolean)}
|
|
401
|
+
>
|
|
402
|
+
{canvas}
|
|
403
|
+
</Workspace>
|
|
404
|
+
<SettingsDialog
|
|
405
|
+
open={state.settingsOpen}
|
|
406
|
+
onClose={() =>
|
|
407
|
+
dispatch({
|
|
408
|
+
type: "HEADER_BUTTON_CLICKED",
|
|
409
|
+
buttonName: "Settings",
|
|
410
|
+
})
|
|
411
|
+
}
|
|
412
|
+
/>
|
|
413
|
+
</HotkeyDiv>
|
|
414
|
+
</FullScreen>
|
|
415
|
+
</FullScreenContainer>
|
|
416
|
+
</ThemeProvider>
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export default MainLayout
|