Skip to content
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 ability to setup text labels content #4029

Merged
merged 4 commits into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Data sorting option (<https://github.com/openvinotoolkit/cvat/pull/3937>)
- Options to change font size & position of text labels on the canvas (<https://github.com/openvinotoolkit/cvat/pull/3972>)
- Add "tag" return type for automatic annotation in Nuclio (<https://github.com/openvinotoolkit/cvat/pull/3896>)
- User is able to customize information that text labels show (<https://github.com/openvinotoolkit/cvat/pull/4029>)

### Changed
- TDB
Expand Down
4 changes: 2 additions & 2 deletions cvat-canvas/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-canvas/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-canvas",
"version": "2.10.2",
"version": "2.11.0",
"description": "Part of Computer Vision Annotation Tool which presents its canvas library",
"main": "src/canvas.ts",
"scripts": {
Expand Down
14 changes: 13 additions & 1 deletion cvat-canvas/src/typescript/canvasModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//
// SPDX-License-Identifier: MIT

import consts from './consts';
import { MasterImpl } from './master';

export interface Size {
Expand Down Expand Up @@ -57,6 +58,7 @@ export interface Configuration {
displayAllText?: boolean;
textFontSize?: number;
textPosition?: 'auto' | 'center';
textContent?: string;
undefinedAttrValue?: string;
showProjections?: boolean;
forceDisableEditing?: boolean;
Expand Down Expand Up @@ -263,6 +265,9 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
displayAllText: false,
autoborders: false,
undefinedAttrValue: '',
textContent: 'id,label,attributes,source,descriptions',
textPosition: 'auto',
textFontSize: consts.DEFAULT_SHAPE_TEXT_SIZE,
},
imageBitmap: false,
image: null,
Expand Down Expand Up @@ -649,14 +654,21 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
this.data.configuration.displayAllText = configuration.displayAllText;
}

if (typeof configuration.textFontSize === 'number') {
if (typeof configuration.textFontSize === 'number' && configuration.textFontSize >= 8) {
bsekachev marked this conversation as resolved.
Show resolved Hide resolved
this.data.configuration.textFontSize = configuration.textFontSize;
}

if (['auto', 'center'].includes(configuration.textPosition)) {
this.data.configuration.textPosition = configuration.textPosition;
}

if (typeof configuration.textContent === 'string') {
const splitted = configuration.textContent.split(',').filter((entry: string) => !!entry);
if (splitted.every((entry: string) => ['id', 'label', 'attributes', 'source', 'descriptions'].includes(entry))) {
this.data.configuration.textContent = configuration.textContent;
}
}

if (typeof configuration.showProjections === 'boolean') {
this.data.configuration.showProjections = configuration.showProjections;
}
Expand Down
73 changes: 49 additions & 24 deletions cvat-canvas/src/typescript/canvasView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1189,9 +1189,11 @@ export class CanvasViewImpl implements CanvasView, Listener {
}
}

const recreateText = configuration.textContent !== this.configuration.textContent;
const updateTextPosition = configuration.displayAllText !== this.configuration.displayAllText ||
configuration.textFontSize !== this.configuration.textFontSize ||
configuration.textPosition !== this.configuration.textPosition;
configuration.textPosition !== this.configuration.textPosition ||
recreateText;

if (configuration.smoothImage === true) {
this.background.classList.remove('cvat_canvas_pixelized');
Expand All @@ -1200,6 +1202,19 @@ export class CanvasViewImpl implements CanvasView, Listener {
}

this.configuration = configuration;
if (recreateText) {
const states = this.controller.objects;
for (const key of Object.keys(this.drawnStates)) {
const clientID = +key;
const [state] = states.filter((_state: any) => _state.clientID === clientID);
if (clientID in this.svgTexts) {
this.svgTexts[clientID].remove();
delete this.svgTexts[clientID];
if (state) this.svgTexts[clientID] = this.addText(state);
}
}
}

if (updateTextPosition) {
for (const i in this.drawnStates) {
if (i in this.svgTexts) {
Expand Down Expand Up @@ -2071,8 +2086,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
// Update text position after corresponding box has been moved, resized, etc.
private updateTextPosition(text: SVG.Text, shape: SVG.Shape): void {
if (text.node.style.display === 'none') return; // wrong transformation matrix
const textFontSize = this.configuration.textFontSize || consts.DEFAULT_SHAPE_TEXT_SIZE;
const textPosition = this.configuration.textPosition || 'auto';
const { textFontSize, textPosition } = this.configuration;

text.untransform();
text.style({ 'font-size': `${textFontSize}px` });
Expand Down Expand Up @@ -2131,8 +2145,8 @@ export class CanvasViewImpl implements CanvasView, Listener {

// Translate found coordinates to text SVG
const [x, y, rotX, rotY]: number[] = translateToSVG(this.text, [
clientX + consts.TEXT_MARGIN,
clientY + consts.TEXT_MARGIN,
clientX + (textPosition === 'auto' ? consts.TEXT_MARGIN : 0),
clientY + (textPosition === 'auto' ? consts.TEXT_MARGIN : 0),
clientCX,
clientCY,
]);
Expand All @@ -2156,6 +2170,13 @@ export class CanvasViewImpl implements CanvasView, Listener {

private addText(state: any): SVG.Text {
const { undefinedAttrValue } = this.configuration;
const content = this.configuration.textContent;
const withID = content.includes('id');
const withAttr = content.includes('attributes');
const withLabel = content.includes('label');
const withSource = content.includes('source');
const withDescriptions = content.includes('descriptions');

const textFontSize = this.configuration.textFontSize || 12;
const {
label, clientID, attributes, source, descriptions,
Expand All @@ -2167,28 +2188,32 @@ export class CanvasViewImpl implements CanvasView, Listener {

return this.adoptedText
.text((block): void => {
block.tspan(`${label.name} ${clientID} (${source})`).style({
block.tspan(`${withLabel ? label.name : ''} ${withID ? clientID : ''} ${withSource ? `(${source})` : ''}`).style({
'text-transform': 'uppercase',
});
for (const desc of descriptions) {
block
.tspan(`${desc}`)
.attr({
dy: '1em',
x: 0,
})
.addClass('cvat_canvas_text_description');
if (withDescriptions) {
for (const desc of descriptions) {
block
.tspan(`${desc}`)
.attr({
dy: '1em',
x: 0,
})
.addClass('cvat_canvas_text_description');
}
}
for (const attrID of Object.keys(attributes)) {
const value = attributes[attrID] === undefinedAttrValue ? '' : attributes[attrID];
block
.tspan(`${attrNames[attrID]}: ${value}`)
.attr({
attrID,
dy: '1em',
x: 0,
})
.addClass('cvat_canvas_text_attribute');
if (withAttr) {
for (const attrID of Object.keys(attributes)) {
const value = attributes[attrID] === undefinedAttrValue ? '' : attributes[attrID];
block
.tspan(`${attrNames[attrID]}: ${value}`)
.attr({
attrID,
dy: '1em',
x: 0,
})
.addClass('cvat_canvas_text_attribute');
}
}
})
.move(0, 0)
Expand Down
4 changes: 2 additions & 2 deletions 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.28.2",
"version": "1.29.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
10 changes: 10 additions & 0 deletions cvat-ui/src/actions/settings-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export enum SettingsActionTypes {
SWITCH_SMOOTH_IMAGE = 'SWITCH_SMOOTH_IMAGE',
SWITCH_TEXT_FONT_SIZE = 'SWITCH_TEXT_FONT_SIZE',
SWITCH_TEXT_POSITION = 'SWITCH_TEXT_POSITION',
SWITCH_TEXT_CONTENT = 'SWITCH_TEXT_CONTENT',
CHANGE_BRIGHTNESS_LEVEL = 'CHANGE_BRIGHTNESS_LEVEL',
CHANGE_CONTRAST_LEVEL = 'CHANGE_CONTRAST_LEVEL',
CHANGE_SATURATION_LEVEL = 'CHANGE_SATURATION_LEVEL',
Expand Down Expand Up @@ -196,6 +197,15 @@ export function switchTextPosition(position: 'auto' | 'center'): AnyAction {
};
}

export function switchTextContent(textContent: string): AnyAction {
return {
type: SettingsActionTypes.SWITCH_TEXT_CONTENT,
payload: {
textContent,
},
};
}

export function changeBrightnessLevel(level: number): AnyAction {
return {
type: SettingsActionTypes.CHANGE_BRIGHTNESS_LEVEL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ interface Props {
showObjectsTextAlways: boolean;
textFontSize: number;
textPosition: 'auto' | 'center';
textContent: string;
showAllInterpolationTracks: boolean;
workspace: Workspace;
automaticBordering: boolean;
Expand Down Expand Up @@ -111,6 +112,7 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
smoothImage,
textFontSize,
textPosition,
textContent,
} = this.props;
const { canvasInstance } = this.props as { canvasInstance: Canvas };

Expand All @@ -130,6 +132,7 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
creationOpacity: selectedOpacity,
textFontSize,
textPosition,
textContent,
});

this.initialSetup();
Expand Down Expand Up @@ -166,6 +169,7 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
showObjectsTextAlways,
textFontSize,
textPosition,
textContent,
showAllInterpolationTracks,
automaticBordering,
intelligentPolygonCrop,
Expand All @@ -182,7 +186,8 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
prevProps.selectedOpacity !== selectedOpacity ||
prevProps.smoothImage !== smoothImage ||
prevProps.textFontSize !== textFontSize ||
prevProps.textPosition !== textPosition
prevProps.textPosition !== textPosition ||
prevProps.textContent !== textContent
) {
canvasInstance.configure({
undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE,
Expand All @@ -194,6 +199,7 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
smoothImage,
textFontSize,
textPosition,
textContent,
});
}

Expand Down
4 changes: 4 additions & 0 deletions cvat-ui/src/components/header/settings-modal/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
}
}

.cvat-workspace-settings-text-content {
width: 100%;
}

.cvat-workspace-settings-approx-poly-threshold {
user-select: none;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
import InputNumber from 'antd/lib/input-number';
import Text from 'antd/lib/typography/Text';
import Slider from 'antd/lib/slider';
import Select from 'antd/lib/select';

import {
MAX_ACCURACY,
marks,
} from 'components/annotation-page/standard-workspace/controls-side-bar/approximation-accuracy';
import { clamp } from 'utils/math';
import { Select } from 'antd';

interface Props {
autoSave: boolean;
Expand All @@ -28,6 +28,7 @@ interface Props {
defaultApproxPolyAccuracy: number;
textFontSize: number;
textPosition: 'center' | 'auto';
textContent: string;
onSwitchAutoSave(enabled: boolean): void;
onChangeAutoSaveInterval(interval: number): void;
onChangeAAMZoomMargin(margin: number): void;
Expand All @@ -38,6 +39,7 @@ interface Props {
onSwitchIntelligentPolygonCrop(enabled: boolean): void;
onChangeTextFontSize(fontSize: number): void;
onChangeTextPosition(position: 'auto' | 'center'): void;
onChangeTextContent(textContent: string[]): void;
}

function WorkspaceSettingsComponent(props: Props): JSX.Element {
Expand All @@ -52,6 +54,7 @@ function WorkspaceSettingsComponent(props: Props): JSX.Element {
defaultApproxPolyAccuracy,
textFontSize,
textPosition,
textContent,
onSwitchAutoSave,
onChangeAutoSaveInterval,
onChangeAAMZoomMargin,
Expand All @@ -62,6 +65,7 @@ function WorkspaceSettingsComponent(props: Props): JSX.Element {
onChangeDefaultApproxPolyAccuracy,
onChangeTextFontSize,
onChangeTextPosition,
onChangeTextContent,
} = props;

const minAutoSaveInterval = 1;
Expand Down Expand Up @@ -137,6 +141,25 @@ function WorkspaceSettingsComponent(props: Props): JSX.Element {
</Text>
</Col>
</Row>
<Row className='cvat-workspace-settings-text-settings'>
<Col span={24}>
<Text>Content of a text</Text>
</Col>
<Col span={16}>
<Select
className='cvat-workspace-settings-text-content'
mode='multiple'
value={textContent.split(',').filter((entry: string) => !!entry)}
onChange={onChangeTextContent}
>
<Select.Option value='id'>ID</Select.Option>
<Select.Option value='label'>Label</Select.Option>
<Select.Option value='attributes'>Attributes</Select.Option>
<Select.Option value='source'>Source</Select.Option>
<Select.Option value='descriptions'>Descriptions</Select.Option>
</Select>
</Col>
</Row>
<Row className='cvat-workspace-settings-text-settings'>
<Col span={12}>
<Text>Position of a text</Text>
Expand Down
Loading