Skip to content

Commit 140301a

Browse files
Merge pull request #9164 from cvat-ai/release-2.31.0
Release v2.31.0
2 parents 552bd53 + d3c7766 commit 140301a

File tree

231 files changed

+16216
-3905
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

231 files changed

+16216
-3905
lines changed

.gitignore

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
# Project Specific
22
/data/
3-
/models/
3+
/events/
44
/share/
55
/static/
66
/db.sqlite3
77
/keys
88
/logs
99
/profiles
10-
/ssh/*
11-
!/ssh/README.md
12-
/Mask_RCNN/
13-
/letsencrypt-webroot/
1410

1511
# Ignore temporary files
1612
docker-compose.override.yml
@@ -63,8 +59,6 @@ cvat-canvas/dist
6359
cvat-canvas3d/dist
6460
cvat-ui/dist
6561

66-
# produced by npm run docs in cvat-core
67-
cvat-core/docs
6862
# produced by prepare in the root package.json script
6963
.husky
7064

.vscode/launch.json

+43
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,26 @@
125125
],
126126
"justMyCode": false,
127127
},
128+
{
129+
"name": "REST API tests: Attach to RQ consensus worker",
130+
"type": "debugpy",
131+
"request": "attach",
132+
"connect": {
133+
"host": "127.0.0.1",
134+
"port": 9096
135+
},
136+
"pathMappings": [
137+
{
138+
"localRoot": "${workspaceFolder}",
139+
"remoteRoot": "/home/django/"
140+
},
141+
{
142+
"localRoot": "${workspaceFolder}/.env",
143+
"remoteRoot": "/opt/venv",
144+
}
145+
],
146+
"justMyCode": false,
147+
},
128148
{
129149
"type": "pwa-chrome",
130150
"request": "launch",
@@ -383,6 +403,28 @@
383403
},
384404
"console": "internalConsole"
385405
},
406+
{
407+
"name": "server: RQ - consensus",
408+
"type": "debugpy",
409+
"request": "launch",
410+
"stopOnEntry": false,
411+
"justMyCode": false,
412+
"python": "${command:python.interpreterPath}",
413+
"program": "${workspaceRoot}/manage.py",
414+
"args": [
415+
"rqworker",
416+
"consensus",
417+
"--worker-class",
418+
"cvat.rqworker.SimpleWorker"
419+
],
420+
"django": true,
421+
"cwd": "${workspaceFolder}",
422+
"env": {
423+
"DJANGO_LOG_SERVER_HOST": "localhost",
424+
"DJANGO_LOG_SERVER_PORT": "8282"
425+
},
426+
"console": "internalConsole"
427+
},
386428
{
387429
"name": "server: migrate",
388430
"type": "debugpy",
@@ -566,6 +608,7 @@
566608
"server: RQ - analytics reports",
567609
"server: RQ - cleaning",
568610
"server: RQ - chunks",
611+
"server: RQ - consensus",
569612
]
570613
}
571614
]

CHANGELOG.md

+51
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616

1717
<!-- scriv-insert-here -->
1818

19+
<a id='changelog-2.31.0'></a>
20+
## \[2.31.0\] - 2025-03-03
21+
22+
### Added
23+
24+
- \[SDK\] Auto-annotation detection functions can now output shape/keypoint attributes
25+
(<https://github.com/cvat-ai/cvat/pull/9090>)
26+
27+
- \[SDK\] Added a utility module for working with label attributes,
28+
`cvat_sdk.attributes`
29+
(<https://github.com/cvat-ai/cvat/pull/9090>)
30+
31+
- Simple merging for consensus-enabled tasks
32+
(<https://github.com/cvat-ai/cvat/pull/8953>)
33+
34+
- A setting to display rectangles and ellipses dimensions and rotation
35+
(<https://github.com/cvat-ai/cvat/pull/9142>)
36+
37+
### Changed
38+
39+
- Hidden points in skeletons now also contribute to the skeleton similarity
40+
in quality computations and in consensus merging
41+
(<https://github.com/cvat-ai/cvat/pull/8953>)
42+
43+
- SDK `task.upload_data()` can accept resources of the `Path` type
44+
when `resource_type` is `REMOTE` or `SHARE`
45+
(<https://github.com/cvat-ai/cvat/pull/9114>)
46+
47+
### Deprecated
48+
49+
- Utilizing `PUT /api/tasks|jobs/id/annotations?rq_id=rq_id` API endpoint
50+
to check the status of the import process
51+
(<https://github.com/cvat-ai/cvat/pull/9102>)
52+
53+
### Fixed
54+
55+
- 500 status code returned by API endpoints that support TUS OPTIONS requests
56+
(<https://github.com/cvat-ai/cvat/pull/9077>)
57+
58+
- Possible race condition that could occur when importing annotations
59+
(<https://github.com/cvat-ai/cvat/pull/9102>)
60+
61+
- Issue label scaling on image filter application
62+
(<https://github.com/cvat-ai/cvat/pull/9126>)
63+
64+
- Invalid display of images in simple GT jobs
65+
(<https://github.com/cvat-ai/cvat/pull/9155>)
66+
67+
- Related images in a simple GT jobs are displayed incorrectly
68+
(<https://github.com/cvat-ai/cvat/pull/9162>)
69+
1970
<a id='changelog-2.30.0'></a>
2071
## \[2.30.0\] - 2025-02-14
2172

Dockerfile

+1-2
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ RUN --mount=type=cache,target=/root/.cache/pip/http-v2 \
8585
-r /tmp/cvat/requirements/${CVAT_CONFIGURATION}.txt \
8686
-w /tmp/wheelhouse
8787

88-
FROM golang:1.23.0 AS build-smokescreen
88+
FROM golang:1.24.0 AS build-smokescreen
8989

9090
RUN git clone --filter=blob:none --no-checkout https://github.com/stripe/smokescreen.git
9191
RUN cd smokescreen && git checkout eb1ac09 && go build -o /tmp/smokescreen
@@ -192,7 +192,6 @@ RUN python -m pip uninstall -y pip
192192

193193
# Install and initialize CVAT, copy all necessary files
194194
COPY cvat/nginx.conf /etc/nginx/nginx.conf
195-
COPY --chown=${USER} components /tmp/components
196195
COPY --chown=${USER} supervisord/ ${HOME}/supervisord
197196
COPY --chown=${USER} manage.py backend_entrypoint.sh wait_for_deps.sh ${HOME}/
198197
COPY --chown=${USER} utils/ ${HOME}/utils

Dockerfile.ci

-18
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,9 @@ USER root
55

66
RUN apt-get update && \
77
DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends install -yq \
8-
gpg-agent \
9-
gnupg2 \
10-
apt-utils \
118
build-essential \
129
python3-dev \
13-
ruby \
1410
&& \
15-
curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
16-
echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list && \
17-
curl https://deb.nodesource.com/setup_20.x | bash - && \
18-
DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends install -yq \
19-
google-chrome-stable \
20-
nodejs \
21-
&& \
22-
npm install --global yarn && \
2311
rm -rf /var/lib/apt/lists/*;
2412

2513
COPY cvat/requirements/ /tmp/cvat/requirements/
@@ -28,12 +16,6 @@ COPY utils/dataset_manifest/requirements.txt /tmp/utils/dataset_manifest/require
2816
RUN python3 -m ensurepip
2917
RUN DATUMARO_HEADLESS=1 python3 -m pip install --no-cache-dir -r /tmp/cvat/requirements/testing.txt
3018

31-
COPY cvat-core ${HOME}/cvat-core
32-
COPY cvat-data ${HOME}/cvat-data
33-
COPY package.json ${HOME}/
34-
COPY yarn.lock ${HOME}/
35-
COPY tests ${HOME}/tests
36-
3719
COPY .coveragerc .
3820

3921
ENTRYPOINT []

Dockerfile.ui

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ DISABLE_SOURCE_MAPS="${DISABLE_SOURCE_MAPS}" \
3434
UI_APP_CONFIG="${UI_APP_CONFIG}" \
3535
SOURCE_MAPS_TOKEN="${SOURCE_MAPS_TOKEN}" yarn run build:cvat-ui
3636

37-
FROM nginx:1.26.1-alpine3.19-slim
37+
FROM nginx:1.27.4-alpine3.21-slim
3838

3939
# Replace default.conf configuration to remove unnecessary rules
4040
COPY cvat-ui/react_nginx.conf /etc/nginx/conf.d/default.conf

cvat-canvas/src/scss/canvas.scss

+5-2
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,15 @@ polyline.cvat_shape_drawing_opacity {
4848
font-weight: bold;
4949
fill: white;
5050
cursor: default;
51-
font-family: Calibri, Candara, Segoe, 'Segoe UI', Optima, Arial, sans-serif;
52-
text-shadow: 0 0 4px black;
51+
filter: drop-shadow(1px 1px 1px black) drop-shadow(-1px -1px 1px black);
5352
user-select: none;
5453
pointer-events: none;
5554
}
5655

56+
.cvat_canvas_text_dimensions {
57+
fill: lightskyblue;
58+
}
59+
5760
.cvat_canvas_text_description {
5861
fill: yellow;
5962
font-style: oblique 40deg;

cvat-canvas/src/typescript/canvasModel.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -457,18 +457,18 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
457457

458458
const { angle } = this.data;
459459

460-
const mutiplier = Math.sin((angle * Math.PI) / 180) + Math.cos((angle * Math.PI) / 180);
460+
const multiplier = Math.sin((angle * Math.PI) / 180) + Math.cos((angle * Math.PI) / 180);
461461
if ((angle / 90) % 2) {
462462
// 90, 270, ..
463463
const topMultiplier = (x - this.data.imageSize.width / 2) * (oldScale / this.data.scale - 1);
464464
const leftMultiplier = (y - this.data.imageSize.height / 2) * (oldScale / this.data.scale - 1);
465-
this.data.top += mutiplier * topMultiplier * this.data.scale;
466-
this.data.left -= mutiplier * leftMultiplier * this.data.scale;
465+
this.data.top += multiplier * topMultiplier * this.data.scale;
466+
this.data.left -= multiplier * leftMultiplier * this.data.scale;
467467
} else {
468468
const leftMultiplier = (x - this.data.imageSize.width / 2) * (oldScale / this.data.scale - 1);
469469
const topMultiplier = (y - this.data.imageSize.height / 2) * (oldScale / this.data.scale - 1);
470-
this.data.left += mutiplier * leftMultiplier * this.data.scale;
471-
this.data.top += mutiplier * topMultiplier * this.data.scale;
470+
this.data.left += multiplier * leftMultiplier * this.data.scale;
471+
this.data.top += multiplier * topMultiplier * this.data.scale;
472472
}
473473

474474
this.notify(UpdateReasons.IMAGE_ZOOMED);
@@ -590,7 +590,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
590590
};
591591

592592
this.data.image = data;
593-
this.fit();
593+
this.resetScale();
594594

595595
// restore correct image position after switching to a new frame
596596
// if corresponding option is disabled
@@ -684,7 +684,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
684684
this.notify(UpdateReasons.SHAPE_FOCUSED);
685685
}
686686

687-
public fit(): void {
687+
private resetScale(): boolean {
688688
const { angle } = this.data;
689689

690690
let updatedScale = this.data.scale;
@@ -713,6 +713,14 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
713713
// scale is changed during zooming or translating
714714
// so, remember fitted scale to compute fit-relative scaling
715715
this.data.fittedScale = this.data.scale;
716+
return true;
717+
}
718+
719+
return false;
720+
}
721+
722+
public fit(): void {
723+
if (this.resetScale()) {
716724
this.notify(UpdateReasons.IMAGE_FITTED);
717725
}
718726
}
@@ -939,7 +947,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
939947

940948
if (typeof configuration.textContent === 'string') {
941949
const splitted = configuration.textContent.split(',').filter((entry: string) => !!entry);
942-
if (splitted.every((entry: string) => ['id', 'label', 'attributes', 'source', 'descriptions'].includes(entry))) {
950+
if (splitted.every((entry: string) => ['id', 'label', 'attributes', 'source', 'descriptions', 'dimensions'].includes(entry))) {
943951
this.data.configuration.textContent = configuration.textContent;
944952
}
945953
}

cvat-canvas/src/typescript/canvasView.ts

+36-11
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
vectorLength, ShapeSizeElement, DrawnState, rotate2DPoints,
3333
readPointsFromShape, setupSkeletonEdges, makeSVGFromTemplate,
3434
imageDataToDataURL, expandChannels, stringifyPoints, zipChannels,
35+
composeShapeDimensions,
3536
} from './shared';
3637
import {
3738
CanvasModel, Geometry, UpdateReasons, FrameZoom, ActiveElement,
@@ -2444,10 +2445,10 @@ export class CanvasViewImpl implements CanvasView, Listener {
24442445
shape.untransform();
24452446
}
24462447

2447-
if (
2448-
state.points.length !== drawnState.points.length ||
2449-
state.points.some((p: number, id: number): boolean => p !== drawnState.points[id])
2450-
) {
2448+
const pointsUpdated = state.points.length !== drawnState.points.length ||
2449+
state.points.some((p: number, id: number): boolean => p !== drawnState.points[id]);
2450+
2451+
if (pointsUpdated) {
24512452
if (state.shapeType === 'mask') {
24522453
// if masks points were updated, draw from scratch
24532454
this.deleteObjects([this.drawnStates[+clientID]]);
@@ -2493,9 +2494,12 @@ export class CanvasViewImpl implements CanvasView, Listener {
24932494

24942495
const stateDescriptions = state.descriptions;
24952496
const drawnStateDescriptions = drawnState.descriptions;
2497+
const rotationUpdated = drawnState.rotation !== state.rotation;
24962498

24972499
if (
24982500
drawnState.label.id !== state.label.id ||
2501+
pointsUpdated ||
2502+
rotationUpdated ||
24992503
drawnStateDescriptions.length !== stateDescriptions.length ||
25002504
drawnStateDescriptions.some((desc: string, id: number): boolean => desc !== stateDescriptions[id])
25012505
) {
@@ -3090,6 +3094,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
30903094
const withLabel = content.includes('label');
30913095
const withSource = content.includes('source');
30923096
const withDescriptions = content.includes('descriptions');
3097+
const withDimensions = content.includes('dimensions');
30933098
const textFontSize = this.configuration.textFontSize || 12;
30943099
const {
30953100
label, clientID, attributes, source, descriptions,
@@ -3116,30 +3121,50 @@ export class CanvasViewImpl implements CanvasView, Listener {
31163121
`${withSource ? `(${source})` : ''}`).style({
31173122
'text-transform': 'uppercase',
31183123
});
3124+
3125+
if (withDimensions && ['rectangle', 'ellipse'].includes(state.shapeType)) {
3126+
let width = state.points[2] - state.points[0];
3127+
let height = state.points[3] - state.points[1];
3128+
3129+
if (state.shapeType === 'ellipse') {
3130+
width *= 2;
3131+
height *= -2;
3132+
}
3133+
3134+
block
3135+
.tspan(composeShapeDimensions(width, height, state.rotation))
3136+
.attr({
3137+
dy: '1.25em',
3138+
x: 0,
3139+
})
3140+
.addClass('cvat_canvas_text_dimensions');
3141+
}
31193142
if (withDescriptions) {
3120-
for (const desc of descriptions) {
3143+
descriptions.forEach((desc: string, idx: number) => {
31213144
block
31223145
.tspan(`${desc}`)
31233146
.attr({
3124-
dy: '1em',
3147+
dy: idx === 0 ? '1.25em' : '1em',
31253148
x: 0,
31263149
})
31273150
.addClass('cvat_canvas_text_description');
3128-
}
3151+
});
31293152
}
31303153
if (withAttr) {
3131-
for (const attrID of Object.keys(attributes)) {
3132-
const values = `${attributes[attrID] === undefinedAttrValue ? '' : attributes[attrID]}`.split('\n');
3154+
Object.keys(attributes).forEach((attrID: string, idx: number) => {
3155+
const values = `${attributes[attrID] === undefinedAttrValue ?
3156+
'' : attributes[attrID]}`.split('\n');
31333157
const parent = block.tspan(`${attrNames[attrID]}: `)
3134-
.attr({ attrID, dy: '1em', x: 0 }).addClass('cvat_canvas_text_attribute');
3158+
.attr({ attrID, dy: idx === 0 ? '1.25em' : '1em', x: 0 })
3159+
.addClass('cvat_canvas_text_attribute');
31353160
values.forEach((attrLine: string, index: number) => {
31363161
parent
31373162
.tspan(attrLine)
31383163
.attr({
31393164
dy: index === 0 ? 0 : '1em',
31403165
});
31413166
});
3142-
}
3167+
});
31433168
}
31443169
})
31453170
.move(0, 0)

0 commit comments

Comments
 (0)