lost-sia 3.1.3 → 3.2.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.
- package/dist/Canvas/Canvas.js +1 -1
- package/dist/SiaViewer.d.ts +19 -0
- package/dist/SiaViewer.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/stories/Canvas/Canvas.stories.d.ts +3 -3
- package/dist/stories/Canvas/CanvasWithOffset.stories.d.ts +6 -6
- package/dist/stories/SIA/SiaViewer.stories.d.ts +52 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -1
- package/package.json +3 -4
- package/src/Canvas/Canvas.tsx +36 -1
- package/src/SiaViewer.tsx +125 -0
- package/src/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.tsx +1 -2
- package/src/index.ts +1 -1
- package/src/stories/SIA/SiaViewer.stories.tsx +105 -0
- package/src/stories/exampleData/exampleExternalAnnotations.ts +22 -22
- package/src/utils/index.ts +1 -0
- package/dist/assets/brand-icons-Cu_C0hZ4.svg +0 -1008
- package/dist/assets/brand-icons-F3SPCeH1.woff +0 -0
- package/dist/assets/brand-icons-XL9sxUpA.woff2 +0 -0
- package/dist/assets/brand-icons-sqJ2Pg7a.eot +0 -0
- package/dist/assets/brand-icons-ubhWoxly.ttf +0 -0
- package/dist/assets/flags-DOLqOU7Y.png +0 -0
- package/dist/assets/icons-BOCtAERH.woff +0 -0
- package/dist/assets/icons-CHzK1VD9.eot +0 -0
- package/dist/assets/icons-D29ZQHHw.ttf +0 -0
- package/dist/assets/icons-Du6TOHnR.woff2 +0 -0
- package/dist/assets/icons-RwhydX30.svg +0 -1518
- package/dist/assets/node_modules/semantic-ui-css/semantic.min-09YPtVE6.css +0 -1
- package/dist/assets/outline-icons-BfdLr8tr.svg +0 -366
- package/dist/assets/outline-icons-DD8jm0uy.ttf +0 -0
- package/dist/assets/outline-icons-DInHoiqI.woff2 +0 -0
- package/dist/assets/outline-icons-LX8adJ4n.eot +0 -0
- package/dist/assets/outline-icons-aQ88nltS.woff +0 -0
- package/src/AnnoExampleViewer.jsx +0 -69
- package/src/InfoBoxes/AnnoDetails.jsx +0 -165
- package/src/InfoBoxes/AnnoStats.jsx +0 -106
- package/src/InfoBoxes/InfoBox.jsx +0 -76
- package/src/InfoBoxes/InfoBoxArea.jsx +0 -152
- package/src/InfoBoxes/LabelInfo.jsx +0 -107
- package/src/SIASettingButton.jsx +0 -126
package/dist/Canvas/Canvas.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsxs as
|
|
1
|
+
import{jsxs as U,jsx as p,Fragment as qe}from"react/jsx-runtime";import{useState as g,useRef as q,useEffect as C}from"react";import b from"../models/AnnotationTool.js";import s from"../models/EditorModes.js";import et from"../utils/KeyMapper.js";import a from"../models/KeyAction.js";import xe from"../Annotation/logic/Annotation.js";import tt from"../models/CanvasAction.js";import nt from"../Annotation/ui/AnnotationComponent.js";import he from"../utils/mouse.js";import R from"../models/AnnotationMode.js";import ot from"./LabelInput.js";import{FontAwesomeIcon as rt}from"@fortawesome/react-fontawesome";import{faBan as st}from"@fortawesome/free-solid-svg-icons";import w from"../models/AnnotationStatus.js";import A from"../utils/transform.js";import it from"../models/NotificationType.js";import at from"../utils/TimeUtils.js";import Ce from"../utils/windowViewport.js";const Dt=({annotations:I=[],annotationSettings:D,defaultLabelId:ee,image:T,isFullscreen:Ie=!1,isImageJunk:Y=!1,isPolygonSelectionMode:z=!1,polygonOperationResult:G={annotationsToDelete:[],polygonsToCreate:[]},possibleLabels:te,preventScrolling:ne=!0,selectedAnnotation:i,selectedAnnoTool:V,toolbarHeight:H=0,uiConfig:P,onAnnoCreated:ve,onAnnoCreationFinished:oe,onAnnoChanged:re,onAnnoEditing:Te=c=>{},onNotification:se=c=>{},onRequestNewAnnoId:k,onSelectAnnotation:v,onSetIsImageJunk:Me,onSetSelectedTool:Oe=c=>{},onShouldDeleteAnno:ie,onTraverseAnnotationHistory:ae})=>{const[c,m]=g(s.VIEW),[be,we]=g(),[ce,De]=g(ee),[L,Se]=g({x:-1,y:-1}),[le,de]=g(0),W={x:L.x,y:L.y},[f,B]=g({x:-1,y:-1}),[d,$]=g({x:-1,y:-1}),[y,ue]=g({x:-1,y:-1}),[u,K]=g(1),[l,S]=g({x:0,y:0}),j={x:l.x+le,y:l.y},[J,F]=g(),[Q,_]=g(!1),fe=q(null),x=q(null),M=q(null),h=((e,t)=>{if(e.x===0||e.y===0||t.x===0||t.y===0)return 0;const n=t.x/e.x,o=t.y/e.y;return Math.min(n,o)})(f,d),_e=()=>{if(M?.current===null)return{x:0,y:0};const e=f.x*h;if(P.imageCentered&&d.x>e){const E=(d.x-e)/2;de(E)}else de(0);const{top:t,left:n}=x.current.getBoundingClientRect(),o={x:n+window.scrollX,y:t+window.scrollY};Se(o)},Ne=new et(e=>Be(e)),Re=e=>{m(s.CREATE);const t=A.convertStageCoordinatesToPercentaged([e],h,f);V===b.BBox&&t.push(t[0]);const n=k(),o=new xe(n,V,t);if(we(performance.now()),ce!==void 0&&(o.labelIds=[ce]),ve(o),V===b.Point){const r={...o,coordinates:[e],annoTime:0};Z(r)}},Ve=()=>{if(i&&![b.Line,b.Polygon].includes(i.type))return;const e=I.find(n=>n.internalId===i?.internalId);if(e===void 0)return;m(s.CREATE),Oe(e.type);const t={...e,mode:R.CREATE,status:w.CREATING,internalId:k(),selectedNode:e.coordinates.length-1};Te(t)},Pe=()=>{const e=i?i.internalId:0,t=I.find(n=>n.internalId>e);if(t)return v(t);if(I.length>0)return v(I[0])},ke=()=>{const e=i?i.internalId:0,t=[...I];t.sort((o,r)=>r.internalId-o.internalId);const n=t.find(o=>o.internalId<e);if(n)return v(n);if(I.length>0)return v(I.at(-1))},Le=()=>{if(i){const e=JSON.stringify(i);localStorage.setItem("lostAnnotationClipboard",e);const t={title:"Success",message:"Annotation copied",type:it.SUCCESS};se(t)}},We=()=>{const e=localStorage.getItem("lostAnnotationClipboard");if(e==null)return;const t=JSON.parse(e);t.internalId=k(),t.externalId="",oe(t,!0),v(t)},Ee=e=>{const t=A.getMostLeftPoints(e),n=A.getTopPoint(t)[0];return A.convertStageToPage(n,W,u,l)},Be=e=>{switch(e){case a.EDIT_LABEL:if(i){const t=A.convertPercentagedCoordinatesToStage(i.coordinates,f,y);F(Ee(t)),_(!0)}break;case a.DELETE_ANNO:i&&ie(i.internalId);break;case a.DELETE_ANNO_IN_CREATION:c===s.CREATE&&(ie(i.internalId),m(s.VIEW));break;case a.ENTER_ANNO_ADD_MODE:console.log("KeyAction TODO: ENTER_ANNO_ADD_MODE");break;case a.LEAVE_ANNO_ADD_MODE:console.log("KeyAction TODO: LEAVE_ANNO_ADD_MODE");break;case a.UNDO:ae(!0);break;case a.REDO:ae(!1);break;case a.TRAVERSE_ANNOS:Pe();break;case a.TRAVERSE_ANNOS_BACKWARDS:ke();break;case a.CAM_MOVE_LEFT:N(20*u,0);break;case a.CAM_MOVE_RIGHT:N(-20*u,0);break;case a.CAM_MOVE_UP:N(0,20*u);break;case a.CAM_MOVE_DOWN:N(0,-20*u);break;case a.CAM_MOVE_STOP:console.log("KeyAction TODO: CAM_MOVE_STOP");break;case a.COPY_ANNOTATION:Le();break;case a.PASTE_ANNOTATION:We();break;case a.RECREATE_ANNO:console.log("KeyAction TODO: RECREATE_ANNO"),Ve();break;case a.TOGGLE_IMAGE_JUNK:if(c===s.ADD||c===s.CREATE)return;Me(!Y);break;default:console.log("Unknown KeyAction",e);break}},N=(e,t)=>{let n=l.x+e/u,o=l.y+t/u;const r=d.x*.45,E=d.x*.55,O=d.y*.45,Qe=d.y*.55,Ze={x:0,y:0},me=Ce.getViewportCoordinates(l,d,u,Ze),Ae=Ce.getViewportCoordinates(l,d,u,d);me.vX>=r?n=l.x-5:Ae.vX<=E?n=l.x+5:me.vY>=O?o=l.y-5:Ae.vY<=Qe&&(o=l.y+5),S({x:n,y:o})},X=(e=>y.x<=0||y.y<=0||f.x<=0||f.y<=0?[]:I.map(n=>({...n,coordinates:A.convertPercentagedCoordinatesToStage(n.coordinates,f,y)})))(),Ke=()=>{if(m(s.VIEW),ue({x:-1,y:-1}),M.current!==null){const{width:e,height:t}=M.current.getBoundingClientRect();B({x:e,y:t})}K(1),S({x:0,y:0}),F(void 0),_(!1)};C(()=>{fe.current?.focus()},[]),C(()=>{const e=t=>{t.button===1&&c===s.CAMERA_MOVE&&m(s.VIEW)};return window.addEventListener("mouseup",e),()=>window.removeEventListener("mouseup",e)},[c]),C(()=>{if(x?.current!==void 0){Ke();const{width:e,height:t}=x.current.getBoundingClientRect(),n=t-H;$({x:e,y:n});const o=new ResizeObserver(()=>{const{width:r,height:E}=x.current.getBoundingClientRect(),O=E-H;$({x:r,y:O})});return o.observe(x.current),()=>o.disconnect()}},[T,Ie]),C(()=>{_e()},[d,f,P]),C(()=>{if(x.current===null)return;const{width:e,height:t}=x.current.getBoundingClientRect(),n=t-H;$({x:e,y:n})},[x]),C(()=>{if(M.current===null)return;const{width:e,height:t}=M.current.getBoundingClientRect();B({x:e,y:t})},[T,d]),C(()=>{if(!T){B({x:-1,y:-1});return}let e=!1;const t=new Image;return t.onload=()=>{if(e)return;const{naturalWidth:n,naturalHeight:o}=t;n>0&&o>0&&B({x:n,y:o})},t.src=T,()=>{e=!0,t.onload=null}},[T]),C(()=>{if(h===0)return;const e={x:f.x*h,y:f.y*h};ue(e)},[h,f]),C(()=>{z&&G.polygonsToCreate!==void 0&&G.polygonsToCreate.forEach(e=>{const t=k(),n=new xe(t,e.type,A.convertPercentagedCoordinatesToStage(e.coordinates,f,y),R.VIEW,w.CREATED);e.labelIds!==void 0&&(n.labelIds=e.labelIds),Z(n)})},[G]);const Z=e=>{m(s.VIEW);const t={...e,mode:R.VIEW};if(e.type!==b.Point){const r=at.getRoundedDuration(be,performance.now());t.annoTime=r}const n=A.convertStageCoordinatesToPercentaged(e.coordinates,h,f);t.coordinates=n,re(t);const o=V===b.Point||z;oe(t,o)},Fe=e=>{Ne.keyDown(e.key,e.shiftKey,e.ctrlKey)&&e.preventDefault()},Xe=e=>{e.preventDefault()},Ue=e=>{if(e.button!==0){if(e.button===1){if(c===s.CREATE||c===s.ADD){e.preventDefault();return}m(s.CAMERA_MOVE)}else if(e.button===2){if(!D.canCreate||c===s.ADD||c===s.CREATE)return;const t=he.getAntiScaledMouseStagePosition(e,W,u,l),n={x:t.x-le,y:t.y};Re(n)}}},Ye=()=>{ne&&(document.body.style.overflow="hidden")},ze=e=>{e.button===1&&c===s.CAMERA_MOVE&&m(s.VIEW)},ge=(e,t)=>{c===s.CAMERA_MOVE&&N(e,t)},Ge=()=>{ne&&(document.body.style.overflow="")},He=e=>{const o=(e.deltaY<0?1:-1)>0?u*1.25:u/1.25,r=he.getAntiScaledMouseStagePosition(e,W,u,l),E=u/o,O={x:E*(r.x+l.x)-r.x,y:E*(r.y+l.y)-r.y};o<1?(K(1),(l.x!=0||l.y!=0)&&S({x:0,y:0})):o>200?(K(200),S(O)):(K(o),S(O))},$e=(e,t)=>{if(t!==tt.ANNO_SELECTED){console.log("Unknown Canvas Action:",t);return}const n={...e,coordinates:A.convertStageCoordinatesToPercentaged([...e.coordinates],h,f)};v(n),D.canLabel&&F(Ee(e.coordinates))},ye=e=>{const t=A.convertStageCoordinatesToPercentaged(e.coordinates,h,f),n={...e,coordinates:t};n.status===w.LOADED&&(n.status=w.CHANGED),re(n)},je=()=>{if(c===s.CAMERA_MOVE)return p(qe,{});const t=[s.CREATE,s.ADD,s.MOVE].includes(c),n=X.map(o=>{const r=o.internalId===i?.internalId;return t&&!r?p("g",{},`annotationComponent_${o.internalId}`):p(nt,{scaledAnnotation:o,annotationSettings:D,possibleLabels:te,svgScale:u,svgTranslation:j,pageToStageOffset:W,nodeRadius:P.nodeRadius,strokeWidth:P.strokeWidth,isSelected:r,isDisabled:z&&r,onFinishAnnoCreate:Z,onLabelIconClicked:()=>_(!0),onAction:$e,onAnnoChanged:ye,onAnnotationModeChange:E=>{E===R.MOVE&&m(s.MOVE),c===s.MOVE&&E===R.VIEW&&m(s.VIEW)},onNotification:se},`annotationComponent_${o.internalId}`)});if(i){const o=X.find(E=>E.internalId===i?.internalId),r=X.indexOf(o);n.push(n.splice(r,1)[0])}return p("g",{children:n})},Je=()=>p("circle",{cx:y.x/2,cy:y.y/2,r:"100%",style:{opacity:0},onContextMenu:e=>e.preventDefault(),onClick:()=>{_(!1)}}),pe={x:L.x+d.x/2,y:L.y+d.y/2};return U("div",{ref:x,style:{flex:"1 1 auto",minHeight:0,display:"flex",flexDirection:"column"},children:[D.canLabel&&p("div",{style:{position:"absolute",left:J?.x??0,top:J?.y??0,display:J?.y===void 0?"none":"inherit",zIndex:Q?7e3:-1},children:p(ot,{defaultLabelId:ee,isVisible:Q,selectedLabelsIds:i?.labelIds,possibleLabels:te,isMultilabel:D.canHaveMultipleLabels,onLabelSelect:e=>{if(setTimeout(()=>_(!1),0),e.length>0){const r=e.filter(E=>!i.labelIds.includes(E));r.length>0&&De(r[0])}const t=X.find(r=>r.internalId===i.internalId);if(!t)return;const n=t.status===w.LOADED?w.CHANGED:t.status,o={...i,coordinates:t.coordinates,labelIds:[...e],status:n};ye(o)}})}),Y&&U("div",{style:{position:"absolute",left:pe.x,top:pe.y,transform:"translate(-50%, -50%)",textAlign:"center",color:"white"},children:[p(rt,{icon:st,size:"5x",style:{marginBottom:15}}),p("h2",{children:"Marked as Junk"})]}),U("svg",{ref:fe,style:{flex:"1 1 auto",minHeight:0},onKeyDown:Fe,onKeyUp:Xe,onMouseMove:e=>ge(e.movementX,e.movementY),tabIndex:0,onMouseDown:e=>Ue(e),children:[U("g",{transform:`scale(${u}) translate(${j.x}, ${j.y})`,onMouseOver:Ye,onMouseLeave:Ge,onMouseUp:ze,onWheel:He,onMouseMove:e=>ge(e.movementX,e.movementY),onClick:()=>{v(void 0)},children:[p("image",{onContextMenu:e=>e.preventDefault(),href:T,ref:M,width:y.x>0?y.x:void 0,height:y.y>0?y.y:void 0}),je()]}),Q&&Je(),Y&&p("rect",{x:"0",y:"0",width:d.x,height:d.y,style:{opacity:.8},onContextMenu:e=>e.preventDefault(),onClick:()=>{F(void 0)}})]})]})};export{Dt as default};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { default as Annotation } from './Annotation/logic/Annotation';
|
|
2
|
+
import { ExternalAnnotation, Label, UiConfig } from './types';
|
|
3
|
+
type SiaViewerProps = {
|
|
4
|
+
image: string;
|
|
5
|
+
annotations?: ExternalAnnotation[];
|
|
6
|
+
possibleLabels: Label[];
|
|
7
|
+
isJunk?: boolean;
|
|
8
|
+
uiConfig?: Partial<UiConfig>;
|
|
9
|
+
enableZoom?: boolean;
|
|
10
|
+
canSelect?: boolean;
|
|
11
|
+
onSelectAnnotation?: (annotation?: Annotation) => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* View-only SIA component. Renders image + annotations without a toolbar and
|
|
15
|
+
* blocks all mutations (create / edit / delete / label). Zoom and selection
|
|
16
|
+
* can be toggled via props.
|
|
17
|
+
*/
|
|
18
|
+
declare const SiaViewer: ({ image, annotations: propAnnotations, possibleLabels, isJunk, uiConfig: propUiConfig, enableZoom, canSelect, onSelectAnnotation, }: SiaViewerProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export default SiaViewer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as r}from"react/jsx-runtime";import{useState as s,useEffect as c}from"react";import p from"./Canvas/Canvas.js";import N from"./models/AnnotationMode.js";import h from"./models/AnnotationTool.js";const E={canCreate:!1,canEdit:!1,canLabel:!1,canHaveMultipleLabels:!1},f={nodeRadius:4,strokeWidth:4,imageCentered:!1},y={flex:"1 1 auto",minHeight:0,display:"flex",flexDirection:"column"},H=({image:n,annotations:e,possibleLabels:u,isJunk:m=!1,uiConfig:a,enableZoom:A=!0,canSelect:l=!0,onSelectAnnotation:I=()=>{}})=>{const[S,d]=s([]),[T,t]=s(),[C,g]=s(f);return c(()=>{if(n===void 0||e===void 0){d([]),t(void 0);return}let o=0;const v=e.map(i=>({...i,internalId:o++,mode:N.VIEW,selectedNode:1,status:i.status,annoTime:i.annoTime??0}));d(v),t(void 0)},[e,n]),c(()=>{g({...f,...a})},[a]),r("div",{style:y,onWheelCapture:o=>{A||o.stopPropagation()},children:r(p,{annotations:S,annotationSettings:E,image:n,isImageJunk:m,isPolygonSelectionMode:!1,possibleLabels:u,selectedAnnotation:l?T:void 0,selectedAnnoTool:h.Point,uiConfig:C,onAnnoCreated:()=>{},onAnnoChanged:()=>{},onAnnoCreationFinished:()=>{},onAnnoEditing:()=>{},onNotification:()=>{},onRequestNewAnnoId:()=>0,onSelectAnnotation:l?o=>{t(o),I(o)}:()=>{},onSetIsImageJunk:()=>{},onSetSelectedTool:()=>{},onShouldDeleteAnno:()=>{},onTraverseAnnotationHistory:()=>{}})})};export{H as default};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export { default as IconButton } from './IconButton';
|
|
3
3
|
export { default as Sia } from './Sia';
|
|
4
|
+
export { default as SiaViewer } from './SiaViewer';
|
|
4
5
|
export { default as transform } from './utils/transform';
|
|
5
6
|
export { default as TagLabel } from './Toolbar/ToolbarItems/ImageToolItems/TagLabel';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
/* empty css
|
|
1
|
+
/* empty css */import{default as t}from"./IconButton.js";import{default as f}from"./Sia.js";import{default as l}from"./SiaViewer.js";import{default as s}from"./utils/transform.js";import{default as d}from"./Toolbar/ToolbarItems/ImageToolItems/TagLabel.js";export{t as IconButton,f as Sia,l as SiaViewer,d as TagLabel,s as transform};
|
|
@@ -2,9 +2,9 @@ import { StoryObj } from '@storybook/react';
|
|
|
2
2
|
import { default as AnnotationTool } from '../../models/AnnotationTool';
|
|
3
3
|
import { AnnotationSettings, UiConfig } from '../../types';
|
|
4
4
|
export declare const ActionsData: {
|
|
5
|
-
onAnnoEvent: any
|
|
6
|
-
onKeyDown: any
|
|
7
|
-
onKeyUp: any
|
|
5
|
+
onAnnoEvent: import('@vitest/spy').Mock<(...args: any[]) => any>;
|
|
6
|
+
onKeyDown: import('@vitest/spy').Mock<(...args: any[]) => any>;
|
|
7
|
+
onKeyUp: import('@vitest/spy').Mock<(...args: any[]) => any>;
|
|
8
8
|
};
|
|
9
9
|
declare const meta: {
|
|
10
10
|
title: string;
|
|
@@ -2,9 +2,9 @@ import { StoryObj } from '@storybook/react';
|
|
|
2
2
|
import { default as AnnotationTool } from '../../models/AnnotationTool';
|
|
3
3
|
import { UiConfig } from '../../types';
|
|
4
4
|
export declare const ActionsData: {
|
|
5
|
-
onAnnoEvent: any
|
|
6
|
-
onKeyDown: any
|
|
7
|
-
onKeyUp: any
|
|
5
|
+
onAnnoEvent: import('@vitest/spy').Mock<(...args: any[]) => any>;
|
|
6
|
+
onKeyDown: import('@vitest/spy').Mock<(...args: any[]) => any>;
|
|
7
|
+
onKeyUp: import('@vitest/spy').Mock<(...args: any[]) => any>;
|
|
8
8
|
};
|
|
9
9
|
declare const meta: {
|
|
10
10
|
title: string;
|
|
@@ -22,9 +22,9 @@ declare const meta: {
|
|
|
22
22
|
tags: string[];
|
|
23
23
|
excludeStories: RegExp;
|
|
24
24
|
args: {
|
|
25
|
-
onAnnoEvent: any
|
|
26
|
-
onKeyDown: any
|
|
27
|
-
onKeyUp: any
|
|
25
|
+
onAnnoEvent: import('@vitest/spy').Mock<(...args: any[]) => any>;
|
|
26
|
+
onKeyDown: import('@vitest/spy').Mock<(...args: any[]) => any>;
|
|
27
|
+
onKeyUp: import('@vitest/spy').Mock<(...args: any[]) => any>;
|
|
28
28
|
};
|
|
29
29
|
};
|
|
30
30
|
export default meta;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { StoryObj } from '@storybook/react';
|
|
2
|
+
export declare const ActionsData: {};
|
|
3
|
+
declare const meta: {
|
|
4
|
+
title: string;
|
|
5
|
+
component: ({ image, annotations: propAnnotations, possibleLabels, isJunk, uiConfig: propUiConfig, enableZoom, canSelect, onSelectAnnotation, }: {
|
|
6
|
+
image: string;
|
|
7
|
+
annotations?: import('../..').ExternalAnnotation[];
|
|
8
|
+
possibleLabels: import('../..').Label[];
|
|
9
|
+
isJunk?: boolean;
|
|
10
|
+
uiConfig?: Partial<import('../..').UiConfig>;
|
|
11
|
+
enableZoom?: boolean;
|
|
12
|
+
canSelect?: boolean;
|
|
13
|
+
onSelectAnnotation?: (annotation?: import('../../models').Annotation) => void;
|
|
14
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
parameters: {
|
|
16
|
+
layout: string;
|
|
17
|
+
};
|
|
18
|
+
tags: string[];
|
|
19
|
+
excludeStories: RegExp;
|
|
20
|
+
args: {};
|
|
21
|
+
};
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof meta>;
|
|
24
|
+
/**
|
|
25
|
+
* SiaViewer with point annotations. No toolbar, no editing.
|
|
26
|
+
* Try clicking annotations to select them and scrolling to zoom.
|
|
27
|
+
*/
|
|
28
|
+
export declare const WithPointAnnotations: Story;
|
|
29
|
+
/**
|
|
30
|
+
* SiaViewer with bbox annotations.
|
|
31
|
+
*/
|
|
32
|
+
export declare const WithBBoxAnnotations: Story;
|
|
33
|
+
/**
|
|
34
|
+
* SiaViewer with line annotations.
|
|
35
|
+
*/
|
|
36
|
+
export declare const WithLineAnnotations: Story;
|
|
37
|
+
/**
|
|
38
|
+
* SiaViewer with polygon annotations.
|
|
39
|
+
*/
|
|
40
|
+
export declare const WithPolygonAnnotations: Story;
|
|
41
|
+
/**
|
|
42
|
+
* SiaViewer with zoom disabled.
|
|
43
|
+
*/
|
|
44
|
+
export declare const ZoomDisabled: Story;
|
|
45
|
+
/**
|
|
46
|
+
* SiaViewer with selection disabled. Annotations cannot be clicked.
|
|
47
|
+
*/
|
|
48
|
+
export declare const SelectionDisabled: Story;
|
|
49
|
+
/**
|
|
50
|
+
* SiaViewer marked as junk.
|
|
51
|
+
*/
|
|
52
|
+
export declare const Junk: Story;
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{default as
|
|
1
|
+
import{default as r}from"./TimeUtils.js";import{SIA_INITIAL_UI_CONFIG as f,uiConfig as I}from"./uiConfig.js";import{getColor as i,getDefaultColor as m}from"./color.js";export{f as SIA_INITIAL_UI_CONFIG,r as TimeUtils,i as getColor,m as getDefaultColor,I as uiConfig};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lost-sia",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0-alpha1",
|
|
4
4
|
"description": "Single Image Annotation Tool",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "l3p-cv/lost-sia",
|
|
@@ -63,9 +63,7 @@
|
|
|
63
63
|
"react": "^19.2.1",
|
|
64
64
|
"react-dom": "^19.2.1",
|
|
65
65
|
"react-draggable": "^4.5.0",
|
|
66
|
-
"sass": "^1.95.0"
|
|
67
|
-
"semantic-ui-css": "2.5.0",
|
|
68
|
-
"semantic-ui-react": "^2.0.3"
|
|
66
|
+
"sass": "^1.95.0"
|
|
69
67
|
},
|
|
70
68
|
"peerDependencies": {
|
|
71
69
|
"@coreui/react": "^5.9.1",
|
|
@@ -81,6 +79,7 @@
|
|
|
81
79
|
"@storybook/addon-links": "^10.1.6",
|
|
82
80
|
"@storybook/react": "^10.1.6",
|
|
83
81
|
"@storybook/react-vite": "^10.1.6",
|
|
82
|
+
"@storybook/test": "^8.6.15",
|
|
84
83
|
"@types/react": "^19.2.7",
|
|
85
84
|
"@typescript-eslint/eslint-plugin": "^8.46.1",
|
|
86
85
|
"@typescript-eslint/parser": "^8.46.1",
|
package/src/Canvas/Canvas.tsx
CHANGED
|
@@ -572,6 +572,37 @@ const Canvas = ({
|
|
|
572
572
|
setImgSize({ x: width, y: height })
|
|
573
573
|
}, [image, canvasSize])
|
|
574
574
|
|
|
575
|
+
// Source of truth for the image's natural size.
|
|
576
|
+
// The SVG <image> element above loads asynchronously, so reading
|
|
577
|
+
// imageRef.getBoundingClientRect() (the effect above) runs before the
|
|
578
|
+
// remote image has decoded and resolves to {0, 0}. That left imgSize at
|
|
579
|
+
// 0, which trips the getFittedImageScale guard (returns 0), so stageSize
|
|
580
|
+
// never got set and the <image> rendered at its natural (huge) pixel size.
|
|
581
|
+
// Preloading via an HTMLImageElement gives us naturalWidth/Height as soon
|
|
582
|
+
// as the bytes are decoded, regardless of the SVG <image> lifecycle.
|
|
583
|
+
useEffect(() => {
|
|
584
|
+
if (!image) {
|
|
585
|
+
setImgSize({ x: -1, y: -1 })
|
|
586
|
+
return
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
let cancelled = false
|
|
590
|
+
const probe = new Image()
|
|
591
|
+
probe.onload = () => {
|
|
592
|
+
if (cancelled) return
|
|
593
|
+
const { naturalWidth, naturalHeight } = probe
|
|
594
|
+
if (naturalWidth > 0 && naturalHeight > 0) {
|
|
595
|
+
setImgSize({ x: naturalWidth, y: naturalHeight })
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
probe.src = image
|
|
599
|
+
|
|
600
|
+
return () => {
|
|
601
|
+
cancelled = true
|
|
602
|
+
probe.onload = null
|
|
603
|
+
}
|
|
604
|
+
}, [image])
|
|
605
|
+
|
|
575
606
|
useEffect(() => {
|
|
576
607
|
if (imageToStageFactor === 0) return
|
|
577
608
|
|
|
@@ -780,7 +811,9 @@ const Canvas = ({
|
|
|
780
811
|
onSelectAnnotation(percentagedAnnotation)
|
|
781
812
|
|
|
782
813
|
// get top left point of annotation
|
|
783
|
-
|
|
814
|
+
if (annotationSettings.canLabel) {
|
|
815
|
+
setLabelInputPosition(getAnnoTopLeftPagePosition(annotation.coordinates))
|
|
816
|
+
}
|
|
784
817
|
}
|
|
785
818
|
|
|
786
819
|
const handleOnAnnoChanged = (annotation: Annotation) => {
|
|
@@ -919,6 +952,7 @@ const Canvas = ({
|
|
|
919
952
|
flexDirection: 'column',
|
|
920
953
|
}}
|
|
921
954
|
>
|
|
955
|
+
{annotationSettings.canLabel && (
|
|
922
956
|
<div
|
|
923
957
|
style={{
|
|
924
958
|
position: 'absolute',
|
|
@@ -973,6 +1007,7 @@ const Canvas = ({
|
|
|
973
1007
|
}}
|
|
974
1008
|
/>
|
|
975
1009
|
</div>
|
|
1010
|
+
)}
|
|
976
1011
|
|
|
977
1012
|
{isImageJunk && (
|
|
978
1013
|
<div
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { CSSProperties, useEffect, useState, WheelEvent } from 'react'
|
|
2
|
+
import Canvas from './Canvas/Canvas'
|
|
3
|
+
import Annotation from './Annotation/logic/Annotation'
|
|
4
|
+
import AnnotationMode from './models/AnnotationMode'
|
|
5
|
+
import AnnotationTool from './models/AnnotationTool'
|
|
6
|
+
import { AnnotationSettings, ExternalAnnotation, Label, UiConfig } from './types'
|
|
7
|
+
|
|
8
|
+
type SiaViewerProps = {
|
|
9
|
+
image: string
|
|
10
|
+
annotations?: ExternalAnnotation[]
|
|
11
|
+
possibleLabels: Label[]
|
|
12
|
+
isJunk?: boolean
|
|
13
|
+
uiConfig?: Partial<UiConfig>
|
|
14
|
+
enableZoom?: boolean
|
|
15
|
+
canSelect?: boolean
|
|
16
|
+
onSelectAnnotation?: (annotation?: Annotation) => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const VIEW_ANNOTATION_SETTINGS: AnnotationSettings = {
|
|
20
|
+
canCreate: false,
|
|
21
|
+
canEdit: false,
|
|
22
|
+
canLabel: false,
|
|
23
|
+
canHaveMultipleLabels: false,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DEFAULT_UI_CONFIG: UiConfig = {
|
|
27
|
+
nodeRadius: 4,
|
|
28
|
+
strokeWidth: 4,
|
|
29
|
+
imageCentered: false,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const containerStyle: CSSProperties = {
|
|
33
|
+
flex: '1 1 auto',
|
|
34
|
+
minHeight: 0,
|
|
35
|
+
display: 'flex',
|
|
36
|
+
flexDirection: 'column',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* View-only SIA component. Renders image + annotations without a toolbar and
|
|
41
|
+
* blocks all mutations (create / edit / delete / label). Zoom and selection
|
|
42
|
+
* can be toggled via props.
|
|
43
|
+
*/
|
|
44
|
+
const SiaViewer = ({
|
|
45
|
+
image,
|
|
46
|
+
annotations: propAnnotations,
|
|
47
|
+
possibleLabels,
|
|
48
|
+
isJunk = false,
|
|
49
|
+
uiConfig: propUiConfig,
|
|
50
|
+
enableZoom = true,
|
|
51
|
+
canSelect = true,
|
|
52
|
+
onSelectAnnotation = () => {},
|
|
53
|
+
}: SiaViewerProps) => {
|
|
54
|
+
const [annotations, setAnnotations] = useState<Annotation[]>([])
|
|
55
|
+
const [selectedAnnotation, setSelectedAnnotation] = useState<
|
|
56
|
+
Annotation | undefined
|
|
57
|
+
>()
|
|
58
|
+
const [uiConfig, setUiConfig] = useState<UiConfig>(DEFAULT_UI_CONFIG)
|
|
59
|
+
|
|
60
|
+
// (re)initialize annotations whenever the annotations or image prop changes
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (image === undefined || propAnnotations === undefined) {
|
|
63
|
+
setAnnotations([])
|
|
64
|
+
setSelectedAnnotation(undefined)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let internalId = 0
|
|
69
|
+
const next: Annotation[] = propAnnotations.map((externalAnno) => ({
|
|
70
|
+
...externalAnno,
|
|
71
|
+
internalId: internalId++,
|
|
72
|
+
mode: AnnotationMode.VIEW,
|
|
73
|
+
selectedNode: 1,
|
|
74
|
+
status: externalAnno.status,
|
|
75
|
+
annoTime: externalAnno.annoTime ?? 0,
|
|
76
|
+
}))
|
|
77
|
+
|
|
78
|
+
setAnnotations(next)
|
|
79
|
+
setSelectedAnnotation(undefined)
|
|
80
|
+
}, [propAnnotations, image])
|
|
81
|
+
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
setUiConfig({ ...DEFAULT_UI_CONFIG, ...propUiConfig })
|
|
84
|
+
}, [propUiConfig])
|
|
85
|
+
|
|
86
|
+
const stopZoom = (e: WheelEvent) => {
|
|
87
|
+
if (!enableZoom) e.stopPropagation()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div style={containerStyle} onWheelCapture={stopZoom}>
|
|
92
|
+
<Canvas
|
|
93
|
+
annotations={annotations}
|
|
94
|
+
annotationSettings={VIEW_ANNOTATION_SETTINGS}
|
|
95
|
+
image={image}
|
|
96
|
+
isImageJunk={isJunk}
|
|
97
|
+
isPolygonSelectionMode={false}
|
|
98
|
+
possibleLabels={possibleLabels}
|
|
99
|
+
selectedAnnotation={canSelect ? selectedAnnotation : undefined}
|
|
100
|
+
selectedAnnoTool={AnnotationTool.Point}
|
|
101
|
+
uiConfig={uiConfig}
|
|
102
|
+
onAnnoCreated={() => {}}
|
|
103
|
+
onAnnoChanged={() => {}}
|
|
104
|
+
onAnnoCreationFinished={() => {}}
|
|
105
|
+
onAnnoEditing={() => {}}
|
|
106
|
+
onNotification={() => {}}
|
|
107
|
+
onRequestNewAnnoId={() => 0}
|
|
108
|
+
onSelectAnnotation={
|
|
109
|
+
canSelect
|
|
110
|
+
? (annotation) => {
|
|
111
|
+
setSelectedAnnotation(annotation)
|
|
112
|
+
onSelectAnnotation(annotation)
|
|
113
|
+
}
|
|
114
|
+
: () => {}
|
|
115
|
+
}
|
|
116
|
+
onSetIsImageJunk={() => {}}
|
|
117
|
+
onSetSelectedTool={() => {}}
|
|
118
|
+
onShouldDeleteAnno={() => {}}
|
|
119
|
+
onTraverseAnnotationHistory={() => {}}
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default SiaViewer
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
import { Label } from '../../../types'
|
|
12
12
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
13
13
|
import { faTag } from '@fortawesome/free-solid-svg-icons'
|
|
14
|
-
import { IconProps } from 'semantic-ui-react'
|
|
15
14
|
import TagLabel from './TagLabel'
|
|
16
15
|
|
|
17
16
|
type ImageLabelInputProps = {
|
|
@@ -69,7 +68,7 @@ const ImageLabelInput = ({
|
|
|
69
68
|
if (!selectedLabelsIds || selectedLabelsIds.length === 0)
|
|
70
69
|
return (
|
|
71
70
|
<div style={{ marginTop: 6 }}>
|
|
72
|
-
<FontAwesomeIcon icon={faTag
|
|
71
|
+
<FontAwesomeIcon icon={faTag} />
|
|
73
72
|
</div>
|
|
74
73
|
)
|
|
75
74
|
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import 'semantic-ui-css/semantic.min.css'
|
|
2
1
|
import './SIA.scss'
|
|
3
2
|
|
|
4
3
|
// export all type definitions
|
|
@@ -6,5 +5,6 @@ export * from './types'
|
|
|
6
5
|
|
|
7
6
|
export { default as IconButton } from './IconButton'
|
|
8
7
|
export { default as Sia } from './Sia'
|
|
8
|
+
export { default as SiaViewer } from './SiaViewer'
|
|
9
9
|
export { default as transform } from './utils/transform'
|
|
10
10
|
export { default as TagLabel } from './Toolbar/ToolbarItems/ImageToolItems/TagLabel'
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
|
|
3
|
+
import SiaViewer from '../../SiaViewer'
|
|
4
|
+
|
|
5
|
+
import exampleImage from '../exampleData/exampleImage'
|
|
6
|
+
import exampleLabels from '../exampleData/exampleLabels'
|
|
7
|
+
import exampleExternalAnnotations from '../exampleData/exampleExternalAnnotations'
|
|
8
|
+
|
|
9
|
+
export const ActionsData = {}
|
|
10
|
+
|
|
11
|
+
const meta = {
|
|
12
|
+
title: 'Components/SiaViewer',
|
|
13
|
+
component: SiaViewer,
|
|
14
|
+
parameters: {
|
|
15
|
+
layout: 'fullscreen',
|
|
16
|
+
},
|
|
17
|
+
tags: ['!autodocs'],
|
|
18
|
+
excludeStories: /.*Data$/,
|
|
19
|
+
args: {
|
|
20
|
+
...ActionsData,
|
|
21
|
+
},
|
|
22
|
+
} satisfies Meta<typeof SiaViewer>
|
|
23
|
+
|
|
24
|
+
export default meta
|
|
25
|
+
type Story = StoryObj<typeof meta>
|
|
26
|
+
|
|
27
|
+
const defaultArgs = {
|
|
28
|
+
...ActionsData,
|
|
29
|
+
image: exampleImage,
|
|
30
|
+
possibleLabels: exampleLabels.voc,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* SiaViewer with point annotations. No toolbar, no editing.
|
|
35
|
+
* Try clicking annotations to select them and scrolling to zoom.
|
|
36
|
+
*/
|
|
37
|
+
export const WithPointAnnotations: Story = {
|
|
38
|
+
args: {
|
|
39
|
+
...defaultArgs,
|
|
40
|
+
annotations: exampleExternalAnnotations.point,
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* SiaViewer with bbox annotations.
|
|
46
|
+
*/
|
|
47
|
+
export const WithBBoxAnnotations: Story = {
|
|
48
|
+
args: {
|
|
49
|
+
...defaultArgs,
|
|
50
|
+
annotations: exampleExternalAnnotations.bbox,
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* SiaViewer with line annotations.
|
|
56
|
+
*/
|
|
57
|
+
export const WithLineAnnotations: Story = {
|
|
58
|
+
args: {
|
|
59
|
+
...defaultArgs,
|
|
60
|
+
annotations: exampleExternalAnnotations.line,
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* SiaViewer with polygon annotations.
|
|
66
|
+
*/
|
|
67
|
+
export const WithPolygonAnnotations: Story = {
|
|
68
|
+
args: {
|
|
69
|
+
...defaultArgs,
|
|
70
|
+
annotations: exampleExternalAnnotations.polygon,
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* SiaViewer with zoom disabled.
|
|
76
|
+
*/
|
|
77
|
+
export const ZoomDisabled: Story = {
|
|
78
|
+
args: {
|
|
79
|
+
...defaultArgs,
|
|
80
|
+
annotations: exampleExternalAnnotations.polygon,
|
|
81
|
+
enableZoom: false,
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* SiaViewer with selection disabled. Annotations cannot be clicked.
|
|
87
|
+
*/
|
|
88
|
+
export const SelectionDisabled: Story = {
|
|
89
|
+
args: {
|
|
90
|
+
...defaultArgs,
|
|
91
|
+
annotations: exampleExternalAnnotations.polygon,
|
|
92
|
+
canSelect: false,
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* SiaViewer marked as junk.
|
|
98
|
+
*/
|
|
99
|
+
export const Junk: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
...defaultArgs,
|
|
102
|
+
annotations: exampleExternalAnnotations.polygon,
|
|
103
|
+
isJunk: true,
|
|
104
|
+
},
|
|
105
|
+
}
|
|
@@ -37,10 +37,10 @@ const point: ExternalAnnotation[] = [
|
|
|
37
37
|
const line: ExternalAnnotation[] = [
|
|
38
38
|
{
|
|
39
39
|
coordinates: [
|
|
40
|
-
{ x:
|
|
41
|
-
{ x:
|
|
42
|
-
{ x:
|
|
43
|
-
{ x:
|
|
40
|
+
{ x: 0.05, y: 0.05 },
|
|
41
|
+
{ x: 0.2, y: 0.1 },
|
|
42
|
+
{ x: 0.25, y: 0.1 },
|
|
43
|
+
{ x: 0.25, y: 0.2 },
|
|
44
44
|
],
|
|
45
45
|
labelIds: [5],
|
|
46
46
|
type: AnnotationTool.Line,
|
|
@@ -48,11 +48,11 @@ const line: ExternalAnnotation[] = [
|
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
coordinates: [
|
|
51
|
-
{ x:
|
|
52
|
-
{ x:
|
|
53
|
-
{ x:
|
|
54
|
-
{ x:
|
|
55
|
-
{ x:
|
|
51
|
+
{ x: 0.26, y: 0.3 },
|
|
52
|
+
{ x: 0.35, y: 0.33 },
|
|
53
|
+
{ x: 0.36, y: 0.32 },
|
|
54
|
+
{ x: 0.37, y: 0.3 },
|
|
55
|
+
{ x: 0.27, y: 0.25 },
|
|
56
56
|
],
|
|
57
57
|
labelIds: [8, 11],
|
|
58
58
|
type: AnnotationTool.Line,
|
|
@@ -63,8 +63,8 @@ const line: ExternalAnnotation[] = [
|
|
|
63
63
|
const bbox: ExternalAnnotation[] = [
|
|
64
64
|
{
|
|
65
65
|
coordinates: [
|
|
66
|
-
{ x:
|
|
67
|
-
{ x:
|
|
66
|
+
{ x: 0.05, y: 0.05 },
|
|
67
|
+
{ x: 0.2, y: 0.2 },
|
|
68
68
|
],
|
|
69
69
|
labelIds: [5],
|
|
70
70
|
type: AnnotationTool.BBox,
|
|
@@ -72,8 +72,8 @@ const bbox: ExternalAnnotation[] = [
|
|
|
72
72
|
},
|
|
73
73
|
{
|
|
74
74
|
coordinates: [
|
|
75
|
-
{ x:
|
|
76
|
-
{ x:
|
|
75
|
+
{ x: 0.25, y: 0.1 },
|
|
76
|
+
{ x: 0.45, y: 0.15 },
|
|
77
77
|
],
|
|
78
78
|
labelIds: [8, 11],
|
|
79
79
|
type: AnnotationTool.BBox,
|
|
@@ -84,10 +84,10 @@ const bbox: ExternalAnnotation[] = [
|
|
|
84
84
|
const polygon: ExternalAnnotation[] = [
|
|
85
85
|
{
|
|
86
86
|
coordinates: [
|
|
87
|
-
{ x:
|
|
88
|
-
{ x:
|
|
89
|
-
{ x:
|
|
90
|
-
{ x:
|
|
87
|
+
{ x: 0.05, y: 0.05 },
|
|
88
|
+
{ x: 0.2, y: 0.1 },
|
|
89
|
+
{ x: 0.25, y: 0.1 },
|
|
90
|
+
{ x: 0.25, y: 0.2 },
|
|
91
91
|
],
|
|
92
92
|
labelIds: [5],
|
|
93
93
|
status: AnnotationStatus.LOADED,
|
|
@@ -95,11 +95,11 @@ const polygon: ExternalAnnotation[] = [
|
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
coordinates: [
|
|
98
|
-
{ x:
|
|
99
|
-
{ x:
|
|
100
|
-
{ x:
|
|
101
|
-
{ x:
|
|
102
|
-
{ x:
|
|
98
|
+
{ x: 0.26, y: 0.3 },
|
|
99
|
+
{ x: 0.35, y: 0.33 },
|
|
100
|
+
{ x: 0.36, y: 0.32 },
|
|
101
|
+
{ x: 0.37, y: 0.3 },
|
|
102
|
+
{ x: 0.27, y: 0.25 },
|
|
103
103
|
],
|
|
104
104
|
labelIds: [8, 11],
|
|
105
105
|
status: AnnotationStatus.LOADED,
|
package/src/utils/index.ts
CHANGED