Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quality control page #8329

Merged
merged 100 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
1f28607
initial commit
klakhov Jul 16, 2024
0350e61
update quality form view
klakhov Jul 16, 2024
0ce7e60
Merge branch 'develop' into kl/support-quality-plugin
klakhov Jul 16, 2024
7e39916
moved quality tab to quality control page
klakhov Jul 17, 2024
e4e16f4
added support for plugin tabs
klakhov Jul 17, 2024
c096c1d
changed yellow quality color
klakhov Jul 18, 2024
6c458e3
Merge branch 'develop' into kl/support-quality-plugin
klakhov Jul 19, 2024
09f22a6
style control improvements
klakhov Jul 19, 2024
7e38402
demo implementation
klakhov Jul 24, 2024
7b03b28
Merge branch 'develop' into kl/support-quality-plugin
klakhov Jul 24, 2024
4d40228
small fix
klakhov Jul 24, 2024
0d41ac7
Merge branch 'develop' into kl/support-quality-plugin
klakhov Jul 25, 2024
f4c747b
moved sorter to table utils
klakhov Jul 25, 2024
097c006
added new quality settings params
klakhov Jul 25, 2024
ff472bf
added support of colors corresponding to target threshold
klakhov Jul 25, 2024
ddc1b61
Merge branch 'develop' into kl/support-quality-plugin
klakhov Jul 31, 2024
04fb850
made quality form public
klakhov Aug 1, 2024
b7e8900
implemented paid placeholder, management tab overload
klakhov Aug 3, 2024
8295dbc
added overview plugin
klakhov Aug 4, 2024
0e963ef
moved overview to plugin
klakhov Aug 5, 2024
ce7c5a3
Merge branch 'develop' into kl/support-quality-plugin
klakhov Aug 8, 2024
d9c5090
supported using job card in plugin
klakhov Aug 8, 2024
39d9c8e
merge develop
klakhov Aug 12, 2024
0fb99f0
moved quality report calculation
klakhov Aug 12, 2024
3a749c7
moved job validation count to plugin
klakhov Aug 13, 2024
16c6e2f
Merge branch 'develop' into kl/support-quality-plugin
klakhov Aug 19, 2024
c0f0d2f
Job validations - common
zhiltsov-max Aug 19, 2024
da2d9fb
Update changelog
zhiltsov-max Aug 19, 2024
65b4591
Remove unused imports
zhiltsov-max Aug 19, 2024
c5fa228
Update test assets
zhiltsov-max Aug 19, 2024
bf09a38
Update server schema
zhiltsov-max Aug 19, 2024
abb214f
Fix formatting
zhiltsov-max Aug 20, 2024
5bff076
removed quality components
klakhov Aug 20, 2024
042709e
updated ground truth test
klakhov Aug 20, 2024
1feede8
Merge branch 'develop' into kl/support-quality-plugin
klakhov Aug 21, 2024
56a8e3b
Merge branch 'kl/quality-plugin-remove-analytics' into kl/support-qua…
klakhov Aug 21, 2024
8bac234
Merge branch 'zm/validation-api' into kl/support-quality-plugin
klakhov Aug 21, 2024
1d29c2f
Refactor field definition
zhiltsov-max Aug 21, 2024
d81e1da
Update server schema
zhiltsov-max Aug 21, 2024
b9ca46c
Merge branch 'develop' into zm/validation-api
zhiltsov-max Aug 21, 2024
b96bb72
adapted settings for new API
klakhov Aug 21, 2024
3a40c2f
added fetching job meta, showing summary component
klakhov Aug 22, 2024
82fdfb9
added allocation table, styles
klakhov Aug 22, 2024
3ce0864
Merge branch 'develop' into zm/validation-api
zhiltsov-max Aug 23, 2024
7df74bb
implemented actual frame deleting/restoring from management page
klakhov Aug 23, 2024
6db52f3
fixed group operations
klakhov Aug 23, 2024
f036ace
Merge branch 'zm/validation-api' into kl/support-quality-plugin
klakhov Aug 23, 2024
d4fb8fa
refactor quality control page
klakhov Aug 23, 2024
5e941ae
fixed count on summary component
klakhov Aug 23, 2024
8b46b85
fixed styles of paid placeholder
klakhov Aug 24, 2024
ab2e42f
added react resizible dep
klakhov Aug 24, 2024
38536bd
fixed types
klakhov Aug 24, 2024
04d4e82
Merge branch 'develop' into kl/support-quality-plugin
klakhov Aug 26, 2024
cf75c91
added settings for paid placeholder
klakhov Aug 26, 2024
a6b9707
fixed eslint
klakhov Aug 26, 2024
17e1f81
fix some minor issues
klakhov Aug 26, 2024
9872394
package & changelog
klakhov Aug 26, 2024
edad81a
added plugin column
klakhov Aug 26, 2024
ed72d7a
fixed linter
klakhov Aug 26, 2024
84ba53d
added plugin for management page
klakhov Aug 26, 2024
d69a92c
minor cleanup
klakhov Aug 26, 2024
51ea349
updated quality settings descriptions
klakhov Aug 26, 2024
7e8c44d
minor bugfixes
klakhov Aug 26, 2024
0a44763
applied coderabbit comments
klakhov Aug 26, 2024
8c4eb52
fixed multitask bug
klakhov Aug 26, 2024
bacafe5
added plugin for sorting columns
klakhov Aug 26, 2024
3e89c5f
adapted quality settings changes
klakhov Aug 27, 2024
ec38612
supported target metric in plugin
klakhov Aug 27, 2024
823e921
support for table actions plugins
klakhov Aug 28, 2024
32145ad
Resolved conflicts
bsekachev Sep 3, 2024
573053b
Accidently removed yarn.lock
bsekachev Sep 3, 2024
2c3e301
Updated yarn
bsekachev Sep 3, 2024
9d98964
Minor updates
bsekachev Sep 3, 2024
b66971e
cvat-core refactoring
bsekachev Sep 4, 2024
54d7601
Refactoring adding a gt task
bsekachev Sep 4, 2024
d7a8c7b
Fixed condition
bsekachev Sep 4, 2024
28f0a04
Do not store placeholder image in the bundle
bsekachev Sep 4, 2024
13c300c
Merge branch 'develop' into kl/support-quality-plugin
bsekachev Sep 5, 2024
696082d
Fixed different issues found in task creation form
bsekachev Sep 5, 2024
86480ee
Added missed field
bsekachev Sep 5, 2024
456062e
Aborted unnecessary changes
bsekachev Sep 5, 2024
418202d
Aborted extra changes
bsekachev Sep 5, 2024
e84b6b8
Fixed imports
bsekachev Sep 5, 2024
5ea5a0a
Reduced code duplication
bsekachev Sep 5, 2024
7a22d16
Do not render quality block
bsekachev Sep 5, 2024
a9d7010
Removed duplicated elements
bsekachev Sep 5, 2024
1e07d34
Fixed classes
bsekachev Sep 5, 2024
59bdcde
Renamed classes
bsekachev Sep 5, 2024
1020819
minor fix
bsekachev Sep 5, 2024
4b0b0ba
Merge branch 'develop' into kl/support-quality-plugin
bsekachev Sep 5, 2024
31cbd5a
Minor codestyle fixes
bsekachev Sep 5, 2024
5884866
Minor fixes
bsekachev Sep 5, 2024
29ce9a6
Updated for plugin
bsekachev Sep 8, 2024
40a80d7
Handle useProjectCloudStorage switch
bsekachev Sep 8, 2024
86f7999
Merged develop
bsekachev Sep 9, 2024
953e3d4
Updated styles on long tables
bsekachev Sep 9, 2024
a41db37
Some code refactoring
bsekachev Sep 10, 2024
dd1be67
Merge branch 'develop' into kl/support-quality-plugin
bsekachev Sep 10, 2024
4f20729
Adjusted for plugin
bsekachev Sep 11, 2024
489c216
Merge branch 'develop' into kl/support-quality-plugin
bsekachev Sep 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions changelog.d/20240826_093730_klakhov_support_quality_plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
### Changed

