Skip to content

Commit

Permalink
Project: export as a dataset (cvat-ai#3365)
Browse files Browse the repository at this point in the history
  • Loading branch information
ActiveChooN authored Aug 6, 2021
1 parent 59af610 commit f18b1cb
Show file tree
Hide file tree
Showing 75 changed files with 2,042 additions and 1,165 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ on:
- 'master'
- 'develop'
pull_request:
branches:
- '*'

jobs:
Unit_testing:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Notification if the browser does not support nesassary API
- Added ability to export project as a dataset (<https://github.com/openvinotoolkit/cvat/pull/3365>)
- Additional inline tips in interactors with demo gifs (<https://github.com/openvinotoolkit/cvat/pull/3473>)

### Changed
Expand Down
2 changes: 1 addition & 1 deletion cvat-core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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": "3.13.3",
"version": "3.14.0",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "babel.config.js",
"scripts": {
Expand Down
43 changes: 19 additions & 24 deletions cvat-core/src/annotations.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand All @@ -8,8 +8,9 @@
const AnnotationsSaver = require('./annotations-saver');
const AnnotationsHistory = require('./annotations-history');
const { checkObjectType } = require('./common');
const { Task } = require('./session');
const { Loader, Dumper } = require('./annotation-formats');
const { Project } = require('./project');
const { Task, Job } = require('./session');
const { Loader } = require('./annotation-formats');
const { ScriptingError, DataError, ArgumentError } = require('./exceptions');

const jobCache = new WeakMap();
Expand Down Expand Up @@ -50,6 +51,7 @@
stopFrame,
frameMeta,
});
// eslint-disable-next-line no-unsanitized/method
collection.import(rawAnnotations);

const saver = new AnnotationsSaver(rawAnnotations.version, collection, session);
Expand Down Expand Up @@ -232,27 +234,12 @@
await serverProxy.annotations.uploadAnnotations(sessionType, session.id, file, loader.name);
}

async function dumpAnnotations(session, name, dumper) {
if (!(dumper instanceof Dumper)) {
throw new ArgumentError('A dumper must be instance of Dumper class');
}

let result = null;
const sessionType = session instanceof Task ? 'task' : 'job';
if (sessionType === 'job') {
result = await serverProxy.annotations.dumpAnnotations(session.task.id, name, dumper.name);
} else {
result = await serverProxy.annotations.dumpAnnotations(session.id, name, dumper.name);
}

return result;
}

function importAnnotations(session, data) {
const sessionType = session instanceof Task ? 'task' : 'job';
const cache = getCache(sessionType);

if (cache.has(session)) {
// eslint-disable-next-line no-unsanitized/method
return cache.get(session).collection.import(data);
}

Expand All @@ -274,16 +261,25 @@
);
}

