Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the feature to Remove annotation objects in a specified range of frames #3617

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
25527db
Test Commit for Remove Range
gudipudiramanakumar Aug 27, 2021
aab67f4
Remove annotations in range merged with remove annotations button merged
gudipudiramanakumar Aug 28, 2021
a085554
Update annotation-reducer.ts
gudipudiramanakumar Aug 28, 2021
7839fe8
Update annotation-actions.ts
gudipudiramanakumar Aug 28, 2021
c092a56
Update annotation-reducer.ts
gudipudiramanakumar Aug 28, 2021
4d39b94
Merge branch 'openvinotoolkit:develop' into feature-removeannotations…
gudipudiramanakumar Sep 4, 2021
cde900f
Converting remove range component to hook based component
gudipudiramanakumar Oct 12, 2021
5ed81c3
Improved clear in cvat core and implemented remove range
gudipudiramanakumar Oct 18, 2021
6f5b922
Merge branch 'feature-removeannotationsinrange' into develop
gudipudiramanakumar Oct 18, 2021
d47db3c
Merge pull request #1 from gudipudiramanakumar/develop
gudipudiramanakumar Oct 18, 2021
25bbb49
Matching only the needed parts
gudipudiramanakumar Oct 18, 2021
6dba4e8
Merge branch 'feature-removeannotationsinrange' of https://github.com…
gudipudiramanakumar Oct 18, 2021
d72833d
Delete out.json
gudipudiramanakumar Oct 18, 2021
abca64f
Update annotations-collection.js
gudipudiramanakumar Oct 18, 2021
fb30089
Added a checkbox to remove range modal
gudipudiramanakumar Oct 19, 2021
2e714f1
ESLint fixed
gudipudiramanakumar Oct 20, 2021
88d1ec1
Merge branch 'openvinotoolkit:develop' into feature-removeannotations…
gudipudiramanakumar Oct 20, 2021
6cc2403
More ESLint and other updates
gudipudiramanakumar Oct 20, 2021
5317c04
Merge branch 'feature-removeannotationsinrange' of https://github.com…
gudipudiramanakumar Oct 20, 2021
c7a4a03
Update annotation-menu.tsx
gudipudiramanakumar Oct 20, 2021
e5c23b0
Update remove-range-confirm.tsx
gudipudiramanakumar Oct 21, 2021
455466f
Changed the approach of removeAnnotations modal
gudipudiramanakumar Oct 23, 2021
a313f46
Merge branch 'openvinotoolkit:develop' into feature-removeannotations…
gudipudiramanakumar Oct 25, 2021
8b0dc67
Added to changelog
gudipudiramanakumar Oct 26, 2021
96e73a7
Merge branch 'feature-removeannotationsinrange' of https://github.com…
gudipudiramanakumar Oct 26, 2021
b92b93c
Merge branch 'openvinotoolkit:develop' into feature-removeannotations…
gudipudiramanakumar Oct 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions cvat-ui/src/actions/annotation-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ export enum AnnotationActionTypes {
UPLOAD_JOB_ANNOTATIONS_FAILED = 'UPLOAD_JOB_ANNOTATIONS_FAILED',
REMOVE_JOB_ANNOTATIONS_SUCCESS = 'REMOVE_JOB_ANNOTATIONS_SUCCESS',
REMOVE_JOB_ANNOTATIONS_FAILED = 'REMOVE_JOB_ANNOTATIONS_FAILED',
REMOVE_ANNOTATIONS_INRANGE_SUCCESS = 'REMOVE_ANNOTATIONS_INRANGE_SUCCESS',
REMOVE_ANNOTATIONS_INRANGE_FAILED = 'REMOVE_ANNOTATIONS_INRANGE_FAILED',
REMOVE_ANNOTATIONS_INRANGE = 'REMOVE_ANNOTATIONS_INRANGE',
CHANGE_REMOVE_ANNOTATIONS_RANGE = 'CHANGE_REMOVE_ANNOTATIONS_RANGE',
UPDATE_CANVAS_CONTEXT_MENU = 'UPDATE_CANVAS_CONTEXT_MENU',
UNDO_ACTION_SUCCESS = 'UNDO_ACTION_SUCCESS',
UNDO_ACTION_FAILED = 'UNDO_ACTION_FAILED',
Expand Down Expand Up @@ -504,6 +508,62 @@ export function changePropagateFrames(frames: number): AnyAction {
};
}


