You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

357 lines
10 KiB
TypeScript

// Copyright (C) 2019-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
import { ShapeType, AttributeType } from './enums';
import { ArgumentError } from './exceptions';
type AttrInputType = 'select' | 'radio' | 'checkbox' | 'number' | 'text';
export interface RawAttribute {
name: string;
mutable: boolean;
input_type: AttrInputType;
default_value: string;
values: string[];
id?: number;
}
/**
* Class representing an attribute
* @memberof module:API.cvat.classes
* @hideconstructor
*/
export class Attribute {
public id?: number;
public defaultValue: string;
public inputType: AttrInputType;
public mutable: boolean;
public name: string;
public values: string[];
constructor(initialData: RawAttribute) {
const data = {
id: undefined,
default_value: undefined,
input_type: undefined,
mutable: undefined,
name: undefined,
values: undefined,
};
for (const key in data) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
if (Object.prototype.hasOwnProperty.call(initialData, key)) {
if (Array.isArray(initialData[key])) {
data[key] = [...initialData[key]];
} else {
data[key] = initialData[key];
}
}
}
}
if (!Object.values(AttributeType).includes(data.input_type)) {
throw new ArgumentError(`Got invalid attribute type ${data.input_type}`);
}
Object.defineProperties(
this,
Object.freeze({
/**
* @name id
* @type {number}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
id: {
get: () => data.id,
},
/**
* @name defaultValue
* @type {string}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
defaultValue: {
get: () => data.default_value,
},
/**
* @name inputType
* @type {module:API.cvat.enums.AttributeType}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
inputType: {
get: () => data.input_type,
},
/**
* @name mutable
* @type {boolean}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
mutable: {
get: () => data.mutable,
},
/**
* @name name
* @type {string}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
name: {
get: () => data.name,
},
/**
* @name values
* @type {string[]}
* @memberof module:API.cvat.classes.Attribute
* @readonly
* @instance
*/
values: {
get: () => [...data.values],
},
}),
);
}
toJSON(): RawAttribute {
const object: RawAttribute = {
name: this.name,
mutable: this.mutable,
input_type: this.inputType,
default_value: this.defaultValue,
values: this.values,
};
if (typeof this.id !== 'undefined') {
object.id = this.id;
}
return object;
}
}
type LabelType = 'rectangle' | 'polygon' | 'polyline' | 'points' | 'ellipse' | 'cuboid' | 'skeleton' | 'mask' | 'tag' | 'any';
export interface RawLabel {
id?: number;
name: string;
color?: string;
type: LabelType;
svg?: string;
sublabels?: RawLabel[];
has_parent?: boolean;
deleted?: boolean;
attributes: RawAttribute[];
}
/**
* Class representing a label
* @memberof module:API.cvat.classes
* @hideconstructor
*/
export class Label {
public name: string;
public readonly id?: number;
public readonly color?: string;
public readonly attributes: Attribute[];
public readonly type: LabelType;
public structure: {
sublabels: Label[];
svg: string;
} | null;
public deleted: boolean;
public readonly hasParent?: boolean;
constructor(initialData: RawLabel) {
const data = {
id: undefined,
name: undefined,
color: undefined,
type: undefined,
structure: undefined,
has_parent: false,
deleted: false,
svg: undefined,
elements: undefined,
sublabels: undefined,
attributes: [],
};
for (const key of Object.keys(data)) {
if (Object.prototype.hasOwnProperty.call(initialData, key)) {
data[key] = initialData[key];
}
}
data.attributes = [];
if (
Object.prototype.hasOwnProperty.call(initialData, 'attributes') &&
Array.isArray(initialData.attributes)
) {
for (const attrData of initialData.attributes) {
data.attributes.push(new Attribute(attrData));
}
}
if (data.type === 'skeleton') {
data.sublabels = data.sublabels.map((internalLabel) => new Label({ ...internalLabel, has_parent: true }));
}
Object.defineProperties(
this,
Object.freeze({
/**
* @name id
* @type {number}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
id: {
get: () => data.id,
},
/**
* @name name
* @type {string}
* @memberof module:API.cvat.classes.Label
* @instance
*/
name: {
get: () => data.name,
set: (name) => {
if (typeof name !== 'string') {
throw new ArgumentError(`Name must be a string, but ${typeof name} was given`);
}
data.name = name;
},
},
/**
* @name color
* @type {string}
* @memberof module:API.cvat.classes.Label
* @instance
*/
color: {
get: () => data.color,
set: (color) => {
if (typeof color === 'string' && color.match(/^#[0-9a-f]{6}$|^$/)) {
data.color = color;
} else {
throw new ArgumentError('Trying to set wrong color format');
}
},
},
/**
* @name attributes
* @type {module:API.cvat.classes.Attribute[]}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
attributes: {
get: () => [...data.attributes],
},
/**
* @typedef {Object} SkeletonStructure
* @property {module:API.cvat.classes.Label[]} sublabels A list of labels the skeleton includes
* @property {Object[]} svg An SVG representation of the skeleton
* A type of a file
* @global
*/
/**
* @name type
* @type {string | undefined}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
type: {
get: () => data.type,
},
/**
* @name type
* @type {SkeletonStructure | undefined}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
structure: {
get: () => {
if (data.type === ShapeType.SKELETON) {
return {
svg: data.svg,
sublabels: [...data.sublabels],
};
}
return null;
},
},
/**
* @name deleted
* @type {boolean}
* @memberof module:API.cvat.classes.Label
* @instance
*/
deleted: {
get: () => data.deleted,
set: (value) => {
data.deleted = value;
},
},
/**
* @name hasParent
* @type {boolean}
* @memberof module:API.cvat.classes.Label
* @readonly
* @instance
*/
hasParent: {
get: () => data.has_parent,
},
}),
);
}
toJSON(): RawLabel {
const object: RawLabel = {
name: this.name,
attributes: [...this.attributes.map((el) => el.toJSON())],
type: this.type,
};
if (typeof this.color !== 'undefined') {
object.color = this.color;
}
if (typeof this.id !== 'undefined') {
object.id = this.id;
}
if (this.deleted) {
object.deleted = this.deleted;
}
if (this.type) {
object.type = this.type;
}
const { structure } = this;
if (structure) {
object.svg = structure.svg;
object.sublabels = structure.sublabels.map((internalLabel) => internalLabel.toJSON());
}
return object;
}
}