- Moved quality control from `analytics` page to `quality control` page
(<https://github.com/cvat-ai/cvat/pull/8329>)

### Removed

- Quality report no longer available in CVAT community version
(<https://github.com/cvat-ai/cvat/pull/8329>)

### Added

- Quality management tab on `quality control` allows to enabling/disabling GT frames
(<https://github.com/cvat-ai/cvat/pull/8329>)
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.1.3",
"version": "15.2.0",
"type": "module",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "src/api.ts",
Expand Down
83 changes: 56 additions & 27 deletions cvat-core/src/annotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,58 +11,80 @@ import AnnotationsHistory from './annotations-history';
import { checkObjectType } from './common';
import Project from './project';
import { Task, Job } from './session';
import { ScriptingError, ArgumentError } from './exceptions';
import { ArgumentError } from './exceptions';
import { getDeletedFrames } from './frames';
import { JobType } from './enums';

type WeakMapItem = { collection: AnnotationsCollection, saver: AnnotationsSaver, history: AnnotationsHistory };
const jobCache = new WeakMap<Task | Job, WeakMapItem>();
const taskCache = new WeakMap<Task | Job, WeakMapItem>();
const jobCollectionCache = new WeakMap<Task | Job, { collection: AnnotationsCollection; saver: AnnotationsSaver; }>();
const taskCollectionCache = new WeakMap<Task | Job, { collection: AnnotationsCollection; saver: AnnotationsSaver; }>();

function getCache(sessionType): WeakMap<Task | Job, WeakMapItem> {
if (sessionType === 'task') {
return taskCache;
}
// save history separately as not all history actions are related to annotations (e.g. delete, restore frame are not)
const jobHistoryCache = new WeakMap<Task | Job, AnnotationsHistory>();
const taskHistoryCache = new WeakMap<Task | Job, AnnotationsHistory>();

if (sessionType === 'job') {
return jobCache;
function getCache(sessionType: 'task' | 'job'): {
collection: typeof jobCollectionCache;
history: typeof jobHistoryCache;
} {
if (sessionType === 'task') {
return {
collection: taskCollectionCache,
history: taskHistoryCache,
};
}

throw new ScriptingError(`Unknown session type was received ${sessionType}`);
return {
collection: jobCollectionCache,
history: jobHistoryCache,
};
}

class InstanceNotInitializedError extends Error {}

function getSession(session): WeakMapItem {
export function getCollection(session): AnnotationsCollection {
const sessionType = session instanceof Task ? 'task' : 'job';
const cache = getCache(sessionType);
const { collection } = getCache(sessionType);

if (cache.has(session)) {
return cache.get(session);
if (collection.has(session)) {
return collection.get(session).collection;
}

throw new InstanceNotInitializedError(
'Session has not been initialized yet. Call annotations.get() or annotations.clear({ reload: true }) before',
);
}

export function getCollection(session): AnnotationsCollection {
return getSession(session).collection;
}

export function getSaver(session): AnnotationsSaver {
return getSession(session).saver;
const sessionType = session instanceof Task ? 'task' : 'job';
const { collection } = getCache(sessionType);

if (collection.has(session)) {
return collection.get(session).saver;
}

throw new InstanceNotInitializedError(
'Session has not been initialized yet. Call annotations.get() or annotations.clear({ reload: true }) before',
);
}

export function getHistory(session): AnnotationsHistory {
return getSession(session).history;
const sessionType = session instanceof Task ? 'task' : 'job';
const { history } = getCache(sessionType);

if (history.has(session)) {
return history.get(session);
}

const initiatedHistory = new AnnotationsHistory();
history.set(session, initiatedHistory);
return initiatedHistory;
}

async function getAnnotationsFromServer(session: Job | Task): Promise<void> {
const sessionType = session instanceof Task ? 'task' : 'job';
const cache = getCache(sessionType);

if (!cache.has(session)) {
if (!cache.collection.has(session)) {
const serializedAnnotations = await serverProxy.annotations.getAnnotations(sessionType, session.id);

// Get meta information about frames
Expand All @@ -74,7 +96,7 @@ async function getAnnotationsFromServer(session: Job | Task): Promise<void> {
}
frameMeta.deleted_frames = await getDeletedFrames(sessionType, session.id);

const history = new AnnotationsHistory();
const history = cache.history.has(session) ? cache.history.get(session) : new AnnotationsHistory();
const collection = new AnnotationsCollection({
labels: session.labels,
history,
Expand All @@ -87,16 +109,21 @@ async function getAnnotationsFromServer(session: Job | Task): Promise<void> {
// eslint-disable-next-line no-unsanitized/method
collection.import(serializedAnnotations);
const saver = new AnnotationsSaver(serializedAnnotations.version, collection, session);
cache.set(session, { collection, saver, history });
cache.collection.set(session, { collection, saver });
cache.history.set(session, history);
}
}

export function clearCache(session): void {
const sessionType = session instanceof Task ? 'task' : 'job';
const cache = getCache(sessionType);

if (cache.has(session)) {
cache.delete(session);
if (cache.collection.has(session)) {
cache.collection.delete(session);
}

if (cache.history.has(session)) {
cache.history.delete(session);
}
}

Expand Down Expand Up @@ -125,7 +152,9 @@ export async function clearAnnotations(
checkObjectType('reload', reload, 'boolean', null);

if (reload) {
cache.delete(session);
cache.collection.delete(session);
// delete history as it may relate to objects from collection we deleted above
cache.history.delete(session);
return getAnnotationsFromServer(session);
}
}
Expand Down
16 changes: 6 additions & 10 deletions cvat-core/src/api-implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
import QualityReport from './quality-report';
import QualityConflict, { ConflictSeverity } from './quality-conflict';
import QualitySettings from './quality-settings';
import { FramesMetaData } from './frames';
import { getFramesMeta } from './frames';
import AnalyticsReport from './analytics-report';
import { listActions, registerAction, runActions } from './annotations-actions';
import { convertDescriptions, getServerAPISchema } from './server-schema';
Expand Down Expand Up @@ -520,9 +520,8 @@ export default function implementAPI(cvat: CVATCore): CVATCore {
const settings = await serverProxy.analytics.quality.settings.get(params);
const schema = await getServerAPISchema();
const descriptions = convertDescriptions(schema.components.schemas.QualitySettings.properties);
return new QualitySettings({
...settings, descriptions,
});

return new QualitySettings({ ...settings, descriptions });
});
implementationMixin(cvat.analytics.performance.reports, async (filter: AnalyticsReportFilter) => {
checkFilter(filter, {
Expand Down Expand Up @@ -557,12 +556,9 @@ export default function implementAPI(cvat: CVATCore): CVATCore {
const params = fieldsToSnakeCase(body);
await serverProxy.analytics.performance.calculate(params, onUpdate);
});
implementationMixin(cvat.frames.getMeta, async (type, id) => {
const result = await serverProxy.frames.getMeta(type, id);
return new FramesMetaData({
...result,
deleted_frames: Object.fromEntries(result.deleted_frames.map((_frame) => [_frame, true])),
});
implementationMixin(cvat.frames.getMeta, async (type: 'job' | 'task', id: number) => {
const result = await getFramesMeta(type, id);
return result;
});

return cvat;
Expand Down
12 changes: 11 additions & 1 deletion cvat-core/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ import Project from './project';
import implementProject from './project-implementation';
import { Attribute, Label } from './labels';
import MLModel from './ml-model';
import { FrameData } from './frames';
import { FrameData, FramesMetaData } from './frames';
import CloudStorage from './cloud-storage';
import Organization from './organization';
import Webhook from './webhook';
import AnnotationGuide from './guide';
import BaseSingleFrameAction from './annotations-actions';
import QualityReport from './quality-report';
import QualityConflict from './quality-conflict';
import QualitySettings from './quality-settings';
import AnalyticsReport from './analytics-report';
import { Request } from './request';

import * as enums from './enums';
Expand Down Expand Up @@ -416,6 +420,12 @@ function build(): CVATCore {
Webhook,
AnnotationGuide,
BaseSingleFrameAction,
QualitySettings,
AnalyticsReport,
QualityConflict,
QualityReport,
Request,
FramesMetaData,
},
utils: {
mask2Rle,
Expand Down
39 changes: 24 additions & 15 deletions cvat-core/src/frames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,19 +435,27 @@ Object.defineProperty(FrameData.prototype.data, 'implementation', {
writable: false,
});

async function getJobMeta(jobID: number): Promise<FramesMetaData> {
if (!frameMetaCache[jobID]) {
frameMetaCache[jobID] = serverProxy.frames.getMeta('job', jobID)
export async 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])),
});
}
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) => {
delete frameMetaCache[jobID];
delete frameMetaCache[id];
throw error;
});
}
return frameMetaCache[jobID];
return frameMetaCache[id];
}

async function saveJobMeta(meta: FramesMetaData, jobID: number): Promise<FramesMetaData> {
Expand Down Expand Up @@ -588,7 +596,7 @@ export async function getFrame(
): Promise<FrameData> {
if (!(jobID in frameDataCache)) {
const blockType = chunkType === 'video' ? BlockType.MP4VIDEO : BlockType.ARCHIVE;
const meta = await getJobMeta(jobID);
const meta = await getFramesMeta('job', jobID);

const mean = meta.frames.reduce((a, b) => a + b.width * b.height, 0) / meta.frames.length;
const stdDev = Math.sqrt(
Expand Down Expand Up @@ -655,31 +663,32 @@ export async function getDeletedFrames(instanceType: 'job' | 'task', id): Promis
throw new Exception(`getDeletedFrames is not implemented for ${instanceType}`);
}

export function deleteFrame(jobID: number, frame: number): void {
const { meta } = frameDataCache[jobID];
export async function deleteFrame(jobID: number, frame: number): Promise<void> {
const meta = await frameMetaCache[jobID];
meta.deletedFrames[frame] = true;
}

export function restoreFrame(jobID: number, frame: number): void {
const { meta } = frameDataCache[jobID];
export async function restoreFrame(jobID: number, frame: number): Promise<void> {
const meta = await frameMetaCache[jobID];
delete meta.deletedFrames[frame];
}

export async function patchMeta(jobID: number): Promise<void> {
const { meta } = frameDataCache[jobID];
export async function patchMeta(jobID: number): Promise<FramesMetaData> {
const meta = await frameMetaCache[jobID];
const updatedFields = meta.getUpdated();

if (Object.keys(updatedFields).length) {
const newMeta = await saveJobMeta(meta, jobID);
frameDataCache[jobID].meta = newMeta;
frameMetaCache[jobID] = saveJobMeta(meta, jobID);
}
const newMeta = await frameMetaCache[jobID];
return newMeta;
}

export async function findFrame(
jobID: number, frameFrom: number, frameTo: number, filters: { offset?: number, notDeleted: boolean },
): Promise<number | null> {
const offset = filters.offset || 1;
const meta = await getJobMeta(jobID);
const meta = await getFramesMeta('job', jobID);

const sign = Math.sign(frameTo - frameFrom);
const predicate = sign > 0 ? (frame) => frame <= frameTo : (frame) => frame >= frameTo;
Expand Down
8 changes: 7 additions & 1 deletion cvat-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import ObjectState from './object-state';
import MLModel from './ml-model';
import Issue from './issue';
import Comment from './comment';
import { FrameData } from './frames';
import { FrameData, FramesMetaData } from './frames';
import CloudStorage from './cloud-storage';
import Organization, { Invitation } from './organization';
import Webhook from './webhook';
Expand Down Expand Up @@ -209,6 +209,12 @@ export default interface CVATCore {
Webhook: typeof Webhook;
AnnotationGuide: typeof AnnotationGuide;
BaseSingleFrameAction: typeof BaseSingleFrameAction;
QualityReport: typeof QualityReport;
QualityConflict: typeof QualityConflict;
QualitySettings: typeof QualitySettings;
AnalyticsReport: typeof AnalyticsReport;
Request: typeof Request;
FramesMetaData: typeof FramesMetaData;
};
utils: {
mask2Rle: typeof mask2Rle;
Expand Down
Loading
Loading