Skip to content

Commit

Permalink
Added hotkeys to change labels (#3070)
Browse files Browse the repository at this point in the history
* temp commit

* Added ability to change default label and object label by using Ctrl+{number}

* Removed extra changes

* Minor refactoring

* Added ability to change assigned keys

* Redesigned popover

* Added changelog record & updated version

* Added memoization

* Some minor changes

* Applied comments

Co-authored-by: Dmitry Kalinin <dmitry.kalinin@intel.com>
  • Loading branch information
Boris Sekachev and ActiveChooN authored Apr 7, 2021
1 parent d2a1d12 commit 7524202
Show file tree
Hide file tree
Showing 19 changed files with 341 additions and 119 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.4.0] - Unreleased

### Added

- Documentation on mask annotation (<https://github.com/openvinotoolkit/cvat/pull/3044>)
- Hotkeys to switch a label of existing object or to change default label (for objects created with N) (<https://github.com/openvinotoolkit/cvat/pull/3070>)

### Changed

-

### Deprecated

-

### Removed

-

### Fixed

- Export of instance masks with holes (<https://github.com/openvinotoolkit/cvat/pull/3044>)

### Security
-

-

## [1.3.0] - 3/31/2021

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.18.1",
"version": "1.19.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
36 changes: 8 additions & 28 deletions cvat-ui/src/actions/annotation-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1130,36 +1130,16 @@ export function saveAnnotationsAsync(sessionInstance: any, afterSave?: () => voi
}

// used to reproduce the latest drawing (in case of tags just creating) by using N
export function rememberObject(
objectType: ObjectType,
labelID: number,
shapeType?: ShapeType,
points?: number,
rectDrawingMethod?: RectDrawingMethod,
): AnyAction {
let activeControl = ActiveControl.CURSOR;
if (shapeType === ShapeType.RECTANGLE) {
activeControl = ActiveControl.DRAW_RECTANGLE;
} else if (shapeType === ShapeType.POLYGON) {
activeControl = ActiveControl.DRAW_POLYGON;
} else if (shapeType === ShapeType.POLYLINE) {
activeControl = ActiveControl.DRAW_POLYLINE;
} else if (shapeType === ShapeType.POINTS) {
activeControl = ActiveControl.DRAW_POINTS;
} else if (shapeType === ShapeType.CUBOID) {
activeControl = ActiveControl.DRAW_CUBOID;
}

export function rememberObject(createParams: {
activeObjectType?: ObjectType;
activeLabelID?: number;
activeShapeType?: ShapeType;
activeNumOfPoints?: number;
activeRectDrawingMethod?: RectDrawingMethod;
}): AnyAction {
return {
type: AnnotationActionTypes.REMEMBER_CREATED_OBJECT,
payload: {
shapeType,
labelID,
objectType,
points,
activeControl,
rectDrawingMethod,
},
payload: createParams,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { CombinedState } from 'reducers/interfaces';
import {
LeftOutlined, RightOutlined, EyeInvisibleFilled, EyeOutlined,
} from '@ant-design/icons';
Expand All @@ -14,6 +13,7 @@ import { Row, Col } from 'antd/lib/grid';
import { changeFrameAsync } from 'actions/annotation-actions';
import { reviewActions } from 'actions/review-actions';
import CVATTooltip from 'components/common/cvat-tooltip';
import { CombinedState } from 'reducers/interfaces';

export default function LabelsListComponent(): JSX.Element {
const dispatch = useDispatch();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand All @@ -10,31 +10,45 @@ import {
LockFilled, UnlockOutlined, EyeInvisibleFilled, EyeOutlined,
} from '@ant-design/icons';

import CVATTooltip from 'components/common/cvat-tooltip';
import LabelKeySelectorPopover from './label-key-selector-popover';

interface Props {
labelName: string;
labelColor: string;
labelID: number;
visible: boolean;
statesHidden: boolean;
statesLocked: boolean;
keyToLabelMapping: Record<string, number>;
hideStates(): void;
showStates(): void;
lockStates(): void;
unlockStates(): void;
updateLabelShortcutKey(updatedKey: string, labelID: number): void;
}

function LabelItemComponent(props: Props): JSX.Element {
const {
labelName,
labelColor,
labelID,
keyToLabelMapping,
visible,
statesHidden,
statesLocked,
hideStates,
showStates,
lockStates,
unlockStates,
updateLabelShortcutKey,
} = props;

// create reversed mapping just to receive key easily
const labelToKeyMapping: Record<string, string> = Object.fromEntries(
Object.entries(keyToLabelMapping).map(([key, _labelID]) => [_labelID, key]),
);
const labelShortcutKey = labelToKeyMapping[labelID] || '?';
const classes = {
lock: {
enabled: { className: 'cvat-label-item-button-lock cvat-label-item-button-lock-enabled' },
Expand All @@ -48,22 +62,37 @@ function LabelItemComponent(props: Props): JSX.Element {

return (
<Row
align='middle'
align='stretch'
justify='space-around'
className='cvat-objects-sidebar-label-item'
style={{ display: visible ? 'flex' : 'none' }}
className={[
'cvat-objects-sidebar-label-item',
visible ? '' : 'cvat-objects-sidebar-label-item-disabled',
].join(' ')}
>
<Col span={4}>
<Button style={{ background: labelColor }} className='cvat-label-item-color-button'>
<Col span={2}>
<div style={{ background: labelColor }} className='cvat-label-item-color'>
{' '}
</Button>
</div>
</Col>
<Col span={14}>
<Text strong className='cvat-text'>
{labelName}
</Text>
<Col span={12}>
<CVATTooltip title={labelName}>
<Text strong className='cvat-text'>
{labelName}
</Text>
</CVATTooltip>
</Col>
<Col span={3}>
<LabelKeySelectorPopover
keyToLabelMapping={keyToLabelMapping}
labelID={labelID}
updateLabelShortcutKey={updateLabelShortcutKey}
>
<Button className='cvat-label-item-setup-shortcut-button' size='small' ghost type='dashed'>
{labelShortcutKey}
</Button>
</LabelKeySelectorPopover>
</Col>
<Col span={2} offset={1}>
{statesLocked ? (
<LockFilled {...classes.lock.enabled} onClick={unlockStates} />
) : (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

import React from 'react';
import { useSelector } from 'react-redux';
import Popover from 'antd/lib/popover';
import Button from 'antd/lib/button';
import { Row, Col } from 'antd/lib/grid';
import Text from 'antd/lib/typography/Text';

import { CombinedState } from 'reducers/interfaces';
import CVATTooltip from 'components/common/cvat-tooltip';

interface LabelKeySelectorPopoverProps {
updateLabelShortcutKey(updatedKey: string, labelID: number): void;
keyToLabelMapping: Record<string, number>;
labelID: number;
children: JSX.Element;
}

interface LabelKeySelectorPopoverContentProps {
updateLabelShortcutKey(updatedKey: string, labelID: number): void;
labelID: number;
keyToLabelMapping: Record<string, number>;
}

function PopoverContent(props: LabelKeySelectorPopoverContentProps): JSX.Element {
const { keyToLabelMapping, labelID, updateLabelShortcutKey } = props;
const labels = useSelector((state: CombinedState) => state.annotation.job.labels);

return (
<div className='cvat-label-item-setup-shortcut-popover'>
{[['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9'], ['0']].map((arr, i_) => (
<Row justify='space-around' gutter={[16, 16]} key={i_}>
{arr.map((i) => {
const previousLabelID = keyToLabelMapping[i];
const labelName = Number.isInteger(previousLabelID) ?
labels.filter((label: any): boolean => label.id === previousLabelID)[0]?.name ||
'undefined' :
'None';

return (
<Col key={i} span={8}>
<CVATTooltip title={labelName}>
<Button onClick={() => updateLabelShortcutKey(i, labelID)}>
<Text>{`${i}:`}</Text>
<Text type='secondary'>{labelName}</Text>
</Button>
</CVATTooltip>
</Col>
);
})}
</Row>
))}
</div>
);
}

const MemoizedContent = React.memo(PopoverContent);

function LabelKeySelectorPopover(props: LabelKeySelectorPopoverProps): JSX.Element {
const {
children, labelID, updateLabelShortcutKey, keyToLabelMapping,
} = props;

return (
<Popover
destroyTooltipOnHide={{ keepParent: false }}
trigger='click'
content={(
<MemoizedContent
keyToLabelMapping={keyToLabelMapping}
labelID={labelID}
updateLabelShortcutKey={updateLabelShortcutKey}
/>
)}
placement='left'
>
{children}
</Popover>
);
}

export default React.memo(LabelKeySelectorPopover);
Loading

0 comments on commit 7524202

Please sign in to comment.