Luu commited on
Commit
d173589
·
1 Parent(s): 26f1fef

Added files except model files, model is too big

Browse files
Files changed (7) hide show
  1. app.py +151 -0
  2. internship.md +205 -0
  3. label_map.pbtxt +10 -0
  4. packages.txt +3 -0
  5. pipeline.config +127 -0
  6. python_tasks.ipynb +0 -0
  7. requirements.txt +5 -0
app.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ from pil import Image
4
+ import streamlit as st
5
+ import tensorflow as tf
6
+ from tensorflow.keras.models import load_model
7
+
8
+ # most of this code has been obtained from Daature's prediction script
9
+ # https://github.com/datature/resources/blob/main/scripts/bounding_box/prediction.py
10
+
11
+ st.set_option('deprecation.showfileUploaderEncoding', False)
12
+
13
+ @st.cache(allow_output_mutation=True)
14
+ def load_model():
15
+ return tf.saved_model.load('./saved_model')
16
+
17
+ def load_label_map(label_map_path):
18
+ """
19
+ Reads label map in the format of .pbtxt and parse into dictionary
20
+ Args:
21
+ label_map_path: the file path to the label_map
22
+ Returns:
23
+ dictionary with the format of {label_index: {'id': label_index, 'name': label_name}}
24
+ """
25
+ label_map = {}
26
+
27
+ with open(label_map_path, "r") as label_file:
28
+ for line in label_file:
29
+ if "id" in line:
30
+ label_index = int(line.split(":")[-1])
31
+ label_name = next(label_file).split(":")[-1].strip().strip('"')
32
+ label_map[label_index] = {"id": label_index, "name": label_name}
33
+ return label_map
34
+
35
+ def predict_class(image, model):
36
+ image = tf.cast(image, tf.float32)
37
+ image = tf.image.resize(image, [150, 150])
38
+ image = np.expand_dims(image, axis = 0)
39
+ return model.predict(image)
40
+
41
+ def plot_boxes_on_img(color_map, classes, bboxes, image_origi, origi_shape):
42
+ for idx, each_bbox in enumerate(bboxes):
43
+ color = color_map[classes[idx]]
44
+
45
+ ## Draw bounding box
46
+ cv2.rectangle(
47
+ image_origi,
48
+ (int(each_bbox[1] * origi_shape[1]),
49
+ int(each_bbox[0] * origi_shape[0]),),
50
+ (int(each_bbox[3] * origi_shape[1]),
51
+ int(each_bbox[2] * origi_shape[0]),),
52
+ color,
53
+ 2,
54
+ )
55
+ ## Draw label background
56
+ cv2.rectangle(
57
+ image_origi,
58
+ (int(each_bbox[1] * origi_shape[1]),
59
+ int(each_bbox[2] * origi_shape[0]),),
60
+ (int(each_bbox[3] * origi_shape[1]),
61
+ int(each_bbox[2] * origi_shape[0] + 15),),
62
+ color,
63
+ -1,
64
+ )
65
+ ## Insert label class & score
66
+ cv2.putText(
67
+ image_origi,
68
+ "Class: {}, Score: {}".format(
69
+ str(category_index[classes[idx]]["name"]),
70
+ str(round(scores[idx], 2)),
71
+ ),
72
+ (int(each_bbox[1] * origi_shape[1]),
73
+ int(each_bbox[2] * origi_shape[0] + 10),),
74
+ cv2.FONT_HERSHEY_SIMPLEX,
75
+ 0.3,
76
+ (0, 0, 0),
77
+ 1,
78
+ cv2.LINE_AA,
79
+ )
80
+ return image_origi
81
+
82
+
83
+ # Webpage code starts here
84
+
85
+ st.title('Banana ripeness detection 🍌')
86
+ st.text('made by XXX') #TODO change with your name
87
+ st.markdown('## Find out if a banana is too ripe!')
88
+
89
+ with st.spinner('Model is being loaded...'):
90
+ model = load_model()
91
+
92
+ # ask user to upload an image
93
+ file = st.file_uploader("Upload an image of a banana", type=["jpg", "png"])
94
+
95
+ if file is None:
96
+ st.text('Waiting for upload...')
97
+ else:
98
+ st.text('Running inference...')
99
+ # open image
100
+ test_image = Image.open(file).convert("RGB")
101
+ origi_shape = np.asarray(test_image).shape
102
+ # resize image to default shape
103
+ default_shape = 320
104
+ image_resized = np.array(test_image.resize((default_shape, default_shape)))
105
+
106
+ ## Load color map
107
+ category_index = load_label_map("./label_map.pbtxt")
108
+ # color of each label. check label_map.pbtxt to check the index for each class
109
+ # TODO Add more colors if there are more classes
110
+ color_map = {
111
+ 2: [255, 0, 0], # overripe -> red
112
+ 1: [0, 255, 0] # ripe -> green
113
+ }
114
+
115
+ ## The model input needs to be a tensor
116
+ input_tensor = tf.convert_to_tensor(image_resized)
117
+ ## The model expects a batch of images, so add an axis with `tf.newaxis`.
118
+ input_tensor = input_tensor[tf.newaxis, ...]
119
+
120
+ ## Feed image into model and obtain output
121
+ detections_output = model(input_tensor)
122
+ num_detections = int(detections_output.pop("num_detections"))
123
+ detections = {key: value[0, :num_detections].numpy() for key, value in detections_output.items()}
124
+ detections["num_detections"] = num_detections
125
+
126
+ ## Filter out predictions below threshold
127
+ # if threshold is higher, there will be fewer predictions
128
+ # TODO change this number to see how the predictions change
129
+ confidence_threshold = 0.5
130
+ indexes = np.where(detections["detection_scores"] > confidence_threshold)
131
+
132
+ ## Extract predicted bounding boxes
133
+ bboxes = detections["detection_boxes"][indexes]
134
+ # there are no predicted boxes
135
+ if len(bboxes) == 0:
136
+ st.error('No boxes predicted')
137
+ # there are predicted boxes
138
+ else:
139
+ st.success('Boxes predicted')
140
+ classes = detections["detection_classes"][indexes].astype(np.int64)
141
+ scores = detections["detection_scores"][indexes]
142
+
143
+ # plot boxes and labels on image
144
+ image_origi = np.array(Image.fromarray(image_resized).resize((origi_shape[1], origi_shape[0])))
145
+ image_origi = plot_boxes_on_img(color_map, classes, bboxes, image_origi, origi_shape)
146
+
147
+ # show image in web page
148
+ st.image(Image.fromarray(image_origi), caption="Image with predictions", width=400)
149
+ st.markdown("### Predicted boxes")
150
+ for idx in range(len((bboxes))):
151
+ st.markdown(f"* Class: {str(category_index[classes[idx]]['name'])}, confidence score: {str(round(scores[idx], 2))}")
internship.md ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Luzie internship
2
+
3
+ * This document contains steps and relevant links to do the steps, but you will still have to read through the links and find your way around the task. If you encounter a problem or an error, search through the documentation or Google it and if you are still stuck after 30 mins, then ask :)
4
+ * At the bottom of each section there are terms that might be new to you. If you don't know them, try to search for their meaning and feel free to write down their definition. There are also some questions included that you can try to answer yourself or we can discuss them at the end of each section
5
+
6
+ - [Luzie internship](#luzie-internship)
7
+ - [0. Introduction](#0-introduction)
8
+ - [1. Create dataset](#1-create-dataset)
9
+ - [2. Label dataset](#2-label-dataset)
10
+ - [3. Train model](#3-train-model)
11
+ - [4. Create website](#4-create-website)
12
+ - [5. Host it in the cloud](#5-host-it-in-the-cloud)
13
+ - [6. Improve system](#6-improve-system)
14
+ - [Resources](#resources)
15
+
16
+ ## 0. Introduction
17
+
18
+ * The goal of this internship is to gather a dataset of banana images, label them, train a machine learning model, and show the model in a website
19
+ * The Machine Learning goal of this project is to determine whether a banana is **ripe** or **edible**
20
+ * In the end we will have something like: https://huggingface.co/spaces/anebz/test
21
+
22
+ ---
23
+
24
+ Basic new terms and questions:
25
+
26
+ * Machine Learning
27
+ * Dataset
28
+ * Model
29
+ * Train a model
30
+ * Classes in machine learning
31
+ * Why only 2 classes? (ripe and edible)
32
+
33
+ ## 1. Create dataset
34
+
35
+ Create a dataset with images relevant for our project, in this case bananas. Since we want to detect **ripe** and **edible** bananas, we need images of both types. Deep neural networks need thousands of images to learn the patterns in the images, but in this project we will use pre-trained models. With only a few of our images, we can obtain good results. To obtain decent results, we should have at least 50 images of each class.
36
+
37
+ This section ends when we have a folder of images in our computer.
38
+
39
+ ---
40
+
41
+ New terms and questions:
42
+
43
+ * Deep neural networks
44
+ * Pre-trained model
45
+ * Why 50 images and not less?
46
+
47
+ ## 2. Label dataset
48
+
49
+ Next step is to label images, to mark if the banana is ripe or edible. If we only mark the class in each image, we are doing classification. But in Computer Vision, a popular task is object detection. This means each image needs two markings: a box where the object is, and a label. To practice object detection, we will mark each image with the box and the label.
50
+
51
+ ![ ](https://cv-tricks.com/wp-content/uploads/2021/08/image1.jpeg)
52
+
53
+ We will use the tool Datature: https://datature.io/
54
+
55
+ It's a startup that created a web interface to annotate images and train computer vision models. The great part of using this is that we don't need any code at all. Resources:
56
+
57
+ * Blogpost about labeling and training a face mask detection model: https://datature.io/blog/train-and-visualize-face-mask-detection-model
58
+ * Datature Nexus documentation: https://docs.datature.io/
59
+
60
+ Use the resources above to find out how to:
61
+
62
+ * Create Datature Nexus account (free trial)
63
+ * Create a project
64
+ * Upload images
65
+ * Create classes
66
+ * Annotate images
67
+ * Create rectangular boxes in images
68
+ * Assign a class to each box
69
+
70
+ This section is finished when all the images have been annotated and reviewed in Nexus.
71
+
72
+ ---
73
+
74
+ New terms and questions:
75
+
76
+ * Computer vision
77
+ * Object detection
78
+ * Bounding box
79
+ * Tag distribution
80
+ * Risks of class imbalance
81
+
82
+ ## 3. Train model
83
+
84
+ Once we have our images, we can train our model! We will have to create a 'Workflow' in Nexus. Try to use the documentation and blogpost to do the following steps:
85
+
86
+ * Build training workflow
87
+ * Select train-test split ratio
88
+ * Select augmentation
89
+ * Model settings
90
+ * Train model
91
+ * Monitor model
92
+ * Loss
93
+ * Precision and recall
94
+ * Export model
95
+
96
+ This section is finished when there is a .zip exported in the computer with the trained model parameters.
97
+
98
+ ---
99
+
100
+ New terms and questions:
101
+
102
+ * Train-test split ratio
103
+ * Augmentation
104
+ * Checkpoint
105
+ * Model training loss
106
+ * Should the loss go up/down?
107
+ * What pre-trained model did the workflow use?
108
+ * Precision and recall
109
+ * Should precision and recall go up/down?
110
+
111
+ ## 4. Create website
112
+
113
+ We will use Streamlit to create the website: https://streamlit.io/. It uses Python to create a website.
114
+
115
+ You can find the website application prepared for you in `app.py`. That is the Python file that uses the Streamlit library to do the following steps:
116
+
117
+ * Load model from the `saved_model/` folder
118
+ * User interface for the user to upload an image
119
+ * Open image, resize, convert to correct format for the model
120
+ * Obtain results from the model prediction, process the data
121
+ * Iterate prediction boxes and plot them on top of the image
122
+ * Show the result image and the prediction boxes
123
+
124
+ You are not required to write any code, everything is done for you and the code should work. However, please read through the Python file and the comments, and try to have a general understand of what the code does. Check especially the #TODO lines which include parameters that we might have to change depending on our project settings.
125
+
126
+ If you have Python installed in your computer, you can install Streamlit from the command line:
127
+
128
+ ```bash
129
+ pip install streamlit
130
+ ```
131
+
132
+ Install the libraries that we need to run the Streamlit app. These libraries are in the file `requirements.txt`:
133
+
134
+ ```bash
135
+ pip install -r requirements.txt
136
+ ```
137
+
138
+ And then run the app.py file:
139
+
140
+ ```bash
141
+ cd path/to/project/
142
+ streamlit run app.py
143
+ ```
144
+
145
+ This will open a new window where we can see the website and interact with it: upload images, and check the results.
146
+
147
+ This section is done when the code is more or less understood, and we have managed to run the app locally and interact with the model in our browser.
148
+
149
+ ---
150
+
151
+ New terms and questions:
152
+
153
+ * What does streamlit do?
154
+ * What is pip?
155
+ * What is the confidence threshold?
156
+
157
+ ## 5. Host it in the cloud
158
+
159
+ For now the script runs in our computer, but we would like to put it in the Internet so anyone can just access a link and get predictions from the model. There are many ways to deploy an app to the cloud, we will use a simple easy way: Huggingface Spaces: https://huggingface.co/spaces/. This is a website from Huggingface where people can showcase their models and interact with them. Sort the spaces by 'Most liked' and explore some of the spaces :)
160
+
161
+ 1. Create Huggingface account
162
+ 2. Create a Space
163
+ 3. Write a name for the space. Remember, this website will be public, so choose a name that matches what the app does! For example: banana-analysis or something like that
164
+ 4. Select Streamlit as the Space SDK
165
+ 5. Choose Public
166
+ 6. When the space is done, clone the repository in your local computer: git clone ...
167
+ 7. Open the folder in Visual Studio code, open README.md and change the emoji for the project
168
+ 8. Copy the model files (saved_model/ folder, label_map.pbtxt file) into this folder
169
+ 9. Copy app.py and requirements.txt into this folder
170
+
171
+ Once all the files we need are in the folder, we can push this to the Huggingface Space. Open Git bash and input these commands:
172
+
173
+ ```bash
174
+ git add .
175
+ git commit -m "Added files"
176
+ git push
177
+ ```
178
+
179
+ The app will take some time to upload the files, especially the model files. After the git push is complete, the Space will take some minutes to build the application and show our Streamlit App in the Huggingface Space.
180
+
181
+ This section is finished when the Streamlit app is displayed in the Space and we can upload an image and obtain predictions.
182
+
183
+ ---
184
+
185
+ New terms and questions:
186
+
187
+ * What is Huggingface? What do they do?
188
+ * What is git? Why is it useful?
189
+ * What is git add, git commit, git push?
190
+
191
+ ## 6. Improve system
192
+
193
+ We have gathered a dataset, labeled the images, trained a model, exported model parameters, created a Streamlit app and hosted it in Huggingface Spaces. Now everyone can access this link and try their images on our app.
194
+
195
+ We will review the Space together, try a test dataset and check if the results are satisfactory. Possibly, the dataset will have to be revised, you will need to add more images, label them, train the model again, export the new model's parameters and update these files in the Space. We might have to do this a few times.
196
+
197
+ In the end, the internship is finished when we have a robust model that can handle all types of images and returns appropriate boxes and labels. Now we can show our app to the world! 🤩
198
+
199
+ ## Resources
200
+
201
+ * Learn Python
202
+ * [Text: Python tutorial](https://www.afterhoursprogramming.com/tutorial/python/python-overview/)
203
+ * [Video: Datacamp course](https://www.datacamp.com/courses/intro-to-python-for-data-science)
204
+ * [Course: Machine Learning](https://www.coursera.org/learn/machine-learning)
205
+ * [Book: Cloud fundamentals](https://cloudresumechallenge.dev/)
label_map.pbtxt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ item {
2
+ id: 1
3
+ name: "ripe"
4
+ }
5
+
6
+ item {
7
+ id: 2
8
+ name: "overripe"
9
+ }
10
+
packages.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ ffmpeg
2
+ libsm6
3
+ libxext6
pipeline.config ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model {
2
+ faster_rcnn {
3
+ num_classes: 2
4
+ image_resizer {
5
+ keep_aspect_ratio_resizer {
6
+ min_dimension: 800
7
+ max_dimension: 1333
8
+ pad_to_max_dimension: true
9
+ }
10
+ }
11
+ feature_extractor {
12
+ type: "faster_rcnn_inception_resnet_v2_keras"
13
+ }
14
+ first_stage_anchor_generator {
15
+ grid_anchor_generator {
16
+ height_stride: 16
17
+ width_stride: 16
18
+ scales: 0.25
19
+ scales: 0.5
20
+ scales: 1.0
21
+ scales: 2.0
22
+ aspect_ratios: 0.5
23
+ aspect_ratios: 1.0
24
+ aspect_ratios: 2.0
25
+ }
26
+ }
27
+ first_stage_box_predictor_conv_hyperparams {
28
+ op: CONV
29
+ regularizer {
30
+ l2_regularizer {
31
+ weight: 0.0
32
+ }
33
+ }
34
+ initializer {
35
+ truncated_normal_initializer {
36
+ stddev: 0.01
37
+ }
38
+ }
39
+ }
40
+ first_stage_nms_score_threshold: 0.0
41
+ first_stage_nms_iou_threshold: 0.7
42
+ first_stage_max_proposals: 300
43
+ first_stage_localization_loss_weight: 2.0
44
+ first_stage_objectness_loss_weight: 1.0
45
+ initial_crop_size: 17
46
+ maxpool_kernel_size: 1
47
+ maxpool_stride: 1
48
+ second_stage_box_predictor {
49
+ mask_rcnn_box_predictor {
50
+ fc_hyperparams {
51
+ op: FC
52
+ regularizer {
53
+ l2_regularizer {
54
+ weight: 0.0
55
+ }
56
+ }
57
+ initializer {
58
+ variance_scaling_initializer {
59
+ factor: 1.0
60
+ uniform: true
61
+ mode: FAN_AVG
62
+ }
63
+ }
64
+ }
65
+ use_dropout: false
66
+ dropout_keep_probability: 1.0
67
+ }
68
+ }
69
+ second_stage_post_processing {
70
+ batch_non_max_suppression {
71
+ score_threshold: 0.0
72
+ iou_threshold: 0.6
73
+ max_detections_per_class: 100
74
+ max_total_detections: 200
75
+ }
76
+ score_converter: SOFTMAX
77
+ }
78
+ second_stage_localization_loss_weight: 2.0
79
+ second_stage_classification_loss_weight: 1.0
80
+ }
81
+ }
82
+ train_config {
83
+ batch_size: 2
84
+ optimizer {
85
+ momentum_optimizer {
86
+ learning_rate {
87
+ cosine_decay_learning_rate {
88
+ learning_rate_base: 0.04
89
+ total_steps: 1000
90
+ warmup_learning_rate: 0.0
91
+ warmup_steps: 250
92
+ }
93
+ }
94
+ momentum_optimizer_value: 0.9
95
+ }
96
+ use_moving_average: false
97
+ }
98
+ gradient_clipping_by_norm: 10.0
99
+ fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED"
100
+ num_steps: 1000
101
+ max_number_of_boxes: 100
102
+ fine_tune_checkpoint_type: "detection"
103
+ use_bfloat16: false
104
+ fine_tune_checkpoint_version: V2
105
+ }
106
+ train_input_reader {
107
+ label_map_path: "PATH_TO_BE_CONFIGURED"
108
+ shuffle: false
109
+ tf_record_input_reader {
110
+ input_path: "PATH_TO_BE_CONFIGURED"
111
+ }
112
+ num_parallel_batches: 1
113
+ }
114
+ eval_config {
115
+ metrics_set: "coco_detection_metrics"
116
+ use_moving_averages: false
117
+ batch_size: 1
118
+ }
119
+ eval_input_reader {
120
+ label_map_path: "PATH_TO_BE_CONFIGURED"
121
+ shuffle: false
122
+ num_epochs: 1
123
+ tf_record_input_reader {
124
+ input_path: "PATH_TO_BE_CONFIGURED"
125
+ }
126
+ num_parallel_batches: 1
127
+ }
python_tasks.ipynb ADDED
File without changes
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ numpy
2
+ opencv-python-headless
3
+ Pillow
4
+ streamlit
5
+ tensorflow