提交 6025151c 编写于 作者: B Boris Sekachev 提交者: Nikita Manovich

CVAT.js other implemented API methods and bug fixes (#569)

* Split implementation

* Group implementation

* Reset cache for created object after saving

* A litle fix

* API methods definitions

* Typos

* API methods annotations.Statistics() and annotationsclear()

* Object selection

* Little fix

* Comments and docs a little changed
上级 0a2cb949
......@@ -23,6 +23,7 @@
objectStateFactory,
} = require('./annotations-objects');
const { checkObjectType } = require('./common');
const Statistics = require('./statistics');
const colors = [
'#0066FF', '#AF593E', '#01A368', '#FF861F', '#ED0A3F', '#FF3F34', '#76D7EA',
......@@ -115,10 +116,14 @@
this.objects = {}; // key is a client id
this.count = 0;
this.flush = false;
this.collectionZ = {}; // key is a frame
this.collectionZ = {}; // key is a frame, {max, min} are values
this.groups = {
max: 0,
}; // it is an object to we can pass it as an argument by a reference
this.injection = {
labels: this.labels,
collectionZ: this.collectionZ,
groups: this.groups,
};
}
......@@ -173,16 +178,6 @@
return data;
}
empty() {
this.shapes = {};
this.tags = {};
this.tracks = [];
this.objects = {}; // by id
this.count = 0;
this.flush = true;
}
get(frame) {
const { tracks } = this;
const shapes = this.shapes[frame] || [];
......@@ -206,7 +201,7 @@
}
merge(objectStates) {
checkObjectType('merged shapes', objectStates, null, Array);
checkObjectType('shapes for merge', objectStates, null, Array);
if (!objectStates.length) return;
const objectsForMerge = objectStates.map((state) => {
checkObjectType('object state', state, null, window.cvat.classes.ObjectState);
......@@ -364,30 +359,264 @@
};
const trackModel = trackFactory(track, clientID, this.injection);
if (trackModel) {
this.tracks.push(trackModel);
this.objects[clientID] = trackModel;
}
this.tracks.push(trackModel);
this.objects[clientID] = trackModel;
// Remove other shapes
for (const object of objectsForMerge) {
object.removed = true;
object.resetCache();
}
}
split(objectState) {
checkObjectType('object state', objectState, window.cvat.classes.ObjectState, null);
split(objectState, frame) {
checkObjectType('object state', objectState, null, window.cvat.classes.ObjectState);
checkObjectType('frame', frame, 'integer', null);
const object = this.objects[objectState.clientID];
if (typeof (object) === 'undefined') {
throw new window.cvat.exceptions.ArgumentError(
'The object has not been saved yet. Call annotations.put(state) before',
);
}
if (objectState.objectType !== window.cvat.enums.ObjectType.TRACK) {
return;
}
const keyframes = Object.keys(object.shapes).sort((a, b) => +a - +b);
if (frame <= +keyframes[0]) {
return;
}
// TODO: split
const labelAttributes = object.label.attributes.reduce((accumulator, attribute) => {
accumulator[attribute.id] = attribute;
return accumulator;
}, {});
const exported = object.toJSON();
const position = {
type: objectState.shapeType,
points: [...objectState.points],
occluded: objectState.occluded,
outside: objectState.outside,
zOrder: 0,
attributes: Object.keys(objectState.attributes)
.reduce((accumulator, attrID) => {
if (!labelAttributes[attrID].mutable) {
accumulator.push({
spec_id: +attrID,
value: objectState.attributes[attrID],
});
}
return accumulator;
}, []),
frame,
};
const prev = {
frame: exported.frame,
group: 0,
label_id: exported.label_id,
attributes: exported.attributes,
shapes: [],
};
const next = JSON.parse(JSON.stringify(prev));
next.frame = frame;
next.shapes.push(JSON.parse(JSON.stringify(position)));
exported.shapes.map((shape) => {
delete shape.id;
if (shape.frame < frame) {
prev.shapes.push(JSON.parse(JSON.stringify(shape)));
} else if (shape.frame > frame) {
next.shapes.push(JSON.parse(JSON.stringify(shape)));
}
return shape;
});
prev.shapes.push(position);
prev.shapes[prev.shapes.length - 1].outside = true;
let clientID = ++this.count;
const prevTrack = trackFactory(prev, clientID, this.injection);
this.tracks.push(prevTrack);
this.objects[clientID] = prevTrack;
clientID = ++this.count;
const nextTrack = trackFactory(next, clientID, this.injection);
this.tracks.push(nextTrack);
this.objects[clientID] = nextTrack;
// Remove source object
object.removed = true;
object.resetCache();
}
group(array) {
checkObjectType('merged shapes', array, Array, null);
for (const shape of array) {
checkObjectType('object state', shape, window.cvat.classes.ObjectState, null);
group(objectStates, reset) {
checkObjectType('shapes for group', objectStates, null, Array);
const objectsForGroup = objectStates.map((state) => {
checkObjectType('object state', state, null, window.cvat.classes.ObjectState);
const object = this.objects[state.clientID];
if (typeof (object) === 'undefined') {
throw new window.cvat.exceptions.ArgumentError(
'The object has not been saved yet. Call annotations.put(state) before',
);
}
return object;
});
const groupIdx = reset ? 0 : ++this.groups.max;
for (const object of objectsForGroup) {
object.group = groupIdx;
object.resetCache();
}
}
// TODO:
clear() {
this.shapes = {};
this.tags = {};
this.tracks = [];
this.objects = {}; // by id
this.count = 0;
this.flush = true;
}
statistics() {
const labels = {};
const skeleton = {
rectangle: {
shape: 0,
track: 0,
},
polygon: {
shape: 0,
track: 0,
},
polyline: {
shape: 0,
track: 0,
},
points: {
shape: 0,
track: 0,
},
tags: 0,
manually: 0,
interpolated: 0,
total: 0,
};
const total = JSON.parse(JSON.stringify(skeleton));
for (const label of Object.values(this.labels)) {
const { name } = label;
labels[name] = JSON.parse(JSON.stringify(skeleton));
}
for (const object of Object.values(this.objects)) {
let objectType = null;
if (object instanceof Shape) {
objectType = 'shape';
} else if (object instanceof Track) {
objectType = 'track';
} else if (object instanceof Tag) {
objectType = 'tag';
} else {
throw window.cvat.exceptions.ScriptingError(
`Unexpected object type: "${objectType}"`,
);
}
const label = object.label.name;
if (objectType === 'tag') {
labels[label].tags++;
labels[label].manually++;
labels[label].total++;
} else {
const { shapeType } = object;
labels[label][shapeType][objectType]++;
if (objectType === 'track') {
const keyframes = Object.keys(object.shapes)
.sort((a, b) => +a - +b).map(el => +el);
let prevKeyframe = keyframes[0];
let visible = false;
for (const keyframe of keyframes) {
if (visible) {
const interpolated = keyframe - prevKeyframe - 1;
labels[label].interpolated += interpolated;
labels[label].total += interpolated;
}
visible = !object.shapes[keyframe].outside;
prevKeyframe = keyframe;
if (visible) {
labels[label].manually++;
labels[label].total++;
}
}
} else {
labels[label].manually++;
labels[label].total++;
}
}
}
for (const label of Object.keys(labels)) {
for (const key of Object.keys(labels[label])) {
if (typeof (labels[label][key]) === 'object') {
for (const objectType of Object.keys(labels[label][key])) {
total[key][objectType] += labels[label][key][objectType];
}
} else {
total[key] += labels[label][key];
}
}
}
return new Statistics(labels, total);
}
put() {
throw new window.cvat.exceptions.ScriptingError(
'Is not implemented',
);
}
select(objectStates, x, y) {
checkObjectType('shapes for select', objectStates, null, Array);
checkObjectType('x coordinate', x, 'number', null);
checkObjectType('y coordinate', y, 'number', null);
let minimumDistance = null;
let minimumState = null;
for (const state of objectStates) {
checkObjectType('object state', state, null, window.cvat.classes.ObjectState);
if (state.outside) continue;
const object = this.objects[state.clientID];
if (typeof (object) === 'undefined') {
throw new window.cvat.exceptions.ArgumentError(
'The object has not been saved yet. Call annotations.put(state) before',
);
}
const distance = object.constructor.distance(state.points, x, y);
if (distance !== null && (minimumDistance === null || distance < minimumDistance)) {
minimumDistance = distance;
minimumState = state;
}
}
return {
state: minimumState,
distance: minimumDistance,
};
}
}
......
......@@ -40,6 +40,8 @@
return attributeAccumulator;
}, {});
this.appendDefaultAttributes(this.label);
injection.groups.max = Math.max(injection.groups.max, this.group);
}
appendDefaultAttributes(label) {
......@@ -283,6 +285,11 @@
// Method is used to export data to the server
toJSON() {
const labelAttributes = this.label.attributes.reduce((accumulator, attribute) => {
accumulator[attribute.id] = attribute;
return accumulator;
}, {});
return {
clientID: this.clientID,
id: this.serverID,
......@@ -290,10 +297,12 @@
label_id: this.label.id,
group: this.group,
attributes: Object.keys(this.attributes).reduce((attributeAccumulator, attrId) => {
attributeAccumulator.push({
spec_id: attrId,
value: this.attributes[attrId],
});
if (!labelAttributes[attrId].mutable) {
attributeAccumulator.push({
spec_id: attrId,
value: this.attributes[attrId],
});
}
return attributeAccumulator;
}, []),
......@@ -306,10 +315,12 @@
outside: this.shapes[frame].outside,
attributes: Object.keys(this.shapes[frame].attributes)
.reduce((attributeAccumulator, attrId) => {
attributeAccumulator.push({
spec_id: attrId,
value: this.shapes[frame].attributes[attrId],
});
if (labelAttributes[attrId].mutable) {
attributeAccumulator.push({
spec_id: attrId,
value: this.shapes[frame].attributes[attrId],
});
}
return attributeAccumulator;
}, []),
......@@ -615,6 +626,19 @@
`No one neightbour frame found for the track with client ID: "${this.id}"`,
);
}
delete(force) {
if (!this.lock || force) {
this.removed = true;
this.resetCache();
}
return true;
}
resetCache() {
this.cache = {};
}
}
class Tag extends Annotation {
......@@ -720,6 +744,18 @@
super(data, clientID, color, injection);
this.shapeType = window.cvat.enums.ObjectShape.RECTANGLE;
}
static distance(points, x, y) {
const [xtl, ytl, xbr, ybr] = points;
if (!(x >= xtl && x <= xbr && y >= ytl && y <= ybr)) {
// Cursor is outside of a box
return null;
}
// The shortest distance from point to an edge
return Math.min.apply(null, [x - xtl, y - ytl, xbr - x, ybr - y]);
}
}
class PolyShape extends Shape {
......@@ -733,6 +769,66 @@
super(data, clientID, color, injection);
this.shapeType = window.cvat.enums.ObjectShape.POLYGON;
}
static distance(points, x, y) {
function position(x1, y1, x2, y2) {
return ((x1 - x) * (y2 - y) - (x2 - x) * (y1 - y));
}
let wn = 0;
const distances = [];
for (let i = 0; i < points.length; i += 2) {
// Current point
const x1 = points[i];
const y1 = points[i + 1];
// Next point
const x2 = i + 2 < points.length ? points[i + 2] : points[0];
const y2 = i + 3 < points.length ? points[i + 3] : points[1];
// Check if a point is inside a polygon
// with a winding numbers algorithm
// https://en.wikipedia.org/wiki/Point_in_polygon#Winding_number_algorithm
if (y1 <= y) {
if (y2 > y) {
if (position(x1, y1, x2, y2) > 0) {
wn++;
}
}
} else if (y2 < y) {
if (position(x1, y1, x2, y2) > 0) {
wn--;
}
}
// Find the shortest distance from point to an edge
if (((x - x1) * (x2 - x)) >= 0 && ((y - y1) * (y2 - y)) >= 0) {
// Find the length of a perpendicular
// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
distances.push(
Math.abs((y2 - y1) * x - (x2 - x1) * y + x2 * y1 - y2 * x1) / Math
.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2)),
);
} else {
// The link below works for lines (which have infinit length)
// There is a case when perpendicular doesn't cross the edge
// In this case we don't use the computed distance
// Instead we use just distance to the nearest point
distances.push(
Math.min(
Math.sqrt(Math.pow(x1 - x, 2) + Math.pow(y1 - y, 2)),
Math.sqrt(Math.pow(x2 - x, 2) + Math.pow(y2 - y, 2)),
),
);
}
}
if (wn !== 0) {
return Math.min.apply(null, distances);
}
return null;
}
}
class PolylineShape extends PolyShape {
......@@ -740,6 +836,42 @@
super(data, clientID, color, injection);
this.shapeType = window.cvat.enums.ObjectShape.POLYLINE;
}
static distance(points, x, y) {
const distances = [];
for (let i = 0; i < points.length - 2; i += 2) {
// Current point
const x1 = points[i];
const y1 = points[i + 1];
// Next point
const x2 = points[i + 2];
const y2 = points[i + 3];
// Find the shortest distance from point to an edge
if (((x - x1) * (x2 - x)) >= 0 && ((y - y1) * (y2 - y)) >= 0) {
// Find the length of a perpendicular
// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
distances.push(
Math.abs((y2 - y1) * x - (x2 - x1) * y + x2 * y1 - y2 * x1) / Math
.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2)),
);
} else {
// The link below works for lines (which have infinit length)
// There is a case when perpendicular doesn't cross the edge
// In this case we don't use the computed distance
// Instead we use just distance to the nearest point
distances.push(
Math.min(
Math.sqrt(Math.pow(x1 - x, 2) + Math.pow(y1 - y, 2)),
Math.sqrt(Math.pow(x2 - x, 2) + Math.pow(y2 - y, 2)),
),
);
}
}
return Math.min.apply(null, distances);
}
}
class PointsShape extends PolyShape {
......@@ -747,6 +879,20 @@
super(data, clientID, color, injection);
this.shapeType = window.cvat.enums.ObjectShape.POINTS;
}
static distance(points, x, y) {
const distances = [];
for (let i = 0; i < points.length; i += 2) {
const x1 = points[i];
const y1 = points[i + 1];
distances.push(
Math.sqrt(Math.pow(x1 - x, 2) + Math.pow(y1 - y, 2)),
);
}
return Math.min.apply(null, distances);
}
}
class RectangleTrack extends Track {
......@@ -1181,6 +1327,11 @@
}
}
RectangleTrack.distance = RectangleShape.distance;
PolygonTrack.distance = PolygonShape.distance;
PolylineTrack.distance = PolylineShape.distance;
PointsTrack.distance = PointsShape.distance;
module.exports = {
RectangleShape,
PolygonShape,
......
......@@ -151,6 +151,10 @@
for (let i = 0; i < indexes[type].length; i++) {
const clientID = indexes[type][i];
this.collection.objects[clientID].serverID = saved[type][i].id;
if (type === 'tracks') {
// We have to reset cache because of old value of serverID was saved there
this.collection.objects[clientID].resetCache();
}
}
}
}
......
......@@ -29,7 +29,7 @@
);
}
async function getAnnotations(session, frame, filter) {
async function getAnnotationsFromServer(session) {
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
......@@ -45,7 +45,12 @@
saver,
};
}
}
async function getAnnotations(session, frame, filter) {
await getAnnotationsFromServer(session);
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
return cache[session.id].collection.get(frame, filter);
}
......@@ -69,7 +74,7 @@
}
throw window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() before',
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
......@@ -82,20 +87,20 @@
}
throw window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() before',
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
function groupAnnotations(session, objectStates) {
function groupAnnotations(session, objectStates, reset) {
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
return cache[session.id].collection.group(objectStates);
return cache[session.id].collection.group(objectStates, reset);
}
throw window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() before',
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
......@@ -110,12 +115,69 @@
return false;
}
async function clearAnnotations(session, reload) {
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
cache[session.id].collection.clear();
}
if (reload) {
delete cache[session.id];
await getAnnotationsFromServer(session);
}
}
function annotationsStatistics(session) {
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
return cache[session.id].collection.statistics();
}
throw window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
function putAnnotations(session, objectStates) {
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
return cache[session.id].collection.put(objectStates);
}
throw window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
function selectObject(session, objectStates, x, y) {
const sessionType = session instanceof window.cvat.classes.Task ? 'task' : 'job';
const cache = getCache(sessionType);
if (session.id in cache) {
return cache[session.id].collection.select(objectStates, x, y);
}
throw window.cvat.exceptions.DataError(
'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before',
);
}
module.exports = {
getAnnotations,
putAnnotations,
saveAnnotations,
hasUnsavedChanges,
mergeAnnotations,
splitAnnotations,
groupAnnotations,
clearAnnotations,
annotationsStatistics,
selectObject,
};
})();
......@@ -54,22 +54,20 @@
}
throw new window.cvat.exceptions.ArgumentError(
`Got "${name}" value of type: "${typeof (value)}". `
+ `Expected "${type}"`,
`"${name}" is expected to be "${type}", but "${typeof (value)}" has been got.`,
);
}
} else if (instance) {
if (!(value instanceof instance)) {
if (value !== undefined) {
throw new window.cvat.exceptions.ArgumentError(
`Got ${value.constructor.name} value for ${name}. `
+ `Expected instance of ${instance.name}`,
`${name} is expected to be ${instance.name}, but `
+ `"${value.constructor.name}" has been got`,
);
}
throw new window.cvat.exceptions.ArgumentError(
`Got undefined value for ${name}. `
+ `Expected instance of ${instance.name}`,
`"${name}" is expected to be ${instance.name}, but "undefined" has been got.`,
);
}
}
......
......@@ -13,11 +13,15 @@
const { getFrame } = require('./frames');
const {
getAnnotations,
putAnnotations,
saveAnnotations,
hasUnsavedChanges,
mergeAnnotations,
splitAnnotations,
groupAnnotations,
clearAnnotations,
selectObject,
annotationsStatistics,
} = require('./annotations');
function buildDublicatedAPI(prototype) {
......@@ -36,9 +40,9 @@
return result;
},
async clear() {
async clear(reload = false) {
const result = await PluginRegistry
.apiWrapper.call(this, prototype.annotations.clear);
.apiWrapper.call(this, prototype.annotations.clear, reload);
return result;
},
......@@ -73,9 +77,10 @@
return result;
},
async select(frame, x, y) {
async select(objectStates, x, y) {
const result = await PluginRegistry
.apiWrapper.call(this, prototype.annotations.select, frame, x, y);
.apiWrapper.call(this,
prototype.annotations.select, objectStates, x, y);
return result;
},
......@@ -97,9 +102,10 @@
return result;
},
async group(objectStates) {
async group(objectStates, reset = false) {
const result = await PluginRegistry
.apiWrapper.call(this, prototype.annotations.group, objectStates);
.apiWrapper.call(this, prototype.annotations.group,
objectStates, reset);
return result;
},
},
......@@ -193,6 +199,10 @@
*/
/**
* Save all changes in annotations on a server
* Objects which hadn't been saved on a server before,
* get a serverID after saving. But received object states aren't updated.
* So, after successful saving it's recommended to update them manually
* (call the annotations.get() again)
* @method save
* @memberof Session.annotations
* @throws {module:API.cvat.exceptions.PluginError}
......@@ -204,10 +214,14 @@
* Its argument is a text string
*/
/**
* Remove all annotations from a session
* Remove all annotations and optionally reinitialize it
* @method clear
* @memberof Session.annotations
* @param {boolean} [reload = false] reset all changes and
* reinitialize annotations by data from a server
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
* @throws {module:API.cvat.exceptions.ServerError}
* @instance
* @async
*/
......@@ -233,7 +247,7 @@
* @async
*/
/**
* Add some annotations to a session
* Create new objects from one-frame states
* @method put
* @memberof Session.annotations
* @param {module:API.cvat.classes.ObjectState[]} data
......@@ -282,14 +296,16 @@
* @async
*/
/**
* Select shape under a cursor using smart alghorithms
* Select shape under a cursor using math alghorithms
* @method select
* @memberof Session.annotations
* @param {integer} frame frame for selecting
* @param {module:API.cvat.classes.ObjectState[]} objectStates
* object which can be selected
* @param {float} x horizontal coordinate
* @param {float} y vertical coordinate
* @returns {(integer|null)}
* an ID of a selected object or null if no one of objects is on position
* @returns {Object}
* a pair of {state: ObjectState, distance: number} for selected object.
* Pair values can be null if there aren't any sutisfied objects
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
* @instance
......@@ -327,6 +343,7 @@
* @method group
* @memberof Session.annotations
* @param {module:API.cvat.classes.ObjectState[]} objectStates
* @param {boolean} reset pass "true" to reset group value (set it to 0)
* @returns {integer} an ID of created group
* @throws {module:API.cvat.exceptions.ArgumentError}
* @throws {module:API.cvat.exceptions.PluginError}
......@@ -585,10 +602,14 @@
// So, we need return it
this.annotations = {
get: Object.getPrototypeOf(this).annotations.get.bind(this),
put: Object.getPrototypeOf(this).annotations.put.bind(this),
save: Object.getPrototypeOf(this).annotations.save.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),
select: Object.getPrototypeOf(this).annotations.select.bind(this),
statistics: Object.getPrototypeOf(this).annotations.statistics.bind(this),
hasUnsavedChanges: Object.getPrototypeOf(this)
.annotations.hasUnsavedChanges.bind(this),
};
......@@ -665,23 +686,48 @@
};
Job.prototype.annotations.save.implementation = async function (onUpdate) {
await saveAnnotations(this, onUpdate);
const result = await saveAnnotations(this, onUpdate);
return result;
};
Job.prototype.annotations.merge.implementation = async function (objectStates) {
await mergeAnnotations(this, objectStates);
const result = await mergeAnnotations(this, objectStates);
return result;
};
Job.prototype.annotations.split.implementation = async function (objectState, frame) {
await splitAnnotations(this, objectState, frame);
const result = await splitAnnotations(this, objectState, frame);
return result;
};
Job.prototype.annotations.group.implementation = async function (objectStates, reset) {
const result = await groupAnnotations(this, objectStates, reset);
return result;
};
Job.prototype.annotations.hasUnsavedChanges.implementation = function () {
const result = hasUnsavedChanges(this);
return result;
};
Job.prototype.annotations.clear.implementation = async function (reload) {
const result = await clearAnnotations(this, reload);
return result;
};
Job.prototype.annotations.select.implementation = function (frame, x, y) {
const result = selectObject(this, frame, x, y);
return result;
};
Job.prototype.annotations.group.implementation = async function (objectStates) {
await groupAnnotations(this, objectStates);
Job.prototype.annotations.statistics.implementation = function () {
const result = annotationsStatistics(this);
return result;
};
Job.prototype.annotations.hasUnsavedChanges.implementation = async function () {
return hasUnsavedChanges(this);
Job.prototype.annotations.put.implementation = function (objectStates) {
const result = putAnnotations(this, objectStates);
return result;
};
/**
......@@ -1090,10 +1136,14 @@
// So, we need return it
this.annotations = {
get: Object.getPrototypeOf(this).annotations.get.bind(this),
put: Object.getPrototypeOf(this).annotations.put.bind(this),
save: Object.getPrototypeOf(this).annotations.save.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),
select: Object.getPrototypeOf(this).annotations.select.bind(this),
statistics: Object.getPrototypeOf(this).annotations.statistics.bind(this),
hasUnsavedChanges: Object.getPrototypeOf(this)
.annotations.hasUnsavedChanges.bind(this),
};
......@@ -1187,7 +1237,8 @@
};
Task.prototype.delete.implementation = async function () {
await serverProxy.tasks.deleteTask(this.id);
const result = await serverProxy.tasks.deleteTask(this.id);
return result;
};
Task.prototype.frames.get.implementation = async function (frame) {
......@@ -1197,8 +1248,8 @@
);
}
const frameData = await getFrame(this.id, this.mode, frame);
return frameData;
const result = await getFrame(this.id, this.mode, frame);
return result;
};
// TODO: Check filter for annotations
......@@ -1215,28 +1266,53 @@
);
}
const annotationsData = await getAnnotations(this, frame, filter);
return annotationsData;
const result = await getAnnotations(this, frame, filter);
return result;
};
Task.prototype.annotations.save.implementation = async function (onUpdate) {
await saveAnnotations(this, onUpdate);
const result = await saveAnnotations(this, onUpdate);
return result;
};
Task.prototype.annotations.merge.implementation = async function (objectStates) {
await mergeAnnotations(this, objectStates);
const result = await mergeAnnotations(this, objectStates);
return result;
};
Task.prototype.annotations.split.implementation = async function (objectState, frame) {
await splitAnnotations(this, objectState, frame);
const result = await splitAnnotations(this, objectState, frame);
return result;
};
Task.prototype.annotations.group.implementation = async function (objectStates, reset) {
const result = await groupAnnotations(this, objectStates, reset);
return result;
};
Task.prototype.annotations.hasUnsavedChanges.implementation = function () {
const result = hasUnsavedChanges(this);
return result;
};
Task.prototype.annotations.clear.implementation = async function (reload) {
const result = await clearAnnotations(this, reload);
return result;
};
Task.prototype.annotations.select.implementation = function (frame, x, y) {
const result = selectObject(this, frame, x, y);
return result;
};
Task.prototype.annotations.group.implementation = async function (objectStates) {
await groupAnnotations(this, objectStates);
Task.prototype.annotations.statistics.implementation = function () {
const result = annotationsStatistics(this);
return result;
};
Task.prototype.annotations.hasUnsavedChanges.implementation = async function () {
return hasUnsavedChanges(this);
Task.prototype.annotations.put.implementation = function (objectStates) {
const result = putAnnotations(this, objectStates);
return result;
};
module.exports = {
......
......@@ -6,7 +6,7 @@
(() => {
/**
* Class representing statistics inside a session
* Class representing collection statistics
* @memberof module:API.cvat.classes
* @hideconstructor
*/
......@@ -14,27 +14,28 @@
constructor(label, total) {
Object.defineProperties(this, Object.freeze({
/**
* Statistics by labels which have a structure:
* Statistics by labels with a structure:
* @example
* {
* label1: {
* label: {
* boxes: {
* tracks: 10,
* shapes: 11,
* },
* polygons: {
* tracks: 12,
* shapes: 13,
* tracks: 13,
* shapes: 14,
* },
* polylines: {
* tracks: 14,
* shapes: 15,
* },
* points: {
* tracks: 16,
* shapes: 17,
* },
* manually: 108,
* points: {
* tracks: 19,
* shapes: 20,
* },
* tags: 66,
* manually: 186,
* interpolated: 500,
* total: 608,
* }
......@@ -49,30 +50,29 @@
get: () => JSON.parse(JSON.stringify(label)),
},
/**
* Total statistics (summed by all labels) which have a structure:
* Total statistics (covers all labels) with a structure:
* @example
* {
* total: {
* boxes: {
* boxes: {
* tracks: 10,
* shapes: 11,
* },
* polygons: {
* tracks: 12,
* shapes: 13,
* },
* polylines: {
* tracks: 14,
* shapes: 15,
* },
* points: {
* tracks: 16,
* shapes: 17,
* },
* manually: 108,
* interpolated: 500,
* total: 608,
* }
* },
* polygons: {
* tracks: 13,
* shapes: 14,
* },
* polylines: {
* tracks: 16,
* shapes: 17,
* },
* points: {
* tracks: 19,
* shapes: 20,
* },
* tags: 66,
* manually: 186,
* interpolated: 500,
* total: 608,
* }
* @name total
* @type {Object}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册