pixelprotest commited on
Commit
a69b646
·
verified ·
1 Parent(s): 42232b2

Upload 3 files

Browse files
Files changed (3) hide show
  1. fox_detect_gradio.py +174 -0
  2. requirements.txt +7 -0
  3. utils.py +108 -0
fox_detect_gradio.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import gradio as gr
4
+ from tensorflow.lite.python.interpreter import Interpreter
5
+
6
+ from utils import (get_labels,
7
+ parse_image_for_detection,
8
+ resize_image,
9
+ normalize_image,
10
+ save_image,
11
+ get_random_images)
12
+
13
+ current_dir = os.path.dirname(__file__)
14
+ MODEL_PATH = os.path.join(current_dir, 'model', 'model.tflite')
15
+ LABEL_PATH = os.path.join(current_dir, 'model', 'labels.txt')
16
+ IMAGES_DIRPATH = os.path.join(current_dir, 'publish_images')
17
+ OUTPUT_DIR = os.path.join(current_dir, 'output')
18
+
19
+ example_image_list = [
20
+ "image_0012.png", ## fox mid grass dark
21
+ "image_0026.png", ## fox hidden
22
+ "image_0010.png", ## fox dark path NICE
23
+ "image_0023.png", ## costume mid garden facing
24
+ "image_0022.png", ## costume mid garden bent over
25
+ "image_0024.png", ## costume closeup
26
+ "image_0027.png", ## fox color
27
+ "image_0018.png", ## fox dark path far
28
+ "image_0011.png", ## fox dark path
29
+ "image_0013.png", ## fox dark terrace bright
30
+ "image_0014.png", ## costume mid garden happy
31
+ "image_0020.png", ## costume far away
32
+ "image_0025.png", ## costume far away
33
+ "image_0015.png", ## fox dark terrace left
34
+ "image_0016.png", ## fox dark path
35
+ "image_0021.png", ## fox dark bottom
36
+ "image_0028.png", ## fox dark path
37
+ "image_0019.png", ## person
38
+ "image_0017.png", ## paddington and ball
39
+ ]
40
+
41
+ def detect(modelpath, img, labels_filepath, min_conf=0.5, output_dir='/content/output'):
42
+ # Load the Tensorflow Lite model into memory ----------------
43
+ interpreter = Interpreter(model_path=modelpath)
44
+ interpreter.resize_tensor_input(0, [1, 320, 320, 3])
45
+ interpreter.allocate_tensors()
46
+ # Get model details -----------------------------------------
47
+ input_details = interpreter.get_input_details()
48
+ detect_height = input_details[0]['shape'][1]
49
+ detect_width = input_details[0]['shape'][2]
50
+ # Get model details -----------------------------------------
51
+
52
+ ## load the labels and parse the image for detection --------
53
+ labels = get_labels(labels_filepath)
54
+ img, image_width, image_height = parse_image_for_detection(img)
55
+ np_image = resize_image(img, detect_width, detect_height)
56
+ np_image = normalize_image(np_image, interpreter)
57
+ # Perform the actual detection by running the model with the image as input
58
+ tensor_index = input_details[0]['index']
59
+ interpreter.set_tensor(tensor_index, np_image)
60
+ interpreter.invoke()
61
+ ## ----------------------------------------------------------
62
+
63
+ ## --- now get the boxes, classes and scores from the detection
64
+ boxes, classes, scores = distill_detections(interpreter)
65
+ img = draw_detections(img, boxes, classes, scores, image_height, image_width, labels, min_conf)
66
+ output_image = save_image(img, output_dir)
67
+
68
+ return output_image
69
+
70
+ def distill_detections(interpreter):
71
+ """ receives the already invoked interpreter and returns the boxes, classes and scores
72
+ """
73
+ output_details = interpreter.get_output_details()
74
+
75
+ boxes_index = 1
76
+ classes_index = 3
77
+ scores_index = 0
78
+
79
+ # Retrieve detection results
80
+ boxes = interpreter.get_tensor(output_details[boxes_index]['index'])[0] # Bounding box coordinates of detected objects 1
81
+ classes = interpreter.get_tensor(output_details[classes_index]['index'])[0] # Class index of detected objects 3
82
+ scores = interpreter.get_tensor(output_details[scores_index]['index'])[0] # Confidence of detected objects 0
83
+
84
+ return boxes, classes, scores
85
+
86
+ def draw_detections(image, boxes, classes, scores, image_height, image_width, labels, min_conf):
87
+ """ receives the original image, the detected boxes, classes and scores.
88
+ and draws the bounding boxes with labels on the image.
89
+ """
90
+ # Loop over all detections and draw detection box if confidence is above minimum threshold
91
+ for i in range(len(scores)):
92
+ if ((scores[i] > min_conf) and (scores[i] <= 1.0)):
93
+ # Get bounding box coordinates and draw box
94
+ # Interpreter can return coordinates that are outside of image dimensions, need to force them to be within image using max() and min()
95
+ ymin = int(max(1,(boxes[i][0] * image_height)))
96
+ xmin = int(max(1,(boxes[i][1] * image_width)))
97
+ ymax = int(min(image_height,(boxes[i][2] * image_height)))
98
+ xmax = int(min(image_width,(boxes[i][3] * image_width)))
99
+
100
+ ## draw a bounding box around the detected object
101
+ cv2.rectangle(image, (xmin,ymin), (xmax,ymax), (10, 255, 0), 2)
102
+
103
+ ## now lets draw the label above the bounding box.
104
+ object_name = labels[int(classes[i])]
105
+ label = '%s: %d%%' % (object_name, int(scores[i]*100)) # Example: 'person: 72%'
106
+ labelSize, baseLine = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.7, 2)
107
+ label_ymin_base = max(ymin, labelSize[1] + 10)
108
+ ## draw the rectangle
109
+ label_xmin = xmin
110
+ label_ymin = label_ymin_base-labelSize[1]-10
111
+ label_xmax = xmin+labelSize[0]
112
+ label_ymax = label_ymin_base+baseLine-10
113
+ ## draw a white rectangle to put the label text into
114
+ cv2.rectangle(image, ## image
115
+ (label_xmin, label_ymin), ## top left
116
+ (label_xmax, label_ymax), ## bottom right
117
+ (255, 255, 255), ## color
118
+ cv2.FILLED)
119
+ ## write the label text
120
+ text_xmin = xmin
121
+ text_ymin = label_ymin_base-7
122
+ cv2.putText(image, ## image
123
+ label, ## str
124
+ (text_xmin, text_ymin),
125
+ cv2.FONT_HERSHEY_SIMPLEX,
126
+ 0.7, ## font scale
127
+ (0, 0, 0), ## color
128
+ 2) ## thickness
129
+ return image
130
+
131
+ def gradio_entry(image, confidence=0.1):
132
+ """ entry point for the gradio interface to run the detection"""
133
+ output_filepath = detect(MODEL_PATH, image, LABEL_PATH, min_conf=confidence, output_dir=OUTPUT_DIR)
134
+ return output_filepath
135
+
136
+ def main(debug=False):
137
+ if debug:
138
+ img = get_random_images(IMAGES_DIRPATH, 10)[0]
139
+ output_filepath = detect(MODEL_PATH, img, LABEL_PATH, min_conf=0.5, output_dir=OUTPUT_DIR)
140
+ os.system(f'open {output_filepath}')
141
+ return
142
+
143
+ default_conf = 0.2
144
+ examples_for_display = []
145
+ examples_for_full = []
146
+ for img in example_image_list:
147
+ img_path = os.path.join(IMAGES_DIRPATH, img)
148
+ examples_for_full.append([img_path, 0.2])
149
+ examples_for_display.append([img_path])
150
+
151
+ input_image = gr.Image(width=800, height=600,
152
+ label='Input Image')
153
+ input_slider_conf = gr.Slider(value=default_conf,
154
+ minimum=0.0, maximum=1.0, step=0.01,
155
+ label="Confidence",
156
+ info="Minimum confidence threshold")
157
+ output_image= gr.Image(width=800, height=600,
158
+ label="Output Image")
159
+ input_widgets = [input_image,
160
+ input_slider_conf]
161
+ interface = gr.Interface(fn=gradio_entry,
162
+ inputs=input_widgets,
163
+ outputs=output_image,
164
+ examples=examples_for_display,
165
+ examples_per_page=18)
166
+ ## now add event handler, so whenever we set the slider value, the full example is selected
167
+ interface.load_data = lambda i: examples_for_full[i] ## loads both the image and the confidence
168
+
169
+ interface.launch()
170
+
171
+ if __name__ == '__main__':
172
+ main(debug=False)
173
+
174
+
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ jupyterlab
2
+ jupyterlab-vim
3
+ ipywidgets
4
+ tensorflow
5
+ opencv-python
6
+ matplotlib
7
+ gradio
utils.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import numpy as np
4
+ # import sys
5
+ import glob
6
+ import gradio as gr
7
+ import random
8
+ # import importlib.util
9
+ import datetime
10
+ # from tensorflow.lite.python.interpreter import Interpreter
11
+
12
+ # import matplotlib
13
+ import matplotlib.pyplot as plt
14
+
15
+ ### ---------------------------- image utils ---------------------------------
16
+ def parse_image_for_detection(img):
17
+ """
18
+ if img comes from gradio, it makes sure its a numpy array
19
+ if img is a file path, it reads it and converts from BGR to RGB
20
+ it also returns the width and height of the image
21
+ """
22
+ if isinstance(img, str):
23
+ ## if its a file path, we read it and convert from BGR to RGB
24
+ image = cv2.imread(img)
25
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
26
+ else:
27
+ ## otherwise assume its a numpy array from Gradio UI.
28
+ ## but make sure that it actually is.
29
+ if not isinstance(img, np.ndarray):
30
+ img = np.array(img)
31
+ image = img
32
+
33
+ ## lets also get the width and height of the original image
34
+ image_height, image_width, _ = image.shape
35
+
36
+ return image, image_width, image_height
37
+
38
+ def resize_image(np_image, width, height):
39
+ image_resized = cv2.resize(np_image, (width, height))
40
+ np_image = np.expand_dims(image_resized, axis=0)
41
+ return np_image
42
+
43
+ def normalize_image(np_image, interpreter):
44
+ ## check if the model expects a floating point input
45
+ is_model_float = (interpreter.get_input_details()[0]['dtype'] == np.float32)
46
+
47
+ ## Normalize pixel values if using a floating model (i.e. if model is non-quantized)
48
+ if is_model_float:
49
+ input_mean = 256.0 / 2.0
50
+ input_std = 256.0 / 2.0
51
+ np_image = (np.float32(np_image) - input_mean) / input_std
52
+ return np_image
53
+
54
+ def save_image(image, output_dir, output_width=1600, output_height=1200, dpi=80):
55
+ """
56
+ saves the image in the output dir, as a matplotlib figure
57
+ """
58
+ ## make sure output directory exists
59
+ os.makedirs(output_dir, exist_ok=True)
60
+
61
+ ## first get the figsize in inches based on pixel output width, height
62
+ figsize = get_figsize_from_pixels(output_width, output_height, dpi=dpi)
63
+
64
+ ## now plot the image
65
+ plt.figure(figsize=figsize)
66
+ plt.imshow(image)
67
+ plt.tight_layout(pad=3)
68
+
69
+ ## generate an output filename with a timestamp
70
+ timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S_%f')
71
+ output_filename = os.path.join(output_dir, f'img_{timestamp}.png')
72
+ ## save the figure with the output filename
73
+ plt.savefig(output_filename, dpi=dpi)
74
+ return output_filename
75
+ ### ---------------------------- image utils ---------------------------------
76
+
77
+
78
+ ### ---------------------------- basic utils ---------------------------------
79
+ def get_labels(labels_filepath):
80
+ with open(labels_filepath, 'r') as f:
81
+ labels = [line.strip() for line in f.readlines()]
82
+ return labels
83
+
84
+ def get_random_images(dirpath, image_count=10):
85
+ """ returns a list of random image filepaths from the dirpath """
86
+ images = glob.glob(dirpath + '/*.jpg') + \
87
+ glob.glob(dirpath + '/*.JPG') + \
88
+ glob.glob(dirpath + '/*.png') + \
89
+ glob.glob(dirpath + '/*.bmp')
90
+
91
+ # img_filepaths = random.sample(images, image_count)
92
+ # return img_filepaths
93
+ return sorted(images)
94
+
95
+ def get_figsize_from_pixels(width, height, dpi=80):
96
+ """ returns the width and height in inches based on the dpi
97
+ used for matplotlib figures
98
+ """
99
+ width_in = width / dpi
100
+ height_in = height / dpi
101
+ return (width_in, height_in)
102
+ ### ---------------------------- basic utils ---------------------------------
103
+
104
+
105
+
106
+
107
+
108
+