S11as

conflicts save

Jul 22nd, 2021
889
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // Copyright (C) 2021 Intel Corporation
  2. //
  3. // SPDX-License-Identifier: MIT
  4.  
  5. import React from 'react';
  6. import { connect } from 'react-redux';
  7. import { Row, Col } from 'antd/lib/grid';
  8. import Popover from 'antd/lib/popover';
  9. import Icon, { AreaChartOutlined, ScissorOutlined } from '@ant-design/icons';
  10. import Text from 'antd/lib/typography/Text';
  11. import Tabs from 'antd/lib/tabs';
  12. import Button from 'antd/lib/button';
  13. import Progress from 'antd/lib/progress';
  14. import notification from 'antd/lib/notification';
  15.  
  16. import { OpenCVIcon } from 'icons';
  17. import { Canvas, convertShapesForInteractor } from 'cvat-canvas-wrapper';
  18. import getCore from 'cvat-core-wrapper';
  19. import openCVWrapper from 'utils/opencv-wrapper/opencv-wrapper';
  20. import { IntelligentScissors } from 'utils/opencv-wrapper/intelligent-scissors';
  21. import {
  22.     CombinedState, ActiveControl, OpenCVTool, ObjectType, ShapeType,
  23. } from 'reducers/interfaces';
  24. import {
  25.     interactWithCanvas,
  26.     fetchAnnotationsAsync,
  27.     updateAnnotationsAsync,
  28.     createAnnotationsAsync,
  29. } from 'actions/annotation-actions';
  30. import LabelSelector from 'components/label-selector/label-selector';
  31. import CVATTooltip from 'components/common/cvat-tooltip';
  32. import { HistogramEqualization } from 'utils/opencv-wrapper/histogram-equalization';
  33. import withVisibilityHandling from './handle-popover-visibility';
  34.  
  35. interface Props {
  36.     labels: any[];
  37.     canvasInstance: Canvas;
  38.     jobInstance: any;
  39.     isActivated: boolean;
  40.     states: any[];
  41.     frame: number;
  42.     curZOrder: number;
  43.     data: any;
  44. }
  45.  
  46. interface DispatchToProps {
  47.     onInteractionStart(activeInteractor: OpenCVTool, activeLabelID: number): void;
  48.     updateAnnotations(statesToUpdate: any[]): void;
  49.     createAnnotations(sessionInstance: any, frame: number, statesToCreate: any[]): void;
  50.     fetchAnnotations(): void;
  51. }
  52.  
  53. interface State {
  54.     libraryInitialized: boolean;
  55.     initializationError: boolean;
  56.     initializationProgress: number;
  57.     activeLabelID: number;
  58.     activeImageModifiers: string[];
  59. }
  60.  
  61. const core = getCore();
  62. const CustomPopover = withVisibilityHandling(Popover, 'opencv-control');
  63.  
  64. function mapStateToProps(state: CombinedState): Props {
  65.     const {
  66.         annotation: {
  67.             annotations: {
  68.                 states,
  69.                 zLayer: { cur: curZOrder },
  70.             },
  71.             job: { instance: jobInstance, labels },
  72.             canvas: { activeControl, instance: canvasInstance },
  73.             player: {
  74.                 frame: { number: frame, data },
  75.             },
  76.         },
  77.     } = state;
  78.  
  79.     return {
  80.         isActivated: activeControl === ActiveControl.OPENCV_TOOLS,
  81.         canvasInstance: canvasInstance as Canvas,
  82.         jobInstance,
  83.         curZOrder,
  84.         labels,
  85.         states,
  86.         frame,
  87.         data,
  88.     };
  89. }
  90.  
  91. const mapDispatchToProps = {
  92.     onInteractionStart: interactWithCanvas,
  93.     updateAnnotations: updateAnnotationsAsync,
  94.     fetchAnnotations: fetchAnnotationsAsync,
  95.     createAnnotations: createAnnotationsAsync,
  96. };
  97.  
  98. class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps, State> {
  99.     private activeTool: IntelligentScissors | null;
  100. <<<<<<< kl/histogram-equalization
  101.     private interactiveStateID: number | null;
  102.     private interactionIsDone: boolean;
  103.     private activeImageModifier: HistogramEqualization | null;
  104. =======
  105. >>>>>>> develop
  106.  
  107.     public constructor(props: Props & DispatchToProps) {
  108.         super(props);
  109.         const { labels } = props;
  110.         this.activeTool = null;
  111. <<<<<<< kl/histogram-equalization
  112.         this.interactiveStateID = null;
  113.         this.interactionIsDone = false;
  114.         this.activeImageModifier = null;
  115.  
  116. =======
  117. >>>>>>> develop
  118.         this.state = {
  119.             libraryInitialized: openCVWrapper.isInitialized,
  120.             initializationError: false,
  121.             initializationProgress: -1,
  122.             activeLabelID: labels.length ? labels[0].id : null,
  123.             activeImageModifiers: [],
  124.         };
  125.     }
  126.  
  127.     public componentDidMount(): void {
  128.         const { canvasInstance } = this.props;
  129.         canvasInstance.html().addEventListener('canvas.interacted', this.interactionListener);
  130. <<<<<<< kl/histogram-equalization
  131.         canvasInstance.html().addEventListener('canvas.canceled', this.cancelListener);
  132.         canvasInstance.html().addEventListener('canvas.setup', this.setupListener);
  133. =======
  134. >>>>>>> develop
  135.     }
  136.  
  137.     public componentDidUpdate(prevProps: Props): void {
  138.         const { isActivated } = this.props;
  139.         if (!prevProps.isActivated && isActivated) {
  140.             // reset flags when before using a tool
  141.             if (this.activeTool) {
  142.                 this.activeTool.reset();
  143.             }
  144.         }
  145.     }
  146.  
  147.     public componentWillUnmount(): void {
  148.         const { canvasInstance } = this.props;
  149.         canvasInstance.html().removeEventListener('canvas.interacted', this.interactionListener);
  150. <<<<<<< kl/histogram-equalization
  151.         canvasInstance.html().removeEventListener('canvas.canceled', this.cancelListener);
  152.         canvasInstance.html().removeEventListener('canvas.setup', this.setupListener);
  153.     }
  154.  
  155.     private getInteractiveState(): any | null {
  156.         const { states } = this.props;
  157.         return states.filter((_state: any): boolean => _state.clientID === this.interactiveStateID)[0] || null;
  158.     }
  159.  
  160.     private setupListener = async ():Promise<void> => {
  161.         const { frame } = this.props;
  162.         console.log(frame, this.activeImageModifier?.currentEqualizedNumber);
  163.         if (this.activeImageModifier && this.activeImageModifier.currentEqualizedNumber !== frame) {
  164.             this.runImageModifier();
  165.         }
  166.     };
  167.  
  168.     private cancelListener = async (): Promise<void> => {
  169.         const {
  170.             fetchAnnotations, isActivated, jobInstance, frame,
  171.         } = this.props;
  172.  
  173.         if (isActivated) {
  174.             if (this.interactiveStateID !== null) {
  175.                 const state = this.getInteractiveState();
  176.                 this.interactiveStateID = null;
  177.                 await state.delete(frame);
  178.                 fetchAnnotations();
  179.             }
  180.  
  181.             await jobInstance.actions.freeze(false);
  182.         }
  183.     };
  184.  
  185. =======
  186.     }
  187.  
  188. >>>>>>> develop
  189.     private interactionListener = async (e: Event): Promise<void> => {
  190.         const {
  191.             createAnnotations, isActivated, jobInstance, frame, labels, curZOrder, canvasInstance,
  192.         } = this.props;
  193.         const { activeLabelID } = this.state;
  194.         if (!isActivated || !this.activeTool) {
  195.             return;
  196.         }
  197.  
  198.         const {
  199.             shapesUpdated, isDone, threshold, shapes,
  200.         } = (e as CustomEvent).detail;
  201.         const pressedPoints = convertShapesForInteractor(shapes, 0).flat();
  202.  
  203.         try {
  204.             if (shapesUpdated) {
  205.                 const result = await this.runCVAlgorithm(pressedPoints, threshold);
  206.                 canvasInstance.interact({
  207.                     enabled: true,
  208.                     intermediateShape: {
  209.                         shapeType: ShapeType.POLYGON,
  210.                         points: result,
  211.                     },
  212.                 });
  213.             }
  214.  
  215.             if (isDone) {
  216.                 const finalObject = new core.classes.ObjectState({
  217.                     frame,
  218.                     objectType: ObjectType.SHAPE,
  219.                     shapeType: ShapeType.POLYGON,
  220.                     label: labels.filter((label: any) => label.id === activeLabelID)[0],
  221.                     // need to recalculate without the latest sliding point
  222.                     points: await this.runCVAlgorithm(pressedPoints, threshold),
  223.                     occluded: false,
  224.                     zOrder: curZOrder,
  225.                 });
  226.                 createAnnotations(jobInstance, frame, [finalObject]);
  227.             }
  228.         } catch (error) {
  229.             notification.error({
  230.                 description: error.toString(),
  231.                 message: 'OpenCV.js processing error occured',
  232.             });
  233.         }
  234.     };
  235.  
  236.     private async runCVAlgorithm(pressedPoints: number[], threshold: number): Promise<number[]> {
  237.         // Getting image data
  238.         const canvas: HTMLCanvasElement | undefined = window.document.getElementById('cvat_canvas_background') as
  239.             | HTMLCanvasElement
  240.             | undefined;
  241.         if (!canvas) {
  242.             throw new Error('Element #cvat_canvas_background was not found');
  243.         }
  244.  
  245.         const { width, height } = canvas;
  246.         const context = canvas.getContext('2d');
  247.         if (!context) {
  248.             throw new Error('Canvas context is empty');
  249.         }
  250.  
  251.         const [x, y] = pressedPoints.slice(-2);
  252.         const startX = Math.round(Math.max(0, x - threshold));
  253.         const startY = Math.round(Math.max(0, y - threshold));
  254.         const segmentWidth = Math.min(2 * threshold, width - startX);
  255.         const segmentHeight = Math.min(2 * threshold, height - startY);
  256.         const imageData = context.getImageData(startX, startY, segmentWidth, segmentHeight);
  257.  
  258.         if (!this.activeTool) return [];
  259.  
  260.         // Handling via OpenCV.js
  261.         const points = await this.activeTool.run(pressedPoints, imageData, startX, startY);
  262.  
  263.         // Increasing number of points artificially
  264.         let minNumberOfPoints = 1;
  265.         // eslint-disable-next-line: eslintdot-notation
  266.         if (this.activeTool.params.shape.shapeType === 'polyline') {
  267.             minNumberOfPoints = 2;
  268.         } else if (this.activeTool.params.shape.shapeType === 'polygon') {
  269.             minNumberOfPoints = 3;
  270.         }
  271.         while (points.length < minNumberOfPoints * 2) {
  272.             points.push(...points.slice(points.length - 2));
  273.         }
  274.  
  275.         return points;
  276.     }
  277.  
  278.     private runImageModifier():void {
  279.         if (this.activeImageModifier) {
  280.             const {
  281.                 data, states, curZOrder, canvasInstance, frame,
  282.             } = this.props;
  283.             const canvas: HTMLCanvasElement | undefined = window.document.getElementById('cvat_canvas_background') as
  284.                 | HTMLCanvasElement
  285.                 | undefined;
  286.             if (!canvas) {
  287.                 throw new Error('Element #cvat_canvas_background was not found');
  288.             }
  289.             const { width, height } = canvas;
  290.             const context = canvas.getContext('2d');
  291.             if (!context) {
  292.                 throw new Error('Canvas context is empty');
  293.             }
  294.             const imageData = context.getImageData(0, 0, width, height);
  295.             this.activeImageModifier.equalize(imageData, frame).then((newBitmap:ImageBitmap|undefined) => {
  296.                 if (newBitmap) {
  297.                     // eslint-disable-next-line no-underscore-dangle
  298.                     data._data.imageData = newBitmap;
  299.                     canvasInstance.setup(data, states, curZOrder, true);
  300.                 }
  301.             });
  302.         }
  303.     }
  304.  
  305.     private renderDrawingContent(): JSX.Element {
  306.         const { activeLabelID } = this.state;
  307.         const { labels, canvasInstance, onInteractionStart } = this.props;
  308.  
  309.         return (
  310.             <>
  311.                 <Row justify='center'>
  312.                     <Col span={24}>
  313.                         <LabelSelector
  314.                             style={{ width: '100%' }}
  315.                             labels={labels}
  316.                             value={activeLabelID}
  317.                             onChange={(label: any) => this.setState({ activeLabelID: label.id })}
  318.                         />
  319.                     </Col>
  320.                 </Row>
  321.                 <Row justify='start' className='cvat-opencv-drawing-tools'>
  322.                     <Col>
  323.                         <CVATTooltip title='Intelligent scissors' className='cvat-opencv-drawing-tool'>
  324.                             <Button
  325.                                 onClick={() => {
  326.                                     this.activeTool = openCVWrapper.segmentation.intelligentScissorsFactory();
  327.                                     canvasInstance.cancel();
  328.                                     onInteractionStart(this.activeTool, activeLabelID);
  329.                                     canvasInstance.interact({
  330.                                         enabled: true,
  331.                                         ...this.activeTool.params.canvas,
  332.                                     });
  333.                                 }}
  334.                             >
  335.                                 <ScissorOutlined />
  336.                             </Button>
  337.                         </CVATTooltip>
  338.                     </Col>
  339.                 </Row>
  340.             </>
  341.         );
  342.     }
  343.  
  344.     private renderImageContent():JSX.Element {
  345.         const { activeImageModifiers } = this.state;
  346.         const histogramActive = activeImageModifiers.includes('histogram');
  347.         return (
  348.             <>
  349.                 <Row justify='start'>
  350.                     <Col>
  351.                         <CVATTooltip title='Histogram equalization' className='cvat-opencv-image-tool'>
  352.                             <Button
  353.                                 className={histogramActive ? 'cvat-opencv-image-tool-active' : ''}
  354.                                 onClick={() => {
  355.                                     if (!this.activeImageModifier) {
  356.                                         const { hist } = openCVWrapper;
  357.                                         this.activeImageModifier = hist;
  358.                                         this.runImageModifier();
  359.                                         this.setState({
  360.                                             activeImageModifiers: ['histogram'],
  361.                                         });
  362.                                     } else {
  363.                                         const {
  364.                                             data, states, curZOrder, canvasInstance,
  365.                                         } = this.props;
  366.                                         this.activeImageModifier.restoreImage().then((newBitmap) => {
  367.                                             // eslint-disable-next-line no-underscore-dangle
  368.                                             data._data.imageData = newBitmap;
  369.                                             canvasInstance.setup(data, states, curZOrder, true);
  370.                                         });
  371.                                         this.activeImageModifier = null;
  372.                                         this.setState({
  373.                                             activeImageModifiers: [],
  374.                                         });
  375.                                     }
  376.                                 }}
  377.                             >
  378.                                 <AreaChartOutlined />
  379.                             </Button>
  380.                         </CVATTooltip>
  381.                     </Col>
  382.                 </Row>
  383.             </>
  384.         );
  385.     }
  386.  
  387.     private renderContent(): JSX.Element {
  388.         const { libraryInitialized, initializationProgress, initializationError } = this.state;
  389.  
  390.         return (
  391.             <div className='cvat-opencv-control-popover-content'>
  392.                 <Row justify='start'>
  393.                     <Col>
  394.                         <Text className='cvat-text-color' strong>
  395.                             OpenCV
  396.                         </Text>
  397.                     </Col>
  398.                 </Row>
  399.                 {libraryInitialized ? (
  400.                     <Tabs tabBarGutter={8}>
  401.                         <Tabs.TabPane key='drawing' tab='Drawing' className='cvat-opencv-control-tabpane'>
  402.                             {this.renderDrawingContent()}
  403.                         </Tabs.TabPane>
  404.                         <Tabs.TabPane key='image' tab='Image' className='cvat-opencv-control-tabpane'>
  405.                             {this.renderImageContent()}
  406.                         </Tabs.TabPane>
  407.                     </Tabs>
  408.                 ) : (
  409.                     <>
  410.                         <Row justify='start' align='middle'>
  411.                             <Col span={initializationProgress >= 0 ? 17 : 24}>
  412.                                 <Button
  413.                                     disabled={initializationProgress !== -1}
  414.                                     className='cvat-opencv-initialization-button'
  415.                                     onClick={async () => {
  416.                                         try {
  417.                                             this.setState({
  418.                                                 initializationError: false,
  419.                                                 initializationProgress: 0,
  420.                                             });
  421.                                             await openCVWrapper.initialize((progress: number) => {
  422.                                                 this.setState({ initializationProgress: progress });
  423.                                             });
  424.                                             this.setState({ libraryInitialized: true });
  425.                                         } catch (error) {
  426.                                             notification.error({
  427.                                                 description: error.toString(),
  428.                                                 message: 'Could not initialize OpenCV library',
  429.                                             });
  430.                                             this.setState({
  431.                                                 initializationError: true,
  432.                                                 initializationProgress: -1,
  433.                                             });
  434.                                         }
  435.                                     }}
  436.                                 >
  437.                                     Load OpenCV
  438.                                 </Button>
  439.                             </Col>
  440.                             {initializationProgress >= 0 && (
  441.                                 <Col span={6} offset={1}>
  442.                                     <Progress
  443.                                         width={8 * 5}
  444.                                         percent={initializationProgress}
  445.                                         type='circle'
  446.                                         status={initializationError ? 'exception' : undefined}
  447.                                     />
  448.                                 </Col>
  449.                             )}
  450.                         </Row>
  451.                     </>
  452.                 )}
  453.             </div>
  454.         );
  455.     }
  456.  
  457.     public render(): JSX.Element {
  458.         const { isActivated, canvasInstance, labels } = this.props;
  459.         const dynamcPopoverPros = isActivated ?
  460.             {
  461.                 overlayStyle: {
  462.                     display: 'none',
  463.                 },
  464.             } :
  465.             {};
  466.  
  467.         const dynamicIconProps = isActivated ?
  468.             {
  469.                 className: 'cvat-opencv-control cvat-active-canvas-control',
  470.                 onClick: (): void => {
  471.                     canvasInstance.interact({ enabled: false });
  472.                 },
  473.             } :
  474.             {
  475.                 className: 'cvat-tools-control',
  476.             };
  477.  
  478.         return !labels.length ? (
  479.             <Icon className='cvat-opencv-control cvat-disabled-canvas-control' component={OpenCVIcon} />
  480.         ) : (
  481.             <CustomPopover
  482.                 {...dynamcPopoverPros}
  483.                 placement='right'
  484.                 overlayClassName='cvat-opencv-control-popover'
  485.                 content={this.renderContent()}
  486.             >
  487.                 <Icon {...dynamicIconProps} component={OpenCVIcon} />
  488.             </CustomPopover>
  489.         );
  490.     }
  491. }
  492.  
  493. export default connect(mapStateToProps, mapDispatchToProps)(OpenCVControlComponent);
RAW Paste Data