Merge branch 'develop' into sk/fix-import-annotations-for-YOLO-format

main
kirill.sizov 5 years ago
commit b36c5f215f

@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Export of instance masks with holes (<https://github.com/openvinotoolkit/cvat/pull/3044>)
- Changing a label on canvas does not work when 'Show object details' enabled (<https://github.com/openvinotoolkit/cvat/pull/3084>)
### Security

@ -1,6 +1,6 @@
{
"name": "cvat-canvas",
"version": "2.4.1",
"version": "2.4.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

@ -1,6 +1,6 @@
{
"name": "cvat-canvas",
"version": "2.4.1",
"version": "2.4.2",
"description": "Part of Computer Vision Annotation Tool which presents its canvas library",
"main": "src/canvas.ts",
"scripts": {

@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -1175,7 +1175,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
}
} else if (reason === UpdateReasons.IMAGE_MOVED) {
this.moveCanvas();
} else if ([UpdateReasons.OBJECTS_UPDATED].includes(reason)) {
} else if (reason === UpdateReasons.OBJECTS_UPDATED) {
if (this.mode === Mode.GROUP) {
this.groupHandler.resetSelectedObjects();
}
@ -1443,6 +1443,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
clientID: state.clientID,
outside: state.outside,
occluded: state.occluded,
source: state.source,
hidden: state.hidden,
lock: state.lock,
shapeType: state.shapeType,
@ -1534,13 +1535,22 @@ export class CanvasViewImpl implements CanvasView, Listener {
}
}
for (const attrID of Object.keys(state.attributes)) {
if (state.attributes[attrID] !== drawnState.attributes[+attrID]) {
if (text) {
const [span] = (text.node.querySelectorAll(`[attrID="${attrID}"]`) as any) as SVGTSpanElement[];
if (span && span.textContent) {
const prefix = span.textContent.split(':').slice(0, -1).join(':');
span.textContent = `${prefix}: ${state.attributes[attrID]}`;
if (drawnState.label.id !== state.label.id) {
// need to remove created text and create it again
if (text) {
text.remove();
this.svgTexts[state.clientID] = this.addText(state);
}
} else {
// check if there are updates in attributes
for (const attrID of Object.keys(state.attributes)) {
if (state.attributes[attrID] !== drawnState.attributes[+attrID]) {
if (text) {
const [span] = text.node.querySelectorAll<SVGTSpanElement>(`[attrID="${attrID}"]`);
if (span && span.textContent) {
const prefix = span.textContent.split(':').slice(0, -1).join(':');
span.textContent = `${prefix}: ${state.attributes[attrID]}`;
}
}
}
}

@ -41,6 +41,7 @@ export interface DrawnState {
occluded?: boolean;
hidden?: boolean;
lock: boolean;
source: 'AUTO' | 'MANUAL';
shapeType: string;
points?: number[];
attributes: Record<number, string>;
@ -176,5 +177,7 @@ export function scalarProduct(a: Vector2D, b: Vector2D): number {
}
export function vectorLength(vector: Vector2D): number {
return Math.sqrt((vector.i ** 2) + (vector.j ** 2));
const sqrI = vector.i ** 2;
const sqrJ = vector.j ** 2;
return Math.sqrt(sqrI + sqrJ);
}

@ -13,38 +13,39 @@
}
},
"@ant-design/icons": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.5.0.tgz",
"integrity": "sha512-ZAKJcmr4DBV3NWr8wm2dCxNKN4eFrX+qCaPsuFejP6FRsf+m5OKxvCVi9bSp1lmKWeOI5yECAx5s0uFm4QHuPw==",
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.6.2.tgz",
"integrity": "sha512-QsBG2BxBYU/rxr2eb8b2cZ4rPKAPBpzAR+0v6rrZLp/lnyvflLH3tw1vregK+M7aJauGWjIGNdFmUfpAOtw25A==",
"requires": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons-svg": "^4.0.0",
"@babel/runtime": "^7.11.2",
"classnames": "^2.2.6",
"insert-css": "^2.0.0",
"rc-util": "^5.0.1"
"rc-util": "^5.9.4"
},
"dependencies": {
"@ant-design/colors": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz",
"integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==",
"@babel/runtime": {
"version": "7.13.10",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz",
"integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==",
"requires": {
"@ctrl/tinycolor": "^3.4.0"
"regenerator-runtime": "^0.13.4"
}
},
"@babel/runtime": {
"version": "7.13.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz",
"integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==",
"rc-util": {
"version": "5.9.8",
"resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.9.8.tgz",
"integrity": "sha512-typLSHYGf5irvGLYQshs0Ra3aze086h0FhzsAkyirMunYZ7b3Te8gKa5PVaanoHaZa9sS6qx98BxgysoRP+6Tw==",
"requires": {
"regenerator-runtime": "^0.13.4"
"@babel/runtime": "^7.12.5",
"react-is": "^16.12.0",
"shallowequal": "^1.1.0"
}
},
"@ctrl/tinycolor": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz",
"integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ=="
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"regenerator-runtime": {
"version": "0.13.7",
@ -1301,9 +1302,9 @@
}
},
"@types/react-dom": {
"version": "16.9.11",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.11.tgz",
"integrity": "sha512-3UuR4MoWf5spNgrG6cwsmT9DdRghcR4IDFOzNZ6+wcmacxkFykcb5ji0nNVm9ckBT4BCxvCrJJbM4+EYsEEVIg==",
"version": "16.9.12",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.12.tgz",
"integrity": "sha512-i7NPZZpPte3jtVOoW+eLB7G/jsX5OM6GqQnH+lC0nq0rqwlK0x8WcMEvYDgFWqWhWMlTltTimzdMax6wYfZssA==",
"requires": {
"@types/react": "^16"
}
@ -1328,9 +1329,9 @@
}
},
"@types/react-router": {
"version": "5.1.12",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.12.tgz",
"integrity": "sha512-0bhXQwHYfMeJlCh7mGhc0VJTRm0Gk+Z8T00aiP4702mDUuLs9SMhnd2DitpjWFjdOecx2UXtICK14H9iMnziGA==",
"version": "5.1.13",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.13.tgz",
"integrity": "sha512-ZIuaO9Yrln54X6elg8q2Ivp6iK6p4syPsefEYAhRDAoqNh48C8VYUmB9RkXjKSQAJSJV0mbIFCX7I4vZDcHrjg==",
"requires": {
"@types/history": "*",
"@types/react": "*"
@ -24869,11 +24870,6 @@
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true
},
"insert-css": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/insert-css/-/insert-css-2.0.0.tgz",
"integrity": "sha1-610Ql7dUL0x56jBg067gfQU4gPQ="
},
"internal-ip": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
@ -28959,11 +28955,12 @@
"integrity": "sha512-WjwvxBSnmLMRcU33do0KixDB+9vP3e84eCse+rd+HNklAMNWyRgZTDEQlay/qK6lcXFPRuEIASJTpEt6pyK7Ww=="
},
"react-redux": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz",
"integrity": "sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA==",
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.3.tgz",
"integrity": "sha512-ZhAmQ1lrK+Pyi0ZXNMUZuYxYAZd59wFuVDGUt536kSGdD0ya9Q7BfsE95E3TsFLE3kOSFp5m6G5qbatE+Ic1+w==",
"requires": {
"@babel/runtime": "^7.12.1",
"@types/react-redux": "^7.1.16",
"hoist-non-react-statics": "^3.3.2",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
@ -28971,9 +28968,9 @@
},
"dependencies": {
"@babel/runtime": {
"version": "7.12.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz",
"integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==",
"version": "7.13.10",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz",
"integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==",
"requires": {
"regenerator-runtime": "^0.13.4"
}

@ -48,14 +48,14 @@
"worker-loader": "^2.0.0"
},
"dependencies": {
"@ant-design/icons": "^4.5.0",
"@ant-design/icons": "^4.6.2",
"@types/lodash": "^4.14.168",
"@types/platform": "^1.3.3",
"@types/react": "^16.14.5",
"@types/react-color": "^3.0.4",
"@types/react-dom": "^16.9.11",
"@types/react-dom": "^16.9.12",
"@types/react-redux": "^7.1.16",
"@types/react-router": "^5.1.12",
"@types/react-router": "^5.1.13",
"@types/react-router-dom": "^5.1.7",
"@types/react-share": "^3.0.3",
"@types/redux-logger": "^3.0.8",
@ -78,7 +78,7 @@
"react-cookie": "^4.0.3",
"react-dom": "^16.14.0",
"react-moment": "^1.1.1",
"react-redux": "^7.2.2",
"react-redux": "^7.2.3",
"react-resizable": "^1.11.1",
"@types/react-resizable": "^1.7.2",
"react-router": "^5.1.0",

@ -60,10 +60,10 @@
```bash
nuctl deploy --project-name cvat \
--path `pwd`/tensorflow/matterport/mask_rcnn/nuclio \
--path serverless/tensorflow/matterport/mask_rcnn/nuclio \
--platform local --base-image tensorflow/tensorflow:1.15.5-gpu-py3 \
--desc "GPU based implementation of Mask RCNN on Python 3, Keras, and TensorFlow." \
--image cvat/tf.matterport.mask_rcnn_gpu
--image cvat/tf.matterport.mask_rcnn_gpu \
--triggers '{"myHttpTrigger": {"maxWorkers": 1}}' \
--resource-limit nvidia.com/gpu=1
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

@ -982,6 +982,20 @@ this way you will change the label color for all jobs in the task.
![](static/documentation/images/image062.jpg)
**Fast label change**
You can change the label of an object using hot keys. In order to do it, you need to assign a number (from 0 to 9) to labels. By default numbers 1,2...0 are assigned to the first ten labels.
To assign a number, click on the button placed at the right of a label name on the sidebar.
![](static/documentation/images/image210.jpg)
After that you will be able to assign a corresponding label to an object
by hovering your mouse cursor over it and pressing `Ctrl + Num(0..9)`.
In case you do not point the cursor to the object, pressing `Ctrl + Num(0..9)` will set a chosen label as default,
so that the next object you create (use `N` key) will automatically have this label assigned.
![](static/documentation/images/image211.jpg)
---
#### Appearance
@ -1795,6 +1809,7 @@ Many UI elements have shortcut hints. Put your pointer to a required element to
| `Ctrl+V` | Paste a shape from internal CVAT clipboard |
| Hold `Ctrl` while pasting | When pasting shape from the buffer for multiple pasting. |
| `Crtl+B` | Make a copy of the object on the following frames |
| `Ctrl+Num(0..9)` | Сhanges the object label if pressed while the cursor is pointed on the object <br> / changes default label if pressed while the cursor is not pointed on an object|
| | _Operations are available only for track_ |
| `K` | Change keyframe property for an active track |
| `O` | Change outside property for an active track |

@ -0,0 +1,147 @@
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
/// <reference types="cypress" />
context('Hotkeys to change labels feature.', () => {
const caseId = '72';
const labelName = `Case ${caseId}`;
const taskName = labelName;
const attrName = `Attr for ${labelName}`;
const textDefaultValue = 'Some default value for type Text';
const imagesCount = 1;
const imageFileName = `image_${labelName.replace(' ', '_').toLowerCase()}`;
const width = 800;
const height = 800;
const posX = 10;
const posY = 10;
const color = 'gray';
const archiveName = `${imageFileName}.zip`;
const archivePath = `cypress/fixtures/${archiveName}`;
const imagesFolder = `cypress/fixtures/${imageFileName}`;
const directoryToArchive = imagesFolder;
const secondLabel = `Case ${caseId} second`
let firstLabelCurrentVal = '';
let secondLabelCurrentVal = '';
function testCheckingAlwaysShowObjectDetails(check) {
cy.openSettings();
cy.get('.cvat-settings-modal').within(() => {
cy.contains('Workspace').click();
cy.get('.cvat-workspace-settings-show-text-always').within(() => {
check
? cy.get('[type="checkbox"]').check().should('be.checked')
: cy.get('[type="checkbox"]').uncheck().should('not.be.checked');
});
});
cy.closeSettings();
}
before(() => {
cy.visit('auth/login');
cy.login();
cy.imageGenerator(imagesFolder, imageFileName, width, height, color, posX, posY, labelName, imagesCount);
cy.createZipArchive(directoryToArchive, archivePath);
cy.createAnnotationTask(taskName, labelName, attrName, textDefaultValue, archiveName);
cy.openTask(taskName);
cy.addNewLabel(secondLabel);
cy.openJob();
});
after(() => {
cy.goToTaskList();
cy.deleteTask(taskName);
});
describe(`Testing case "${caseId}"`, () => {
// Collect labels text. Since the server can return them in reverse order.
it('Collect label values relative to hotkeys.', () => {
cy.get('.cvat-objects-sidebar-tabs').within(() => {
cy.contains('[role="tab"]', 'Labels').click();
});
cy.get('.cvat-objects-sidebar-label-item').then(($objectsSidebarLabelItem) => {
firstLabelCurrentVal = $objectsSidebarLabelItem[0].innerText.slice(0, -2);
secondLabelCurrentVal = $objectsSidebarLabelItem[1].innerText.slice(0, -2);
});
cy.get('.cvat-objects-sidebar-tabs').within(() => {
cy.contains('[role="tab"]', 'Objects').click();
});
});
it('Changing a label for a shape using hotkey.', () => {
const createPolygonShape = {
reDraw: false,
type: 'Shape',
labelName: firstLabelCurrentVal,
pointsMap: [
{ x: 200, y: 200 },
{ x: 300, y: 200 },
{ x: 300, y: 300 },
],
complete: true,
numberOfPoints: null,
};
// Set settings "Always show object details" to check issue 3083
testCheckingAlwaysShowObjectDetails(true);
cy.createPolygon(createPolygonShape);
cy.get('#cvat-objects-sidebar-state-item-1').find('.cvat-objects-sidebar-state-item-label-selector').should('have.text', firstLabelCurrentVal);
cy.get('.cvat-canvas-container').click(270, 260);
cy.get('#cvat_canvas_shape_1').should('have.class', 'cvat_canvas_shape_activated');
cy.contains('tspan', `${firstLabelCurrentVal} 1 (manual)`).should('be.visible');
cy.get('body').type('{Ctrl}2')
cy.get('#cvat-objects-sidebar-state-item-1').find('.cvat-objects-sidebar-state-item-label-selector').should('have.text', secondLabelCurrentVal);
cy.contains('tspan', `${secondLabelCurrentVal} 1 (manual)`).should('be.visible');
// Unset settings "Always show object details"
testCheckingAlwaysShowObjectDetails();
});
it('Changing default label before drawing a shape.', () => {
cy.interactControlButton('draw-rectangle');
cy.switchLabel(firstLabelCurrentVal, 'draw-rectangle');
cy.get('.cvat-draw-rectangle-popover-visible').within(() => {
cy.contains('button', 'Shape').click();
});
cy.get('body').type('{Ctrl}2');
cy.contains(`Default label was changed to "${secondLabelCurrentVal}"`).should('exist');
cy.get('.cvat-canvas-container').click(500, 500).click(600, 600);
cy.get('#cvat-objects-sidebar-state-item-2').find('.cvat-objects-sidebar-state-item-label-selector').should('have.text', secondLabelCurrentVal);
});
it('Check changing shortcut for a label.', () => {
// Go to a labels tab
cy.get('.cvat-objects-sidebar-tabs').within(() => {
cy.contains('[role="tab"]', 'Labels').click();
});
cy.contains('.cvat-label-item-setup-shortcut-button', '1').click();
cy.get('.cvat-label-item-setup-shortcut-popover').should('be.visible').within(() => {
cy.get('[type="button"]').then(($btn) => {
expect($btn[0].innerText).to.be.equal(`1:${firstLabelCurrentVal}`);
expect($btn[1].innerText).to.be.equal(`2:${secondLabelCurrentVal}`);
expect($btn[2].innerText).to.be.equal('3:None');
// Click to "3" button
cy.get($btn[2]).click();
});
});
cy.get('.cvat-label-item-setup-shortcut-popover').should('be.visible').within(() => {
cy.get('[type="button"]').then(($btn) => {
// Buttons 1 and 3 have changed values
expect($btn[0].innerText).to.be.equal('1:None');
expect($btn[2].innerText).to.be.equal(`3:${firstLabelCurrentVal}`);
});
});
cy.contains('.cvat-label-item-setup-shortcut-button', '3').should('exist');
cy.get('.cvat-canvas-container').click(); // Hide shortcut popover
// Go to "Objects" tab
cy.get('.cvat-objects-sidebar-tabs').within(() => {
cy.contains('[role="tab"]', 'Objects').click();
});
// Checking the label change via the new hotkey value
cy.get('.cvat-canvas-container').click(270, 260);
cy.get('#cvat_canvas_shape_1').should('have.class', 'cvat_canvas_shape_activated');
cy.get('body').type('{Ctrl}3');
cy.contains('tspan', `${firstLabelCurrentVal} 1 (manual)`).should('be.visible');
cy.get('#cvat-objects-sidebar-state-item-1').find('.cvat-objects-sidebar-state-item-label-selector').should('have.text', firstLabelCurrentVal);
});
});
});
Loading…
Cancel
Save