未验证 提交 72655536 编写于 作者: B Boris Sekachev 提交者: GitHub

Supported type for all labels (#5273)

* Supported type for all labels

* Multiple fixes in UI for new type scheme

* Fixed on 3d annotation view

* Fixed tests

* Updated version && changelog

* Fixed test

* Added type

* Fixed typos

* Fixed minor comments
上级 6acab035
......@@ -70,6 +70,7 @@ non-ascii paths while adding files from "Connected file share" (issue #4428)
- Create manifest with cvat/server docker container command (<https://github.com/opencv/cvat/pull/5172>)
- Cannot assign a resource to a user who has an organization (<https://github.com/opencv/cvat/pull/5218>)
- Logs and annotations are not saved when logout from a job page (<https://github.com/opencv/cvat/pull/5266>)
- Added "type" field for all the labels, allows to reduce number of controls on annotation view (<https://github.com/opencv/cvat/pull/5273>)
- Occluded not applied on canvas instantly for a skeleton elements (<https://github.com/opencv/cvat/pull/5259>)
- Oriented bounding boxes broken with COCO format ss(<https://github.com/opencv/cvat/pull/5219>)
- Fixed upload resumption in production environments
......
......@@ -481,3 +481,16 @@ export enum WebhookSourceType {
export enum WebhookContentType {
JSON = 'application/json',
}
export enum LabelType {
ANY = 'any',
RECTANGLE = 'rectangle',
POLYGON = 'polygon',
POLYLINE = 'polyline',
POINTS = 'points',
ELLIPSE = 'ellipse',
CUBOID = 'cuboid',
SKELETON = 'skeleton',
MASK = 'mask',
TAG = 'tag',
}
{
"name": "cvat-ui",
"version": "1.43.2",
"version": "1.44.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
......
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
import { SelectValue } from 'antd/lib/select';
import Layout, { SiderProps } from 'antd/lib/layout';
import Text from 'antd/lib/typography/Text';
import { filterApplicableLabels } from 'utils/filter-applicable-labels';
import { Label } from 'cvat-core-wrapper';
import { Canvas } from 'cvat-canvas-wrapper';
import { Canvas3d } from 'cvat-canvas3d-wrapper';
import { LogType } from 'cvat-logger';
......@@ -147,6 +149,7 @@ function AttributeAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.
activatedStateID === null || activatedIndex === -1 ? null : filteredStates[activatedIndex];
const activeAttribute = activeObjectState ? labelAttrMap[activeObjectState.label.id] : null;
const applicableLabels = activeObjectState ? filterApplicableLabels(activeObjectState, labels) : [];
if (canvasIsReady) {
if (activeObjectState) {
......@@ -308,12 +311,10 @@ function AttributeAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.
nextObject={nextObject}
/>
<ObjectBasicsEditor
currentLabel={activeObjectState.label.name}
labels={labels}
changeLabel={(value: SelectValue): void => {
const labelName = value as string;
const [newLabel] = labels.filter((_label): boolean => _label.name === labelName);
activeObjectState.label = newLabel;
currentLabel={activeObjectState.label.id}
labels={applicableLabels}
changeLabel={(value: Label): void => {
activeObjectState.label = value;
updateAnnotations([activeObjectState]);
}}
/>
......
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
import React from 'react';
import Select, { SelectValue } from 'antd/lib/select';
import { Label } from 'cvat-core-wrapper';
import LabelSelector from 'components/label-selector/label-selector';
interface Props {
currentLabel: string;
labels: any[];
changeLabel(value: SelectValue): void;
currentLabel: number;
labels: Label[];
changeLabel(value: Label): void;
}
function ObjectBasicsEditor(props: Props): JSX.Element {
......@@ -16,15 +18,12 @@ function ObjectBasicsEditor(props: Props): JSX.Element {
return (
<div className='cvat-attribute-annotation-sidebar-basics-editor'>
<Select value={currentLabel} onChange={changeLabel} style={{ width: '50%' }}>
{labels.map(
(label: any): JSX.Element => (
<Select.Option value={label.name} key={label.name}>
{label.name}
</Select.Option>
),
)}
</Select>
<LabelSelector
style={{ width: '50%' }}
labels={labels}
value={currentLabel}
onChange={changeLabel}
/>
</div>
);
}
......
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
......@@ -10,7 +11,7 @@
.attribute-annotation-sidebar:not(.ant-layout-sider-collapsed) {
background: $background-color-2;
padding: 5px;
padding: $grid-unit-size;
> .ant-layout-sider-children {
display: flex;
......@@ -24,7 +25,7 @@
align-items: center;
justify-content: space-between;
font-size: 18px;
margin-top: 10px;
margin-top: $grid-unit-size;
> span {
max-width: 60%;
......@@ -40,13 +41,13 @@
.cvat-attribute-annotation-sidebar-basics-editor {
display: flex;
align-items: center;
justify-content: space-between;
justify-content: center;
font-size: 18px;
margin: 10px 0;
margin: $grid-unit-size 0;
}
.attribute-annotations-sidebar-not-found-wrapper {
margin-top: 20px;
margin-top: $grid-unit-size * 3;
text-align: center;
flex-grow: 10;
}
......@@ -56,7 +57,7 @@
}
.attribute-annotation-sidebar-attr-list-wrapper {
margin: 10px 0 10px 10px;
margin: $grid-unit-size 0 $grid-unit-size $grid-unit-size;
}
.attribute-annotation-sidebar-attr-elem-wrapper {
......@@ -75,5 +76,5 @@
}
.cvat-sidebar-collapse-button-spacer {
height: 32px;
height: $grid-unit-size * 4;
}
......@@ -12,7 +12,8 @@ import Icon, { VerticalAlignBottomOutlined } from '@ant-design/icons';
import InputNumber from 'antd/lib/input-number';
import Select from 'antd/lib/select';
import { getCore } from 'cvat-core-wrapper';
import { filterApplicableForType } from 'utils/filter-applicable-labels';
import { getCore, Label, LabelType } from 'cvat-core-wrapper';
import { Canvas, CanvasMode } from 'cvat-canvas-wrapper';
import {
BrushIcon, EraserIcon, PolygonMinusIcon, PolygonPlusIcon,
......@@ -44,6 +45,7 @@ function BrushTools(): React.ReactPortal | null {
const [brushForm, setBrushForm] = useState<'circle' | 'square'>('circle');
const [[top, left], setTopLeft] = useState([0, 0]);
const [brushSize, setBrushSize] = useState(10);
const [applicableLabels, setApplicableLabels] = useState<Label[]>([]);
const [removeUnderlyingPixels, setRemoveUnderlyingPixels] = useState(false);
const dragBar = useDraggable(
......@@ -99,6 +101,10 @@ function BrushTools(): React.ReactPortal | null {
}
}, [currentTool, brushSize, brushForm, visible, defaultLabelID, editableState]);
useEffect(() => {
setApplicableLabels(filterApplicableForType(LabelType.MASK, labels));
}, [labels]);
useEffect(() => {
const canvasContainer = window.document.getElementsByClassName('cvat-canvas-container')[0];
if (canvasContainer) {
......@@ -254,9 +260,9 @@ function BrushTools(): React.ReactPortal | null {
icon={<VerticalAlignBottomOutlined />}
onClick={() => setRemoveUnderlyingPixels(!removeUnderlyingPixels)}
/>
{ !editableState && (
{ !editableState && !!applicableLabels.length && (
<LabelSelector
labels={labels}
labels={applicableLabels}
value={defaultLabelID}
onChange={({ id: labelID }: { id: number }) => {
if (Number.isInteger(labelID)) {
......
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
......@@ -6,7 +7,8 @@ import React, { useCallback, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import message from 'antd/lib/message';
import { CombinedState } from 'reducers';
import { LabelType } from 'cvat-core-wrapper';
import { CombinedState, ObjectType } from 'reducers';
import { rememberObject, updateAnnotationsAsync } from 'actions/annotation-actions';
import LabelItemContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/label-item';
import GlobalHotKeys from 'utils/mousetrap-react';
......@@ -15,6 +17,8 @@ function LabelsListComponent(): JSX.Element {
const dispatch = useDispatch();
const labels = useSelector((state: CombinedState) => state.annotation.job.labels);
const activatedStateID = useSelector((state: CombinedState) => state.annotation.annotations.activatedStateID);
const activeShapeType = useSelector((state: CombinedState) => state.annotation.drawing.activeShapeType);
const activeObjectType = useSelector((state: CombinedState) => state.annotation.drawing.activeObjectType);
const states = useSelector((state: CombinedState) => state.annotation.annotations.states);
const keyMap = useSelector((state: CombinedState) => state.shortcuts.keyMap);
......@@ -72,14 +76,22 @@ function LabelsListComponent(): JSX.Element {
if (Number.isInteger(labelID) && label) {
if (Number.isInteger(activatedStateID)) {
const activatedState = states.filter((state: any) => state.clientID === activatedStateID)[0];
if (activatedState) {
const bothAreTags = activatedState.objectType === ObjectType.TAG && label.type === ObjectType.TAG;
const labelIsApplicable = label.type === LabelType.ANY ||
activatedState.shapeType === label.type || bothAreTags;
if (activatedState && labelIsApplicable) {
activatedState.label = label;
dispatch(updateAnnotationsAsync([activatedState]));
}
} else {
dispatch(rememberObject({ activeLabelID: labelID }));
message.destroy();
message.success(`Default label was changed to "${label.name}"`);
const bothAreTags = activeObjectType === ObjectType.TAG && label.type === ObjectType.TAG;
const labelIsApplicable = label.type === LabelType.ANY ||
activeShapeType === label.type || bothAreTags;
if (labelIsApplicable) {
dispatch(rememberObject({ activeLabelID: labelID }));
message.destroy();
message.success(`Default label has been changed to "${label.name}"`);
}
}
}
},
......
// Copyright (C) 2021-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
import React from 'react';
import Layout from 'antd/lib/layout';
import { ActiveControl } from 'reducers';
import { Label, LabelType } from 'cvat-core-wrapper';
import { Canvas3d as Canvas } from 'cvat-canvas3d-wrapper';
import MoveControl, {
Props as MoveControlProps,
......@@ -20,13 +22,14 @@ import GroupControl, {
} from 'components/annotation-page/standard-workspace/controls-side-bar/group-control';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import ControlVisibilityObserver from 'components/annotation-page/standard-workspace/controls-side-bar/control-visibility-observer';
import { filterApplicableForType } from 'utils/filter-applicable-labels';
interface Props {
keyMap: KeyMap;
canvasInstance: Canvas;
activeControl: ActiveControl;
normalizedKeyMap: Record<string, string>;
labels: any[];
labels: Label[];
jobInstance: any;
repeatDrawShape(): void;
redrawShape(): void;
......@@ -55,6 +58,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
jobInstance,
} = props;
const applicableLabels = filterApplicableForType(LabelType.CUBOID, labels);
const preventDefault = (event: KeyboardEvent | undefined): void => {
if (event) {
event.preventDefault();
......@@ -74,7 +78,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
},
};
if (labels.length) {
if (applicableLabels.length) {
handlers = {
...handlers,
PASTE_SHAPE: (event: KeyboardEvent | undefined) => {
......@@ -138,7 +142,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
<ObservedDrawCuboidControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_CUBOID}
disabled={!labels.length}
disabled={!applicableLabels.length}
/>
<ObservedGroupControl
switchGroupShortcut={normalizedKeyMap.SWITCH_GROUP_MODE}
......@@ -146,7 +150,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
canvasInstance={canvasInstance}
activeControl={activeControl}
groupObjects={groupObjects}
disabled={!labels.length}
disabled={!applicableLabels.length}
jobInstance={jobInstance}
/>
</Layout.Sider>
......
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
import React, { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { Row, Col } from 'antd/lib/grid';
import Text from 'antd/lib/typography/Text';
import Select from 'antd/lib/select';
import { CombinedState, DimensionType } from 'reducers';
import { DimensionType } from 'reducers';
import { Label } from 'cvat-core-wrapper';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import { shift } from 'utils/math';
......@@ -18,6 +19,7 @@ interface ShortcutLabelMap {
type Props = {
onShortcutPress(event: KeyboardEvent | undefined, labelID: number): void;
labels: Label[];
};
const defaultShortcutLabelMap = {
......@@ -34,8 +36,7 @@ const defaultShortcutLabelMap = {
} as ShortcutLabelMap;
const ShortcutsSelect = (props: Props): JSX.Element => {
const { onShortcutPress } = props;
const { labels } = useSelector((state: CombinedState) => state.annotation.job);
const { labels, onShortcutPress } = props;
const [shortcutLabelMap, setShortcutLabelMap] = useState(defaultShortcutLabelMap);
const keyMap: KeyMap = {};
......@@ -69,7 +70,7 @@ const ShortcutsSelect = (props: Props): JSX.Element => {
if (event) {
event.preventDefault();
}
onShortcutPress(event, label.id);
onShortcutPress(event, label.id as number);
};
});
......
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
......@@ -23,10 +24,11 @@ import {
} from 'actions/annotation-actions';
import { Canvas } from 'cvat-canvas-wrapper';
import { Canvas3d } from 'cvat-canvas3d-wrapper';
import { getCore, Label, LabelType } from 'cvat-core-wrapper';
import { CombinedState, ObjectType } from 'reducers';
import { filterApplicableForType } from 'utils/filter-applicable-labels';
import { adjustContextImagePosition } from 'components/annotation-page/standard-workspace/context-image/context-image';
import LabelSelector from 'components/label-selector/label-selector';
import { getCore } from 'cvat-core-wrapper';
import isAbleToChangeFrame from 'utils/is-able-to-change-frame';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import ShortcutsSelect from './shortcuts-select';
......@@ -114,12 +116,15 @@ function TagAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.Elemen
}
};
const controlsDisabled = !labels.length || frameData.deleted;
const defaultLabelID = labels.length ? labels[0].id : null;
const [applicableLabels, setApplicableLabels] = useState<Label[]>(
filterApplicableForType(LabelType.TAG, labels),
);
const controlsDisabled = !applicableLabels.length || frameData.deleted;
const defaultLabelID = applicableLabels.length ? applicableLabels[0].id as number : null;
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [frameTags, setFrameTags] = useState([] as any[]);
const [selectedLabelID, setSelectedLabelID] = useState<number>(defaultLabelID);
const [selectedLabelID, setSelectedLabelID] = useState<number | null>(defaultLabelID);
const [skipFrame, setSkipFrame] = useState(false);
useEffect(() => {
......@@ -128,6 +133,10 @@ function TagAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.Elemen
}
}, []);
useEffect(() => {
setApplicableLabels(filterApplicableForType(LabelType.TAG, labels));
}, [labels]);
useEffect(() => {
const listener = (event: Event): void => {
if (
......@@ -211,7 +220,9 @@ function TagAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.Elemen
const handlers = {
SWITCH_DRAW_MODE: (event: KeyboardEvent | undefined) => {
preventDefault(event);
onShortcutPress(event, selectedLabelID);
if (selectedLabelID !== null) {
onShortcutPress(event, selectedLabelID);
}
},
};
......@@ -259,7 +270,7 @@ function TagAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.Elemen
<Row justify='start' className='cvat-tag-annotation-sidebar-label-select'>
<Col>
<LabelSelector
labels={labels}
labels={applicableLabels}
value={selectedLabelID}
onChange={onChangeLabel}
onEnterPress={onAddTag}
......@@ -267,7 +278,7 @@ function TagAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.Elemen
<Button
type='primary'
className='cvat-add-tag-button'
onClick={() => onAddTag(selectedLabelID)}
onClick={() => onAddTag(selectedLabelID as number)}
icon={<PlusOutlined />}
/>
</Col>
......@@ -286,7 +297,7 @@ function TagAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.Elemen
</Row>
<Row>
<Col>
<ShortcutsSelect onShortcutPress={onShortcutPress} />
<ShortcutsSelect labels={applicableLabels} onShortcutPress={onShortcutPress} />
</Col>
</Row>
<Row justify='center' className='cvat-tag-annotation-sidebar-shortcut-help'>
......
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
......@@ -13,7 +14,7 @@ import Form, { FormInstance } from 'antd/lib/form';
import Badge from 'antd/lib/badge';
import { Store } from 'antd/lib/form/interface';
import { RawAttribute } from 'cvat-core-wrapper';
import { RawAttribute, LabelType } from 'cvat-core-wrapper';
import CVATTooltip from 'components/common/cvat-tooltip';
import ColorPicker from 'components/annotation-page/standard-workspace/objects-side-bar/color-picker';
import { ColorizeIcon } from 'icons';
......@@ -78,7 +79,7 @@ export default class LabelForm extends React.Component<Props> {
name: values.name,
id: label ? label.id : idGenerator(),
color: values.color,
type: label && label.id as number > 0 ? label?.type : values.type || label?.type || 'any',
type: values.type || label?.type || LabelType.ANY,
attributes: (values.attributes || []).map((attribute: Store) => {
let attrValues: string | string[] = attribute.values;
if (!Array.isArray(attrValues)) {
......@@ -451,6 +452,37 @@ export default class LabelForm extends React.Component<Props> {
);
}
private renderLabelTypeInput(): JSX.Element {
const { onSkeletonSubmit } = this.props;
const isSkeleton = !!onSkeletonSubmit;
const types = Object.values(LabelType)
.filter((type: string) => type !== LabelType.SKELETON);
const { label } = this.props;
const defaultType = isSkeleton ? LabelType.SKELETON : LabelType.ANY;
const value = label ? label.type : defaultType;
return (
<Form.Item name='type' initialValue={value}>
<Select disabled={isSkeleton} showSearch={false}>
{isSkeleton && (
<Select.Option
className='cvat-label-type-option-skeleton'
value='skeleton'
>
Skeleton
</Select.Option>
)}
{ types.map((type: string): JSX.Element => (
<Select.Option className={`cvat-label-type-option-${type}`} key={type} value={type}>
{`${type[0].toUpperCase()}${type.slice(1)}`}
</Select.Option>
)) }
</Select>
</Form.Item>
);
}
private renderNewAttributeButton(): JSX.Element {
return (
<Form.Item>
......@@ -554,7 +586,8 @@ export default class LabelForm extends React.Component<Props> {
return (
<Form onFinish={this.handleSubmit} layout='vertical' ref={this.formRef}>
<Row justify='start' align='top'>
<Col span={10}>{this.renderLabelNameInput()}</Col>
<Col span={8}>{this.renderLabelNameInput()}</Col>
<Col span={3} offset={1}>{this.renderLabelTypeInput()}</Col>
<Col span={3} offset={1}>
{this.renderChangeColorButton()}
</Col>
......
......@@ -23,9 +23,9 @@ import {
import ObjectStateItemComponent from 'components/annotation-page/standard-workspace/objects-side-bar/object-item';
import { getColor } from 'components/annotation-page/standard-workspace/objects-side-bar/shared';
import { shift } from 'utils/math';
import { Label } from 'cvat-core-wrapper';
import { Canvas, CanvasMode } from 'cvat-canvas-wrapper';
import { Canvas3d } from 'cvat-canvas3d-wrapper';
import { filterApplicableLabels } from 'utils/filter-applicable-labels';
interface OwnProps {
readonly: boolean;
......@@ -326,6 +326,8 @@ class ObjectItemContainer extends React.PureComponent<Props> {
jobInstance,
} = this.props;
const applicableLabels = filterApplicableLabels(objectState, labels);
return (
<ObjectStateItemComponent
jobInstance={jobInstance}
......@@ -341,9 +343,7 @@ class ObjectItemContainer extends React.PureComponent<Props> {
attributes={attributes}
elements={objectState.elements}
normalizedKeyMap={normalizedKeyMap}
labels={labels.filter((label: Label) => (
[objectState.shapeType || objectState.objectType, 'any'].includes(label.type)
))}
labels={applicableLabels}
colorBy={colorBy}
activate={this.activate}
remove={this.remove}
......
......@@ -8,7 +8,7 @@ import Webhook from 'cvat-core/src/webhook';
import {
Label, Attribute, RawAttribute, RawLabel,
} from 'cvat-core/src/labels';
import { ShapeType } from 'cvat-core/src/enums';
import { ShapeType, LabelType } from 'cvat-core/src/enums';
import { Storage, StorageData } from 'cvat-core/src/storage';
const cvat: any = _cvat;
......@@ -28,6 +28,7 @@ export {
Label,
Attribute,
ShapeType,
LabelType,
Storage,
Webhook,
};
......
......@@ -81,7 +81,7 @@ const defaultState: AnnotationState = {
},
drawing: {
activeShapeType: ShapeType.RECTANGLE,
activeLabelID: 0,
activeLabelID: null,
activeObjectType: ObjectType.SHAPE,
},
annotations: {
......@@ -222,7 +222,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
},
drawing: {
...state.drawing,
activeLabelID: job.labels.length ? job.labels[0].id : null,
activeLabelID: defaultLabel ? defaultLabel.id : null,
activeObjectType: job.mode === 'interpolation' ? ObjectType.TRACK : ObjectType.SHAPE,
activeShapeType,
},
......
......@@ -692,7 +692,7 @@ export interface AnnotationState {
activeRectDrawingMethod?: RectDrawingMethod;
activeCuboidDrawingMethod?: CuboidDrawingMethod;
activeNumOfPoints?: number;
activeLabelID: number;
activeLabelID: number | null;
activeObjectType: ObjectType;
activeInitialState?: any;
};
......
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
import { Label, ObjectState, LabelType } from 'cvat-core-wrapper';
export function filterApplicableForType(type: LabelType, labels: Label[]): Label[] {
const applicableLabels = labels.filter((label: Label) => (
[type, 'any'].includes(label.type)
));
return applicableLabels;
}
export function filterApplicableLabels(objectState: ObjectState, labels: Label[]): Label[] {
const applicableLabels = filterApplicableForType((objectState.shapeType || 'tag') as unknown as LabelType, labels);
// a label the object has at this moment considered like applicable label
if (!applicableLabels.find((label: Label) => label.id === objectState.label.id)) {
return [objectState.label, ...applicableLabels];
}
return applicableLabels;
}
......@@ -135,6 +135,10 @@ class LabelSerializer(SublabelSerializer):
except models.Label.DoesNotExist:
raise exceptions.NotFound(detail='Not found label with id #{} to change'.format(validated_data['id']))
db_label.name = validated_data.get('name', db_label.name)
updated_type = validated_data.get('type', db_label.type)
if 'skeleton' not in [db_label.type, updated_type]:
# do not permit to change types from/to "skeleton"
db_label.type = updated_type
logger.info("{}({}) label was updated".format(db_label.name, db_label.id))
else:
db_label = models.Label.objects.create(name=validated_data.get('name'), type=validated_data.get('type'),
......
......@@ -126,7 +126,7 @@ context('Hotkeys to change labels feature.', () => {
cy.contains('button', 'Shape').click();
});
cy.get('body').type('{Ctrl}2');
cy.contains(`Default label was changed to "${secondLabelCurrentVal}"`).should('exist');
cy.contains(`Default label has been changed to "${secondLabelCurrentVal}"`).should('exist');
cy.get('.cvat-canvas-container').click(500, 500).click(600, 600);
cy.get('#cvat-objects-sidebar-state-item-2')
.find('.cvat-objects-sidebar-state-item-label-selector')
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册