Skip to content

Commit

Permalink
Added the feature to Remove annotation objects in a specified range o…
Browse files Browse the repository at this point in the history
…f frames (#3617)

* Test Commit for Remove Range

Test Commit for Remove Range

* Remove annotations in range merged with remove annotations button merged

Remove annotations in range merged with remove annotations button merged

* Update annotation-reducer.ts

* Update annotation-actions.ts

* Update annotation-reducer.ts

* Converting remove range component to hook based component

Removed all the global states previously used and converted all the parameters to local state in annotation menu and remove range component.

* Improved clear in cvat core and implemented remove range

Added arguments of startframe and endframe to clear method in annotation-collection, and also added the updating of the states with payload on removeannotationsinrangeasync action in the reducer.

* Matching only the needed parts

There are few additional old files that were needed to be removed to be completely matched with develop branch of cvat

* Delete out.json

* Update annotations-collection.js

* Added a checkbox to remove range modal

Added a checkbox to remove range modal that can be used to select if only the keyframes should be deleted in tracks or the whole track

* ESLint fixed

All the updated files were formatted as per ESLint except one line in that even cvat base is also overlooking i.e.
Row 162, Column 15: "JSX props should not use functions" in cvat\cvat-ui\src\components\annotation-page\top-bar\annotation-menu.tsx.

* More ESLint and other updates

Changed all the suggested changes and also removed unnecessary files in dist.
Removed unnecessary explicit removals in objects and additional wrappers.

* Update annotation-menu.tsx

Fixed the mistake of wrong variable name.

* Update remove-range-confirm.tsx

Additional ESLint Issue fixed

* Changed the approach of removeAnnotations modal

Changed the approach of removeAnnotations modal so that it could match the implementation of all the other components

* Added to changelog

Fixed type annotations in the annotation-menu component for remove annotations, and updated cvat-ui and cvat-core npm versions.
  • Loading branch information
gudipudiramanakumar authored Oct 26, 2021
1 parent 4b2769c commit 78158cb
Show file tree
Hide file tree
Showing 12 changed files with 128 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Progress bar for manifest creating (<https://github.com/openvinotoolkit/cvat/pull/3712>)
- Add a tutorial on attaching cloud storage AWS-S3 (<https://github.com/openvinotoolkit/cvat/pull/3745>)
and Azure Blob Container (<https://github.com/openvinotoolkit/cvat/pull/3778>)
- The feature to remove annotations in a specified range of frames (<https://github.com/openvinotoolkit/cvat/pull/3617>)

### Changed

Expand Down
4 changes: 2 additions & 2 deletions cvat-core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cvat-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-core",
"version": "3.16.1",
"version": "3.17.0",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "babel.config.js",
"scripts": {
Expand Down
42 changes: 34 additions & 8 deletions cvat-core/src/annotations-collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -553,14 +553,40 @@
return groupIdx;
}

clear() {
this.shapes = {};
this.tags = {};
this.tracks = [];
this.objects = {}; // by id
this.count = 0;

this.flush = true;
clear(startframe, endframe, delTrackKeyframesOnly) {
if (startframe !== undefined && endframe !== undefined) {
// If only a range of annotations need to be cleared
for (let frame = startframe; frame <= endframe; frame++) {
this.shapes[frame] = [];
this.tags[frame] = [];
}
const { tracks } = this;
tracks.forEach((track) => {
if (track.frame <= endframe) {
if (delTrackKeyframesOnly) {
for (const keyframe in track.shapes) {
if (keyframe >= startframe && keyframe <= endframe) { delete track.shapes[keyframe]; }
}
} else if (track.frame >= startframe) {
const index = tracks.indexOf(track);
if (index > -1) { tracks.splice(index, 1); }
}
}
});
} else if (startframe === undefined && endframe === undefined) {
// If all annotations need to be cleared
this.shapes = {};
this.tags = {};
this.tracks = [];
this.objects = {}; // by id
this.count = 0;

this.flush = true;
} else {
// If inputs provided were wrong
throw Error('Could not remove the annotations, please provide both inputs or'
+ ' leave the inputs below empty to remove all the annotations from this job');
}
}

statistics() {
Expand Down
4 changes: 2 additions & 2 deletions cvat-core/src/annotations.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,13 @@
return false;
}

async function clearAnnotations(session, reload) {
async function clearAnnotations(session, reload, startframe, endframe, delTrackKeyframesOnly) {
checkObjectType('reload', reload, 'boolean', null);
const sessionType = session instanceof Task ? 'task' : 'job';
const cache = getCache(sessionType);

if (cache.has(session)) {
cache.get(session).collection.clear();
cache.get(session).collection.clear(startframe, endframe, delTrackKeyframesOnly);
}

if (reload) {
Expand Down
8 changes: 4 additions & 4 deletions cvat-core/src/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
return result;
},

async clear(reload = false) {
const result = await PluginRegistry.apiWrapper.call(this, prototype.annotations.clear, reload);
async clear(reload = false, startframe = undefined, endframe = undefined, delTrackKeyframesOnly = true) {
const result = await PluginRegistry.apiWrapper.call(this, prototype.annotations.clear, reload, startframe, endframe, delTrackKeyframesOnly);
return result;
},

Expand Down Expand Up @@ -1897,8 +1897,8 @@
return result;
};

Job.prototype.annotations.clear.implementation = async function (reload) {
const result = await clearAnnotations(this, reload);
Job.prototype.annotations.clear.implementation = async function (reload, startframe, endframe, delTrackKeyframesOnly) {
const result = await clearAnnotations(this, reload, startframe, endframe, delTrackKeyframesOnly);
return result;
};

Expand Down
4 changes: 2 additions & 2 deletions cvat-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cvat-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.24.1",
"version": "1.25.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
15 changes: 11 additions & 4 deletions cvat-ui/src/actions/annotation-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,17 +306,24 @@ export function updateCanvasContextMenu(
};
}

export function removeAnnotationsAsync(sessionInstance: any): ThunkAction {
export function removeAnnotationsAsync(
startFrame: number, endFrame: number, delTrackKeyframesOnly: boolean,
): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
await sessionInstance.annotations.clear();
await sessionInstance.actions.clear();
const history = await sessionInstance.actions.get();
const {
filters, frame, showAllInterpolationTracks, jobInstance,
} = receiveAnnotationsParameters();
await jobInstance.annotations.clear(false, startFrame, endFrame, delTrackKeyframesOnly);
await jobInstance.actions.clear();
const history = await jobInstance.actions.get();
const states = await jobInstance.annotations.get(frame, showAllInterpolationTracks, filters);

dispatch({
type: AnnotationActionTypes.REMOVE_JOB_ANNOTATIONS_SUCCESS,
payload: {
history,
states,
},
});
} catch (error) {
Expand Down
59 changes: 53 additions & 6 deletions cvat-ui/src/components/annotation-page/top-bar/annotation-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
// SPDX-License-Identifier: MIT

import React from 'react';

import Menu from 'antd/lib/menu';
import Modal from 'antd/lib/modal';
import Text from 'antd/lib/typography/Text';
import {
InputNumber, Tooltip, Checkbox, Collapse,
} from 'antd';
// eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';

Expand All @@ -20,6 +25,8 @@ interface Props {
jobInstance: any;
onClickMenu(params: MenuInfo): void;
onUploadAnnotations(format: string, file: File): void;
stopFrame: number;
removeAnnotations(startnumber: number, endnumber: number, delTrackKeyframesOnly:boolean): void;
setForceExitAnnotationFlag(forceExit: boolean): void;
saveAnnotations(jobInstance: any, afterSave?: () => void): void;
}
Expand All @@ -41,8 +48,10 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
loadActivity,
isReviewer,
jobInstance,
stopFrame,
onClickMenu,
onUploadAnnotations,
removeAnnotations,
setForceExitAnnotationFlag,
saveAnnotations,
} = props;
Expand Down Expand Up @@ -80,14 +89,52 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
}

if (params.key === Actions.REMOVE_ANNO) {
let removeFrom: number;
let removeUpTo: number;
let removeOnlyKeyframes = false;
const { Panel } = Collapse;
Modal.confirm({
title: 'All the annotations will be removed',
content:
'You are going to remove all the annotations from the client. ' +
'It will stay on the server till you save the job. Continue?',
title: 'Remove Annotations',
content: (
<div>
<Text>You are going to remove the annotations from the client. </Text>
<Text>It will stay on the server till you save the job. Continue?</Text>
<br />
<br />
<Collapse bordered={false}>
<Panel header={<Text>Select Range</Text>} key={1}>
<Text>From: </Text>
<InputNumber
min={0}
max={stopFrame}
onChange={(value) => {
removeFrom = value;
}}
/>
<Text> To: </Text>
<InputNumber
min={0}
max={stopFrame}
onChange={(value) => { removeUpTo = value; }}
/>
<Tooltip title='Applicable only for annotations in range'>
<br />
<br />
<Checkbox
onChange={(check) => {
removeOnlyKeyframes = check.target.checked;
}}
>
Delete only keyframes for tracks
</Checkbox>
</Tooltip>
</Panel>
</Collapse>
</div>
),
className: 'cvat-modal-confirm-remove-annotation',
onOk: () => {
onClickMenu(params);
removeAnnotations(removeFrom, removeUpTo, removeOnlyKeyframes);
},
okButtonProps: {
type: 'primary',
Expand Down Expand Up @@ -127,7 +174,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
const is2d = jobInstance.task.dimension === DimensionType.DIM_2D;

return (
<Menu onClick={onClickMenuWrapper} className='cvat-annotation-menu' selectable={false}>
<Menu onClick={(params: MenuInfo) => onClickMenuWrapper(params)} className='cvat-annotation-menu' selectable={false}>
{LoadSubmenu({
loaders,
loadActivity,
Expand Down
20 changes: 13 additions & 7 deletions cvat-ui/src/containers/annotation-page/top-bar/annotation-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,26 @@ import AnnotationMenuComponent, { Actions } from 'components/annotation-page/top
import { updateJobAsync } from 'actions/tasks-actions';
import {
uploadJobAnnotationsAsync,
removeAnnotationsAsync,
saveAnnotationsAsync,
switchRequestReviewDialog as switchRequestReviewDialogAction,
switchSubmitReviewDialog as switchSubmitReviewDialogAction,
setForceExitAnnotationFlag as setForceExitAnnotationFlagAction,
removeAnnotationsAsync as removeAnnotationsAsyncAction,
} from 'actions/annotation-actions';
import { exportActions } from 'actions/export-actions';

interface StateToProps {
annotationFormats: any;
jobInstance: any;
stopFrame: number;
loadActivity: string | null;
user: any;
}

interface DispatchToProps {
loadAnnotations(job: any, loader: any, file: File): void;
showExportModal(task: any): void;
removeAnnotations(sessionInstance: any): void;
removeAnnotations(startnumber:number, endnumber:number, delTrackKeyframesOnly:boolean): void;
switchRequestReviewDialog(visible: boolean): void;
switchSubmitReviewDialog(visible: boolean): void;
setForceExitAnnotationFlag(forceExit: boolean): void;
Expand All @@ -43,7 +44,10 @@ function mapStateToProps(state: CombinedState): StateToProps {
const {
annotation: {
activities: { loads: jobLoads },
job: { instance: jobInstance },
job: {
instance: jobInstance,
instance: { stopFrame },
},
},
formats: { annotationFormats },
tasks: {
Expand All @@ -58,6 +62,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
return {
loadActivity: taskID in loads || jobID in jobLoads ? loads[taskID] || jobLoads[jobID] : null,
jobInstance,
stopFrame,
annotationFormats,
user,
};
Expand All @@ -71,8 +76,8 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
showExportModal(task: any): void {
dispatch(exportActions.openExportModal(task));
},
removeAnnotations(sessionInstance: any): void {
dispatch(removeAnnotationsAsync(sessionInstance));
removeAnnotations(startnumber: number, endnumber: number, delTrackKeyframesOnly:boolean) {
dispatch(removeAnnotationsAsyncAction(startnumber, endnumber, delTrackKeyframesOnly));
},
switchRequestReviewDialog(visible: boolean): void {
dispatch(switchRequestReviewDialogAction(visible));
Expand All @@ -97,6 +102,7 @@ type Props = StateToProps & DispatchToProps & RouteComponentProps;
function AnnotationMenuContainer(props: Props): JSX.Element {
const {
jobInstance,
stopFrame,
user,
annotationFormats: { loaders, dumpers },
history,
Expand All @@ -122,8 +128,6 @@ function AnnotationMenuContainer(props: Props): JSX.Element {
const [action] = params.keyPath;
if (action === Actions.EXPORT_TASK_DATASET) {
showExportModal(jobInstance.task);
} else if (action === Actions.REMOVE_ANNO) {
removeAnnotations(jobInstance);
} else if (action === Actions.REQUEST_REVIEW) {
switchRequestReviewDialog(true);
} else if (action === Actions.SUBMIT_REVIEW) {
Expand Down Expand Up @@ -151,10 +155,12 @@ function AnnotationMenuContainer(props: Props): JSX.Element {
loadActivity={loadActivity}
onUploadAnnotations={onUploadAnnotations}
onClickMenu={onClickMenu}
removeAnnotations={removeAnnotations}
setForceExitAnnotationFlag={setForceExitAnnotationFlag}
saveAnnotations={saveAnnotations}
jobInstance={jobInstance}
isReviewer={isReviewer}
stopFrame={stopFrame}
/>
);
}
Expand Down
5 changes: 4 additions & 1 deletion cvat-ui/src/reducers/annotation-reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -917,16 +917,19 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
},
};
}
// Added Remove Annotations
case AnnotationActionTypes.REMOVE_JOB_ANNOTATIONS_SUCCESS: {
const { history } = action.payload;
const { states } = action.payload;
return {
...state,
annotations: {
...state.annotations,
history,
states,
selectedStatesID: [],
activatedStateID: null,
collapsed: {},
states: [],
},
};
}
Expand Down

0 comments on commit 78158cb

Please sign in to comment.