alexrods commited on
Commit
b412cbb
1 Parent(s): bc6f589

Upload 5 files

Browse files

Upload app.py file, and the logic to make inference

Files changed (5) hide show
  1. app.py +33 -0
  2. centroidtracker.py +163 -0
  3. prediction.py +203 -0
  4. requirements.txt +13 -0
  5. trackableobject.py +11 -0
app.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from prediction import smartcities
3
+
4
+ # Streamlit Interface
5
+ st.header("Smart City Cars and Bikes detection")
6
+ st.markdown("Upload a video or select the example")
7
+
8
+ ## Select video to inference
9
+ file_video = st.file_upload(" Upload a video ", type=["mp4"])
10
+ example = open("test_video.mp4")
11
+ st.video(example, width=250)
12
+ if st.button("example"):
13
+ file_video = "test_video.mp4"
14
+
15
+ ##
16
+ if file_video is not None:
17
+ video = open(file_video)
18
+ video_bytes = video.read()
19
+ output = smartcities(video_bytes)
20
+ col1, col2 = st.columns(2)
21
+
22
+ if output is not None:
23
+ with col1:
24
+ st.subheader("Input: ")
25
+ st.video(video_bytes)
26
+ with col2:
27
+ st.subheader("Output: ")
28
+ st.video(output)
29
+
30
+
31
+
32
+
33
+
centroidtracker.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import the necessary packages
2
+ from scipy.spatial import distance as dist
3
+ from collections import OrderedDict
4
+ import numpy as np
5
+
6
+ class CentroidTracker:
7
+ def __init__(self, maxDisappeared=50, maxDistance=50):
8
+ # initialize the next unique object ID along with two ordered
9
+ # dictionaries used to keep track of mapping a given object
10
+ # ID to its centroid and number of consecutive frames it has
11
+ # been marked as "disappeared", respectively
12
+ self.nextObjectID = 0
13
+ self.objects = OrderedDict()
14
+ self.disappeared = OrderedDict()
15
+
16
+ # store the number of maximum consecutive frames a given
17
+ # object is allowed to be marked as "disappeared" until we
18
+ # need to deregister the object from tracking
19
+ self.maxDisappeared = maxDisappeared
20
+
21
+ # store the maximum distance between centroids to associate
22
+ # an object -- if the distance is larger than this maximum
23
+ # distance we'll start to mark the object as "disappeared"
24
+ self.maxDistance = maxDistance
25
+
26
+ def register(self, centroid):
27
+ # when registering an object we use the next available object
28
+ # ID to store the centroid
29
+ self.objects[self.nextObjectID] = centroid
30
+ self.disappeared[self.nextObjectID] = 0
31
+ self.nextObjectID += 1
32
+
33
+ def deregister(self, objectID):
34
+ # to deregister an object ID we delete the object ID from
35
+ # both of our respective dictionaries
36
+ del self.objects[objectID]
37
+ del self.disappeared[objectID]
38
+
39
+ def update(self, rects):
40
+ # check to see if the list of input bounding box rectangles
41
+ # is empty
42
+ if len(rects) == 0:
43
+ # loop over any existing tracked objects and mark them
44
+ # as disappeared
45
+ for objectID in list(self.disappeared.keys()):
46
+ self.disappeared[objectID] += 1
47
+
48
+ # if we have reached a maximum number of consecutive
49
+ # frames where a given object has been marked as
50
+ # missing, deregister it
51
+ if self.disappeared[objectID] > self.maxDisappeared:
52
+ self.deregister(objectID)
53
+
54
+ # return early as there are no centroids or tracking info
55
+ # to update
56
+ return self.objects
57
+
58
+ # initialize an array of input centroids for the current frame
59
+ inputCentroids = np.zeros((len(rects), 2), dtype="int")
60
+
61
+ # loop over the bounding box rectangles
62
+ for (i, (startX, startY, endX, endY)) in enumerate(rects):
63
+ # use the bounding box coordinates to derive the centroid
64
+ cX = int((startX + endX) / 2.0)
65
+ cY = int((startY + endY) / 2.0)
66
+ inputCentroids[i] = (cX, cY)
67
+
68
+ # if we are currently not tracking any objects take the input
69
+ # centroids and register each of them
70
+ if len(self.objects) == 0:
71
+ for i in range(0, len(inputCentroids)):
72
+ self.register(inputCentroids[i])
73
+
74
+ # otherwise, are are currently tracking objects so we need to
75
+ # try to match the input centroids to existing object
76
+ # centroids
77
+ else:
78
+ # grab the set of object IDs and corresponding centroids
79
+ objectIDs = list(self.objects.keys())
80
+ objectCentroids = list(self.objects.values())
81
+
82
+ # compute the distance between each pair of object
83
+ # centroids and input centroids, respectively -- our
84
+ # goal will be to match an input centroid to an existing
85
+ # object centroid
86
+ D = dist.cdist(np.array(objectCentroids), inputCentroids)
87
+
88
+ # in order to perform this matching we must (1) find the
89
+ # smallest value in each row and then (2) sort the row
90
+ # indexes based on their minimum values so that the row
91
+ # with the smallest value as at the *front* of the index
92
+ # list
93
+ rows = D.min(axis=1).argsort()
94
+
95
+ # next, we perform a similar process on the columns by
96
+ # finding the smallest value in each column and then
97
+ # sorting using the previously computed row index list
98
+ cols = D.argmin(axis=1)[rows]
99
+
100
+ # in order to determine if we need to update, register,
101
+ # or deregister an object we need to keep track of which
102
+ # of the rows and column indexes we have already examined
103
+ usedRows = set()
104
+ usedCols = set()
105
+
106
+ # loop over the combination of the (row, column) index
107
+ # tuples
108
+ for (row, col) in zip(rows, cols):
109
+ # if we have already examined either the row or
110
+ # column value before, ignore it
111
+ if row in usedRows or col in usedCols:
112
+ continue
113
+
114
+ # if the distance between centroids is greater than
115
+ # the maximum distance, do not associate the two
116
+ # centroids to the same object
117
+ if D[row, col] > self.maxDistance:
118
+ continue
119
+
120
+ # otherwise, grab the object ID for the current row,
121
+ # set its new centroid, and reset the disappeared
122
+ # counter
123
+ objectID = objectIDs[row]
124
+ self.objects[objectID] = inputCentroids[col]
125
+ self.disappeared[objectID] = 0
126
+
127
+ # indicate that we have examined each of the row and
128
+ # column indexes, respectively
129
+ usedRows.add(row)
130
+ usedCols.add(col)
131
+
132
+ # compute both the row and column index we have NOT yet
133
+ # examined
134
+ unusedRows = set(range(0, D.shape[0])).difference(usedRows)
135
+ unusedCols = set(range(0, D.shape[1])).difference(usedCols)
136
+
137
+ # in the event that the number of object centroids is
138
+ # equal or greater than the number of input centroids
139
+ # we need to check and see if some of these objects have
140
+ # potentially disappeared
141
+ if D.shape[0] >= D.shape[1]:
142
+ # loop over the unused row indexes
143
+ for row in unusedRows:
144
+ # grab the object ID for the corresponding row
145
+ # index and increment the disappeared counter
146
+ objectID = objectIDs[row]
147
+ self.disappeared[objectID] += 1
148
+
149
+ # check to see if the number of consecutive
150
+ # frames the object has been marked "disappeared"
151
+ # for warrants deregistering the object
152
+ if self.disappeared[objectID] > self.maxDisappeared:
153
+ self.deregister(objectID)
154
+
155
+ # otherwise, if the number of input centroids is greater
156
+ # than the number of existing object centroids we need to
157
+ # register each new input centroid as a trackable object
158
+ else:
159
+ for col in unusedCols:
160
+ self.register(inputCentroids[col])
161
+
162
+ # return the set of trackable objects
163
+ return self.objects
prediction.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import tensorflow as tf
3
+ import numpy as np
4
+ import imutils
5
+ import time
6
+ import dlib
7
+ import cv2
8
+ from PIL import Image
9
+ import matplotlib.pyplot as plt
10
+ from imutils.video import VideoStream
11
+ from imutils.video import FPS
12
+ from centroidtracker import CentroidTracker
13
+ from trackableobject import TrackableObject
14
+ # import base64
15
+
16
+
17
+ class smartcities:
18
+ def __init__(self):
19
+ detect_fn = tf.saved_model.load("model/saved_model")
20
+ self.detect_fn = detect_fn
21
+
22
+ def predict(self):
23
+ # Ruta del video (Se debe cargar de manera manual)
24
+ PATH_VIDEO = "/tmp/in_video.mp4"
25
+ video_result = open(PATH_VIDEO, "wb")
26
+ # video_result.write(base64.b64decode(image_64_decode))
27
+
28
+ # Ruta del video en donde almacenaremos los resultados
29
+ PATH_OUTPUT = "/tmp/video_out.mp4"
30
+
31
+ # Cuántos frames vamos a saltarnos (Durante estos frames nuestro algoritmo de seguimiento funciona)
32
+ SKIP_FPS = 30
33
+
34
+ # Cuál será el umbral mínimo par que se considere una detección
35
+ TRESHOLD = 0.5
36
+
37
+ # Cargamos el video
38
+ vs = cv2.VideoCapture(PATH_VIDEO)
39
+
40
+ # Inicializamos el writer para poder guardar el video
41
+ writer = None
42
+
43
+ # Definimos ancho y alto
44
+ W = int(vs.get(cv2.CAP_PROP_FRAME_WIDTH))
45
+ H = int(vs.get(cv2.CAP_PROP_FRAME_HEIGHT))
46
+
47
+ # Inicializamos la clase centroid tracker con dos variable fundamentales
48
+ # maxDissapared (Si pasa ese tiempo y no se detecta más el centroide lo elimina)
49
+ # Si la distancia es mayor a maxDistance no lo podra asociar como si fuera el mismo objecto.
50
+ ct = CentroidTracker(maxDisappeared= 40, maxDistance = 50)
51
+
52
+ # Inicializamos variables principales
53
+ trackers = []
54
+ trackableObjects = {}
55
+
56
+ totalFrame = 0
57
+ totalDown = 0
58
+ totalUp = 0
59
+
60
+ DIRECTION_PEOPLE = True
61
+
62
+ # Creamos un umbral para sabre si el carro paso de izquierda a derecha o viceversa
63
+ # En este caso lo deje fijo pero se pudiese configurar según la ubicación de la cámara.
64
+ POINT = [0, int((H/2)-H*0.1), W, int(H*0.1)]
65
+
66
+ # Los FPS nos van a permitir ver el rendimiento de nuestro modelo y si funciona en tiempo real.
67
+ fps = FPS().start()
68
+
69
+ # Definimos el formato del archivo resultante y las rutas.
70
+ fourcc = cv2.VideoWriter_fourcc(*'MP4V')
71
+ writer = cv2.VideoWriter(PATH_OUTPUT, fourcc, 20.0, (W, H), True)
72
+
73
+ # Bucle que recorre todo el video
74
+ while True:
75
+ # Leemos el primer frame
76
+ ret, frame = vs.read()
77
+
78
+ # Si ya no hay más frame, significa que el video termino y por tanto se sale del bucle
79
+ if frame is None:
80
+ break
81
+
82
+ status = "Waiting"
83
+ rects = []
84
+
85
+ # Nos saltamos los frames especificados.
86
+ if totalFrame % SKIP_FPS == 0:
87
+ status = "Detecting"
88
+ trackers = []
89
+ # Tomamos la imagen la convertimos a array luego a tensor
90
+ image_np = np.array(frame)
91
+
92
+ input_tensor = tf.convert_to_tensor(image_np)
93
+ input_tensor = input_tensor[tf.newaxis, ...]
94
+
95
+ # Predecimos los objectos y clases de la imagen
96
+ detections = self.detect_fn(input_tensor)
97
+
98
+ detection_scores = np.array(detections["detection_scores"][0])
99
+ # Realizamos una limpieza para solo obtener las clasificaciones mayores al umbral.
100
+ detection_clean = [x for x in detection_scores if x >= TRESHOLD]
101
+
102
+ # Recorremos las detecciones
103
+ for x in range(len(detection_clean)):
104
+ idx = int(detections['detection_classes'][0][x])
105
+ # Tomamos los bounding box
106
+ ymin, xmin, ymax, xmax = np.array(detections['detection_boxes'][0][x])
107
+ box = [xmin, ymin, xmax, ymax] * np.array([W,H, W, H])
108
+
109
+ (startX, startY, endX, endY) = box.astype("int")
110
+
111
+ # Con la función de dlib empezamos a hacer seguimiento de los boudiung box obtenidos
112
+ tracker = dlib.correlation_tracker()
113
+ rect = dlib.rectangle(startX, startY, endX, endY)
114
+ tracker.start_track(frame, rect)
115
+
116
+ trackers.append(tracker)
117
+ else:
118
+ # En caso de que no hagamos detección haremos seguimiento
119
+ # Recorremos los objetos que se les está realizando seguimiento
120
+ for tracker in trackers:
121
+ status = "Tracking"
122
+ # Actualizamos y buscamos los nuevos bounding box
123
+ tracker.update(frame)
124
+ pos = tracker.get_position()
125
+
126
+ startX = int(pos.left())
127
+ startY = int(pos.top())
128
+ endX = int(pos.right())
129
+ endY = int(pos.bottom())
130
+
131
+ rects.append((startX, startY, endX, endY))
132
+
133
+ # Dibujamos el umbral de conteo
134
+ cv2.rectangle(frame, (POINT[0], POINT[1]), (POINT[0]+ POINT[2], POINT[1] + POINT[3]), (255, 0, 255), 2)
135
+
136
+ objects = ct.update(rects)
137
+
138
+ # Recorremos cada una de las detecciones
139
+ for (objectID, centroid) in objects.items():
140
+ # Revisamos si el objeto ya se ha contado
141
+ to = trackableObjects.get(objectID, None)
142
+ if to is None:
143
+ to = TrackableObject(objectID, centroid)
144
+
145
+ else:
146
+ # Si no se ha contado, analizamos la dirección del objeto
147
+ y = [c[1] for c in to.centroids]
148
+ direction = centroid[1] - np.mean(y)
149
+ to.centroids.append(centroid)
150
+ if not to.counted:
151
+ if centroid[0] > POINT[0] and centroid[0] < (POINT[0]+ POINT[2]) and centroid[1] > POINT[1] and centroid[1] < (POINT[1]+POINT[3]):
152
+ if DIRECTION_PEOPLE:
153
+ if direction >0:
154
+ totalUp += 1
155
+ to.counted = True
156
+ else:
157
+ totalDown +=1
158
+ to.counted = True
159
+ else:
160
+ if direction <0:
161
+ totalUp += 1
162
+ to.counted = True
163
+ else:
164
+ totalDown +=1
165
+ to.counted = True
166
+
167
+ trackableObjects[objectID] = to
168
+
169
+ # Dibujamos el centroide y el ID de la detección encontrada
170
+ text = "ID {}".format(objectID)
171
+ cv2.putText(frame, text, (centroid[0]-10, centroid[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 2)
172
+ cv2.circle(frame, (centroid[0], centroid[1]), 4, (0,255,0), -1)
173
+
174
+ # Totalizamos los resultados finales
175
+ info = [
176
+ ("Subiendo", totalUp),
177
+ ("Bajando", totalDown),
178
+ ("Estado", status),
179
+ ]
180
+
181
+ for (i, (k,v)) in enumerate(info):
182
+ text = "{}: {}".format(k,v)
183
+ cv2.putText(frame, text, (10, H - ((i*20) + 20)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,0,255), 2)
184
+
185
+ # Almacenamos el framme en nuestro video resultante.
186
+ writer.write(frame)
187
+ totalFrame += 1
188
+ fps.update()
189
+
190
+ # Terminamos de analizar FPS y mostramos resultados finales
191
+ fps.stop()
192
+
193
+ print("Tiempo completo {}".format(fps.elapsed()))
194
+ print("Tiempo aproximado por frame {}".format(fps.fps()))
195
+
196
+ # Cerramos el stream the almacenar video y de consumir el video.
197
+ writer.release()
198
+ vs.release()
199
+
200
+ video = open(PATH_OUTPUT, "rb")
201
+ video_read = video.read()
202
+
203
+ return video_read
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ tf_slim
2
+ tf-models-official
3
+ lvis
4
+ Cython
5
+ contextlib2
6
+ pillow
7
+ lxml
8
+ matplotlib
9
+ pycocotools
10
+ imutils
11
+ numpy
12
+ dlib
13
+ opencv-contrib-python
trackableobject.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ class TrackableObject:
3
+ def __init__(self, objectID, centroid):
4
+ # store the object ID, then initialize a list of centroids
5
+ # using the current centroid
6
+ self.objectID = objectID
7
+ self.centroids = [centroid]
8
+
9
+ # initialize a boolean used to indicate if the object has
10
+ # already been counted or not
11
+ self.counted = False