//To remove annotation objects present in given range of frames
export function removeAnnotationsinRangeAsync(sessionInstance: any, startFrame: number, endFrame: number, force: boolean): ThunkAction{
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters();
try {

for(let frame = startFrame; frame<=endFrame; frame++){
await dispatch(changeFrameAsync(frame));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to navigate each frame here? In this case annotation removing can fetch huge amount of time (getting frames from the server, decoding them, etc).


const states = await sessionInstance.annotations.get(frame, showAllInterpolationTracks, filters );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If showAllInterpolationTracks is true, it will remove all the tracks in the job, even if they are not presented in range.
If filters is not empty, it will not remove some objects which are not filtered according to filters.


states.forEach(async (state: any) => {
await dispatch(removeObjectAsync(sessionInstance,state,force));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested it with undo/redo? The objects probably can be restored with undo in this case, at the same time when remove all annotations, they can't be restored.

});
}

dispatch({
type: AnnotationActionTypes.REMOVE_ANNOTATIONS_INRANGE_SUCCESS,
payload: {
startFrame,
endFrame,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really need this payload? It was not changed in the function, so, I believe we do not need to pass it to reducers

},
});

}catch(error){
dispatch({
type: AnnotationActionTypes.REMOVE_ANNOTATIONS_INRANGE_FAILED,
payload: {
error,
},
});
}
}
}


export function removeAnnotationsinRange(sessionInstance: any | null): AnyAction {
return {
type: AnnotationActionTypes.REMOVE_ANNOTATIONS_INRANGE,
payload: {
sessionInstance,
},
};
}

export function changeRemoveAnnotationRange(startFrame: number, endFrame: number): AnyAction {
return {
type: AnnotationActionTypes.CHANGE_REMOVE_ANNOTATIONS_RANGE,
payload: {
startFrame,
endFrame,
},
};
}

export function removeObjectAsync(sessionInstance: any, objectState: any, force: boolean): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
import React from 'react';
import Menu from 'antd/lib/menu';
import Modal from 'antd/lib/modal';
import Button from 'antd/lib/button';
// eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';

import ExportDatasetModal from 'components/export-dataset/export-dataset-modal';
import LoadSubmenu from 'components/actions-menu/load-submenu';
import { DimensionType } from '../../../reducers/interfaces';

import RemoveAnnotationsRangeContainer from 'containers/annotation-page/top-bar/remove-range-confirm';

interface Props {
taskMode: string;
loaders: any[];
Expand All @@ -20,6 +23,7 @@ interface Props {
isReviewer: boolean;
jobInstance: any;
onClickMenu(params: MenuInfo, file?: File): void;
removeRange(): void;
setForceExitAnnotationFlag(forceExit: boolean): void;
saveAnnotations(jobInstance: any, afterSave?: () => void): void;
}
Expand All @@ -42,6 +46,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
isReviewer,
jobInstance,
onClickMenu,
removeRange,
setForceExitAnnotationFlag,
saveAnnotations,
} = props;
Expand Down Expand Up @@ -106,21 +111,42 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
} else {
onClickMenu(copyParams);
}
} else if (copyParams.key === Actions.REMOVE_ANNO) {
} else if (copyParams.key === Actions.REMOVE_ANNO) {
Modal.confirm({
title: 'All the annotations will be removed',
title: 'Remove Annotations',
content:
'You are going to remove all the annotations from the client. ' +
'Select whether to remove all annotations from the job or remove within a range' + '\n' +
'It will stay on the server till you save the job. Continue?',
className: 'cvat-modal-confirm-remove-annotation',
onOk: () => {
onClickMenu(copyParams);
Modal.confirm({
Copy link
Member

@bsekachev bsekachev Oct 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest following:

pseudocode

let removeFrom;
let removeUpTo;
let removeOnlyKeyframes = false;
Modal.confirm({
    content: (
        <div>  // of course, need to format content properly  
            <Text>message</Text> // of course, need to format content properly 
            <Text>Leave the inputs below empty to remove all the annotations from this job</Text>
            <Text>From:</Text><InputNumber onChange={(value) => removeFrom = value} />
            <Text>To:</Text><InputNumbe onChange={(value) => removeUpTo= value} />
            <Tooltip title='Applicable if remove annotations in range'>
                <Checkbox onClick={(checked) => removeOnlyKeyframes = checked}>Delete only keyframes for tracks</Checkbox>
            </Tooltip>
        </div>
    )

    onOk: () => removeAnnotations(removeFrom, removeUpTo, )
})

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you do not need remove-range-confirm.tsx Component.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll try this approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also suggest clubbing the removeAnnotationsAsync and removeAnnotationsinRangeAsync actions as one action

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I think yes. They are very similar each other.

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?',
className: 'cvat-modal-confirm-remove-annotation',
onOk: () => {
onClickMenu(copyParams);
},
okButtonProps: {
type: 'primary',
danger: true,
},
okText: 'Delete',
});
},
okButtonProps: {
type: 'primary',
danger: true,
danger: true
},
okText: 'Remove All',
onCancel: () => {
removeRange();
},
cancelText: "Select Range",
cancelButtonProps: {
type: 'primary',
},
okText: 'Delete',
});
} else if ([Actions.REQUEST_REVIEW].includes(copyParams.key as Actions)) {
checkUnsavedChanges(copyParams);
Expand Down Expand Up @@ -178,6 +204,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
)}
{jobStatus === 'completed' && <Menu.Item key={Actions.RENEW_JOB}>Renew the job</Menu.Item>}
<ExportDatasetModal />
<RemoveAnnotationsRangeContainer/>
</Menu>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (C) 2020-2021 Intel Corporation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright (C) 2020-2021 Intel Corporation
// Copyright (C) 2021 Intel Corporation

