Skip to content

Commit

Permalink
Merge branch 'develop' into bs/fixed_tracks
Browse files Browse the repository at this point in the history
  • Loading branch information
bsekachev authored May 27, 2024
2 parents 209c908 + f892bd4 commit 8aa3b9c
Showing 185 changed files with 4,719 additions and 3,952 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/schedule.yml
Original file line number Diff line number Diff line change
@@ -313,6 +313,13 @@ jobs:
name: cypress_screenshots
path: ${{ github.workspace }}/tests/cypress/screenshots

- name: Uploading cypress videos as an artifact
if: failure()
uses: actions/upload-artifact@v3.1.1
with:
name: cypress_videos_${{ matrix.specs }}
path: ${{ github.workspace }}/tests/cypress/videos

- name: Uploading "cvat" container logs as an artifact
if: failure()
uses: actions/upload-artifact@v3.1.1
55 changes: 55 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -16,6 +16,61 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

<!-- scriv-insert-here -->

<a id='changelog-2.14.0'></a>
## \[2.14.0\] - 2024-05-21

### Added

- Added security headers enforcing strict `Referrer-Policy` for cross origins and disabling MIME type sniffing via `X-Content-Type-Options`.
(<https://github.com/opencv/cvat/pull/7752>)

- \[Helm\] Ability to specify ServiceAccount for backend pods
(<https://github.com/cvat-ai/cvat/pull/7894>)

### Changed

- Working time rounding to a minimal value of 1 hour is not applied to the annotation speed metric any more
(<https://github.com/cvat-ai/cvat/pull/7898>)

- Total annotation speed metric renamed to Average annotation speed
(<https://github.com/cvat-ai/cvat/pull/7898>)

- Ground truth jobs are not considered when computing analytics report for a task/project
(<https://github.com/cvat-ai/cvat/pull/7919>)

### Fixed

- Fixed calculation of annotation speed metrics for analytics reports
(<https://github.com/opencv/cvat/pull/7144>)

- \[Helm\] Prevented spurious 200 OK responses from API endpoints
before the backend is ready
(<https://github.com/cvat-ai/cvat/pull/7859>)

- Analytic reports incorrect count of objects for a skeleton track/shape
(<https://github.com/cvat-ai/cvat/pull/7883>)

- Analytic reports incorrect number of objects for a track (always less by 1)
(<https://github.com/cvat-ai/cvat/pull/7883>)

- REST API allowed to create several attributes with the same name within one label
(<https://github.com/cvat-ai/cvat/pull/7890>)

- Job's/task's status are not updated when job's state updated to completed and stage is already acceptance
(<https://github.com/cvat-ai/cvat/pull/7901>)

- Exception: Cannot read properties of undefined (reading 'onBlockUpdated')
(<https://github.com/cvat-ai/cvat/pull/7913>)

- One more found way to create an empty mask
(<https://github.com/cvat-ai/cvat/pull/7915>)

- Slice function may not work in Google Chrome < 110
(<https://github.com/cvat-ai/cvat/pull/7916>)

- Selecting a skeleton by cursor does not work correctly when there are some hidden points
(<https://github.com/cvat-ai/cvat/pull/7921>)

<a id='changelog-2.13.0'></a>
## \[2.13.0\] - 2024-05-09

1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -249,7 +249,6 @@ questions and get our support.
- [How to Use CVAT (Roboflow guide)](https://blog.roboflow.com/cvat/)
- [How to auto-label data in CVAT with one of 50,000+ models on Roboflow Universe](https://blog.roboflow.com/how-to-use-roboflow-models-in-cvat/)

<!-- prettier-ignore-start -->
<!-- Badges -->

[docker-server-pulls-img]: https://img.shields.io/docker/pulls/cvat/server.svg?style=flat-square&label=server%20pulls
4 changes: 4 additions & 0 deletions changelog.d/20240509_160805_boris_upgrade_react.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Changed

- Upgraded React and Antd dependencies, it leads to stylistic changes in the user interface
(<https://github.com/cvat-ai/cvat/pull/7466>)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Fixed

- Fix missing serviceName field in kvrocks (issue #7741)
(<https://github.com/cvat-ai/cvat/pull/7924>)
4 changes: 4 additions & 0 deletions changelog.d/20240521_182333_klakhov_fix_conflicts_render.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Fixed

- UI crash on hovering conflict related to hidden objects
(<https://github.com/cvat-ai/cvat/pull/7917>)
2 changes: 1 addition & 1 deletion cvat-canvas/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-canvas",
"version": "2.20.1",
"version": "2.20.2",
"type": "module",
"description": "Part of Computer Vision Annotation Tool which presents its canvas library",
"main": "src/canvas.ts",
6 changes: 5 additions & 1 deletion cvat-canvas/src/typescript/canvasModel.ts
Original file line number Diff line number Diff line change
@@ -651,8 +651,12 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
}

public highlight(clientIDs: number[], severity: HighlightSeverity | null): void {
const elementsIDs = clientIDs.filter((id: number): boolean => (
this.objects.find((_state: any): boolean => _state.clientID === id)
));

this.data.highlightedElements = {
elementsIDs: clientIDs,
elementsIDs,
severity,
};

13 changes: 10 additions & 3 deletions cvat-canvas/src/typescript/masksHandler.ts
Original file line number Diff line number Diff line change
@@ -201,8 +201,8 @@ export class MasksHandlerImpl implements MasksHandler {
.reduce((acc: TwoCornerBox, rect: BoundingRect) => {
acc.top = Math.floor(Math.max(0, Math.min(rect.top, acc.top)));
acc.left = Math.floor(Math.max(0, Math.min(rect.left, acc.left)));
acc.bottom = Math.floor(Math.min(height, Math.max(rect.top + rect.height, acc.bottom)));
acc.right = Math.floor(Math.min(width, Math.max(rect.left + rect.width, acc.right)));
acc.bottom = Math.floor(Math.min(height - 1, Math.max(rect.top + rect.height, acc.bottom)));
acc.right = Math.floor(Math.min(width - 1, Math.max(rect.left + rect.width, acc.right)));
return acc;
}, {
left: Number.MAX_SAFE_INTEGER,
@@ -572,7 +572,14 @@ export class MasksHandlerImpl implements MasksHandler {
image.globalCompositeOperation = 'xor';
image.opacity = 0.5;
this.canvas.add(image);
this.drawnObjects.push(image);
/*
when we paste a mask, we do not need additional logic implemented
in MasksHandlerImpl::createDrawnObjectsArray.push using JS Proxy
because we will not work with any drawing tools here, and it will cause the issue
because this.tools may be undefined here
when it is used inside the push custom implementation
*/
this.drawnObjects = [image];
this.canvas.renderAll();
} finally {
resolve();
11 changes: 11 additions & 0 deletions cvat-canvas/src/typescript/shared.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (C) 2019-2022 Intel Corporation
// Copyright (C) 2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

@@ -535,5 +536,15 @@ export function segmentsFromPoints(points: number[], circuit = false): Segment[]
}, []);
}

export function toReversed<T>(array: Array<T>): Array<T> {
// actually toReversed already exists in ESMA specification
// but not all CVAT customers uses a browser fresh enough to use it
// instead of using a library with polyfills I will prefer just to rewrite it with reduceRight
return array.reduceRight<Array<T>>((acc, val: T) => {
acc.push(val);
return acc;
}, []);
}

export type Segment = [[number, number], [number, number]];
export type PropType<T, Prop extends keyof T> = T[Prop];
12 changes: 5 additions & 7 deletions cvat-canvas/src/typescript/sliceHandler.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright (C) 2023 CVAT.ai Corporation
// Copyright (C) 2023-2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import * as SVG from 'svg.js';
import {
stringifyPoints, translateToCanvas, translateFromCanvas, translateToSVG,
findIntersection, zipChannels, Segment, findClosestPointOnSegment, segmentsFromPoints,
toReversed,
} from './shared';
import {
Geometry, SliceData, Configuration, CanvasHint,
@@ -294,8 +295,7 @@ export class SliceHandlerImpl implements SliceHandler {
const d2 = Math.sqrt((p2[0] - p[0]) ** 2 + (p2[1] - p[1]) ** 2);

if (d2 > d1) {
// @ts-ignore error TS2551 (need to update typescript up to 5.2)
contour2.push(...otherPoints.toReversed().flat());
contour2.push(...toReversed<[number, number]>(otherPoints).flat());
} else {
contour2.push(...otherPoints.flat());
}
@@ -312,8 +312,7 @@ export class SliceHandlerImpl implements SliceHandler {
...firstSegmentPoint, // first intersection
// intermediate points (reversed if intersections order was swopped)
...(firstSegmentIdx === firstIntersectedSegmentIdx ?
// @ts-ignore error TS2551 (need to update typescript up to 5.2)
intermediatePoints : intermediatePoints.toReversed()
intermediatePoints : toReversed<[number, number]>(intermediatePoints)
).flat(),
// second intersection
...secondSegmentPoint,
@@ -326,8 +325,7 @@ export class SliceHandlerImpl implements SliceHandler {
...firstSegmentPoint, // first intersection
// intermediate points (reversed if intersections order was swopped)
...(firstSegmentIdx === firstIntersectedSegmentIdx ?
// @ts-ignore error TS2551 (need to update typescript up to 5.2)
intermediatePoints : intermediatePoints.toReversed()
intermediatePoints : toReversed<[number, number]>(intermediatePoints)
).flat(),
...secondSegmentPoint,
// all the previous contours points N, N-1, .. until (including) the first intersected segment
2 changes: 1 addition & 1 deletion cvat-cli/requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
cvat-sdk~=2.14.0
cvat-sdk~=2.15.0
Pillow>=10.3.0
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
2 changes: 1 addition & 1 deletion cvat-cli/src/cvat_cli/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = "2.14.0"
VERSION = "2.15.0"
2 changes: 1 addition & 1 deletion cvat-core/src/annotations-actions.ts
Original file line number Diff line number Diff line change
@@ -166,7 +166,7 @@ async function runSingleFrameChain(
// Ignore deleted frames
if (!frameData.deleted) {
// Get annotations according to filter
const states: ObjectState[] = await getAnnotations(instance, frame, false, filters, null);
const states: ObjectState[] = await getAnnotations(instance, frame, false, filters);
const frameCollectionIDs = states.reduce<IDsToHandle>((acc, val) => {
if (val.objectType === ObjectType.SHAPE) {
acc.shapes.push(val.clientID as number);
38 changes: 22 additions & 16 deletions cvat-core/src/annotations-collection.ts
Original file line number Diff line number Diff line change
@@ -116,7 +116,7 @@ export default class Collection {
};
}

import(data: Omit<SerializedCollection, 'version'>): ImportedCollection {
public import(data: Omit<SerializedCollection, 'version'>): ImportedCollection {
const result = {
tags: [],
shapes: [],
@@ -159,7 +159,7 @@ export default class Collection {
return result;
}

export(): Omit<SerializedCollection, 'version'> {
public export(): Omit<SerializedCollection, 'version'> {
const data = {
tracks: this.tracks.filter((track) => !track.removed).map((track) => track.toJSON() as SerializedTrack),
shapes: Object.values(this.shapes)
@@ -215,7 +215,7 @@ export default class Collection {
return objectStates;
}

_mergeInternal(objectsForMerge: (Track | Shape)[], shapeType: ShapeType, label: Label): SerializedTrack {
private _mergeInternal(objectsForMerge: (Track | Shape)[], shapeType: ShapeType, label: Label): SerializedTrack {
const keyframes: Record<number, SerializedTrack['shapes'][0]> = {}; // frame: position
const elements = {}; // element_sublabel_id: [element], each sublabel will be merged recursively

@@ -398,7 +398,7 @@ export default class Collection {
return track;
}

merge(objectStates: ObjectState[]): void {
public merge(objectStates: ObjectState[]): void {
checkObjectType('shapes to merge', objectStates, null, Array);
if (!objectStates.length) return;
const objectsForMerge = objectStates.map((state) => {
@@ -455,7 +455,7 @@ export default class Collection {
);
}

_splitInternal(objectState: ObjectState, object: Track, frame: number): SerializedTrack[] {
private _splitInternal(objectState: ObjectState, object: Track, frame: number): SerializedTrack[] {
const labelAttributes = labelAttributesAsDict(object.label);
// first clear all server ids which may exist in the object being splitted
const copy = trackFactory(object.toJSON(), -1, this.injection);
@@ -523,7 +523,7 @@ export default class Collection {
return [prev, next];
}

split(objectState: ObjectState, frame: number): void {
public split(objectState: ObjectState, frame: number): void {
checkObjectType('object state', objectState, null, ObjectState);
checkObjectType('frame', frame, 'integer', null);

@@ -564,7 +564,7 @@ export default class Collection {
);
}

group(objectStates: ObjectState[], reset: boolean): number {
public group(objectStates: ObjectState[], reset: boolean): number {
checkObjectType('shapes to group', objectStates, null, Array);

const objectsForGroup = objectStates.map((state) => {
@@ -605,7 +605,7 @@ export default class Collection {
return groupIdx;
}

join(objectStates: ObjectState[], points: number[]): void {
public join(objectStates: ObjectState[], points: number[]): void {
checkObjectType('shapes to join', objectStates, null, Array);
checkObjectType('joined rle mask', points, null, Array);

@@ -690,7 +690,7 @@ export default class Collection {
}
}

slice(state: ObjectState, results: number[][]): void {
public slice(state: ObjectState, results: number[][]): void {
if (results.length !== 2) {
throw new Error('Not supported slicing count');
}
@@ -774,7 +774,7 @@ export default class Collection {
);
}

clear(startframe: number, endframe: number, delTrackKeyframesOnly: boolean): void {
public clear(startframe: number, endframe: number, delTrackKeyframesOnly: boolean): void {
if (startframe !== undefined && endframe !== undefined) {
// If only a range of annotations need to be cleared
for (let frame = startframe; frame <= endframe; frame++) {
@@ -818,7 +818,7 @@ export default class Collection {
}
}

statistics(): Statistics {
public statistics(): Statistics {
const labels = {};
const shapes = ['rectangle', 'polygon', 'polyline', 'points', 'ellipse', 'cuboid', 'skeleton'];
const body = {
@@ -958,7 +958,7 @@ export default class Collection {
return new Statistics(labels, total);
}

put(objectStates: ObjectState[]): number[] {
public put(objectStates: ObjectState[]): number[] {
checkObjectType('shapes for put', objectStates, null, Array);
const constructed = {
shapes: [],
@@ -1149,7 +1149,7 @@ export default class Collection {
return importedArray.map((value) => value.clientID);
}

select(objectStates: ObjectState[], x: number, y: number): {
public select(objectStates: ObjectState[], x: number, y: number): {
state: ObjectState,
distance: number | null,
} {
@@ -1195,7 +1195,13 @@ export default class Collection {
throw new ArgumentError(`Unknown shape type "${state.shapeType}"`);
}

const distance = distanceMetric(state.points, x, y, state.rotation);
let points = [];
if (state.shapeType === ShapeType.SKELETON) {
points = state.elements.filter((el) => !el.outside && !el.hidden).map((el) => el.points).flat();
} else {
points = state.points;
}
const distance = distanceMetric(points, x, y, state.rotation);
if (distance !== null && (minimumDistance === null || distance < minimumDistance)) {
minimumDistance = distance;
minimumState = state;
@@ -1208,7 +1214,7 @@ export default class Collection {
};
}

_searchEmpty(
private _searchEmpty(
frameFrom: number,
frameTo: number,
searchParameters: {
@@ -1254,7 +1260,7 @@ export default class Collection {
return null;
}

search(
public search(
frameFrom: number,
frameTo: number,
searchParameters: {
3 changes: 1 addition & 2 deletions cvat-core/src/annotations-objects.ts
Original file line number Diff line number Diff line change
@@ -1977,8 +1977,7 @@ export class SkeletonShape extends Shape {
return null;
}

// The shortest distance from point to an edge
return Math.min.apply(null, [x - xtl, y - ytl, xbr - x, ybr - y]);
return Math.min.apply(null, distances);
}

// Method is used to export data to the server
2 changes: 1 addition & 1 deletion cvat-sdk/gen/generate.sh
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ set -e

GENERATOR_VERSION="v6.0.1"

VERSION="2.14.0"
VERSION="2.15.0"
LIB_NAME="cvat_sdk"
LAYER1_LIB_NAME="${LIB_NAME}/api_client"
DST_DIR="$(cd "$(dirname -- "$0")/.." && pwd)"
Loading

0 comments on commit 8aa3b9c

Please sign in to comment.