Skip to content

Commit

Permalink
Merge pull request #8527 from cvat-ai/release-2.21.0
Browse files Browse the repository at this point in the history
Release v2.21.0
  • Loading branch information
cvat-bot[bot] authored Oct 10, 2024
2 parents 941f5c0 + 61270d9 commit 67d3645
Show file tree
Hide file tree
Showing 114 changed files with 11,875 additions and 3,459 deletions.
67 changes: 67 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

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

<a id='changelog-2.21.0'></a>
## \[2.21.0\] - 2024-10-10

### Added

- New task mode: Honeypots (GT pool)
(<https://github.com/cvat-ai/cvat/pull/8348>)
- New task creation options for quality control: Honeypots (GT pool), GT job
(<https://github.com/cvat-ai/cvat/pull/8348>)
- New GT job frame selection method: `random_per_job`,
which guarantees each job will have GT overlap
(<https://github.com/cvat-ai/cvat/pull/8348>)
- \[Server API\] POST `/jobs/`: new frame selection parameters,
which accept percentages, instead of absolute values
(<https://github.com/cvat-ai/cvat/pull/8348>)
- \[Server API\] GET `/api/tasks/{id}/` got a new `validation_mode` field,
reflecting the current validation configuration (immutable)
(<https://github.com/cvat-ai/cvat/pull/8348>)
- \[Server API\] POST `/api/tasks/{id}/data` got a new `validation_params` field,
which allows to enable `GT` and `GT_POOL` validation for a task on its creation
(<https://github.com/cvat-ai/cvat/pull/8348>)

- Added custom certificates documentation
(<https://github.com/cvat-ai/cvat/pull/7508>)

- Support for YOLOv8 Classification format
(<https://github.com/cvat-ai/cvat/pull/8475>)

- \[Server API\] An option to change real frames for honeypot frames in tasks with honeypots
(<https://github.com/cvat-ai/cvat/pull/8471>)
- \[Server API\] New endpoints for validation configuration management in tasks and jobs
`/api/tasks/{id}/validation_layout`, `/api/jobs/{id}/validation_layout`
(<https://github.com/cvat-ai/cvat/pull/8471>)

- \[Helm\] Readiness and liveness probes
(<https://github.com/cvat-ai/cvat/pull/8488>)

### Changed

- \[Server API\] POST `/jobs/` `.frames` field now expects relative frame numbers
instead of absolute (source data) ones
(<https://github.com/cvat-ai/cvat/pull/8348>)

- \[Server API\] Now chunks in tasks can be changed.
There are new API elements to check chunk relevancy, if they are cached:
`/api/tasks/{id}/data/meta` got a new field `chunks_updated_date`,
`/api/tasks/{id}/data/?type=chunk` got 2 new headers: `X-Updated-Date`, `X-Checksum`
(<https://github.com/cvat-ai/cvat/pull/8471>)

- Made the `PATCH` endpoints for projects, tasks, jobs and memberships check
the input more strictly
(<https://github.com/cvat-ai/cvat/pull/8493>):

- unknown fields are rejected;
- updating a field now requires the same level of permissions regardless of
whether the new value is the same as the old value.

- \[Server API\] Quality report computation is now allowed to regular users
(<https://github.com/cvat-ai/cvat/pull/8511>)

### Fixed

- Invalid chunks for GT jobs when `?number` is used in the request and task frame step > 1
(<https://github.com/cvat-ai/cvat/pull/8510>)
- Invalid output of frames for specific GT frame requests with `api/jobs/{id}/data/?type=frame`
(<https://github.com/cvat-ai/cvat/pull/8510>)

<a id='changelog-2.20.0'></a>
## \[2.20.0\] - 2024-10-01

Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,10 @@ For more information about the supported formats, see:
| [LFW](http://vis-www.cs.umass.edu/lfw/) | ✔️ | ✔️ |
| [Supervisely Point Cloud Format](https://docs.supervise.ly/data-organization/00_ann_format_navi) | ✔️ | ✔️ |
| [YOLOv8 Detection](https://docs.ultralytics.com/datasets/detect/) | ✔️ | ✔️ |
| [YOLOv8 Oriented Bounding Boxes](https://docs.ultralytics.com/datasets/obb/) | ✔️ | ✔️ |
| [YOLOv8 Segmentation](https://docs.ultralytics.com/datasets/segment/) | ✔️ | ✔️ |
| [YOLOv8 Pose](https://docs.ultralytics.com/datasets/pose/) | ✔️ | ✔️ |
| [YOLOv8 Oriented Bounding Boxes](https://docs.ultralytics.com/datasets/obb/) | ✔️ | ✔️ |
| [YOLOv8 Segmentation](https://docs.ultralytics.com/datasets/segment/) | ✔️ | ✔️ |
| [YOLOv8 Pose](https://docs.ultralytics.com/datasets/pose/) | ✔️ | ✔️ |
| [YOLOv8 Classification](https://docs.ultralytics.com/datasets/classify/) | ✔️ | ✔️ |

<!--lint enable maximum-line-length-->

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.20.0
cvat-sdk~=2.21.0
Pillow>=10.3.0
setuptools>=70.0.0 # 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.20.0"
VERSION = "2.21.0"
2 changes: 2 additions & 0 deletions cvat-core/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import QualityReport from './quality-report';
import QualityConflict from './quality-conflict';
import QualitySettings from './quality-settings';
import AnalyticsReport from './analytics-report';
import ValidationLayout from './validation-layout';
import { Request } from './request';

import * as enums from './enums';
Expand Down Expand Up @@ -426,6 +427,7 @@ function build(): CVATCore {
QualityReport,
Request,
FramesMetaData,
ValidationLayout,
},
utils: {
mask2Rle,
Expand Down
61 changes: 59 additions & 2 deletions cvat-core/src/frames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { FieldUpdateTrigger } from './common';
// frame storage by job id
const frameDataCache: Record<string, {
meta: FramesMetaData;
metaFetchedTimestamp: number;
chunkSize: number;
mode: 'annotation' | 'interpolation';
startFrame: number;
Expand Down Expand Up @@ -57,6 +58,7 @@ export class FramesMetaData {
public stopFrame: number;
public frameStep: number;
public chunkCount: number;
public chunksUpdatedDate: string;

#updateTrigger: FieldUpdateTrigger;

Expand All @@ -71,6 +73,7 @@ export class FramesMetaData {
size: undefined,
start_frame: undefined,
stop_frame: undefined,
chunks_updated_date: undefined,
};

this.#updateTrigger = new FieldUpdateTrigger();
Expand Down Expand Up @@ -149,6 +152,9 @@ export class FramesMetaData {
frameStep: {
get: () => frameStep,
},
chunksUpdatedDate: {
get: () => data.chunks_updated_date,
},
}),
);

Expand Down Expand Up @@ -592,13 +598,45 @@ function getFrameMeta(jobID, frame): SerializedFramesMetaData['frames'][0] {
return frameMeta;
}

async function refreshJobCacheIfOutdated(jobID: number): Promise<void> {
const cached = frameDataCache[jobID];
if (!cached) {
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;

if (isOutdated) {
// get metadata again if outdated
const meta = await getFramesMeta('job', jobID, true);
if (new Date(meta.chunksUpdatedDate) > new Date(cached.meta.chunksUpdatedDate)) {
// chunks were re-defined. Existing data not relevant anymore
// currently we only re-write meta, remove all cached frames from provider and clear cached context images
// other parameters (e.g. chunkSize) are not supposed to be changed
cached.meta = meta;
cached.provider.cleanup(Number.MAX_SAFE_INTEGER);
for (const frame of Object.keys(cached.contextCache)) {
for (const image of Object.values(cached.contextCache[+frame].data)) {
// close images to immediate memory release
image.close();
}
}
cached.contextCache = {};
}

cached.metaFetchedTimestamp = Date.now();
}
}

export function getContextImage(jobID: number, frame: number): Promise<Record<string, ImageBitmap>> {
return new Promise<Record<string, ImageBitmap>>((resolve, reject) => {
if (!(jobID in frameDataCache)) {
reject(new Error(
'Frame data was not initialized for this job. Try first requesting any frame.',
));
}

const frameData = frameDataCache[jobID];
const requestId = frame;
const { startFrame } = frameData;
Expand Down Expand Up @@ -695,7 +733,9 @@ export async function getFrame(
dimension: DimensionType,
getChunk: (chunkIndex: number, quality: ChunkQuality) => Promise<ArrayBuffer>,
): Promise<FrameData> {
if (!(jobID in frameDataCache)) {
const dataCacheExists = jobID in frameDataCache;

if (!dataCacheExists) {
const blockType = chunkType === 'video' ? BlockType.MP4VIDEO : BlockType.ARCHIVE;
const meta = await getFramesMeta('job', jobID);

Expand All @@ -718,6 +758,7 @@ export async function getFrame(

frameDataCache[jobID] = {
meta,
metaFetchedTimestamp: Date.now(),
chunkSize,
mode,
startFrame,
Expand All @@ -743,6 +784,22 @@ export async function getFrame(
};
}

// basically the following functions may be affected if job cache is outdated
// - getFrame
// - getContextImage
// - getCachedChunks
// And from this idea we should call refreshJobCacheIfOutdated from each one
// Hovewer, following from the order, these methods are usually called
// it may lead to even more confusing behaviour
//
// Usually user first receives frame, then user receives ranges and finally user receives context images
// In this case (extremely rare, but nevertheless possible) user may get context images related to another frame
// - if cache gets outdated after getFrame() call
// - and before getContextImage() call
// - and both calls refer to the same frame that is refreshed honeypot frame and this frame has context images
// Thus, it is better to only call `refreshJobCacheIfOutdated` from getFrame()
await refreshJobCacheIfOutdated(jobID);

const frameMeta = getFrameMeta(jobID, frame);
frameDataCache[jobID].provider.setRenderSize(frameMeta.width, frameMeta.height);
frameDataCache[jobID].decodeForward = isPlaying;
Expand All @@ -759,7 +816,7 @@ export async function getFrame(
});
}

export async function getDeletedFrames(instanceType: 'job' | 'task', id): Promise<Record<number, boolean>> {
export async function getDeletedFrames(instanceType: 'job' | 'task', id: number): Promise<Record<number, boolean>> {
if (instanceType === 'job') {
const { meta } = frameDataCache[id];
return meta.deletedFrames;
Expand Down
2 changes: 2 additions & 0 deletions cvat-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import QualityConflict from './quality-conflict';
import QualitySettings from './quality-settings';
import AnalyticsReport from './analytics-report';
import AnnotationGuide from './guide';
import ValidationLayout from './validation-layout';
import { Request } from './request';
import BaseSingleFrameAction, { listActions, registerAction, runActions } from './annotations-actions';
import {
Expand Down Expand Up @@ -215,6 +216,7 @@ export default interface CVATCore {
AnalyticsReport: typeof AnalyticsReport;
Request: typeof Request;
FramesMetaData: typeof FramesMetaData;
ValidationLayout: typeof ValidationLayout;
};
utils: {
mask2Rle: typeof mask2Rle;
Expand Down
Loading

0 comments on commit 67d3645

Please sign in to comment.