react-image-annotate-master-custom 0.0.1
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/Annotator/index.js +164 -0
- package/Annotator/reducers/combine-reducers.js +14 -0
- package/Annotator/reducers/convert-expanding-line-to-polygon.js +73 -0
- package/Annotator/reducers/fix-twisted.js +4 -0
- package/Annotator/reducers/general-reducer.js +1073 -0
- package/Annotator/reducers/get-active-image.js +27 -0
- package/Annotator/reducers/get-implied-video-regions.js +180 -0
- package/Annotator/reducers/history-handler.js +38 -0
- package/Annotator/reducers/image-reducer.js +20 -0
- package/Annotator/reducers/video-reducer.js +88 -0
- package/ClassSelectionMenu/index.js +135 -0
- package/Crosshairs/index.js +53 -0
- package/DebugSidebarBox/index.js +20 -0
- package/DemoSite/Editor.js +194 -0
- package/DemoSite/ErrorBoundaryDialog.js +64 -0
- package/DemoSite/index.js +69 -0
- package/FullImageSegmentationAnnotator/index.js +7 -0
- package/HighlightBox/index.js +105 -0
- package/HistorySidebarBox/index.js +71 -0
- package/ImageCanvas/index.js +428 -0
- package/ImageCanvas/region-tools.js +165 -0
- package/ImageCanvas/styles.js +31 -0
- package/ImageCanvas/use-mouse.js +180 -0
- package/ImageCanvas/use-project-box.js +27 -0
- package/ImageCanvas/use-wasd-mode.js +62 -0
- package/ImageMask/index.js +133 -0
- package/ImageMask/load-image.js +25 -0
- package/ImageSelectorSidebarBox/index.js +60 -0
- package/KeyframeTimeline/get-time-string.js +27 -0
- package/KeyframeTimeline/index.js +233 -0
- package/KeyframesSelectorSidebarBox/index.js +93 -0
- package/LandingPage/index.js +159 -0
- package/MainLayout/icon-dictionary.js +104 -0
- package/MainLayout/index.js +359 -0
- package/MainLayout/styles.js +25 -0
- package/MainLayout/types.js +0 -0
- package/MainLayout/use-implied-video-regions.js +13 -0
- package/PointDistances/index.js +73 -0
- package/PreventScrollToParents/index.js +51 -0
- package/README.md +101 -0
- package/RegionLabel/index.js +197 -0
- package/RegionLabel/styles.js +48 -0
- package/RegionSelectAndTransformBoxes/index.js +166 -0
- package/RegionSelectorSidebarBox/index.js +248 -0
- package/RegionSelectorSidebarBox/styles.js +53 -0
- package/RegionShapes/index.js +275 -0
- package/RegionTags/index.js +138 -0
- package/SettingsDialog/index.js +52 -0
- package/SettingsProvider/index.js +53 -0
- package/Shortcuts/ShortcutField.js +46 -0
- package/Shortcuts/index.js +133 -0
- package/ShortcutsManager/index.js +155 -0
- package/Sidebar/index.js +69 -0
- package/SidebarBoxContainer/index.js +93 -0
- package/SmallToolButton/index.js +42 -0
- package/TagsSidebarBox/index.js +105 -0
- package/TaskDescriptionSidebarBox/index.js +58 -0
- package/Theme/index.js +30 -0
- package/VideoOrImageCanvasBackground/index.js +151 -0
- package/colors.js +14 -0
- package/hooks/use-event-callback.js +10 -0
- package/hooks/use-exclude-pattern.js +24 -0
- package/hooks/use-load-image.js +26 -0
- package/hooks/use-window-size.js +46 -0
- package/hooks/xpattern.js +1 -0
- package/index.js +3 -0
- package/lib.js +3 -0
- package/package.json +91 -0
- package/stories.js +5 -0
- package/utils/get-from-local-storage.js +7 -0
- package/utils/get-hotkey-help-text.js +9 -0
- package/utils/get-landmarks-with-transform.js +40 -0
- package/utils/set-in-local-storage.js +3 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread";
|
|
2
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
|
|
3
|
+
import { FullScreen, useFullScreenHandle } from "react-full-screen";
|
|
4
|
+
import React, { useCallback, useRef } from "react";
|
|
5
|
+
import { makeStyles } from "@mui/styles";
|
|
6
|
+
import { createTheme, ThemeProvider } from "@mui/material/styles";
|
|
7
|
+
import { styled } from "@mui/material/styles";
|
|
8
|
+
import ClassSelectionMenu from "../ClassSelectionMenu";
|
|
9
|
+
import DebugBox from "../DebugSidebarBox";
|
|
10
|
+
import HistorySidebarBox from "../HistorySidebarBox";
|
|
11
|
+
import ImageCanvas from "../ImageCanvas";
|
|
12
|
+
import ImageSelector from "../ImageSelectorSidebarBox";
|
|
13
|
+
import KeyframeTimeline from "../KeyframeTimeline";
|
|
14
|
+
import KeyframesSelector from "../KeyframesSelectorSidebarBox";
|
|
15
|
+
import RegionSelector from "../RegionSelectorSidebarBox";
|
|
16
|
+
import SettingsDialog from "../SettingsDialog";
|
|
17
|
+
import TagsSidebarBox from "../TagsSidebarBox";
|
|
18
|
+
import TaskDescription from "../TaskDescriptionSidebarBox";
|
|
19
|
+
import Workspace from "react-material-workspace-layout/Workspace";
|
|
20
|
+
import classnames from "classnames";
|
|
21
|
+
import getActiveImage from "../Annotator/reducers/get-active-image";
|
|
22
|
+
import getHotkeyHelpText from "../utils/get-hotkey-help-text";
|
|
23
|
+
import iconDictionary from "./icon-dictionary";
|
|
24
|
+
import styles from "./styles";
|
|
25
|
+
import { useDispatchHotkeyHandlers } from "../ShortcutsManager";
|
|
26
|
+
import useEventCallback from "use-event-callback";
|
|
27
|
+
import useImpliedVideoRegions from "./use-implied-video-regions";
|
|
28
|
+
import useKey from "use-key-hook";
|
|
29
|
+
import { useSettings } from "../SettingsProvider";
|
|
30
|
+
import { withHotKeys } from "react-hotkeys"; // import Fullscreen from "../Fullscreen"
|
|
31
|
+
|
|
32
|
+
var emptyArr = [];
|
|
33
|
+
var theme = createTheme();
|
|
34
|
+
var useStyles = makeStyles(function (theme) {
|
|
35
|
+
return styles;
|
|
36
|
+
});
|
|
37
|
+
var HotkeyDiv = withHotKeys(function (_ref) {
|
|
38
|
+
var hotKeys = _ref.hotKeys,
|
|
39
|
+
children = _ref.children,
|
|
40
|
+
divRef = _ref.divRef,
|
|
41
|
+
props = _objectWithoutProperties(_ref, ["hotKeys", "children", "divRef"]);
|
|
42
|
+
|
|
43
|
+
return React.createElement("div", Object.assign({}, _objectSpread({}, hotKeys, props), {
|
|
44
|
+
ref: divRef
|
|
45
|
+
}), children);
|
|
46
|
+
});
|
|
47
|
+
var FullScreenContainer = styled("div")(function (_ref2) {
|
|
48
|
+
var theme = _ref2.theme;
|
|
49
|
+
return {
|
|
50
|
+
width: "100%",
|
|
51
|
+
height: "100%",
|
|
52
|
+
"& .fullscreen": {
|
|
53
|
+
width: "100%",
|
|
54
|
+
height: "100%"
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
export var MainLayout = function MainLayout(_ref3) {
|
|
59
|
+
var state = _ref3.state,
|
|
60
|
+
dispatch = _ref3.dispatch,
|
|
61
|
+
_ref3$alwaysShowNextB = _ref3.alwaysShowNextButton,
|
|
62
|
+
alwaysShowNextButton = _ref3$alwaysShowNextB === void 0 ? false : _ref3$alwaysShowNextB,
|
|
63
|
+
_ref3$alwaysShowPrevB = _ref3.alwaysShowPrevButton,
|
|
64
|
+
alwaysShowPrevButton = _ref3$alwaysShowPrevB === void 0 ? false : _ref3$alwaysShowPrevB,
|
|
65
|
+
RegionEditLabel = _ref3.RegionEditLabel,
|
|
66
|
+
onRegionClassAdded = _ref3.onRegionClassAdded,
|
|
67
|
+
hideHeader = _ref3.hideHeader,
|
|
68
|
+
hideHeaderText = _ref3.hideHeaderText,
|
|
69
|
+
_ref3$hideNext = _ref3.hideNext,
|
|
70
|
+
hideNext = _ref3$hideNext === void 0 ? false : _ref3$hideNext,
|
|
71
|
+
_ref3$hidePrev = _ref3.hidePrev,
|
|
72
|
+
hidePrev = _ref3$hidePrev === void 0 ? false : _ref3$hidePrev,
|
|
73
|
+
_ref3$hideClone = _ref3.hideClone,
|
|
74
|
+
hideClone = _ref3$hideClone === void 0 ? false : _ref3$hideClone,
|
|
75
|
+
_ref3$hideRegionLabel = _ref3.hideRegionLabel,
|
|
76
|
+
hideRegionLabel = _ref3$hideRegionLabel === void 0 ? false : _ref3$hideRegionLabel,
|
|
77
|
+
_ref3$hideSettings = _ref3.hideSettings,
|
|
78
|
+
hideSettings = _ref3$hideSettings === void 0 ? false : _ref3$hideSettings,
|
|
79
|
+
_ref3$hideFullScreen = _ref3.hideFullScreen,
|
|
80
|
+
hideFullScreen = _ref3$hideFullScreen === void 0 ? false : _ref3$hideFullScreen,
|
|
81
|
+
_ref3$hideSave = _ref3.hideSave,
|
|
82
|
+
hideSave = _ref3$hideSave === void 0 ? false : _ref3$hideSave,
|
|
83
|
+
_ref3$hideRightSideba = _ref3.hideRightSidebar,
|
|
84
|
+
hideRightSidebar = _ref3$hideRightSideba === void 0 ? false : _ref3$hideRightSideba,
|
|
85
|
+
_ref3$hideLeftSidebar = _ref3.hideLeftSidebar,
|
|
86
|
+
hideLeftSidebar = _ref3$hideLeftSidebar === void 0 ? false : _ref3$hideLeftSidebar;
|
|
87
|
+
var classes = useStyles();
|
|
88
|
+
var settings = useSettings();
|
|
89
|
+
var fullScreenHandle = useFullScreenHandle();
|
|
90
|
+
var memoizedActionFns = useRef({});
|
|
91
|
+
|
|
92
|
+
var action = function action(type) {
|
|
93
|
+
for (var _len = arguments.length, params = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
94
|
+
params[_key - 1] = arguments[_key];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
var fnKey = "".concat(type, "(").concat(params.join(","), ")");
|
|
98
|
+
if (memoizedActionFns.current[fnKey]) return memoizedActionFns.current[fnKey];
|
|
99
|
+
|
|
100
|
+
var fn = function fn() {
|
|
101
|
+
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
102
|
+
args[_key2] = arguments[_key2];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return params.length > 0 ? dispatch(_objectSpread({
|
|
106
|
+
type: type
|
|
107
|
+
}, params.reduce(function (acc, p, i) {
|
|
108
|
+
return acc[p] = args[i], acc;
|
|
109
|
+
}, {}))) : dispatch(_objectSpread({
|
|
110
|
+
type: type
|
|
111
|
+
}, args[0]));
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
memoizedActionFns.current[fnKey] = fn;
|
|
115
|
+
return fn;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
var _getActiveImage = getActiveImage(state),
|
|
119
|
+
currentImageIndex = _getActiveImage.currentImageIndex,
|
|
120
|
+
activeImage = _getActiveImage.activeImage;
|
|
121
|
+
|
|
122
|
+
var nextImage;
|
|
123
|
+
|
|
124
|
+
if (currentImageIndex !== null) {
|
|
125
|
+
nextImage = state.images[currentImageIndex + 1];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
useKey(function () {
|
|
129
|
+
return dispatch({
|
|
130
|
+
type: "CANCEL"
|
|
131
|
+
});
|
|
132
|
+
}, {
|
|
133
|
+
detectKeys: [27]
|
|
134
|
+
});
|
|
135
|
+
var isAVideoFrame = activeImage && activeImage.frameTime !== undefined;
|
|
136
|
+
var innerContainerRef = useRef();
|
|
137
|
+
var hotkeyHandlers = useDispatchHotkeyHandlers({
|
|
138
|
+
dispatch: dispatch
|
|
139
|
+
});
|
|
140
|
+
var impliedVideoRegions = useImpliedVideoRegions(state);
|
|
141
|
+
var refocusOnMouseEvent = useCallback(function (e) {
|
|
142
|
+
if (!innerContainerRef.current) return;
|
|
143
|
+
if (innerContainerRef.current.contains(document.activeElement)) return;
|
|
144
|
+
|
|
145
|
+
if (innerContainerRef.current.contains(e.target)) {
|
|
146
|
+
innerContainerRef.current.focus();
|
|
147
|
+
e.target.focus();
|
|
148
|
+
}
|
|
149
|
+
}, []);
|
|
150
|
+
var canvas = React.createElement(ImageCanvas, Object.assign({}, settings, {
|
|
151
|
+
showCrosshairs: settings.showCrosshairs && !["select", "pan", "zoom"].includes(state.selectedTool),
|
|
152
|
+
key: state.selectedImage,
|
|
153
|
+
showMask: state.showMask,
|
|
154
|
+
fullImageSegmentationMode: state.fullImageSegmentationMode,
|
|
155
|
+
autoSegmentationOptions: state.autoSegmentationOptions,
|
|
156
|
+
showTags: state.showTags,
|
|
157
|
+
allowedArea: state.allowedArea,
|
|
158
|
+
hideRegionLabel: hideRegionLabel,
|
|
159
|
+
modifyingAllowedArea: state.selectedTool === "modify-allowed-area",
|
|
160
|
+
regionClsList: state.regionClsList,
|
|
161
|
+
regionTagList: state.regionTagList,
|
|
162
|
+
regions: state.annotationType === "image" ? activeImage.regions || [] : impliedVideoRegions,
|
|
163
|
+
realSize: activeImage ? activeImage.realSize : undefined,
|
|
164
|
+
videoPlaying: state.videoPlaying,
|
|
165
|
+
imageSrc: state.annotationType === "image" ? activeImage.src : null,
|
|
166
|
+
videoSrc: state.annotationType === "video" ? state.videoSrc : null,
|
|
167
|
+
pointDistancePrecision: state.pointDistancePrecision,
|
|
168
|
+
createWithPrimary: state.selectedTool.includes("create"),
|
|
169
|
+
dragWithPrimary: state.selectedTool === "pan",
|
|
170
|
+
zoomWithPrimary: state.selectedTool === "zoom",
|
|
171
|
+
showPointDistances: state.showPointDistances,
|
|
172
|
+
videoTime: state.annotationType === "image" ? state.selectedImageFrameTime : state.currentVideoTime,
|
|
173
|
+
keypointDefinitions: state.keypointDefinitions,
|
|
174
|
+
onMouseMove: action("MOUSE_MOVE"),
|
|
175
|
+
onMouseDown: action("MOUSE_DOWN"),
|
|
176
|
+
onMouseUp: action("MOUSE_UP"),
|
|
177
|
+
onChangeRegion: action("CHANGE_REGION", "region"),
|
|
178
|
+
onBeginRegionEdit: action("OPEN_REGION_EDITOR", "region"),
|
|
179
|
+
onCloseRegionEdit: action("CLOSE_REGION_EDITOR", "region"),
|
|
180
|
+
onDeleteRegion: action("DELETE_REGION", "region"),
|
|
181
|
+
onBeginBoxTransform: action("BEGIN_BOX_TRANSFORM", "box", "directions"),
|
|
182
|
+
onBeginMovePolygonPoint: action("BEGIN_MOVE_POLYGON_POINT", "polygon", "pointIndex"),
|
|
183
|
+
onBeginMoveKeypoint: action("BEGIN_MOVE_KEYPOINT", "region", "keypointId"),
|
|
184
|
+
onAddPolygonPoint: action("ADD_POLYGON_POINT", "polygon", "point", "pointIndex"),
|
|
185
|
+
onSelectRegion: action("SELECT_REGION", "region"),
|
|
186
|
+
onBeginMovePoint: action("BEGIN_MOVE_POINT", "point"),
|
|
187
|
+
onImageLoaded: action("IMAGE_LOADED", "image"),
|
|
188
|
+
RegionEditLabel: RegionEditLabel,
|
|
189
|
+
onImageOrVideoLoaded: action("IMAGE_OR_VIDEO_LOADED", "metadata"),
|
|
190
|
+
onChangeVideoTime: action("CHANGE_VIDEO_TIME", "newTime"),
|
|
191
|
+
onChangeVideoPlaying: action("CHANGE_VIDEO_PLAYING", "isPlaying"),
|
|
192
|
+
onRegionClassAdded: onRegionClassAdded,
|
|
193
|
+
allowComments: state.allowComments
|
|
194
|
+
}));
|
|
195
|
+
var onClickIconSidebarItem = useEventCallback(function (item) {
|
|
196
|
+
dispatch({
|
|
197
|
+
type: "SELECT_TOOL",
|
|
198
|
+
selectedTool: item.name
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
var onClickHeaderItem = useEventCallback(function (item) {
|
|
202
|
+
if (item.name === "Fullscreen") {
|
|
203
|
+
fullScreenHandle.enter();
|
|
204
|
+
} else if (item.name === "Window") {
|
|
205
|
+
fullScreenHandle.exit();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
dispatch({
|
|
209
|
+
type: "HEADER_BUTTON_CLICKED",
|
|
210
|
+
buttonName: item.name
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
var debugModeOn = Boolean(window.localStorage.$ANNOTATE_DEBUG_MODE && state);
|
|
214
|
+
var nextImageHasRegions = !nextImage || nextImage.regions && nextImage.regions.length > 0;
|
|
215
|
+
return React.createElement(ThemeProvider, {
|
|
216
|
+
theme: theme
|
|
217
|
+
}, React.createElement(FullScreenContainer, null, React.createElement(FullScreen, {
|
|
218
|
+
handle: fullScreenHandle,
|
|
219
|
+
onChange: function onChange(open) {
|
|
220
|
+
if (!open) {
|
|
221
|
+
fullScreenHandle.exit();
|
|
222
|
+
action("HEADER_BUTTON_CLICKED", "buttonName")("Window");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}, React.createElement(HotkeyDiv, {
|
|
226
|
+
tabIndex: -1,
|
|
227
|
+
divRef: innerContainerRef,
|
|
228
|
+
onMouseDown: refocusOnMouseEvent,
|
|
229
|
+
onMouseOver: refocusOnMouseEvent,
|
|
230
|
+
allowChanges: true,
|
|
231
|
+
handlers: hotkeyHandlers,
|
|
232
|
+
className: classnames(classes.container, state.fullScreen && "Fullscreen")
|
|
233
|
+
}, React.createElement(Workspace, {
|
|
234
|
+
allowFullscreen: true,
|
|
235
|
+
iconDictionary: iconDictionary,
|
|
236
|
+
hideHeader: hideHeader,
|
|
237
|
+
hideHeaderText: hideHeaderText,
|
|
238
|
+
headerLeftSide: [state.annotationType === "video" ? React.createElement(KeyframeTimeline, {
|
|
239
|
+
currentTime: state.currentVideoTime,
|
|
240
|
+
duration: state.videoDuration,
|
|
241
|
+
onChangeCurrentTime: action("CHANGE_VIDEO_TIME", "newTime"),
|
|
242
|
+
keyframes: state.keyframes
|
|
243
|
+
}) : activeImage ? React.createElement("div", {
|
|
244
|
+
className: classes.headerTitle
|
|
245
|
+
}, activeImage.name) : null].filter(Boolean),
|
|
246
|
+
headerItems: [!hidePrev && {
|
|
247
|
+
name: "Prev"
|
|
248
|
+
}, !hideNext && {
|
|
249
|
+
name: "Next"
|
|
250
|
+
}, state.annotationType !== "video" ? null : !state.videoPlaying ? {
|
|
251
|
+
name: "Play"
|
|
252
|
+
} : {
|
|
253
|
+
name: "Pause"
|
|
254
|
+
}, !hideClone && !nextImageHasRegions && activeImage.regions && {
|
|
255
|
+
name: "Clone"
|
|
256
|
+
}, !hideSettings && {
|
|
257
|
+
name: "Settings"
|
|
258
|
+
}, !hideFullScreen && (state.fullScreen ? {
|
|
259
|
+
name: "Window"
|
|
260
|
+
} : {
|
|
261
|
+
name: "Fullscreen"
|
|
262
|
+
}), !hideSave && {
|
|
263
|
+
name: "Save"
|
|
264
|
+
}].filter(Boolean),
|
|
265
|
+
onClickHeaderItem: onClickHeaderItem,
|
|
266
|
+
onClickIconSidebarItem: onClickIconSidebarItem,
|
|
267
|
+
selectedTools: [state.selectedTool, state.showTags && "show-tags", state.showMask && "show-mask"].filter(Boolean),
|
|
268
|
+
iconSidebarItems: hideLeftSidebar ? [] : [{
|
|
269
|
+
name: "select",
|
|
270
|
+
helperText: "Select" + getHotkeyHelpText("select_tool"),
|
|
271
|
+
alwaysShowing: true
|
|
272
|
+
}, {
|
|
273
|
+
name: "pan",
|
|
274
|
+
helperText: "Drag/Pan (right or middle click)" + getHotkeyHelpText("pan_tool"),
|
|
275
|
+
alwaysShowing: true
|
|
276
|
+
}, {
|
|
277
|
+
name: "zoom",
|
|
278
|
+
helperText: "Zoom In/Out (scroll)" + getHotkeyHelpText("zoom_tool"),
|
|
279
|
+
alwaysShowing: false
|
|
280
|
+
}, {
|
|
281
|
+
name: "show-tags",
|
|
282
|
+
helperText: "Show / Hide Tags",
|
|
283
|
+
alwaysShowing: false
|
|
284
|
+
}, {
|
|
285
|
+
name: "create-point",
|
|
286
|
+
helperText: "Add Point" + getHotkeyHelpText("create_point")
|
|
287
|
+
}, {
|
|
288
|
+
name: "create-box",
|
|
289
|
+
helperText: "Add Bounding Box" + getHotkeyHelpText("create_bounding_box")
|
|
290
|
+
}, {
|
|
291
|
+
name: "create-polygon",
|
|
292
|
+
helperText: "Add Polygon" + getHotkeyHelpText("create_polygon")
|
|
293
|
+
}, {
|
|
294
|
+
name: "create-line",
|
|
295
|
+
helperText: "Add Line"
|
|
296
|
+
}, {
|
|
297
|
+
name: "create-expanding-line",
|
|
298
|
+
helperText: "Add Expanding Line"
|
|
299
|
+
}, {
|
|
300
|
+
name: "create-keypoints",
|
|
301
|
+
helperText: "Add Keypoints (Pose)"
|
|
302
|
+
}, state.fullImageSegmentationMode && {
|
|
303
|
+
name: "show-mask",
|
|
304
|
+
alwaysShowing: true,
|
|
305
|
+
helperText: "Show / Hide Mask"
|
|
306
|
+
}, {
|
|
307
|
+
name: "modify-allowed-area",
|
|
308
|
+
helperText: "Modify Allowed Area"
|
|
309
|
+
}].filter(Boolean).filter(function (a) {
|
|
310
|
+
return a.alwaysShowing || state.enabledTools.includes(a.name);
|
|
311
|
+
}),
|
|
312
|
+
rightSidebarItems: hideRightSidebar ? [] : [debugModeOn && React.createElement(DebugBox, {
|
|
313
|
+
state: debugModeOn,
|
|
314
|
+
lastAction: state.lastAction
|
|
315
|
+
}), state.taskDescription && React.createElement(TaskDescription, {
|
|
316
|
+
description: state.taskDescription
|
|
317
|
+
}), state.regionClsList && React.createElement(ClassSelectionMenu, {
|
|
318
|
+
selectedCls: state.selectedCls,
|
|
319
|
+
regionClsList: state.regionClsList,
|
|
320
|
+
onSelectCls: action("SELECT_CLASSIFICATION", "cls")
|
|
321
|
+
}), state.labelImages && React.createElement(TagsSidebarBox, {
|
|
322
|
+
currentImage: activeImage,
|
|
323
|
+
imageClsList: state.imageClsList,
|
|
324
|
+
imageTagList: state.imageTagList,
|
|
325
|
+
onChangeImage: action("CHANGE_IMAGE", "delta"),
|
|
326
|
+
expandedByDefault: true
|
|
327
|
+
}), // (state.images?.length || 0) > 1 && (
|
|
328
|
+
// <ImageSelector
|
|
329
|
+
// onSelect={action("SELECT_REGION", "region")}
|
|
330
|
+
// images={state.images}
|
|
331
|
+
// />
|
|
332
|
+
// ),
|
|
333
|
+
React.createElement(RegionSelector, {
|
|
334
|
+
regions: activeImage ? activeImage.regions : emptyArr,
|
|
335
|
+
onSelectRegion: action("SELECT_REGION", "region"),
|
|
336
|
+
onDeleteRegion: action("DELETE_REGION", "region"),
|
|
337
|
+
onChangeRegion: action("CHANGE_REGION", "region")
|
|
338
|
+
}), state.keyframes && React.createElement(KeyframesSelector, {
|
|
339
|
+
onChangeVideoTime: action("CHANGE_VIDEO_TIME", "newTime"),
|
|
340
|
+
onDeleteKeyframe: action("DELETE_KEYFRAME", "time"),
|
|
341
|
+
onChangeCurrentTime: action("CHANGE_VIDEO_TIME", "newTime"),
|
|
342
|
+
currentTime: state.currentVideoTime,
|
|
343
|
+
duration: state.videoDuration,
|
|
344
|
+
keyframes: state.keyframes
|
|
345
|
+
}), React.createElement(HistorySidebarBox, {
|
|
346
|
+
history: state.history,
|
|
347
|
+
onRestoreHistory: action("RESTORE_HISTORY")
|
|
348
|
+
})].filter(Boolean)
|
|
349
|
+
}, canvas), React.createElement(SettingsDialog, {
|
|
350
|
+
open: state.settingsOpen,
|
|
351
|
+
onClose: function onClose() {
|
|
352
|
+
return dispatch({
|
|
353
|
+
type: "HEADER_BUTTON_CLICKED",
|
|
354
|
+
buttonName: "Settings"
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
})))));
|
|
358
|
+
};
|
|
359
|
+
export default MainLayout;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { grey } from "@mui/material/colors";
|
|
2
|
+
export default {
|
|
3
|
+
container: {
|
|
4
|
+
display: "flex",
|
|
5
|
+
flexGrow: 1,
|
|
6
|
+
flexDirection: "column",
|
|
7
|
+
height: "100%",
|
|
8
|
+
maxHeight: "100vh",
|
|
9
|
+
backgroundColor: "#fff",
|
|
10
|
+
overflow: "hidden",
|
|
11
|
+
"&.fullscreen": {
|
|
12
|
+
position: "absolute",
|
|
13
|
+
zIndex: 99999,
|
|
14
|
+
left: 0,
|
|
15
|
+
right: 0,
|
|
16
|
+
top: 0,
|
|
17
|
+
bottom: 0
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
headerTitle: {
|
|
21
|
+
fontWeight: "bold",
|
|
22
|
+
color: grey[700],
|
|
23
|
+
paddingLeft: 16
|
|
24
|
+
}
|
|
25
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import getImpliedVideoRegions from "../Annotator/reducers/get-implied-video-regions.js";
|
|
3
|
+
var emptyArr = [];
|
|
4
|
+
export default (function (state) {
|
|
5
|
+
if (state.annotationType !== "video") return emptyArr;
|
|
6
|
+
var keyframes = state.keyframes,
|
|
7
|
+
_state$currentVideoTi = state.currentVideoTime,
|
|
8
|
+
currentVideoTime = _state$currentVideoTi === void 0 ? 0 : _state$currentVideoTi; // TODO memoize
|
|
9
|
+
|
|
10
|
+
return useMemo(function () {
|
|
11
|
+
return getImpliedVideoRegions(keyframes, currentVideoTime);
|
|
12
|
+
}, [keyframes, currentVideoTime]);
|
|
13
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, { Fragment } from "react";
|
|
2
|
+
import { styled } from "@mui/material/styles";
|
|
3
|
+
import { createTheme, ThemeProvider } from "@mui/material/styles";
|
|
4
|
+
var theme = createTheme();
|
|
5
|
+
var Svg = styled("svg")(function (_ref) {
|
|
6
|
+
var theme = _ref.theme;
|
|
7
|
+
return {
|
|
8
|
+
pointerEvents: "none",
|
|
9
|
+
position: "absolute",
|
|
10
|
+
zIndex: 1,
|
|
11
|
+
left: 0,
|
|
12
|
+
top: 0,
|
|
13
|
+
width: "100%",
|
|
14
|
+
height: "100%",
|
|
15
|
+
"& text": {
|
|
16
|
+
fill: "#fff"
|
|
17
|
+
},
|
|
18
|
+
"& path": {
|
|
19
|
+
vectorEffect: "non-scaling-stroke",
|
|
20
|
+
strokeWidth: 2,
|
|
21
|
+
opacity: 0.5,
|
|
22
|
+
stroke: "#FFF",
|
|
23
|
+
fill: "none",
|
|
24
|
+
strokeDasharray: 5,
|
|
25
|
+
animationDuration: "4s",
|
|
26
|
+
animationTimingFunction: "linear",
|
|
27
|
+
animationIterationCount: "infinite",
|
|
28
|
+
animationPlayState: "running"
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
export var PointDistances = function PointDistances(_ref2) {
|
|
33
|
+
var projectRegionBox = _ref2.projectRegionBox,
|
|
34
|
+
regions = _ref2.regions,
|
|
35
|
+
pointDistancePrecision = _ref2.pointDistancePrecision,
|
|
36
|
+
realSize = _ref2.realSize;
|
|
37
|
+
return React.createElement(ThemeProvider, {
|
|
38
|
+
theme: theme
|
|
39
|
+
}, React.createElement(Svg, null, regions.filter(function (r1) {
|
|
40
|
+
return r1.type === "point";
|
|
41
|
+
}).flatMap(function (r1, i1) {
|
|
42
|
+
return regions.filter(function (r2, i2) {
|
|
43
|
+
return i2 > i1;
|
|
44
|
+
}).filter(function (r2) {
|
|
45
|
+
return r2.type === "point";
|
|
46
|
+
}).map(function (r2) {
|
|
47
|
+
var pr1 = projectRegionBox(r1);
|
|
48
|
+
var pr2 = projectRegionBox(r2);
|
|
49
|
+
var prm = {
|
|
50
|
+
x: (pr1.x + pr1.w / 2 + pr2.x + pr2.w / 2) / 2,
|
|
51
|
+
y: (pr1.y + pr1.h / 2 + pr2.y + pr2.h / 2) / 2
|
|
52
|
+
};
|
|
53
|
+
var displayDistance;
|
|
54
|
+
|
|
55
|
+
if (realSize) {
|
|
56
|
+
var w = realSize.w,
|
|
57
|
+
h = realSize.h,
|
|
58
|
+
unitName = realSize.unitName;
|
|
59
|
+
displayDistance = Math.sqrt(Math.pow(r1.x * w - r2.x * w, 2) + Math.pow(r1.y * h - r2.y * h, 2)).toFixed(pointDistancePrecision) + unitName;
|
|
60
|
+
} else {
|
|
61
|
+
displayDistance = (Math.sqrt(Math.pow(r1.x - r2.x, 2) + Math.pow(r1.y - r2.y, 2)) * 100).toFixed(pointDistancePrecision) + "%";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return React.createElement(Fragment, null, React.createElement("path", {
|
|
65
|
+
d: "M".concat(pr1.x + pr1.w / 2, ",").concat(pr1.y + pr1.h / 2, " L").concat(pr2.x + pr2.w / 2, ",").concat(pr2.y + pr2.h / 2)
|
|
66
|
+
}), React.createElement("text", {
|
|
67
|
+
x: prm.x,
|
|
68
|
+
y: prm.y
|
|
69
|
+
}, displayDistance));
|
|
70
|
+
});
|
|
71
|
+
})));
|
|
72
|
+
};
|
|
73
|
+
export default PointDistances;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
|
2
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
|
|
3
|
+
import React, { useState } from "react";
|
|
4
|
+
import { RemoveScroll } from "react-remove-scroll";
|
|
5
|
+
import { styled } from "@mui/material/styles";
|
|
6
|
+
import { createTheme, ThemeProvider } from "@mui/material/styles";
|
|
7
|
+
import useEventCallback from "use-event-callback";
|
|
8
|
+
var theme = createTheme();
|
|
9
|
+
var Container = styled("div")(function (_ref) {
|
|
10
|
+
var theme = _ref.theme;
|
|
11
|
+
return {
|
|
12
|
+
"& > div": {
|
|
13
|
+
width: "100%",
|
|
14
|
+
height: "100%"
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
export var PreventScrollToParents = function PreventScrollToParents(_ref2) {
|
|
19
|
+
var children = _ref2.children,
|
|
20
|
+
otherProps = _objectWithoutProperties(_ref2, ["children"]);
|
|
21
|
+
|
|
22
|
+
var _useState = useState(false),
|
|
23
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
24
|
+
mouseOver = _useState2[0],
|
|
25
|
+
changeMouseOver = _useState2[1];
|
|
26
|
+
|
|
27
|
+
var onMouseMove = useEventCallback(function (e) {
|
|
28
|
+
if (!mouseOver) changeMouseOver(true);
|
|
29
|
+
|
|
30
|
+
if (otherProps.onMouseMove) {
|
|
31
|
+
otherProps.onMouseMove(e);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
var onMouseLeave = useEventCallback(function (e) {
|
|
35
|
+
setTimeout(function () {
|
|
36
|
+
if (mouseOver) {
|
|
37
|
+
changeMouseOver(false);
|
|
38
|
+
}
|
|
39
|
+
}, 100);
|
|
40
|
+
});
|
|
41
|
+
return React.createElement(ThemeProvider, {
|
|
42
|
+
theme: theme
|
|
43
|
+
}, React.createElement(Container, Object.assign({}, otherProps, {
|
|
44
|
+
onMouseMove: onMouseMove,
|
|
45
|
+
onMouseLeave: onMouseLeave
|
|
46
|
+
}), React.createElement(RemoveScroll, {
|
|
47
|
+
enabled: mouseOver,
|
|
48
|
+
removeScrollBar: false
|
|
49
|
+
}, children)));
|
|
50
|
+
};
|
|
51
|
+
export default PreventScrollToParents;
|
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# React Image Annotate
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/react-image-annotate)
|
|
4
|
+
|
|
5
|
+
The best image/video annotation tool ever. [Check out the demo here](https://universaldatatool.github.io/react-image-annotate/). Or the [code sandbox here](https://codesandbox.io/s/react-image-annotate-example-38tsc?file=/src/App.js:0-403).
|
|
6
|
+
|
|
7
|
+
## Sponsors
|
|
8
|
+
|
|
9
|
+
[](https://wao.ai)
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Simple input/output format
|
|
14
|
+
- Bounding Box, Point and Polygon Annotation
|
|
15
|
+
- Zooming, Scaling, Panning
|
|
16
|
+
- Multiple Images
|
|
17
|
+
- Cursor Crosshair
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
`npm install react-image-annotate`
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
import React from "react";
|
|
27
|
+
import ReactImageAnnotate from "react-image-annotate";
|
|
28
|
+
|
|
29
|
+
const App = () => (
|
|
30
|
+
<ReactImageAnnotate
|
|
31
|
+
labelImages
|
|
32
|
+
regionClsList={["Alpha", "Beta", "Charlie", "Delta"]}
|
|
33
|
+
regionTagList={["tag1", "tag2", "tag3"]}
|
|
34
|
+
images={[
|
|
35
|
+
{
|
|
36
|
+
src: "https://placekitten.com/408/287",
|
|
37
|
+
name: "Image 1",
|
|
38
|
+
regions: []
|
|
39
|
+
}
|
|
40
|
+
]}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export default App;
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
To get the proper fonts, make sure to import the Inter UI or Roboto font, the
|
|
49
|
+
following line added to a css file should suffice.
|
|
50
|
+
|
|
51
|
+
```css
|
|
52
|
+
@import url("https://rsms.me/inter/inter.css");
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Props
|
|
56
|
+
|
|
57
|
+
All of the following properties can be defined on the Annotator...
|
|
58
|
+
|
|
59
|
+
| Prop | Type (\* = required) | Description | Default |
|
|
60
|
+
| ------------------------ | ------------------------------------------------ | --------------------------------------------------------------------------------------- | ------------- |
|
|
61
|
+
| `taskDescription` | \*`string` | Markdown description for what to do in the image. | |
|
|
62
|
+
| `allowedArea` | `{ x: number, y: number, w: number, h: number }` | Area that is available for annotation. | Entire image. |
|
|
63
|
+
| `regionTagList` | `Array<string>` | Allowed "tags" (mutually inclusive classifications) for regions. | |
|
|
64
|
+
| `regionClsList` | `Array<string>` | Allowed "classes" (mutually exclusive classifications) for regions. | |
|
|
65
|
+
| `imageTagList` | `Array<string>` | Allowed tags for entire image. | |
|
|
66
|
+
| `imageClsList` | `Array<string>` | Allowed classes for entire image. | |
|
|
67
|
+
| `enabledTools` | `Array<string>` | Tools allowed to be used. e.g. "select", "create-point", "create-box", "create-polygon" | Everything. |
|
|
68
|
+
| `showTags` | `boolean` | Show tags and allow tags on regions. | `true` |
|
|
69
|
+
| `selectedImage` | `string` | URL of initially selected image. | |
|
|
70
|
+
| `images` | `Array<Image>` | Array of images to load into annotator | |
|
|
71
|
+
| `showPointDistances` | `boolean` | Show distances between points. | `false` |
|
|
72
|
+
| `pointDistancePrecision` | `number` | Precision on displayed points (e.g. 3 => 0.123) | |
|
|
73
|
+
| `onExit` | `MainLayoutState => any` | Called when "Save" is called. | |
|
|
74
|
+
| `RegionEditLabel` | `Node` | React Node overriding the form to update the region (see [`RegionLabel`](https://github.com/waoai/react-image-annotate/blob/master/src/RegionLabel/index.js)) | |
|
|
75
|
+
| `allowComments` | `boolean` | Show a textarea to add comments on each annotation. | `false` |
|
|
76
|
+
| `hidePrev` | `boolean` | Hide `Previous Image` button from the header bar. | `false` |
|
|
77
|
+
| `hideNext` | `boolean` | Hide `Next Image` button from the header bar. | `false` |
|
|
78
|
+
| `hideClone` | `boolean` | Hide `Clone` button from the header bar. | `false` |
|
|
79
|
+
| `hideSettings` | `boolean` | Hide `Settings` button from the header bar. | `false` |
|
|
80
|
+
| `hideFullScreen` | `boolean` | Hide `FullScreen/Window` button from the header bar. | `false` |
|
|
81
|
+
| `hideSave` | `boolean` | Hide `Save` button from the header bar. | `false` |
|
|
82
|
+
|
|
83
|
+
## Developers
|
|
84
|
+
|
|
85
|
+
### Development
|
|
86
|
+
|
|
87
|
+
This project uses [react-storybook](https://storybook.js.org/). To begin developing run the following commands in the cloned repo.
|
|
88
|
+
|
|
89
|
+
1. `yarn install`
|
|
90
|
+
2. `yarn storybook`
|
|
91
|
+
|
|
92
|
+
A browser tab will automatically open with the project components.
|
|
93
|
+
|
|
94
|
+
See more details in the [contributing guidelines](https://github.com/waoai/react-image-annotate/wiki/Setup-for-Development).
|
|
95
|
+
|
|
96
|
+
### Icons
|
|
97
|
+
|
|
98
|
+
Consult these icon repositories:
|
|
99
|
+
|
|
100
|
+
- [Material Icons](https://material.io/tools/icons/)
|
|
101
|
+
- [Font Awesome Icons](https://fontawesome.com/icons?d=gallery&m=free)
|