Skip to content

Commit

Permalink
CVAT UI: tag annotation workspace (#1570)
Browse files Browse the repository at this point in the history
  • Loading branch information
ActiveChooN authored Jun 29, 2020
1 parent db29291 commit 27dc52a
Show file tree
Hide file tree
Showing 11 changed files with 522 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Highlights for the first point of a polygon/polyline and direction (<https://github.com/opencv/cvat/pull/1571>)
- Ability to change orientation for poylgons/polylines in context menu (<https://github.com/opencv/cvat/pull/1571>)
- Ability to set the first point for polygons in points context menu (<https://github.com/opencv/cvat/pull/1571>)
- Added new tag annotation workspace (<https://github.com/opencv/cvat/pull/1570>)

### Changed
- Removed information about e-mail from the basic user information (<https://github.com/opencv/cvat/pull/1627>)
Expand Down
2 changes: 1 addition & 1 deletion 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.4.2",
"version": "1.5.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
11 changes: 9 additions & 2 deletions cvat-ui/src/components/annotation-page/annotation-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import AnnotationTopBarContainer from 'containers/annotation-page/top-bar/top-ba
import StatisticsModalContainer from 'containers/annotation-page/top-bar/statistics-modal';
import StandardWorkspaceComponent from './standard-workspace/standard-workspace';
import AttributeAnnotationWorkspace from './attribute-annotation-workspace/attribute-annotation-workspace';
import TagAnnotationWorkspace from './tag-annotation-workspace/tag-annotation-workspace';

interface Props {
job: any | null | undefined;
Expand Down Expand Up @@ -70,15 +71,21 @@ export default function AnnotationPageComponent(props: Props): JSX.Element {
<Layout.Header className='cvat-annotation-header'>
<AnnotationTopBarContainer />
</Layout.Header>
{ workspace === Workspace.STANDARD ? (
{ workspace === Workspace.STANDARD && (
<Layout.Content>
<StandardWorkspaceComponent />
</Layout.Content>
) : (
)}
{ workspace === Workspace.ATTRIBUTE_ANNOTATION && (
<Layout.Content>
<AttributeAnnotationWorkspace />
</Layout.Content>
)}
{ workspace === Workspace.TAG_ANNOTATION && (
<Layout.Content>
<TagAnnotationWorkspace />
</Layout.Content>
)}
<StatisticsModalContainer />
</Layout>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
}

if (prevProps.frame !== frameData.number
&& resetZoom
&& workspace !== Workspace.ATTRIBUTE_ANNOTATION
&& ((resetZoom && workspace !== Workspace.ATTRIBUTE_ANNOTATION) ||
workspace === Workspace.TAG_ANNOTATION)
) {
canvasInstance.html().addEventListener('canvas.setup', () => {
canvasInstance.fit();
Expand Down Expand Up @@ -462,7 +462,7 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
onActivateObject,
} = this.props;

if (workspace === Workspace.ATTRIBUTE_ANNOTATION) {
if (workspace !== Workspace.STANDARD) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT

@import 'base.scss';

.cvat-tag-annotation-workspace.ant-layout {
height: 100%;
}

.cvat-tag-annotation-sidebar {
background: $background-color-2;
padding: 5px;

> div > .ant-row-flex > .ant-col > .ant-tag {
margin: 4px;
}
}

.cvat-tag-annotation-sidebar-label-select {
padding-top: 40px;
padding-bottom: 15px;

> .ant-col > .ant-select {
padding-left: 5px;
width: 230px;
}
}

.cvat-tag-annotation-sidebar-shortcut-help {
padding-top: 15px;
text-align: center;
}

.cvat-tag-annotation-sidebar-buttons,
.cvat-tag-anntation-sidebar-checkbox-skip-frame {
padding-bottom: 15px;
}

.cvat-tag-annotation-label-selects {
padding-top: 10px;

.ant-select {
width: 230px;
padding: 5px 10px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT

import React, { useState, useEffect, Fragment } from 'react';
import { useSelector } from 'react-redux';
import { GlobalHotKeys, KeyMap } from 'react-hotkeys';
import { Row, Col } from 'antd/lib/grid';
import Text from 'antd/lib/typography/Text';
import Select from 'antd/lib/select';

import { CombinedState } from 'reducers/interfaces';
import { shift } from 'utils/math';

interface ShortcutLabelMap {
[index: number]: any;
}

type Props = {
onAddTag(labelID: number): void;
};

const defaultShortcutLabelMap = {
1: '',
2: '',
3: '',
4: '',
5: '',
6: '',
7: '',
8: '',
9: '',
0: '',
} as ShortcutLabelMap;

const ShortcutsSelect = (props: Props): JSX.Element => {
const { onAddTag } = props;
const { labels } = useSelector((state: CombinedState) => state.annotation.job);
const [shortcutLabelMap, setShortcutLabelMap] = useState(defaultShortcutLabelMap);

const keyMap: KeyMap = {};
const handlers: {
[key: string]: (keyEvent?: KeyboardEvent) => void;
} = {};

useEffect(() => {
const newShortcutLabelMap = { ...shortcutLabelMap };
(labels as any[]).slice(0, 10).forEach((label, index) => {
newShortcutLabelMap[(index + 1) % 10] = label.id;
});
setShortcutLabelMap(newShortcutLabelMap);
}, []);


Object.keys(shortcutLabelMap).map((id) => Number.parseInt(id, 10))
.filter((id) => shortcutLabelMap[id]).forEach((id: number): void => {
const [label] = labels.filter((_label) => _label.id === shortcutLabelMap[id]);
const key = `SETUP_${id}_TAG`;
keyMap[key] = {
name: `Setup ${label.name} tag`,
description: `Setup tag with "${label.name}" label`,
sequence: `${id}`,
action: 'keydown',
};

handlers[key] = (event: KeyboardEvent | undefined) => {
if (event) {
event.preventDefault();
}

onAddTag(label.id);
};
});

const onChangeShortcutLabel = (value: string, id: number): void => {
const newShortcutLabelMap = { ...shortcutLabelMap };
newShortcutLabelMap[id] = value ? Number.parseInt(value, 10) : '';
setShortcutLabelMap(newShortcutLabelMap);
};

return (
<div className='cvat-tag-annotation-label-selects'>
<GlobalHotKeys keyMap={keyMap as KeyMap} handlers={handlers} allowChanges />
<Row>
<Col>
<Text strong>Shortcuts for labels:</Text>
</Col>
</Row>
{shift(Object.keys(shortcutLabelMap), 1).slice(0, Math.min(labels.length, 10)).map((id) => (
<Row key={id}>
<Col>
<Text strong>{`Key ${id}:`}</Text>
<Select
value={`${shortcutLabelMap[Number.parseInt(id, 10)]}`}
onChange={(value: string) => {
onChangeShortcutLabel(value, Number.parseInt(id, 10));
}}
size='default'
style={{ width: 200 }}
className='cvat-tag-annotation-label-select'
>
<Select.Option value=''>
<Text type='secondary'>
None
</Text>
</Select.Option>
{
(labels as any[]).map((label: any) => (
<Select.Option
key={label.id}
value={`${label.id}`}
>
{label.name}
</Select.Option>
))
}
</Select>
</Col>
</Row>
))}
</div>
);
};

export default ShortcutsSelect;
Loading

0 comments on commit 27dc52a

Please sign in to comment.