Demo Site
For every model, a demo site should be created to demonstrate the reliability and use of the service, especially to product owners & clients. Streamlit is an amazing python library used to create ML demo sites fast, while providing a beautiful & consistent template.
To facilitate deployment, we should always ensure both requirements.txt
& Dockerfile
are created & tested.
Below is an example how we can develop a simple object detection demo site.
"""streamlit server for demo site"""
import json
import time
import requests
import streamlit as st
from PIL import Image
from utils_image import encode_image, draw_on_image, json2array_yolo
# streamlit settings
st.set_page_config(page_title='Demo Site')
json_data = \
{
"requests": [
{
"features": [
{
"maxResults": 20,
"min_height": 0.03,
"min_width": 0.03,
"score_th": 0.3,
"nms_iou": 0.4,
}
],
"image": {
"content": None
}
}
]
}
def hide_navbar():
"""hide navbar so its not apparent this is from streamlit"""
hide_streamlit_style = """
<style>
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
def send2api(api, image, json_data, token, \
maxfeatures, min_height, min_width, \
score_th, nms_iou):
"""Sends JSON request & recieve a JSON response
Args
----
api (str): API endpoint
image (image file): opened image file
json_data (dict): json request template
token (str): API token
maxfeatures (int): max no. of objects to detect in image
min_height (float): min height of bounding box (relative to H) to be included
min_width (float): min width of bounding box (relative to W) to be included
score_th (float): min prediciton score for bounding box to be included
nms_iou (float): intersection over union, for non-max suppression
Returns
-------
json_response (dict): API response
"""
base64_bytes = encode_image(image)
token = {"X-Bedrock-Api-Token": token}
json_data["requests"][0]["image"]["content"] = base64_bytes
json_data["requests"][0]["features"][0]["maxResults"] = maxfeatures
json_data["requests"][0]["features"][0]["min_height"] = min_height
json_data["requests"][0]["features"][0]["min_width"] = min_width
json_data["requests"][0]["features"][0]["score_th"] = score_th
json_data["requests"][0]["features"][0]["nms_iou"] = nms_iou
response = requests.post(api, headers=token, json=json_data)
# response = requests.post(api, json=json_data)
json_response = response.content.decode('utf-8')
json_response = json.loads(json_response)
return json_response
def main(api):
"""design streamlit fronend"""
st.title("SafetyCone Detection")
token = st.text_input("API Token", type="password")
uploaded_file = st.file_uploader("Upload an image.")
if uploaded_file is not None and api != "":
image = Image.open(uploaded_file)
# header
st.subheader("Uploaded Image")
st.image(image, width=400)
# sidebar
st.sidebar.title("Change Parameters")
maxfeatures = st.sidebar.slider("Max Features", min_value=1, max_value=50, value=20, step=1)
min_height = st.sidebar.slider("Min Height", min_value=0.01, max_value=0.05, value=0.03, step=0.01)
min_width = st.sidebar.slider("Min Width", min_value=0.01, max_value=0.05, value=0.03, step=0.01)
score_th = st.sidebar.slider("Score Th", min_value=0.1, max_value=0.5, value=0.3, step=0.1)
nms_iou = st.sidebar.slider("NMS IOU", min_value=0.1, max_value=0.5, value=0.4, step=0.1)
if st.button("Send API Request"):
st.title("Results")
# send request
start_time = time.time()
json_response = send2api(api, image, json_data, token, \
maxfeatures, min_height, min_width, \
score_th, nms_iou)
latency = time.time() - start_time
st.write("**Est. latency = `{:.3f} s`**".format(latency))
# result image
st.subheader("Visualize Output")
bboxes_json = json_response["safetycone"]["boundingPoly"]["normalizedVertices"]
bboxes_array = json2array_yolo(bboxes_json)
class_mapper = {0: "safetycone"}
image_res = draw_on_image(image, bboxes_array, class_mapper)
st.image(image_res, width=400)
# api response
st.subheader("API Response")
st.json(json.dumps(json_response, indent=2))
if __name__ == "__main__":
api = "http://localhost:5000"
hide_navbar()
main(api)
To launch the app, use streamlit run app.py
.
After uploading a picture, the results are shown as such.