Single-point interpolation (#453)

main
Boris Sekachev 7 years ago committed by Nikita Manovich
parent fa4cf1eb00
commit 58e72a2986

@ -7,9 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Installation guide
- Linear interpolation for a single point
### Changed
-
- Outside and keyframe buttons in the side panel for all interpolation shapes (they were only for boxes before)
### Deprecated
-
@ -20,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Fixed incorrect width of shapes borders in some cases
- Fixed annotation parser for tracks with a start frame less than the first segment frame
- Fixed interpolation on the server near outside frames
### Security
-

@ -1090,12 +1090,19 @@ class TrackManager(ObjectManager):
step = np.subtract(shape1["points"], shape0["points"]) / distance
for frame in range(shape0["frame"] + 1, shape1["frame"]):
off = frame - shape0["frame"]
points = shape0["points"] + step * off
if shape1["outside"]:
points = np.asarray(shape0["points"]).reshape(-1, 2)
else:
points = (shape0["points"] + step * off).reshape(-1, 2)
shape = copy.deepcopy(shape0)
broken_line = geometry.LineString(points.reshape(-1, 2)).simplify(0.05, False)
if len(points) == 1:
shape["points"] = points.flatten()
else:
broken_line = geometry.LineString(points).simplify(0.05, False)
shape["points"] = [x for p in broken_line.coords for x in p]
shape["keyframe"] = False
shape["frame"] = frame
shape["points"] = [x for p in broken_line.coords for x in p]
shapes.append(shape)
return shapes

@ -54,12 +54,12 @@ class ShapeCreatorModel extends Listener {
});
}
if (this._defaultMode === 'interpolation' && this._defaultType === 'box') {
// FIXME: In the future we have to make some generic solution
if (this._defaultMode === 'interpolation' && ['box', 'points'].includes(this._defaultType)) {
data.shapes = [];
data.shapes.push(Object.assign({}, result, data));
this._shapeCollection.add(data, `interpolation_box`);
}
else {
this._shapeCollection.add(data, `interpolation_${this._defaultType}`);
} else {
Object.assign(data, result);
this._shapeCollection.add(data, `annotation_${this._defaultType}`);
}
@ -213,11 +213,14 @@ class ShapeCreatorView {
}
this._typeSelector.on('change', (e) => {
let type = $(e.target).prop('value');
if (type != 'box' && this._modeSelector.prop('value') != 'annotation') {
// FIXME: In the future we have to make some generic solution
const mode = this._modeSelector.prop('value');
const type = $(e.target).prop('value');
if (type !== 'box' && !(type === 'points' && this._polyShapeSize === 1)
&& mode !== 'annotation') {
this._modeSelector.prop('value', 'annotation');
this._controller.setDefaultShapeMode('annotation');
showMessage('Poly shapes available only like annotation shapes');
showMessage('Only the annotation mode allowed for the shape');
}
this._controller.setDefaultShapeType(type);
}).trigger('change');
@ -227,20 +230,30 @@ class ShapeCreatorView {
}).trigger('change');
this._modeSelector.on('change', (e) => {
let mode = $(e.target).prop('value');
if (mode != 'annotation' && this._typeSelector.prop('value') != 'box') {
// FIXME: In the future we have to make some generic solution
const mode = $(e.target).prop('value');
const type = this._typeSelector.prop('value');
if (mode !== 'annotation' && !(type === 'points' && this._polyShapeSize === 1)
&& type !== 'box') {
this._typeSelector.prop('value', 'box');
this._controller.setDefaultShapeType('box');
showMessage('Only boxes available like interpolation shapes');
showMessage('Only boxes and single point allowed in the interpolation mode');
}
this._controller.setDefaultShapeMode(mode);
}).trigger('change');
this._polyShapeSizeInput.on('change', (e) => {
e.stopPropagation();
let size = + e.target.value;
let size = +e.target.value;
if (size < 0) size = 0;
if (size > 100) size = 0;
const mode = this._modeSelector.prop('value');
const type = this._typeSelector.prop('value');
if (mode === 'interpolation' && type === 'points' && size !== 1) {
showMessage('Only single point allowed in the interpolation mode');
size = 1;
}
e.target.value = size || '';
this._polyShapeSize = size;
}).trigger('change');
@ -265,6 +278,7 @@ class ShapeCreatorView {
let size = this._polyShapeSize;
let sizeDecrement = function() {
if (!--size) {
numberOfPoints = this._polyShapeSize;
this._drawInstance.draw('done');
}
}.bind(this);
@ -323,7 +337,7 @@ class ShapeCreatorView {
this._drawInstance.draw('point', e);
lastPoint = {
x: e.clientX,
y: e.clientY
y: e.clientY,
};
}
}

@ -341,8 +341,8 @@ class ShapeModel extends Listener {
}
switchOutside(frame) {
// Only for interpolation boxes
if (this._type != 'interpolation_box') {
// Only for interpolation shapes
if (this._type.split('_')[0] !== 'interpolation') {
return;
}
@ -379,7 +379,7 @@ class ShapeModel extends Listener {
if (frame < this._frame) {
if (this._frame in this._attributes.mutable) {
this._attributes.mutable[frame] = this._attributes.mutable[this._frame];
delete(this._attributes.mutable[this._frame]);
delete (this._attributes.mutable[this._frame]);
}
this._frame = frame;
}
@ -388,17 +388,17 @@ class ShapeModel extends Listener {
}
switchKeyFrame(frame) {
// Only for interpolation boxes
if (this._type != 'interpolation_box') {
// Only for interpolation shapes
if (this._type.split('_')[0] !== 'interpolation') {
return;
}
// Undo/redo code
let oldPos = Object.assign({}, this._positions[frame]);
const oldPos = Object.assign({}, this._positions[frame]);
window.cvat.addAction('Change Keyframe', () => {
this.switchKeyFrame(frame);
if (Object.keys(oldPos).length && oldPos.outside) {
this.switchOutside(frame);
if (frame in this._positions) {
this.updatePosition(frame, oldPos);
}
}, () => {
this.switchKeyFrame(frame);
@ -411,19 +411,18 @@ class ShapeModel extends Listener {
this._frame = Object.keys(this._positions).map((el) => +el).sort((a,b) => a - b)[1];
if (frame in this._attributes.mutable) {
this._attributes.mutable[this._frame] = this._attributes.mutable[frame];
delete(this._attributes.mutable[frame]);
delete (this._attributes.mutable[frame]);
}
}
delete(this._positions[frame]);
}
else {
delete (this._positions[frame]);
} else {
let position = this._interpolatePosition(frame);
this.updatePosition(frame, position, true);
if (frame < this._frame) {
if (this._frame in this._attributes.mutable) {
this._attributes.mutable[frame] = this._attributes.mutable[this._frame];
delete(this._attributes.mutable[this._frame]);
delete (this._attributes.mutable[this._frame]);
}
this._frame = frame;
}
@ -917,7 +916,7 @@ class PolyShapeModel extends ShapeModel {
}
return Object.assign({}, leftPos, {
outside: leftFrame != frame,
outside: leftPos.outside || leftFrame !== frame,
});
}
@ -1145,6 +1144,60 @@ class PointsModel extends PolyShapeModel {
this._minPoints = 1;
}
_interpolatePosition(frame) {
if (this._type.startsWith('annotation')) {
return Object.assign({}, this._positions[this._frame], {
outside: this._frame !== frame,
});
}
let [leftFrame, rightFrame] = this._neighboringFrames(frame);
if (frame in this._positions) {
leftFrame = frame;
}
let leftPos = null;
let rightPos = null;
if (leftFrame != null) leftPos = this._positions[leftFrame];
if (rightFrame != null) rightPos = this._positions[rightFrame];
if (!leftPos) {
if (rightPos) {
return Object.assign({}, rightPos, {
outside: true,
});
}
return {
outside: true,
};
}
if (frame === leftFrame || leftPos.outside || !rightPos || rightPos.outside) {
return Object.assign({}, leftPos);
}
const rightPoints = PolyShapeModel.convertStringToNumberArray(rightPos.points);
const leftPoints = PolyShapeModel.convertStringToNumberArray(leftPos.points);
if (rightPoints.length === leftPoints.length && leftPoints.length === 1) {
const moveCoeff = (frame - leftFrame) / (rightFrame - leftFrame);
const interpolatedPoints = [{
x: leftPoints[0].x + (rightPoints[0].x - leftPoints[0].x) * moveCoeff,
y: leftPoints[0].y + (rightPoints[0].y - leftPoints[0].y) * moveCoeff,
}];
return Object.assign({}, leftPos, {
points: PolyShapeModel.convertNumberArrayToString(interpolatedPoints),
});
}
return Object.assign({}, leftPos, {
outside: true,
});
}
distance(mousePos, frame) {
let pos = this._interpolatePosition(frame);
if (pos.outside) return Number.MAX_SAFE_INTEGER;
@ -1958,19 +2011,17 @@ class ShapeView extends Listener {
if (type.split('_')[0] == 'interpolation') {
let interpolationCenter = document.createElement('center');
if (type.split('_')[1] == 'box') {
let outsideButton = document.createElement('button');
outsideButton.classList.add('graphicButton', 'outsideButton');
let outsideButton = document.createElement('button');
outsideButton.classList.add('graphicButton', 'outsideButton');
let keyframeButton = document.createElement('button');
keyframeButton.classList.add('graphicButton', 'keyFrameButton');
let keyframeButton = document.createElement('button');
keyframeButton.classList.add('graphicButton', 'keyFrameButton');
interpolationCenter.appendChild(outsideButton);
interpolationCenter.appendChild(keyframeButton);
interpolationCenter.appendChild(outsideButton);
interpolationCenter.appendChild(keyframeButton);
this._uis.buttons['outside'] = outsideButton;
this._uis.buttons['keyframe'] = keyframeButton;
}
this._uis.buttons['outside'] = outsideButton;
this._uis.buttons['keyframe'] = keyframeButton;
let prevKeyFrameButton = document.createElement('button');
prevKeyFrameButton.classList.add('graphicButton', 'prevKeyFrameButton');
@ -3125,7 +3176,7 @@ class PointsView extends PolyShapeView {
_drawPointMarkers(position) {
if (this._uis.points) {
if (this._uis.points || position.outside) {
return;
}

Loading…
Cancel
Save