lost-sia 2.0.0-alpha9 → 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/package.json +3 -2
- package/src/Annotation/Annotation.jsx +1 -0
- package/src/Annotation/Polygon.jsx +16 -0
- package/src/Canvas.jsx +192 -26
- package/src/Sia.jsx +8 -4
- package/src/ToolBar.jsx +56 -11
- package/src/ToolbarItem.jsx +1 -1
- package/src/types/canvasActions.js +1 -0
- package/src/types/modes.js +1 -0
- package/src/types/toolbarEvents.js +2 -0
- package/src/types/tools.js +6 -0
- package/src/utils/annoConversion.js +2 -2
- package/src/utils/keyActions.js +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lost-sia",
|
|
3
|
-
"version": "2.0.0
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Single Image Annotation Tool",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "l3p-cv/lost-sia",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"@fortawesome/free-regular-svg-icons": "^6.5.2",
|
|
43
43
|
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
|
44
44
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
|
45
|
+
"lodash-es": "^4.17.21",
|
|
45
46
|
"react": "^18.0.0",
|
|
46
47
|
"react-dom": "^18.0.0",
|
|
47
48
|
"react-draggable": "^4.4.6",
|
|
@@ -90,4 +91,4 @@
|
|
|
90
91
|
"plugin:storybook/recommended"
|
|
91
92
|
]
|
|
92
93
|
}
|
|
93
|
-
}
|
|
94
|
+
}
|
|
@@ -135,6 +135,11 @@ class Polygon extends Component {
|
|
|
135
135
|
);
|
|
136
136
|
}
|
|
137
137
|
break;
|
|
138
|
+
case modes.ADD:
|
|
139
|
+
if (e.button === 0) {
|
|
140
|
+
this.removeNode(e, idx)
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
138
143
|
default:
|
|
139
144
|
break;
|
|
140
145
|
}
|
|
@@ -228,6 +233,17 @@ class Polygon extends Component {
|
|
|
228
233
|
this.performedAction(newAnno, canvasActions.ANNO_ADDED_NODE);
|
|
229
234
|
}
|
|
230
235
|
|
|
236
|
+
removeNode(e, idx) {
|
|
237
|
+
let newAnnoData = [...this.state.anno.data]
|
|
238
|
+
newAnnoData.splice(idx, 1)
|
|
239
|
+
const newAnno = {
|
|
240
|
+
...this.state.anno,
|
|
241
|
+
data: newAnnoData,
|
|
242
|
+
};
|
|
243
|
+
this.setState({ anno: newAnno });
|
|
244
|
+
this.performedAction(newAnno, canvasActions.ANNO_REMOVED_SPECIFIC_NODE);
|
|
245
|
+
}
|
|
246
|
+
|
|
231
247
|
removeLastNode() {
|
|
232
248
|
const newAnno = {
|
|
233
249
|
...this.state.anno,
|
package/src/Canvas.jsx
CHANGED
|
@@ -1,37 +1,29 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
1
2
|
import React, { Component } from "react";
|
|
2
|
-
import
|
|
3
|
+
import { uniqueId } from "lodash-es";
|
|
3
4
|
import Annotation from "./Annotation/Annotation";
|
|
4
5
|
import AnnoLabelInput from "./AnnoLabelInput";
|
|
6
|
+
import AnnoToolBar from "./AnnoToolBar";
|
|
5
7
|
import ImgBar from "./ImgBar";
|
|
6
|
-
import Prompt from "./Prompt";
|
|
7
8
|
import LabelInput from "./LabelInput";
|
|
8
|
-
import
|
|
9
|
+
import Prompt from "./Prompt";
|
|
9
10
|
|
|
10
|
-
import
|
|
11
|
-
import * as keyActions from "./utils/keyActions";
|
|
12
|
-
import KeyMapper from "./utils/keyActions";
|
|
13
|
-
import * as TOOLS from "./types/tools";
|
|
14
|
-
import * as modes from "./types/modes";
|
|
15
|
-
import UndoRedo from "./utils/hist";
|
|
16
|
-
import * as transformAnnos from "./utils/transform";
|
|
11
|
+
import { Button, Dimmer, Header, Icon, Loader } from "semantic-ui-react";
|
|
17
12
|
import * as annoStatus from "./types/annoStatus";
|
|
18
13
|
import * as canvasActions from "./types/canvasActions";
|
|
19
|
-
import
|
|
20
|
-
Loader,
|
|
21
|
-
Dimmer,
|
|
22
|
-
Icon,
|
|
23
|
-
Header,
|
|
24
|
-
Button,
|
|
25
|
-
Form,
|
|
26
|
-
TextArea,
|
|
27
|
-
} from "semantic-ui-react";
|
|
28
|
-
import * as mouse from "./utils/mouse";
|
|
29
|
-
import * as colorlut from "./utils/colorlut";
|
|
14
|
+
import * as modes from "./types/modes";
|
|
30
15
|
import * as notificationType from "./types/notificationType";
|
|
16
|
+
import * as TOOLS from "./types/tools";
|
|
17
|
+
import * as annoConversion from "./utils/annoConversion";
|
|
18
|
+
import * as colorlut from "./utils/colorlut";
|
|
19
|
+
import UndoRedo from "./utils/hist";
|
|
20
|
+
import KeyMapper, * as keyActions from "./utils/keyActions";
|
|
21
|
+
import * as mouse from "./utils/mouse";
|
|
22
|
+
import * as transformAnnos from "./utils/transform";
|
|
31
23
|
import * as wv from "./utils/windowViewport";
|
|
32
24
|
|
|
33
|
-
import "./SIA.scss";
|
|
34
25
|
import InfoBoxes from "./InfoBoxes/InfoBoxArea";
|
|
26
|
+
import "./SIA.scss";
|
|
35
27
|
|
|
36
28
|
/**
|
|
37
29
|
* SIA Canvas element that handles annotations within an image
|
|
@@ -192,6 +184,8 @@ class Canvas extends Component {
|
|
|
192
184
|
possibleLabels: undefined,
|
|
193
185
|
annoCommentInputTrigger: 0,
|
|
194
186
|
imgActions: [],
|
|
187
|
+
isDrawingSamBBox: false, // Flag to indicate if a SAM bbox is currently being drawn,
|
|
188
|
+
samBBoxStartPoint: null,
|
|
195
189
|
};
|
|
196
190
|
this.img = React.createRef();
|
|
197
191
|
this.svg = React.createRef();
|
|
@@ -386,6 +380,13 @@ class Canvas extends Component {
|
|
|
386
380
|
} else if (e.button === 1) {
|
|
387
381
|
this.setMode(modes.CAMERA_MOVE);
|
|
388
382
|
} else if (e.button === 2) {
|
|
383
|
+
if (
|
|
384
|
+
this.props.selectedTool === TOOLS.POSITIVE_POINT ||
|
|
385
|
+
this.props.selectedTool === TOOLS.NEGATIVE_POINT ||
|
|
386
|
+
this.props.selectedTool === TOOLS.SAM_BBOX
|
|
387
|
+
) {
|
|
388
|
+
return; // do nothing for sam tools
|
|
389
|
+
}
|
|
389
390
|
//Create annotation on right click
|
|
390
391
|
this.createNewAnnotation(e);
|
|
391
392
|
}
|
|
@@ -396,6 +397,13 @@ class Canvas extends Component {
|
|
|
396
397
|
// this.collectAnnos()
|
|
397
398
|
this.setMode(modes.CAMERA_MOVE);
|
|
398
399
|
} else if (e.button === 2) {
|
|
400
|
+
if (
|
|
401
|
+
this.props.selectedTool === TOOLS.POSITIVE_POINT ||
|
|
402
|
+
this.props.selectedTool === TOOLS.NEGATIVE_POINT ||
|
|
403
|
+
this.props.selectedTool === TOOLS.SAM_BBOX
|
|
404
|
+
) {
|
|
405
|
+
return; // Note: do nothing for sam tools
|
|
406
|
+
}
|
|
399
407
|
//Create annotation on right click
|
|
400
408
|
this.createNewAnnotation(e);
|
|
401
409
|
} else if (e.button === 0) {
|
|
@@ -427,6 +435,7 @@ class Canvas extends Component {
|
|
|
427
435
|
handleKeyAction(action) {
|
|
428
436
|
const anno = this.findAnno(this.state.selectedAnnoId);
|
|
429
437
|
const camKeyStepSize = 20 * this.state.svg.scale;
|
|
438
|
+
console.log('handleKeyAction', action, anno)
|
|
430
439
|
|
|
431
440
|
switch (action) {
|
|
432
441
|
case keyActions.EDIT_LABEL:
|
|
@@ -499,7 +508,7 @@ class Canvas extends Component {
|
|
|
499
508
|
this.recreateAnnotation(this.state.selectedAnnoId);
|
|
500
509
|
break;
|
|
501
510
|
default:
|
|
502
|
-
|
|
511
|
+
// console.warn("Unknown key action", action);
|
|
503
512
|
}
|
|
504
513
|
}
|
|
505
514
|
|
|
@@ -585,6 +594,7 @@ class Canvas extends Component {
|
|
|
585
594
|
// console.log("handleAnnoEvent", pAction, anno);
|
|
586
595
|
let newAnnos = undefined;
|
|
587
596
|
let actionHistoryStore = undefined;
|
|
597
|
+
console.log('canvasActions: ', anno, pAction)
|
|
588
598
|
|
|
589
599
|
switch (pAction) {
|
|
590
600
|
case canvasActions.ANNO_ENTER_CREATE_MODE:
|
|
@@ -644,7 +654,7 @@ class Canvas extends Component {
|
|
|
644
654
|
break;
|
|
645
655
|
case canvasActions.ANNO_ADDED_NODE:
|
|
646
656
|
actionHistoryStore = [...this.state.imgActions, pAction];
|
|
647
|
-
newAnnos = this.updateSelectedAnno(anno, modes.
|
|
657
|
+
newAnnos = this.updateSelectedAnno(anno, modes.ADD);
|
|
648
658
|
this.pushHist(newAnnos, anno.id, pAction, this.state.showSingleAnno);
|
|
649
659
|
this.handleAnnoSaveEvent(pAction, anno, {
|
|
650
660
|
imgActions: actionHistoryStore,
|
|
@@ -665,6 +675,21 @@ class Canvas extends Component {
|
|
|
665
675
|
});
|
|
666
676
|
}
|
|
667
677
|
break;
|
|
678
|
+
case canvasActions.ANNO_REMOVED_SPECIFIC_NODE:
|
|
679
|
+
actionHistoryStore = [...this.state.imgActions, pAction];
|
|
680
|
+
if (!this.checkAnnoLength(anno)) {
|
|
681
|
+
newAnnos = this.updateSelectedAnno(anno, modes.DELETED);
|
|
682
|
+
this.showSingleAnno(undefined);
|
|
683
|
+
} else {
|
|
684
|
+
newAnnos = this.updateSelectedAnno(anno, modes.ADD)
|
|
685
|
+
}
|
|
686
|
+
this.pushHist(newAnnos, anno.id, pAction, this.state.showSingleAnno);
|
|
687
|
+
if (anno.status !== annoStatus.NEW) {
|
|
688
|
+
this.handleAnnoSaveEvent(pAction, anno, {
|
|
689
|
+
imgActions: actionHistoryStore,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
break;
|
|
668
693
|
case canvasActions.ANNO_EDITED:
|
|
669
694
|
actionHistoryStore = [...this.state.imgActions, pAction];
|
|
670
695
|
anno = this.stopAnnotimeMeasure(anno);
|
|
@@ -860,6 +885,53 @@ class Canvas extends Component {
|
|
|
860
885
|
}
|
|
861
886
|
}
|
|
862
887
|
|
|
888
|
+
handleContextMenu(e) {
|
|
889
|
+
e.preventDefault();
|
|
890
|
+
if (
|
|
891
|
+
this.props.selectedTool === TOOLS.POSITIVE_POINT ||
|
|
892
|
+
this.props.selectedTool === TOOLS.NEGATIVE_POINT
|
|
893
|
+
) {
|
|
894
|
+
const { x, y } = this.getMousePosition(e);
|
|
895
|
+
|
|
896
|
+
const imgWidth = this.state.svg.width - 2 * this.state.imageOffset.x;
|
|
897
|
+
const imgHeight = this.state.svg.height - 2 * this.state.imageOffset.y;
|
|
898
|
+
|
|
899
|
+
const normX = (x - this.state.imageOffset.x) / imgWidth;
|
|
900
|
+
const normY = (y - this.state.imageOffset.y) / imgHeight;
|
|
901
|
+
|
|
902
|
+
const type =
|
|
903
|
+
this.props.selectedTool === TOOLS.POSITIVE_POINT
|
|
904
|
+
? "positive"
|
|
905
|
+
: "negative";
|
|
906
|
+
|
|
907
|
+
this.props.onSamPointClick(x, y, normX, normY, type);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
handleMouseDown(e) {
|
|
912
|
+
if (this.props.selectedTool === TOOLS.SAM_BBOX) {
|
|
913
|
+
if (e.button !== 2) {
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
e.preventDefault();
|
|
918
|
+
const { x, y } = this.getMousePosition(e);
|
|
919
|
+
|
|
920
|
+
this.setState({
|
|
921
|
+
isDrawingSamBBox: true,
|
|
922
|
+
samBBoxStartPoint: { x, y },
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
// start a new rectangle
|
|
926
|
+
this.props.onUpdateSamBBox({
|
|
927
|
+
x,
|
|
928
|
+
y,
|
|
929
|
+
width: 0,
|
|
930
|
+
height: 0,
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
863
935
|
handleImgBarMouseEnter(e) {
|
|
864
936
|
this.setState({ imgBarVisible: false });
|
|
865
937
|
}
|
|
@@ -870,6 +942,64 @@ class Canvas extends Component {
|
|
|
870
942
|
|
|
871
943
|
handleSvgMouseMove(e) {
|
|
872
944
|
this.mousePosAbs = mouse.getMousePositionAbs(e, this.state.svg);
|
|
945
|
+
|
|
946
|
+
if (this.props.selectedTool === TOOLS.SAM_BBOX) {
|
|
947
|
+
if (!this.state.isDrawingSamBBox || !this.state.samBBoxStartPoint) {
|
|
948
|
+
// If not drawing a SAM bbox, do nothing
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
const { x, y } = this.getMousePosition(e);
|
|
953
|
+
const startPoint = this.state.samBBoxStartPoint;
|
|
954
|
+
|
|
955
|
+
const imgWidth = this.state.svg.width - 2 * this.state.imageOffset.x;
|
|
956
|
+
const imgHeight = this.state.svg.height - 2 * this.state.imageOffset.y;
|
|
957
|
+
|
|
958
|
+
const xMin = Math.min(startPoint.x, x);
|
|
959
|
+
const yMin = Math.min(startPoint.y, y);
|
|
960
|
+
const width = Math.abs(x - startPoint.x);
|
|
961
|
+
const height = Math.abs(y - startPoint.y);
|
|
962
|
+
|
|
963
|
+
// normalized coordinates
|
|
964
|
+
const xMinNorm = (xMin - this.state.imageOffset.x) / imgWidth;
|
|
965
|
+
const yMinNorm = (yMin - this.state.imageOffset.y) / imgHeight;
|
|
966
|
+
const xMaxNorm = (xMin + width - this.state.imageOffset.x) / imgWidth;
|
|
967
|
+
const yMaxNorm = (yMin + height - this.state.imageOffset.y) / imgHeight;
|
|
968
|
+
|
|
969
|
+
// update the rectangle with the new width and height
|
|
970
|
+
this.props.onUpdateSamBBox({
|
|
971
|
+
x: xMin,
|
|
972
|
+
y: yMin,
|
|
973
|
+
width,
|
|
974
|
+
height,
|
|
975
|
+
xMinNorm,
|
|
976
|
+
yMinNorm,
|
|
977
|
+
xMaxNorm,
|
|
978
|
+
yMaxNorm,
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
handleMouseUp(e) {
|
|
984
|
+
if (this.props.selectedTool === TOOLS.SAM_BBOX) {
|
|
985
|
+
if (e.button !== 2) {
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
e.preventDefault();
|
|
990
|
+
this.setState({
|
|
991
|
+
isDrawingSamBBox: false,
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
handleMouseLeave(e) {
|
|
997
|
+
if (this.props.selectedTool === TOOLS.SAM_BBOX) {
|
|
998
|
+
// If mouse leaves the canvas while drawing a SAM bbox, reset the state
|
|
999
|
+
this.setState({
|
|
1000
|
+
isDrawingSamBBox: false,
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
873
1003
|
}
|
|
874
1004
|
|
|
875
1005
|
handleNotification(messageObj) {
|
|
@@ -929,7 +1059,7 @@ class Canvas extends Component {
|
|
|
929
1059
|
// const corrected = transform.correctAnnotation(anno.data, this.props.svg, this.props.imageOffset)
|
|
930
1060
|
if (this.clipboard) {
|
|
931
1061
|
// let annos = [...this.state.annos]
|
|
932
|
-
const uid =
|
|
1062
|
+
const uid = uniqueId("new");
|
|
933
1063
|
// this.handleAnnoEvent()
|
|
934
1064
|
const newData = this.clipboard.data.map((e) => {
|
|
935
1065
|
return { x: e.x + offset, y: e.y + offset };
|
|
@@ -1333,6 +1463,7 @@ class Canvas extends Component {
|
|
|
1333
1463
|
//Do not create new Annotation if controlKey was pressed!
|
|
1334
1464
|
let allowed = false;
|
|
1335
1465
|
if (this.keyMapper.controlDown) return;
|
|
1466
|
+
if (this.keyMapper.shiftDown) return;
|
|
1336
1467
|
if (this.props.selectedTool) {
|
|
1337
1468
|
const maxAnnos = this.props.canvasConfig.annos.maxAnnos;
|
|
1338
1469
|
if (maxAnnos) {
|
|
@@ -1364,7 +1495,7 @@ class Canvas extends Component {
|
|
|
1364
1495
|
const mousePos = this.getMousePosition(e);
|
|
1365
1496
|
// const selAnno = this.findAnno(this.state.selectedAnnoId)
|
|
1366
1497
|
let newAnno = {
|
|
1367
|
-
id: this.props.nextAnnoId ? this.props.nextAnnoId :
|
|
1498
|
+
id: this.props.nextAnnoId ? this.props.nextAnnoId : uniqueId("new"),
|
|
1368
1499
|
type: this.props.selectedTool,
|
|
1369
1500
|
data: [
|
|
1370
1501
|
{
|
|
@@ -1587,6 +1718,7 @@ class Canvas extends Component {
|
|
|
1587
1718
|
* the new annos list that was set in state
|
|
1588
1719
|
*/
|
|
1589
1720
|
updateSelectedAnno(anno, mode = undefined, returnNewAnno = false) {
|
|
1721
|
+
// console.log('updateSelectedAnno: ', mode, anno)
|
|
1590
1722
|
if (!anno) return;
|
|
1591
1723
|
const { newAnnos, newAnno } = this.mergeSelectedAnno(anno, mode);
|
|
1592
1724
|
this.setState({
|
|
@@ -1878,6 +2010,34 @@ class Canvas extends Component {
|
|
|
1878
2010
|
);
|
|
1879
2011
|
}
|
|
1880
2012
|
|
|
2013
|
+
renderSamPoints() {
|
|
2014
|
+
return this.props.samPoints.map((point, index) => (
|
|
2015
|
+
<circle
|
|
2016
|
+
key={index}
|
|
2017
|
+
cx={point.x}
|
|
2018
|
+
cy={point.y}
|
|
2019
|
+
r={5}
|
|
2020
|
+
fill={point.type === "positive" ? "lightgreen" : "lightcoral"}
|
|
2021
|
+
/>
|
|
2022
|
+
));
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
renderSamBBox() {
|
|
2026
|
+
return (
|
|
2027
|
+
this.props.samBBox && (
|
|
2028
|
+
<rect
|
|
2029
|
+
x={this.props.samBBox.x}
|
|
2030
|
+
y={this.props.samBBox.y}
|
|
2031
|
+
width={this.props.samBBox.width}
|
|
2032
|
+
height={this.props.samBBox.height}
|
|
2033
|
+
fill="rgba(0, 0, 255, 0.1)"
|
|
2034
|
+
stroke="blue"
|
|
2035
|
+
strokeWidth="2"
|
|
2036
|
+
/>
|
|
2037
|
+
)
|
|
2038
|
+
);
|
|
2039
|
+
}
|
|
2040
|
+
|
|
1881
2041
|
render() {
|
|
1882
2042
|
const selectedAnno = this.findAnno(this.state.selectedAnnoId);
|
|
1883
2043
|
return (
|
|
@@ -1970,6 +2130,10 @@ class Canvas extends Component {
|
|
|
1970
2130
|
onKeyUp={(e) => this.onKeyUp(e)}
|
|
1971
2131
|
onClick={(e) => this.handleCanvasClick(e)}
|
|
1972
2132
|
onMouseMove={(e) => this.handleSvgMouseMove(e)}
|
|
2133
|
+
onContextMenu={(e) => this.handleContextMenu(e)}
|
|
2134
|
+
onMouseDown={(e) => this.handleMouseDown(e)}
|
|
2135
|
+
onMouseUp={(e) => this.handleMouseUp(e)}
|
|
2136
|
+
onMouseLeave={(e) => this.handleMouseLeave(e)}
|
|
1973
2137
|
tabIndex="0"
|
|
1974
2138
|
>
|
|
1975
2139
|
<g
|
|
@@ -2005,6 +2169,8 @@ class Canvas extends Component {
|
|
|
2005
2169
|
}
|
|
2006
2170
|
/>
|
|
2007
2171
|
{this.renderAnnotations()}
|
|
2172
|
+
{this.renderSamPoints()}
|
|
2173
|
+
{this.renderSamBBox()}
|
|
2008
2174
|
</g>
|
|
2009
2175
|
</svg>
|
|
2010
2176
|
<img
|
package/src/Sia.jsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import ToolBar from "./ToolBar";
|
|
4
3
|
import Canvas from "./Canvas";
|
|
5
|
-
import * as tbe from "./types/toolbarEvents";
|
|
6
|
-
import * as annoActions from "./types/canvasActions";
|
|
7
4
|
import { noAnnos } from "./siaDummyData";
|
|
5
|
+
import ToolBar from "./ToolBar";
|
|
6
|
+
import * as annoActions from "./types/canvasActions";
|
|
7
|
+
import * as tbe from "./types/toolbarEvents";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* SIA element that handles annotations within an image
|
|
@@ -446,6 +446,10 @@ const Sia = (props) => {
|
|
|
446
446
|
isImageChanging={props.isImageChanging}
|
|
447
447
|
isStaticPosition={props.isStaticPosition}
|
|
448
448
|
fixedImageSize={props.fixedImageSize}
|
|
449
|
+
samPoints={props.samPoints || []}
|
|
450
|
+
onSamPointClick={props.onSamPointClick}
|
|
451
|
+
samBBox={props.samBBox || null}
|
|
452
|
+
onUpdateSamBBox={props.onUpdateSamBBox}
|
|
449
453
|
/>
|
|
450
454
|
<ToolBar
|
|
451
455
|
onToolBarEvent={(e, data) => handleToolBarEvent(e, data)}
|
package/src/ToolBar.jsx
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
faPaperPlane,
|
|
3
|
+
faSquare,
|
|
4
|
+
faTrashCan,
|
|
5
|
+
} from "@fortawesome/free-regular-svg-icons";
|
|
3
6
|
import {
|
|
4
7
|
faArrowLeft,
|
|
5
8
|
faArrowRight,
|
|
6
9
|
faBan,
|
|
7
10
|
faCheck,
|
|
8
11
|
faMaximize,
|
|
12
|
+
faMinusCircle,
|
|
13
|
+
faPlusCircle,
|
|
9
14
|
faQuestion,
|
|
10
15
|
faSave,
|
|
11
16
|
faSearch,
|
|
@@ -13,18 +18,19 @@ import {
|
|
|
13
18
|
faTrash,
|
|
14
19
|
faVectorSquare,
|
|
15
20
|
} from "@fortawesome/free-solid-svg-icons";
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
18
|
-
import SIAFilterButton from "./SIAFilterButton";
|
|
21
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
22
|
+
import { Button, Card } from "semantic-ui-react";
|
|
19
23
|
import Prompt from "./Prompt";
|
|
24
|
+
import SIAFilterButton from "./SIAFilterButton";
|
|
25
|
+
import SIASettingButton from "./SIASettingButton";
|
|
20
26
|
import "./Toolbar.css";
|
|
21
27
|
|
|
22
|
-
import * as TOOLS from "./types/tools";
|
|
23
|
-
import * as siaIcons from "./utils/siaIcons";
|
|
24
|
-
import * as tbe from "./types/toolbarEvents";
|
|
25
28
|
import { CSidebar, CSidebarNav } from "@coreui/react";
|
|
26
|
-
import ToolbarItem from "./ToolbarItem";
|
|
27
29
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
30
|
+
import ToolbarItem from "./ToolbarItem";
|
|
31
|
+
import * as tbe from "./types/toolbarEvents";
|
|
32
|
+
import * as TOOLS from "./types/tools";
|
|
33
|
+
import * as siaIcons from "./utils/siaIcons";
|
|
28
34
|
|
|
29
35
|
const ToolBar = (props) => {
|
|
30
36
|
const [position, setPosition] = useState({
|
|
@@ -125,6 +131,44 @@ const ToolBar = (props) => {
|
|
|
125
131
|
if (!props.enabled.toolSelection) return null;
|
|
126
132
|
if (!props.canvasConfig.annos.actions.draw) return null;
|
|
127
133
|
let btns = [];
|
|
134
|
+
|
|
135
|
+
if (props.canvasConfig.tools.sam) {
|
|
136
|
+
btns.push(
|
|
137
|
+
<ToolbarItem
|
|
138
|
+
color="#0081FB"
|
|
139
|
+
active={props.active.selectedTool === TOOLS.POSITIVE_POINT}
|
|
140
|
+
onClick={(e) => onClick(e, TOOLS.POSITIVE_POINT)}
|
|
141
|
+
faIcon={faPlusCircle}
|
|
142
|
+
/>,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
btns.push(
|
|
146
|
+
<ToolbarItem
|
|
147
|
+
color="#0081FB"
|
|
148
|
+
active={props.active.selectedTool === TOOLS.NEGATIVE_POINT}
|
|
149
|
+
onClick={(e) => onClick(e, TOOLS.NEGATIVE_POINT)}
|
|
150
|
+
faIcon={faMinusCircle}
|
|
151
|
+
/>,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
btns.push(
|
|
155
|
+
<ToolbarItem
|
|
156
|
+
color="#0081FB"
|
|
157
|
+
active={props.active.selectedTool === TOOLS.SAM_BBOX}
|
|
158
|
+
onClick={(e) => onClick(e, TOOLS.SAM_BBOX)}
|
|
159
|
+
faIcon={faSquare}
|
|
160
|
+
/>,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
btns.push(
|
|
164
|
+
<ToolbarItem
|
|
165
|
+
color="#0081FB"
|
|
166
|
+
onClick={(e) => triggerToolBarEvent(tbe.CLEAR_SAM_HELPER_ANNOS)}
|
|
167
|
+
faIcon={faTrashCan}
|
|
168
|
+
/>,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
128
172
|
if (props.canvasConfig.tools.point) {
|
|
129
173
|
btns.push(
|
|
130
174
|
<ToolbarItem
|
|
@@ -279,8 +323,9 @@ const ToolBar = (props) => {
|
|
|
279
323
|
<Card.Content description="Redo: Hit STRG + R" />
|
|
280
324
|
</Card>
|
|
281
325
|
<Card>
|
|
282
|
-
<Card.Content header="Add a node to Line/Polygon" />
|
|
283
|
-
<Card.Content description="Hit STRG + Click left on the line" />
|
|
326
|
+
<Card.Content header="Add/ Remove a node to Line/Polygon" />
|
|
327
|
+
<Card.Content description="Add: Hit STRG + Click left on the line" />
|
|
328
|
+
<Card.Content description="Remove: Hit STRG + Click left on the node to delete" />
|
|
284
329
|
</Card>
|
|
285
330
|
<Card>
|
|
286
331
|
<Card.Content header="Remove a node from Line/Polygon in create mode" />
|
package/src/ToolbarItem.jsx
CHANGED
|
@@ -8,6 +8,7 @@ export const ANNO_EDITED = "annoEdited";
|
|
|
8
8
|
export const ANNO_ENTER_EDIT_MODE = "annoEnterEditMode";
|
|
9
9
|
export const ANNO_ADDED_NODE = "annoAddedNode";
|
|
10
10
|
export const ANNO_REMOVED_NODE = "annoRemovedNode";
|
|
11
|
+
export const ANNO_REMOVED_SPECIFIC_NODE = "annoRemovedSpecificNode";
|
|
11
12
|
export const ANNO_LABEL_UPDATE = "annoLabelUpdate";
|
|
12
13
|
export const ANNO_CREATED_NODE = "annoCreatedNode";
|
|
13
14
|
export const ANNO_CREATED_FINAL_NODE = "annoCreatedFinalNode";
|
package/src/types/modes.js
CHANGED
|
@@ -3,6 +3,7 @@ export const EDIT = "edit";
|
|
|
3
3
|
export const CREATE = "create";
|
|
4
4
|
export const MOVE = "move";
|
|
5
5
|
export const ADD = "add";
|
|
6
|
+
export const REMOVE_NODE = "removeNode";
|
|
6
7
|
export const CAMERA_MOVE = "cameraMove";
|
|
7
8
|
export const EDIT_LABEL = "editLabel";
|
|
8
9
|
export const DELETED = "deleted";
|
|
@@ -13,6 +13,7 @@ export const EDIT_STROKE_WIDTH = "editStrokeWidth";
|
|
|
13
13
|
export const EDIT_NODE_RADIUS = "editNodeRadius";
|
|
14
14
|
export const APPLY_FILTER = "applyFilter";
|
|
15
15
|
export const SAVE = "save";
|
|
16
|
+
export const CLEAR_SAM_HELPER_ANNOS = "clearSamHelperAnnos"; // for clearing SAM helper annotations
|
|
16
17
|
|
|
17
18
|
export default {
|
|
18
19
|
TOOL_SELECTED,
|
|
@@ -30,4 +31,5 @@ export default {
|
|
|
30
31
|
EDIT_NODE_RADIUS,
|
|
31
32
|
APPLY_FILTER,
|
|
32
33
|
SAVE,
|
|
34
|
+
CLEAR_POINTS: CLEAR_SAM_HELPER_ANNOS,
|
|
33
35
|
};
|
package/src/types/tools.js
CHANGED
|
@@ -2,10 +2,16 @@ export const POINT = "point";
|
|
|
2
2
|
export const BBOX = "bBox";
|
|
3
3
|
export const LINE = "line";
|
|
4
4
|
export const POLYGON = "polygon";
|
|
5
|
+
export const POSITIVE_POINT = "positivePoint"; // for positive point SAM
|
|
6
|
+
export const NEGATIVE_POINT = "negativePoint"; // for negative point SAM
|
|
7
|
+
export const SAM_BBOX = "samBBox"; // bbox for guiding SAM
|
|
5
8
|
|
|
6
9
|
export default {
|
|
7
10
|
POINT,
|
|
8
11
|
BBOX,
|
|
9
12
|
LINE,
|
|
10
13
|
POLYGON,
|
|
14
|
+
POSITIVE_POINT,
|
|
15
|
+
NEGATIVE_POINT,
|
|
16
|
+
SAM_BBOX,
|
|
11
17
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as transform from "./transform";
|
|
2
2
|
import * as annoStatus from "../types/annoStatus";
|
|
3
3
|
import * as modes from "../types/modes";
|
|
4
|
-
import
|
|
4
|
+
import { uniqueId } from "lodash-es";
|
|
5
5
|
|
|
6
6
|
function _fixBackendAnnoElement(element) {
|
|
7
7
|
return {
|
|
8
8
|
...element,
|
|
9
|
-
id: element.id ? element.id :
|
|
9
|
+
id: element.id ? element.id : uniqueId("new"),
|
|
10
10
|
annoTime: element.annoTime ? element.annoTime : 0.0,
|
|
11
11
|
mode: element.mode ? element.mode : modes.VIEW,
|
|
12
12
|
status: element.status ? element.status : annoStatus.DATABASE,
|
package/src/utils/keyActions.js
CHANGED
|
@@ -19,6 +19,7 @@ export const TOGGLE_ANNO_COMMENT_INPUT = "toggleAnnoCommentInput";
|
|
|
19
19
|
class KeyMapper {
|
|
20
20
|
constructor(keyActionHandler = undefined) {
|
|
21
21
|
this.controlDown = false;
|
|
22
|
+
this.shiftDown = false;
|
|
22
23
|
this.keyActionHandler = keyActionHandler;
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -37,6 +38,9 @@ class KeyMapper {
|
|
|
37
38
|
this.controlDown = true;
|
|
38
39
|
this.triggerKeyAction(ENTER_ANNO_ADD_MODE);
|
|
39
40
|
break;
|
|
41
|
+
case "Shift":
|
|
42
|
+
this.shiftDown = true;
|
|
43
|
+
break;
|
|
40
44
|
case "z":
|
|
41
45
|
if (this.controlDown) {
|
|
42
46
|
this.triggerKeyAction(UNDO);
|
|
@@ -91,6 +95,9 @@ class KeyMapper {
|
|
|
91
95
|
this.controlDown = false;
|
|
92
96
|
this.triggerKeyAction(LEAVE_ANNO_ADD_MODE);
|
|
93
97
|
break;
|
|
98
|
+
case "Shift":
|
|
99
|
+
this.shiftDown = false;
|
|
100
|
+
break;
|
|
94
101
|
default:
|
|
95
102
|
break;
|
|
96
103
|
}
|