CVAT.js API methods were implemented (#572)

* annotations.put()

* Fixed annotations.put()

* Dump & upload

* Two fixes

* Tested upload

* Updated built of cvat.js

* Remote files through cvat.js
main
Boris Sekachev 7 years ago committed by Nikita Manovich
parent 3ba80d2e77
commit 8fede3a6e6

File diff suppressed because one or more lines are too long

@ -582,10 +582,106 @@
return new Statistics(labels, total);
}
put() {
throw new window.cvat.exceptions.ScriptingError(
'Is not implemented',
);
put(objectStates) {
checkObjectType('shapes for put', objectStates, null, Array);
const constructed = {
shapes: [],
tracks: [],
tags: [],
};
function convertAttributes(accumulator, attrID) {
const specID = +attrID;
const value = this.attributes[attrID];
checkObjectType('attribute id', specID, 'integer', null);
checkObjectType('attribute value', value, 'string', null);
accumulator.push({
spec_id: specID,
value,
});
return accumulator;
}
for (const state of objectStates) {
checkObjectType('object state', state, null, window.cvat.classes.ObjectState);
checkObjectType('state client ID', state.clientID, 'undefined', null);
checkObjectType('state frame', state.frame, 'integer', null);
checkObjectType('state attributes', state.attributes, null, Object);
checkObjectType('state label', state.label, null, window.cvat.classes.Label);
const attributes = Object.keys(state.attributes)
.reduce(convertAttributes.bind(state), []);
const labelAttributes = state.label.attributes.reduce((accumulator, attribute) => {
accumulator[attribute.id] = attribute;
return accumulator;
}, {});
// Construct whole objects from states
if (state.objectType === 'tag') {
constructed.tags.push({
attributes,
frame: state.frame,
label_id: state.label.id,
group: 0,
});
} else {
checkObjectType('state occluded', state.occluded, 'boolean', null);
checkObjectType('state points', state.points, null, Array);
for (const coord of state.points) {
checkObjectType('point coordinate', coord, 'number', null);
}
if (!Object.values(window.cvat.enums.ObjectShape).includes(state.shapeType)) {
throw new window.cvat.exceptions.ArgumentError(
'Object shape must be one of: '
+ `${JSON.stringify(Object.values(window.cvat.enums.ObjectShape))}`,
);
}
if (state.objectType === 'shape') {
constructed.shapes.push({
attributes,
frame: state.frame,
group: 0,
label_id: state.label.id,
occluded: state.occluded,
points: [...state.points],
type: state.shapeType,
z_order: 0,
});
} else if (state.objectType === 'track') {
constructed.tracks.push({
attributes: attributes
.filter(attr => !labelAttributes[attr.spec_id].mutable),
frame: state.frame,
group: 0,
label_id: state.label.id,
shapes: [{
attributes: attributes
.filter(attr => labelAttributes[attr.spec_id].mutable),
frame: state.frame,
occluded: state.occluded,
outside: false,
points: [...state.points],
type: state.shapeType,
z_order: 0,
}],
});
} else {
throw new window.cvat.exceptions.ArgumentError(
'Object type must be one of: '
+ `${JSON.stringify(Object.values(window.cvat.enums.ObjectType))}`,
);
}
}
}
// Add constructed objects to a collection
this.import(constructed);
}
select(objectStates, x, y) {

@ -168,6 +168,18 @@
);
}
async function uploadAnnotations(session, file, format) {
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
await serverProxy.annotations.uploadAnnotations(sessionType, session.id, file, format);
}
async function dumpAnnotations(session, name, format) {
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const result = await serverProxy.annotations
.dumpAnnotations(sessionType, session.id, name, format);
return result;
}
module.exports = {
getAnnotations,
putAnnotations,
@ -179,5 +191,7 @@
clearAnnotations,
annotationsStatistics,
selectObject,
uploadAnnotations,
dumpAnnotations,
};
})();

