Skip to content

Commit

Permalink
hide mask on shortcut
Browse files Browse the repository at this point in the history
  • Loading branch information
klakhov committed Oct 16, 2024
1 parent b1b89e7 commit dc04778
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 28 deletions.
6 changes: 6 additions & 0 deletions cvat-canvas/src/typescript/canvasModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export interface Configuration {
controlPointsSize?: number;
outlinedBorders?: string | false;
resetZoom?: boolean;
hideEditedObject?: boolean;
}

export interface BrushTool {
Expand Down Expand Up @@ -416,6 +417,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
textPosition: consts.DEFAULT_SHAPE_TEXT_POSITION,
textContent: consts.DEFAULT_SHAPE_TEXT_CONTENT,
undefinedAttrValue: consts.DEFAULT_UNDEFINED_ATTR_VALUE,
hideEditedObject: false,
},
imageBitmap: false,
image: null,
Expand Down Expand Up @@ -981,6 +983,10 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
this.data.configuration.CSSImageFilter = configuration.CSSImageFilter;
}

if (typeof configuration.hideEditedObject === 'boolean') {
this.data.configuration.hideEditedObject = configuration.hideEditedObject;
}

this.notify(UpdateReasons.CONFIG_UPDATED);
}

Expand Down
29 changes: 25 additions & 4 deletions cvat-canvas/src/typescript/masksHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export class MasksHandlerImpl implements MasksHandler {
private startTimestamp: number;
private geometry: Geometry;
private drawingOpacity: number;
private isHidden: boolean;

private keepDrawnPolygon(): void {
const canvasWrapper = this.canvas.getElement().parentElement;
Expand Down Expand Up @@ -217,12 +218,22 @@ export class MasksHandlerImpl implements MasksHandler {
private imageDataFromCanvas(wrappingBBox: WrappingBBox): Uint8ClampedArray {
const imageData = this.canvas.toCanvasElement()
.getContext('2d').getImageData(
wrappingBBox.left, wrappingBBox.top,
wrappingBBox.right - wrappingBBox.left + 1, wrappingBBox.bottom - wrappingBBox.top + 1,
wrappingBBox.left,
wrappingBBox.top,
wrappingBBox.right - wrappingBBox.left + 1,
wrappingBBox.bottom - wrappingBBox.top + 1,
).data;
return imageData;
}

private updateHidden(value: boolean) {
if (value) {
this.canvas.getElement().parentElement.style.display = 'none';
} else {
this.canvas.getElement().parentElement.style.display = 'block';
}
}

private updateBrushTools(brushTool?: BrushTool, opts: Partial<BrushTool> = {}): void {
if (this.isPolygonDrawing) {
// tool was switched from polygon to brush for example
Expand Down Expand Up @@ -530,6 +541,10 @@ export class MasksHandlerImpl implements MasksHandler {

public configurate(configuration: Configuration): void {
this.colorBy = configuration.colorBy;

if (this.isHidden !== configuration.hideEditedObject) {
this.updateHidden(configuration.hideEditedObject);
}
}

public transform(geometry: Geometry): void {
Expand Down Expand Up @@ -563,7 +578,10 @@ export class MasksHandlerImpl implements MasksHandler {
const color = fabric.Color.fromHex(this.getStateColor(drawData.initialState)).getSource();
const [left, top, right, bottom] = points.slice(-4);
const imageBitmap = expandChannels(color[0], color[1], color[2], points);
imageDataToDataURL(imageBitmap, right - left + 1, bottom - top + 1,
imageDataToDataURL(
imageBitmap,
right - left + 1,
bottom - top + 1,
(dataURL: string) => new Promise((resolve) => {
fabric.Image.fromURL(dataURL, (image: fabric.Image) => {
try {
Expand Down Expand Up @@ -654,7 +672,10 @@ export class MasksHandlerImpl implements MasksHandler {
const color = fabric.Color.fromHex(this.getStateColor(editData.state)).getSource();
const [left, top, right, bottom] = points.slice(-4);
const imageBitmap = expandChannels(color[0], color[1], color[2], points);
imageDataToDataURL(imageBitmap, right - left + 1, bottom - top + 1,
imageDataToDataURL(
imageBitmap,
right - left + 1,
bottom - top + 1,
(dataURL: string) => new Promise((resolve) => {
fabric.Image.fromURL(dataURL, (image: fabric.Image) => {
try {
Expand Down
39 changes: 34 additions & 5 deletions cvat-ui/src/actions/annotation-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ export enum AnnotationActionTypes {
COLLAPSE_APPEARANCE = 'COLLAPSE_APPEARANCE',
COLLAPSE_OBJECT_ITEMS = 'COLLAPSE_OBJECT_ITEMS',
ACTIVATE_OBJECT = 'ACTIVATE_OBJECT',
EDIT_OBJECT = 'EDIT_OBJECT',
UPDATE_EDITED_STATE = 'UPDATE_EDITED_STATE',
HIDE_EDITED_STATE = 'HIDE_EDITED_STATE',
RESET_EDITED_STATE = 'RESET_EDITED_STATE',
REMOVE_OBJECT = 'REMOVE_OBJECT',
REMOVE_OBJECT_SUCCESS = 'REMOVE_OBJECT_SUCCESS',
REMOVE_OBJECT_FAILED = 'REMOVE_OBJECT_FAILED',
Expand Down Expand Up @@ -243,16 +245,43 @@ export function highlightConflict(conflict: QualityConflict | null): AnyAction {
};
}

// TODO: think about better name for this action
export function editObject(objectType: ShapeType | null): AnyAction {
export function updateEditedState(shapeType: ShapeType | null, editedState: ObjectState | null): AnyAction {
return {
type: AnnotationActionTypes.EDIT_OBJECT,
type: AnnotationActionTypes.UPDATE_EDITED_STATE,
payload: {
objectType,
shapeType,
editedState,
},
};
}

export function resetEditedState(): AnyAction {
return {
type: AnnotationActionTypes.RESET_EDITED_STATE,
payload: {},
};
}

// TODO: change to regular func, we dont need async
export function hideEditedState(hide: boolean): ThunkAction {
return async (dispatch: ThunkDispatch, getState): Promise<void> => {
const state = getState();
const { instance: canvas } = state.annotation.canvas;
if (canvas) {
(canvas as Canvas).configure({
hideEditedObject: hide,
});
}

dispatch({
type: AnnotationActionTypes.HIDE_EDITED_STATE,
payload: {
hide,
},
});
};
}

function wrapAnnotationsInGTJob(states: ObjectState[]): ObjectState[] {
return states.map((state: ObjectState) => new Proxy(state, {
get(_state, prop) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ import {
fetchAnnotationsAsync,
getDataFailed,
canvasErrorOccurred,
editObject,
updateEditedState,
resetEditedState,
} from 'actions/annotation-actions';
import {
switchGrid,
Expand Down Expand Up @@ -147,7 +148,8 @@ interface DispatchToProps {
onGetDataFailed(error: Error): void;
onCanvasErrorOccurred(error: Error): void;
onStartIssue(position: number[]): void;
onEditObject(objectType: ShapeType | null): void;
onUpdateEditedObject(objectType: ShapeType | null, editedState: ObjectState | null): void;
onResetEditedObject(): void;
}

function mapStateToProps(state: CombinedState): StateToProps {
Expand Down Expand Up @@ -349,8 +351,11 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
onStartIssue(position: number[]): void {
dispatch(reviewActions.startIssue(position));
},
onEditObject(objectType: ShapeType | null): void {
dispatch(editObject(objectType));
onUpdateEditedObject(objectType: ShapeType | null, editedState: ObjectState | null): void {
dispatch(updateEditedState(objectType, editedState));
},
onResetEditedObject(): void {
dispatch(resetEditedState());
},
};
}
Expand Down Expand Up @@ -639,7 +644,7 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {
private onCanvasShapeDrawn = (event: any): void => {
const {
jobInstance, activeLabelID, activeObjectType, frame, updateActiveControl, onCreateAnnotations,
onEditObject,
onResetEditedObject,
} = this.props;

if (!event.detail.continue) {
Expand Down Expand Up @@ -675,7 +680,7 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {

const objectState = new cvat.classes.ObjectState(state);
onCreateAnnotations([objectState]);
onEditObject(null);
onResetEditedObject();
};

private onCanvasObjectsMerged = (event: any): void => {
Expand Down Expand Up @@ -837,19 +842,19 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {
};

private onCanvasDrawStart = (event: any): void => {
const { onEditObject } = this.props;
onEditObject(event.detail.drawData.shapeType);
const { onUpdateEditedObject } = this.props;
onUpdateEditedObject(event.detail.drawData.shapeType, null);
};

private onCanvasEditStart = (event: any): void => {
const { updateActiveControl, onEditObject } = this.props;
const { updateActiveControl, onUpdateEditedObject } = this.props;
updateActiveControl(ActiveControl.EDIT);
onEditObject(event.detail.state.shapeType);
onUpdateEditedObject(event.detail.state.shapeType, event.detail.state);
};

private onCanvasEditDone = (event: any): void => {
const {
activeControl, onUpdateAnnotations, updateActiveControl, onEditObject,
activeControl, onUpdateAnnotations, updateActiveControl, onResetEditedObject,
} = this.props;
const { state, points, rotation } = event.detail;
if (state.rotation !== rotation) {
Expand All @@ -863,7 +868,7 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {
updateActiveControl(ActiveControl.CURSOR);
}
onUpdateAnnotations([state]);
onEditObject(null);
onResetEditedObject();
};

private onCanvasSliceDone = (event: any): void => {
Expand Down Expand Up @@ -903,9 +908,9 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {
};

private onCanvasCancel = (): void => {
const { onResetCanvas, onEditObject } = this.props;
const { onResetCanvas, onResetEditedObject } = this.props;
onResetCanvas();
onEditObject(null);
onResetEditedObject();
};

private onCanvasFindObject = async (e: any): Promise<void> => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
switchPropagateVisibility as switchPropagateVisibilityAction,
removeObject as removeObjectAction,
fetchAnnotationsAsync,
hideEditedState,
} from 'actions/annotation-actions';
import {
changeShowGroundTruth as changeShowGroundTruthAction,
Expand Down Expand Up @@ -56,6 +57,11 @@ interface StateToProps {
normalizedKeyMap: Record<string, string>;
showGroundTruth: boolean;
workspace: Workspace;
editedState: {
shapeType: ShapeType | null;
editedState: ObjectState | null;
editedStateHidden: boolean;
},
}

interface DispatchToProps {
Expand All @@ -67,6 +73,7 @@ interface DispatchToProps {
changeFrame(frame: number): void;
changeGroupColor(group: number, color: string): void;
changeShowGroundTruth(value: boolean): void;
changeHideEditedState(value: boolean): void;
}

const componentShortcuts = {
Expand Down Expand Up @@ -180,6 +187,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
collapsedAll,
activatedStateID,
activatedElementID,
edited,
zLayer: { min: minZLayer, max: maxZLayer },
},
job: { instance: jobInstance },
Expand Down Expand Up @@ -233,6 +241,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
normalizedKeyMap,
showGroundTruth,
workspace,
editedState: edited,
};
}

Expand Down Expand Up @@ -263,6 +272,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
dispatch(changeShowGroundTruthAction(value));
dispatch(fetchAnnotationsAsync());
},
changeHideEditedState(value: boolean): void {
dispatch(hideEditedState(value));
},
};
}

Expand Down Expand Up @@ -475,6 +487,11 @@ class ObjectsListContainer extends React.PureComponent<Props, State> {
SWITCH_HIDDEN: (event: KeyboardEvent | undefined) => {
preventDefault(event);
const state = activatedState();
const { editedState, changeHideEditedState } = this.props;
console.log(editedState);
if (editedState.shapeType === ShapeType.MASK) {
changeHideEditedState(!editedState.editedStateHidden);
}
if (state) {
state.hidden = !state.hidden;
updateAnnotations([state]);
Expand Down
43 changes: 39 additions & 4 deletions cvat-ui/src/reducers/annotation-reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ const defaultState: AnnotationState = {
activatedStateID: null,
activatedElementID: null,
activatedAttributeID: null,
editedState: null,
edited: {
shapeType: null,
editedState: null,
editedStateHidden: false,
},
highlightedConflict: null,
saving: {
forceExit: false,
Expand Down Expand Up @@ -623,13 +627,44 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
},
};
}
case AnnotationActionTypes.EDIT_OBJECT: {
const { objectType } = action.payload;
case AnnotationActionTypes.RESET_EDITED_STATE: {
return {
...state,
annotations: {
...state.annotations,
edited: {
...state.annotations.edited,
shapeType: null,
editedState: null,
editedStateHidden: false,
},
},
};
}
case AnnotationActionTypes.UPDATE_EDITED_STATE: {
const { shapeType, editedState } = action.payload;
return {
...state,
annotations: {
...state.annotations,
editedState: objectType,
edited: {
...state.annotations.edited,
shapeType: (shapeType ?? null),
editedState: (editedState ?? null),
},
},
};
}
case AnnotationActionTypes.HIDE_EDITED_STATE: {
const { hide } = action.payload;
return {
...state,
annotations: {
...state.annotations,
edited: {
...state.annotations.edited,
editedStateHidden: hide,
},
},
};
}
Expand Down
7 changes: 6 additions & 1 deletion cvat-ui/src/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Webhook, MLModel, Organization, Job, Task, Project, Label, User,
QualityConflict, FramesMetaData, RQStatus, Event, Invitation, SerializedAPISchema,
Request, TargetMetric, ValidationLayout,
ObjectState,
} from 'cvat-core-wrapper';
import { IntelligentScissors } from 'utils/opencv-wrapper/intelligent-scissors';
import { KeyMap, KeyMapItem } from 'utils/mousetrap-react';
Expand Down Expand Up @@ -770,7 +771,11 @@ export interface AnnotationState {
activatedStateID: number | null;
activatedElementID: number | null;
activatedAttributeID: number | null;
editedState: ShapeType | null;
edited: {
shapeType: ShapeType | null;
editedState: ObjectState | null;
editedStateHidden: boolean;
};
highlightedConflict: QualityConflict | null;
collapsed: Record<number, boolean>;
collapsedAll: boolean;
Expand Down

0 comments on commit dc04778

Please sign in to comment.