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 rotated bounding boxes #3832

Merged
merged 50 commits into from
Nov 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
aacfe4e
Client part for rotated boxes
bsekachev Oct 20, 2021
85ee87e
Fixed several issues with coordinate system
bsekachev Oct 20, 2021
13c565b
Removed extra code
bsekachev Oct 20, 2021
56ec47b
Fixed one more bug when shape goes outside
bsekachev Oct 20, 2021
78f7332
Fixed selectable points stroke
bsekachev Oct 20, 2021
bf42066
Fixed text position for not rotated bounding boxes
bsekachev Oct 20, 2021
3e14d81
Added server part [draft]
bsekachev Oct 26, 2021
9728c0c
Redesigned interpolation, added additional precise to angle
bsekachev Oct 28, 2021
508c9dc
Fixed data type, added additional check
bsekachev Oct 28, 2021
526d193
Added message, fixed styles
bsekachev Oct 28, 2021
4a436db
Fixed styles
bsekachev Oct 28, 2021
15584cf
Removed unused import
bsekachev Oct 28, 2021
96da4c5
Merged develop
bsekachev Oct 29, 2021
116f912
Fixed styles
bsekachev Oct 29, 2021
0809a53
Removed extra blank lines
bsekachev Oct 29, 2021
8f16667
Changed type: int -> float
bsekachev Oct 29, 2021
57b3aa9
Fixed a couple of issues
bsekachev Oct 29, 2021
566c90e
Added split/merge for rotated shapes, updated tests
bsekachev Oct 29, 2021
dd22471
Added rotation to mot format
bsekachev Oct 29, 2021
5b53143
Fixed one cypress test
bsekachev Oct 29, 2021
dbbeafd
Added missed arguments
bsekachev Oct 29, 2021
31c9ead
Rotation field added to tests
bsekachev Oct 29, 2021
93a31e4
Added missed fields to tests
bsekachev Oct 29, 2021
c4a9d08
Revedsed velodyne points
bsekachev Nov 1, 2021
7cd6a2a
Added rotation tests
bsekachev Nov 2, 2021
43d55ce
Updated version & changelog
bsekachev Nov 2, 2021
8d30527
Update cvat/apps/dataset_manager/annotation.py
Nov 3, 2021
81d1790
Added rotation angle
bsekachev Nov 15, 2021
954d280
Removed current migration file
bsekachev Nov 15, 2021
ad8d003
Merge branch 'develop' into bs/rotated_boxes
bsekachev Nov 15, 2021
f7a44c6
Reworked migration
bsekachev Nov 15, 2021
f851019
Merge branch 'bs/rotated_boxes' of github.com:openvinotoolkit/cvat in…
bsekachev Nov 15, 2021
df59173
Added angle visualization
bsekachev Nov 15, 2021
87d5908
Added default values, export attr/mask
bsekachev Nov 15, 2021
d080706
Removed unnesassary changes
bsekachev Nov 15, 2021
1baefbf
Updated changelog
bsekachev Nov 15, 2021
9041473
Merge branch 'develop' into bs/rotated_boxes
Nov 15, 2021
980e1ac
Reworked transformation
bsekachev Nov 16, 2021
0ef2eb2
Fixed linter
bsekachev Nov 16, 2021
f863440
Added transformation to multiple formats
bsekachev Nov 16, 2021
65b76a0
Copy list instead of working with source
bsekachev Nov 16, 2021
e6c0659
Added snap to angle 15 degree
bsekachev Nov 17, 2021
600fe67
Fixed issues with destroy
bsekachev Nov 17, 2021
2fe257d
Fixed some issues with rotation
bsekachev Nov 17, 2021
f6154c8
Fixed tests
bsekachev Nov 17, 2021
71f2ee2
Changed 'key' to 'code'
bsekachev Nov 17, 2021
f74bc1e
Fixed missed keyup arguments
bsekachev Nov 17, 2021
be8fed9
Added hint
bsekachev Nov 17, 2021
e610b89
Fixed firefox issues, fixed bitmap
bsekachev Nov 17, 2021
87d3eef
Fixed issue with outside/occluded/keyframe
bsekachev Nov 17, 2021
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
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@
"type": "node",
"request": "launch",
"name": "jest debug",
"program": "${workspaceFolder}/cvat-core/node_modules/.bin/jest",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": [
"--config",
"${workspaceFolder}/cvat-core/jest.config.js"
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add LFW format (<https://github.com/openvinotoolkit/cvat/pull/3770>)
- Add Cityscapes format (<https://github.com/openvinotoolkit/cvat/pull/3758>)
- Add Open Images V6 format (<https://github.com/openvinotoolkit/cvat/pull/3679>)
- Rotated bounding boxes (<https://github.com/openvinotoolkit/cvat/pull/3832>)

### Changed
- TDB
Expand Down
9 changes: 7 additions & 2 deletions cvat-canvas/src/scss/canvas.scss
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ polyline.cvat_canvas_shape_splitting {
stroke-dasharray: 5;
}

.svg_select_points_rot {
fill: white;
}

.cvat_canvas_shape .svg_select_points,
.cvat_canvas_shape .cvat_canvas_cuboid_projections {
stroke-dasharray: none;
Expand Down Expand Up @@ -166,8 +170,9 @@ polyline.cvat_canvas_shape_splitting {

.cvat_canvas_removable_interaction_point {
cursor:
url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAxMCAxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEgMUw5IDlNMSA5TDkgMSIgc3Ryb2tlPSJibGFjayIvPgo8L3N2Zz4K')
10 10,
url(
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAxMCAxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEgMUw5IDlNMSA5TDkgMSIgc3Ryb2tlPSJibGFjayIvPgo8L3N2Zz4K'
) 10 10,
auto;
}

Expand Down
233 changes: 178 additions & 55 deletions cvat-canvas/src/typescript/canvasView.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions cvat-canvas/src/typescript/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const UNDEFINED_ATTRIBUTE_VALUE = '__undefined__';
const ARROW_PATH = 'M13.162 6.284L.682.524a.483.483 0 0 0-.574.134.477.477 0 ' +
'0 0-.012.59L4.2 6.72.096 12.192a.479.479 0 0 0 .585.724l12.48-5.76a.48.48 0 0 0 0-.872z';
const BASE_PATTERN_SIZE = 5;
const SNAP_TO_ANGLE_RESIZE_DEFAULT = 0.1;
const SNAP_TO_ANGLE_RESIZE_SHIFT = 15;

export default {
BASE_STROKE_WIDTH,
Expand All @@ -33,4 +35,6 @@ export default {
UNDEFINED_ATTRIBUTE_VALUE,
ARROW_PATH,
BASE_PATTERN_SIZE,
SNAP_TO_ANGLE_RESIZE_DEFAULT,
SNAP_TO_ANGLE_RESIZE_SHIFT,
};
29 changes: 8 additions & 21 deletions cvat-canvas/src/typescript/interactionHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,25 @@ export interface InteractionHandler {
transform(geometry: Geometry): void;
interact(interactData: InteractionData): void;
configurate(config: Configuration): void;
destroy(): void;
cancel(): void;
}

export class InteractionHandlerImpl implements InteractionHandler {
private onInteraction: (shapes: InteractionResult[] | null, shapesUpdated?: boolean, isDone?: boolean) => void;

private configuration: Configuration;

private geometry: Geometry;

private canvas: SVG.Container;

private interactionData: InteractionData;

private cursorPosition: { x: number; y: number };

private shapesWereUpdated: boolean;

private interactionShapes: SVG.Shape[];

private currentInteractionShape: SVG.Shape | null;

private crosshair: Crosshair;

private threshold: SVG.Rect | null;

private thresholdRectSize: number;

private intermediateShape: PropType<InteractionData, 'intermediateShape'>;

private drawnIntermediateShape: SVG.Shape;

private thresholdWasModified: boolean;

private prepareResult(): InteractionResult[] {
Expand Down Expand Up @@ -473,13 +460,8 @@ export class InteractionHandlerImpl implements InteractionHandler {
}
});

window.addEventListener('keyup', this.onKeyUp);
window.addEventListener('keydown', this.onKeyDown);

this.canvas.on('destroy.canvas', ():void => {
window.removeEventListener('keyup', this.onKeyUp);
window.removeEventListener('keydown', this.onKeyDown);
});
window.document.addEventListener('keyup', this.onKeyUp);
window.document.addEventListener('keydown', this.onKeyDown);
}

public transform(geometry: Geometry): void {
Expand Down Expand Up @@ -560,4 +542,9 @@ export class InteractionHandlerImpl implements InteractionHandler {
this.release();
this.onInteraction(null);
}

public destroy(): void {
window.document.removeEventListener('keyup', this.onKeyUp);
window.document.removeEventListener('keydown', this.onKeyDown);
}
}
44 changes: 38 additions & 6 deletions cvat-canvas/src/typescript/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface DrawnState {
source: 'AUTO' | 'MANUAL';
shapeType: string;
points?: number[];
rotation: number;
attributes: Record<number, string>;
descriptions: string[];
zOrder?: number;
Expand Down Expand Up @@ -95,16 +96,30 @@ export function displayShapeSize(shapesContainer: SVG.Container, textContainer:
.fill('white')
.addClass('cvat_canvas_text'),
update(shape: SVG.Shape): void {
const bbox = shape.bbox();
const text = `${bbox.width.toFixed(1)}x${bbox.height.toFixed(1)}`;
const [x, y]: number[] = translateToSVG(
let text = `${Math.round(shape.width())}x${Math.round(shape.height())}px`;
if (shape.type === 'rect') {
let rotation = shape.transform().rotation || 0;
// be sure, that rotation in range [0; 360]
while (rotation < 0) rotation += 360;
rotation %= 360;
if (rotation) {
text = `${text} ${rotation.toFixed(1)}\u00B0`;
}
}
const [x, y, cx, cy]: number[] = translateToSVG(
(textContainer.node as any) as SVGSVGElement,
translateFromSVG((shapesContainer.node as any) as SVGSVGElement, [bbox.x, bbox.y]),
);
translateFromSVG((shapesContainer.node as any) as SVGSVGElement, [
shape.x(),
shape.y(),
shape.cx(),
shape.cy(),
]),
).map((coord: number): number => Math.round(coord));
this.sizeElement
.clear()
.plain(text)
.move(x + consts.TEXT_MARGIN, y + consts.TEXT_MARGIN);
.move(x + consts.TEXT_MARGIN, y + consts.TEXT_MARGIN)
.rotate(shape.transform().rotation, cx, cy);
},
rm(): void {
if (this.sizeElement) {
Expand All @@ -117,6 +132,23 @@ export function displayShapeSize(shapesContainer: SVG.Container, textContainer:
return shapeSize;
}

export function rotate2DPoints(cx: number, cy: number, angle: number, points: number[]): number[] {
const rad = (Math.PI / 180) * angle;
const cos = Math.cos(rad);
const sin = Math.sin(rad);
const result = [];
for (let i = 0; i < points.length; i += 2) {
const x = points[i];
const y = points[i + 1];
result.push(
(x - cx) * cos - (y - cy) * sin + cx,
(y - cy) * cos + (x - cx) * sin + cy,
);
}

return result;
}

export function pointsToNumberArray(points: string | Point[]): number[] {
if (Array.isArray(points)) {
return points.reduce((acc: number[], point: Point): number[] => {
Expand Down
15 changes: 10 additions & 5 deletions cvat-canvas/src/typescript/svg.patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,23 @@ SVG.Element.prototype.resize = function constructor(...args: any): any {
handler = this.remember('_resizeHandler');
handler.resize = function (e: any) {
const { event } = e.detail;
this.rotationPointPressed = e.type === 'rot';
if (
event.button === 0 &&
// ignore shift key for cuboid change perspective
(!event.shiftKey || this.el.parent().hasClass('cvat_canvas_shape_cuboid')) &&
!event.altKey
// ignore shift key for cuboids (change perspective) and rectangles (precise rotation)
(!event.shiftKey || (
this.el.parent().hasClass('cvat_canvas_shape_cuboid')
|| this.el.type === 'rect')
) && !event.altKey
) {
return handler.constructor.prototype.resize.call(this, e);
}
};
handler.update = function (e: any) {
this.m = this.el.node.getScreenCTM().inverse();
return handler.constructor.prototype.update.call(this, e);
if (!this.rotationPointPressed) {
this.m = this.el.node.getScreenCTM().inverse();
}
handler.constructor.prototype.update.call(this, e);
};
} else {
originalResize.call(this, ...args);
Expand Down
4 changes: 2 additions & 2 deletions 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.17.0",
"version": "3.19.0",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "babel.config.js",
"scripts": {
Expand Down
13 changes: 11 additions & 2 deletions cvat-core/src/annotations-collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@
const object = this.objects[state.clientID];
if (typeof object === 'undefined') {
throw new ArgumentError(
'The object has not been saved yet. Call ObjectState.put([state]) before you can merge it',
'The object is not in collection yet. Call ObjectState.put([state]) before you can merge it',
);
}
return object;
Expand Down Expand Up @@ -282,6 +282,7 @@
frame: object.frame,
points: [...object.points],
occluded: object.occluded,
rotation: object.rotation,
zOrder: object.zOrder,
outside: false,
attributes: Object.keys(object.attributes).reduce((accumulator, attrID) => {
Expand Down Expand Up @@ -333,6 +334,7 @@
type: shapeType,
frame: +keyframe,
points: [...shape.points],
rotation: shape.rotation,
occluded: shape.occluded,
outside: shape.outside,
zOrder: shape.zOrder,
Expand Down Expand Up @@ -442,6 +444,7 @@
const position = {
type: objectState.shapeType,
points: [...objectState.points],
rotation: objectState.rotation,
occluded: objectState.occluded,
outside: objectState.outside,
zOrder: objectState.zOrder,
Expand Down Expand Up @@ -481,6 +484,12 @@
return shape;
});
prev.shapes.push(position);

// add extra keyframe if no other keyframes before outside
if (!prev.shapes.some((shape) => shape.frame === frame - 1)) {
prev.shapes.push(JSON.parse(JSON.stringify(position)));
prev.shapes[prev.shapes.length - 2].frame -= 1;
}
prev.shapes[prev.shapes.length - 1].outside = true;

let clientID = ++this.count;
Expand Down Expand Up @@ -844,7 +853,7 @@
if (typeof object === 'undefined') {
throw new ArgumentError('The object has not been saved yet. Call annotations.put([state]) before');
}
const distance = object.constructor.distance(state.points, x, y);
const distance = object.constructor.distance(state.points, x, y, state.rotation);
if (distance !== null && (minimumDistance === null || distance < minimumDistance)) {
minimumDistance = distance;
minimumState = state;
Expand Down
Loading