diff --git a/src/Annotator/index.js b/src/Annotator/index.js index 8cf69613..c74e432a 100644 --- a/src/Annotator/index.js +++ b/src/Annotator/index.js @@ -22,7 +22,7 @@ import useEventCallback from "use-event-callback" import makeImmutable, { without } from "seamless-immutable" type Props = { - taskDescription: string, + taskDescription?: string, allowedArea?: { x: number, y: number, w: number, h: number }, regionTagList?: Array, regionClsList?: Array, @@ -68,7 +68,7 @@ export const Annotator = ({ imageTagList = [], imageClsList = [], keyframes = {}, - taskDescription, + taskDescription = "", fullImageSegmentationMode = false, RegionEditLabel, videoSrc, diff --git a/src/Annotator/index.story.js b/src/Annotator/index.story.js index 1aef1e9d..4fe87faa 100644 --- a/src/Annotator/index.story.js +++ b/src/Annotator/index.story.js @@ -14,16 +14,18 @@ import Annotator from "./" import { testRegions } from "../ImageCanvas/index.story" +const middlewares = [ + (store) => (next) => (action) => { + actionAddon(action.type)(action) + return next(action) + }, +] + storiesOf("Annotator", module) .add("Basic", () => ( (next) => (action) => { - actionAddon(action.type)(action) - return next(action) - }, - ]} + middlewares={middlewares} labelImages regionClsList={["Alpha", "Beta", "Charlie", "Delta"]} regionTagList={["tag1", "tag2", "tag3"]} @@ -112,12 +114,7 @@ storiesOf("Annotator", module) .add("Annotator w/o No Region Labels or Image Labels", () => ( (next) => (action) => { - actionAddon(action.type)(action) - return next(action) - }, - ]} + middlewares={middlewares} images={[ { src: exampleImage, @@ -132,12 +129,7 @@ storiesOf("Annotator", module) onExit={actionAddon("onExit")} enabledTools={[]} showTags={false} - middlewares={[ - (store) => (next) => (action) => { - actionAddon(action.type)(action) - return next(action) - }, - ]} + middlewares={middlewares} images={[ { src: exampleImage, @@ -195,12 +187,7 @@ storiesOf("Annotator", module) .add("Car Annotation", () => ( (next) => (action) => { - actionAddon(action.type)(action) - return next(action) - }, - ]} + middlewares={middlewares} labelImages regionClsList={["Car", "Sign", "Construction Barrier"]} regionTagList={["Moving", "Stopped", "Obstacle"]} @@ -650,18 +637,30 @@ storiesOf("Annotator", module) .add("CORs Error (Pixel Segmentation)", () => ( (next) => (action) => { - actionAddon(action.type)(action) - return next(action) + middlewares={middlewares} + labelImages + fullImageSegmentationMode + regionClsList={["Alpha", "Beta", "Charlie", "Delta"]} + images={[ + { + src: "https://placebear.com/200/300", + name: "Frame 0036", }, ]} + /> + )) + .add("Modify Allowed Area", () => ( + Math.random().toString().split(".")[1] export default (state: MainLayoutState, action: Action) => { if ( state.allowedArea && + state.selectedTool !== "modify-allowed-area" && ["MOUSE_DOWN", "MOUSE_UP", "MOUSE_MOVE"].includes(action.type) ) { const aa = state.allowedArea @@ -241,6 +242,17 @@ export default (state: MainLayoutState, action: Action) => { } case "MOVE_REGION": { const { regionId } = state.mode + if (regionId === "$$allowed_area") { + const { + allowedArea: { w, h }, + } = state + return setIn(state, ["allowedArea"], { + x: x - w / 2, + y: y - h / 2, + w, + h, + }) + } const regionIndex = getRegionIndex(regionId) if (regionIndex === null) return state return setIn( @@ -255,9 +267,6 @@ export default (state: MainLayoutState, action: Action) => { freedom: [xFree, yFree], original: { x: ox, y: oy, w: ow, h: oh }, } = state.mode - const regionIndex = getRegionIndex(regionId) - if (regionIndex === null) return state - const box = activeImage.regions[regionIndex] const dx = xFree === 0 ? ox : xFree === -1 ? Math.min(ox + ow, x) : ox const dw = @@ -282,6 +291,19 @@ export default (state: MainLayoutState, action: Action) => { state = setIn(state, ["mode", "freedom"], [xFree, yFree * -1]) } + if (regionId === "$$allowed_area") { + return setIn(state, ["allowedArea"], { + x: dx, + w: dw, + y: dy, + h: dh, + }) + } + + const regionIndex = getRegionIndex(regionId) + if (regionIndex === null) return state + const box = activeImage.regions[regionIndex] + return setIn(state, [...pathToActiveImage, "regions", regionIndex], { ...box, x: dx, @@ -699,6 +721,9 @@ export default (state: MainLayoutState, action: Action) => { } else if (action.selectedTool === "show-mask") { return setIn(state, ["showMask"], !state.showMask) } + if (action.selectedTool === "modify-allowed-area" && !state.allowedArea) { + state = setIn(state, ["allowedArea"], { x: 0, y: 0, w: 1, h: 1 }) + } state = setIn(state, ["mode"], null) return setIn(state, ["selectedTool"], action.selectedTool) } diff --git a/src/IconTools/index.js b/src/IconTools/index.js deleted file mode 100644 index b7e2c61e..00000000 --- a/src/IconTools/index.js +++ /dev/null @@ -1,134 +0,0 @@ -// @flow - -import React, { useMemo } from "react" -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" -import { - faArrowsAlt, - faMousePointer, - faExpandArrowsAlt, - faGripLines, - faTag, - faPaintBrush, - faCrosshairs, - faDrawPolygon, - faVectorSquare, - faHandPaper, - faSearch, - faMask, -} from "@fortawesome/free-solid-svg-icons" -import SmallToolButton, { SelectedTool } from "../SmallToolButton" -import { makeStyles } from "@material-ui/core/styles" -import { grey } from "@material-ui/core/colors" - -const useStyles = makeStyles({ - iconTools: { - display: "flex", - padding: 4, - flexDirection: "column", - zIndex: 9, - boxShadow: "0px 0px 5px rgba(0,0,0,0.1)", - borderRight: `1px solid ${grey[300]}`, - backgroundColor: grey[100], - }, - grow: { flexGrow: 1 }, -}) - -type Props = { - showTags?: boolean, - showMask?: boolean, - enabledTools?: Array, - selectedTool: string, - onClickTool: (string) => any, -} - -const defaultTools = [ - "select", - "create-point", - "create-box", - "create-polygon", - "create-expanding-line", -] - -export const IconTools = ({ - showTags, - showMask, - selectedTool, - onClickTool, - enabledTools = defaultTools, -}: Props) => { - const classes = useStyles() - const selectedToolContextValue = useMemo( - () => ({ enabledTools, selectedTool, onClickTool }), - [enabledTools, selectedTool] - ) - return ( -
- - } - /> - } - /> - } - /> - {/* } - /> - - } - /> */} - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - -
- ) -} - -export default IconTools diff --git a/src/ImageCanvas/index.js b/src/ImageCanvas/index.js index 64036e3d..5266a07a 100644 --- a/src/ImageCanvas/index.js +++ b/src/ImageCanvas/index.js @@ -57,6 +57,7 @@ type Props = { zoomOnAllowedArea?: boolean, fullImageSegmentationMode?: boolean, autoSegmentationOptions?: Object, + modifyingAllowedArea?: boolean, onChangeRegion: (Region) => any, onBeginRegionEdit: (Region) => any, @@ -126,6 +127,7 @@ export const ImageCanvas = ({ onChangeVideoPlaying, onRegionClassAdded, zoomOnAllowedArea = true, + modifyingAllowedArea = false, }: Props) => { const classes = useStyles() @@ -325,7 +327,24 @@ export const ImageCanvas = ({ {imageLoaded && !dragging && ( )) + .add("Modify Allowed Area", () => ( + + )) diff --git a/src/MainLayout/icon-dictionary.js b/src/MainLayout/icon-dictionary.js index 754e888a..2481e2c9 100644 --- a/src/MainLayout/icon-dictionary.js +++ b/src/MainLayout/icon-dictionary.js @@ -15,6 +15,7 @@ import { faHandPaper, faSearch, faMask, + faEdit, } from "@fortawesome/free-solid-svg-icons" import FullscreenIcon from "@material-ui/icons/Fullscreen" @@ -63,6 +64,9 @@ export const iconDictionary = { "show-mask": () => ( ), + "modify-allowed-area": () => ( + + ), window: FullscreenIcon, } diff --git a/src/MainLayout/index.js b/src/MainLayout/index.js index 30c2ba77..fdb37cbb 100644 --- a/src/MainLayout/index.js +++ b/src/MainLayout/index.js @@ -115,6 +115,7 @@ export const MainLayout = ({ autoSegmentationOptions={state.autoSegmentationOptions} showTags={state.showTags} allowedArea={state.allowedArea} + modifyingAllowedArea={state.selectedTool === "modify-allowed-area"} regionClsList={state.regionClsList} regionTagList={state.regionTagList} regions={ @@ -244,19 +245,22 @@ export const MainLayout = ({ { name: "select", helperText: "Select", - selected: state.selectedTool, + alwaysShowing: true, }, { name: "pan", helperText: "Drag/Pan", + alwaysShowing: true, }, { name: "zoom", helperText: "Zoom In/Out", + alwaysShowing: true, }, { name: "show-tags", helperText: "Show / Hide Tags", + alwaysShowing: true, }, { name: "create-point", @@ -274,11 +278,20 @@ export const MainLayout = ({ name: "create-expanding-line", helperText: "Add Expanding Line", }, - { + state.fullImageSegmentationMode && { name: "show-mask", + alwaysShowing: true, helperText: "Show / Hide Mask", }, - ]} + { + name: "modify-allowed-area", + helperText: "Modify Allowed Area", + }, + ] + .filter(Boolean) + .filter( + (a) => a.alwaysShowing || state.enabledTools.includes(a.name) + )} rightSidebarItems={[ debugModeOn && ( diff --git a/src/MainLayout/index.story.js b/src/MainLayout/index.story.js index 63709af0..f5dda4d3 100644 --- a/src/MainLayout/index.story.js +++ b/src/MainLayout/index.story.js @@ -44,7 +44,7 @@ storiesOf("MainLayout", module) }, ], mode: null, - enabledTools: [], + enabledTools: ["create-point", "create-polygon", "modify-allowed-area"], history: [{ name: "Reset Stuff", state: null, time: moment() }], }} dispatch={(a) => action(a.type)(a)}