Skip to content

Commit

Permalink
Merge branch 'develop' into bs/fixed_issue_6788
Browse files Browse the repository at this point in the history
  • Loading branch information
bsekachev committed Sep 6, 2023
2 parents 8d3d5f6 + 7a2bde2 commit 6698fd2
Show file tree
Hide file tree
Showing 20 changed files with 433 additions and 220 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## \[Unreleased\]
### Added
- Ability to hide/show an object in review mode (<https://github.com/opencv/cvat/pull/6808>)
- Gamma correcton filter (<https://github.com/opencv/cvat/pull/6771>)

### Changed
- \[Helm\] Database migrations now run in a separate job instead of the server pod,
Expand Down
11 changes: 11 additions & 0 deletions cvat-canvas/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright (C) 2019-2022 Intel Corporation
// Copyright (C) 2023 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

const { join } = require('path');

module.exports = {
ignorePatterns: [
'.eslintrc.js',
Expand All @@ -13,4 +16,12 @@ module.exports = {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
rules: {
'import/no-extraneous-dependencies': [
'error',
{
packageDir: [__dirname, join(__dirname, '../')]
},
],
}
};
2 changes: 0 additions & 2 deletions cvat-canvas/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
],
"dependencies": {
"@types/polylabel": "^1.0.5",
"@types/fabric": "^4.5.7",
"fabric": "^5.2.1",
"polylabel": "^1.1.0",
"svg.draggable.js": "2.2.2",
"svg.draw.js": "^2.0.4",
Expand Down
8 changes: 8 additions & 0 deletions cvat-ui/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2023 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

const globalConfig = require('../.eslintrc.js');
const { join } = require('path');

module.exports = {
parserOptions: {
Expand All @@ -29,5 +31,11 @@ module.exports = {
'react/jsx-indent-props': ['warn', 4],
'react/jsx-props-no-spreading': 0,
'jsx-quotes': ['error', 'prefer-single'],
'import/no-extraneous-dependencies': [
'error',
{
packageDir: [__dirname, join(__dirname, '../')]
},
],
},
};
2 changes: 1 addition & 1 deletion cvat-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.55.8",
"version": "1.56.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
31 changes: 31 additions & 0 deletions cvat-ui/src/actions/settings-actions.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2023 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import { AnyAction } from 'redux';
import {
GridColor, ColorBy, SettingsState, ToolsBlockerState,
} from 'reducers';
import { ImageFilter, ImageFilterAlias } from 'utils/image-processing';

export enum SettingsActionTypes {
SWITCH_ROTATE_ALL = 'SWITCH_ROTATE_ALL',
Expand Down Expand Up @@ -45,6 +47,9 @@ export enum SettingsActionTypes {
SWITCH_TOOLS_BLOCKER_STATE = 'SWITCH_TOOLS_BLOCKER_STATE',
SWITCH_SHOWING_DELETED_FRAMES = 'SWITCH_SHOWING_DELETED_FRAMES',
SWITCH_SHOWING_TAGS_ON_FRAME = 'SWITCH_SHOWING_TAGS_ON_FRAME',
ENABLE_IMAGE_FILTER = 'ENABLE_IMAGE_FILTER',
DISABLE_IMAGE_FILTER = 'DISABLE_IMAGE_FILTER',
RESET_IMAGE_FILTERS = 'RESET_IMAGE_FILTERS',
}

export function changeShapesOpacity(opacity: number): AnyAction {
Expand Down Expand Up @@ -378,3 +383,29 @@ export function switchShowingTagsOnFrame(showTagsOnFrame: boolean): AnyAction {
},
};
}

export function enableImageFilter(filter: ImageFilter, options: object | null = null): AnyAction {
return {
type: SettingsActionTypes.ENABLE_IMAGE_FILTER,
payload: {
filter,
options,
},
};
}

export function disableImageFilter(filterAlias: ImageFilterAlias): AnyAction {
return {
type: SettingsActionTypes.DISABLE_IMAGE_FILTER,
payload: {
filterAlias,
},
};
}

export function resetImageFilters(): AnyAction {
return {
type: SettingsActionTypes.RESET_IMAGE_FILTERS,
payload: {},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import Slider from 'antd/lib/slider';
import Spin from 'antd/lib/spin';
import Dropdown from 'antd/lib/dropdown';
import { PlusCircleOutlined, UpOutlined } from '@ant-design/icons';
import notification from 'antd/lib/notification';
import debounce from 'lodash/debounce';

import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import {
Expand Down Expand Up @@ -57,6 +59,7 @@ import {
import { reviewActions } from 'actions/review-actions';

import { filterAnnotations } from 'utils/filter-annotations';
import { ImageFilter } from 'utils/image-processing';
import ImageSetupsContent from './image-setups-content';
import BrushTools from './brush-tools';

Expand Down Expand Up @@ -113,6 +116,7 @@ interface StateToProps {
showGroundTruth: boolean;
highlightedConflict: QualityConflict | null;
groundTruthJobFramesMeta: FramesMetaData | null;
imageFilters: ImageFilter[];
}

interface DispatchToProps {
Expand Down Expand Up @@ -194,6 +198,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
shapes: {
opacity, colorBy, selectedOpacity, outlined, outlineColor, showBitmap, showProjections, showGroundTruth,
},
imageFilters,
},
shortcuts: { keyMap },
review: { conflicts },
Expand Down Expand Up @@ -253,6 +258,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
showGroundTruth,
highlightedConflict,
groundTruthJobFramesMeta,
imageFilters,
};
}

Expand Down Expand Up @@ -358,6 +364,8 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
type Props = StateToProps & DispatchToProps;

class CanvasWrapperComponent extends React.PureComponent<Props> {
private debouncedUpdate = debounce(this.updateCanvas.bind(this), 250, { leading: true });

public componentDidMount(): void {
const {
automaticBordering,
Expand Down Expand Up @@ -445,6 +453,7 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {
statesSources,
showGroundTruth,
highlightedConflict,
imageFilters,
} = this.props;
const { canvasInstance } = this.props as { canvasInstance: Canvas };

Expand Down Expand Up @@ -530,13 +539,21 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {
});
}

if (prevProps.imageFilters !== imageFilters) {
canvasInstance.configure({ forceFrameUpdate: true });
}

if (
prevProps.annotations !== annotations ||
prevProps.statesSources !== statesSources ||
prevProps.frameData !== frameData ||
prevProps.curZLayer !== curZLayer
) {
this.updateCanvas();
} else if (prevProps.imageFilters !== imageFilters) {
// In case of frequent image filters changes, we apply debounced canvas update
// that makes UI smoother
this.debouncedUpdate();
}

if (prevProps.showBitmap !== showBitmap) {
Expand Down Expand Up @@ -897,9 +914,11 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {

private updateCanvas(): void {
const {
curZLayer, annotations, frameData, canvasInstance, statesSources,
workspace, groundTruthJobFramesMeta, frame,
curZLayer, annotations, frameData, statesSources,
workspace, groundTruthJobFramesMeta, frame, imageFilters,
} = this.props;
const { canvasInstance } = this.props as { canvasInstance: Canvas };

if (frameData !== null && canvasInstance) {
const filteredAnnotations = filterAnnotations(annotations, {
statesSources,
Expand All @@ -908,12 +927,54 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {
workspace,
exclude: [ObjectType.TAG],
});

const proxy = new Proxy(frameData, {
get: (_frameData, prop, receiver) => {
if (prop === 'data') {
return async () => {
const originalImage = await _frameData.data();
const imageIsNotProcessed = imageFilters.some((imageFilter: ImageFilter) => (
imageFilter.modifier.currentProcessedImage !== frame
));

if (imageIsNotProcessed) {
try {
const { renderWidth, renderHeight, imageData: imageBitmap } = originalImage;

const offscreen = new OffscreenCanvas(renderWidth, renderHeight);
const ctx = offscreen.getContext('2d') as OffscreenCanvasRenderingContext2D;
ctx.drawImage(imageBitmap, 0, 0);
const imageData = ctx.getImageData(0, 0, renderWidth, renderHeight);

const newImageData = imageFilters
.reduce((oldImageData, activeImageModifier) => activeImageModifier
.modifier.processImage(oldImageData, frame), imageData);
const newImageBitmap = await createImageBitmap(newImageData);
return {
renderWidth,
renderHeight,
imageData: newImageBitmap,
};
} catch (error: any) {
notification.error({
description: error.toString(),
message: 'Image processing error occurred',
className: 'cvat-notification-notice-image-processing-error',
});
}
}

return originalImage;
};
}
return Reflect.get(_frameData, prop, receiver);
},
});
canvasInstance.setup(
frameData,
proxy,
frameData.deleted ? [] : filteredAnnotations,
curZLayer,
);
canvasInstance.configure({ forceFrameUpdate: false });
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (C) 2023 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import React, { useEffect, useState, useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Row, Col } from 'antd/lib/grid';
import { CombinedState } from 'reducers';
import Text from 'antd/lib/typography/Text';
import Slider from 'antd/lib/slider';

import {
enableImageFilter,
disableImageFilter,
} from 'actions/settings-actions';
import GammaCorrection from 'utils/fabric-wrapper/gamma-correciton';
import { ImageFilterAlias, hasFilter } from 'utils/image-processing';

import './image-setups.scss';

export default function GammaFilter(): JSX.Element {
const dispatch = useDispatch();
const [gamma, setGamma] = useState<number>(1);
const filters = useSelector((state: CombinedState) => state.settings.imageFilters);
const gammaFilter = hasFilter(filters, ImageFilterAlias.GAMMA_CORRECTION);

const onChangeGamma = useCallback((newGamma: number): void => {
setGamma(newGamma);
if (newGamma === 1) {
if (gammaFilter) {
dispatch(disableImageFilter(ImageFilterAlias.GAMMA_CORRECTION));
}
} else {
const convertedGamma = [newGamma, newGamma, newGamma];
if (gammaFilter) {
dispatch(enableImageFilter(gammaFilter, { gamma: convertedGamma }));
} else {
dispatch(enableImageFilter({
modifier: new GammaCorrection({ gamma: convertedGamma }),
alias: ImageFilterAlias.GAMMA_CORRECTION,
}));
}
}
}, [gammaFilter]);

useEffect(() => {
if (filters.length === 0) {
setGamma(1);
}
}, [filters]);

return (
<div className='cvat-image-setups-filters'>
<Row justify='space-around'>
<Col span={24}>
<Row className='cvat-image-setups-gamma'>
<Col span={6}>
<Text className='cvat-text-color'> Gamma </Text>
</Col>
<Col span={12}>
<Slider
min={0.2}
max={2.6}
value={gamma}
step={0.01}
onChange={onChangeGamma}
/>
</Col>
</Row>
</Col>
</Row>
</div>
);
}
Loading

0 comments on commit 6698fd2

Please sign in to comment.