Merge branch 'develop' into bs/bitmap

main
Boris Sekachev 6 years ago committed by GitHub
commit c49a0c19fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,8 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.0-alpha] - Unreleased
## [1.0.0-beta] - Unreleased
### Added
- Special behaviour for attribute value ``__undefined__`` (invisibility, no shortcuts to be set in AAM)
- Dialog window with some helpful information about using filters
- Ability to display a bitmap in the new UI
### Changed
@ -18,11 +20,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
-
### Fixed
-
- New shape is added when press ``esc`` when drawing instead of cancellation
### Security
-
## [1.0.0-alpha] - 2020-03-31
### Added
- Data streaming using chunks (https://github.com/opencv/cvat/pull/1007)
- New UI: showing file names in UI (https://github.com/opencv/cvat/pull/1311)
- New UI: delete a point from context menu (https://github.com/opencv/cvat/pull/1292)
### Fixed
- Git app cannot clone a repository (https://github.com/opencv/cvat/pull/1330)
- New UI: preview position in task details (https://github.com/opencv/cvat/pull/1312)
- AWS deployment (https://github.com/opencv/cvat/pull/1316)
## [0.6.1] - 2020-03-21
### Changed
- VOC task export now does not use official label map by default, but takes one

@ -1417,7 +1417,9 @@ export class CanvasViewImpl implements CanvasView, Listener {
return this.adoptedText.text((block): void => {
block.tspan(`${label.name} ${clientID}`).style('text-transform', 'uppercase');
for (const attrID of Object.keys(attributes)) {
block.tspan(`${attrNames[attrID]}: ${attributes[attrID]}`).attr({
const value = attributes[attrID] === consts.UNDEFINED_ATTRIBUTE_VALUE
? '' : attributes[attrID];
block.tspan(`${attrNames[attrID]}: ${value}`).attr({
attrID,
dy: '1em',
x: 0,

@ -10,6 +10,7 @@ const AREA_THRESHOLD = 9;
const SIZE_THRESHOLD = 3;
const POINTS_STROKE_WIDTH = 1.5;
const POINTS_SELECTED_STROKE_WIDTH = 4;
const UNDEFINED_ATTRIBUTE_VALUE = '__undefined__';
export default {
BASE_STROKE_WIDTH,
@ -20,4 +21,5 @@ export default {
SIZE_THRESHOLD,
POINTS_STROKE_WIDTH,
POINTS_SELECTED_STROKE_WIDTH,
UNDEFINED_ATTRIBUTE_VALUE,
};

@ -50,6 +50,7 @@ export class DrawHandlerImpl implements DrawHandler {
// so, methods like draw() just undefined for SVG.Shape, but nevertheless they exist
private drawInstance: any;
private initialized: boolean;
private canceled: boolean;
private pointsGroup: SVG.G | null;
private shapeSizeElement: ShapeSizeElement;
@ -149,6 +150,7 @@ export class DrawHandlerImpl implements DrawHandler {
// Clear drawing
this.drawInstance.draw('stop');
}
this.drawInstance.off();
this.drawInstance.remove();
this.drawInstance = null;
@ -161,6 +163,10 @@ export class DrawHandlerImpl implements DrawHandler {
if (this.crosshair) {
this.removeCrosshair();
}
if (!this.drawData.initialState) {
this.onDrawDone(null);
}
}
private initDrawing(): void {
@ -175,8 +181,9 @@ export class DrawHandlerImpl implements DrawHandler {
const bbox = (e.target as SVGRectElement).getBBox();
const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox);
const { shapeType } = this.drawData;
this.cancel();
this.release();
if (this.canceled) return;
if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) {
this.onDrawDone({
shapeType,
@ -290,11 +297,11 @@ export class DrawHandlerImpl implements DrawHandler {
this.drawInstance.on('drawdone', (e: CustomEvent): void => {
const targetPoints = pointsToArray((e.target as SVGElement).getAttribute('points'));
const { points, box } = this.getFinalPolyshapeCoordinates(targetPoints);
const { shapeType } = this.drawData;
this.cancel();
this.release();
if (this.canceled) return;
if (shapeType === 'polygon'
&& ((box.xbr - box.xtl) * (box.ybr - box.ytl) >= consts.AREA_THRESHOLD)
&& points.length >= 3 * 2) {
@ -598,6 +605,7 @@ export class DrawHandlerImpl implements DrawHandler {
this.canvas = canvas;
this.text = text;
this.initialized = false;
this.canceled = false;
this.drawData = null;
this.geometry = null;
this.crosshair = null;
@ -671,17 +679,18 @@ export class DrawHandlerImpl implements DrawHandler {
this.geometry = geometry;
if (drawData.enabled) {
this.canceled = false;
this.drawData = drawData;
this.initDrawing();
this.startDraw();
} else {
this.cancel();
this.release();
this.drawData = drawData;
}
}
public cancel(): void {
this.canceled = true;
this.release();
this.onDrawDone(null);
}
}

@ -441,7 +441,7 @@
* Returns the ranges of cached frames
* @method ranges
* @memberof Session.frames
* @returns {Array{string}}
* @returns {Array.<string>}
* @instance
* @async
*/
@ -520,7 +520,8 @@
* @returns {HistoryActions}
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
* @returns {[string, number][]} array of pairs [action name, frame number]
* @returns {Array.<Array.<string|number>>}
* array of pairs [action name, frame number]
* @instance
* @async
*/

@ -2,9 +2,14 @@
//
// SPDX-License-Identifier: MIT
import React from 'react';
import React, { useState } from 'react';
import { connect } from 'react-redux';
import Select, { SelectValue, LabeledValue } from 'antd/lib/select';
import Title from 'antd/lib/typography/Title';
import Text from 'antd/lib/typography/Text';
import Paragraph from 'antd/lib/typography/Paragraph';
import Tooltip from 'antd/lib/tooltip';
import Modal from 'antd/lib/modal';
import Icon from 'antd/lib/icon';
import {
@ -16,6 +21,8 @@ import { CombinedState } from 'reducers/interfaces';
interface StateToProps {
annotationsFilters: string[];
annotationsFiltersHistory: string[];
searchForwardShortcut: string;
searchBackwardShortcut: string;
}
interface DispatchToProps {
@ -30,11 +37,16 @@ function mapStateToProps(state: CombinedState): StateToProps {
filtersHistory: annotationsFiltersHistory,
},
},
shortcuts: {
normalizedKeyMap,
},
} = state;
return {
annotationsFilters,
annotationsFiltersHistory,
searchForwardShortcut: normalizedKeyMap.SEARCH_FORWARD,
searchBackwardShortcut: normalizedKeyMap.SEARCH_BACKWARD,
};
}
@ -56,13 +68,74 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
};
}
function filtersHelpModalContent(
searchForwardShortcut: string,
searchBackwardShortcut: string,
): JSX.Element {
return (
<>
<Paragraph>
<Title level={3}>General</Title>
</Paragraph>
<Paragraph>
You can use filters to display only subset of objects on a frame
or to search objects that satisfy the filters using hotkeys
<Text strong>
{` ${searchForwardShortcut} `}
</Text>
and
<Text strong>
{` ${searchBackwardShortcut} `}
</Text>
</Paragraph>
<Paragraph>
<Text strong>Supported properties: </Text>
width, height, label, serverID, clientID, type, shape, occluded
<br />
<Text strong>Supported operators: </Text>
==, !=, &gt;, &gt;=, &lt;, &lt;=, ~=, (), &amp; and |
<br />
<Text strong>
If you have double quotes in your query string,
please escape them using back slash: \&quot; (see the latest example)
</Text>
<br />
All properties and values are case-sensitive.
CVAT uses json queries to perform search.
</Paragraph>
<Paragraph>
<Title level={3}>Examples</Title>
<ul>
<li>label==&quot;car&quot; | label==[&quot;road sign&quot;]</li>
<li>width &gt;= height</li>
<li>attr[&quot;Attribute 1&quot;] == attr[&quot;Attribute 2&quot;]</li>
<li>clientID == 50</li>
<li>
(label==&quot;car&quot; &amp; attr[&quot;parked&quot;]==true)
| (label==&quot;pedestrian&quot; &amp; width &gt; 150)
</li>
<li>
(( label==[&quot;car \&quot;mazda\&quot;&quot;])
&amp; (attr[&quot;sunglasses&quot;]==true
| (width &gt; 150 | height &gt; 150 &amp; (clientID == serverID)))))
</li>
</ul>
</Paragraph>
</>
);
}
function AnnotationsFiltersInput(props: StateToProps & DispatchToProps): JSX.Element {
const {
annotationsFilters,
annotationsFiltersHistory,
searchForwardShortcut,
searchBackwardShortcut,
changeAnnotationsFilters,
} = props;
const [underCursor, setUnderCursor] = useState(false);
return (
<Select
className='cvat-annotations-filters-input'
@ -70,13 +143,36 @@ function AnnotationsFiltersInput(props: StateToProps & DispatchToProps): JSX.Ele
value={annotationsFilters}
mode='tags'
style={{ width: '100%' }}
placeholder={(
<>
<Icon type='filter' />
<span style={{ marginLeft: 5 }}>Annotations filter</span>
</>
)}
placeholder={
underCursor ? (
<>
<Tooltip title='Click to open help'>
<Icon
type='filter'
onClick={(e: React.MouseEvent) => {
e.stopPropagation();
Modal.info({
width: 700,
title: 'How to use filters?',
content: filtersHelpModalContent(
searchForwardShortcut,
searchBackwardShortcut,
),
});
}}
/>
</Tooltip>
</>
) : (
<>
<Icon style={{ transform: 'scale(0.9)' }} type='filter' />
<span style={{ marginLeft: 5 }}>Annotations filters</span>
</>
)
}
onChange={changeAnnotationsFilters}
onMouseEnter={() => setUnderCursor(true)}
onMouseLeave={() => setUnderCursor(false)}
>
{annotationsFiltersHistory.map((element: string): JSX.Element => (
<Select.Option key={element} value={element}>{element}</Select.Option>

@ -10,6 +10,8 @@ import Select, { SelectValue } from 'antd/lib/select';
import Radio, { RadioChangeEvent } from 'antd/lib/radio';
import Input from 'antd/lib/input';
import consts from 'consts';
interface InputElementParameters {
attrID: number;
inputType: string;
@ -53,7 +55,10 @@ function renderInputElement(parameters: InputElementParameters): JSX.Element {
)}
>
{values.map((value: string): JSX.Element => (
<Select.Option key={value} value={value}>{value}</Select.Option>
<Select.Option key={value} value={value}>
{value === consts.UNDEFINED_ATTRIBUTE_VALUE
? consts.NO_BREAK_SPACE : value}
</Select.Option>
))}
</Select>
</div>
@ -71,7 +76,10 @@ function renderInputElement(parameters: InputElementParameters): JSX.Element {
)}
>
{values.map((value: string): JSX.Element => (
<Radio style={{ display: 'block' }} key={value} value={value}>{value}</Radio>
<Radio style={{ display: 'block' }} key={value} value={value}>
{value === consts.UNDEFINED_ATTRIBUTE_VALUE
? consts.NO_BREAK_SPACE : value}
</Radio>
))}
</Radio.Group>
</div>
@ -193,7 +201,9 @@ function renderList(parameters: ListParameters): JSX.Element | null {
[key: string]: (keyEvent?: KeyboardEvent) => void;
} = {};
values.slice(0, 10).forEach((value: string, index: number): void => {
const filteredValues = values
.filter((value: string): boolean => value !== consts.UNDEFINED_ATTRIBUTE_VALUE);
filteredValues.slice(0, 10).forEach((value: string, index: number): void => {
const key = `SET_${index}_VALUE`;
keyMap[key] = {
name: `Set value "${value}"`,
@ -214,7 +224,7 @@ function renderList(parameters: ListParameters): JSX.Element | null {
return (
<div className='attribute-annotation-sidebar-attr-list-wrapper'>
<GlobalHotKeys keyMap={keyMap as KeyMap} handlers={handlers} allowChanges />
{values.map((value: string, index: number): JSX.Element => (
{filteredValues.map((value: string, index: number): JSX.Element => (
<div key={value}>
<Text strong>{`${index}:`}</Text>
<Text>{` ${value}`}</Text>

@ -48,9 +48,7 @@
margin: 10px 0px 10px 10px;
}
.attribute-annotation-sidebar-attr-elem-wrapper {
display: inline-block;
width: 60%;
}

@ -20,7 +20,7 @@ import Text from 'antd/lib/typography/Text';
import Tooltip from 'antd/lib/tooltip';
import ColorChanger from 'components/annotation-page/standard-workspace/objects-side-bar/color-changer';
import consts from 'consts';
import {
ObjectOutsideIcon,
FirstIcon,
@ -30,10 +30,10 @@ import {
BackgroundIcon,
ForegroundIcon,
} from 'icons';
import { ObjectType, ShapeType } from 'reducers/interfaces';
import { clamp } from 'utils/math';
function ItemMenu(
serverID: number | undefined,
locked: boolean,
@ -508,7 +508,10 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element
}}
>
{ attrValues.map((value: string): JSX.Element => (
<Radio key={value} value={value}>{value}</Radio>
<Radio key={value} value={value}>
{value === consts.UNDEFINED_ATTRIBUTE_VALUE
? consts.NO_BREAK_SPACE : value}
</Radio>
)) }
</Radio.Group>
</fieldset>
@ -534,7 +537,10 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element
className='cvat-object-item-select-attribute'
>
{ attrValues.map((value: string): JSX.Element => (
<Select.Option key={value} value={value}>{value}</Select.Option>
<Select.Option key={value} value={value}>
{value === consts.UNDEFINED_ATTRIBUTE_VALUE
? consts.NO_BREAK_SPACE : value}
</Select.Option>
)) }
</Select>
</Col>

@ -0,0 +1,11 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
const UNDEFINED_ATTRIBUTE_VALUE = '__undefined__';
const NO_BREAK_SPACE = '\u00a0';
export default {
UNDEFINED_ATTRIBUTE_VALUE,
NO_BREAK_SPACE,
};

@ -2,6 +2,6 @@
//
// SPDX-License-Identifier: MIT
export default function isDev() {
export default function isDev(): boolean {
return process.env.NODE_ENV === 'development';
}

@ -4,6 +4,6 @@
from cvat.utils.version import get_version
VERSION = (1, 0, 0, 'alpha', 0)
VERSION = (1, 0, 0, 'beta', 0)
__version__ = get_version(VERSION)

@ -293,7 +293,10 @@ class TrackManager(ObjectManager):
@staticmethod
def normalize_shape(shape):
points = np.asarray(shape["points"]).reshape(-1, 2)
points = list(shape["points"])
if len(points) == 2:
points.extend(points) # duplicate points for single point case
points = np.asarray(points).reshape(-1, 2)
broken_line = geometry.LineString(points)
points = []
for off in range(0, 100, 1):

@ -0,0 +1,39 @@
# Copyright (C) 2020 Intel Corporation
#
# SPDX-License-Identifier: MIT
from cvat.apps.engine.data_manager import TrackManager
from unittest import TestCase
class TrackManagerTest(TestCase):
def test_single_point_interpolation(self):
track = {
"frame": 0,
"label_id": 0,
"group": None,
"attributes": [],
"shapes": [
{
"frame": 0,
"points": [1.0, 2.0],
"type": "points",
"occluded": False,
"outside": False,
"attributes": []
},
{
"frame": 2,
"attributes": [],
"points": [3.0, 4.0, 5.0, 6.0],
"type": "points",
"occluded": False,
"outside": True
},
]
}
interpolated = TrackManager.get_interpolated_shapes(track, 0, 2)
self.assertEqual(len(interpolated), 3)
Loading…
Cancel
Save