diff --git a/changelog.d/20240711_094827_boris_do_not_reset_opacity.md b/changelog.d/20240711_094827_boris_do_not_reset_opacity.md new file mode 100644 index 000000000000..bc0a4f993965 --- /dev/null +++ b/changelog.d/20240711_094827_boris_do_not_reset_opacity.md @@ -0,0 +1,4 @@ +### Changed + +- Do not reset opacity level each time frame switched if there are masks on the frame + () diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 3b3448584229..8dc884f75252 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -2576,7 +2576,7 @@ export class CanvasViewImpl implements CanvasView, Listener { } if (drawnState.shapeType === 'mask') { - shape.attr('opacity', `${this.configuration.shapeOpacity}`); + shape.attr('opacity', `${Math.sqrt(this.configuration.shapeOpacity)}`); } else { shape.attr('fill-opacity', `${this.configuration.shapeOpacity}`); } @@ -2666,7 +2666,7 @@ export class CanvasViewImpl implements CanvasView, Listener { } if (state.shapeType === 'mask') { - shape.attr('opacity', `${this.configuration.selectedShapeOpacity}`); + shape.attr('opacity', `${Math.sqrt(this.configuration.selectedShapeOpacity)}`); } else { shape.attr('fill-opacity', `${this.configuration.selectedShapeOpacity}`); } @@ -3169,7 +3169,8 @@ export class CanvasViewImpl implements CanvasView, Listener { id: `cvat_canvas_shape_${state.clientID}`, 'shape-rendering': 'geometricprecision', 'data-z-order': state.zOrder, - opacity: colorization['fill-opacity'], + // apply sqrt function to colorization to enhance displaying the mask on the canvas + opacity: Math.sqrt(colorization['fill-opacity']), stroke: colorization.stroke, }).addClass('cvat_canvas_shape'); image.move(this.geometry.offset + left, this.geometry.offset + top); diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index 6cdc5832b127..3cdf573813f6 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -894,7 +894,6 @@ export function getJobAsync({ const { settings: { - workspace: { showAllInterpolationTracks }, player: { showDeletedFrames }, }, } = state; @@ -939,7 +938,8 @@ export function getJobAsync({ // do nothing, user will be notified when data request is done } - const states = await job.annotations.get(frameNumber, showAllInterpolationTracks, filters); + await job.annotations.clear({ reload: true }); + const issues = await job.issues(); const colors = [...cvat.enums.colors]; @@ -974,7 +974,6 @@ export function getJobAsync({ groundTruthInstance: gtJob || null, groundTruthJobFramesMeta, issues, - states, conflicts, frameNumber, frameFilename: frameData.filename, diff --git a/cvat-ui/src/reducers/settings-reducer.ts b/cvat-ui/src/reducers/settings-reducer.ts index a0af660d3f8f..0c662908d767 100644 --- a/cvat-ui/src/reducers/settings-reducer.ts +++ b/cvat-ui/src/reducers/settings-reducer.ts @@ -1,18 +1,16 @@ // Copyright (C) 2020-2022 Intel Corporation -// Copyright (C) 2023 CVAT.ai Corporation +// Copyright (C) 2023-2024 CVAT.ai Corporation // // SPDX-License-Identifier: MIT import { AnyAction } from 'redux'; -import { BoundariesActionTypes } from 'actions/boundaries-actions'; import { AuthActionTypes } from 'actions/auth-actions'; import { SettingsActionTypes } from 'actions/settings-actions'; import { AnnotationActionTypes } from 'actions/annotation-actions'; import { SettingsState, GridColor, FrameSpeed, ColorBy, } from 'reducers'; -import { clampOpacity } from 'utils/clamp-opacity'; const defaultState: SettingsState = { shapes: { @@ -438,38 +436,15 @@ export default (state = defaultState, action: AnyAction): SettingsState => { imageFilters: [], }; } - case AnnotationActionTypes.FETCH_ANNOTATIONS_SUCCESS: - case AnnotationActionTypes.CHANGE_FRAME_SUCCESS: { - const { states } = action.payload; - const { shapes } = state; - const [clampedOpacity, clampedSelectedOpacity] = clampOpacity(states, shapes); - return { - ...state, - shapes: { - ...state.shapes, - opacity: clampedOpacity, - selectedOpacity: clampedSelectedOpacity, - }, - }; - } - case BoundariesActionTypes.RESET_AFTER_ERROR: case AnnotationActionTypes.GET_JOB_SUCCESS: { - const { job, states } = action.payload; - const { shapes } = state; const filters = [...state.imageFilters]; filters.forEach((imageFilter) => { imageFilter.modifier.currentProcessedImage = null; }); - const [clampedOpacity, clampedSelectedOpacity] = clampOpacity(states, shapes, job); - return { ...state, - shapes: { - ...defaultState.shapes, - opacity: clampedOpacity, - selectedOpacity: clampedSelectedOpacity, - }, + imageFilters: filters, }; } diff --git a/cvat-ui/src/utils/clamp-opacity.ts b/cvat-ui/src/utils/clamp-opacity.ts deleted file mode 100644 index f520260324e3..000000000000 --- a/cvat-ui/src/utils/clamp-opacity.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2023 CVAT.ai Corporation -// -// SPDX-License-Identifier: MIT - -import { Job } from 'cvat-core-wrapper'; -import { ShapeType, DimensionType } from 'cvat-core/src/enums'; -import ObjectState from 'cvat-core/src/object-state'; -import { SettingsState } from 'reducers'; - -export function clampOpacity( - states: ObjectState[], - shapes: SettingsState['shapes'], - job?: Job, -): [number, number] { - const ENHANCED_DEFAULT_OPACITY = 30; - const ENHANCED_DEFAULT_SELECTED_OPACITY = 60; - - if (shapes.opacity >= ENHANCED_DEFAULT_OPACITY && shapes.selectedOpacity >= ENHANCED_DEFAULT_SELECTED_OPACITY) { - return [shapes.opacity, shapes.selectedOpacity]; - } - - const opacity = Math.max(shapes.opacity, ENHANCED_DEFAULT_OPACITY); - const selectedOpacity = Math.max(shapes.selectedOpacity, ENHANCED_DEFAULT_SELECTED_OPACITY); - - if (job?.dimension === DimensionType.DIMENSION_3D) { - return [opacity, selectedOpacity]; - } - - const withMasks = states - .some((_state: ObjectState): boolean => _state.shapeType === ShapeType.MASK); - if (withMasks) { - return [opacity, selectedOpacity]; - } - - return [shapes.opacity, shapes.selectedOpacity]; -} diff --git a/tests/cypress/e2e/actions_objects/regression_tests.js b/tests/cypress/e2e/actions_objects/regression_tests.js index f34a513d4662..b1d9a6df6e8f 100644 --- a/tests/cypress/e2e/actions_objects/regression_tests.js +++ b/tests/cypress/e2e/actions_objects/regression_tests.js @@ -44,7 +44,7 @@ context('Regression tests', () => { taskID = response.taskID; [jobID] = response.jobIDs; - cy.headlessCreateObject([rectanglePayload], jobID); + cy.headlessCreateObjects([rectanglePayload], jobID); cy.visit(`/tasks/${taskID}/jobs/${jobID}`); }); }); diff --git a/tests/cypress/e2e/issues_prs/issue_7176_opacity_reset.js b/tests/cypress/e2e/issues_prs/issue_7176_opacity_reset.js deleted file mode 100644 index 056583ab4ad0..000000000000 --- a/tests/cypress/e2e/issues_prs/issue_7176_opacity_reset.js +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2023 CVAT.ai Corporation -// -// SPDX-License-Identifier: MIT - -/// - -const issueId = '7176'; - -context('Opacity and Selected opacity reset on each frame change', { scrollBehavior: false }, () => { - const taskName = `Test issue #${issueId}`; - const serverFiles = ['images/image_1.jpg', 'images/image_2.jpg', 'images/image_3.jpg']; - const labelName = `issue#${issueId}`; - const createRectangleShape2Points = { - points: 'By 2 Points', - type: 'Shape', - labelName, - firstX: 100, - firstY: 350, - secondX: 200, - secondY: 450, - }; - const drawingActions = [{ - method: 'brush', - coordinates: [[300, 300], [700, 300], [700, 700], [300, 700]], - }]; - - let taskID = null; - let jobID = null; - - function checkOpacitySliders(opacity, selectedOpacity) { - cy.get('.cvat-appearance-opacity-slider').find('[role="slider"]').should('have.attr', 'aria-valuenow', opacity); - cy.get('.cvat-appearance-selected-opacity-slider').find('[role="slider"]').should('have.attr', 'aria-valuenow', selectedOpacity); - } - - function setSliderValue(slider, value) { - let arrowsString = ''; - cy.get(slider) - .within(() => { - cy.get('[role="slider"]') - .should('have.attr', 'aria-valuenow') - .then(($ariaValueNow) => { - const steps = value - $ariaValueNow; - arrowsString = (steps > 0 ? '{rightarrow}' : '{leftarrow}').repeat(Math.abs(steps)); - }) - .then(() => { - cy.get('.ant-slider-handle').type(arrowsString); - }); - }); - } - - before(() => { - cy.visit('auth/login'); - cy.login(); - cy.headlessCreateTask({ - labels: [{ name: labelName, attributes: [], type: 'any' }], - name: taskName, - project_id: null, - source_storage: { location: 'local' }, - target_storage: { location: 'local' }, - }, { - server_files: serverFiles, - image_quality: 70, - use_zip_chunks: true, - use_cache: true, - sorting_method: 'lexicographical', - }).then((response) => { - taskID = response.taskID; - [jobID] = response.jobIDs; - }).then(() => { - cy.visit(`/tasks/${taskID}/jobs/${jobID}`); - cy.get('.cvat-canvas-container').should('exist').and('be.visible'); - }); - - cy.goCheckFrameNumber(2); - cy.startMaskDrawing(); - cy.drawMask(drawingActions); - cy.get('.cvat-brush-tools-finish').click(); - }); - - after(() => { - cy.logout(); - cy.getAuthKey().then((response) => { - const authKey = response.body.key; - cy.request({ - method: 'DELETE', - url: `/api/tasks/${taskID}`, - headers: { - Authorization: `Token ${authKey}`, - }, - }); - }); - }); - - describe(`Testing issue "${issueId}"`, () => { - it('Set low opacity value, draw a rectangle.', () => { - cy.goCheckFrameNumber(0); - setSliderValue('.cvat-appearance-opacity-slider', 20); - setSliderValue('.cvat-appearance-selected-opacity-slider', 40); - cy.createRectangle(createRectangleShape2Points); - checkOpacitySliders(20, 40); - }); - - it('Go to the next frame, draw a rectangle. Opacity does not reset', () => { - cy.goCheckFrameNumber(1); - cy.createRectangle(createRectangleShape2Points); - checkOpacitySliders(20, 40); - }); - - it('Go to the next frame with mask. Opacity resets.', () => { - cy.goCheckFrameNumber(2); - checkOpacitySliders(30, 60); - }); - - it('Decrease opacity. Opacity does not reset on frames without masks. Opacity resets on frames with masks', () => { - setSliderValue('.cvat-appearance-opacity-slider', 20); - setSliderValue('.cvat-appearance-selected-opacity-slider', 50); - cy.goCheckFrameNumber(0); - checkOpacitySliders(20, 50); - cy.goCheckFrameNumber(1); - checkOpacitySliders(20, 50); - cy.goCheckFrameNumber(2); - checkOpacitySliders(30, 60); - }); - - it('Increase opacity. Opacity does not reset on high values', () => { - setSliderValue('.cvat-appearance-opacity-slider', 40); - setSliderValue('.cvat-appearance-selected-opacity-slider', 70); - for (let i = 0; i < 3; i++) { - cy.goCheckFrameNumber(0); - checkOpacitySliders(40, 70); - } - }); - }); -}); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 0ede94763d91..170220b8e952 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -266,7 +266,7 @@ Cypress.Commands.add('headlessLogin', (username = Cypress.env('user'), password }); }); -Cypress.Commands.add('headlessCreateObject', (objects, jobID) => { +Cypress.Commands.add('headlessCreateObjects', (objects, jobID) => { cy.window().then(async ($win) => { const job = (await $win.cvat.jobs.get({ jobID }))[0]; await job.annotations.clear({ reload: true });