Skip to content

Commit

Permalink
Merge pull request #8794 from cvat-ai/release-2.23.1
Browse files Browse the repository at this point in the history
Release v2.23.1
  • Loading branch information
cvat-bot[bot] authored Dec 9, 2024
2 parents 424142e + 195f427 commit e50cf53
Show file tree
Hide file tree
Showing 49 changed files with 1,417 additions and 392 deletions.
41 changes: 41 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

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

<a id='changelog-2.23.1'></a>
## \[2.23.1\] - 2024-12-09

### Changed

- \[CLI\] Log messages are now printed on stderr rather than stdout
(<https://github.com/cvat-ai/cvat/pull/8784>)

### Fixed

- Optimized memory consumption and reduced the number of database queries
when importing annotations to a task with a lot of jobs and images
(<https://github.com/cvat-ai/cvat/pull/8676>)

- Incorrect display of validation frames on the task quality management page
(<https://github.com/cvat-ai/cvat/pull/8731>)

- Player may navigate to removed frames when playing
(<https://github.com/cvat-ai/cvat/pull/8747>)

- User may navigate forward with a keyboard when a modal opened
(<https://github.com/cvat-ai/cvat/pull/8748>)

- fit:canvas event is not generated if to fit it from the controls sidebar
(<https://github.com/cvat-ai/cvat/pull/8750>)

- Color of 'Create object URL' button for a not saved on the server object
(<https://github.com/cvat-ai/cvat/pull/8752>)

- Failed request for a chunk inside a job after it was recently modified by updating `validation_layout`
(<https://github.com/cvat-ai/cvat/pull/8772>)

- Memory consumption during preparation of image chunks
(<https://github.com/cvat-ai/cvat/pull/8778>)

- Possible endless lock acquisition for chunk preparation job
(<https://github.com/cvat-ai/cvat/pull/8769>)

- Fixed issue: Cannot read properties of undefined (reading 'getUpdated')
(<https://github.com/cvat-ai/cvat/pull/8785>)

<a id='changelog-2.23.0'></a>
## \[2.23.0\] - 2024-11-29

Expand Down
1 change: 0 additions & 1 deletion Dockerfile.ui
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ COPY cvat-canvas3d/ /tmp/cvat-canvas3d/
COPY cvat-canvas/ /tmp/cvat-canvas/
COPY cvat-ui/ /tmp/cvat-ui/

ARG WA_PAGE_VIEW_HIT
ARG UI_APP_CONFIG
ARG CLIENT_PLUGINS
ARG DISABLE_SOURCE_MAPS
Expand Down
24 changes: 15 additions & 9 deletions cvat-canvas/src/typescript/canvasModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -687,28 +687,34 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
public fit(): void {
const { angle } = this.data;

let updatedScale = this.data.scale;
if ((angle / 90) % 2) {
// 90, 270, ..
this.data.scale = Math.min(
updatedScale = Math.min(
this.data.canvasSize.width / this.data.imageSize.height,
this.data.canvasSize.height / this.data.imageSize.width,
);
} else {
this.data.scale = Math.min(
updatedScale = Math.min(
this.data.canvasSize.width / this.data.imageSize.width,
this.data.canvasSize.height / this.data.imageSize.height,
);
}

this.data.scale = Math.min(Math.max(this.data.scale, FrameZoom.MIN), FrameZoom.MAX);
this.data.top = this.data.canvasSize.height / 2 - this.data.imageSize.height / 2;
this.data.left = this.data.canvasSize.width / 2 - this.data.imageSize.width / 2;
updatedScale = Math.min(Math.max(updatedScale, FrameZoom.MIN), FrameZoom.MAX);
const updatedTop = this.data.canvasSize.height / 2 - this.data.imageSize.height / 2;
const updatedLeft = this.data.canvasSize.width / 2 - this.data.imageSize.width / 2;

// scale is changed during zooming or translating
// so, remember fitted scale to compute fit-relative scaling
this.data.fittedScale = this.data.scale;
if (updatedScale !== this.data.scale || updatedTop !== this.data.top || updatedLeft !== this.data.left) {
this.data.scale = updatedScale;
this.data.top = updatedTop;
this.data.left = updatedLeft;

this.notify(UpdateReasons.IMAGE_FITTED);
// scale is changed during zooming or translating
// so, remember fitted scale to compute fit-relative scaling
this.data.fittedScale = this.data.scale;
this.notify(UpdateReasons.IMAGE_FITTED);
}
}

public grid(stepX: number, stepY: number): void {
Expand Down
15 changes: 9 additions & 6 deletions cvat-canvas/src/typescript/canvasView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1651,12 +1651,6 @@ export class CanvasViewImpl implements CanvasView, Listener {
// Setup event handlers
this.canvas.addEventListener('dblclick', (e: MouseEvent): void => {
this.controller.fit();
this.canvas.dispatchEvent(
new CustomEvent('canvas.fit', {
bubbles: false,
cancelable: true,
}),
);
e.preventDefault();
});

Expand Down Expand Up @@ -1896,6 +1890,15 @@ export class CanvasViewImpl implements CanvasView, Listener {
}),
);
} else if ([UpdateReasons.IMAGE_ZOOMED, UpdateReasons.IMAGE_FITTED].includes(reason)) {
if (reason === UpdateReasons.IMAGE_FITTED) {
this.canvas.dispatchEvent(
new CustomEvent('canvas.fit', {
bubbles: false,
cancelable: true,
}),
);
}

this.moveCanvas();
this.transformCanvas();
} else if (reason === UpdateReasons.IMAGE_ROTATED) {
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.23.0
cvat-sdk~=2.23.1
Pillow>=10.3.0
setuptools>=70.0.0 # not directly required, pinned by Snyk to avoid a vulnerability
3 changes: 2 additions & 1 deletion cvat-cli/src/cvat_cli/_internal/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,12 @@ def configure_parser(self, parser: argparse.ArgumentParser) -> None:
)

def execute(self, client: Client, *, filename: str, status_check_period: int) -> None:
client.tasks.create_from_backup(
task = client.tasks.create_from_backup(
filename=filename,
status_check_period=status_check_period,
pbar=DeferredTqdmProgressReporter(),
)
print(f"Created task ID", task.id)


@COMMANDS.command_class("auto-annotate")
Expand Down
2 changes: 1 addition & 1 deletion cvat-cli/src/cvat_cli/_internal/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def configure_logger(logger: logging.Logger, parsed_args: argparse.Namespace) ->
formatter = logging.Formatter(
"[%(asctime)s] %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", style="%"
)
handler = logging.StreamHandler(sys.stdout)
handler = logging.StreamHandler(sys.stderr)
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(level)
Expand Down
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.23.0"
VERSION = "2.23.1"
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": "15.3.0",
"version": "15.3.1",
"type": "module",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "src/api.ts",
Expand Down
6 changes: 6 additions & 0 deletions cvat-core/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,12 @@ function build(): CVATCore {
set requestsStatusDelay(value) {
config.requestsStatusDelay = value;
},
get jobMetaDataReloadPeriod() {
return config.jobMetaDataReloadPeriod;
},
set jobMetaDataReloadPeriod(value) {
config.jobMetaDataReloadPeriod = value;
},
},
client: {
version: `${pjson.version}`,
Expand Down
2 changes: 2 additions & 0 deletions cvat-core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const config = {
globalObjectsCounter: 0,

requestsStatusDelay: null,

jobMetaDataReloadPeriod: 1 * 60 * 60 * 1000, // 1 hour
};

export default config;
68 changes: 41 additions & 27 deletions cvat-core/src/frames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import serverProxy from './server-proxy';
import { SerializedFramesMetaData } from './server-response-types';
import { Exception, ArgumentError, DataError } from './exceptions';
import { FieldUpdateTrigger } from './common';
import config from './config';

// frame storage by job id
const frameDataCache: Record<string, {
Expand Down Expand Up @@ -535,41 +536,55 @@ Object.defineProperty(FrameData.prototype.data, 'implementation', {
writable: false,
});

export async function getFramesMeta(type: 'job' | 'task', id: number, forceReload = false): Promise<FramesMetaData> {
export function getFramesMeta(type: 'job' | 'task', id: number, forceReload = false): Promise<FramesMetaData> {
if (type === 'task') {
// we do not cache task meta currently. So, each new call will results to the server request
const result = await serverProxy.frames.getMeta('task', id);
return new FramesMetaData({
...result,
deleted_frames: Object.fromEntries(result.deleted_frames.map((_frame) => [_frame, true])),
});
return serverProxy.frames.getMeta('task', id).then((serialized) => (
new FramesMetaData({
...serialized,
deleted_frames: Object.fromEntries(serialized.deleted_frames.map((_frame) => [_frame, true])),
})
));
}

if (!(id in frameMetaCache) || forceReload) {
frameMetaCache[id] = serverProxy.frames.getMeta('job', id)
.then((serverMeta) => new FramesMetaData({
...serverMeta,
deleted_frames: Object.fromEntries(serverMeta.deleted_frames.map((_frame) => [_frame, true])),
}))
.catch((error) => {
const previousCache = frameMetaCache[id];
frameMetaCache[id] = new Promise((resolve, reject) => {
serverProxy.frames.getMeta('job', id).then((serialized) => {
const framesMetaData = new FramesMetaData({
...serialized,
deleted_frames: Object.fromEntries(serialized.deleted_frames.map((_frame) => [_frame, true])),
});
resolve(framesMetaData);
}).catch((error: unknown) => {
delete frameMetaCache[id];
throw error;
if (previousCache instanceof Promise) {
frameMetaCache[id] = previousCache;
}
reject(error);
});
});
}

return frameMetaCache[id];
}

async function saveJobMeta(meta: FramesMetaData, jobID: number): Promise<FramesMetaData> {
frameMetaCache[jobID] = serverProxy.frames.saveMeta('job', jobID, {
deleted_frames: Object.keys(meta.deletedFrames).map((frame) => +frame),
})
.then((serverMeta) => new FramesMetaData({
...serverMeta,
deleted_frames: Object.fromEntries(serverMeta.deleted_frames.map((_frame) => [_frame, true])),
}))
.catch((error) => {
delete frameMetaCache[jobID];
throw error;
function saveJobMeta(meta: FramesMetaData, jobID: number): Promise<FramesMetaData> {
frameMetaCache[jobID] = new Promise<FramesMetaData>((resolve, reject) => {
serverProxy.frames.saveMeta('job', jobID, {
deleted_frames: Object.keys(meta.deletedFrames).map((frame) => +frame),
}).then((serverMeta) => {
const updatedMetaData = new FramesMetaData({
...serverMeta,
deleted_frames: Object.fromEntries(serverMeta.deleted_frames.map((_frame) => [_frame, true])),
});
resolve(updatedMetaData);
}).catch((error) => {
frameMetaCache[jobID] = Promise.resolve(meta);
reject(error);
});
});

return frameMetaCache[jobID];
}

Expand Down Expand Up @@ -597,8 +612,7 @@ async function refreshJobCacheIfOutdated(jobID: number): Promise<void> {
throw new Error('Frame data cache is abscent');
}

const META_DATA_RELOAD_PERIOD = 1 * 60 * 60 * 1000; // 1 hour
const isOutdated = (Date.now() - cached.metaFetchedTimestamp) > META_DATA_RELOAD_PERIOD;
const isOutdated = (Date.now() - cached.metaFetchedTimestamp) > config.jobMetaDataReloadPeriod;

if (isOutdated) {
// get metadata again if outdated
Expand Down Expand Up @@ -834,7 +848,7 @@ export async function patchMeta(jobID: number): Promise<FramesMetaData> {
const updatedFields = meta.getUpdated();

if (Object.keys(updatedFields).length) {
frameMetaCache[jobID] = saveJobMeta(meta, jobID);
await saveJobMeta(meta, jobID);
}
const newMeta = await frameMetaCache[jobID];
return newMeta;
Expand Down
1 change: 1 addition & 0 deletions cvat-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ export default interface CVATCore {
onOrganizationChange: (newOrgId: number | null) => void | null;
globalObjectsCounter: typeof config.globalObjectsCounter;
requestsStatusDelay: typeof config.requestsStatusDelay;
jobMetaDataReloadPeriod: typeof config.jobMetaDataReloadPeriod;
},
client: {
version: string;
Expand Down
1 change: 0 additions & 1 deletion cvat-sdk/cvat_sdk/core/proxies/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,6 @@ class TasksRepo(
ModelCreateMixin[Task, models.ITaskWriteRequest],
ModelRetrieveMixin[Task],
ModelListMixin[Task],
ModelDeleteMixin,
):
_entity_type = Task

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.23.0"
VERSION="2.23.1"
LIB_NAME="cvat_sdk"
LAYER1_LIB_NAME="${LIB_NAME}/api_client"
DST_DIR="$(cd "$(dirname -- "$0")/.." && pwd)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,10 @@
}

button {
color: $text-color;
&:not(:disabled) {
color: $text-color;
}

width: 100%;
height: 100%;
text-align: left;
Expand Down
5 changes: 1 addition & 4 deletions cvat-ui/src/components/cvat-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ import { Organization, getCore } from 'cvat-core-wrapper';
import {
ErrorState, NotificationState, NotificationsState, PluginsState,
} from 'reducers';
import { customWaViewHit } from 'utils/environment';
import showPlatformNotification, {
platformInfo,
stopNotifications,
Expand Down Expand Up @@ -142,7 +141,7 @@ class CVATApplication extends React.PureComponent<CVATAppProps & RouteComponentP

public componentDidMount(): void {
const core = getCore();
const { history, location, onChangeLocation } = this.props;
const { history, onChangeLocation } = this.props;
const {
HEALTH_CHECK_RETRIES, HEALTH_CHECK_PERIOD, HEALTH_CHECK_REQUEST_TIMEOUT,
SERVER_UNAVAILABLE_COMPONENT, RESET_NOTIFICATIONS_PATHS,
Expand Down Expand Up @@ -170,9 +169,7 @@ class CVATApplication extends React.PureComponent<CVATAppProps & RouteComponentP
}
};

customWaViewHit(location.pathname, location.search, location.hash);
history.listen((newLocation) => {
customWaViewHit(newLocation.pathname, newLocation.search, newLocation.hash);
const { location: prevLocation } = this.props;

onChangeLocation(prevLocation.pathname, newLocation.pathname);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from 'cvat-core-wrapper';
import CVATTooltip from 'components/common/cvat-tooltip';
import { sorter } from 'utils/quality';
import { ValidationMode } from 'components/create-task-page/quality-configuration-form';

interface Props {
task: Task;
Expand Down Expand Up @@ -52,7 +53,11 @@ function AllocationTable(props: Readonly<Props>): JSX.Element {
const data = validationLayout.validationFrames.map((frame: number, index: number) => ({
key: frame,
frame,
name: gtJobMeta.frames[index]?.name ?? gtJobMeta.frames[0].name,
name: gtJobMeta.frames[
// - gt job meta starts from the 0 task frame;
// - honeypot gt job meta starts from the job start frame;
(validationLayout.mode === ValidationMode.GT) ? frame : index
]?.name ?? gtJobMeta.frames[0].name,
active: !disabledFrames.includes(frame),
}));

Expand Down
Loading

0 comments on commit e50cf53

Please sign in to comment.