async function exportDataset(session, format) {
async function exportDataset(instance, format, name, saveImages = false) {
if (!(format instanceof String || typeof format === 'string')) {
throw new ArgumentError('Format must be a string');
}
if (!(session instanceof Task)) {
throw new ArgumentError('A dataset can only be created from a task');
if (!(instance instanceof Task || instance instanceof Project || instance instanceof Job)) {
throw new ArgumentError('A dataset can only be created from a job, task or project');
}
if (typeof saveImages !== 'boolean') {
throw new ArgumentError('Save images parameter must be a boolean');
}

let result = null;
result = await serverProxy.tasks.exportDataset(session.id, format);
if (instance instanceof Task) {
result = await serverProxy.tasks.exportDataset(instance.id, format, name, saveImages);
} else if (instance instanceof Job) {
result = await serverProxy.tasks.exportDataset(instance.task.id, format, name, saveImages);
} else {
result = await serverProxy.projects.exportDataset(instance.id, format, name, saveImages);
}

return result;
}
Expand Down Expand Up @@ -367,7 +363,6 @@
annotationsStatistics,
selectObject,
uploadAnnotations,
dumpAnnotations,
importAnnotations,
exportAnnotations,
exportDataset,
Expand Down
3 changes: 2 additions & 1 deletion cvat-core/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ function build() {
const Review = require('./review');
const { Job, Task } = require('./session');
const { Project } = require('./project');
const implementProject = require('./project-implementation');
const { Attribute, Label } = require('./labels');
const MLModel = require('./ml-model');
const { FrameData } = require('./frames');
Expand Down Expand Up @@ -754,7 +755,7 @@ function build() {
*/
classes: {
User,
Project,
Project: implementProject(Project),
Task,
Job,
Log,
Expand Down
74 changes: 74 additions & 0 deletions cvat-core/src/project-implementation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

(() => {
const serverProxy = require('./server-proxy');
const { getPreview } = require('./frames');

const { Project } = require('./project');
const { exportDataset } = require('./annotations');

function implementProject(projectClass) {
projectClass.prototype.save.implementation = async function () {
const trainingProjectCopy = this.trainingProject;
if (typeof this.id !== 'undefined') {
// project has been already created, need to update some data
const projectData = {
name: this.name,
assignee_id: this.assignee ? this.assignee.id : null,
bug_tracker: this.bugTracker,
labels: [...this._internalData.labels.map((el) => el.toJSON())],
};

if (trainingProjectCopy) {
projectData.training_project = trainingProjectCopy;
}

await serverProxy.projects.save(this.id, projectData);
return this;
}

// initial creating
const projectSpec = {
name: this.name,
labels: [...this.labels.map((el) => el.toJSON())],
};

if (this.bugTracker) {
projectSpec.bug_tracker = this.bugTracker;
}

if (trainingProjectCopy) {
projectSpec.training_project = trainingProjectCopy;
}

const project = await serverProxy.projects.create(projectSpec);
return new Project(project);
};

projectClass.prototype.delete.implementation = async function () {
const result = await serverProxy.projects.delete(this.id);
return result;
};

projectClass.prototype.preview.implementation = async function () {
if (!this._internalData.task_ids.length) {
return '';
}
const frameData = await getPreview(this._internalData.task_ids[0]);
return frameData;
};

projectClass.prototype.annotations.exportDataset.implementation = async function (
format, saveImages, customName,
) {
const result = exportDataset(this, format, customName, saveImages);
return result;
};

return projectClass;
}

module.exports = implementProject;
})();
86 changes: 31 additions & 55 deletions cvat-core/src/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@

(() => {
const PluginRegistry = require('./plugins');
const serverProxy = require('./server-proxy');
const { ArgumentError } = require('./exceptions');
const { Task } = require('./session');
const { Label } = require('./labels');
const { getPreview } = require('./frames');
const User = require('./user');

/**
Expand Down Expand Up @@ -203,7 +201,7 @@
},
},
/**
* Tasks linked with the project
* Tasks related with the project
* @name tasks
* @type {module:API.cvat.classes.Task[]}
* @memberof module:API.cvat.classes.Project
Expand All @@ -214,7 +212,7 @@
get: () => [...data.tasks],
},
/**
* Subsets array for linked tasks
* Subsets array for related tasks
* @name subsets
* @type {string[]}
* @memberof module:API.cvat.classes.Project
Expand Down Expand Up @@ -254,6 +252,13 @@
},
}),
);

// When we call a function, for example: project.annotations.get()
// In the method get we lose the project context
// So, we need return it
this.annotations = {
exportDataset: Object.getPrototypeOf(this).annotations.exportDataset.bind(this),
};
}

/**
Expand Down Expand Up @@ -289,7 +294,7 @@
}

/**
* Method deletes a task from a server
* Method deletes a project from a server
* @method delete
* @memberof module:API.cvat.classes.Project
* @readonly
Expand All @@ -304,57 +309,28 @@
}
}

Object.defineProperties(
Project.prototype,
Object.freeze({
annotations: Object.freeze({
value: {
async exportDataset(format, saveImages, customName = '') {
const result = await PluginRegistry.apiWrapper.call(
this,
Project.prototype.annotations.exportDataset,
format,
saveImages,
customName,
);
return result;
},
},
writable: true,
}),
}),
);

module.exports = {
Project,
};

Project.prototype.save.implementation = async function () {
const trainingProjectCopy = this.trainingProject;
if (typeof this.id !== 'undefined') {
// project has been already created, need to update some data
const projectData = {
name: this.name,
assignee_id: this.assignee ? this.assignee.id : null,
bug_tracker: this.bugTracker,
labels: [...this._internalData.labels.map((el) => el.toJSON())],
};

if (trainingProjectCopy) {
projectData.training_project = trainingProjectCopy;
}

await serverProxy.projects.save(this.id, projectData);
return this;
}

// initial creating
const projectSpec = {
name: this.name,
labels: [...this.labels.map((el) => el.toJSON())],
};

if (this.bugTracker) {
projectSpec.bug_tracker = this.bugTracker;
}

if (trainingProjectCopy) {
projectSpec.training_project = trainingProjectCopy;
}

const project = await serverProxy.projects.create(projectSpec);
return new Project(project);
};

Project.prototype.delete.implementation = async function () {
const result = await serverProxy.projects.delete(this.id);
return result;
};

Project.prototype.preview.implementation = async function () {
if (!this._internalData.task_ids.length) {
return '';
}
const frameData = await getPreview(this._internalData.task_ids[0]);
return frameData;
};
})();
Loading

0 comments on commit f18b1cb

Please sign in to comment.