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.

179 lines
5.9 KiB
JavaScript

/*
* Copyright (C) 2019 Intel Corporation
* SPDX-License-Identifier: MIT
*/
/* global
require:false
global:false
*/
(() => {
const PluginRegistry = require('./plugins');
const serverProxy = require('./server-proxy');
const { ArgumentError } = require('./exceptions');
const { isBrowser, isNode } = require('browser-or-node');
// This is the frames storage
const frameDataCache = {};
const frameCache = {};
/**
* Class provides meta information about specific frame and frame itself
* @memberof module:API.cvat.classes
* @hideconstructor
*/
class FrameData {
constructor(width, height, tid, number) {
Object.defineProperties(this, Object.freeze({
/**
* @name width
* @type {integer}
* @memberof module:API.cvat.classes.FrameData
* @readonly
* @instance
*/
width: {
value: width,
writable: false,
},
/**
* @name height
* @type {integer}
* @memberof module:API.cvat.classes.FrameData
* @readonly
* @instance
*/
height: {
value: height,
writable: false,
},
tid: {
value: tid,
writable: false,
},
/**
* @name number
* @type {integer}
* @memberof module:API.cvat.classes.FrameData
* @readonly
* @instance
*/
number: {
value: number,
writable: false,
},
}));
}
/**
* Method returns URL encoded image which can be placed in the img tag
* @method data
* @returns {string}
* @memberof module:API.cvat.classes.FrameData
* @instance
* @async
* @param {function} [onServerRequest = () => {}]
* callback which will be called if data absences local
* @throws {module:API.cvat.exception.ServerError}
* @throws {module:API.cvat.exception.PluginError}
*/
async data(onServerRequest = () => {}) {
const result = await PluginRegistry
.apiWrapper.call(this, FrameData.prototype.data, onServerRequest);
return result;
}
}
FrameData.prototype.data.implementation = async function (onServerRequest) {
return new Promise(async (resolve, reject) => {
try {
if (this.number in frameCache[this.tid]) {
resolve(frameCache[this.tid][this.number]);
} else {
onServerRequest();
const frame = await serverProxy.frames.getData(this.tid, this.number);
if (isNode) {
frameCache[this.tid][this.number] = global.Buffer.from(frame, 'binary').toString('base64');
resolve(frameCache[this.tid][this.number]);
} else if (isBrowser) {
const reader = new FileReader();
reader.onload = () => {
const image = new Image(frame.width, frame.height);
image.onload = () => {
frameCache[this.tid][this.number] = image;
resolve(frameCache[this.tid][this.number]);
};
image.src = reader.result;
};
reader.readAsDataURL(frame);
}
}
} catch (exception) {
reject(exception);
}
});
};
async function getPreview(taskID) {
return new Promise(async (resolve, reject) => {
try {
// Just go to server and get preview (no any cache)
const result = await serverProxy.frames.getPreview(taskID);
if (isNode) {
resolve(global.Buffer.from(result, 'binary').toString('base64'));
} else if (isBrowser) {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.readAsDataURL(result);
}
} catch (error) {
reject(error);
}
});
}
async function getFrame(taskID, mode, frame) {
if (!(taskID in frameDataCache)) {
frameDataCache[taskID] = {
meta: await serverProxy.frames.getMeta(taskID),
};
frameCache[taskID] = {};
}
if (!(frame in frameDataCache[taskID])) {
let size = null;
if (mode === 'interpolation') {
[size] = frameDataCache[taskID].meta;
} else if (mode === 'annotation') {
if (frame >= frameDataCache[taskID].meta.length) {
throw new ArgumentError(
`Meta information about frame ${frame} can't be received from the server`,
);
} else {
size = frameDataCache[taskID].meta[frame];
}
} else {
throw new ArgumentError(
`Invalid mode is specified ${mode}`,
);
}
frameDataCache[taskID][frame] = new FrameData(size.width, size.height, taskID, frame);
}
return frameDataCache[taskID][frame];
}
module.exports = {
FrameData,
getFrame,
getPreview,
};
})();