UNPKG

label-studio

Version:

Data Labeling Tool that is backend agnostic and can be embedded into your applications

296 lines (250 loc) 7.59 kB
import React, { Fragment } from "react"; import { Line, Group } from "react-konva"; import { observer, inject } from "mobx-react"; import { types, getParentOfType, getRoot } from "mobx-state-tree"; import NormalizationMixin from "../mixins/Normalization"; import RegionsMixin from "../mixins/Regions"; import Registry from "../core/Registry"; import { BrushLabelsModel } from "../tags/control/BrushLabels"; import { ImageModel } from "../tags/object/Image"; import { LabelsModel } from "../tags/control/Labels"; import { RatingModel } from "../tags/control/Rating"; import { guidGenerator } from "../core/Helpers"; const Points = types .model("Points", { id: types.identifier, type: types.optional(types.enumeration(["add", "eraser"]), "add"), points: types.array(types.number), }) .actions(self => ({ setType(type) { self.type = type; }, addPoints(points) { self.points = [...self.points, ...points]; }, })); /** * Rectangle object for Bounding Box * */ const Model = types .model({ id: types.identifier, pid: types.optional(types.string, guidGenerator), type: "brushregion", states: types.maybeNull(types.array(types.union(LabelsModel, RatingModel, BrushLabelsModel))), coordstype: types.optional(types.enumeration(["px", "perc"]), "px"), /** * Higher values will result in a more curvy line. A value of 0 will result in no interpolation. */ tension: types.optional(types.number, 1.0), /** * Stroke color */ strokeColor: types.optional(types.string, "red"), /** * Stroke width */ strokeWidth: types.optional(types.number, 25), /** * Determines node opacity. Can be any number between 0 and 1 */ opacity: types.optional(types.number, 0.5), /** * Set scale x */ scaleX: types.optional(types.number, 1), /** * Set scale y */ scaleY: types.optional(types.number, 1), /** * Points array of brush */ points: types.array(Points), current: types.maybeNull(types.reference(Points)), // points: types.array(types.array(types.number)), // eraserpoints: types.array(types.array(types.number)), mode: types.optional(types.string, "brush"), }) .views(self => ({ get parent() { return getParentOfType(self, ImageModel); }, get completion() { return getRoot(self).completionStore.selected; }, })) .actions(self => ({ addPoints({ type }) { const p = Points.create({ id: guidGenerator(), type: type }); self.points.push(p); self.current = p; // console.log("addPoints"); return p; }, addPointsCurrent(x, y) { // console.log("addPointsCurrent"); self.current.addPoints([x, y]); }, afterAttach() {}, unselectRegion() { self.selected = false; self.parent.setSelected(undefined); self.completion.setHighlightedNode(null); }, selectRegion() { self.selected = true; self.completion.setHighlightedNode(self); self.parent.setSelected(self.id); }, // addPoints(x, y, mode) { // if (mode) self.mode = "eraser"; // self.points.push(x); // self.points.push(y); // }, // addEraserPoints(x, y) { // self.eraserpoints = [...self.eraserpoints, x, y]; // }, setScale(x, y) { self.scaleX = x; self.scaleY = y; }, updateImageSize(wp, hp, sw, sh) { if (self.parent.initialWidth > 1 && self.parent.initialHeight > 1) { let ratioX = self.parent.stageWidth / self.parent.initialWidth; let ratioY = self.parent.stageHeight / self.parent.initialHeight; self.setScale(ratioX, ratioY); } }, addState(state) { self.states.push(state); }, toStateJSON() { const parent = self.parent; let fromElement = parent.states()[0]; if (parent.states().length > 1) { parent.states().forEach(state => { if (state.type === "brushlabels") { fromElement = state; } }); } const buildTree = obj => { const tree = { id: self.id, from_name: fromElement.name, to_name: parent.name, source: parent.value, type: "brush", value: { points: self.points, eraserpoints: self.eraserpoints, }, }; if (self.normalization) tree["normalization"] = self.normalization; return tree; }; if (self.states && self.states.length) { return self.states.map(s => { const tree = buildTree(s); // in case of labels it's gonna be, labels: ["label1", "label2"] tree["value"][s.type] = s.getSelectedNames(); tree["type"] = s.type; return tree; }); } else { return buildTree(parent); } }, })); const BrushRegionModel = types.compose("BrushRegionModel", RegionsMixin, NormalizationMixin, Model); const HtxBrushLayer = observer(({ store, item, points }) => { let currentPoints = []; points.points.forEach(point => { currentPoints.push(point); }); return points.type === "add" ? ( <HtxBrushAddLine item={item} points={currentPoints} /> ) : ( <HtxBrushEraserLine item={item} points={currentPoints} /> ); }); const HtxBrushAddLine = observer(({ store, item, points }) => { let highlightOptions = { shadowColor: "red", shadowBlur: 5, shadowOffsetY: 5, shadowOpacity: 1, }; let highlight = item.selected ? highlightOptions : null; // {...highlight} return ( <Line strokeWidth={item.strokeWidth} points={points} stroke={item.strokeColor} opacity={item.mode === "brush" ? item.opacity : 1} globalCompositeOperation={"source-over"} tension={item.tension} lineJoin={"round"} lineCap="round" /> ); }); const HtxBrushEraserLine = ({ store, item, points }) => { return ( <Line strokeWidth={item.strokeWidth} points={points} tension={item.tension} lineJoin={"round"} lineCap="round" stroke={item.strokeColor} opacity={1} globalCompositeOperation={"destination-out"} /> ); }; const HtxBrushView = ({ store, item }) => { return ( <Fragment> <Group scaleX={item.scaleX} scaleY={item.scaleY} onMouseOver={e => { const stage = item.parent.stageRef; if (store.completionStore.selected.relationMode) { item.setHighlight(true); stage.container().style.cursor = "crosshair"; } else { stage.container().style.cursor = "pointer"; } }} onMouseOut={e => { const stage = item.parent.stageRef; stage.container().style.cursor = "default"; if (store.completionStore.selected.relationMode) { item.setHighlight(false); } }} onClick={e => { const stage = item.parent.stageRef; if (store.completionStore.selected.relationMode) { stage.container().style.cursor = "default"; } item.setHighlight(false); item.onClickRegion(); }} > {item.points.map(p => ( <HtxBrushLayer store={store} item={item} points={p} /> ))} </Group> </Fragment> ); }; const HtxBrush = inject("store")(observer(HtxBrushView)); Registry.addTag("brushregion", BrushRegionModel, HtxBrush); export { BrushRegionModel, HtxBrush };