OpenCV.js caching and autoload (#30)

main
Kirill Lakhov 4 years ago committed by GitHub
parent 67eca0669e
commit 36fdcea8d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added Python SDK package (`cvat-sdk`)
- Previews for jobs
- Documentation for LDAP authentication (<https://github.com/cvat-ai/cvat/pull/39>)
- OpenCV.js caching and autoload (<https://github.com/cvat-ai/cvat/pull/30>)
- Publishing dev version of CVAT docker images (<https://github.com/cvat-ai/cvat/pull/53>)
### Changed

@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.40.0",
"version": "1.40.1",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {

@ -5,10 +5,15 @@
import React, { useState } from 'react';
import Popover, { PopoverProps } from 'antd/lib/popover';
interface OwnProps {
overlayClassName?: string;
onVisibleChange?: (visible: boolean) => void;
}
export default function withVisibilityHandling(WrappedComponent: typeof Popover, popoverType: string) {
return (props: PopoverProps): JSX.Element => {
return (props: OwnProps & PopoverProps): JSX.Element => {
const [visible, setVisible] = useState<boolean>(false);
const { overlayClassName, ...rest } = props;
const { overlayClassName, onVisibleChange, ...rest } = props;
const overlayClassNames = typeof overlayClassName === 'string' ? overlayClassName.split(/\s+/) : [];
const popoverClassName = `cvat-${popoverType}-popover`;
overlayClassNames.push(popoverClassName);
@ -34,6 +39,7 @@ export default function withVisibilityHandling(WrappedComponent: typeof Popover,
}
}
setVisible(_visible);
if (onVisibleChange) onVisibleChange(_visible);
}}
/>
);

@ -210,6 +210,7 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
const { canvasInstance } = this.props;
canvasInstance.html().removeEventListener('canvas.interacted', this.interactionListener);
canvasInstance.html().removeEventListener('canvas.setup', this.runImageModifier);
openCVWrapper.removeProgressCallback();
}
private interactionListener = async (e: Event): Promise<void> => {
@ -286,7 +287,7 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
});
createAnnotations(jobInstance, frame, [finalObject]);
}
} catch (error) {
} catch (error: any) {
notification.error({
description: error.toString(),
message: 'OpenCV.js processing error occured',
@ -342,9 +343,9 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
// update annotations on a canvas
fetchAnnotations();
} catch (err) {
} catch (error: any) {
notification.error({
description: err.toString(),
description: error.toString(),
message: 'Tracking error occured',
});
}
@ -397,7 +398,7 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
frameData.imageData = imageBitmap;
canvasInstance.setup(frameData, states, curZOrder);
}
} catch (error) {
} catch (error: any) {
notification.error({
description: error.toString(),
message: 'OpenCV.js processing error occured',
@ -426,7 +427,7 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
objectState.points = points;
objectState.save().then(() => {
shape.shapePoints = points;
}).catch((error) => {
}).catch((error: any) => {
reject(error);
});
}
@ -590,6 +591,33 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
}
}
private async initializeOpenCV():Promise<void> {
try {
this.setState({
initializationError: false,
initializationProgress: 0,
});
await openCVWrapper.initialize((progress: number) => {
this.setState({ initializationProgress: progress });
});
const trackers = Object.values(openCVWrapper.tracking);
this.setState({
libraryInitialized: true,
activeTracker: trackers[0],
trackers,
});
} catch (error: any) {
notification.error({
description: error.toString(),
message: 'Could not initialize OpenCV library',
});
this.setState({
initializationError: true,
initializationProgress: -1,
});
}
}
private renderDrawingContent(): JSX.Element {
const { activeLabelID } = this.state;
const { labels, canvasInstance, onInteractionStart } = this.props;
@ -773,42 +801,21 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
) : (
<>
<Row justify='start' align='middle'>
<Col span={initializationProgress >= 0 ? 17 : 24}>
<Button
disabled={initializationProgress !== -1}
className='cvat-opencv-initialization-button'
onClick={async () => {
try {
this.setState({
initializationError: false,
initializationProgress: 0,
});
await openCVWrapper.initialize((progress: number) => {
this.setState({ initializationProgress: progress });
});
const trackers = Object.values(openCVWrapper.tracking);
this.setState({
libraryInitialized: true,
activeTracker: trackers[0],
trackers,
});
} catch (error) {
notification.error({
description: error.toString(),
message: 'Could not initialize OpenCV library',
});
this.setState({
initializationError: true,
initializationProgress: -1,
});
}
}}
>
Load OpenCV
</Button>
<Col>
{
initializationProgress >= 0 ?
<Text>OpenCV is loading</Text> : (
<Button
className='cvat-opencv-initialization-button'
onClick={() => { this.initializeOpenCV(); }}
>
Reload OpenCV
</Button>
)
}
</Col>
{initializationProgress >= 0 && (
<Col span={6} offset={1}>
<Col>
<Progress
width={8 * 5}
percent={initializationProgress}
@ -857,8 +864,13 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
placement='right'
overlayClassName='cvat-opencv-control-popover'
content={this.renderContent()}
afterVisibleChange={() => {
if (libraryInitialized !== openCVWrapper.isInitialized) {
onVisibleChange={(visible: boolean) => {
const { initializationProgress } = this.state;
if (!visible || initializationProgress >= 0) return;
if (!openCVWrapper.isInitialized || openCVWrapper.initializationInProgress) {
this.initializeOpenCV();
} else if (libraryInitialized !== openCVWrapper.isInitialized) {
this.setState({
libraryInitialized: openCVWrapper.isInitialized,
});

@ -226,6 +226,10 @@
width: $grid-unit-size * 14;
justify-content: center;
}
.ant-progress {
margin-left: $grid-unit-size;
}
}
.cvat-opencv-initialization-button {

@ -30,13 +30,17 @@ export interface Tracking {
export class OpenCVWrapper {
private initialized: boolean;
private cv: any;
private onProgress: ((percent: number) => void) | null;
private injectionProcess: Promise<void> | null;
public constructor() {
this.initialized = false;
this.cv = null;
this.onProgress = null;
this.injectionProcess = null;
}
public async initialize(onProgress: (percent: number) => void): Promise<void> {
private async inject(): Promise<void> {
const response = await fetch(`${baseURL}/opencv/opencv.js`);
if (response.status !== 200) {
throw new Error(`Response status ${response.status}. ${response.statusText}`);
@ -67,7 +71,7 @@ export class OpenCVWrapper {
// Cypress workaround: content-length is always zero in cypress, it is done optional here
// Just progress bar will be disabled
const percentage = contentLength ? (receivedLength * 100) / +(contentLength as string) : 0;
onProgress(+percentage.toFixed(0));
if (this.onProgress) this.onProgress(+percentage.toFixed(0));
}
}
@ -79,13 +83,32 @@ export class OpenCVWrapper {
const global = window as any;
this.cv = await global.cv;
}
public async initialize(onProgress: (percent: number) => void): Promise<void> {
this.onProgress = onProgress;
if (!this.injectionProcess) {
this.injectionProcess = this.inject();
}
await this.injectionProcess;
this.injectionProcess = null;
this.initialized = true;
}
public removeProgressCallback(): void {
this.onProgress = null;
}
public get isInitialized(): boolean {
return this.initialized;
}
public get initializationInProgress(): boolean {
return !!this.injectionProcess;
}
public get contours(): Contours {
if (!this.initialized) {
throw new Error('Need to initialize OpenCV first');

@ -7,4 +7,6 @@ def OpenCVLibrary(request):
dirname = os.path.join(settings.STATIC_ROOT, 'opencv', 'js')
pattern = os.path.join(dirname, 'opencv_*.js')
path = glob.glob(pattern)[0]
return sendfile(request, path)
response = sendfile(request, path)
response['Cache-Control'] = "public, max-age=604800"
return response

@ -9,7 +9,8 @@ The tool based on [Open CV](https://opencv.org/) Computer Vision library
which is an open-source product that includes many CV algorithms.
Some of these algorithms can be used to simplify the annotation process.
First step to work with OpenCV is to load it into CVAT. Click on the toolbar icon, then click `Load OpenCV`.
First step to work with OpenCV is to load it into CVAT.
Click on the toolbar icon, library will be downloaded automatically.
![](/images/image198.jpg)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 15 KiB

@ -80,7 +80,9 @@ context('OpenCV. Intelligent scissors. Histogram Equalization. TrackerMIL.', ()
describe(`Testing case "${caseId}"`, () => {
it('Load OpenCV.', () => {
cy.interactOpenCVControlButton();
cy.get('.cvat-opencv-control-popover').find('.cvat-opencv-initialization-button').click();
cy.get('.cvat-opencv-control-popover').within(() => {
cy.contains('OpenCV is loading').should('not.exist');
});
// Intelligent cissors button be visible
cy.get('.cvat-opencv-drawing-tool').should('exist').and('be.visible');
});

Loading…
Cancel
Save