//
// SPDX-License-Identifier: MIT

import React from 'react';

import Modal from 'antd/lib/modal';
import InputNumber from 'antd/lib/input-number';
import Text from 'antd/lib/typography/Text';
import { clamp } from 'utils/math';

interface Props {
visible: boolean;
startFrame: number;
endFrame: number;
frameNumber: number;
stopFrame: number;
removeinRange(): void;
cancel(): void;
changeRemoveAnnotationsRange(startFrame: number, endFrame: number): void;
}

export default function RemoveRangeConfirmComponent(props: Props): JSX.Element {
const {
visible,
startFrame,
endFrame,
frameNumber,
stopFrame,
removeinRange,
changeRemoveAnnotationsRange,
cancel,
} = props;

const minStartFrames = 0;

const minEndFrames = Math.max(startFrame,0);

return (
<Modal
okType='primary'
okText='Yes'
cancelText='Cancel'
onOk={removeinRange}
onCancel={cancel}
title='Confirm to remove annotations in range'
visible={visible}
>
<div className='cvat-propagate-confirm'>
<Text>Do you want to remove the annotations on</Text>
<InputNumber
className='cvat-propagate-confirm-object-on-frames'
size='small'
min={minStartFrames}
max={stopFrame}
value={startFrame}
onChange={(value: number | undefined | string) => {
if (typeof value !== 'undefined') {
value=Math.floor(clamp(+value, 0, stopFrame-1));
changeRemoveAnnotationsRange(value,endFrame);
}
}}
/>
{startFrame > 1 ? <Text> frames </Text> : <Text> frame </Text>}
<Text>up to the </Text>
<InputNumber
className='cvat-propagate-confirm-object-up-to-frame'
size='small'
min={minEndFrames}
max={stopFrame}
value={endFrame}
onChange={(value: number | undefined | string) => {
if (typeof value !== 'undefined') {
value=Math.floor(clamp(+value, 1, stopFrame));
changeRemoveAnnotationsRange(startFrame,value);
}
}}
/>
<Text>frame</Text>
</div>
</Modal>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
switchRequestReviewDialog as switchRequestReviewDialogAction,
switchSubmitReviewDialog as switchSubmitReviewDialogAction,
setForceExitAnnotationFlag as setForceExitAnnotationFlagAction,
removeAnnotationsinRange as removeAnnotationsinRangeAction,
} from 'actions/annotation-actions';
import { exportActions } from 'actions/export-actions';

Expand All @@ -32,6 +33,7 @@ interface DispatchToProps {
loadAnnotations(job: any, loader: any, file: File): void;
showExportModal(task: any): void;
removeAnnotations(sessionInstance: any): void;
removeAnnotationsinRange(sessionInstance: any): void;
switchRequestReviewDialog(visible: boolean): void;
switchSubmitReviewDialog(visible: boolean): void;
setForceExitAnnotationFlag(forceExit: boolean): void;
Expand Down Expand Up @@ -74,6 +76,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
removeAnnotations(sessionInstance: any): void {
dispatch(removeAnnotationsAsync(sessionInstance));
},
removeAnnotationsinRange(sessionInstance: any){
dispatch(removeAnnotationsinRangeAction(sessionInstance));
},
switchRequestReviewDialog(visible: boolean): void {
dispatch(switchRequestReviewDialogAction(visible));
},
Expand Down Expand Up @@ -103,6 +108,7 @@ function AnnotationMenuContainer(props: Props): JSX.Element {
loadActivity,
loadAnnotations,
showExportModal,
removeAnnotationsinRange,
removeAnnotations,
switchRequestReviewDialog,
switchSubmitReviewDialog,
Expand All @@ -111,6 +117,10 @@ function AnnotationMenuContainer(props: Props): JSX.Element {
updateJob,
} = props;

const removeRange= (): void => {
removeAnnotationsinRange(jobInstance);
}

const onClickMenu = (params: MenuInfo, file?: File): void => {
if (params.keyPath.length > 1) {
const [additionalKey, action] = params.keyPath;
Expand Down Expand Up @@ -154,6 +164,7 @@ function AnnotationMenuContainer(props: Props): JSX.Element {
dumpers={dumpers}
loadActivity={loadActivity}
onClickMenu={onClickMenu}
removeRange={removeRange}
setForceExitAnnotationFlag={setForceExitAnnotationFlag}
saveAnnotations={saveAnnotations}
jobInstance={jobInstance}
Expand Down
Loading