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

Histogram equalization feature #3447

Merged
merged 12 commits into from
Jul 27, 2021
8 changes: 4 additions & 4 deletions cvat-canvas/src/typescript/canvas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -28,7 +28,7 @@ const CanvasVersion = pjson.version;

interface Canvas {
html(): HTMLDivElement;
setup(frameData: any, objectStates: any[], zLayer?: number): void;
setup(frameData: any, objectStates: any[], zLayer?: number, forceUpdate?: boolean): void;
setupIssueRegions(issueRegions: Record<number, number[]>): void;
activate(clientID: number | null, attributeID?: number): void;
rotate(rotationAngle: number): void;
Expand Down Expand Up @@ -72,8 +72,8 @@ class CanvasImpl implements Canvas {
return this.view.html();
}

public setup(frameData: any, objectStates: any[], zLayer = 0): void {
this.model.setup(frameData, objectStates, zLayer);
public setup(frameData: any, objectStates: any[], zLayer = 0, forceUpdate = false): void {
this.model.setup(frameData, objectStates, zLayer, forceUpdate);
}

public setupIssueRegions(issueRegions: Record<number, number[]>): void {
Expand Down
6 changes: 3 additions & 3 deletions cvat-canvas/src/typescript/canvasModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export interface CanvasModel {
zoom(x: number, y: number, direction: number): void;
move(topOffset: number, leftOffset: number): void;

setup(frameData: any, objectStates: any[], zLayer: number): void;
setup(frameData: any, objectStates: any[], zLayer: number, forceUpdate: boolean): void;
setupIssueRegions(issueRegions: Record<number, number[]>): void;
activate(clientID: number | null, attributeID: number | null): void;
rotate(rotationAngle: number): void;
Expand Down Expand Up @@ -386,14 +386,14 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
this.notify(UpdateReasons.ZOOM_CANVAS);
}

public setup(frameData: any, objectStates: any[], zLayer: number): void {
public setup(frameData: any, objectStates: any[], zLayer: number, forceUpdate: boolean = false): void {
if (this.data.imageID !== frameData.number) {
if ([Mode.EDIT, Mode.DRAG, Mode.RESIZE].includes(this.data.mode)) {
throw Error(`Canvas is busy. Action: ${this.data.mode}`);
}
}

if (frameData.number === this.data.imageID) {
if (frameData.number === this.data.imageID && !forceUpdate) {
this.data.zLayer = zLayer;
this.data.objects = objectStates;
this.notify(UpdateReasons.OBJECTS_UPDATED);
Expand Down
2 changes: 2 additions & 0 deletions cvat-core/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function build() {
const { Project } = require('./project');
const { Attribute, Label } = require('./labels');
const MLModel = require('./ml-model');
const { FrameData } = require('./frames');

const enums = require('./enums');

Expand Down Expand Up @@ -765,6 +766,7 @@ function build() {
Comment,
Issue,
Review,
FrameData,
},
};

Expand Down
2 changes: 1 addition & 1 deletion cvat-core/src/plugins.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2021 Intel Corporation
klakhov marked this conversation as resolved.
Show resolved Hide resolved
//
// SPDX-License-Identifier: MIT

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import React from 'react';
import { connect } from 'react-redux';
import { Row, Col } from 'antd/lib/grid';
import Popover from 'antd/lib/popover';
import Icon, { ScissorOutlined } from '@ant-design/icons';
import Icon, { AreaChartOutlined, ScissorOutlined } from '@ant-design/icons';
import Text from 'antd/lib/typography/Text';
import Tabs from 'antd/lib/tabs';
import Button from 'antd/lib/button';
Expand All @@ -29,6 +29,7 @@ import {
} from 'actions/annotation-actions';
import LabelSelector from 'components/label-selector/label-selector';
import CVATTooltip from 'components/common/cvat-tooltip';
import { HistogramEqualization } from 'utils/opencv-wrapper/histogram-equalization';
import withVisibilityHandling from './handle-popover-visibility';

interface Props {
Expand All @@ -39,6 +40,7 @@ interface Props {
states: any[];
frame: number;
curZOrder: number;
data: any;
klakhov marked this conversation as resolved.
Show resolved Hide resolved
}

interface DispatchToProps {
Expand All @@ -53,6 +55,7 @@ interface State {
initializationError: boolean;
initializationProgress: number;
activeLabelID: number;
activeImageModifiers: string[];
}

const core = getCore();
Expand All @@ -68,7 +71,7 @@ function mapStateToProps(state: CombinedState): Props {
job: { instance: jobInstance, labels },
canvas: { activeControl, instance: canvasInstance },
player: {
frame: { number: frame },
frame: { number: frame, data },
},
},
} = state;
Expand All @@ -81,6 +84,7 @@ function mapStateToProps(state: CombinedState): Props {
labels,
states,
frame,
data,
};
}

Expand All @@ -93,22 +97,27 @@ const mapDispatchToProps = {

class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps, State> {
private activeTool: IntelligentScissors | null;
private activeImageModifier: HistogramEqualization | null;

public constructor(props: Props & DispatchToProps) {
super(props);
const { labels } = props;
this.activeTool = null;
this.activeImageModifier = null;

this.state = {
libraryInitialized: openCVWrapper.isInitialized,
initializationError: false,
initializationProgress: -1,
activeLabelID: labels.length ? labels[0].id : null,
activeImageModifiers: [],
};
}

public componentDidMount(): void {
const { canvasInstance } = this.props;
canvasInstance.html().addEventListener('canvas.interacted', this.interactionListener);
canvasInstance.html().addEventListener('canvas.setup', this.setupListener);
}

public componentDidUpdate(prevProps: Props): void {
Expand All @@ -124,8 +133,16 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
public componentWillUnmount(): void {
const { canvasInstance } = this.props;
canvasInstance.html().removeEventListener('canvas.interacted', this.interactionListener);
canvasInstance.html().removeEventListener('canvas.setup', this.setupListener);
}

private setupListener = async ():Promise<void> => {
klakhov marked this conversation as resolved.
Show resolved Hide resolved
const { frame } = this.props;
if (this.activeImageModifier && this.activeImageModifier.currentEqualizedNumber !== frame) {
this.runImageModifier();
}
};

private interactionListener = async (e: Event): Promise<void> => {
const {
createAnnotations, isActivated, jobInstance, frame, labels, curZOrder, canvasInstance,
Expand Down Expand Up @@ -215,6 +232,33 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
return points;
}

private runImageModifier():void {
if (this.activeImageModifier) {
const {
data, states, curZOrder, canvasInstance, frame,
} = this.props;
const canvas: HTMLCanvasElement | undefined = window.document.getElementById('cvat_canvas_background') as
| HTMLCanvasElement
| undefined;
if (!canvas) {
throw new Error('Element #cvat_canvas_background was not found');
}
const { width, height } = canvas;
const context = canvas.getContext('2d');
if (!context) {
throw new Error('Canvas context is empty');
}
const imageData = context.getImageData(0, 0, width, height);
this.activeImageModifier.equalize(imageData, frame).then((newBitmap:ImageBitmap|undefined) => {
if (newBitmap) {
// eslint-disable-next-line no-underscore-dangle
data._data.imageData = newBitmap;
canvasInstance.setup(data, states, curZOrder, true);
}
});
}
}
klakhov marked this conversation as resolved.
Show resolved Hide resolved

private renderDrawingContent(): JSX.Element {
const { activeLabelID } = this.state;
const { labels, canvasInstance, onInteractionStart } = this.props;
Expand Down Expand Up @@ -254,6 +298,49 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
);
}

private renderImageContent():JSX.Element {
const { activeImageModifiers } = this.state;
const histogramActive = activeImageModifiers.includes('histogram');
return (
<>
klakhov marked this conversation as resolved.
Show resolved Hide resolved
<Row justify='start'>
<Col>
<CVATTooltip title='Histogram equalization' className='cvat-opencv-image-tool'>
<Button
className={histogramActive ? 'cvat-opencv-image-tool-active' : ''}
onClick={() => {
if (!this.activeImageModifier) {
klakhov marked this conversation as resolved.
Show resolved Hide resolved
const { hist } = openCVWrapper;
this.activeImageModifier = hist;
this.runImageModifier();
this.setState({
activeImageModifiers: ['histogram'],
});
} else {
const {
data, states, curZOrder, canvasInstance,
} = this.props;
this.activeImageModifier.restoreImage().then((newBitmap) => {
klakhov marked this conversation as resolved.
Show resolved Hide resolved
// eslint-disable-next-line no-underscore-dangle
data._data.imageData = newBitmap;
klakhov marked this conversation as resolved.
Show resolved Hide resolved
canvasInstance.setup(data, states, curZOrder, true);
});
this.activeImageModifier = null;
this.setState({
activeImageModifiers: [],
});
}
}}
>
<AreaChartOutlined />
</Button>
</CVATTooltip>
</Col>
</Row>
</>
);
}

private renderContent(): JSX.Element {
const { libraryInitialized, initializationProgress, initializationError } = this.state;

Expand All @@ -271,7 +358,9 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
<Tabs.TabPane key='drawing' tab='Drawing' className='cvat-opencv-control-tabpane'>
{this.renderDrawingContent()}
</Tabs.TabPane>
<Tabs.TabPane disabled key='image' tab='Image' className='cvat-opencv-control-tabpane' />
<Tabs.TabPane key='image' tab='Image' className='cvat-opencv-control-tabpane'>
{this.renderImageContent()}
</Tabs.TabPane>
</Tabs>
) : (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,15 @@
}
}

.cvat-opencv-image-tool {
@extend .cvat-opencv-drawing-tool;
}

.cvat-opencv-image-tool-active {
color: #40a9ff;
border-color: #40a9ff;
}

.cvat-setup-tag-popover-content,
.cvat-draw-shape-popover-content {
padding: $grid-unit-size;
Expand Down
Loading