lost-sia 1.2.0 → 2.0.0-alpha0

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