Skip to content

Commit f164b6c

Browse files
authored
DEXTR tool (#147)
* Installed dextr python package * Created DEXTR web client tool * Finished webclient DEXTR tool * Fixed DEXTR tool bugs * Linitng * Adding build to production
1 parent f482059 commit f164b6c

File tree

9 files changed

+216
-12
lines changed

9 files changed

+216
-12
lines changed

app/api/models.py

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,74 @@
11
from flask_restplus import Namespace, Resource, reqparse
22
from werkzeug.datastructures import FileStorage
3-
from imantics import Image as ImanticsImage
3+
from imantics import Mask
44
from flask_login import login_required
55
from ..config import Config
66
from PIL import Image
7+
from ..models import ImageModel
78

89
import os
910

10-
MODEL_LOADED = len(Config.MASK_RCNN_FILE) != 0 and os.path.isfile(Config.MASK_RCNN_FILE)
11-
12-
if MODEL_LOADED:
11+
MASKRCNN_LOADED = os.path.isfile(Config.MASK_RCNN_FILE)
12+
if MASKRCNN_LOADED:
1313
from ..util.mask_rcnn import model as maskrcnn
1414
else:
1515
print("MaskRCNN model is disabled.", flush=True)
1616

17+
DEXTR_LOADED = os.path.isfile(Config.DEXTR_FILE)
18+
if DEXTR_LOADED:
19+
from ..util.dextr import model as dextr
20+
else:
21+
print("DEXTR model is disabled.", flush=True)
22+
1723
api = Namespace('model', description='Model related operations')
1824

1925

2026
image_upload = reqparse.RequestParser()
2127
image_upload.add_argument('image', location='files', type=FileStorage, required=True, help='Image')
2228

29+
dextr_args = reqparse.RequestParser()
30+
dextr_args.add_argument('points', location='json', type=list, required=True)
31+
dextr_args.add_argument('padding', location='json', type=int, default=50)
32+
dextr_args.add_argument('threshold', location='json', type=int, default=80)
33+
34+
35+
@api.route('/dextr/<int:image_id>')
36+
class MaskRCNN(Resource):
37+
38+
@login_required
39+
@api.expect(dextr_args)
40+
def post(self, image_id):
41+
""" COCO data test """
42+
43+
if not DEXTR_LOADED:
44+
return {"disabled": True, "message": "DEXTR is disabled"}, 400
45+
46+
args = dextr_args.parse_args()
47+
points = args.get('points')
48+
padding = args.get('padding')
49+
threshold = args.get('threshold')
50+
51+
if len(points) != 4:
52+
return {"message": "Invalid points entered"}, 400
53+
54+
image_model = ImageModel.objects(id=image_id).first()
55+
if not image_model:
56+
return {"message": "Invalid image ID"}, 400
57+
58+
image = Image.open(image_model.path)
59+
result = dextr.predict_mask(image, points)
60+
61+
return { "segmentaiton": Mask(result).polygons().segmentation }
62+
63+
2364
@api.route('/maskrcnn')
2465
class MaskRCNN(Resource):
2566

2667
@login_required
2768
@api.expect(image_upload)
2869
def post(self):
2970
""" COCO data test """
30-
if not MODEL_LOADED:
71+
if not MASKRCNN_LOADED:
3172
return {"disabled": True, "coco": {}}
3273

3374
args = image_upload.parse_args()

app/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,5 @@ class Config:
3131
# Models
3232
MASK_RCNN_FILE = os.getenv("MASK_RCNN_FILE", "")
3333
MASK_RCNN_CLASSES = os.getenv("MASK_RCNN_CLASSES", "BG")
34+
35+
DEXTR_FILE = os.getenv("DEXTR_FILE", "/models/dextr_pascal-sbd.h5")

app/util/dextr.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from dextr import DEXTR
2+
from ..config import Config
3+
import os
4+
5+
6+
model = DEXTR(nb_classes=1, resnet_layers=101, input_shape=(512, 512), weights_path=Config.DEXTR_FILE,
7+
num_input_channels=4, classifier='psp', sigmoid=True)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<template>
2+
<div v-show="dextr.isActive">
3+
<PanelInputNumber
4+
name="Padding"
5+
min="0"
6+
max="1000"
7+
step="2"
8+
v-model="dextr.settings.padding"
9+
/>
10+
<PanelInputNumber
11+
name="Threshold"
12+
min="0"
13+
max="100"
14+
step="5"
15+
v-model="dextr.settings.threshold"
16+
/>
17+
</div>
18+
</template>
19+
20+
<script>
21+
import PanelInputNumber from "@/components/PanelInputNumber";
22+
23+
export default {
24+
name: "DEXTRPanel",
25+
components: { PanelInputNumber },
26+
props: {
27+
dextr: {
28+
type: Object,
29+
required: true
30+
}
31+
}
32+
};
33+
</script>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<script>
2+
import paper from "paper";
3+
import tool from "@/mixins/toolBar/tool";
4+
import axios from "axios";
5+
6+
export default {
7+
name: "DEXTRTool",
8+
mixins: [tool],
9+
props: {
10+
scale: {
11+
type: Number,
12+
default: 1
13+
}
14+
},
15+
data() {
16+
return {
17+
icon: "fa-crosshairs",
18+
name: "DEXTR",
19+
cursor: "crosshair",
20+
settings: {
21+
padding: 50,
22+
threshold: 80
23+
},
24+
points: []
25+
};
26+
},
27+
methods: {
28+
createPoint(point) {
29+
let paperPoint = new paper.Path.Circle(point, 5);
30+
paperPoint.fillColor = this.$parent.currentAnnotation.color;
31+
paperPoint.data.point = point;
32+
this.points.push(paperPoint);
33+
},
34+
onMouseDown(event) {
35+
this.createPoint(event.point);
36+
}
37+
},
38+
computed: {
39+
isDisabled() {
40+
return this.$parent.current.annotation == -1;
41+
}
42+
},
43+
watch: {
44+
points(newPoints) {
45+
if (newPoints.length == 4) {
46+
let points = this.points;
47+
this.points = [];
48+
49+
let pointsList = [];
50+
let width = this.$parent.image.raster.width / 2;
51+
let height = this.$parent.image.raster.height / 2;
52+
53+
points.forEach(point => {
54+
let pt = point.position;
55+
56+
pointsList.push([
57+
Math.round(width + pt.x),
58+
Math.round(height + pt.y)
59+
]);
60+
});
61+
62+
axios
63+
.post(`/api/model/dextr/${this.$parent.image.id}`, {
64+
points: pointsList,
65+
...this.settings
66+
})
67+
.then(response => {
68+
let segments = response.data.segmentaiton;
69+
let center = new paper.Point(width, height);
70+
71+
let compoundPath = new paper.CompoundPath();
72+
for (let i = 0; i < segments.length; i++) {
73+
let polygon = segments[i];
74+
let path = new paper.Path();
75+
76+
for (let j = 0; j < polygon.length; j += 2) {
77+
let point = new paper.Point(polygon[j], polygon[j + 1]);
78+
path.add(point.subtract(center));
79+
}
80+
path.closePath();
81+
compoundPath.addChild(path);
82+
}
83+
84+
this.$parent.uniteCurrentAnnotation(compoundPath);
85+
})
86+
.finally(() => points.forEach(point => point.remove()));
87+
}
88+
}
89+
}
90+
};
91+
</script>

client/src/views/Annotator.vue

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@
4545
@setcursor="setCursor"
4646
ref="keypoint"
4747
/>
48+
<DEXTRTool
49+
v-model="activeTool"
50+
:scale="image.scale"
51+
@setcursor="setCursor"
52+
ref="dextr"
53+
/>
4854
</div>
4955
<hr />
5056

@@ -171,6 +177,11 @@
171177
:current-annotation="currentAnnotation"
172178
/>
173179
</div>
180+
<div v-if="$refs.dextr != null">
181+
<DEXTRPanel
182+
:dextr="$refs.dextr"
183+
/>
184+
</div>
174185
</div>
175186
</div>
176187
</aside>
@@ -205,6 +216,7 @@ import MagicWandTool from "@/components/annotator/tools/MagicWandTool";
205216
import EraserTool from "@/components/annotator/tools/EraserTool";
206217
import BrushTool from "@/components/annotator/tools/BrushTool";
207218
import KeypointTool from "@/components/annotator/tools/KeypointTool";
219+
import DEXTRTool from "@/components/annotator/tools/DEXTRTool";
208220
209221
import CopyAnnotationsButton from "@/components/annotator/tools/CopyAnnotationsButton";
210222
import CenterButton from "@/components/annotator/tools/CenterButton";
@@ -224,6 +236,7 @@ import MagicWandPanel from "@/components/annotator/panels/MagicWandPanel";
224236
import BrushPanel from "@/components/annotator/panels/BrushPanel";
225237
import EraserPanel from "@/components/annotator/panels/EraserPanel";
226238
import KeypointPanel from "@/components/annotator/panels/KeypointPanel";
239+
import DEXTRPanel from "@/components/annotator/panels/DEXTRPanel";
227240
228241
import { mapMutations } from "vuex";
229242
@@ -255,7 +268,9 @@ export default {
255268
HideAllButton,
256269
ShowAllButton,
257270
KeypointPanel,
258-
AnnotateButton
271+
AnnotateButton,
272+
DEXTRTool,
273+
DEXTRPanel
259274
},
260275
mixins: [toastrs, shortcuts],
261276
props: {

docker/api/Dockerfile

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@ WORKDIR /workspace/
55
# Install python package dependices
66
COPY requirements.txt requirements.txt
77
RUN pip install -r requirements.txt && \
8-
pip install gunicorn[eventlet]==19.9.0 && \
9-
pip install pycocotools
8+
pip install gunicorn[eventlet]==19.9.0 && \
9+
pip install pycocotools
1010

1111
# Install maskrcnn
12-
RUN git clone --single-branch --depth 1 https://github.com/matterport/Mask_RCNN.git $TEMP_MRCNN_DIR /tmp/maskrcnn && \
13-
cd /tmp/maskrcnn && \
14-
pip install -r requirements.txt && \
15-
python3 setup.py install
12+
RUN git clone --single-branch --depth 1 https://github.com/matterport/Mask_RCNN.git /tmp/maskrcnn && \
13+
cd /tmp/maskrcnn && \
14+
pip install -r requirements.txt && \
15+
python3 setup.py install
16+
17+
# Install DEXTR
18+
RUN git clone --single --depth 1 https://github.com/jsbroks/dextr-keras.git /tmp/dextr && \
19+
cd /tmp/dextr && \
20+
pip install -r requirements.txt && \
21+
python setup.py install
1622

1723
COPY ./.git /workspace/.git
1824

docker/production/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ RUN git clone --single-branch --depth 1 https://github.com/matterport/Mask_RCNN.
3737
pip install -r requirements.txt && \
3838
python3 setup.py install
3939

40+
# Install DEXTR
41+
RUN git clone --single --depth 1 https://github.com/jsbroks/dextr-keras.git /tmp/dextr && \
42+
cd /tmp/dextr && \
43+
pip install -r requirements.txt && \
44+
python setup.py install
45+
4046
COPY ./.git /workspace/.git
4147
COPY ./app /workspace/app
4248

models/dextr_pascal_sbd.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/bash
2+
3+
wget https://github.com/jsbroks/dextr-keras/releases/download/v1.0.0/dextr_pascal-sbd.h5

0 commit comments

Comments
 (0)