Skip to content

Commit 7c957d2

Browse files
committed
feat: add mask template deployments
1 parent 41332f8 commit 7c957d2

File tree

18 files changed

+1451
-4
lines changed

18 files changed

+1451
-4
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ workspace-box:
2727
workspace-mask:
2828
python scripts/workspace/mask/workspace.py --save_dir=$(SAVE_DIR) --name=$(NAME)
2929
cp -r scripts/workspace/mask/files/* $(SAVE_DIR)/$(NAME)
30+
python scripts/workspace/mask/replace.py --dir=$(SAVE_DIR)/$(NAME)/deployments --pattern="{{.Name}}" --repl=$(NAME)
3031
cp models/research/object_detection/model_main_tf2.py $(SAVE_DIR)/$(NAME)
3132
cp models/research/object_detection/exporter_main_v2.py $(SAVE_DIR)/$(NAME)
3233
touch $(SAVE_DIR)/$(NAME)/annotations/label_map.pbtxt

scripts/workspace/box/files/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
gen-tfrecord:
2-
python ../../scripts/preprocessing/generate_tfrecord.py \
2+
python generate_tfrecord.py \
33
-x images/train \
44
-l annotations/label_map.pbtxt \
55
-o annotations/train.record
66

7-
python ../../scripts/preprocessing/generate_tfrecord.py \
7+
python generate_tfrecord.py \
88
-x images/val \
99
-l annotations/label_map.pbtxt \
1010
-o annotations/val.record
File renamed without changes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM python:3.8-slim-buster
2+
3+
WORKDIR /app
4+
5+
RUN pip3 install --no-cache-dir Flask pillow tensorflow==2.6.0
6+
7+
COPY . .
8+
RUN rm -rf .vscode README.md Dockerfile Makefile
9+
10+
CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build:
2+
docker build --tag {{.Name}}-flask .
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from enum import Enum
2+
import json
3+
4+
5+
class Response:
6+
def __init__(self, code, message, data=''):
7+
self.code = code
8+
self.data = data
9+
self.message = message
10+
11+
12+
class Error(Enum):
13+
NO_FILE_PART = Response(1000, "No image file part")
14+
NO_SELECTED_FILE = Response(1001, "No selected file")
15+
NO_ALLOWED_FILE = Response(1002, "No allowed file")
16+
17+
18+
def response_data(message, data=''):
19+
res = Response(0, message, data)
20+
return jsonify(res)
21+
22+
23+
def response_err(e):
24+
res = e.value
25+
return jsonify(res)
26+
27+
28+
def jsonify(r):
29+
return json.dumps(r.__dict__, ensure_ascii=False)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from flask import Flask, request, url_for
2+
import os
3+
from api import response_data, response_err, Error
4+
from file import allowed_file, make_upload_dir, uuid_filename
5+
from model import predict_image
6+
7+
app = Flask(__name__)
8+
app.config['JSON_AS_ASCII'] = False
9+
app.config['UPLOAD_FOLDER'] = make_upload_dir()
10+
11+
SERVER_URL = os.getenv('SERVER_URL')
12+
print('SERVER_URL:' + SERVER_URL)
13+
14+
15+
@app.route('/{{.Name}}/v1/predict', methods=['POST'])
16+
def predict():
17+
if 'image' not in request.files:
18+
return response_err(Error.NO_FILE_PART)
19+
20+
f = request.files['image']
21+
if f.filename == '':
22+
return response_err(Error.NO_SELECTED_FILE)
23+
24+
if not allowed_file(f.filename):
25+
return response_err(Error.NO_ALLOWED_FILE)
26+
27+
filename = uuid_filename(f.filename)
28+
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
29+
f.save(file_path)
30+
31+
predict_image(SERVER_URL, file_path, filename)
32+
33+
url = url_for('static', filename=filename)
34+
35+
return response_data('predict successfully', {
36+
"image_url": url,
37+
})
38+
39+
40+
@app.route("/")
41+
def hello_world():
42+
return "It works!"
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import os
2+
import uuid
3+
from werkzeug.utils import secure_filename
4+
5+
# https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types
6+
ALLOWED_EXTENSIONS = {
7+
'apng', 'avif', 'jpg', 'jpeg', 'jfif', 'pjpeg', 'pjp', 'png', 'svg',
8+
'webp', 'bmp', 'ico', 'cur', 'tif', 'tiff'
9+
}
10+
11+
12+
def allowed_file(filename):
13+
return '.' in filename and \
14+
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
15+
16+
17+
def make_upload_dir(subdir='uploads'):
18+
return make_dir(subdir)
19+
20+
21+
def make_save_dir(subdir='static'):
22+
return make_dir(subdir)
23+
24+
25+
def make_dir(subdir):
26+
basepath = os.path.dirname(__file__)
27+
dir = os.path.join(basepath, subdir)
28+
if not os.path.exists(dir):
29+
os.makedirs(dir)
30+
return dir
31+
32+
33+
def file_extension(filename):
34+
_, extension = os.path.splitext(filename)
35+
return extension
36+
37+
38+
def uuid_filename(filename):
39+
extension = file_extension(filename)
40+
uuid_name = uuid.uuid4().hex + extension
41+
uuid_name = secure_filename(uuid_name)
42+
return uuid_name
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import requests
2+
import os
3+
from PIL import Image
4+
import numpy as np
5+
import time
6+
import tensorflow as tf
7+
from object_detection.utils import label_map_util
8+
from object_detection.utils import ops as utils_ops
9+
from object_detection.utils import visualization_utils as viz_utils
10+
11+
from file import make_save_dir
12+
13+
SERVER_URL = 'http://localhost:8501/v1/models/{{.Name}}:predict'
14+
15+
# Labels
16+
BASEPATH = os.path.dirname(os.path.realpath(__file__))
17+
PATH_TO_LABELS = os.path.join(BASEPATH, 'label_map.pbtxt')
18+
category_index = label_map_util.create_category_index_from_labelmap(
19+
PATH_TO_LABELS, use_display_name=True)
20+
21+
22+
def predict_image(server_url, file_path, file_name):
23+
image = Image.open(file_path)
24+
image_np = np.array(image)
25+
26+
detections = request(image_np, server_url)
27+
28+
image_np_with_detections = visualize(image_np, detections)
29+
30+
save_path = os.path.join(make_save_dir(), file_name)
31+
im = Image.fromarray(image_np_with_detections)
32+
im.save(save_path)
33+
34+
return save_path
35+
36+
37+
def request(image_np, server_url=SERVER_URL):
38+
payload = {"inputs": [image_np.tolist()]}
39+
headers = {"content-type": "application/json"}
40+
start = time.time()
41+
res = requests.post(server_url, json=payload, headers=headers)
42+
end = time.time()
43+
print('duration: %.2fs' % (end - start))
44+
json = res.json()
45+
detections = json['outputs']
46+
return detections
47+
48+
49+
def visualize(image_np, detections):
50+
image_np_with_detections = image_np.copy()
51+
52+
# https://github.com/vijaydwivedi75/Custom-Mask-RCNN_TF/blob/master/mask_rcnn_eval.ipynb
53+
# The following processing is only for single image
54+
detection_boxes = tf.squeeze(detections['detection_boxes'], [0])
55+
detection_masks = tf.squeeze(detections['detection_masks'], [0])
56+
# Reframe is required to translate mask from box coordinates to image coordinates and fit the image size.
57+
real_num_detection = tf.cast(detections['num_detections'][0], tf.int32)
58+
detection_boxes = tf.slice(detection_boxes, [0, 0],
59+
[real_num_detection, -1])
60+
detection_masks = tf.slice(detection_masks, [0, 0, 0],
61+
[real_num_detection, -1, -1])
62+
detection_masks_reframed = utils_ops.reframe_box_masks_to_image_masks(
63+
detection_masks, detection_boxes, image_np.shape[0], image_np.shape[1])
64+
detection_masks_reframed = tf.cast(
65+
tf.greater(detection_masks_reframed, 0.5), tf.uint8)
66+
# Follow the convention by adding back the batch dimension
67+
detections['detection_masks'] = tf.expand_dims(detection_masks_reframed, 0)
68+
69+
detections['num_detections'] = int(detections['num_detections'][0])
70+
detections['detection_classes'] = np.array(
71+
detections['detection_classes'][0], dtype=np.uint8)
72+
detections['detection_boxes'] = np.array(detections['detection_boxes'][0])
73+
detections['detection_scores'] = np.array(
74+
detections['detection_scores'][0])
75+
detections['detection_masks'] = detections['detection_masks'][0].numpy()
76+
77+
viz_utils.visualize_boxes_and_labels_on_image_array(
78+
image_np_with_detections,
79+
detections['detection_boxes'],
80+
detections['detection_classes'],
81+
detections['detection_scores'],
82+
category_index,
83+
instance_masks=detections.get('detection_masks'),
84+
use_normalized_coordinates=True,
85+
max_boxes_to_draw=200,
86+
min_score_thresh=.30)
87+
88+
return image_np_with_detections
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
server {
2+
listen 80;
3+
listen 443 ssl;
4+
server_name api.makeoptim.com;
5+
ssl_certificate /home/lindaoqiang/keys/api.makeoptim.com.pem;
6+
ssl_certificate_key /home/lindaoqiang/keys/api.makeoptim.com.key;
7+
8+
location / {
9+
proxy_set_header Host $host;
10+
proxy_set_header X-Real-IP $remote_addr;
11+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
12+
proxy_pass http://127.0.0.1:5000/;
13+
}
14+
}

0 commit comments

Comments
 (0)