Fixed possible color collisions in the generated colormap (#4007)

main
Andrey Zhavoronkov 4 years ago committed by GitHub
parent cc057a7a77
commit 23c80c4cfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Order of labels in tasks and projects (<https://github.com/openvinotoolkit/cvat/pull/3987>)
- Fixed task creating with large files via webpage (<https://github.com/openvinotoolkit/cvat/pull/3692>)
- Added information to export CVAT_HOST when performing local installation for accessing over network (<https://github.com/openvinotoolkit/cvat/pull/4014>)
- Fixed possible color collisions in the generated colormap (<https://github.com/openvinotoolkit/cvat/pull/4007>)
### Security
- TDB

@ -1,9 +1,11 @@
# Copyright (C) 2019 Intel Corporation
# Copyright (C) 2019-2021 Intel Corporation
#
# SPDX-License-Identifier: MIT
import os.path as osp
from hashlib import blake2s
import itertools
import operator
from datumaro.util.os_util import make_file_name
@ -20,7 +22,6 @@ def get_color_from_index(index):
return tuple(color)
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:
@ -61,19 +62,40 @@ def make_colormap(instance_data):
return {label['name']: [hex2rgb(label['color']), [], []] for label in labels}
def generate_color(color, used_colors):
def tint_shade_color():
for added_color in (255, 0):
for factor in range(1, 10):
yield tuple(map(lambda c: int(c + (added_color - c) * factor / 10), color))
def get_label_color(label_name, label_names):
def get_unused_color():
def get_avg_color(index):
sorted_colors = sorted(used_colors, key=operator.itemgetter(index))
max_dist_pair = max(zip(sorted_colors, sorted_colors[1:]),
key=lambda c_pair: c_pair[1][index] - c_pair[0][index])
return (max_dist_pair[0][index] + max_dist_pair[1][index]) // 2
return tuple(get_avg_color(i) for i in range(3))
#try to tint and shade color firstly
for new_color in tint_shade_color():
if new_color not in used_colors:
return new_color
return get_unused_color()
def get_label_color(label_name, label_colors):
predefined = parse_default_colors()
normalized_names = [normalize_label(l_name) for l_name in label_names]
label_colors = tuple(hex2rgb(c) for c in label_colors if c)
used_colors = set(itertools.chain(predefined.values(), label_colors))
normalized_name = normalize_label(label_name)
color = predefined.get(normalized_name, None)
name_hash = int.from_bytes(blake2s(normalized_name.encode(), digest_size=4).digest(), byteorder="big")
offset = name_hash + normalized_names.count(normalized_name)
if color is None:
color = get_color_from_index(DEFAULT_COLORMAP_CAPACITY + offset)
elif normalized_names.count(normalized_name):
color = get_color_from_index(DEFAULT_COLORMAP_CAPACITY + offset - 1)
name_hash = int.from_bytes(blake2s(normalized_name.encode(), digest_size=3).digest(), byteorder="big")
color = get_color_from_index(name_hash)
if color in label_colors:
color = generate_color(color, used_colors)
return rgb2hex(color)

@ -11,7 +11,7 @@ def alter_label_colors(apps, schema_editor):
label_names = list()
for label in labels:
label.color = get_label_color(label.name, label_names)
label_names.append(label.name)
label_names.append(label.color)
label.save()
class Migration(migrations.Migration):

@ -111,10 +111,10 @@ class LabelSerializer(serializers.ModelSerializer):
db_label.delete()
return
if not validated_data.get('color', None):
label_names = [l.name for l in
label_colors = [l.color for l in
instance[tuple(instance.keys())[0]].label_set.exclude(id=db_label.id).order_by('id')
]
db_label.color = get_label_color(db_label.name, label_names)
db_label.color = get_label_color(db_label.name, label_colors)
else:
db_label.color = validated_data.get('color', db_label.color)
db_label.save()
@ -390,12 +390,12 @@ class TaskSerializer(WriteOnceMixin, serializers.ModelSerializer):
labels = validated_data.pop('label_set', [])
db_task = models.Task.objects.create(**validated_data)
label_names = list()
label_colors = list()
for label in labels:
attributes = label.pop('attributespec_set')
if not label.get('color', None):
label['color'] = get_label_color(label['name'], label_names)
label_names.append(label['name'])
label['color'] = get_label_color(label['name'], label_colors)
label_colors.append(label['color'])
db_label = models.Label.objects.create(task=db_task, **label)
for attr in attributes:
models.AttributeSpec.objects.create(label=db_label, **attr)
@ -558,12 +558,12 @@ class ProjectSerializer(serializers.ModelSerializer):
training_project=tr_p)
else:
db_project = models.Project.objects.create(**validated_data)
label_names = list()
label_colors = list()
for label in labels:
attributes = label.pop('attributespec_set')
if not label.get('color', None):
label['color'] = get_label_color(label['name'], label_names)
label_names.append(label['name'])
label['color'] = get_label_color(label['name'], label_colors)
label_colors.append(label['color'])
db_label = models.Label.objects.create(project=db_project, **label)
for attr in attributes:
models.AttributeSpec.objects.create(label=db_label, **attr)

@ -8,16 +8,17 @@ import { taskName, labelName } from '../../support/const_canvas3d';
context('Canvas 3D functionality. Interaction with cuboid via sidebar.', () => {
const caseId = '78';
const secondLabel = 'car';
const screenshotsPath = 'cypress/screenshots/canvas3d_functionality_2/case_78_canvas3d_functionality_cuboid_label.js';
const cuboidCreationParams = {
labelName: labelName,
labelName,
};
const secondLabel = 'car';
const secondLabelAdditionalAttrs = false;
const secondLabelColorRed = 'ff0000';
before(() => {
cy.openTask(taskName)
cy.addNewLabel(secondLabel);
cy.openTask(taskName);
cy.addNewLabel(secondLabel, secondLabelAdditionalAttrs, secondLabelColorRed);
cy.openJob();
cy.wait(1000); // Waiting for the point cloud to display
cy.customScreenshot('.cvat-canvas3d-perspective', 'canvas3d_perspective_before_all');
@ -32,7 +33,7 @@ context('Canvas 3D functionality. Interaction with cuboid via sidebar.', () => {
cy.get('#cvat-objects-sidebar-state-item-1')
.trigger('mouseover')
.should('have.class', 'cvat-objects-sidebar-state-active-item')
.wait(1000); //Wating for cuboid activation
.wait(1000); // Wating for cuboid activation
cy.customScreenshot('.cvat-canvas3d-perspective', 'canvas3d_perspective_after_activating_cuboid');
cy.compareImagesAndCheckResult(
`${screenshotsPath}/canvas3d_perspective_before_all.png`,
@ -46,7 +47,10 @@ context('Canvas 3D functionality. Interaction with cuboid via sidebar.', () => {
['canvas3d_sideview_before_all.png', 'canvas3d_sideview_activating_cuboid.png'],
['canvas3d_frontview_before_all.png', 'canvas3d_frontview_activating_cuboid.png'],
].forEach(([viewBefore, viewAfterCubiodActivation]) => {
cy.compareImagesAndCheckResult(`${screenshotsPath}/${viewBefore}`, `${screenshotsPath}/${viewAfterCubiodActivation}`);
cy.compareImagesAndCheckResult(
`${screenshotsPath}/${viewBefore}`,
`${screenshotsPath}/${viewAfterCubiodActivation}`,
);
});
});
@ -67,14 +71,17 @@ context('Canvas 3D functionality. Interaction with cuboid via sidebar.', () => {
['canvas3d_sideview_activating_cuboid.png', 'canvas3d_sideview_change_label_cuboid.png'],
['canvas3d_frontview_activating_cuboid.png', 'canvas3d_frontview_change_label_cuboid.png'],
].forEach(([viewAfterCubiodActivation, viewAfterCubiodChangeLabel]) => {
cy.compareImagesAndCheckResult(`${screenshotsPath}/${viewAfterCubiodActivation}`, `${screenshotsPath}/${viewAfterCubiodChangeLabel}`);
cy.compareImagesAndCheckResult(
`${screenshotsPath}/${viewAfterCubiodActivation}`,
`${screenshotsPath}/${viewAfterCubiodChangeLabel}`,
);
});
});
it('Lock/unlock a cuboid via sidear. The control points of the cuboid on the top/side/front view are locked/unlocked.', () => {
cy.get('#cvat-objects-sidebar-state-item-1')
.find('.cvat-object-item-button-lock')
.click({force: true}); // Lock the cubiod
.click({ force: true }); // Lock the cubiod
cy.get('.cvat-object-item-button-lock-enabled').should('exist');
['topview', 'sideview', 'frontview'].forEach((view) => {
cy.customScreenshot(`.cvat-canvas3d-${view}`, `canvas3d_${view}_lock_cuboid`);
@ -84,9 +91,12 @@ context('Canvas 3D functionality. Interaction with cuboid via sidebar.', () => {
['canvas3d_sideview_change_label_cuboid.png', 'canvas3d_sideview_lock_cuboid.png'],
['canvas3d_frontview_change_label_cuboid.png', 'canvas3d_frontview_lock_cuboid.png'],
].forEach(([viewAfterCubiodChangeLabel, viewAfterCubiodLock]) => {
cy.compareImagesAndCheckResult(`${screenshotsPath}/${viewAfterCubiodChangeLabel}`, `${screenshotsPath}/${viewAfterCubiodLock}`);
cy.compareImagesAndCheckResult(
`${screenshotsPath}/${viewAfterCubiodChangeLabel}`,
`${screenshotsPath}/${viewAfterCubiodLock}`,
);
});
cy.get('.cvat-object-item-button-lock-enabled').click({force: true}); // Unlock the cubiod
cy.get('.cvat-object-item-button-lock-enabled').click({ force: true }); // Unlock the cubiod
cy.get('.cvat-object-item-button-lock').should('exist').trigger('mouseout');
['topview', 'sideview', 'frontview'].forEach((view) => {
cy.customScreenshot(`.cvat-canvas3d-${view}`, `canvas3d_${view}_unlock_cuboid`);
@ -96,20 +106,23 @@ context('Canvas 3D functionality. Interaction with cuboid via sidebar.', () => {
['canvas3d_sideview_lock_cuboid.png', 'canvas3d_sideview_unlock_cuboid.png'],
['canvas3d_frontview_lock_cuboid.png', 'canvas3d_frontview_unlock_cuboid.png'],
].forEach(([viewAfterCubiodLock, viewAfterCubiodUnlock]) => {
cy.compareImagesAndCheckResult(`${screenshotsPath}/${viewAfterCubiodLock}`, `${screenshotsPath}/${viewAfterCubiodUnlock}`);
cy.compareImagesAndCheckResult(
`${screenshotsPath}/${viewAfterCubiodLock}`,
`${screenshotsPath}/${viewAfterCubiodUnlock}`,
);
});
});
it('Switch occluded property for a cuboid via sidear. The cuboid on the perpective view are occluded.', () => {
cy.get('#cvat-objects-sidebar-state-item-1')
.find('.cvat-object-item-button-occluded')
.click({force: true}); // Switch occluded property
.click({ force: true }); // Switch occluded property
cy.customScreenshot('.cvat-canvas3d-perspective', 'canvas3d_perspective_enable_occlud_cuboid');
cy.compareImagesAndCheckResult(
`${screenshotsPath}/canvas3d_perspective_after_activating_cuboid.png`,
`${screenshotsPath}/canvas3d_perspective_enable_occlud_cuboid.png`,
);
cy.get('.cvat-object-item-button-occluded-enabled').click({force: true}); // Switch occluded property again
cy.get('.cvat-object-item-button-occluded-enabled').click({ force: true }); // Switch occluded property again
cy.customScreenshot('.cvat-canvas3d-perspective', 'canvas3d_perspective_disable_occlud_cuboid');
cy.compareImagesAndCheckResult(
`${screenshotsPath}/canvas3d_perspective_enable_occlud_cuboid.png`,
@ -120,7 +133,7 @@ context('Canvas 3D functionality. Interaction with cuboid via sidebar.', () => {
it('Hide/unhide a cuboid via sidear. The cuboid on the perpective/top/side/front view be hidden/unhidden.', () => {
cy.get('#cvat-objects-sidebar-state-item-1')
.find('.cvat-object-item-button-hidden')
.click({force: true}); // Hide the cuboid
.click({ force: true }); // Hide the cuboid
cy.customScreenshot('.cvat-canvas3d-perspective', 'canvas3d_perspective_hide_cuboid');
cy.compareImagesAndCheckResult(
`${screenshotsPath}/canvas3d_perspective_disable_occlud_cuboid.png`,
@ -134,9 +147,12 @@ context('Canvas 3D functionality. Interaction with cuboid via sidebar.', () => {
['canvas3d_sideview_unlock_cuboid.png', 'canvas3d_sideview_hide_cuboid.png'],
['canvas3d_frontview_unlock_cuboid.png', 'canvas3d_frontview_hide_cuboid.png'],
].forEach(([viewAfterCubiodUnlock, viewAfterCubiodHide]) => {
cy.compareImagesAndCheckResult(`${screenshotsPath}/${viewAfterCubiodUnlock}`, `${screenshotsPath}/${viewAfterCubiodHide}`);
cy.compareImagesAndCheckResult(
`${screenshotsPath}/${viewAfterCubiodUnlock}`,
`${screenshotsPath}/${viewAfterCubiodHide}`,
);
});
cy.get('.cvat-object-item-button-hidden-enabled').click({force: true}); // Unhide the cuboid
cy.get('.cvat-object-item-button-hidden-enabled').click({ force: true }); // Unhide the cuboid
cy.customScreenshot('.cvat-canvas3d-perspective', 'canvas3d_perspective_unhide_cuboid');
cy.compareImagesAndCheckResult(
`${screenshotsPath}/canvas3d_perspective_hide_cuboid.png`,
@ -150,7 +166,10 @@ context('Canvas 3D functionality. Interaction with cuboid via sidebar.', () => {
['canvas3d_sideview_hide_cuboid.png', 'canvas3d_sideview_unhide_cuboid.png'],
['canvas3d_frontview_hide_cuboid.png', 'canvas3d_frontview_unhide_cuboid.png'],
].forEach(([viewAfterCubiodHide, viewAfterCubiodUnhide]) => {
cy.compareImagesAndCheckResult(`${screenshotsPath}/${viewAfterCubiodHide}`, `${screenshotsPath}/${viewAfterCubiodUnhide}`);
cy.compareImagesAndCheckResult(
`${screenshotsPath}/${viewAfterCubiodHide}`,
`${screenshotsPath}/${viewAfterCubiodUnhide}`,
);
});
});
});

Loading…
Cancel
Save