From 38acc99f277e54b9b0d28500f76576dc9fceb71a Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 10 Sep 2020 07:27:47 +0300 Subject: [PATCH] Added automatic annotation progress to the task view (#2148) * Added automatic annotation progress to the task view * Updated changelog & version Co-authored-by: Nikita Manovich --- CHANGELOG.md | 1 + cvat-ui/src/components/task-page/details.tsx | 47 +++++++------- .../automatic-annotation-progress.tsx | 63 +++++++++++++++++++ .../src/components/tasks-page/task-item.tsx | 48 ++------------ cvat-ui/src/containers/task-page/details.tsx | 22 ++++--- 5 files changed, 106 insertions(+), 75 deletions(-) create mode 100644 cvat-ui/src/components/tasks-page/automatic-annotation-progress.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f0e8912..a751b7c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ability to work with data on the fly (https://github.com/opencv/cvat/pull/2007) - Annotation in process outline color wheel () - On the fly annotation using DL detectors () +- Displaying automatic annotation progress on a task view () - Automatic tracking of bounding boxes using serverless functions () - [Datumaro] CLI command for dataset equality comparison () - [Datumaro] Merging of datasets with different labels () diff --git a/cvat-ui/src/components/task-page/details.tsx b/cvat-ui/src/components/task-page/details.tsx index 84c9aa1f..aa3813d0 100644 --- a/cvat-ui/src/components/task-page/details.tsx +++ b/cvat-ui/src/components/task-page/details.tsx @@ -16,6 +16,8 @@ import moment from 'moment'; import getCore from 'cvat-core-wrapper'; import patterns from 'utils/validation-patterns'; import { getReposData, syncRepos } from 'utils/git-utils'; +import { ActiveInference } from 'reducers/interfaces'; +import AutomaticAnnotationProgress from 'components/tasks-page/automatic-annotation-progress'; import UserSelector from './user-selector'; import LabelsEditorComponent from '../labels-editor/labels-editor'; @@ -26,6 +28,8 @@ interface Props { taskInstance: any; installedGit: boolean; // change to git repos url registeredUsers: any[]; + activeInference: ActiveInference | null; + cancelAutoAnnotation(): void; onTaskUpdate: (taskInstance: any) => void; } @@ -125,10 +129,7 @@ export default class DetailsComponent extends React.PureComponent private renderTaskName(): JSX.Element { const { name } = this.state; - const { - taskInstance, - onTaskUpdate, - } = this.props; + const { taskInstance, onTaskUpdate } = this.props; return ( private renderParameters(): JSX.Element { const { taskInstance } = this.props; - const { overlap } = taskInstance; - const { segmentSize } = taskInstance; - const { imageQuality } = taskInstance; + const { overlap, segmentSize, imageQuality } = taskInstance; const zOrder = taskInstance.zOrder.toString(); return ( @@ -197,11 +196,7 @@ export default class DetailsComponent extends React.PureComponent<Props, State> } private renderUsers(): JSX.Element { - const { - taskInstance, - registeredUsers, - onTaskUpdate, - } = this.props; + const { taskInstance, registeredUsers, onTaskUpdate } = this.props; const owner = taskInstance.owner ? taskInstance.owner.username : null; const assignee = taskInstance.assignee ? taskInstance.assignee.username : null; const created = moment(taskInstance.createdDate).format('MMMM Do YYYY'); @@ -246,10 +241,7 @@ export default class DetailsComponent extends React.PureComponent<Props, State> private renderDatasetRepository(): JSX.Element | boolean { const { taskInstance } = this.props; - const { - repository, - repositoryStatus, - } = this.state; + const { repository, repositoryStatus } = this.state; return ( !!repository @@ -321,10 +313,7 @@ export default class DetailsComponent extends React.PureComponent<Props, State> } private renderBugTracker(): JSX.Element { - const { - taskInstance, - onTaskUpdate, - } = this.props; + const { taskInstance, onTaskUpdate } = this.props; const { bugTracker, bugTrackerEditing } = this.state; let shown = false; @@ -400,10 +389,7 @@ export default class DetailsComponent extends React.PureComponent<Props, State> } private renderLabelsEditor(): JSX.Element { - const { - taskInstance, - onTaskUpdate, - } = this.props; + const { taskInstance, onTaskUpdate } = this.props; return ( <Row> @@ -424,6 +410,7 @@ export default class DetailsComponent extends React.PureComponent<Props, State> } public render(): JSX.Element { + const { activeInference, cancelAutoAnnotation } = this.props; return ( <div className='cvat-task-details'> <Row type='flex' justify='start' align='middle'> @@ -446,7 +433,17 @@ export default class DetailsComponent extends React.PureComponent<Props, State> </Col> <Col md={16} lg={17} xl={17} xxl={18}> { this.renderUsers() } - { this.renderBugTracker() } + <Row type='flex' justify='space-between' align='middle'> + <Col span={12}> + { this.renderBugTracker() } + </Col> + <Col span={10}> + <AutomaticAnnotationProgress + activeInference={activeInference} + cancelAutoAnnotation={cancelAutoAnnotation} + /> + </Col> + </Row> { this.renderDatasetRepository() } { this.renderLabelsEditor() } </Col> diff --git a/cvat-ui/src/components/tasks-page/automatic-annotation-progress.tsx b/cvat-ui/src/components/tasks-page/automatic-annotation-progress.tsx new file mode 100644 index 00000000..1ffde381 --- /dev/null +++ b/cvat-ui/src/components/tasks-page/automatic-annotation-progress.tsx @@ -0,0 +1,63 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import React from 'react'; +import { Row, Col } from 'antd/lib/grid'; +import Text from 'antd/lib/typography/Text'; +import Progress from 'antd/lib/progress'; +import Tooltip from 'antd/lib/tooltip'; +import Icon from 'antd/lib/icon'; +import Modal from 'antd/lib/modal'; +import { ActiveInference } from 'reducers/interfaces'; + +interface Props { + activeInference: ActiveInference | null; + cancelAutoAnnotation(): void; +} + +export default function AutomaticAnnotationProgress(props: Props): JSX.Element | null { + const { activeInference, cancelAutoAnnotation } = props; + if (!activeInference) return null; + + return ( + <> + <Row> + <Col> + <Text strong>Automatic annotation</Text> + </Col> + </Row> + <Row type='flex' justify='space-between'> + <Col span={22}> + <Progress + percent={Math.floor(activeInference.progress)} + strokeColor={{ + from: '#108ee9', + to: '#87d068', + }} + showInfo={false} + strokeWidth={5} + size='small' + /> + </Col> + <Col span={1} className='close-auto-annotation-icon'> + <Tooltip title='Cancel automatic annotation' mouseLeaveDelay={0}> + <Icon + type='close' + onClick={() => { + Modal.confirm({ + title: 'You are going to cancel automatic annotation?', + content: 'Reached progress will be lost. Continue?', + okType: 'danger', + onOk() { + cancelAutoAnnotation(); + }, + }); + }} + /> + </Tooltip> + </Col> + </Row> + </> + ); +} diff --git a/cvat-ui/src/components/tasks-page/task-item.tsx b/cvat-ui/src/components/tasks-page/task-item.tsx index 66c9a0d3..d5166ae3 100644 --- a/cvat-ui/src/components/tasks-page/task-item.tsx +++ b/cvat-ui/src/components/tasks-page/task-item.tsx @@ -10,14 +10,13 @@ import { Row, Col } from 'antd/lib/grid'; import Button from 'antd/lib/button'; import Icon from 'antd/lib/icon'; import Dropdown from 'antd/lib/dropdown'; -import Tooltip from 'antd/lib/tooltip'; -import Modal from 'antd/lib/modal'; import Progress from 'antd/lib/progress'; import moment from 'moment'; import ActionsMenuContainer from 'containers/actions-menu/actions-menu'; import { ActiveInference } from 'reducers/interfaces'; import { MenuIcon } from 'icons'; +import AutomaticAnnotationProgress from './automatic-annotation-progress'; export interface TaskItemProps { taskInstance: any; @@ -123,47 +122,10 @@ class TaskItemComponent extends React.PureComponent<TaskItemProps & RouteCompone /> </Col> </Row> - { activeInference - && ( - <> - <Row> - <Col> - <Text strong>Automatic annotation</Text> - </Col> - </Row> - <Row type='flex' justify='space-between'> - <Col span={22}> - <Progress - percent={Math.floor(activeInference.progress)} - strokeColor={{ - from: '#108ee9', - to: '#87d068', - }} - showInfo={false} - strokeWidth={5} - size='small' - /> - </Col> - <Col span={1} className='close-auto-annotation-icon'> - <Tooltip title='Cancel automatic annotation' mouseLeaveDelay={0}> - <Icon - type='close' - onClick={() => { - Modal.confirm({ - title: 'You are going to cancel automatic annotation?', - content: 'Reached progress will be lost. Continue?', - okType: 'danger', - onOk() { - cancelAutoAnnotation(); - }, - }); - }} - /> - </Tooltip> - </Col> - </Row> - </> - )} + <AutomaticAnnotationProgress + activeInference={activeInference} + cancelAutoAnnotation={cancelAutoAnnotation} + /> </Col> ); } diff --git a/cvat-ui/src/containers/task-page/details.tsx b/cvat-ui/src/containers/task-page/details.tsx index 2aa0ef91..d806d962 100644 --- a/cvat-ui/src/containers/task-page/details.tsx +++ b/cvat-ui/src/containers/task-page/details.tsx @@ -7,10 +7,8 @@ import { connect } from 'react-redux'; import DetailsComponent from 'components/task-page/details'; import { updateTaskAsync } from 'actions/tasks-actions'; -import { - Task, - CombinedState, -} from 'reducers/interfaces'; +import { cancelInferenceAsync } from 'actions/models-actions'; +import { Task, CombinedState, ActiveInference } from 'reducers/interfaces'; interface OwnProps { task: Task; @@ -18,26 +16,32 @@ interface OwnProps { interface StateToProps { registeredUsers: any[]; + activeInference: ActiveInference | null; installedGit: boolean; } interface DispatchToProps { + cancelAutoAnnotation(): void; onTaskUpdate: (taskInstance: any) => void; } -function mapStateToProps(state: CombinedState): StateToProps { +function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { const { list } = state.plugins; return { registeredUsers: state.users.users, installedGit: list.GIT_INTEGRATION, + activeInference: state.models.inferences[own.task.instance.id] || null, }; } -function mapDispatchToProps(dispatch: any): DispatchToProps { +function mapDispatchToProps(dispatch: any, own: OwnProps): DispatchToProps { return { onTaskUpdate: (taskInstance: any): void => dispatch(updateTaskAsync(taskInstance)), + cancelAutoAnnotation(): void { + dispatch(cancelInferenceAsync(own.task.instance.id)); + }, }; } @@ -46,7 +50,9 @@ function TaskPageContainer(props: StateToProps & DispatchToProps & OwnProps): JS const { task, installedGit, + activeInference, registeredUsers, + cancelAutoAnnotation, onTaskUpdate, } = props; @@ -55,8 +61,10 @@ function TaskPageContainer(props: StateToProps & DispatchToProps & OwnProps): JS previewImage={task.preview} taskInstance={task.instance} installedGit={installedGit} - onTaskUpdate={onTaskUpdate} registeredUsers={registeredUsers} + activeInference={activeInference} + onTaskUpdate={onTaskUpdate} + cancelAutoAnnotation={cancelAutoAnnotation} /> ); }