-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
Changes from 6 commits
25527db
aab67f4
a085554
7839fe8
c092a56
4d39b94
cde900f
5ed81c3
6f5b922
d47db3c
25bbb49
6dba4e8
d72833d
abca64f
fb30089
2e714f1
88d1ec1
6cc2403
5317c04
c7a4a03
e5c23b0
455466f
a313f46
8b0dc67
96e73a7
b92b93c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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', | ||
|
@@ -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)); | ||
|
||
const states = await sessionInstance.annotations.get(frame, showAllInterpolationTracks, filters ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If |
||
|
||
states.forEach(async (state: any) => { | ||
await dispatch(removeObjectAsync(sessionInstance,state,force)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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[]; | ||
|
@@ -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; | ||
} | ||
|
@@ -42,6 +46,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element { | |
isReviewer, | ||
jobInstance, | ||
onClickMenu, | ||
removeRange, | ||
setForceExitAnnotationFlag, | ||
saveAnnotations, | ||
} = props; | ||
|
@@ -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({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest following: pseudocode
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here you do not need remove-range-confirm.tsx Component. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I'll try this approach. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
|
@@ -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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
// | ||||||
// 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> | ||||||
); | ||||||
} |
There was a problem hiding this comment.
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).