diff --git a/.vscode/launch.json b/.vscode/launch.json index d6e8e878dec..658abd2fe48 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -42,8 +42,11 @@ "request": "launch", "stopOnEntry": false, "justMyCode": false, - "pythonPath": "${command:python.interpreterPath}", + "python": "${command:python.interpreterPath}", "program": "${workspaceRoot}/manage.py", + "env": { + "CVAT_SERVERLESS": "1", + }, "args": [ "runserver", "--noreload", @@ -73,7 +76,7 @@ "request": "launch", "stopOnEntry": false, "justMyCode": false, - "pythonPath": "${command:python.interpreterPath}", + "python": "${command:python.interpreterPath}", "program": "${workspaceRoot}/manage.py", "args": [ "rqworker", @@ -92,7 +95,7 @@ "request": "launch", "stopOnEntry": false, "justMyCode": false, - "pythonPath": "${command:python.interpreterPath}", + "python": "${command:python.interpreterPath}", "program": "${workspaceRoot}/manage.py", "args": [ "rqscheduler", @@ -108,7 +111,7 @@ "request": "launch", "justMyCode": false, "stopOnEntry": false, - "pythonPath":"${command:python.interpreterPath}", + "python":"${command:python.interpreterPath}", "program": "${workspaceRoot}/manage.py", "args": [ "rqworker", @@ -127,7 +130,7 @@ "request": "launch", "justMyCode": false, "stopOnEntry": false, - "pythonPath": "${command:python.interpreterPath}", + "python": "${command:python.interpreterPath}", "program": "${workspaceRoot}/manage.py", "args": [ "update_git_states" @@ -143,7 +146,7 @@ "request": "launch", "justMyCode": false, "stopOnEntry": false, - "pythonPath": "${command:python.interpreterPath}", + "python": "${command:python.interpreterPath}", "program": "${workspaceRoot}/manage.py", "args": [ "migrate" @@ -159,7 +162,7 @@ "request": "launch", "justMyCode": false, "stopOnEntry": false, - "pythonPath": "${command:python.interpreterPath}", + "python": "${command:python.interpreterPath}", "program": "${workspaceRoot}/manage.py", "args": [ "test", diff --git a/CHANGELOG.md b/CHANGELOG.md index b66384c05bd..c764acae68f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,11 +13,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - CVAT-3D: Load all frames corresponding to the job instance () - Intelligent scissors with OpenCV javascript () +- [Inside Outside Guidence](https://github.com/shiyinzhang/Inside-Outside-Guidance) serverless + function for interative segmentation ### Changed - Updated HTTPS install README section (cleanup and described more robust deploy) - Logstash is improved for using with configurable elasticsearch outputs () +- Bumped nuclio version to 1.5.16 +- All methods for interative segmentation accept negative points as well ### Deprecated diff --git a/README.md b/README.md index 07a3da833bb..7137a54bc2a 100644 --- a/README.md +++ b/README.md @@ -63,18 +63,26 @@ For more information about supported formats look at the | [ImageNet](http://www.image-net.org) | X | X | | [CamVid](http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/) | X | X | -## Deep learning models for automatic labeling +## Deep learning serverless functions for automatic labeling + + | Name | Type | Framework | CPU | GPU | | ------------------------------------------------------------------------------------------------------- | ---------- | ---------- | --- | --- | -| [Deep Extreme Cut](/serverless/openvino/dextr/nuclio) | interactor | OpenVINO | X | +| [Deep Extreme Cut](/serverless/openvino/dextr/nuclio) | interactor | OpenVINO | X | | +| [Faster RCNN](/serverless/openvino/omz/public/faster_rcnn_inception_v2_coco/nuclio) | detector | OpenVINO | X | | +| [Mask RCNN](/serverless/openvino/omz/public/mask_rcnn_inception_resnet_v2_atrous_coco/nuclio) | detector | OpenVINO | X | | +| [YOLO v3](/serverless/openvino/omz/public/yolo-v3-tf/nuclio) | detector | OpenVINO | X | | +| [Object reidentification](/serverless/openvino/omz/intel/person-reidentification-retail-300/nuclio) | reid | OpenVINO | X | | +| [Semantic segmentation for ADAS](/serverless/openvino/omz/intel/semantic-segmentation-adas-0001/nuclio) | detector | OpenVINO | X | | +| [Text detection v4](/serverless/openvino/omz/intel/text-detection-0004/nuclio) | detector | OpenVINO | X | | +| [SiamMask](/serverless/pytorch/foolwood/siammask/nuclio) | tracker | PyTorch | X | | +| [f-BRS](/serverless/pytorch/saic-vul/fbrs/nuclio) | interactor | PyTorch | X | | +| [Inside-Outside Guidance](/serverless/pytorch/shiyinzhang/iog/nuclio) | interactor | PyTorch | X | | | [Faster RCNN](/serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio) | detector | TensorFlow | X | X | -| [Mask RCNN](/serverless/openvino/omz/public/mask_rcnn_inception_resnet_v2_atrous_coco/nuclio) | detector | OpenVINO | X | -| [YOLO v3](/serverless/openvino/omz/public/yolo-v3-tf/nuclio) | detector | OpenVINO | X | -| [Text detection v4](/serverless/openvino/omz/intel/text-detection-0004/nuclio) | detector | OpenVINO | X | -| [Semantic segmentation for ADAS](/serverless/openvino/omz/intel/semantic-segmentation-adas-0001/nuclio) | detector | OpenVINO | X | -| [Mask RCNN](/serverless/tensorflow/matterport/mask_rcnn/nuclio) | detector | TensorFlow | X | -| [Object reidentification](/serverless/openvino/omz/intel/person-reidentification-retail-300/nuclio) | reid | OpenVINO | X | +| [Mask RCNN](/serverless/tensorflow/matterport/mask_rcnn/nuclio) | detector | TensorFlow | X | | + + ## Online demo: [cvat.org](https://cvat.org) @@ -93,11 +101,12 @@ Limitations: ## REST API -Automatically generated Swagger documentation for Django REST API is -available on `/api/swagger` -(default: `localhost:8080/api/swagger`). +Automatically generated Swagger documentation for Django REST API is available +on `/api/swagger`(default: `localhost:8080/api/swagger`). -Swagger documentation is visiable on allowed hostes, Update environement variable in docker-compose.yml file with cvat hosted machine IP or domain name. Example - `ALLOWED_HOSTS: 'localhost, 127.0.0.1'`) +Swagger documentation is visiable on allowed hostes, Update environement +variable in docker-compose.yml file with cvat hosted machine IP or domain +name. Example - `ALLOWED_HOSTS: 'localhost, 127.0.0.1'`. ## LICENSE @@ -129,4 +138,6 @@ Other ways to ask questions and get our support: ## Projects using CVAT -- [Onepanel](https://github.com/onepanelio/core) - Onepanel is an open source vision AI platform that fully integrates CVAT with scalable data processing and parallelized training pipelines. +- [Onepanel](https://github.com/onepanelio/core) - Onepanel is an open source + vision AI platform that fully integrates CVAT with scalable data processing + and parallelized training pipelines. diff --git a/components/serverless/docker-compose.serverless.yml b/components/serverless/docker-compose.serverless.yml index de94f6166b8..8938d5c5320 100644 --- a/components/serverless/docker-compose.serverless.yml +++ b/components/serverless/docker-compose.serverless.yml @@ -2,7 +2,7 @@ version: '3.3' services: serverless: container_name: nuclio - image: quay.io/nuclio/dashboard:1.5.8-amd64 + image: quay.io/nuclio/dashboard:1.5.16-amd64 restart: always networks: default: @@ -16,6 +16,7 @@ services: https_proxy: no_proxy: 172.28.0.1,${no_proxy} NUCLIO_CHECK_FUNCTION_CONTAINERS_HEALTHINESS: 'true' + NUCLIO_DASHBOARD_DEFAULT_FUNCTION_MOUNT_MODE: 'volume' ports: - '8070:8070' diff --git a/cvat-core/src/server-proxy.js b/cvat-core/src/server-proxy.js index f18f62f02b7..1fafc92ae75 100644 --- a/cvat-core/src/server-proxy.js +++ b/cvat-core/src/server-proxy.js @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2020 Intel Corporation +// Copyright (C) 2021 Intel Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-core/src/session.js b/cvat-core/src/session.js index f3c89da2099..31fc5fe19e7 100644 --- a/cvat-core/src/session.js +++ b/cvat-core/src/session.js @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2020 Intel Corporation +// Copyright (C) 2021 Intel Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-data/src/js/unzip_imgs.worker.js b/cvat-data/src/js/unzip_imgs.worker.js index 91b95dfde0c..3434e3b8aca 100644 --- a/cvat-data/src/js/unzip_imgs.worker.js +++ b/cvat-data/src/js/unzip_imgs.worker.js @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2021 Intel Corporation // // SPDX-License-Identifier: MIT @@ -7,7 +7,9 @@ const JSZip = require('jszip'); onmessage = (e) => { const zip = new JSZip(); if (e.data) { - const { start, end, block, dimension, dimension2D } = e.data; + const { + start, end, block, dimension, dimension2D, + } = e.data; zip.loadAsync(block).then((_zip) => { let index = start; diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index b0d2a4fee1a..ff9de4e803c 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.14.1", + "version": "1.14.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 02372b86c21..e78795fb9c2 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.14.1", + "version": "1.14.2", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index 4a949adaf0a..194edc1c06a 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -1362,10 +1362,7 @@ export function pasteShapeAsync(): ThunkAction { }; } -export function interactWithCanvas( - activeInteractor: Model | OpenCVTool, - activeLabelID: number, -): AnyAction { +export function interactWithCanvas(activeInteractor: Model | OpenCVTool, activeLabelID: number): AnyAction { return { type: AnnotationActionTypes.INTERACT_WITH_CANVAS, payload: { diff --git a/cvat-ui/src/components/annotation-page/annotation-page.tsx b/cvat-ui/src/components/annotation-page/annotation-page.tsx index 35c179339cf..feb6f7b6d72 100644 --- a/cvat-ui/src/components/annotation-page/annotation-page.tsx +++ b/cvat-ui/src/components/annotation-page/annotation-page.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2021 Intel Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx index 90b0e45befd..d490342712a 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx @@ -170,7 +170,7 @@ class OpenCVControlComponent extends React.PureComponent { try { result = await core.lambda.call(jobInstance.task, interactor, { frame, - points: convertShapesForInteractor((e as CustomEvent).detail.shapes), + pos_points: convertShapesForInteractor((e as CustomEvent).detail.shapes, 0), + neg_points: convertShapesForInteractor((e as CustomEvent).detail.shapes, 2), }); if (this.interactionIsAborted) { diff --git a/cvat-ui/src/components/annotation-page/top-bar/right-group.tsx b/cvat-ui/src/components/annotation-page/top-bar/right-group.tsx index 2e1b92afb62..dfd1a62cff8 100644 --- a/cvat-ui/src/components/annotation-page/top-bar/right-group.tsx +++ b/cvat-ui/src/components/annotation-page/top-bar/right-group.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2021 Intel Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx b/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx index 2e9212dee32..38f559edab2 100644 --- a/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx +++ b/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2021 Intel Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/components/labels-editor/common.ts b/cvat-ui/src/components/labels-editor/common.ts index f843218e2e7..d0c75f9f1e7 100644 --- a/cvat-ui/src/components/labels-editor/common.ts +++ b/cvat-ui/src/components/labels-editor/common.ts @@ -26,23 +26,23 @@ function validateParsedAttribute(attr: Attribute): void { if (!['number', 'undefined'].includes(typeof attr.id)) { throw new Error( - `Attribute: "${attr.name}". ` + `Type of attribute id must be a number or undefined. Got value ${attr.id}`, + `Attribute: "${attr.name}". Type of attribute id must be a number or undefined. Got value ${attr.id}`, ); } if (!['checkbox', 'number', 'text', 'radio', 'select'].includes((attr.input_type || '').toLowerCase())) { - throw new Error(`Attribute: "${attr.name}". ` + `Unknown input type: ${attr.input_type}`); + throw new Error(`Attribute: "${attr.name}". Unknown input type: ${attr.input_type}`); } if (typeof attr.mutable !== 'boolean') { throw new Error( - `Attribute: "${attr.name}". ` + `Mutable flag must be a boolean value. Got value ${attr.mutable}`, + `Attribute: "${attr.name}". Mutable flag must be a boolean value. Got value ${attr.mutable}`, ); } if (!Array.isArray(attr.values)) { throw new Error( - `Attribute: "${attr.name}". ` + `Attribute values must be an array. Got type ${typeof attr.values}`, + `Attribute: "${attr.name}". Attribute values must be an array. Got type ${typeof attr.values}`, ); } @@ -52,7 +52,7 @@ function validateParsedAttribute(attr: Attribute): void { for (const value of attr.values) { if (typeof value !== 'string') { - throw new Error(`Attribute: "${attr.name}". ` + `Each value must be a string. Got value ${value}`); + throw new Error(`Attribute: "${attr.name}". Each value must be a string. Got value ${value}`); } } } @@ -64,12 +64,12 @@ export function validateParsedLabel(label: Label): void { if (!['number', 'undefined'].includes(typeof label.id)) { throw new Error( - `Label "${label.name}". ` + `Type of label id must be only a number or undefined. Got value ${label.id}`, + `Label "${label.name}". Type of label id must be only a number or undefined. Got value ${label.id}`, ); } if (typeof label.color !== 'string') { - throw new Error(`Label "${label.name}". ` + `Label color must be a string. Got ${typeof label.color}`); + throw new Error(`Label "${label.name}". Label color must be a string. Got ${typeof label.color}`); } if (!label.color.match(/^#[0-9a-fA-F]{6}$|^$/)) { @@ -80,7 +80,7 @@ export function validateParsedLabel(label: Label): void { } if (!Array.isArray(label.attributes)) { - throw new Error(`Label "${label.name}". ` + `attributes must be an array. Got type ${typeof label.attributes}`); + throw new Error(`Label "${label.name}". Attributes must be an array. Got type ${typeof label.attributes}`); } for (const attr of label.attributes) { diff --git a/cvat-ui/src/containers/actions-menu/actions-menu.tsx b/cvat-ui/src/containers/actions-menu/actions-menu.tsx index dc84b47ac1a..acc6a45156e 100644 --- a/cvat-ui/src/containers/actions-menu/actions-menu.tsx +++ b/cvat-ui/src/containers/actions-menu/actions-menu.tsx @@ -9,7 +9,9 @@ import ActionsMenuComponent, { Actions } from 'components/actions-menu/actions-m import { CombinedState } from 'reducers/interfaces'; import { modelsActions } from 'actions/models-actions'; -import { dumpAnnotationsAsync, loadAnnotationsAsync, exportDatasetAsync, deleteTaskAsync } from 'actions/tasks-actions'; +import { + dumpAnnotationsAsync, loadAnnotationsAsync, exportDatasetAsync, deleteTaskAsync, +} from 'actions/tasks-actions'; // eslint-disable-next-line import/no-extraneous-dependencies import { MenuInfo } from 'rc-menu/lib/interface'; diff --git a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx index 86ad23d0a73..5019b143e92 100644 --- a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx +++ b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2021 Intel Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/cvat-canvas-wrapper.ts b/cvat-ui/src/cvat-canvas-wrapper.ts index 91fcc7ed3ae..3b0c1e21174 100644 --- a/cvat-ui/src/cvat-canvas-wrapper.ts +++ b/cvat-ui/src/cvat-canvas-wrapper.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2020-2021 Intel Corporation // // SPDX-License-Identifier: MIT @@ -12,20 +12,20 @@ import { InteractionResult as _InteractionResult, } from 'cvat-canvas/src/typescript/canvas'; -export function convertShapesForInteractor(shapes: InteractionResult[]): number[][] { +export function convertShapesForInteractor(shapes: InteractionResult[], button: number): number[][] { const reducer = (acc: number[][], _: number, index: number, array: number[]): number[][] => { - if (!(index % 2)) { // 0, 2, 4 - acc.push([ - array[index], - array[index + 1], - ]); + if (!(index % 2)) { + // 0, 2, 4 + acc.push([array[index], array[index + 1]]); } return acc; }; - return shapes.filter((shape: InteractionResult): boolean => shape.shapeType === 'points' && shape.button === 0) + return shapes + .filter((shape: InteractionResult): boolean => shape.shapeType === 'points' && shape.button === button) .map((shape: InteractionResult): number[] => shape.points) - .flat().reduce(reducer, []); + .flat() + .reduce(reducer, []); } export type InteractionData = _InteractionData; diff --git a/cvat-ui/src/reducers/annotation-reducer.ts b/cvat-ui/src/reducers/annotation-reducer.ts index 0f7d648b5c0..0db2fbaa349 100644 --- a/cvat-ui/src/reducers/annotation-reducer.ts +++ b/cvat-ui/src/reducers/annotation-reducer.ts @@ -1044,8 +1044,9 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { }, canvas: { ...state.canvas, - activeControl: activeInteractor - .type.startsWith('opencv') ? ActiveControl.OPENCV_TOOLS : ActiveControl.AI_TOOLS, + activeControl: activeInteractor.type.startsWith('opencv') ? + ActiveControl.OPENCV_TOOLS : + ActiveControl.AI_TOOLS, }, }; } diff --git a/cvat/apps/documentation/installation.md b/cvat/apps/documentation/installation.md index c852d773374..da7c9c199bd 100644 --- a/cvat/apps/documentation/installation.md +++ b/cvat/apps/documentation/installation.md @@ -284,7 +284,8 @@ Please see the [Docker documentation](https://docs.docker.com/network/proxy/) fo ```bash # Build and run containers with Analytics component support: -docker-compose -f docker-compose.yml -f components/analytics/docker-compose.analytics.yml up -d --build +docker-compose -f docker-compose.yml \ + -f components/analytics/docker-compose.analytics.yml up -d --build ``` ### Semi-automatic and automatic annotation diff --git a/cvat/apps/documentation/installation_automatic_annotation.md b/cvat/apps/documentation/installation_automatic_annotation.md index e3343211ffd..e68ce8b4812 100644 --- a/cvat/apps/documentation/installation_automatic_annotation.md +++ b/cvat/apps/documentation/installation_automatic_annotation.md @@ -1,13 +1,14 @@ - ### Semi-automatic and Automatic Annotation - > **⚠ WARNING: Do not use `docker-compose up`** -> If you did, make sure all containers are stopped by `docker-compose down`. +> If you did, make sure all containers are stopped by `docker-compose down`. + - To bring up cvat with auto annotation tool, from cvat root directory, you need to run: + ```bash docker-compose -f docker-compose.yml -f components/serverless/docker-compose.serverless.yml up -d ``` + If you did any changes to the docker-compose files, make sure to add `--build` at the end. To stop the containers, simply run: @@ -17,10 +18,11 @@ ``` - You have to install `nuctl` command line tool to build and deploy serverless - functions. Download [version 1.5.8](https://github.com/nuclio/nuclio/releases). + functions. Download [version 1.5.16](https://github.com/nuclio/nuclio/releases/tag/1.5.16). It is important that the version you download matches the version in [docker-compose.serverless.yml](/components/serverless/docker-compose.serverless.yml) After downloading the nuclio, give it a proper permission and do a softlink + ``` sudo chmod +x nuctl--linux-amd64 sudo ln -sf $(pwd)/nuctl--linux-amd64 /usr/local/bin/nuctl @@ -45,10 +47,13 @@ --volume `pwd`/serverless/openvino/common:/opt/nuclio/common \ --platform local ``` + **Note:** + - See [deploy_cpu.sh](/serverless/deploy_cpu.sh) for more examples. #### GPU Support + You will need to install Nvidia Container Toolkit and make sure your docker supports GPU. Follow [Nvidia docker instructions](https://www.tensorflow.org/install/docker#gpu_support). Also you will need to add `--resource-limit nvidia.com/gpu=1` to the nuclio deployment command. As an example, below will run on the GPU: @@ -63,9 +68,10 @@ ``` **Note:** - - Since the model is loaded during deployment, the number of GPU functions you can deploy will be limited to your GPU memory. - - See [deploy_gpu.sh](/serverless/deploy_gpu.sh) script for more examples. + - Since the model is loaded during deployment, the number of GPU functions you can deploy will be limited to your GPU memory. + + - See [deploy_gpu.sh](/serverless/deploy_gpu.sh) script for more examples. ####Debugging Nuclio Functions: @@ -76,6 +82,7 @@ ```bash docker logs ``` + e.g., ```bash @@ -83,9 +90,10 @@ ``` - If you would like to debug a code inside a container, you can use vscode to directly attach to a container [instructions](https://code.visualstudio.com/docs/remote/attach-container). To apply your changes, make sure to restart the container. + ```bash docker restart ``` > **⚠ WARNING:** - > Do not use nuclio dashboard to stop the container because with any modifications, it rebuilds the container and you will lose your changes. \ No newline at end of file + > Do not use nuclio dashboard to stop the container because with any modifications, it rebuilds the container and you will lose your changes. diff --git a/cvat/apps/lambda_manager/views.py b/cvat/apps/lambda_manager/views.py index bd24858128d..c7986394f6d 100644 --- a/cvat/apps/lambda_manager/views.py +++ b/cvat/apps/lambda_manager/views.py @@ -86,13 +86,14 @@ def __init__(self, gateway, data): # ID of the function (e.g. omz.public.yolo-v3) self.id = data['metadata']['name'] # type of the function (e.g. detector, interactor) - kind = data['metadata']['annotations'].get('type') + meta_anno = data['metadata']['annotations'] + kind = meta_anno.get('type') try: self.kind = LambdaType(kind) except ValueError: self.kind = LambdaType.UNKNOWN # dictionary of labels for the function (e.g. car, person) - spec = json.loads(data['metadata']['annotations'].get('spec') or '[]') + spec = json.loads(meta_anno.get('spec') or '[]') labels = [item['name'] for item in spec] if len(labels) != len(set(labels)): raise ValidationError( @@ -106,10 +107,11 @@ def __init__(self, gateway, data): # http port to access the serverless function self.port = data["status"].get("httpPort") # framework which is used for the function (e.g. tensorflow, openvino) - self.framework = data['metadata']['annotations'].get('framework') + self.framework = meta_anno.get('framework') # display name for the function - self.name = data['metadata']['annotations'].get('name', self.id) - self.min_pos_points = int(data['metadata']['annotations'].get('min_pos_points', 1)) + self.name = meta_anno.get('name', self.id) + self.min_pos_points = int(meta_anno.get('min_pos_points', 1)) + self.startswith_box = bool(meta_anno.get('startswith_box', False)) self.gateway = gateway def to_dict(self): @@ -117,13 +119,22 @@ def to_dict(self): 'id': self.id, 'kind': str(self.kind), 'labels': self.labels, - 'state': self.state, 'description': self.description, 'framework': self.framework, - 'name': self.name, - 'min_pos_points': self.min_pos_points + 'name': self.name } + if self.kind is LambdaType.INTERACTOR: + response.update({ + 'min_pos_points': self.min_pos_points, + 'startswith_box': self.startswith_box + }) + + if self.kind is LambdaType.TRACKER: + response.update({ + 'state': self.state + }) + return response def invoke(self, db_task, data): @@ -155,7 +166,8 @@ def invoke(self, db_task, data): elif self.kind == LambdaType.INTERACTOR: payload.update({ "image": self._get_image(db_task, data["frame"], quality), - "points": data["points"], + "pos_points": data["pos_points"], + "neg_points": data["neg_points"] }) elif self.kind == LambdaType.REID: payload.update({ diff --git a/serverless/openvino/common/model_loader.py b/serverless/common/openvino/model_loader.py similarity index 100% rename from serverless/openvino/common/model_loader.py rename to serverless/common/openvino/model_loader.py diff --git a/serverless/openvino/common/python3 b/serverless/common/openvino/python3 similarity index 62% rename from serverless/openvino/common/python3 rename to serverless/common/openvino/python3 index fca7518d409..dfd05669c2b 100755 --- a/serverless/openvino/common/python3 +++ b/serverless/common/openvino/python3 @@ -3,5 +3,5 @@ args=$@ . /opt/intel/openvino/bin/setupvars.sh -PYTHONPATH=/opt/nuclio/common:$PYTHONPATH +PYTHONPATH=/opt/nuclio/common/openvino:$PYTHONPATH /usr/bin/python3 $args diff --git a/serverless/deploy_cpu.sh b/serverless/deploy_cpu.sh index a86149fef7a..531a21269f6 100755 --- a/serverless/deploy_cpu.sh +++ b/serverless/deploy_cpu.sh @@ -2,57 +2,19 @@ # Sample commands to deploy nuclio functions on CPU SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +FUNCTIONS_DIR=${1:-$SCRIPT_DIR} nuctl create project cvat -nuctl deploy --project-name cvat \ - --path "$SCRIPT_DIR/openvino/omz/public/faster_rcnn_inception_v2_coco/nuclio" \ - --volume "$SCRIPT_DIR/openvino/common:/opt/nuclio/common" \ - --platform local -nuctl deploy --project-name cvat \ - --path "$SCRIPT_DIR/openvino/omz/public/mask_rcnn_inception_resnet_v2_atrous_coco/nuclio" \ - --volume "$SCRIPT_DIR/openvino/common:/opt/nuclio/common" \ - --platform local +for func_config in $(find "$FUNCTIONS_DIR" -name "function.yaml") +do + func_root=$(dirname "$func_config") + echo Deploying $(dirname "$func_root") function... + nuctl deploy --project-name cvat --path "$func_root" \ + --volume "$SCRIPT_DIR/common:/opt/nuclio/common" \ + --platform local +done -nuctl deploy --project-name cvat \ - --path "$SCRIPT_DIR/openvino/omz/public/yolo-v3-tf/nuclio" \ - --volume "$SCRIPT_DIR/openvino/common:/opt/nuclio/common" \ - --platform local - -nuctl deploy --project-name cvat \ - --path "$SCRIPT_DIR/openvino/omz/intel/text-detection-0004/nuclio" \ - --volume "$SCRIPT_DIR/openvino/common:/opt/nuclio/common" \ - --platform local - -nuctl deploy --project-name cvat \ - --path "$SCRIPT_DIR/openvino/omz/intel/semantic-segmentation-adas-0001/nuclio" \ - --volume "$SCRIPT_DIR/openvino/common:/opt/nuclio/common" \ - --platform local - -nuctl deploy --project-name cvat \ - --path "$SCRIPT_DIR/openvino/omz/intel/person-reidentification-retail-300/nuclio" \ - --volume "$SCRIPT_DIR/openvino/common:/opt/nuclio/common" \ - --platform local - -nuctl deploy --project-name cvat \ - --path "$SCRIPT_DIR/openvino/dextr/nuclio" \ - --volume "$SCRIPT_DIR/openvino/common:/opt/nuclio/common" \ - --platform local - -nuctl deploy --project-name cvat \ - --path "$SCRIPT_DIR/tensorflow/matterport/mask_rcnn/nuclio" \ - --platform local - -nuctl deploy --project-name cvat \ - --path "$SCRIPT_DIR/tensorflow/faster_rcnn_inception_v2_coco/nuclio" \ - --platform local - -nuctl deploy --project-name cvat \ - --path "$SCRIPT_DIR/pytorch/foolwood/siammask/nuclio" \ - --platform local +nuctl get function -nuctl deploy --project-name cvat \ - --path "$SCRIPT_DIR/pytorch/saic-vul/fbrs/nuclio" \ - --platform local -nuctl get function diff --git a/serverless/openvino/dextr/nuclio/function.yaml b/serverless/openvino/dextr/nuclio/function.yaml index 6faa753ef91..825599428f6 100644 --- a/serverless/openvino/dextr/nuclio/function.yaml +++ b/serverless/openvino/dextr/nuclio/function.yaml @@ -15,7 +15,7 @@ spec: eventTimeout: 30s env: - name: NUCLIO_PYTHON_EXE_PATH - value: /opt/nuclio/common/python3 + value: /opt/nuclio/common/openvino/python3 build: image: cvat/openvino.dextr @@ -51,3 +51,4 @@ spec: restartPolicy: name: always maximumRetryCount: 3 + mountMode: volume diff --git a/serverless/openvino/dextr/nuclio/main.py b/serverless/openvino/dextr/nuclio/main.py index 73617002c69..10f47026bb2 100644 --- a/serverless/openvino/dextr/nuclio/main.py +++ b/serverless/openvino/dextr/nuclio/main.py @@ -15,7 +15,7 @@ def init_context(context): def handler(context, event): context.logger.info("call handler") data = event.body - points = data["points"] + points = data["pos_points"] buf = io.BytesIO(base64.b64decode(data["image"].encode('utf-8'))) image = Image.open(buf) diff --git a/serverless/openvino/omz/intel/person-reidentification-retail-300/nuclio/function.yaml b/serverless/openvino/omz/intel/person-reidentification-retail-300/nuclio/function.yaml index ab8845e42ee..ffa81ae8111 100644 --- a/serverless/openvino/omz/intel/person-reidentification-retail-300/nuclio/function.yaml +++ b/serverless/openvino/omz/intel/person-reidentification-retail-300/nuclio/function.yaml @@ -14,7 +14,7 @@ spec: eventTimeout: 30s env: - name: NUCLIO_PYTHON_EXE_PATH - value: /opt/nuclio/common/python3 + value: /opt/nuclio/common/openvino/python3 build: image: cvat/openvino.omz.intel.person-reidentification-retail-0300 @@ -46,3 +46,4 @@ spec: restartPolicy: name: always maximumRetryCount: 3 + mountMode: volume diff --git a/serverless/openvino/omz/intel/semantic-segmentation-adas-0001/nuclio/function.yaml b/serverless/openvino/omz/intel/semantic-segmentation-adas-0001/nuclio/function.yaml index b871a01cf8c..20e4941e26e 100644 --- a/serverless/openvino/omz/intel/semantic-segmentation-adas-0001/nuclio/function.yaml +++ b/serverless/openvino/omz/intel/semantic-segmentation-adas-0001/nuclio/function.yaml @@ -37,7 +37,7 @@ spec: eventTimeout: 30s env: - name: NUCLIO_PYTHON_EXE_PATH - value: /opt/nuclio/common/python3 + value: /opt/nuclio/common/openvino/python3 build: image: cvat/openvino.omz.intel.semantic-segmentation-adas-0001 @@ -73,3 +73,4 @@ spec: restartPolicy: name: always maximumRetryCount: 3 + mountMode: volume diff --git a/serverless/openvino/omz/intel/text-detection-0004/nuclio/function.yaml b/serverless/openvino/omz/intel/text-detection-0004/nuclio/function.yaml index 7625933d862..eff14c11414 100644 --- a/serverless/openvino/omz/intel/text-detection-0004/nuclio/function.yaml +++ b/serverless/openvino/omz/intel/text-detection-0004/nuclio/function.yaml @@ -17,7 +17,7 @@ spec: eventTimeout: 30s env: - name: NUCLIO_PYTHON_EXE_PATH - value: /opt/nuclio/common/python3 + value: /opt/nuclio/common/openvino/python3 build: image: cvat/openvino.omz.intel.text-detection-0004 @@ -47,3 +47,4 @@ spec: restartPolicy: name: always maximumRetryCount: 3 + mountMode: volume diff --git a/serverless/openvino/omz/public/faster_rcnn_inception_v2_coco/nuclio/function.yaml b/serverless/openvino/omz/public/faster_rcnn_inception_v2_coco/nuclio/function.yaml index 47c3f2fe9e7..556154e0688 100644 --- a/serverless/openvino/omz/public/faster_rcnn_inception_v2_coco/nuclio/function.yaml +++ b/serverless/openvino/omz/public/faster_rcnn_inception_v2_coco/nuclio/function.yaml @@ -96,7 +96,7 @@ spec: eventTimeout: 30s env: - name: NUCLIO_PYTHON_EXE_PATH - value: /opt/nuclio/common/python3 + value: /opt/nuclio/common/openvino/python3 build: image: cvat/openvino.omz.public.faster_rcnn_inception_v2_coco @@ -128,3 +128,4 @@ spec: restartPolicy: name: always maximumRetryCount: 3 + mountMode: volume diff --git a/serverless/openvino/omz/public/mask_rcnn_inception_resnet_v2_atrous_coco/nuclio/function.yaml b/serverless/openvino/omz/public/mask_rcnn_inception_resnet_v2_atrous_coco/nuclio/function.yaml index 88a1a817f70..8e7854e9f97 100644 --- a/serverless/openvino/omz/public/mask_rcnn_inception_resnet_v2_atrous_coco/nuclio/function.yaml +++ b/serverless/openvino/omz/public/mask_rcnn_inception_resnet_v2_atrous_coco/nuclio/function.yaml @@ -99,7 +99,7 @@ spec: eventTimeout: 60s env: - name: NUCLIO_PYTHON_EXE_PATH - value: /opt/nuclio/common/python3 + value: /opt/nuclio/common/openvino/python3 build: image: cvat/openvino.omz.public.mask_rcnn_inception_resnet_v2_atrous_coco @@ -137,3 +137,4 @@ spec: restartPolicy: name: always maximumRetryCount: 3 + mountMode: volume diff --git a/serverless/openvino/omz/public/yolo-v3-tf/nuclio/function.yaml b/serverless/openvino/omz/public/yolo-v3-tf/nuclio/function.yaml index 793f26164a8..5e3966f1421 100644 --- a/serverless/openvino/omz/public/yolo-v3-tf/nuclio/function.yaml +++ b/serverless/openvino/omz/public/yolo-v3-tf/nuclio/function.yaml @@ -96,7 +96,7 @@ spec: eventTimeout: 30s env: - name: NUCLIO_PYTHON_EXE_PATH - value: /opt/nuclio/common/python3 + value: /opt/nuclio/common/openvino/python3 build: image: cvat/openvino.omz.public.yolo-v3-tf @@ -128,3 +128,4 @@ spec: restartPolicy: name: always maximumRetryCount: 3 + mountMode: volume diff --git a/serverless/pytorch/foolwood/siammask/nuclio/function.yaml b/serverless/pytorch/foolwood/siammask/nuclio/function.yaml index a19fdb52bb3..5b078127c2b 100644 --- a/serverless/pytorch/foolwood/siammask/nuclio/function.yaml +++ b/serverless/pytorch/foolwood/siammask/nuclio/function.yaml @@ -54,3 +54,4 @@ spec: restartPolicy: name: always maximumRetryCount: 3 + mountMode: volume diff --git a/serverless/pytorch/saic-vul/fbrs/nuclio/function.yaml b/serverless/pytorch/saic-vul/fbrs/nuclio/function.yaml index 4d36482a0f2..50a25753d7c 100644 --- a/serverless/pytorch/saic-vul/fbrs/nuclio/function.yaml +++ b/serverless/pytorch/saic-vul/fbrs/nuclio/function.yaml @@ -57,3 +57,4 @@ spec: restartPolicy: name: always maximumRetryCount: 3 + mountMode: volume diff --git a/serverless/pytorch/saic-vul/fbrs/nuclio/main.py b/serverless/pytorch/saic-vul/fbrs/nuclio/main.py index 300bc294c6a..ad1f8eddf88 100644 --- a/serverless/pytorch/saic-vul/fbrs/nuclio/main.py +++ b/serverless/pytorch/saic-vul/fbrs/nuclio/main.py @@ -19,8 +19,8 @@ def init_context(context): def handler(context, event): context.logger.info("call handler") data = event.body - pos_points = data["points"] - neg_points = [] + pos_points = data["pos_points"] + neg_points = data["neg_points"] threshold = data.get("threshold", 0.5) buf = io.BytesIO(base64.b64decode(data["image"].encode('utf-8'))) image = Image.open(buf) diff --git a/serverless/pytorch/shiyinzhang/iog/nuclio/function.yaml b/serverless/pytorch/shiyinzhang/iog/nuclio/function.yaml new file mode 100644 index 00000000000..f84f543ced1 --- /dev/null +++ b/serverless/pytorch/shiyinzhang/iog/nuclio/function.yaml @@ -0,0 +1,71 @@ +metadata: + name: pth.shiyinzhang.iog + namespace: cvat + annotations: + name: IOG + type: interactor + spec: + framework: pytorch + min_pos_points: 1 + startswith_box: true + +spec: + description: Interactive Object Segmentation with Inside-Outside Guidance + runtime: 'python:3.6' + handler: main:handler + eventTimeout: 30s + env: + - name: PYTHONPATH + value: /opt/nuclio/iog + + build: + image: cvat/pth.shiyinzhang.iog + baseImage: continuumio/miniconda3 + + directives: + preCopy: + - kind: WORKDIR + value: /opt/nuclio + - kind: RUN + value: conda create -y -n iog python=3.6 + - kind: SHELL + value: '["conda", "run", "-n", "iog", "/bin/bash", "-c"]' + - kind: RUN + value: conda install -y -c anaconda curl + - kind: RUN + value: conda install -y pytorch=0.4 torchvision=0.2 -c pytorch + - kind: RUN + value: conda install -y -c conda-forge pycocotools opencv scipy + - kind: RUN + value: git clone https://github.com/shiyinzhang/Inside-Outside-Guidance.git iog + - kind: WORKDIR + value: /opt/nuclio/iog + - kind: ENV + value: fileid=1Lm1hhMhhjjnNwO4Pf7SC6tXLayH2iH0l + - kind: ENV + value: filename=IOG_PASCAL_SBD.pth + - kind: RUN + value: curl -c ./cookie -s -L "https://drive.google.com/uc?export=download&id=${fileid}" + - kind: RUN + value: echo "/download/ {print \$NF}" > confirm_code.awk + - kind: RUN + value: curl -Lb ./cookie "https://drive.google.com/uc?export=download&confirm=`awk -f confirm_code.awk ./cookie`&id=${fileid}" -o ${filename} + - kind: WORKDIR + value: /opt/nuclio + - kind: ENTRYPOINT + value: '["conda", "run", "-n", "iog"]' + + triggers: + myHttpTrigger: + maxWorkers: 2 + kind: 'http' + workerAvailabilityTimeoutMilliseconds: 10000 + attributes: + maxRequestBodySize: 33554432 # 32MB + + platform: + attributes: + restartPolicy: + name: always + maximumRetryCount: 3 + mountMode: volume diff --git a/serverless/pytorch/shiyinzhang/iog/nuclio/main.py b/serverless/pytorch/shiyinzhang/iog/nuclio/main.py new file mode 100644 index 00000000000..16c4d732dbe --- /dev/null +++ b/serverless/pytorch/shiyinzhang/iog/nuclio/main.py @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Intel Corporation +# +# SPDX-License-Identifier: MIT + +import json +import base64 +from PIL import Image +import io +import numpy as np +from model_handler import ModelHandler + +def init_context(context): + context.logger.info("Init context... 0%") + + model = ModelHandler() + setattr(context.user_data, 'model', model) + + context.logger.info("Init context...100%") + +def handler(context, event): + context.logger.info("call handler") + data = event.body + pos_points = data["pos_points"] + neg_points = data["neg_points"] + obj_bbox = data.get("obj_bbox", None) + threshold = data.get("threshold", 0.8) + buf = io.BytesIO(base64.b64decode(data["image"].encode('utf-8'))) + image = Image.open(buf) + + if obj_bbox is None: + x, y = np.split(np.transpose(np.array(neg_points)), 2) + obj_bbox = [np.min(x), np.min(y), np.max(x), np.max(y)] + neg_points = [] + + polygon = context.user_data.model.handle(image, obj_bbox, + pos_points, neg_points, threshold) + return context.Response(body=json.dumps(polygon), + headers={}, + content_type='application/json', + status_code=200) diff --git a/serverless/pytorch/shiyinzhang/iog/nuclio/model_handler.py b/serverless/pytorch/shiyinzhang/iog/nuclio/model_handler.py new file mode 100644 index 00000000000..5d972915d35 --- /dev/null +++ b/serverless/pytorch/shiyinzhang/iog/nuclio/model_handler.py @@ -0,0 +1,123 @@ +# Copyright (C) 2020 Intel Corporation +# +# SPDX-License-Identifier: MIT + +import numpy as np +import os +import cv2 +import torch +from networks.mainnetwork import Network +from dataloaders import helpers + +def convert_mask_to_polygon(mask): + mask = np.array(mask, dtype=np.uint8) + cv2.normalize(mask, mask, 0, 255, cv2.NORM_MINMAX) + contours = None + if int(cv2.__version__.split('.')[0]) > 3: + contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_KCOS)[0] + else: + contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_KCOS)[1] + + contours = max(contours, key=lambda arr: arr.size) + if contours.shape.count(1): + contours = np.squeeze(contours) + if contours.size < 3 * 2: + raise Exception('Less then three point have been detected. Can not build a polygon.') + + polygon = [] + for point in contours: + polygon.append([int(point[0]), int(point[1])]) + + return polygon + +class ModelHandler: + def __init__(self): + base_dir = os.environ.get("MODEL_PATH", "/opt/nuclio/iog") + model_path = os.path.join(base_dir, "IOG_PASCAL_SBD.pth") + self.device = torch.device("cpu") + + # Number of input channels (RGB + heatmap of IOG points) + self.net = Network(nInputChannels=5, num_classes=1, backbone='resnet101', + output_stride=16, sync_bn=None, freeze_bn=False) + + pretrain_dict = torch.load(model_path) + self.net.load_state_dict(pretrain_dict) + self.net.to(self.device) + self.net.eval() + + def handle(self, image, bbox, pos_points, neg_points, threshold): + with torch.no_grad(): + # extract a crop with padding from the image + crop_padding = 30 + crop_bbox = [ + max(bbox[0] - crop_padding, 0), + max(bbox[1] - crop_padding, 0), + min(bbox[2] + crop_padding, image.width - 1), + min(bbox[3] + crop_padding, image.height - 1) + ] + crop_shape = ( + int(crop_bbox[2] - crop_bbox[0] + 1), # width + int(crop_bbox[3] - crop_bbox[1] + 1), # height + ) + + # try to use crop_from_bbox(img, bbox, zero_pad) here + input_crop = np.array(image.crop(crop_bbox)).astype(np.float32) + + # resize the crop + input_crop = cv2.resize(input_crop, (512, 512), interpolation=cv2.INTER_NEAREST) + crop_scale = (512 / crop_shape[0], 512 / crop_shape[1]) + + def translate_points_to_crop(points): + points = [ + ((p[0] - crop_bbox[0]) * crop_scale[0], # x + (p[1] - crop_bbox[1]) * crop_scale[1]) # y + for p in points] + + return points + + pos_points = translate_points_to_crop(pos_points) + neg_points = translate_points_to_crop(neg_points) + + # Create IOG image + pos_gt = np.zeros(shape=input_crop.shape[:2], dtype=np.float64) + neg_gt = np.zeros(shape=input_crop.shape[:2], dtype=np.float64) + for p in pos_points: + pos_gt = np.maximum(pos_gt, helpers.make_gaussian(pos_gt.shape, center=p)) + for p in neg_points: + neg_gt = np.maximum(neg_gt, helpers.make_gaussian(neg_gt.shape, center=p)) + iog_image = np.stack((pos_gt, neg_gt), axis=2).astype(dtype=input_crop.dtype) + + # Convert iog_image to an image (0-255 values) + cv2.normalize(iog_image, iog_image, 0, 255, cv2.NORM_MINMAX) + + # Concatenate input crop and IOG image + input_blob = np.concatenate((input_crop, iog_image), axis=2) + + # numpy image: H x W x C + # torch image: C X H X W + input_blob = input_blob.transpose((2, 0, 1)) + # batch size is 1 + input_blob = np.array([input_blob]) + input_tensor = torch.from_numpy(input_blob) + + input_tensor = input_tensor.to(self.device) + output_mask = self.net.forward(input_tensor)[4] + output_mask = output_mask.to(self.device) + pred = np.transpose(output_mask.data.numpy()[0, :, :, :], (1, 2, 0)) + pred = pred > threshold + pred = np.squeeze(pred) + + # Convert a mask to a polygon + polygon = convert_mask_to_polygon(pred) + def translate_points_to_image(points): + points = [ + (p[0] / crop_scale[0] + crop_bbox[0], # x + p[1] / crop_scale[1] + crop_bbox[1]) # y + for p in points] + + return points + + polygon = translate_points_to_image(polygon) + + return polygon + diff --git a/serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio/function.yaml b/serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio/function.yaml index 4e2cf02f6e2..4e6da8ebe59 100644 --- a/serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio/function.yaml +++ b/serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio/function.yaml @@ -129,3 +129,4 @@ spec: restartPolicy: name: always maximumRetryCount: 3 + mountMode: volume diff --git a/serverless/tensorflow/matterport/mask_rcnn/nuclio/function.yaml b/serverless/tensorflow/matterport/mask_rcnn/nuclio/function.yaml index ef0bf20cef4..1d3ee39b4e5 100644 --- a/serverless/tensorflow/matterport/mask_rcnn/nuclio/function.yaml +++ b/serverless/tensorflow/matterport/mask_rcnn/nuclio/function.yaml @@ -108,15 +108,16 @@ spec: - kind: WORKDIR value: /opt/nuclio - kind: RUN - value: apt update && apt install --no-install-recommends -y git curl libsm6 libxext6 libxrender-dev + value: apt update && apt install --no-install-recommends -y git curl libsm6 libxext6 libgl1-mesa-glx - kind: RUN value: git clone https://github.com/matterport/Mask_RCNN.git - kind: RUN value: curl -L https://github.com/matterport/Mask_RCNN/releases/download/v2.0/mask_rcnn_coco.h5 -o Mask_RCNN/mask_rcnn_coco.h5 - kind: RUN - value: pip3 install -r Mask_RCNN/requirements.txt + value: pip3 install scipy cython matplotlib scikit-image opencv-python-headless h5py \ + imgaug IPython[all] tensorflow==1.13.1 keras==2.1.0 pillow pyyaml - kind: RUN - value: pip3 install pycocotools tensorflow==1.13.1 keras==2.1.0 pillow pyyaml + value: pip3 install pycocotools triggers: myHttpTrigger: @@ -131,3 +132,4 @@ spec: restartPolicy: name: always maximumRetryCount: 3 + mountMode: volume