Skip to content

Commit

Permalink
Added rotated bounding boxes (#3832)
Browse files Browse the repository at this point in the history
Co-authored-by: Maxim Zhiltsov <[email protected]>
  • Loading branch information
Boris Sekachev and Maxim Zhiltsov authored Nov 18, 2021
1 parent 14262fa commit 7bab58e
Show file tree
Hide file tree
Showing 52 changed files with 1,079 additions and 1,999 deletions.
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

0 comments on commit 7bab58e

Please sign in to comment.