Skip to content

Commit

Permalink
Merge pull request #7926 from cvat-ai/release-2.14.0
Browse files Browse the repository at this point in the history
Release v2.14.0
  • Loading branch information
cvat-bot[bot] authored May 22, 2024
2 parents 9dcba6a + 23a4bd8 commit 24facf5
Show file tree
Hide file tree
Showing 46 changed files with 464 additions and 202 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/schedule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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/[email protected]
with:
name: cypress_videos_${{ matrix.specs }}
path: ${{ github.workspace }}/tests/cypress/videos

- name: Uploading "cvat" container logs as an artifact
if: failure()
uses: actions/[email protected]
Expand Down
55 changes: 55 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
13 changes: 10 additions & 3 deletions cvat-canvas/src/typescript/masksHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down
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

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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());
}
Expand All @@ -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,
Expand All @@ -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
Expand Down
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.13.0
cvat-sdk~=2.14.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.13.0"
VERSION = "2.14.0"
38 changes: 22 additions & 16 deletions cvat-core/src/annotations-collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [],
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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');
}
Expand Down Expand Up @@ -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++) {
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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: [],
Expand Down Expand Up @@ -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,
} {
Expand Down Expand Up @@ -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;
Expand All @@ -1208,7 +1214,7 @@ export default class Collection {
};
}

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

search(
public search(
frameFrom: number,
frameTo: number,
searchParameters: {
Expand Down
3 changes: 1 addition & 2 deletions cvat-core/src/annotations-objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1936,8 +1936,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
Expand Down
2 changes: 1 addition & 1 deletion cvat-sdk/gen/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ set -e

GENERATOR_VERSION="v6.0.1"

VERSION="2.13.0"
VERSION="2.14.0"
LIB_NAME="cvat_sdk"
LAYER1_LIB_NAME="${LIB_NAME}/api_client"
DST_DIR="$(cd "$(dirname -- "$0")/.." && pwd)"
Expand Down
2 changes: 2 additions & 0 deletions cvat-ui/react_nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ server {
add_header Cross-Origin-Embedder-Policy "credentialless";
add_header Expires 0;
add_header X-Frame-Options "deny";
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Content-Type-Options "nosniff" always;
}

location /assets {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {

const result = await jobInstance.annotations.select(event.detail.states, event.detail.x, event.detail.y);
if (result && result.state) {
if (['polyline', 'points'].includes(result.state.shapeType)) {
if ([ShapeType.POLYLINE, ShapeType.POINTS].includes(result.state.shapeType)) {
if (result.distance > MAX_DISTANCE_TO_OPEN_SHAPE) {
return;
}
Expand Down
8 changes: 8 additions & 0 deletions cvat-ui/src/reducers/tasks-reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BoundariesActionTypes } from 'actions/boundaries-actions';
import { TasksActionTypes } from 'actions/tasks-actions';
import { AuthActionTypes } from 'actions/auth-actions';

import { ProjectsActionTypes } from 'actions/projects-actions';
import { TasksState } from '.';

const defaultState: TasksState = {
Expand Down Expand Up @@ -77,6 +78,13 @@ export default (state: TasksState = defaultState, action: AnyAction): TasksState
initialized: true,
fetching: false,
};
case ProjectsActionTypes.DELETE_PROJECT_SUCCESS: {
const { projectId } = action.payload;
return {
...state,
current: state.current.filter((_task) => _task.projectId !== projectId),
};
}
case TasksActionTypes.DELETE_TASK: {
const { taskID } = action.payload;
const { deletes } = state.activities;
Expand Down
2 changes: 1 addition & 1 deletion cvat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

from cvat.utils.version import get_version

VERSION = (2, 13, 0, 'final', 0)
VERSION = (2, 14, 0, 'final', 0)

__version__ = get_version(VERSION)
Loading

0 comments on commit 24facf5

Please sign in to comment.