lost-sia 1.3.0 → 2.0.0-alpha1

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 (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +0 -0
  3. package/package.json +60 -47
  4. package/src/AnnoExampleViewer.jsx +57 -0
  5. package/src/AnnoLabelInput.jsx +99 -0
  6. package/src/AnnoToolBar.jsx +132 -0
  7. package/src/Annotation/AnnoBar.jsx +148 -0
  8. package/src/Annotation/Annotation.jsx +358 -0
  9. package/src/Annotation/Annotation.scss +47 -0
  10. package/src/Annotation/BBox.jsx +291 -0
  11. package/src/Annotation/Edge.jsx +82 -0
  12. package/src/Annotation/InfSelectionArea.jsx +71 -0
  13. package/src/Annotation/Line.jsx +60 -0
  14. package/src/Annotation/Node.jsx +278 -0
  15. package/src/Annotation/Point.jsx +196 -0
  16. package/src/Annotation/Polygon.jsx +361 -0
  17. package/src/Canvas.jsx +1843 -0
  18. package/src/ImgBar.jsx +123 -0
  19. package/src/InfoBoxes/AnnoDetails.jsx +166 -0
  20. package/src/InfoBoxes/AnnoStats.jsx +104 -0
  21. package/src/InfoBoxes/InfoBox.jsx +78 -0
  22. package/src/InfoBoxes/InfoBoxArea.jsx +155 -0
  23. package/src/InfoBoxes/LabelInfo.jsx +95 -0
  24. package/src/LabelInput.jsx +224 -0
  25. package/src/Prompt.jsx +46 -0
  26. package/src/SIA.scss +10 -0
  27. package/src/SIAFilterButton.jsx +178 -0
  28. package/src/SIASettingButton.jsx +122 -0
  29. package/src/Sia.jsx +491 -0
  30. package/src/SiaPopup.jsx +10 -0
  31. package/src/ToolBar.jsx +399 -0
  32. package/src/Toolbar.css +13 -0
  33. package/src/ToolbarItem.jsx +25 -0
  34. package/src/filterTools.js +3 -0
  35. package/src/index.js +16 -0
  36. package/src/siaDummyData.js +265 -0
  37. package/src/style.scss +3 -0
  38. package/src/test.js +7 -0
  39. package/src/types/annoStatus.js +4 -0
  40. package/src/types/canvasActions.js +57 -0
  41. package/src/types/cursorstyles.js +3 -0
  42. package/src/types/modes.js +8 -0
  43. package/src/types/notificationType.js +4 -0
  44. package/src/types/toolbarEvents.js +33 -0
  45. package/src/types/tools.js +11 -0
  46. package/src/utils/annoConversion.js +115 -0
  47. package/src/utils/colorlut.js +67 -0
  48. package/src/utils/constraints.js +75 -0
  49. package/src/utils/hist.js +67 -0
  50. package/src/utils/keyActions.js +107 -0
  51. package/src/utils/mouse.js +14 -0
  52. package/src/utils/siaIcons.jsx +95 -0
  53. package/src/utils/transform.js +318 -0
  54. package/src/utils/uiConfig.js +57 -0
  55. package/src/utils/windowViewport.js +35 -0
  56. package/CHANGELOG.md +0 -169
  57. package/dist/index.css +0 -24823
  58. package/dist/index.es.js +0 -10482
  59. package/dist/index.es.js.map +0 -1
  60. package/dist/index.js +0 -10497
  61. package/dist/index.js.map +0 -1
package/src/Sia.jsx ADDED
@@ -0,0 +1,491 @@
1
+ import React, { useRef, useEffect, useState } from 'react'
2
+
3
+ import ToolBar from './ToolBar'
4
+ import Canvas from './Canvas'
5
+ import * as tbe from './types/toolbarEvents'
6
+ import * as annoActions from './types/canvasActions'
7
+ import { noAnnos } from './siaDummyData'
8
+
9
+ /**
10
+ * SIA element that handles annotations within an image
11
+ *
12
+ * @param {boolean} isStaticPosition - Use static positioning instead of "fixed"
13
+ *
14
+ * @param {integer} fixedImageSize - Use fixed image size if specified
15
+ *
16
+ * @param {object} annos - A json object containing all annotation
17
+ * information for an image
18
+ * {
19
+ * bBoxes: [{
20
+ * id: int, // -> Not required if status === annoStatus.NEW
21
+ * data: {},
22
+ * labelIds: list of int, // -> optional
23
+ * status: see annoStatus, // -> optional
24
+ * annoTime: float, // -> optional
25
+ * },...],
26
+ * points: []
27
+ * lines: []
28
+ * polygons: []
29
+ * }
30
+ * @param {object} annoSaveResponse - Backend response when updating an annotation in backend
31
+ * {
32
+ * tempId: int or str, // temporal frontend Id
33
+ * dbId: int, // Id from backend
34
+ * newStatus: str // new Status for the annotation
35
+ * }
36
+ * @param {object} possibleLabels - Possible labels that can be assigned to
37
+ * an annotation.
38
+ * [{
39
+ * id: int,
40
+ * description: str,
41
+ * label: str, (name of the label)
42
+ * color: str (color is optional)
43
+ * }, ...]
44
+ * @param {blob} imageBlob - The actual image blob that will be displayed
45
+ * @param {object} imageMeta - Meta information for the current image
46
+ * {
47
+ * "id": int,
48
+ * "number": int, // -> number of image in current annotask
49
+ * "amount": int, // -> total number of images in current annotask
50
+ * "isFirst": bool, // -> True if this image is the first image
51
+ * "isLast": bool, // -> True if current image is the last image in annotask
52
+ * "labelIds": list of int, // -> List of label for the current image
53
+ * "isJunk": bool, // -> Indicates wether current image is a junk image
54
+ * "annoTime": float, // -> Total annotation time for the current image
55
+ * "description": str or null // -> Description or comment for the current image
56
+ * }
57
+ * @param {object} exampleImg - Example for a selected label
58
+ * {
59
+ * "anno": {
60
+ * "id": int, // -> ID of the example annotation
61
+ * "comment": null or str // -> Comment that has been assigned to this example
62
+ * },
63
+ * "img": image blob
64
+ * }
65
+ * @param {bool} isJunk - Indicates wether the current image is junk or not
66
+ * @param {object} uiConfig - User interface configs
67
+ * {
68
+ * nodesRadius: int, strokeWidth: int,
69
+ * layoutOffset: {left:int, top:int, right:int, bottom:int}, -> Offset of the canvas inside the container
70
+ * imgBarVisible: bool,
71
+ * imgLabelInputVisible: bool,
72
+ * centerCanvasInContainer: bool, -> Center the canvas in the middle of the container.
73
+ * maxCanvas: bool -> Maximize Canvas Size. Do not fit canvas to image size.
74
+ * }
75
+ * @param {int} layoutUpdate - A counter that triggers a layout update
76
+ * everytime it is incremented.
77
+ * @param {string} selectedTool - The tool that is selected to draw an
78
+ * annotation. Possible choices are: 'bBox', 'point', 'line', 'polygon'
79
+ * @param {object} canvasConfig - Configuration for this canvas
80
+ * {
81
+ * annos:{
82
+ * tools: {
83
+ * point: bool,
84
+ * line: bool,
85
+ * polygon: bool,
86
+ * bbox: bool
87
+ * },
88
+ * multilabels: bool,
89
+ * actions: {
90
+ * draw: bool,
91
+ * label: bool,
92
+ * edit: bool,
93
+ * },
94
+ * maxAnnos: null or int,
95
+ * minArea: int
96
+ * },
97
+ * img: {
98
+ * multilabels: bool,
99
+ * actions: {
100
+ * label: bool,
101
+ * }
102
+ * },
103
+ * allowedToMarkExample: bool, -> Indicates wether the current user is allowed to mark an annotation as example.
104
+ * }
105
+ * @param {str or int} defaultLabel (optional) - Name or ID of the default label that is used
106
+ * when no label was selected by the annotator. If not set "no label" will be used.
107
+ * If ID is used, it needs to be one of the possible label ids.
108
+ * @param {bool} blocked Block canvas view with loading dimmer.
109
+ * @param {bool} fullscreen Set fullscreen mode if provided
110
+ * @param {bool} preventScrolling Prevent scrolling on mouseEnter
111
+ * @param {bool} lockedAnnos A list of AnnoIds of annos that should only be displayed.
112
+ * Such annos can not be edited in any way.
113
+ * @param {object} filter Information for the filter Popup
114
+ * {
115
+ * "clahe": {
116
+ * "clipLimit": int,
117
+ * "active": bool
118
+ * },
119
+ * }
120
+ * @param {bool | object} toolbarEnabled Defines which toolbar buttons are
121
+ * displayed or if toolbar is shown at all.
122
+ * false | {
123
+ * imgLabel: bool,
124
+ * nextPrev: bool,
125
+ * toolSelection: bool,
126
+ * fullscreen: bool,
127
+ * junk: bool,
128
+ * deleteAll: bool,
129
+ * settings: bool | {infoBoxes: bool, annoStyle: bool},
130
+ * filter: bool | {rotate: bool, clahe:bool},
131
+ * help: bool
132
+ * }
133
+ * @event onAnnoSaveEvent - Callback with update information for a single
134
+ * annotation or the current image that can be used for backend updates
135
+ * args: {
136
+ * action: the action that was performed in frontend,
137
+ * anno: anno information,
138
+ * img: image information
139
+ * }
140
+ * @event onNotification - Callback for Notification messages
141
+ * args: {title: str, message: str, type: str}
142
+ * @event onCanvasKeyDown - Fires for keyDown on canvas
143
+ * @event onAnnoEvent - Fires when an anno performed an action
144
+ * args: {anno: annoObject, newAnnos: list of annoObjects, pAction: str}
145
+ * @event onGetAnnoExample - Fires when anno example is requested by canvas
146
+ * {
147
+ * id: int, // -> ID of the annotation that will be requested as example
148
+ * comment: null or str
149
+ * }
150
+ * @event onCanvasEvent - Fires on canvas event
151
+ * args: {action: action, data: dataObject}
152
+ * action -> CANVAS_SVG_UPDATE
153
+ * data: {width: int, height: int, scale: float, translateX: float,
154
+ * translateY:float}
155
+ * action -> CANVAS_UI_CONFIG_UPDATE
156
+ * action -> CANVAS_LABEL_INPUT_CLOSE
157
+ * action -> CANVAS_IMG_LOADED
158
+ * action -> CANVAS_IMGBAR_CLOSE
159
+ * @event onToolBarEvent - Fires on Toolbar event
160
+ * args: {e: event, data: data object}
161
+ *
162
+ * e -> DELETE_ALL_ANNOS
163
+ * e -> TOOL_SELECTED
164
+ * data: 'bbox', 'point', 'line', 'polygon'
165
+ * e -> GET_NEXT_IMAGE
166
+ * data: int // -> Image ID
167
+ * e -> GET_PREV_IMAGE
168
+ * data: int // -> Image ID
169
+ * e -> TASK_FINISHED
170
+ * data: null
171
+ * e -> SHOW_IMAGE_LABEL_INPUT
172
+ * data: null
173
+ * e -> IMG_IS_JUNK
174
+ * data: null
175
+ * e -> APPLY_FILTER
176
+ * data: {
177
+ * "clahe": {
178
+ * "clipLimit": int,
179
+ * "active": bool
180
+ * },
181
+ * "rotate": {
182
+ * "angle": 90 | -90 | 180,
183
+ * "active": bool
184
+ * }
185
+ * }
186
+ * e -> SHOW_ANNO_DETAILS
187
+ * data: null
188
+ * e -> SHOW_LABEL_INFO
189
+ * data: null
190
+ * e -> SHOW_ANNO_STATS
191
+ * data: null
192
+ * e -> EDIT_STROKE_WIDTH
193
+ * data: int // -> Stroke width
194
+ * e -> EDIT_NODE_RADIUS
195
+ * data: int // -> Radius
196
+ * @event onGetFunction - Get special canvas functions for manipulation from outside canvas
197
+ * deleteAllAnnos()
198
+ * unloadImage()
199
+ * resetZoom()
200
+ * getAnnos(annos,removeFrontendIds)
201
+ */
202
+ const Sia = (props) => {
203
+
204
+ const [fullscreenCSS, setFullscreenCSS] = useState('')
205
+ const [fullscreen, setFullscreen] = useState()
206
+ const [annos, setAnnos] = useState(noAnnos)
207
+ const [layoutUpdate, setLayoutUpdate] = useState(0)
208
+ const [svg, setSvg] = useState()
209
+ const [externalConfigUpdate, setExternalConfigUpdate] = useState(false)
210
+ const [uiConfig, setUiConfig] = useState(
211
+ {
212
+ "nodeRadius": 4,
213
+ "strokeWidth": 4,
214
+ "annoDetails": {
215
+ "visible": false
216
+ },
217
+ "labelInfo": {
218
+ "visible": false
219
+ },
220
+ "annoStats": {
221
+ "visible": false
222
+ },
223
+ "layoutOffset": {
224
+ "left": 20,
225
+ "top": 0,
226
+ "bottom": 5,
227
+ "right": 5
228
+ },
229
+ "imgBarVisible": true,
230
+ "imgLabelInputVisible": false,
231
+ "centerCanvasInContainer": true,
232
+ "maxCanvas": true
233
+ }
234
+ )
235
+ const containerRef = useRef()
236
+
237
+ useEffect(() => {
238
+ doLayoutUpdate()
239
+ }, [props.layoutUpdate])
240
+
241
+ useEffect(() => {
242
+ console.log(annos)
243
+ }, [annos])
244
+
245
+ useEffect(() => {
246
+ console.log('props.annos', props.annos)
247
+ if (props.annos) {
248
+ setAnnos(props.annos)
249
+ } else {
250
+ setAnnos({ ...noAnnos })
251
+
252
+ }
253
+ }, [props.annos])
254
+
255
+ useEffect(() => {
256
+ console.log('props.fullscreen', props.fullscreen)
257
+ console.log('fullscreen', fullscreen)
258
+ if (typeof props.fullscreen === 'boolean') {
259
+ if (fullscreen !== props.fullscreen) {
260
+ setFullscreen(props.fullscreen)
261
+ }
262
+ }
263
+ }, [props.fullscreen])
264
+
265
+ useEffect(() => {
266
+ if (fullscreen !== undefined) {
267
+ console.log('effect fullscreen', fullscreen)
268
+ // toggleFullscreen()
269
+ applyFullscreen(fullscreen)
270
+ }
271
+ }, [fullscreen])
272
+
273
+ useEffect(() => {
274
+ setExternalConfigUpdate(true)
275
+ setUiConfig({ ...uiConfig, ...props.uiConfig })
276
+ }, [props.uiConfig])
277
+
278
+ useEffect(() => {
279
+ if (externalConfigUpdate) {
280
+ setExternalConfigUpdate(false)
281
+ } else {
282
+ if (props.onCanvasEvent) {
283
+ props.onCanvasEvent(annoActions.CANVAS_UI_CONFIG_UPDATE, uiConfig)
284
+ }
285
+ }
286
+ }, [uiConfig])
287
+
288
+ const doLayoutUpdate = () => {
289
+ setLayoutUpdate(layoutUpdate + 1)
290
+ }
291
+
292
+ const handleAnnoEvent = (anno, annos, action) => {
293
+ console.log('handleAnnoEvent anno, annos, action', anno, annos, action)
294
+ if (props.onAnnoEvent) {
295
+ props.onAnnoEvent(anno, annos, action)
296
+ }
297
+
298
+ }
299
+
300
+ const handleNotification = (msg) => {
301
+ if (props.onNotification) {
302
+ props.onNotification(msg)
303
+ }
304
+
305
+ }
306
+
307
+ const handleCanvasKeyDown = (e) => {
308
+ if (props.onCanvasKeyDown) {
309
+ props.onCanvasKeyDown(e)
310
+ }
311
+
312
+ }
313
+
314
+ const handleCanvasEvent = (e, data) => {
315
+ switch (e) {
316
+ case annoActions.CANVAS_SVG_UPDATE:
317
+ setSvg(data)
318
+ break
319
+ case annoActions.CANVAS_UI_CONFIG_UPDATE:
320
+ setUiConfig({ ...uiConfig, ...data })
321
+ break
322
+ default:
323
+ break
324
+ }
325
+ if (props.onCanvasEvent) {
326
+ props.onCanvasEvent(e, data)
327
+ }
328
+ }
329
+
330
+ const handleGetFunction = (canvasFunction) => {
331
+ if (props.onGetFunction) {
332
+ props.onGetFunction(canvasFunction)
333
+ }
334
+ }
335
+
336
+ const handleAnnoSaveEvent = (action, saveData) => {
337
+ if (props.onAnnoSaveEvent) {
338
+ props.onAnnoSaveEvent(action, saveData)
339
+ }
340
+ }
341
+
342
+ const applyFullscreen = (full) => {
343
+ if (full) {
344
+ setFullscreenCSS('sia-fullscreen')
345
+ setUiConfig({
346
+ ...uiConfig,
347
+ layoutOffset: {
348
+ ...uiConfig.layoutOffset,
349
+ left: 50,
350
+ top: 5,
351
+ }
352
+ })
353
+ doLayoutUpdate()
354
+ } else {
355
+ setFullscreenCSS('')
356
+ setUiConfig({
357
+ ...uiConfig,
358
+ layoutOffset: {
359
+ ...uiConfig.layoutOffset,
360
+ left: 20,
361
+ top: 0,
362
+ }
363
+ })
364
+ doLayoutUpdate()
365
+ }
366
+
367
+ }
368
+
369
+ const toggleFullscreen = () => {
370
+ if (fullscreen) {
371
+ setFullscreen(false)
372
+ } else {
373
+ setFullscreen(true)
374
+ }
375
+ }
376
+
377
+ const handleToolBarEvent = (e, data) => {
378
+ switch (e) {
379
+ case tbe.SET_FULLSCREEN:
380
+ toggleFullscreen()
381
+ break
382
+ case tbe.SHOW_ANNO_DETAILS:
383
+ setUiConfig({
384
+ ...uiConfig,
385
+ annoDetails: {
386
+ ...uiConfig.annoDetails,
387
+ visible: !uiConfig.annoDetails.visible,
388
+ },
389
+ })
390
+ break
391
+ case tbe.SHOW_LABEL_INFO:
392
+ setUiConfig({
393
+ ...uiConfig,
394
+ labelInfo: {
395
+ ...uiConfig.labelInfo,
396
+ visible: !uiConfig.labelInfo.visible,
397
+ },
398
+ })
399
+ break
400
+ case tbe.SHOW_ANNO_STATS:
401
+ setUiConfig({
402
+ ...uiConfig,
403
+ annoStats: {
404
+ ...uiConfig.annoStats,
405
+ visible: !uiConfig.annoStats.visible,
406
+ },
407
+ })
408
+ break
409
+ case tbe.EDIT_STROKE_WIDTH:
410
+ setUiConfig({ ...uiConfig, strokeWidth: data })
411
+ break
412
+ case tbe.EDIT_NODE_RADIUS:
413
+ setUiConfig({ ...uiConfig, nodeRadius: data })
414
+ break
415
+ default:
416
+ break
417
+ }
418
+ if (props.onToolBarEvent) {
419
+ props.onToolBarEvent(e, data)
420
+ }
421
+ }
422
+
423
+ return (
424
+ <div className={`sia-app ${fullscreenCSS}`} ref={containerRef}>
425
+ <Canvas
426
+ container={containerRef}
427
+
428
+ onAnnoEvent={
429
+ (anno, annos, action) => handleAnnoEvent(anno, annos, action)
430
+ }
431
+ onNotification={
432
+ (messageObj) => handleNotification(messageObj)
433
+ }
434
+ onKeyDown={
435
+ e => handleCanvasKeyDown(e)
436
+ }
437
+ onCanvasEvent={
438
+ (action, data) => handleCanvasEvent(action, data)
439
+ }
440
+ onGetAnnoExample={
441
+ (exampleArgs) => props.onGetAnnoExample ? props.onGetAnnoExample(exampleArgs) : {}
442
+ }
443
+ onGetFunction={(canvasFunc) => handleGetFunction(canvasFunc)}
444
+ onAnnoSaveEvent={(saveData) => handleAnnoSaveEvent(saveData)}
445
+
446
+ annoSaveResponse={props.annoSaveResponse}
447
+ canvasConfig={props.canvasConfig}
448
+ uiConfig={uiConfig}
449
+ annos={annos}
450
+ annoTaskId={props.annoTaskId}
451
+ imageMeta={props.imageMeta}
452
+ imageBlob={props.imageBlob}
453
+ possibleLabels={props.possibleLabels}
454
+ exampleImg={props.exampleImg}
455
+ lockedAnnos={props.lockedAnnos}
456
+ layoutUpdate={layoutUpdate}
457
+ selectedTool={props.selectedTool}
458
+ isJunk={props.isJunk}
459
+ blocked={props.blockCanvas}
460
+ defaultLabel={props.defaultLabel}
461
+ preventScrolling={props.preventScrolling}
462
+ isImageChanging={props.isImageChanging}
463
+ isStaticPosition={props.isStaticPosition}
464
+ fixedImageSize={props.fixedImageSize}
465
+ />
466
+ <ToolBar
467
+ onToolBarEvent={
468
+ (e, data) => handleToolBarEvent(e, data)
469
+ }
470
+ imageMeta={props.imageMeta}
471
+ layoutUpdate={layoutUpdate}
472
+ svg={svg}
473
+ active={{
474
+ isJunk: props.isJunk,
475
+ selectedTool: props.selectedTool,
476
+ fullscreen: props.fullscreenMode
477
+ }}
478
+ enabled={props.toolbarEnabled}
479
+ canvasConfig={props.canvasConfig}
480
+ uiConfig={uiConfig}
481
+ filter={props.filter}
482
+ onImgageSearchClicked={() => {
483
+ if (props.onImgageSearchClicked) return props.onImgageSearchClicked()
484
+ }}
485
+ />
486
+ </div>
487
+ )
488
+
489
+ }
490
+
491
+ export default Sia
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Popup } from 'semantic-ui-react'
3
+
4
+ const SiaPopup = ({ content, trigger }) => {
5
+
6
+ return <Popup inverted style={{ opacity: 0.9 }} content={content} trigger={trigger} />
7
+
8
+ }
9
+
10
+ export default SiaPopup