Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add gamma correction filter #6771

Merged
merged 44 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
e9547e4
initial commit
klakhov Aug 25, 2023
f2faf9f
added image filter actions
klakhov Aug 30, 2023
eed9053
run image modifiers from high-level component
klakhov Aug 30, 2023
a9c859f
draft of adding/removing filters
klakhov Aug 30, 2023
1437e48
removed image filtering logic from opencv component
klakhov Aug 30, 2023
90c601d
added gamma checkbox
klakhov Aug 30, 2023
d4188c1
fixed adding/removing several filters to image
klakhov Aug 31, 2023
b2821bd
moved image processing to separate util
klakhov Aug 31, 2023
3ebd1fd
added image filters component
klakhov Aug 31, 2023
094b4ba
reworked filters class system
klakhov Aug 31, 2023
f4ffb17
improved filter sliders & added reset
klakhov Aug 31, 2023
29619de
improved gamma correction class
klakhov Aug 31, 2023
c67ffd7
improved image-processing component
klakhov Aug 31, 2023
232dda5
Merge branch 'develop' into kl/gamma-correction
klakhov Aug 31, 2023
e26d971
fixed headers
klakhov Aug 31, 2023
ee7c6bf
reverted yarn.lock changes
klakhov Aug 31, 2023
cdeb4d9
fixed eslint
klakhov Aug 31, 2023
85e7be1
updated histogram equalization test
klakhov Aug 31, 2023
5b7c959
updated version & changelog
klakhov Aug 31, 2023
0d43f96
removed unnecessary canvas configure
klakhov Aug 31, 2023
efc7d52
Merge branch 'develop' into kl/gamma-correction
bsekachev Sep 2, 2023
1493185
removed rgb sliders
klakhov Sep 4, 2023
87199fe
reworked actions
klakhov Sep 5, 2023
0844604
reset gamma on filters reset
klakhov Sep 5, 2023
29d13e4
codestyle improvements
klakhov Sep 5, 2023
1e294a7
empty implementation for filter configuration
klakhov Sep 5, 2023
2a3eace
added global fabric dependency
klakhov Sep 5, 2023
d305453
moved image processing logic to canvas wrapper
klakhov Sep 5, 2023
5738eb0
refactored code in canvas-wrapper
klakhov Sep 5, 2023
6b223fd
changed ui of color settings
klakhov Sep 5, 2023
7d77190
Merge branch 'kl/gamma-correction' of https://github.com/cvat-ai/cvat…
klakhov Sep 5, 2023
46f0241
Merge branch 'develop' into kl/gamma-correction
klakhov Sep 5, 2023
3410674
added `no-extraneous-dependencies` eslint rule settings for global de…
klakhov Sep 5, 2023
43e7631
added error handling
klakhov Sep 5, 2023
846aa5f
Merge branch 'develop' into kl/gamma-correction
klakhov Sep 5, 2023
5b2b761
added copy data for fabric filters, removed cache for histogram
klakhov Sep 5, 2023
aa31a1e
added debounce
klakhov Sep 5, 2023
aefd222
fixed eslint
klakhov Sep 5, 2023
a2c8820
upodated eslint + removed comment
klakhov Sep 5, 2023
a952d90
Merge branch 'develop' into kl/gamma-correction
klakhov Sep 6, 2023
32422e0
reset filters on open job
klakhov Sep 6, 2023
28e61c2
debounced canvas update on filter changes
klakhov Sep 6, 2023
68ae2f4
updated debounce time
klakhov Sep 6, 2023
3ba408c
Merge branch 'develop' into kl/gamma-correction
klakhov Sep 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## \[Unreleased\]
### Added
- TDB
- 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
4 changes: 4 additions & 0 deletions cvat-canvas/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (C) 2019-2022 Intel Corporation
// Copyright (C) 2023 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

Expand All @@ -13,4 +14,7 @@ module.exports = {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
rules: {
'import/no-extraneous-dependencies': ['error', { packageDir: ['.', '../'] }],
},
};
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
3 changes: 3 additions & 0 deletions cvat-canvas3d/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ module.exports = {
'node_modules/**',
'dist/**',
],
rules: {
'import/no-extraneous-dependencies': ['error', { packageDir: ['.', '../'] }],
},
};
3 changes: 3 additions & 0 deletions cvat-core/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ module.exports = {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
rules: {
'import/no-extraneous-dependencies': ['error', { packageDir: ['.', '../'] }],
},
};
3 changes: 3 additions & 0 deletions cvat-data/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ module.exports = {
tsconfigRootDir: __dirname,
},
plugins: ['jest'],
rules: {
'import/no-extraneous-dependencies': ['error', { packageDir: ['.', '../'] }],
},
};
2 changes: 2 additions & 0 deletions cvat-ui/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2023 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -29,5 +30,6 @@ 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: ['.', '../'] }],
},
};
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.6",
"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 @@ -380,3 +385,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,7 @@ 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 GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import {
Expand Down Expand Up @@ -57,6 +58,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 +115,7 @@ interface StateToProps {
showGroundTruth: boolean;
highlightedConflict: QualityConflict | null;
groundTruthJobFramesMeta: FramesMetaData | null;
imageFilters: ImageFilter[];
}

interface DispatchToProps {
Expand Down Expand Up @@ -194,6 +197,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 +257,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
showGroundTruth,
highlightedConflict,
groundTruthJobFramesMeta,
imageFilters,
};
}

Expand Down Expand Up @@ -445,6 +450,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,11 +536,18 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {
});
}

if (
prevProps.imageFilters !== imageFilters
) {
klakhov marked this conversation as resolved.
Show resolved Hide resolved
canvasInstance.configure({ forceFrameUpdate: true });
}

if (
prevProps.annotations !== annotations ||
prevProps.statesSources !== statesSources ||
prevProps.frameData !== frameData ||
prevProps.curZLayer !== curZLayer
prevProps.curZLayer !== curZLayer ||
prevProps.imageFilters !== imageFilters
) {
this.updateCanvas();
}
Expand Down Expand Up @@ -897,9 +910,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 +923,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,73 @@
// Copyright (C) 2023 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import React, { useCallback, useEffect, useState } 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) => {
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,
}));
}
}
}, [filters]);

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={(value) => onChangeGamma(value)}
klakhov marked this conversation as resolved.
Show resolved Hide resolved
/>
</Col>
</Row>
</Col>
</Row>
</div>
);
}
Loading