From 70c7b4b8053290e613da81466a4e44c117960539 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Sat, 16 Mar 2019 22:34:16 -0400 Subject: [PATCH 1/6] Installed dextr python package --- app/api/models.py | 48 ++++++++++++++++++++++++++++++++++---- app/config.py | 2 ++ app/util/dextr.py | 7 ++++++ docker/api/Dockerfile | 18 +++++++++----- models/dextr_pascal_sbd.sh | 3 +++ 5 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 app/util/dextr.py create mode 100644 models/dextr_pascal_sbd.sh diff --git a/app/api/models.py b/app/api/models.py index 5b0c4a13..2087052b 100644 --- a/app/api/models.py +++ b/app/api/models.py @@ -1,25 +1,63 @@ from flask_restplus import Namespace, Resource, reqparse from werkzeug.datastructures import FileStorage -from imantics import Image as ImanticsImage +from imantics import Mask from flask_login import login_required from ..config import Config from PIL import Image +from ..models import ImageModel import os -MODEL_LOADED = len(Config.MASK_RCNN_FILE) != 0 and os.path.isfile(Config.MASK_RCNN_FILE) - -if MODEL_LOADED: +MASKRCNN_LOADED = os.path.isfile(Config.MASK_RCNN_FILE) +if MASKRCNN_LOADED: from ..util.mask_rcnn import model as maskrcnn else: print("MaskRCNN model is disabled.", flush=True) +DEXTR_LOADED = os.path.isfile(Config.DEXTR_FILE) +if DEXTR_LOADED: + from ..util.dextr import model as dextr +else: + print("DEXTR model is disabled.", flush=True) + api = Namespace('model', description='Model related operations') image_upload = reqparse.RequestParser() image_upload.add_argument('image', location='files', type=FileStorage, required=True, help='Image') +dextr_args = reqparse.RequestParser() +dextr_args.add_argument('points', type=list, required=True) + + +@api.route('/dextr/') +class MaskRCNN(Resource): + + @login_required + @api.expect(dextr_args) + def get(self, image_id): + """ COCO data test """ + + if not DEXTR_LOADED: + return {"disabled": True, "message": "DEXTR is disabled"}, 400 + + args = dextr_args.parse_args() + points = args.get('points') + + if len(points) != 4: + return {"message": "Invalid points entered"}, 400 + + image_model = ImageModel.objects(id=image_id).first() + if not image_model: + return {"message": "Invalid image ID"}, 400 + + image = Image.open(image_model.path) + + result = dextr.predict_mask(image, points) + + return { "segmentaiton": Mask(result).polygons().segmentation } + + @api.route('/maskrcnn') class MaskRCNN(Resource): @@ -27,7 +65,7 @@ class MaskRCNN(Resource): @api.expect(image_upload) def post(self): """ COCO data test """ - if not MODEL_LOADED: + if not MASKRCNN_LOADED: return {"disabled": True, "coco": {}} args = image_upload.parse_args() diff --git a/app/config.py b/app/config.py index e1a4917d..bf4e50b4 100644 --- a/app/config.py +++ b/app/config.py @@ -31,3 +31,5 @@ class Config: # Models MASK_RCNN_FILE = os.getenv("MASK_RCNN_FILE", "") MASK_RCNN_CLASSES = os.getenv("MASK_RCNN_CLASSES", "BG") + + DEXTR_FILE = os.getenv("DEXTR_FILE", "/models/dextr_pascal-sbd.h5") diff --git a/app/util/dextr.py b/app/util/dextr.py new file mode 100644 index 00000000..14e2e7e2 --- /dev/null +++ b/app/util/dextr.py @@ -0,0 +1,7 @@ +from dextr import DEXTR +from ..config import Config +import os + + +model = DEXTR(nb_classes=1, resnet_layers=101, input_shape=(512, 512), weights_path=Config.DEXTR_FILE, + num_input_channels=4, classifier='psp', sigmoid=True) \ No newline at end of file diff --git a/docker/api/Dockerfile b/docker/api/Dockerfile index c2aa1727..b3e12ac6 100644 --- a/docker/api/Dockerfile +++ b/docker/api/Dockerfile @@ -5,14 +5,20 @@ WORKDIR /workspace/ # Install python package dependices COPY requirements.txt requirements.txt RUN pip install -r requirements.txt && \ - pip install gunicorn[eventlet]==19.9.0 && \ - pip install pycocotools + pip install gunicorn[eventlet]==19.9.0 && \ + pip install pycocotools # Install maskrcnn -RUN git clone --single-branch --depth 1 https://github.com/matterport/Mask_RCNN.git $TEMP_MRCNN_DIR /tmp/maskrcnn && \ - cd /tmp/maskrcnn && \ - pip install -r requirements.txt && \ - python3 setup.py install +RUN git clone --single-branch --depth 1 https://github.com/matterport/Mask_RCNN.git /tmp/maskrcnn && \ + cd /tmp/maskrcnn && \ + pip install -r requirements.txt && \ + python3 setup.py install + +# Install DEXTR +RUN git clone --single --depth 1 https://github.com/jsbroks/dextr-keras.git /tmp/dextr && \ + cd /tmp/dextr && \ + pip install -r requirements.txt && \ + python setup.py install COPY ./.git /workspace/.git diff --git a/models/dextr_pascal_sbd.sh b/models/dextr_pascal_sbd.sh new file mode 100644 index 00000000..66a76b9a --- /dev/null +++ b/models/dextr_pascal_sbd.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +wget https://github.com/jsbroks/dextr-keras/releases/download/v1.0.0/dextr_pascal-sbd.h5 \ No newline at end of file From 720e5436911abc4d3a5e59e6851462085d1c14a8 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Sat, 16 Mar 2019 22:43:57 -0400 Subject: [PATCH 2/6] Created DEXTR web client tool --- .../components/annotator/tools/DEXTRTool.vue | 39 +++++++++++++++++++ client/src/views/Annotator.vue | 9 ++++- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 client/src/components/annotator/tools/DEXTRTool.vue diff --git a/client/src/components/annotator/tools/DEXTRTool.vue b/client/src/components/annotator/tools/DEXTRTool.vue new file mode 100644 index 00000000..379d9253 --- /dev/null +++ b/client/src/components/annotator/tools/DEXTRTool.vue @@ -0,0 +1,39 @@ + diff --git a/client/src/views/Annotator.vue b/client/src/views/Annotator.vue index 50d497ef..071fa8d0 100755 --- a/client/src/views/Annotator.vue +++ b/client/src/views/Annotator.vue @@ -45,6 +45,11 @@ @setcursor="setCursor" ref="keypoint" /> +
@@ -205,6 +210,7 @@ import MagicWandTool from "@/components/annotator/tools/MagicWandTool"; import EraserTool from "@/components/annotator/tools/EraserTool"; import BrushTool from "@/components/annotator/tools/BrushTool"; import KeypointTool from "@/components/annotator/tools/KeypointTool"; +import DEXTRTool from "@/components/annotator/tools/DEXTRTool" import CopyAnnotationsButton from "@/components/annotator/tools/CopyAnnotationsButton"; import CenterButton from "@/components/annotator/tools/CenterButton"; @@ -255,7 +261,8 @@ export default { HideAllButton, ShowAllButton, KeypointPanel, - AnnotateButton + AnnotateButton, + DEXTRTool }, mixins: [toastrs, shortcuts], props: { From 714580033bf33379e838b8f258877baa10f94c82 Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Sun, 17 Mar 2019 12:58:09 -0400 Subject: [PATCH 3/6] Finished webclient DEXTR tool --- app/api/models.py | 11 +-- .../annotator/panels/DEXTRPanel.vue | 36 ++++++++++ .../components/annotator/tools/DEXTRTool.vue | 67 +++++++++++++++++-- client/src/views/Annotator.vue | 11 ++- 4 files changed, 115 insertions(+), 10 deletions(-) create mode 100755 client/src/components/annotator/panels/DEXTRPanel.vue diff --git a/app/api/models.py b/app/api/models.py index 2087052b..d701e278 100644 --- a/app/api/models.py +++ b/app/api/models.py @@ -27,7 +27,9 @@ image_upload.add_argument('image', location='files', type=FileStorage, required=True, help='Image') dextr_args = reqparse.RequestParser() -dextr_args.add_argument('points', type=list, required=True) +dextr_args.add_argument('points', location='json', type=list, required=True) +dextr_args.add_argument('padding', location='json', type=int, default=50) +dextr_args.add_argument('threshold', location='json', type=int, default=80) @api.route('/dextr/') @@ -35,7 +37,7 @@ class MaskRCNN(Resource): @login_required @api.expect(dextr_args) - def get(self, image_id): + def post(self, image_id): """ COCO data test """ if not DEXTR_LOADED: @@ -43,7 +45,9 @@ def get(self, image_id): args = dextr_args.parse_args() points = args.get('points') - + padding = args.get('padding') + threshold = args.get('threshold') + if len(points) != 4: return {"message": "Invalid points entered"}, 400 @@ -52,7 +56,6 @@ def get(self, image_id): return {"message": "Invalid image ID"}, 400 image = Image.open(image_model.path) - result = dextr.predict_mask(image, points) return { "segmentaiton": Mask(result).polygons().segmentation } diff --git a/client/src/components/annotator/panels/DEXTRPanel.vue b/client/src/components/annotator/panels/DEXTRPanel.vue new file mode 100755 index 00000000..e24eacd1 --- /dev/null +++ b/client/src/components/annotator/panels/DEXTRPanel.vue @@ -0,0 +1,36 @@ + + + diff --git a/client/src/components/annotator/tools/DEXTRTool.vue b/client/src/components/annotator/tools/DEXTRTool.vue index 379d9253..0f970dd4 100644 --- a/client/src/components/annotator/tools/DEXTRTool.vue +++ b/client/src/components/annotator/tools/DEXTRTool.vue @@ -1,6 +1,7 @@ diff --git a/client/src/views/Annotator.vue b/client/src/views/Annotator.vue index 00032f85..3d14968f 100755 --- a/client/src/views/Annotator.vue +++ b/client/src/views/Annotator.vue @@ -48,6 +48,7 @@ From dc3511525591ab3f084b6909f478ea24373475fd Mon Sep 17 00:00:00 2001 From: Justin Brooks Date: Sun, 17 Mar 2019 13:07:40 -0400 Subject: [PATCH 5/6] Linitng --- client/src/components/annotator/panels/DEXTRPanel.vue | 5 +---- client/src/components/annotator/tools/DEXTRTool.vue | 10 +--------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/client/src/components/annotator/panels/DEXTRPanel.vue b/client/src/components/annotator/panels/DEXTRPanel.vue index e24eacd1..ab185705 100755 --- a/client/src/components/annotator/panels/DEXTRPanel.vue +++ b/client/src/components/annotator/panels/DEXTRPanel.vue @@ -18,14 +18,11 @@