@ -18,7 +18,7 @@
/**
* @param {Object} serialized - is an dictionary which contains
* initial information about an ObjectState;
* Necessary fields: type, shape
* Necessary fields: objectType, shapeType
* Necessary fields for objects which haven't been added to collection yet: frame
* Optional fields: points, group, zOrder, outside, occluded,
* attributes, lock, label, mode, color, keyframe, clientID, serverID

@ -512,6 +512,75 @@
return response.data;
}
// Session is 'task' or 'job'
async function uploadAnnotations(session, id, file, format) {
const { backendAPI } = window.cvat.config;
let annotationData = new FormData();
annotationData.append('annotation_file', file);
return new Promise((resolve, reject) => {
async function request() {
try {
const response = await Axios
.post(`${backendAPI}/${session}s/${id}/annotations?upload_format=${format}`, annotationData, {
proxy: window.cvat.config.proxy,
});
if (response.status === 202) {
annotationData = new FormData();
setTimeout(request, 3000);
} else {
resolve();
}
} catch (errorData) {
const code = errorData.response
? errorData.response.status : errorData.code;
const error = new window.cvat.exceptions.ServerError(
`Could not upload annotations for the ${session} ${id}`,
code,
);
reject(error);
}
}
setTimeout(request);
});
}
// Session is 'task' or 'job'
async function dumpAnnotations(id, name, format) {
const { backendAPI } = window.cvat.config;
const filename = name.replace(/\//g, '_');
let url = `${backendAPI}/tasks/${id}/annotations/${filename}?dump_format=${format}`;
return new Promise((resolve, reject) => {
async function request() {
try {
const response = await Axios
.get(`${url}`, {
proxy: window.cvat.config.proxy,
});
if (response.status === 202) {
setTimeout(request, 3000);
} else {
url = `${url}&action=download`;
resolve(url);
}
} catch (errorData) {
const code = errorData.response
? errorData.response.status : errorData.code;
const error = new window.cvat.exceptions.ServerError(
`Could not dump annotations for the task ${id} from the server`,
code,
);
reject(error);
}
}
setTimeout(request);
});
}
// Set csrftoken header from browser cookies if it exists
// NodeJS env returns 'undefined'
// So in NodeJS we need login after each run
@ -570,6 +639,8 @@
value: Object.freeze({
updateAnnotations,
getAnnotations,
dumpAnnotations,
uploadAnnotations,
}),
writable: false,
},

@ -22,15 +22,17 @@
clearAnnotations,
selectObject,
annotationsStatistics,
uploadAnnotations,
dumpAnnotations,
} = require('./annotations');
function buildDublicatedAPI(prototype) {
Object.defineProperties(prototype, {
annotations: Object.freeze({
value: {
async upload(file) {
async upload(file, format) {
const result = await PluginRegistry
.apiWrapper.call(this, prototype.annotations.upload, file);
.apiWrapper.call(this, prototype.annotations.upload, file, format);
return result;
},
@ -46,9 +48,9 @@
return result;
},
async dump() {
async dump(name, format) {
const result = await PluginRegistry
.apiWrapper.call(this, prototype.annotations.dump);
.apiWrapper.call(this, prototype.annotations.dump, name, format);
return result;
},
@ -188,9 +190,11 @@
*/
/**
* Upload annotations from a dump file
* You need upload annotations from a server again after successful executing
* @method upload
* @memberof Session.annotations
* @param {File} [annotations] - text file with annotations
* @param {File} annotations - a text file with annotations
* @param {string} format - a format of the file
* @instance
* @async
* @throws {module:API.cvat.exceptions.PluginError}
@ -230,6 +234,8 @@
* Method always dumps annotations for a whole task.
* @method dump
* @memberof Session.annotations
* @param {string} name - a name of a file with annotations
* @param {string} format - a format of the file
* @returns {string} URL which can be used in order to get a dump file
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ServerError}
@ -237,8 +243,7 @@
* @async
*/
/**
* Collect some statistics from a session.
* For example number of shapes, tracks, polygons etc
* Collect short statistics about a task or a job.
* @method statistics
* @memberof Session.annotations
* @returns {module:API.cvat.classes.Statistics} statistics object
@ -248,6 +253,7 @@
*/
/**
* Create new objects from one-frame states
* After successful adding you need to update object states on a frame
* @method put
* @memberof Session.annotations
* @param {module:API.cvat.classes.ObjectState[]} data
@ -604,10 +610,12 @@
get: Object.getPrototypeOf(this).annotations.get.bind(this),
put: Object.getPrototypeOf(this).annotations.put.bind(this),
save: Object.getPrototypeOf(this).annotations.save.bind(this),
dump: Object.getPrototypeOf(this).annotations.dump.bind(this),
merge: Object.getPrototypeOf(this).annotations.merge.bind(this),
split: Object.getPrototypeOf(this).annotations.split.bind(this),
group: Object.getPrototypeOf(this).annotations.group.bind(this),
clear: Object.getPrototypeOf(this).annotations.clear.bind(this),
upload: Object.getPrototypeOf(this).annotations.upload.bind(this),
select: Object.getPrototypeOf(this).annotations.select.bind(this),
statistics: Object.getPrototypeOf(this).annotations.statistics.bind(this),
hasUnsavedChanges: Object.getPrototypeOf(this)
@ -730,6 +738,16 @@
return result;
};
Job.prototype.annotations.upload.implementation = async function (file, format) {
const result = await uploadAnnotations(this, file, format);
return result;
};
Job.prototype.annotations.dump.implementation = async function (name, format) {
const result = await dumpAnnotations(this, name, format);
return result;
};
/**
* Class representing a task
* @memberof module:API.cvat.classes
@ -1138,10 +1156,12 @@
get: Object.getPrototypeOf(this).annotations.get.bind(this),
put: Object.getPrototypeOf(this).annotations.put.bind(this),
save: Object.getPrototypeOf(this).annotations.save.bind(this),
dump: Object.getPrototypeOf(this).annotations.dump.bind(this),
merge: Object.getPrototypeOf(this).annotations.merge.bind(this),
split: Object.getPrototypeOf(this).annotations.split.bind(this),
group: Object.getPrototypeOf(this).annotations.group.bind(this),
clear: Object.getPrototypeOf(this).annotations.clear.bind(this),
upload: Object.getPrototypeOf(this).annotations.upload.bind(this),
select: Object.getPrototypeOf(this).annotations.select.bind(this),
statistics: Object.getPrototypeOf(this).annotations.statistics.bind(this),
hasUnsavedChanges: Object.getPrototypeOf(this)
@ -1229,7 +1249,7 @@
const taskFiles = {
client_files: this.clientFiles,
server_files: this.serverFiles,
remote_files: [], // hasn't been supported yet
remote_files: this.remoteFiles,
};
const task = await serverProxy.tasks.createTask(taskData, taskFiles, onUpdate);
@ -1315,6 +1335,16 @@
return result;
};
Task.prototype.annotations.upload.implementation = async function (file, format) {
const result = await uploadAnnotations(this, file, format);
return result;
};
Task.prototype.annotations.dump.implementation = async function (name, format) {
const result = await dumpAnnotations(this, name, format);
return result;
};
module.exports = {
Job,
Task,

Loading…
Cancel
Save