1
0
mirror of https://github.com/openvinotoolkit/cvat.git synced 2022-03-09 18:58:10 +03:00

Moved to locked deleted frames

This commit is contained in:
Dmitry Kalinin
2022-03-09 13:42:51 +03:00
parent 1f23dea077
commit 700d4629be
12 changed files with 141 additions and 89 deletions

View File

@@ -1772,15 +1772,17 @@ export function deleteFrameAsync(): ThunkAction {
annotation: {
player: {
frame: {
data: frameData,
data: frameData, number: frame,
},
},
annotations: { filters },
job: {
instance: jobInstance,
},
},
settings: {
player: { showDeletedFrames },
workspace: { showAllInterpolationTracks },
},
} = state;
@@ -1789,12 +1791,22 @@ export function deleteFrameAsync(): ThunkAction {
type: AnnotationActionTypes.DELETE_FRAME,
});
await jobInstance.frames.delete(frameData.number);
const data = await jobInstance.frames.get(frameData.number);
await jobInstance.annotations.clear(false, frame, frame, false);
await jobInstance.actions.clear();
const history = await jobInstance.actions.get();
await jobInstance.annotations.save();
const states = await jobInstance.annotations.get(frame, showAllInterpolationTracks, filters);
await jobInstance.frames.delete(frame);
const data = await jobInstance.frames.get(frame);
dispatch({
type: AnnotationActionTypes.DELETE_FRAME_SUCCESS,
payload: { data },
payload: {
data,
history,
states,
},
});
if (!showDeletedFrames) {
@@ -1802,6 +1814,7 @@ export function deleteFrameAsync(): ThunkAction {
frameData.number, frameData.stopFrame, frameData.startFrame,
));
}
dispatch(saveAnnotationsAsync(jobInstance));
} catch (error) {
dispatch({
type: AnnotationActionTypes.DELETE_FRAME_FAILED,

View File

@@ -683,7 +683,7 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
if (frameData !== null && canvasInstance) {
canvasInstance.setup(
frameData,
annotations.filter((e) => e.objectType !== ObjectType.TAG),
frameData.deleted ? [] : annotations.filter((e) => e.objectType !== ObjectType.TAG),
curZLayer,
);
}

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2020-2021 Intel Corporation
// Copyright (C) 2020-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@@ -21,6 +21,7 @@ interface Props {
activeControl: ActiveControl;
keyMap: KeyMap;
normalizedKeyMap: Record<string, string>;
frameData: any;
rotateFrame(rotation: Rotation): void;
selectIssuePosition(enabled: boolean): void;
@@ -28,9 +29,11 @@ interface Props {
export default function ControlsSideBarComponent(props: Props): JSX.Element {
const {
canvasInstance, activeControl, normalizedKeyMap, keyMap, rotateFrame, selectIssuePosition,
canvasInstance, activeControl, normalizedKeyMap, keyMap, rotateFrame, selectIssuePosition, frameData,
} = props;
const controlsDisabled = frameData.deleted;
const preventDefault = (event: KeyboardEvent | undefined): void => {
if (event) {
event.preventDefault();
@@ -42,26 +45,32 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
OPEN_REVIEW_ISSUE: keyMap.OPEN_REVIEW_ISSUE,
};
const handlers = {
let handlers: any = {
CANCEL: (event: KeyboardEvent | undefined) => {
preventDefault(event);
if (activeControl !== ActiveControl.CURSOR) {
canvasInstance.cancel();
}
},
OPEN_REVIEW_ISSUE: (event: KeyboardEvent | undefined) => {
preventDefault(event);
if (activeControl === ActiveControl.OPEN_ISSUE) {
canvasInstance.selectRegion(false);
selectIssuePosition(false);
} else {
canvasInstance.cancel();
canvasInstance.selectRegion(true);
selectIssuePosition(true);
}
},
};
if (!controlsDisabled) {
handlers = {
...handlers,
OPEN_REVIEW_ISSUE: (event: KeyboardEvent | undefined) => {
preventDefault(event);
if (activeControl === ActiveControl.OPEN_ISSUE) {
canvasInstance.selectRegion(false);
selectIssuePosition(false);
} else {
canvasInstance.cancel();
canvasInstance.selectRegion(true);
selectIssuePosition(true);
}
},
};
}
return (
<Layout.Sider className='cvat-canvas-controls-sidebar' theme='light' width={44}>
<GlobalHotKeys keyMap={subKeyMap} handlers={handlers} />
@@ -87,6 +96,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
canvasInstance={canvasInstance}
activeControl={activeControl}
selectIssuePosition={selectIssuePosition}
disabled={controlsDisabled}
/>
</Layout.Sider>
);

View File

@@ -13,33 +13,40 @@ import CVATTooltip from 'components/common/cvat-tooltip';
interface Props {
canvasInstance: Canvas;
activeControl: ActiveControl;
disabled: boolean;
selectIssuePosition(enabled: boolean): void;
}
function CreateIssueControl(props: Props): JSX.Element {
const { activeControl, canvasInstance, selectIssuePosition } = props;
const {
activeControl, canvasInstance, selectIssuePosition, disabled,
} = props;
return (
<CVATTooltip title='Open an issue' placement='right'>
<Icon
component={RectangleIcon}
className={
activeControl === ActiveControl.OPEN_ISSUE ?
'cvat-issue-control cvat-active-canvas-control' :
'cvat-issue-control'
}
onClick={(): void => {
if (activeControl === ActiveControl.OPEN_ISSUE) {
canvasInstance.selectRegion(false);
selectIssuePosition(false);
} else {
canvasInstance.cancel();
canvasInstance.selectRegion(true);
selectIssuePosition(true);
disabled ? (
<Icon component={RectangleIcon} className='cvat-issue-control' />
) : (
<CVATTooltip title='Open an issue' placement='right'>
<Icon
component={RectangleIcon}
className={
activeControl === ActiveControl.OPEN_ISSUE ?
'cvat-issue-control cvat-active-canvas-control' :
'cvat-issue-control'
}
}}
/>
</CVATTooltip>
onClick={(): void => {
if (activeControl === ActiveControl.OPEN_ISSUE) {
canvasInstance.selectRegion(false);
selectIssuePosition(false);
} else {
canvasInstance.cancel();
canvasInstance.selectRegion(true);
selectIssuePosition(true);
}
}}
/>
</CVATTooltip>
)
);
}

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2020-2021 Intel Corporation
// Copyright (C) 2020-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@@ -34,6 +34,7 @@ interface Props {
keyMap: KeyMap;
normalizedKeyMap: Record<string, string>;
labels: any[];
frameData: any;
mergeObjects(enabled: boolean): void;
groupObjects(enabled: boolean): void;
@@ -80,8 +81,11 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
pasteShape,
resetGroup,
redrawShape,
frameData,
} = props;
const controlsDisabled = !labels.length || frameData.deleted;
const preventDefault = (event: KeyboardEvent | undefined): void => {
if (event) {
event.preventDefault();
@@ -111,7 +115,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
},
};
if (labels.length) {
if (!controlsDisabled) {
handlers = {
...handlers,
PASTE_SHAPE: (event: KeyboardEvent | undefined) => {
@@ -226,34 +230,34 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
<ObservedDrawRectangleControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_RECTANGLE}
disabled={!labels.length}
disabled={controlsDisabled}
/>
<ObservedDrawPolygonControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_POLYGON}
disabled={!labels.length}
disabled={controlsDisabled}
/>
<ObservedDrawPolylineControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_POLYLINE}
disabled={!labels.length}
disabled={controlsDisabled}
/>
<ObservedDrawPointsControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_POINTS}
disabled={!labels.length}
disabled={controlsDisabled}
/>
<ObservedDrawEllipseControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_ELLIPSE}
disabled={!labels.length}
disabled={controlsDisabled}
/>
<ObservedDrawCuboidControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_CUBOID}
disabled={!labels.length}
disabled={controlsDisabled}
/>
<ObservedSetupTagControl canvasInstance={canvasInstance} isDrawing={false} disabled={!labels.length} />
<ObservedSetupTagControl canvasInstance={canvasInstance} isDrawing={false} disabled={controlsDisabled} />
<hr />
@@ -262,7 +266,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
canvasInstance={canvasInstance}
activeControl={activeControl}
mergeObjects={mergeObjects}
disabled={!labels.length}
disabled={controlsDisabled}
/>
<ObservedGroupControl
switchGroupShortcut={normalizedKeyMap.SWITCH_GROUP_MODE}
@@ -270,14 +274,14 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
canvasInstance={canvasInstance}
activeControl={activeControl}
groupObjects={groupObjects}
disabled={!labels.length}
disabled={controlsDisabled}
/>
<ObservedSplitControl
canvasInstance={canvasInstance}
switchSplitShortcut={normalizedKeyMap.SWITCH_SPLIT_MODE}
activeControl={activeControl}
splitTrack={splitTrack}
disabled={!labels.length}
disabled={controlsDisabled}
/>
<ExtraControlsControl />

View File

@@ -824,7 +824,9 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
}
public render(): JSX.Element {
const { isActivated, canvasInstance, labels } = this.props;
const {
isActivated, canvasInstance, labels, frameData,
} = this.props;
const { libraryInitialized, approxPolyAccuracy, mode } = this.state;
const dynamcPopoverPros = isActivated ?
{
@@ -845,7 +847,7 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
className: 'cvat-tools-control',
};
return !labels.length ? (
return !labels.length || frameData.deleted ? (
<Icon className='cvat-opencv-control cvat-disabled-canvas-control' component={OpenCVIcon} />
) : (
<>

View File

@@ -62,6 +62,7 @@ interface StateToProps {
curZOrder: number;
defaultApproxPolyAccuracy: number;
toolsBlockerState: ToolsBlockerState;
frameData: any;
}
interface DispatchToProps {
@@ -77,29 +78,42 @@ const core = getCore();
const CustomPopover = withVisibilityHandling(Popover, 'tools-control');
function mapStateToProps(state: CombinedState): StateToProps {
const { annotation } = state;
const { settings } = state;
const { number: frame } = annotation.player.frame;
const { instance: jobInstance } = annotation.job;
const { instance: canvasInstance, activeControl } = annotation.canvas;
const { models } = state;
const { interactors, detectors, trackers } = models;
const { toolsBlockerState } = state.settings.workspace;
const {
annotation: {
job: { instance: jobInstance, labels },
canvas: { instance: canvasInstance, activeControl },
player: {
frame: { number: frame, data: frameData },
},
annotations: {
zLayer: { cur: curZOrder },
states,
},
drawing: { activeLabelID },
},
models: {
interactors, detectors, trackers,
},
settings: {
workspace: { toolsBlockerState, defaultApproxPolyAccuracy },
},
} = state;
return {
interactors,
detectors,
trackers,
isActivated: activeControl === ActiveControl.AI_TOOLS,
activeLabelID: annotation.drawing.activeLabelID,
labels: annotation.job.labels,
states: annotation.annotations.states,
activeLabelID,
labels,
states,
canvasInstance: canvasInstance as Canvas,
jobInstance,
frame,
curZOrder: annotation.annotations.zLayer.cur,
defaultApproxPolyAccuracy: settings.workspace.defaultApproxPolyAccuracy,
curZOrder,
defaultApproxPolyAccuracy,
toolsBlockerState,
frameData,
};
}
@@ -1095,7 +1109,7 @@ export class ToolsControlComponent extends React.PureComponent<Props, State> {
public render(): JSX.Element | null {
const {
interactors, detectors, trackers, isActivated, canvasInstance, labels,
interactors, detectors, trackers, isActivated, canvasInstance, labels, frameData,
} = this.props;
const {
fetching, approxPolyAccuracy, pointsRecieved, mode, portals,
@@ -1122,7 +1136,7 @@ export class ToolsControlComponent extends React.PureComponent<Props, State> {
className: 'cvat-tools-control',
};
const showAnyContent = !!labels.length;
const showAnyContent = !(!labels.length || frameData.deleted);
const showInteractionContent = isActivated && mode === 'interaction' && pointsRecieved;
const showDetectionContent = fetching && mode === 'detection';

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2020-2021 Intel Corporation
// Copyright (C) 2020-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@@ -40,6 +40,7 @@ interface StateToProps {
frameNumber: number;
keyMap: KeyMap;
normalizedKeyMap: Record<string, string>;
frameData: any;
}
interface DispatchToProps {
@@ -53,7 +54,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
const {
annotation: {
player: {
frame: { number: frameNumber },
frame: { number: frameNumber, data: frameData },
},
annotations: { states },
job: { instance: jobInstance, labels },
@@ -70,6 +71,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
frameNumber,
keyMap,
normalizedKeyMap,
frameData,
};
}
@@ -102,6 +104,7 @@ function TagAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.Elemen
onRememberObject,
createAnnotations,
keyMap,
frameData,
} = props;
const preventDefault = (event: KeyboardEvent | undefined): void => {
@@ -110,6 +113,7 @@ function TagAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.Elemen
}
};
const controlsDisabled = !labels.length || frameData.deleted;
const defaultLabelID = labels.length ? labels[0].id : null;
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
@@ -199,7 +203,7 @@ function TagAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.Elemen
},
};
return !labels.length ? (
return controlsDisabled ? (
<Layout.Sider {...siderProps}>
{/* eslint-disable-next-line */}
<span
@@ -215,7 +219,7 @@ function TagAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.Elemen
</span>
<Row justify='center' className='labels-tag-annotation-sidebar-not-found-wrapper'>
<Col>
<Text strong>No labels are available.</Text>
<Text strong>Can&apos;t place tag on this frame.</Text>
</Col>
</Row>
</Layout.Sider>

View File

@@ -60,7 +60,8 @@ function PlayerNavigation(props: Props): JSX.Element {
const showDeleteFrameDialog = useCallback(() => {
if (!playing) {
modal.confirm({
content: 'Do you want to delete this frame?',
title: 'Do you want to delete this frame?',
content: 'All annotations from this frame will be deleted. You will be able to restore frame (but not annotations) later.',
className: 'cvat-modal-delete-frame',
okText: 'Delete',
okType: 'danger',

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2020-2021 Intel Corporation
// Copyright (C) 2020-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@@ -16,6 +16,7 @@ interface StateToProps {
activeControl: ActiveControl;
keyMap: KeyMap;
normalizedKeyMap: Record<string, string>;
frameData: any;
}
interface DispatchToProps {
@@ -27,6 +28,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
const {
annotation: {
canvas: { instance: canvasInstance, activeControl },
player: { frame: { data: frameData } },
},
settings: {
player: { rotateAll },
@@ -40,6 +42,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
activeControl,
normalizedKeyMap,
keyMap,
frameData,
};
}

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2020-2021 Intel Corporation
// Copyright (C) 2020-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@@ -26,6 +26,7 @@ interface StateToProps {
keyMap: KeyMap;
normalizedKeyMap: Record<string, string>;
labels: any[];
frameData: any;
}
interface DispatchToProps {
@@ -44,6 +45,9 @@ function mapStateToProps(state: CombinedState): StateToProps {
annotation: {
canvas: { instance: canvasInstance, activeControl },
job: { labels },
player: {
frame: { data: frameData },
},
},
settings: {
player: { rotateAll },
@@ -58,6 +62,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
labels,
normalizedKeyMap,
keyMap,
frameData,
};
}

View File

@@ -419,11 +419,7 @@ class TaskData(InstanceLabelData):
@property
def tracks(self):
for idx, _track in enumerate(self._annotation_ir.tracks):
track = copy.deepcopy(_track)
track["shapes"] = [shape for shape in track["shapes"] if shape["frame"] not in self._db_task.data.deleted_frames]
if not track["shapes"]:
continue
for idx, track in enumerate(self._annotation_ir.tracks):
tracked_shapes = TrackManager.get_interpolated_shapes(
track, 0, self._db_task.data.size)
for tracked_shape in tracked_shapes:
@@ -432,9 +428,6 @@ class TaskData(InstanceLabelData):
tracked_shape["group"] = track["group"]
tracked_shape["source"] = track["source"]
tracked_shape["label_id"] = track["label_id"]
if tracked_shape["frame"] + 1 in self._db_task.data.deleted_frames \
or tracked_shape["frame"] - 1 in self._db_task.data.deleted_frames:
tracked_shape["keyframe"] = True
yield TaskData.Track(
label=self._get_label_name(track["label_id"]),
@@ -882,11 +875,7 @@ class ProjectData(InstanceLabelData):
def tracks(self):
idx = 0
for task in self._db_tasks.values():
for _track in self._annotation_irs[task.id].tracks:
track = copy.deepcopy(_track)
track["shapes"] = [shape for shape in track["shapes"] if shape["frame"] not in task.data.deleted_frames]
if not track["shapes"]:
continue
for track in self._annotation_irs[task.id].tracks:
tracked_shapes = TrackManager.get_interpolated_shapes(
track, 0, task.data.size
)
@@ -901,7 +890,7 @@ class ProjectData(InstanceLabelData):
group=track["group"],
source=track["source"],
shapes=[self._export_tracked_shape(shape, task.id)
for shape in tracked_shapes],
for shape in tracked_shapes if shape["frame"] not in task.data.deleted_frames],
task_id=task.id
)
idx+=1