Merge branch 'develop' of https://github.com/opencv/cvat into upstream/develop
commit
3236546907
File diff suppressed because one or more lines are too long
@ -1,38 +0,0 @@
|
|||||||
## [Keras+Tensorflow Mask R-CNN Segmentation](https://github.com/matterport/Mask_RCNN)
|
|
||||||
|
|
||||||
### What is it?
|
|
||||||
- This application allows you automatically to segment many various objects on images.
|
|
||||||
- It's based on Feature Pyramid Network (FPN) and a ResNet101 backbone.
|
|
||||||
|
|
||||||
- It uses a pre-trained model on MS COCO dataset
|
|
||||||
- It supports next classes (use them in "labels" row):
|
|
||||||
```python
|
|
||||||
'BG', 'person', 'bicycle', 'car', 'motorcycle', 'airplane',
|
|
||||||
'bus', 'train', 'truck', 'boat', 'traffic light',
|
|
||||||
'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird',
|
|
||||||
'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear',
|
|
||||||
'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie',
|
|
||||||
'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball',
|
|
||||||
'kite', 'baseball bat', 'baseball glove', 'skateboard',
|
|
||||||
'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup',
|
|
||||||
'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
|
|
||||||
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza',
|
|
||||||
'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed',
|
|
||||||
'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote',
|
|
||||||
'keyboard', 'cell phone', 'microwave', 'oven', 'toaster',
|
|
||||||
'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors',
|
|
||||||
'teddy bear', 'hair drier', 'toothbrush'.
|
|
||||||
```
|
|
||||||
- Component adds "Run Auto Segmentation" button into dashboard.
|
|
||||||
|
|
||||||
### Build docker image
|
|
||||||
```bash
|
|
||||||
# From project root directory
|
|
||||||
docker-compose -f docker-compose.yml -f components/auto_segmentation/docker-compose.auto_segmentation.yml build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run docker container
|
|
||||||
```bash
|
|
||||||
# From project root directory
|
|
||||||
docker-compose -f docker-compose.yml -f components/auto_segmentation/docker-compose.auto_segmentation.yml up -d
|
|
||||||
```
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2018 Intel Corporation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
#
|
|
||||||
version: "2.3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
cvat:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
args:
|
|
||||||
AUTO_SEGMENTATION: "yes"
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
MASK_RCNN_URL=https://github.com/matterport/Mask_RCNN
|
|
||||||
|
|
||||||
cd ${HOME} && \
|
|
||||||
git clone ${MASK_RCNN_URL}.git && \
|
|
||||||
curl -L ${MASK_RCNN_URL}/releases/download/v2.0/mask_rcnn_coco.h5 -o Mask_RCNN/mask_rcnn_coco.h5
|
|
||||||
|
|
||||||
# TODO remove useless files
|
|
||||||
# tensorflow and Keras are installed globally
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
## [NVIDIA CUDA Toolkit](https://developer.nvidia.com/cuda-toolkit)
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
* NVIDIA GPU with a compute capability [3.0 - 7.2]
|
|
||||||
* Latest GPU driver
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
#### Install the latest driver for your graphics card
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo add-apt-repository ppa:graphics-drivers/ppa
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-cache search nvidia-* # find latest nvidia driver
|
|
||||||
sudo apt-get --no-install-recommends install nvidia-* # install the nvidia driver
|
|
||||||
sudo apt-get --no-install-recommends install mesa-common-dev
|
|
||||||
sudo apt-get --no-install-recommends install freeglut3-dev
|
|
||||||
sudo apt-get --no-install-recommends install nvidia-modprobe
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Reboot your PC and verify installation by `nvidia-smi` command.
|
|
||||||
|
|
||||||
#### Install [Nvidia-Docker](https://github.com/NVIDIA/nvidia-docker)
|
|
||||||
|
|
||||||
Please be sure that installation was successful.
|
|
||||||
```bash
|
|
||||||
docker info | grep 'Runtimes' # output should contains 'nvidia'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build docker image
|
|
||||||
```bash
|
|
||||||
# From project root directory
|
|
||||||
docker-compose -f docker-compose.yml -f components/cuda/docker-compose.cuda.yml build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run docker container
|
|
||||||
```bash
|
|
||||||
# From project root directory
|
|
||||||
docker-compose -f docker-compose.yml -f components/cuda/docker-compose.cuda.yml up -d
|
|
||||||
```
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2018 Intel Corporation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
#
|
|
||||||
version: "2.3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
cvat:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
args:
|
|
||||||
CUDA_SUPPORT: "yes"
|
|
||||||
runtime: "nvidia"
|
|
||||||
environment:
|
|
||||||
NVIDIA_VISIBLE_DEVICES: all
|
|
||||||
NVIDIA_DRIVER_CAPABILITIES: compute,utility
|
|
||||||
# That environment variable is used by the Nvidia Container Runtime.
|
|
||||||
# The Nvidia Container Runtime parses this as:
|
|
||||||
# :space:: logical OR
|
|
||||||
# ,: Logical AND
|
|
||||||
# https://gitlab.com/nvidia/container-images/cuda/issues/31#note_149432780
|
|
||||||
NVIDIA_REQUIRE_CUDA: "cuda>=10.0 brand=tesla,driver>=384,driver<385 brand=tesla,driver>=410,driver<411"
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Copyright (C) 2018 Intel Corporation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
#
|
|
||||||
set -e
|
|
||||||
|
|
||||||
NVIDIA_GPGKEY_SUM=d1be581509378368edeec8c1eb2958702feedf3bc3d17011adbf24efacce4ab5 && \
|
|
||||||
NVIDIA_GPGKEY_FPR=ae09fe4bbd223a84b2ccfce3f60f4b3d7fa2af80 && \
|
|
||||||
apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/7fa2af80.pub && \
|
|
||||||
apt-key adv --export --no-emit-version -a $NVIDIA_GPGKEY_FPR | tail -n +5 > cudasign.pub && \
|
|
||||||
echo "$NVIDIA_GPGKEY_SUM cudasign.pub" | sha256sum -c --strict - && rm cudasign.pub && \
|
|
||||||
echo "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64 /" > /etc/apt/sources.list.d/cuda.list && \
|
|
||||||
echo "deb http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1604/x86_64 /" > /etc/apt/sources.list.d/nvidia-ml.list
|
|
||||||
|
|
||||||
CUDA_VERSION=10.0.130
|
|
||||||
NCCL_VERSION=2.5.6
|
|
||||||
CUDNN_VERSION=7.6.5.32
|
|
||||||
CUDA_PKG_VERSION="10-0=$CUDA_VERSION-1"
|
|
||||||
echo 'export PATH=/usr/local/nvidia/bin:/usr/local/cuda/bin:${PATH}' >> ${HOME}/.bashrc
|
|
||||||
echo 'export LD_LIBRARY_PATH=/usr/local/nvidia/lib:/usr/local/nvidia/lib64:${LD_LIBRARY_PATH}' >> ${HOME}/.bashrc
|
|
||||||
|
|
||||||
apt-get update && apt-get install -y --no-install-recommends --allow-unauthenticated \
|
|
||||||
cuda-cudart-$CUDA_PKG_VERSION \
|
|
||||||
cuda-compat-10-0 \
|
|
||||||
cuda-libraries-$CUDA_PKG_VERSION \
|
|
||||||
cuda-nvtx-$CUDA_PKG_VERSION \
|
|
||||||
libnccl2=$NCCL_VERSION-1+cuda10.0 \
|
|
||||||
libcudnn7=$CUDNN_VERSION-1+cuda10.0 && \
|
|
||||||
ln -s cuda-10.0 /usr/local/cuda && \
|
|
||||||
apt-mark hold libnccl2 libcudnn7 && \
|
|
||||||
rm -rf /var/lib/apt/lists/* \
|
|
||||||
/etc/apt/sources.list.d/nvidia-ml.list /etc/apt/sources.list.d/cuda.list
|
|
||||||
|
|
||||||
python3 -m pip uninstall -y tensorflow
|
|
||||||
python3 -m pip install --no-cache-dir tensorflow-gpu==1.15.2
|
|
||||||
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
## [Intel OpenVINO toolkit](https://software.intel.com/en-us/openvino-toolkit)
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
* Intel Core with 6th generation and higher or Intel Xeon CPUs.
|
|
||||||
|
|
||||||
### Preparation
|
|
||||||
|
|
||||||
- Download the latest [OpenVINO toolkit](https://software.intel.com/en-us/openvino-toolkit) .tgz installer
|
|
||||||
(offline or online) for Ubuntu platforms. Note that OpenVINO does not maintain forward compatability between
|
|
||||||
Intermediate Representations (IRs), so the version of OpenVINO in CVAT and the version used to translate the
|
|
||||||
models needs to be the same.
|
|
||||||
- Put downloaded file into ```cvat/components/openvino```.
|
|
||||||
- Accept EULA in the `cvat/components/openvino/eula.cfg` file.
|
|
||||||
|
|
||||||
### Build docker image
|
|
||||||
```bash
|
|
||||||
# From project root directory
|
|
||||||
docker-compose -f docker-compose.yml -f components/openvino/docker-compose.openvino.yml build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run docker container
|
|
||||||
```bash
|
|
||||||
# From project root directory
|
|
||||||
docker-compose -f docker-compose.yml -f components/openvino/docker-compose.openvino.yml up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
You should be able to login and see the web interface for CVAT now, complete with the new "Model Manager" button.
|
|
||||||
|
|
||||||
### OpenVINO Models
|
|
||||||
|
|
||||||
Clone the [Open Model Zoo](https://github.com/opencv/open_model_zoo). `$ git clone https://github.com/opencv/open_model_zoo.git`
|
|
||||||
|
|
||||||
Install the appropriate libraries. Currently that command would be `$ pip install -r open_model_zoo/tools/downloader/requirements.in`
|
|
||||||
|
|
||||||
Download the models using `downloader.py` file in `open_model_zoo/tools/downloader/`.
|
|
||||||
The `--name` command can be used to specify specific models.
|
|
||||||
The `--print_all` command can print all the available models.
|
|
||||||
Specific models that are already integrated into Cvat can be found [here](https://github.com/opencv/cvat/tree/develop/utils/open_model_zoo).
|
|
||||||
|
|
||||||
From the web user interface in CVAT, upload the models using the model manager.
|
|
||||||
You'll need to include the xml and bin file from the model downloader.
|
|
||||||
You'll need to include the python and JSON files from scratch or by using the ones in the CVAT libary.
|
|
||||||
See [here](https://github.com/opencv/cvat/tree/develop/cvat/apps/auto_annotation) for instructions for creating custom
|
|
||||||
python and JSON files.
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2018 Intel Corporation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
#
|
|
||||||
version: "2.3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
cvat:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
args:
|
|
||||||
OPENVINO_TOOLKIT: "yes"
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# Accept actual EULA from openvino installation archive. Valid values are: {accept, decline}
|
|
||||||
ACCEPT_EULA=accept
|
|
||||||
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Copyright (C) 2018 Intel Corporation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
#
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [[ `lscpu | grep -o "GenuineIntel"` != "GenuineIntel" ]]; then
|
|
||||||
echo "OpenVINO supports only Intel CPUs"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ `lscpu | grep -o "sse4" | head -1` != "sse4" ]] && [[ `lscpu | grep -o "avx2" | head -1` != "avx2" ]]; then
|
|
||||||
echo "OpenVINO expects your CPU to support SSE4 or AVX2 instructions"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
cd /tmp/components/openvino
|
|
||||||
|
|
||||||
tar -xzf `ls | grep "openvino_toolkit"`
|
|
||||||
cd `ls -d */ | grep "openvino_toolkit"`
|
|
||||||
|
|
||||||
apt-get update && apt-get --no-install-recommends install -y sudo cpio && \
|
|
||||||
if [ -f "install_cv_sdk_dependencies.sh" ]; then ./install_cv_sdk_dependencies.sh; \
|
|
||||||
else ./install_openvino_dependencies.sh; fi && SUDO_FORCE_REMOVE=yes apt-get remove -y sudo
|
|
||||||
|
|
||||||
|
|
||||||
cat ../eula.cfg >> silent.cfg
|
|
||||||
./install.sh -s silent.cfg
|
|
||||||
|
|
||||||
cd /tmp/components && rm openvino -r
|
|
||||||
|
|
||||||
if [ -f "/opt/intel/computer_vision_sdk/bin/setupvars.sh" ]; then
|
|
||||||
echo "source /opt/intel/computer_vision_sdk/bin/setupvars.sh" >> ${HOME}/.bashrc;
|
|
||||||
echo -e '\nexport IE_PLUGINS_PATH=${IE_PLUGINS_PATH}' >> /opt/intel/computer_vision_sdk/bin/setupvars.sh;
|
|
||||||
else
|
|
||||||
echo "source /opt/intel/openvino/bin/setupvars.sh" >> ${HOME}/.bashrc;
|
|
||||||
echo -e '\nexport IE_PLUGINS_PATH=${IE_PLUGINS_PATH}' >> /opt/intel/openvino/bin/setupvars.sh;
|
|
||||||
fi
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
## [Tensorflow Object Detector](https://github.com/tensorflow/models/tree/master/research/object_detection)
|
|
||||||
|
|
||||||
### What is it?
|
|
||||||
* This application allows you automatically to annotate many various objects on images.
|
|
||||||
* It uses [Faster RCNN Inception Resnet v2 Atrous Coco Model](http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_resnet_v2_atrous_coco_2018_01_28.tar.gz) from [tensorflow detection model zoo](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md)
|
|
||||||
* It can work on CPU (with Tensorflow or OpenVINO) or GPU (with Tensorflow GPU).
|
|
||||||
* It supports next classes (just specify them in "labels" row):
|
|
||||||
```
|
|
||||||
'surfboard', 'car', 'skateboard', 'boat', 'clock',
|
|
||||||
'cat', 'cow', 'knife', 'apple', 'cup', 'tv',
|
|
||||||
'baseball_bat', 'book', 'suitcase', 'tennis_racket',
|
|
||||||
'stop_sign', 'couch', 'cell_phone', 'keyboard',
|
|
||||||
'cake', 'tie', 'frisbee', 'truck', 'fire_hydrant',
|
|
||||||
'snowboard', 'bed', 'vase', 'teddy_bear',
|
|
||||||
'toaster', 'wine_glass', 'traffic_light',
|
|
||||||
'broccoli', 'backpack', 'carrot', 'potted_plant',
|
|
||||||
'donut', 'umbrella', 'parking_meter', 'bottle',
|
|
||||||
'sandwich', 'motorcycle', 'bear', 'banana',
|
|
||||||
'person', 'scissors', 'elephant', 'dining_table',
|
|
||||||
'toothbrush', 'toilet', 'skis', 'bowl', 'sheep',
|
|
||||||
'refrigerator', 'oven', 'microwave', 'train',
|
|
||||||
'orange', 'mouse', 'laptop', 'bench', 'bicycle',
|
|
||||||
'fork', 'kite', 'zebra', 'baseball_glove', 'bus',
|
|
||||||
'spoon', 'horse', 'handbag', 'pizza', 'sports_ball',
|
|
||||||
'airplane', 'hair_drier', 'hot_dog', 'remote',
|
|
||||||
'sink', 'dog', 'bird', 'giraffe', 'chair'.
|
|
||||||
```
|
|
||||||
* Component adds "Run TF Annotation" button into dashboard.
|
|
||||||
|
|
||||||
|
|
||||||
### Build docker image
|
|
||||||
```bash
|
|
||||||
# From project root directory
|
|
||||||
docker-compose -f docker-compose.yml -f components/tf_annotation/docker-compose.tf_annotation.yml build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run docker container
|
|
||||||
```bash
|
|
||||||
# From project root directory
|
|
||||||
docker-compose -f docker-compose.yml -f components/tf_annotation/docker-compose.tf_annotation.yml up -d
|
|
||||||
```
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (C) 2018 Intel Corporation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
#
|
|
||||||
version: "2.3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
cvat:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
args:
|
|
||||||
TF_ANNOTATION: "yes"
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Copyright (C) 2018 Intel Corporation
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
#
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cd ${HOME} && \
|
|
||||||
curl http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_resnet_v2_atrous_coco_2018_01_28.tar.gz -o model.tar.gz && \
|
|
||||||
tar -xzf model.tar.gz && rm model.tar.gz && \
|
|
||||||
mv faster_rcnn_inception_resnet_v2_atrous_coco_2018_01_28 ${HOME}/rcnn && cd ${HOME} && \
|
|
||||||
mv rcnn/frozen_inference_graph.pb rcnn/inference_graph.pb
|
|
||||||
|
|
||||||
# tensorflow is installed globally
|
|
||||||
@ -1,6 +1,5 @@
|
|||||||
docs
|
docs
|
||||||
node_modules
|
node_modules
|
||||||
reports
|
reports
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
yarn.lock
|
||||||
dist
|
dist
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global
|
||||||
|
require:false
|
||||||
|
*/
|
||||||
|
|
||||||
|
const serverProxy = require('./server-proxy');
|
||||||
|
const { ArgumentError } = require('./exceptions');
|
||||||
|
const { Task } = require('./session');
|
||||||
|
const MLModel = require('./ml-model');
|
||||||
|
const { RQStatus } = require('./enums');
|
||||||
|
|
||||||
|
class LambdaManager {
|
||||||
|
constructor() {
|
||||||
|
this.listening = {};
|
||||||
|
this.cachedList = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async list() {
|
||||||
|
if (Array.isArray(this.cachedList)) {
|
||||||
|
return [...this.cachedList];
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await serverProxy.lambda.list();
|
||||||
|
const models = [];
|
||||||
|
|
||||||
|
for (const model of result) {
|
||||||
|
models.push(new MLModel({
|
||||||
|
id: model.id,
|
||||||
|
name: model.name,
|
||||||
|
description: model.description,
|
||||||
|
framework: model.framework,
|
||||||
|
labels: [...model.labels],
|
||||||
|
type: model.kind,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cachedList = models;
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(task, model, args) {
|
||||||
|
if (!(task instanceof Task)) {
|
||||||
|
throw new ArgumentError(
|
||||||
|
`Argument task is expected to be an instance of Task class, but got ${typeof (task)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(model instanceof MLModel)) {
|
||||||
|
throw new ArgumentError(
|
||||||
|
`Argument model is expected to be an instance of MLModel class, but got ${typeof (model)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args && typeof (args) !== 'object') {
|
||||||
|
throw new ArgumentError(
|
||||||
|
`Argument args is expected to be an object, but got ${typeof (model)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = args;
|
||||||
|
body.task = task.id;
|
||||||
|
body.function = model.id;
|
||||||
|
|
||||||
|
const result = await serverProxy.lambda.run(body);
|
||||||
|
return result.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async call(task, model, args) {
|
||||||
|
const body = args;
|
||||||
|
body.task = task.id;
|
||||||
|
const result = await serverProxy.lambda.call(model.id, body);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async requests() {
|
||||||
|
const result = await serverProxy.lambda.requests();
|
||||||
|
return result.filter((request) => ['queued', 'started'].includes(request.status));
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancel(requestID) {
|
||||||
|
if (typeof (requestID) !== 'string') {
|
||||||
|
throw new ArgumentError(`Request id argument is required to be a string. But got ${requestID}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.listening[requestID]) {
|
||||||
|
clearTimeout(this.listening[requestID].timeout);
|
||||||
|
delete this.listening[requestID];
|
||||||
|
}
|
||||||
|
await serverProxy.lambda.cancel(requestID);
|
||||||
|
}
|
||||||
|
|
||||||
|
async listen(requestID, onUpdate) {
|
||||||
|
const timeoutCallback = async () => {
|
||||||
|
try {
|
||||||
|
this.listening[requestID].timeout = null;
|
||||||
|
const response = await serverProxy.lambda.status(requestID);
|
||||||
|
|
||||||
|
if (response.status === RQStatus.QUEUED || response.status === RQStatus.STARTED) {
|
||||||
|
onUpdate(response.status, response.progress || 0);
|
||||||
|
this.listening[requestID].timeout = setTimeout(timeoutCallback, 2000);
|
||||||
|
} else {
|
||||||
|
if (response.status === RQStatus.FINISHED) {
|
||||||
|
onUpdate(response.status, response.progress || 100);
|
||||||
|
} else {
|
||||||
|
onUpdate(response.status, response.progress || 0, response.exc_info || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this.listening[requestID];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
onUpdate(RQStatus.UNKNOWN, 0, `Could not get a status of the request ${requestID}. ${error.toString()}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.listening[requestID] = {
|
||||||
|
onUpdate,
|
||||||
|
timeout: setTimeout(timeoutCallback, 2000),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new LambdaManager();
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019-2020 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a machine learning model
|
||||||
|
* @memberof module:API.cvat.classes
|
||||||
|
*/
|
||||||
|
class MLModel {
|
||||||
|
constructor(data) {
|
||||||
|
this._id = data.id;
|
||||||
|
this._name = data.name;
|
||||||
|
this._labels = data.labels;
|
||||||
|
this._framework = data.framework;
|
||||||
|
this._description = data.description;
|
||||||
|
this._type = data.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get id() {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get name() {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string[]}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get labels() {
|
||||||
|
if (Array.isArray(this._labels)) {
|
||||||
|
return [...this._labels];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get framework() {
|
||||||
|
return this._framework;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get description() {
|
||||||
|
return this._description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {module:API.cvat.enums.ModelType}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get type() {
|
||||||
|
return this._type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MLModel;
|
||||||
@ -1,227 +0,0 @@
|
|||||||
// Copyright (C) 2020 Intel Corporation
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { Row, Col } from 'antd/lib/grid';
|
|
||||||
import Modal from 'antd/lib/modal';
|
|
||||||
import Menu from 'antd/lib/menu';
|
|
||||||
import Text from 'antd/lib/typography/Text';
|
|
||||||
import InputNumber from 'antd/lib/input-number';
|
|
||||||
import Tooltip from 'antd/lib/tooltip';
|
|
||||||
|
|
||||||
import { clamp } from 'utils/math';
|
|
||||||
import { run, cancel } from 'utils/reid-utils';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { CombinedState } from 'reducers/interfaces';
|
|
||||||
import { fetchAnnotationsAsync } from 'actions/annotation-actions';
|
|
||||||
|
|
||||||
interface InputModalProps {
|
|
||||||
visible: boolean;
|
|
||||||
onCancel(): void;
|
|
||||||
onSubmit(threshold: number, distance: number): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function InputModal(props: InputModalProps): JSX.Element {
|
|
||||||
const { visible, onCancel, onSubmit } = props;
|
|
||||||
const [threshold, setThreshold] = useState(0.5);
|
|
||||||
const [distance, setDistance] = useState(50);
|
|
||||||
|
|
||||||
const [thresholdMin, thresholdMax] = [0.05, 0.95];
|
|
||||||
const [distanceMin, distanceMax] = [1, 1000];
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
closable={false}
|
|
||||||
width={300}
|
|
||||||
visible={visible}
|
|
||||||
onCancel={onCancel}
|
|
||||||
onOk={() => onSubmit(threshold, distance)}
|
|
||||||
okText='Merge'
|
|
||||||
>
|
|
||||||
<Row type='flex'>
|
|
||||||
<Col span={10}>
|
|
||||||
<Tooltip title='Similarity of objects on neighbour frames is calculated using AI model'>
|
|
||||||
<Text>Similarity threshold: </Text>
|
|
||||||
</Tooltip>
|
|
||||||
</Col>
|
|
||||||
<Col span={12}>
|
|
||||||
<InputNumber
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
min={thresholdMin}
|
|
||||||
max={thresholdMax}
|
|
||||||
step={0.05}
|
|
||||||
value={threshold}
|
|
||||||
onChange={(value: number | undefined) => {
|
|
||||||
if (typeof (value) === 'number') {
|
|
||||||
setThreshold(clamp(value, thresholdMin, thresholdMax));
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row type='flex'>
|
|
||||||
<Col span={10}>
|
|
||||||
<Tooltip title='The value defines max distance to merge (between centers of two objects on neighbour frames)'>
|
|
||||||
<Text>Max pixel distance: </Text>
|
|
||||||
</Tooltip>
|
|
||||||
</Col>
|
|
||||||
<Col span={12}>
|
|
||||||
<InputNumber
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
min={distanceMin}
|
|
||||||
max={distanceMax}
|
|
||||||
step={5}
|
|
||||||
value={distance}
|
|
||||||
onChange={(value: number | undefined) => {
|
|
||||||
if (typeof (value) === 'number') {
|
|
||||||
setDistance(clamp(value, distanceMin, distanceMax));
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InProgressDialogProps {
|
|
||||||
visible: boolean;
|
|
||||||
progress: number;
|
|
||||||
onCancel(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function InProgressDialog(props: InProgressDialogProps): JSX.Element {
|
|
||||||
const { visible, onCancel, progress } = props;
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
closable={false}
|
|
||||||
width={300}
|
|
||||||
visible={visible}
|
|
||||||
okText='Cancel'
|
|
||||||
okButtonProps={{
|
|
||||||
type: 'danger',
|
|
||||||
}}
|
|
||||||
onOk={onCancel}
|
|
||||||
cancelButtonProps={{
|
|
||||||
style: {
|
|
||||||
display: 'none',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text>{`Merging is in progress ${progress}%`}</Text>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const reidContainer = window.document.createElement('div');
|
|
||||||
reidContainer.setAttribute('id', 'cvat-reid-wrapper');
|
|
||||||
window.document.body.appendChild(reidContainer);
|
|
||||||
|
|
||||||
|
|
||||||
interface StateToProps {
|
|
||||||
jobInstance: any | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DispatchToProps {
|
|
||||||
updateAnnotations(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapStateToProps(state: CombinedState): StateToProps {
|
|
||||||
const {
|
|
||||||
annotation: {
|
|
||||||
job: {
|
|
||||||
instance: jobInstance,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} = state;
|
|
||||||
|
|
||||||
return {
|
|
||||||
jobInstance,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch: any): DispatchToProps {
|
|
||||||
return {
|
|
||||||
updateAnnotations(): void {
|
|
||||||
dispatch(fetchAnnotationsAsync());
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function ReIDPlugin(props: StateToProps & DispatchToProps): JSX.Element {
|
|
||||||
const { jobInstance, updateAnnotations, ...rest } = props;
|
|
||||||
const [showInputDialog, setShowInputDialog] = useState(false);
|
|
||||||
const [showInProgressDialog, setShowInProgressDialog] = useState(false);
|
|
||||||
const [progress, setProgress] = useState(0);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
ReactDOM.render((
|
|
||||||
<>
|
|
||||||
<InProgressDialog
|
|
||||||
visible={showInProgressDialog}
|
|
||||||
progress={progress}
|
|
||||||
onCancel={() => {
|
|
||||||
cancel(jobInstance.id);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<InputModal
|
|
||||||
visible={showInputDialog}
|
|
||||||
onCancel={() => setShowInputDialog(false)}
|
|
||||||
onSubmit={async (threshold: number, distance: number) => {
|
|
||||||
setProgress(0);
|
|
||||||
setShowInputDialog(false);
|
|
||||||
setShowInProgressDialog(true);
|
|
||||||
|
|
||||||
const onUpdatePercentage = (percent: number): void => {
|
|
||||||
setProgress(percent);
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const annotations = await jobInstance.annotations.export();
|
|
||||||
const merged = await run({
|
|
||||||
threshold,
|
|
||||||
distance,
|
|
||||||
onUpdatePercentage,
|
|
||||||
jobID: jobInstance.id,
|
|
||||||
annotations,
|
|
||||||
});
|
|
||||||
await jobInstance.annotations.clear();
|
|
||||||
updateAnnotations(); // one more call to do not confuse canvas
|
|
||||||
await jobInstance.annotations.import(merged);
|
|
||||||
updateAnnotations();
|
|
||||||
} catch (error) {
|
|
||||||
Modal.error({
|
|
||||||
title: 'Could not merge annotations',
|
|
||||||
content: error.toString(),
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setShowInProgressDialog(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
), reidContainer);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Menu.Item
|
|
||||||
{...rest}
|
|
||||||
key='run_reid'
|
|
||||||
title='Run algorithm that merges separated bounding boxes automatically'
|
|
||||||
onClick={() => {
|
|
||||||
if (jobInstance) {
|
|
||||||
setShowInputDialog(true);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Run ReID merge
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps,
|
|
||||||
)(ReIDPlugin);
|
|
||||||
@ -0,0 +1,166 @@
|
|||||||
|
// Copyright (C) 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Form, { FormComponentProps } from 'antd/lib/form/Form';
|
||||||
|
import Button from 'antd/lib/button';
|
||||||
|
import Icon from 'antd/lib/icon';
|
||||||
|
import Input from 'antd/lib/input';
|
||||||
|
|
||||||
|
import patterns from 'utils/validation-patterns';
|
||||||
|
|
||||||
|
export interface ChangePasswordData {
|
||||||
|
oldPassword: string;
|
||||||
|
newPassword1: string;
|
||||||
|
newPassword2: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangePasswordFormProps = {
|
||||||
|
fetching: boolean;
|
||||||
|
onSubmit(loginData: ChangePasswordData): void;
|
||||||
|
} & FormComponentProps;
|
||||||
|
|
||||||
|
class ChangePasswordFormComponent extends React.PureComponent<ChangePasswordFormProps> {
|
||||||
|
private validateConfirmation = (_: any, value: string, callback: Function): void => {
|
||||||
|
const { form } = this.props;
|
||||||
|
if (value && value !== form.getFieldValue('newPassword1')) {
|
||||||
|
callback('Two passwords that you enter is inconsistent!');
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private validatePassword = (_: any, value: string, callback: Function): void => {
|
||||||
|
const { form } = this.props;
|
||||||
|
if (!patterns.validatePasswordLength.pattern.test(value)) {
|
||||||
|
callback(patterns.validatePasswordLength.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!patterns.passwordContainsNumericCharacters.pattern.test(value)) {
|
||||||
|
callback(patterns.passwordContainsNumericCharacters.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!patterns.passwordContainsUpperCaseCharacter.pattern.test(value)) {
|
||||||
|
callback(patterns.passwordContainsUpperCaseCharacter.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!patterns.passwordContainsLowerCaseCharacter.pattern.test(value)) {
|
||||||
|
callback(patterns.passwordContainsLowerCaseCharacter.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
form.validateFields(['newPassword2'], { force: true });
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleSubmit = (e: React.FormEvent): void => {
|
||||||
|
e.preventDefault();
|
||||||
|
const {
|
||||||
|
form,
|
||||||
|
onSubmit,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
form.validateFields((error, values): void => {
|
||||||
|
if (!error) {
|
||||||
|
const validatedFields = {
|
||||||
|
...values,
|
||||||
|
confirmations: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
onSubmit(validatedFields);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private renderOldPasswordField(): JSX.Element {
|
||||||
|
const { form } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form.Item hasFeedback>
|
||||||
|
{form.getFieldDecorator('oldPassword', {
|
||||||
|
rules: [{
|
||||||
|
required: true,
|
||||||
|
message: 'Please input your current password!',
|
||||||
|
}],
|
||||||
|
})(<Input.Password
|
||||||
|
autoComplete='new-password'
|
||||||
|
prefix={<Icon type='lock' style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
|
||||||
|
placeholder='Current password'
|
||||||
|
/>)}
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderNewPasswordField(): JSX.Element {
|
||||||
|
const { form } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form.Item hasFeedback>
|
||||||
|
{form.getFieldDecorator('newPassword1', {
|
||||||
|
rules: [{
|
||||||
|
required: true,
|
||||||
|
message: 'Please input new password!',
|
||||||
|
}, {
|
||||||
|
validator: this.validatePassword,
|
||||||
|
}],
|
||||||
|
})(<Input.Password
|
||||||
|
autoComplete='new-password'
|
||||||
|
prefix={<Icon type='lock' style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
|
||||||
|
placeholder='New password'
|
||||||
|
/>)}
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderNewPasswordConfirmationField(): JSX.Element {
|
||||||
|
const { form } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form.Item hasFeedback>
|
||||||
|
{form.getFieldDecorator('newPassword2', {
|
||||||
|
rules: [{
|
||||||
|
required: true,
|
||||||
|
message: 'Please confirm your new password!',
|
||||||
|
}, {
|
||||||
|
validator: this.validateConfirmation,
|
||||||
|
}],
|
||||||
|
})(<Input.Password
|
||||||
|
autoComplete='new-password'
|
||||||
|
prefix={<Icon type='lock' style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
|
||||||
|
placeholder='Confirm new password'
|
||||||
|
/>)}
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
const { fetching } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
onSubmit={this.handleSubmit}
|
||||||
|
className='change-password-form'
|
||||||
|
>
|
||||||
|
{this.renderOldPasswordField()}
|
||||||
|
{this.renderNewPasswordField()}
|
||||||
|
{this.renderNewPasswordConfirmationField()}
|
||||||
|
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type='primary'
|
||||||
|
htmlType='submit'
|
||||||
|
className='change-password-form-button'
|
||||||
|
loading={fetching}
|
||||||
|
disabled={fetching}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Form.create<ChangePasswordFormProps>()(ChangePasswordFormComponent);
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright (C) 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import Modal from 'antd/lib/modal';
|
||||||
|
import Title from 'antd/lib/typography/Title';
|
||||||
|
|
||||||
|
import { changePasswordAsync } from 'actions/auth-actions';
|
||||||
|
import { CombinedState } from 'reducers/interfaces';
|
||||||
|
import ChangePasswordForm, { ChangePasswordData } from './change-password-form';
|
||||||
|
|
||||||
|
|
||||||
|
interface StateToProps {
|
||||||
|
fetching: boolean;
|
||||||
|
visible: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchToProps {
|
||||||
|
onChangePassword(
|
||||||
|
oldPassword: string,
|
||||||
|
newPassword1: string,
|
||||||
|
newPassword2: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChangePasswordPageComponentProps {
|
||||||
|
fetching: boolean;
|
||||||
|
visible: boolean;
|
||||||
|
onChangePassword: (oldPassword: string, newPassword1: string, newPassword2: string) => void;
|
||||||
|
onClose(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: CombinedState): StateToProps {
|
||||||
|
return {
|
||||||
|
fetching: state.auth.fetching,
|
||||||
|
visible: state.auth.showChangePasswordDialog,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch: any): DispatchToProps {
|
||||||
|
return ({
|
||||||
|
onChangePassword(oldPassword: string, newPassword1: string, newPassword2: string): void {
|
||||||
|
dispatch(changePasswordAsync(oldPassword, newPassword1, newPassword2));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChangePasswordComponent(props: ChangePasswordPageComponentProps): JSX.Element {
|
||||||
|
const {
|
||||||
|
fetching,
|
||||||
|
onChangePassword,
|
||||||
|
visible,
|
||||||
|
onClose,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={<Title level={3}>Change password</Title>}
|
||||||
|
okType='primary'
|
||||||
|
okText='Submit'
|
||||||
|
footer={null}
|
||||||
|
visible={visible}
|
||||||
|
destroyOnClose
|
||||||
|
onCancel={onClose}
|
||||||
|
>
|
||||||
|
<ChangePasswordForm
|
||||||
|
onSubmit={(changePasswordData: ChangePasswordData): void => {
|
||||||
|
onChangePassword(
|
||||||
|
changePasswordData.oldPassword,
|
||||||
|
changePasswordData.newPassword1,
|
||||||
|
changePasswordData.newPassword2,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
fetching={fetching}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(ChangePasswordComponent);
|
||||||
@ -1,160 +0,0 @@
|
|||||||
// Copyright (C) 2020 Intel Corporation
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { Row, Col } from 'antd/lib/grid';
|
|
||||||
import Icon from 'antd/lib/icon';
|
|
||||||
import Alert from 'antd/lib/alert';
|
|
||||||
import Button from 'antd/lib/button';
|
|
||||||
import Tooltip from 'antd/lib/tooltip';
|
|
||||||
import message from 'antd/lib/message';
|
|
||||||
import notification from 'antd/lib/notification';
|
|
||||||
import Text from 'antd/lib/typography/Text';
|
|
||||||
|
|
||||||
import consts from 'consts';
|
|
||||||
import ConnectedFileManager, {
|
|
||||||
FileManagerContainer,
|
|
||||||
} from 'containers/file-manager/file-manager';
|
|
||||||
import { ModelFiles } from 'reducers/interfaces';
|
|
||||||
|
|
||||||
import CreateModelForm, {
|
|
||||||
CreateModelForm as WrappedCreateModelForm,
|
|
||||||
} from './create-model-form';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
createModel(name: string, files: ModelFiles, global: boolean): void;
|
|
||||||
isAdmin: boolean;
|
|
||||||
modelCreatingStatus: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class CreateModelContent extends React.PureComponent<Props> {
|
|
||||||
private modelForm: WrappedCreateModelForm;
|
|
||||||
private fileManagerContainer: FileManagerContainer;
|
|
||||||
|
|
||||||
public constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
this.modelForm = null as any as WrappedCreateModelForm;
|
|
||||||
this.fileManagerContainer = null as any as FileManagerContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidUpdate(prevProps: Props): void {
|
|
||||||
const { modelCreatingStatus } = this.props;
|
|
||||||
|
|
||||||
if (prevProps.modelCreatingStatus !== 'CREATED'
|
|
||||||
&& modelCreatingStatus === 'CREATED') {
|
|
||||||
message.success('The model has been uploaded');
|
|
||||||
this.modelForm.resetFields();
|
|
||||||
this.fileManagerContainer.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleSubmitClick = (): void => {
|
|
||||||
const { createModel } = this.props;
|
|
||||||
this.modelForm.submit()
|
|
||||||
.then((data) => {
|
|
||||||
const {
|
|
||||||
local,
|
|
||||||
share,
|
|
||||||
} = this.fileManagerContainer.getFiles();
|
|
||||||
|
|
||||||
const files = local.length ? local : share;
|
|
||||||
const grouppedFiles: ModelFiles = {
|
|
||||||
xml: '',
|
|
||||||
bin: '',
|
|
||||||
py: '',
|
|
||||||
json: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
(files as any).reduce((acc: ModelFiles, value: File | string): ModelFiles => {
|
|
||||||
const name = typeof value === 'string' ? value : value.name;
|
|
||||||
const [extension] = name.split('.').reverse();
|
|
||||||
if (extension in acc) {
|
|
||||||
acc[extension] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, grouppedFiles);
|
|
||||||
|
|
||||||
if (Object.keys(grouppedFiles)
|
|
||||||
.map((key: string) => grouppedFiles[key])
|
|
||||||
.filter((val) => !!val).length !== 4) {
|
|
||||||
notification.error({
|
|
||||||
message: 'Could not upload a model',
|
|
||||||
description: 'Please, specify correct files',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
createModel(data.name, grouppedFiles, data.global);
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
notification.error({
|
|
||||||
message: 'Could not upload a model',
|
|
||||||
description: 'Please, check input fields',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
|
||||||
const {
|
|
||||||
modelCreatingStatus,
|
|
||||||
} = this.props;
|
|
||||||
const loading = !!modelCreatingStatus
|
|
||||||
&& modelCreatingStatus !== 'CREATED';
|
|
||||||
const status = modelCreatingStatus
|
|
||||||
&& modelCreatingStatus !== 'CREATED' ? modelCreatingStatus : '';
|
|
||||||
|
|
||||||
const { AUTO_ANNOTATION_GUIDE_URL } = consts;
|
|
||||||
return (
|
|
||||||
<Row type='flex' justify='start' align='middle' className='cvat-create-model-content'>
|
|
||||||
<Col span={24}>
|
|
||||||
<Tooltip title='Click to open guide'>
|
|
||||||
<Icon
|
|
||||||
onClick={(): void => {
|
|
||||||
// false positive
|
|
||||||
// eslint-disable-next-line
|
|
||||||
window.open(AUTO_ANNOTATION_GUIDE_URL, '_blank');
|
|
||||||
}}
|
|
||||||
type='question-circle'
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Col>
|
|
||||||
<Col span={24}>
|
|
||||||
<CreateModelForm
|
|
||||||
wrappedComponentRef={
|
|
||||||
(ref: WrappedCreateModelForm): void => {
|
|
||||||
this.modelForm = ref;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col span={24}>
|
|
||||||
<Text type='danger'>* </Text>
|
|
||||||
<Text className='cvat-text-color'>Select files:</Text>
|
|
||||||
</Col>
|
|
||||||
<Col span={24}>
|
|
||||||
<ConnectedFileManager
|
|
||||||
ref={
|
|
||||||
(container: FileManagerContainer): void => {
|
|
||||||
this.fileManagerContainer = container;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
withRemote={false}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col span={18}>
|
|
||||||
{status && <Alert message={`${status}`} />}
|
|
||||||
</Col>
|
|
||||||
<Col span={6}>
|
|
||||||
<Button
|
|
||||||
type='primary'
|
|
||||||
disabled={loading}
|
|
||||||
loading={loading}
|
|
||||||
onClick={this.handleSubmitClick}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
// Copyright (C) 2020 Intel Corporation
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { Row, Col } from 'antd/lib/grid';
|
|
||||||
import Form, { FormComponentProps } from 'antd/lib/form/Form';
|
|
||||||
import Input from 'antd/lib/input';
|
|
||||||
import Tooltip from 'antd/lib/tooltip';
|
|
||||||
import Checkbox from 'antd/lib/checkbox';
|
|
||||||
import Text from 'antd/lib/typography/Text';
|
|
||||||
|
|
||||||
type Props = FormComponentProps;
|
|
||||||
|
|
||||||
export class CreateModelForm extends React.PureComponent<Props> {
|
|
||||||
public submit(): Promise<{name: string; global: boolean}> {
|
|
||||||
const { form } = this.props;
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
form.validateFields((errors, values): void => {
|
|
||||||
if (!errors) {
|
|
||||||
resolve({
|
|
||||||
name: values.name,
|
|
||||||
global: values.global,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
reject(errors);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public resetFields(): void {
|
|
||||||
const { form } = this.props;
|
|
||||||
form.resetFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
|
||||||
const { form } = this.props;
|
|
||||||
const { getFieldDecorator } = form;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form onSubmit={(e: React.FormEvent): void => e.preventDefault()}>
|
|
||||||
<Row>
|
|
||||||
<Col span={24}>
|
|
||||||
<Text type='danger'>* </Text>
|
|
||||||
<Text className='cvat-text-color'>Name:</Text>
|
|
||||||
</Col>
|
|
||||||
<Col span={14}>
|
|
||||||
<Form.Item hasFeedback>
|
|
||||||
{ getFieldDecorator('name', {
|
|
||||||
rules: [{
|
|
||||||
required: true,
|
|
||||||
message: 'Please, specify a model name',
|
|
||||||
}],
|
|
||||||
})(<Input placeholder='Model name' />)}
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col span={8} offset={2}>
|
|
||||||
<Form.Item>
|
|
||||||
<Tooltip title='Will this model be availabe for everyone?'>
|
|
||||||
{ getFieldDecorator('global', {
|
|
||||||
initialValue: false,
|
|
||||||
valuePropName: 'checked',
|
|
||||||
})(
|
|
||||||
<Checkbox>
|
|
||||||
<Text className='cvat-text-color'>
|
|
||||||
Load globally
|
|
||||||
</Text>
|
|
||||||
</Checkbox>,
|
|
||||||
)}
|
|
||||||
</Tooltip>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Form.create()(CreateModelForm);
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
// Copyright (C) 2020 Intel Corporation
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
import './styles.scss';
|
|
||||||
import React from 'react';
|
|
||||||
import { Row, Col } from 'antd/lib/grid';
|
|
||||||
import Text from 'antd/lib/typography/Text';
|
|
||||||
|
|
||||||
import { ModelFiles } from 'reducers/interfaces';
|
|
||||||
import CreateModelContent from './create-model-content';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
createModel(name: string, files: ModelFiles, global: boolean): void;
|
|
||||||
isAdmin: boolean;
|
|
||||||
modelCreatingStatus: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function CreateModelPageComponent(props: Props): JSX.Element {
|
|
||||||
const {
|
|
||||||
isAdmin,
|
|
||||||
modelCreatingStatus,
|
|
||||||
createModel,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Row type='flex' justify='center' align='top' className='cvat-create-model-form-wrapper'>
|
|
||||||
<Col md={20} lg={16} xl={14} xxl={9}>
|
|
||||||
<Text className='cvat-title'>Upload a new model</Text>
|
|
||||||
<CreateModelContent
|
|
||||||
isAdmin={isAdmin}
|
|
||||||
modelCreatingStatus={modelCreatingStatus}
|
|
||||||
createModel={createModel}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
// Copyright (C) 2020 Intel Corporation
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
@import '../../base.scss';
|
|
||||||
|
|
||||||
.cvat-create-model-form-wrapper {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 40px;
|
|
||||||
overflow-y: auto;
|
|
||||||
height: 90%;
|
|
||||||
|
|
||||||
> div > span {
|
|
||||||
font-size: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cvat-create-model-content {
|
|
||||||
margin-top: 20px;
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
border: 1px solid $border-color-1;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 20px;
|
|
||||||
background: $background-color-1;
|
|
||||||
text-align: initial;
|
|
||||||
|
|
||||||
> div:nth-child(1) > i {
|
|
||||||
float: right;
|
|
||||||
font-size: 20px;
|
|
||||||
color: $danger-icon-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div:nth-child(4) {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div:nth-child(6) > button {
|
|
||||||
margin-top: 10px;
|
|
||||||
float: right;
|
|
||||||
width: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue