IOG serverless function + some fixes (#2578)

* Initial version of Inside Outside Guidance serverless function
* Support neg_points in interactors
* Improved deployment process of serverless functions
* Improve installation.md for serverless functions.
* Update CHANGELOG, use NUCLIO_DASHBOARD_DEFAULT_FUNCTION_MOUNT_MODE as
recommended by nuclio developers.
* Disable warning from markdown linter about max line length for a table.
* Fix IOG function with conda environment
* Fix tensorflow matterport/mask_rcnn

Co-authored-by: Boris Sekachev <boris.sekachev@intel.com>
main
Nikita Manovich 5 years ago committed by GitHub
parent 51c3dd8fe3
commit 97cb892844
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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",

@ -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
(<https://github.com/openvinotoolkit/cvat/pull/2645>)
- Intelligent scissors with OpenCV javascript (<https://github.com/openvinotoolkit/cvat/pull/2689>)
- [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 (<https://github.com/openvinotoolkit/cvat/pull/2531>)
- Bumped nuclio version to 1.5.16
- All methods for interative segmentation accept negative points as well
### Deprecated

@ -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
<!--lint disable maximum-line-length-->
| 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 | |
<!--lint enable maximum-line-length-->
## 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 `<cvat_origin>/api/swagger`
(default: `localhost:8080/api/swagger`).
Automatically generated Swagger documentation for Django REST API is available
on `<cvat_origin>/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.

@ -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'

@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

@ -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;

@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.14.1",
"version": "1.14.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

@ -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": {

@ -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: {

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

@ -170,7 +170,7 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
const {
shapesUpdated, isDone, threshold, shapes,
} = (e as CustomEvent).detail;
const pressedPoints = convertShapesForInteractor(shapes).flat();
const pressedPoints = convertShapesForInteractor(shapes, 0).flat();
this.interactionIsDone = isDone;
try {

@ -220,7 +220,8 @@ export class ToolsControlComponent extends React.PureComponent<Props, State> {
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) {

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

@ -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) {

@ -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';

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

@ -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;

@ -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,
},
};
}

@ -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

@ -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-<version>-linux-amd64
sudo ln -sf $(pwd)/nuctl-<version>-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 <name of your container>
```
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 <name_of_the_container>
```
> **⚠ WARNING:**
> Do not use nuclio dashboard to stop the container because with any modifications, it rebuilds the container and you will lose your changes.
> Do not use nuclio dashboard to stop the container because with any modifications, it rebuilds the container and you will lose your changes.

@ -86,13 +86,14 @@ class LambdaFunction:
# 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 @@ class LambdaFunction:
# 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 @@ class LambdaFunction:
'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 @@ class LambdaFunction:
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({

@ -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

@ -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

@ -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

@ -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)

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -54,3 +54,4 @@ spec:
restartPolicy:
name: always
maximumRetryCount: 3
mountMode: volume

@ -57,3 +57,4 @@ spec:
restartPolicy:
name: always
maximumRetryCount: 3
mountMode: volume

@ -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)

@ -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

@ -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)

@ -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

@ -129,3 +129,4 @@ spec:
restartPolicy:
name: always
maximumRetryCount: 3
mountMode: volume

@ -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

Loading…
Cancel
Save