Skip to content

Commit

Permalink
Minor Improvements (#6)
Browse files Browse the repository at this point in the history
Suppress Tensorflow warnings.
Update predict notebook.
Reduce gunicorn processes to save memory.
Added print statements. Students were confused if a cell had executed.
Cleanup.
  • Loading branch information
cfchase authored Dec 24, 2021
1 parent 92e5468 commit bde9062
Show file tree
Hide file tree
Showing 6 changed files with 507 additions and 83 deletions.
2 changes: 1 addition & 1 deletion 0_sandbox.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.6"
"version": "3.8.8"
}
},
"nbformat": 4,
Expand Down
46 changes: 28 additions & 18 deletions 1_explore.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@
"from PIL import ImageDraw\n",
"from PIL import ImageFont\n",
"\n",
"import matplotlib.pyplot as plt"
"import matplotlib.pyplot as plt\n",
"import os\n",
"\n",
"os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'\n",
"\n",
"print('Imported libraries')"
]
},
{
Expand All @@ -49,7 +54,9 @@
"source": [
"import boto3\n",
"from botocore import UNSIGNED\n",
"from botocore.client import Config"
"from botocore.client import Config\n",
"\n",
"print('Imported s3 libraries')"
]
},
{
Expand All @@ -59,7 +66,9 @@
"outputs": [],
"source": [
"s3 = boto3.client('s3', config=Config(signature_version=UNSIGNED))\n",
"s3.download_file('dog-detector', 'twodogs.jpg', 'twodogs.jpg')"
"s3.download_file('dog-detector', 'twodogs.jpg', 'twodogs.jpg')\n",
"\n",
"print('Downloaded twodogs.jpg')"
]
},
{
Expand Down Expand Up @@ -105,7 +114,9 @@
"def load_img(path):\n",
" img = tf.io.read_file(path)\n",
" img = tf.image.decode_jpeg(img, channels=3)\n",
" return img"
" return img\n",
"\n",
"print('Defined load_img function')"
]
},
{
Expand Down Expand Up @@ -135,7 +146,9 @@
"source": [
"model_dir = 'models/openimages_v4_ssd_mobilenet_v2_1'\n",
"saved_model = tf.saved_model.load(model_dir)\n",
"detector = saved_model.signatures['default']"
"detector = saved_model.signatures['default']\n",
"\n",
"print('Loaded model into the detector variable')"
]
},
{
Expand All @@ -144,7 +157,8 @@
"metadata": {},
"outputs": [],
"source": [
"converted_img = tf.image.convert_image_dtype(tf_dogs, tf.float32)[tf.newaxis, ...]"
"converted_img = tf.image.convert_image_dtype(tf_dogs, tf.float32)[tf.newaxis, ...]\n",
"converted_img"
]
},
{
Expand Down Expand Up @@ -250,7 +264,10 @@
" font,\n",
" display_str_list=[display_str])\n",
" np.copyto(image, np.array(image_pil))\n",
" return image"
" return image\n",
"\n",
"\n",
"print('Defined image display functions')"
]
},
{
Expand All @@ -263,15 +280,8 @@
"\n",
"image_with_boxes = draw_boxes(\n",
" tf_dogs.numpy(), result[\"detection_boxes\"],\n",
" result[\"detection_class_entities\"], result[\"detection_scores\"])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
" result[\"detection_class_entities\"], result[\"detection_scores\"])\n",
"\n",
"display_image(image_with_boxes)"
]
},
Expand Down Expand Up @@ -348,9 +358,9 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.6"
"version": "3.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
}
191 changes: 148 additions & 43 deletions 2_predict.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"source": [
"# Object Detection\n",
"\n",
"In notebook [1_explore.ipynb](1_explore.ipynb) we saw how to use a pre-trained object detection model to identify objects in a static image. In this notebook, we consider which parts of the code we used are crucial in making a prediction, and remove the code which was merely used to explore the data. \n",
"In notebook [1_explore.ipynb](1_explore.ipynb) we saw how to use a pre-trained object detection model to identify objects in a static image. In this notebook, we consider which parts of the code we used are crucial in making a prediction, and remove the code which was merely used to explore the data.\n",
"\n",
"You might have noticed that this folder we are working in contains afew files we haven't talked about. This 'project' has been set up for us by our Super Star Application Developer, and contains everything we need to easily go from experiment to application. \n",
"You might have noticed that this folder we are working in contains afew files we haven't talked about. This 'project' has been set up for us by our Super Star Application Developer, and contains everything we need to easily go from experiment to application.\n",
"\n",
"The process we will be using to create our application is called source-to-image, or s2i. Don't worry if you've never heard of it! We will step you through what you need to do! For now, take a look at the way in which the project is organised:\n",
"\n",
Expand Down Expand Up @@ -58,11 +58,13 @@
},
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"tags": []
},
"source": [
"## Create a Predict Function\n",
"\n",
"Extract the prediction logic into a standalone python file, `prediction.py` in a `predict` function. Also, make sure `requirements.txt` is updated with any additional packages you've used and need for prediction."
"Extract the prediction logic into a standalone function called `predict`."
]
},
{
Expand All @@ -73,6 +75,9 @@
"source": [
"import tensorflow as tf\n",
"import base64\n",
"import os\n",
"\n",
"os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'\n",
"\n",
"model_dir = 'models/openimages_v4_ssd_mobilenet_v2_1'\n",
"saved_model = tf.saved_model.load(model_dir)\n",
Expand All @@ -84,46 +89,20 @@
" img_bytes = base64.decodebytes(base64img.encode())\n",
" detections = detect(img_bytes)\n",
" cleaned = clean_detections(detections)\n",
" \n",
"\n",
" return { 'detections': cleaned }\n",
"\n",
"\n",
"def detect(img): \n",
"def detect(img):\n",
" image = tf.image.decode_jpeg(img, channels=3)\n",
" converted_img = tf.image.convert_image_dtype(image, tf.float32)[tf.newaxis, ...]\n",
" result = detector(converted_img)\n",
" num_detections = len(result[\"detection_scores\"])\n",
" \n",
"\n",
" output_dict = {key:value.numpy().tolist() for key, value in result.items()}\n",
" output_dict['num_detections'] = num_detections\n",
" \n",
" return output_dict\n",
"\n",
"\n",
"def clean_detections(detections):\n",
" cleaned = []\n",
" # max_boxes = 10\n",
" # num_detections = min(detections['num_detections'], max_boxes)\n",
" num_detections = detections['num_detections']\n",
"\n",
" for i in range(0, num_detections):\n",
" label = detections['detection_class_entities'][i].decode('utf-8')\n",
" score = detections['detection_scores'][i]\n",
" if score > 0.3:\n",
" d = {\n",
" 'box': {\n",
" 'yMin': detections['detection_boxes'][i][0],\n",
" 'xMin': detections['detection_boxes'][i][1],\n",
" 'yMax': detections['detection_boxes'][i][2],\n",
" 'xMax': detections['detection_boxes'][i][3]\n",
" },\n",
" 'class': label,\n",
" 'label': label,\n",
" 'score': score,\n",
" }\n",
" cleaned.append(d)\n",
"\n",
" return cleaned\n",
" return output_dict\n",
"\n",
"\n",
"def clean_detections(detections):\n",
Expand All @@ -148,6 +127,17 @@
" return cleaned"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": []
},
"source": [
"### Testing the Prediction Function\n",
"\n",
"Let's try our new `predict` function and make sure it works"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -156,29 +146,144 @@
"source": [
"import json\n",
"\n",
"with open('sample-requests/twodogs.json') as json_file:\n",
" data = json.load(json_file)\n",
" \n",
"result = predict(data)\n",
"print(result['detections'])"
"#image file location.\n",
"my_image = 'twodogs.jpg'\n",
"\n",
"with open(my_image, \"rb\") as image_file:\n",
" encoded_image = base64.b64encode(image_file.read()).decode('utf-8')\n",
"content = {\"image\": encoded_image}\n",
"\n",
"result = predict(content)\n",
"result['detections']"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": []
},
"source": [
"### Visualizing the Results\n",
"\n",
"That JSON is a bit hard to read, let's take a look at what it would look like if we used that data to draw the boxes\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"import numpy as np\n",
"from PIL import Image\n",
"from PIL import ImageColor\n",
"from PIL import ImageDraw\n",
"from PIL import ImageFont\n",
"\n",
"\n",
"def display_image(image):\n",
" fig = plt.figure(figsize=(10, 10))\n",
" plt.grid(False)\n",
" plt.imshow(image)\n",
"\n",
"\n",
"def draw_bounding_box_on_image(image,\n",
" ymin,\n",
" xmin,\n",
" ymax,\n",
" xmax,\n",
" color,\n",
" font,\n",
" thickness=4,\n",
" display_str_list=()):\n",
" \"\"\"Adds a bounding box to an image.\"\"\"\n",
" draw = ImageDraw.Draw(image)\n",
" im_width, im_height = image.size\n",
" (left, right, top, bottom) = (xmin * im_width, xmax * im_width,\n",
" ymin * im_height, ymax * im_height)\n",
" draw.line([(left, top), (left, bottom), (right, bottom), (right, top),\n",
" (left, top)], width=thickness, fill=color)\n",
"\n",
" display_str_heights = [font.getsize(ds)[1] for ds in display_str_list]\n",
" total_display_str_height = (1 + 2 * 0.05) * sum(display_str_heights)\n",
" if top > total_display_str_height:\n",
" text_bottom = top\n",
" else:\n",
" text_bottom = top + total_display_str_height\n",
"\n",
" for display_str in display_str_list[::-1]:\n",
" text_width, text_height = font.getsize(display_str)\n",
" margin = np.ceil(0.05 * text_height)\n",
" draw.rectangle([(left, text_bottom - text_height - 2 * margin),\n",
" (left + text_width, text_bottom)], fill=color)\n",
" draw.text((left + margin, text_bottom - text_height - margin),\n",
" display_str, fill=\"black\", font=font)\n",
" text_bottom -= text_height - 2 * margin\n",
"\n",
"\n",
"def draw_boxes(image, detections):\n",
" \"\"\"Overlay labeled boxes on an image with formatted scores and label names.\"\"\"\n",
" colors = list(ImageColor.colormap.values())\n",
" class_colors = {}\n",
" font = ImageFont.load_default()\n",
" image_pil = Image.open(image)\n",
"\n",
" for d in detections:\n",
" display_str = \"{}: {}%\".format(d['class'], int(100 * d['score']))\n",
" if not class_colors.get(d['class']):\n",
" class_colors[d['class']] = colors[hash(d['class']) % len(colors)]\n",
" color = class_colors.get(d['class'])\n",
" draw_bounding_box_on_image(\n",
" image_pil,\n",
" d['box']['yMin'],\n",
" d['box']['xMin'],\n",
" d['box']['yMax'],\n",
" d['box']['xMax'],\n",
" color,\n",
" font,\n",
" display_str_list=[display_str])\n",
" return image_pil\n",
" image_pil.show()\n",
"\n",
"\n",
"draw_boxes(my_image, result['detections'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So, you can see that we only need a few lines of code to be able to make a prediction - much of the code in the first notebook was for exploration only. \n",
"## Extract the Predict function into Python files\n",
"\n",
"Now that we've got a working function, extract the prediction logic into a standalone python file, `prediction.py` in a `predict` function. Also, make sure `requirements.txt` is updated with any additional packages you've used and need for prediction.\n",
"\n",
"## Test the function from your Python file\n",
"\n",
"We've put the code from this notebook into the `prediction.py` file, as that is where the source-to-image builder will look for our prediction function. "
"We can make sure the extraction worked properly by loading the function from our `prediction.py` file and testing it out to make sure it works the same."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
"source": [
"import json\n",
"from prediction import predict\n",
"\n",
"#image file location.\n",
"my_image = 'twodogs.jpg'\n",
"\n",
"with open(my_image, \"rb\") as image_file:\n",
" encoded_image = base64.b64encode(image_file.read()).decode('utf-8')\n",
"content = {\"image\": encoded_image}\n",
"\n",
"result = predict(content)\n",
"print(result['detections'])\n",
"draw_boxes(my_image, result['detections'])"
]
},
{
"cell_type": "code",
Expand Down Expand Up @@ -212,7 +317,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.6"
"version": "3.8.8"
}
},
"nbformat": 4,
Expand Down
Loading

0 comments on commit bde9062

Please sign in to comment.