start_no_thread.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. import datetime
  2. import time
  3. import threading
  4. import queue
  5. import itertools
  6. from collections import defaultdict
  7. from statistics import mean
  8. import cv2
  9. import imutils
  10. import numpy as np
  11. import subprocess as sp
  12. from scipy.spatial import distance as dist
  13. import tflite_runtime.interpreter as tflite
  14. from tflite_runtime.interpreter import load_delegate
  15. def load_labels(path, encoding='utf-8'):
  16. """Loads labels from file (with or without index numbers).
  17. Args:
  18. path: path to label file.
  19. encoding: label file encoding.
  20. Returns:
  21. Dictionary mapping indices to labels.
  22. """
  23. with open(path, 'r', encoding=encoding) as f:
  24. lines = f.readlines()
  25. if not lines:
  26. return {}
  27. if lines[0].split(' ', maxsplit=1)[0].isdigit():
  28. pairs = [line.split(' ', maxsplit=1) for line in lines]
  29. return {int(index): label.strip() for index, label in pairs}
  30. else:
  31. return {index: line.strip() for index, line in enumerate(lines)}
  32. def draw_box_with_label(frame, x_min, y_min, x_max, y_max, label, info, thickness=2, color=None, position='ul'):
  33. if color is None:
  34. color = (0,0,255)
  35. display_text = "{}: {}".format(label, info)
  36. cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), color, thickness)
  37. font_scale = 0.5
  38. font = cv2.FONT_HERSHEY_SIMPLEX
  39. # get the width and height of the text box
  40. size = cv2.getTextSize(display_text, font, fontScale=font_scale, thickness=2)
  41. text_width = size[0][0]
  42. text_height = size[0][1]
  43. line_height = text_height + size[1]
  44. # set the text start position
  45. if position == 'ul':
  46. text_offset_x = x_min
  47. text_offset_y = 0 if y_min < line_height else y_min - (line_height+8)
  48. elif position == 'ur':
  49. text_offset_x = x_max - (text_width+8)
  50. text_offset_y = 0 if y_min < line_height else y_min - (line_height+8)
  51. elif position == 'bl':
  52. text_offset_x = x_min
  53. text_offset_y = y_max
  54. elif position == 'br':
  55. text_offset_x = x_max - (text_width+8)
  56. text_offset_y = y_max
  57. # make the coords of the box with a small padding of two pixels
  58. textbox_coords = ((text_offset_x, text_offset_y), (text_offset_x + text_width + 2, text_offset_y + line_height))
  59. cv2.rectangle(frame, textbox_coords[0], textbox_coords[1], color, cv2.FILLED)
  60. cv2.putText(frame, display_text, (text_offset_x, text_offset_y + line_height - 3), font, fontScale=font_scale, color=(0, 0, 0), thickness=2)
  61. def calculate_region(frame_shape, xmin, ymin, xmax, ymax, multiplier=2):
  62. # size is larger than longest edge
  63. size = int(max(xmax-xmin, ymax-ymin)*multiplier)
  64. # if the size is too big to fit in the frame
  65. if size > min(frame_shape[0], frame_shape[1]):
  66. size = min(frame_shape[0], frame_shape[1])
  67. # x_offset is midpoint of bounding box minus half the size
  68. x_offset = int((xmax-xmin)/2.0+xmin-size/2.0)
  69. # if outside the image
  70. if x_offset < 0:
  71. x_offset = 0
  72. elif x_offset > (frame_shape[1]-size):
  73. x_offset = (frame_shape[1]-size)
  74. # y_offset is midpoint of bounding box minus half the size
  75. y_offset = int((ymax-ymin)/2.0+ymin-size/2.0)
  76. # if outside the image
  77. if y_offset < 0:
  78. y_offset = 0
  79. elif y_offset > (frame_shape[0]-size):
  80. y_offset = (frame_shape[0]-size)
  81. return (x_offset, y_offset, x_offset+size, y_offset+size)
  82. def intersection(box_a, box_b):
  83. return (
  84. max(box_a[0], box_b[0]),
  85. max(box_a[1], box_b[1]),
  86. min(box_a[2], box_b[2]),
  87. min(box_a[3], box_b[3])
  88. )
  89. def area(box):
  90. return (box[2]-box[0] + 1)*(box[3]-box[1] + 1)
  91. def intersection_over_union(box_a, box_b):
  92. # determine the (x, y)-coordinates of the intersection rectangle
  93. intersect = intersection(box_a, box_b)
  94. # compute the area of intersection rectangle
  95. inter_area = max(0, intersect[2] - intersect[0] + 1) * max(0, intersect[3] - intersect[1] + 1)
  96. if inter_area == 0:
  97. return 0.0
  98. # compute the area of both the prediction and ground-truth
  99. # rectangles
  100. box_a_area = (box_a[2] - box_a[0] + 1) * (box_a[3] - box_a[1] + 1)
  101. box_b_area = (box_b[2] - box_b[0] + 1) * (box_b[3] - box_b[1] + 1)
  102. # compute the intersection over union by taking the intersection
  103. # area and dividing it by the sum of prediction + ground-truth
  104. # areas - the interesection area
  105. iou = inter_area / float(box_a_area + box_b_area - inter_area)
  106. # return the intersection over union value
  107. return iou
  108. def clipped(obj, frame_shape):
  109. # if the object is within 5 pixels of the region border, and the region is not on the edge
  110. # consider the object to be clipped
  111. box = obj[2]
  112. region = obj[3]
  113. if ((region[0] > 5 and box[0]-region[0] <= 5) or
  114. (region[1] > 5 and box[1]-region[1] <= 5) or
  115. (frame_shape[1]-region[2] > 5 and region[2]-box[2] <= 5) or
  116. (frame_shape[0]-region[3] > 5 and region[3]-box[3] <= 5)):
  117. return True
  118. else:
  119. return False
  120. def filtered(obj):
  121. if obj[0] != 'person':
  122. return True
  123. return False
  124. def create_tensor_input(frame, region):
  125. cropped_frame = frame[region[1]:region[3], region[0]:region[2]]
  126. # Resize to 300x300 if needed
  127. if cropped_frame.shape != (300, 300, 3):
  128. # TODO: use Pillow-SIMD?
  129. cropped_frame = cv2.resize(cropped_frame, dsize=(300, 300), interpolation=cv2.INTER_LINEAR)
  130. # Expand dimensions since the model expects images to have shape: [1, 300, 300, 3]
  131. return np.expand_dims(cropped_frame, axis=0)
  132. class MotionDetector():
  133. # TODO: add motion masking
  134. def __init__(self, frame_shape, resize_factor=4):
  135. self.resize_factor = resize_factor
  136. self.motion_frame_size = (int(frame_shape[0]/resize_factor), int(frame_shape[1]/resize_factor))
  137. self.avg_frame = np.zeros(self.motion_frame_size, np.float)
  138. self.avg_delta = np.zeros(self.motion_frame_size, np.float)
  139. self.motion_frame_count = 0
  140. self.frame_counter = 0
  141. def detect(self, frame):
  142. motion_boxes = []
  143. # resize frame
  144. resized_frame = cv2.resize(frame, dsize=(self.motion_frame_size[1], self.motion_frame_size[0]), interpolation=cv2.INTER_LINEAR)
  145. # convert to grayscale
  146. gray = cv2.cvtColor(resized_frame, cv2.COLOR_BGR2GRAY)
  147. # it takes ~30 frames to establish a baseline
  148. # dont bother looking for motion
  149. if self.frame_counter < 30:
  150. self.frame_counter += 1
  151. else:
  152. # compare to average
  153. frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(self.avg_frame))
  154. # compute the average delta over the past few frames
  155. # the alpha value can be modified to configure how sensitive the motion detection is.
  156. # higher values mean the current frame impacts the delta a lot, and a single raindrop may
  157. # register as motion, too low and a fast moving person wont be detected as motion
  158. # this also assumes that a person is in the same location across more than a single frame
  159. cv2.accumulateWeighted(frameDelta, self.avg_delta, 0.2)
  160. # compute the threshold image for the current frame
  161. current_thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
  162. # black out everything in the avg_delta where there isnt motion in the current frame
  163. avg_delta_image = cv2.convertScaleAbs(self.avg_delta)
  164. avg_delta_image[np.where(current_thresh==[0])] = [0]
  165. # then look for deltas above the threshold, but only in areas where there is a delta
  166. # in the current frame. this prevents deltas from previous frames from being included
  167. thresh = cv2.threshold(avg_delta_image, 25, 255, cv2.THRESH_BINARY)[1]
  168. # dilate the thresholded image to fill in holes, then find contours
  169. # on thresholded image
  170. thresh = cv2.dilate(thresh, None, iterations=2)
  171. cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  172. cnts = imutils.grab_contours(cnts)
  173. # loop over the contours
  174. for c in cnts:
  175. # if the contour is big enough, count it as motion
  176. contour_area = cv2.contourArea(c)
  177. if contour_area > 100:
  178. # cv2.drawContours(resized_frame, [c], -1, (255,255,255), 2)
  179. x, y, w, h = cv2.boundingRect(c)
  180. motion_boxes.append((x*self.resize_factor, y*self.resize_factor, (x+w)*self.resize_factor, (y+h)*self.resize_factor))
  181. if len(motion_boxes) > 0:
  182. self.motion_frame_count += 1
  183. # TODO: this really depends on FPS
  184. if self.motion_frame_count >= 10:
  185. # only average in the current frame if the difference persists for at least 3 frames
  186. cv2.accumulateWeighted(gray, self.avg_frame, 0.2)
  187. else:
  188. # when no motion, just keep averaging the frames together
  189. cv2.accumulateWeighted(gray, self.avg_frame, 0.2)
  190. self.motion_frame_count = 0
  191. return motion_boxes
  192. class ObjectDetector():
  193. def __init__(self, model_file, label_file):
  194. self.labels = load_labels(label_file)
  195. edge_tpu_delegate = None
  196. try:
  197. edge_tpu_delegate = load_delegate('libedgetpu.so.1.0')
  198. except ValueError:
  199. print("No EdgeTPU detected. Falling back to CPU.")
  200. if edge_tpu_delegate is None:
  201. self.interpreter = tflite.Interpreter(
  202. model_path=model_file)
  203. else:
  204. self.interpreter = tflite.Interpreter(
  205. model_path=model_file,
  206. experimental_delegates=[edge_tpu_delegate])
  207. self.interpreter.allocate_tensors()
  208. self.tensor_input_details = self.interpreter.get_input_details()
  209. self.tensor_output_details = self.interpreter.get_output_details()
  210. def detect(self, tensor_input, threshold=.4):
  211. self.interpreter.set_tensor(self.tensor_input_details[0]['index'], tensor_input)
  212. self.interpreter.invoke()
  213. boxes = np.squeeze(self.interpreter.get_tensor(self.tensor_output_details[0]['index']))
  214. label_codes = np.squeeze(self.interpreter.get_tensor(self.tensor_output_details[1]['index']))
  215. scores = np.squeeze(self.interpreter.get_tensor(self.tensor_output_details[2]['index']))
  216. detections = []
  217. for i, score in enumerate(scores):
  218. label = self.labels[int(label_codes[i])]
  219. if score < threshold:
  220. break
  221. detections.append((
  222. label,
  223. float(score),
  224. boxes[i]
  225. ))
  226. return detections
  227. class ObjectTracker():
  228. def __init__(self, max_disappeared):
  229. self.tracked_objects = {}
  230. self.disappeared = {}
  231. self.max_disappeared = max_disappeared
  232. def register(self, index, frame_time, obj):
  233. id = f"{frame_time}-{index}"
  234. obj['id'] = id
  235. obj['frame_time'] = frame_time
  236. obj['top_score'] = obj['score']
  237. self.add_history(obj)
  238. self.tracked_objects[id] = obj
  239. self.disappeared[id] = 0
  240. def deregister(self, id):
  241. del self.tracked_objects[id]
  242. del self.disappeared[id]
  243. def update(self, id, new_obj):
  244. self.disappeared[id] = 0
  245. self.tracked_objects[id].update(new_obj)
  246. self.add_history(self.tracked_objects[id])
  247. if self.tracked_objects[id]['score'] > self.tracked_objects[id]['top_score']:
  248. self.tracked_objects[id]['top_score'] = self.tracked_objects[id]['score']
  249. def add_history(self, obj):
  250. entry = {
  251. 'score': obj['score'],
  252. 'box': obj['box'],
  253. 'region': obj['region'],
  254. 'centroid': obj['centroid'],
  255. 'frame_time': obj['frame_time']
  256. }
  257. if 'history' in obj:
  258. obj['history'].append(entry)
  259. else:
  260. obj['history'] = [entry]
  261. def match_and_update(self, frame_time, new_objects):
  262. if len(new_objects) == 0:
  263. for id in list(self.tracked_objects.keys()):
  264. if self.disappeared[id] >= self.max_disappeared:
  265. self.deregister(id)
  266. else:
  267. self.disappeared[id] += 1
  268. return
  269. # group by name
  270. new_object_groups = defaultdict(lambda: [])
  271. for obj in new_objects:
  272. new_object_groups[obj[0]].append({
  273. 'label': obj[0],
  274. 'score': obj[1],
  275. 'box': obj[2],
  276. 'region': obj[3]
  277. })
  278. # track objects for each label type
  279. for label, group in new_object_groups.items():
  280. current_objects = [o for o in self.tracked_objects.values() if o['label'] == label]
  281. current_ids = [o['id'] for o in current_objects]
  282. current_centroids = np.array([o['centroid'] for o in current_objects])
  283. # compute centroids of new objects
  284. for obj in group:
  285. centroid_x = int((obj['box'][0]+obj['box'][2]) / 2.0)
  286. centroid_y = int((obj['box'][1]+obj['box'][3]) / 2.0)
  287. obj['centroid'] = (centroid_x, centroid_y)
  288. if len(current_objects) == 0:
  289. for index, obj in enumerate(group):
  290. self.register(index, frame_time, obj)
  291. return
  292. new_centroids = np.array([o['centroid'] for o in group])
  293. # compute the distance between each pair of tracked
  294. # centroids and new centroids, respectively -- our
  295. # goal will be to match each new centroid to an existing
  296. # object centroid
  297. D = dist.cdist(current_centroids, new_centroids)
  298. # in order to perform this matching we must (1) find the
  299. # smallest value in each row and then (2) sort the row
  300. # indexes based on their minimum values so that the row
  301. # with the smallest value is at the *front* of the index
  302. # list
  303. rows = D.min(axis=1).argsort()
  304. # next, we perform a similar process on the columns by
  305. # finding the smallest value in each column and then
  306. # sorting using the previously computed row index list
  307. cols = D.argmin(axis=1)[rows]
  308. # in order to determine if we need to update, register,
  309. # or deregister an object we need to keep track of which
  310. # of the rows and column indexes we have already examined
  311. usedRows = set()
  312. usedCols = set()
  313. # loop over the combination of the (row, column) index
  314. # tuples
  315. for (row, col) in zip(rows, cols):
  316. # if we have already examined either the row or
  317. # column value before, ignore it
  318. if row in usedRows or col in usedCols:
  319. continue
  320. # otherwise, grab the object ID for the current row,
  321. # set its new centroid, and reset the disappeared
  322. # counter
  323. objectID = current_ids[row]
  324. self.update(objectID, group[col])
  325. # indicate that we have examined each of the row and
  326. # column indexes, respectively
  327. usedRows.add(row)
  328. usedCols.add(col)
  329. # compute the column index we have NOT yet examined
  330. unusedRows = set(range(0, D.shape[0])).difference(usedRows)
  331. unusedCols = set(range(0, D.shape[1])).difference(usedCols)
  332. # in the event that the number of object centroids is
  333. # equal or greater than the number of input centroids
  334. # we need to check and see if some of these objects have
  335. # potentially disappeared
  336. if D.shape[0] >= D.shape[1]:
  337. for row in unusedRows:
  338. id = current_ids[row]
  339. if self.disappeared[id] >= self.max_disappeared:
  340. self.deregister(id)
  341. else:
  342. self.disappeared[id] += 1
  343. # if the number of input centroids is greater
  344. # than the number of existing object centroids we need to
  345. # register each new input centroid as a trackable object
  346. else:
  347. for col in unusedCols:
  348. self.register(col, frame_time, group[col])
  349. def main():
  350. frames = 0
  351. # frame_queue = queue.Queue(maxsize=5)
  352. # frame_cache = {}
  353. frame_shape = (1080,1920,3)
  354. # frame_shape = (720,1280,3)
  355. frame_size = frame_shape[0]*frame_shape[1]*frame_shape[2]
  356. frame = np.zeros(frame_shape, np.uint8)
  357. motion_detector = MotionDetector(frame_shape, resize_factor=6)
  358. object_detector = ObjectDetector('/lab/mobilenet_ssd_v2_coco_quant_postprocess_edgetpu.tflite', '/lab/labelmap.txt')
  359. # object_detector = ObjectDetector('/lab/detect.tflite', '/lab/labelmap.txt')
  360. object_tracker = ObjectTracker(10)
  361. # f = open('/debug/input/back.rgb24', 'rb')
  362. # f = open('/debug/back.raw_video', 'rb')
  363. # f = open('/debug/ali-jake.raw_video', 'rb')
  364. # -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format yuv420p -i output.mp4 -f rawvideo -pix_fmt rgb24 pipe:
  365. ffmpeg_cmd = (['ffmpeg'] +
  366. ['-hide_banner','-loglevel','panic'] +
  367. ['-hwaccel','vaapi','-hwaccel_device','/dev/dri/renderD129','-hwaccel_output_format','yuv420p'] +
  368. ['-i', '/debug/input/output.mp4'] +
  369. # ['-i', '/debug/back-ali-jake.mp4'] +
  370. ['-f','rawvideo','-pix_fmt','rgb24'] +
  371. ['pipe:'])
  372. print(" ".join(ffmpeg_cmd))
  373. ffmpeg_process = sp.Popen(ffmpeg_cmd, stdout = sp.PIPE, bufsize=frame_size)
  374. total_detections = 0
  375. start = datetime.datetime.now().timestamp()
  376. frame_times = []
  377. while True:
  378. start_frame = datetime.datetime.now().timestamp()
  379. frame_detections = 0
  380. frame_bytes = ffmpeg_process.stdout.read(frame_size)#f.read(frame_size)
  381. if not frame_bytes:
  382. break
  383. frame_time = datetime.datetime.now().timestamp()
  384. # Store frame in numpy array
  385. frame[:] = (np
  386. .frombuffer(frame_bytes, np.uint8)
  387. .reshape(frame_shape))
  388. frames += 1
  389. # look for motion
  390. motion_boxes = motion_detector.detect(frame)
  391. tracked_objects = object_tracker.tracked_objects.values()
  392. # merge areas of motion that intersect with a known tracked object into a single area to look at
  393. areas_of_interest = []
  394. used_motion_boxes = []
  395. for obj in tracked_objects:
  396. x_min, y_min, x_max, y_max = obj['box']
  397. for m_index, motion_box in enumerate(motion_boxes):
  398. if area(intersection(obj['box'], motion_box))/area(motion_box) > .5:
  399. used_motion_boxes.append(m_index)
  400. x_min = min(obj['box'][0], motion_box[0])
  401. y_min = min(obj['box'][1], motion_box[1])
  402. x_max = max(obj['box'][2], motion_box[2])
  403. y_max = max(obj['box'][3], motion_box[3])
  404. areas_of_interest.append((x_min, y_min, x_max, y_max))
  405. unused_motion_boxes = set(range(0, len(motion_boxes))).difference(used_motion_boxes)
  406. # compute motion regions
  407. motion_regions = [calculate_region(frame_shape, motion_boxes[i][0], motion_boxes[i][1], motion_boxes[i][2], motion_boxes[i][3], 1.2)
  408. for i in unused_motion_boxes]
  409. # compute tracked object regions
  410. object_regions = [calculate_region(frame_shape, a[0], a[1], a[2], a[3], 1.2)
  411. for a in areas_of_interest]
  412. # merge regions with high IOU
  413. merged_regions = motion_regions+object_regions
  414. while True:
  415. max_iou = 0.0
  416. max_indices = None
  417. region_indices = range(len(merged_regions))
  418. for a, b in itertools.combinations(region_indices, 2):
  419. iou = intersection_over_union(merged_regions[a], merged_regions[b])
  420. if iou > max_iou:
  421. max_iou = iou
  422. max_indices = (a, b)
  423. if max_iou > 0.1:
  424. a = merged_regions[max_indices[0]]
  425. b = merged_regions[max_indices[1]]
  426. merged_regions.append(calculate_region(frame_shape,
  427. min(a[0], b[0]),
  428. min(a[1], b[1]),
  429. max(a[2], b[2]),
  430. max(a[3], b[3]),
  431. 1
  432. ))
  433. del merged_regions[max(max_indices[0], max_indices[1])]
  434. del merged_regions[min(max_indices[0], max_indices[1])]
  435. else:
  436. break
  437. # resize regions and detect
  438. detections = []
  439. for region in merged_regions:
  440. tensor_input = create_tensor_input(frame, region)
  441. region_detections = object_detector.detect(tensor_input)
  442. frame_detections += 1
  443. for d in region_detections:
  444. if filtered(d):
  445. continue
  446. box = d[2]
  447. size = region[2]-region[0]
  448. x_min = int((box[1] * size) + region[0])
  449. y_min = int((box[0] * size) + region[1])
  450. x_max = int((box[3] * size) + region[0])
  451. y_max = int((box[2] * size) + region[1])
  452. detections.append((
  453. d[0],
  454. d[1],
  455. (x_min, y_min, x_max, y_max),
  456. region))
  457. #########
  458. # merge objects, check for clipped objects and look again up to N times
  459. #########
  460. refining = True
  461. refine_count = 0
  462. while refining and refine_count < 4:
  463. refining = False
  464. # group by name
  465. detected_object_groups = defaultdict(lambda: [])
  466. for detection in detections:
  467. detected_object_groups[detection[0]].append(detection)
  468. selected_objects = []
  469. for group in detected_object_groups.values():
  470. # apply non-maxima suppression to suppress weak, overlapping bounding boxes
  471. boxes = [(o[2][0], o[2][1], o[2][2]-o[2][0], o[2][3]-o[2][1])
  472. for o in group]
  473. confidences = [o[1] for o in group]
  474. idxs = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
  475. for index in idxs:
  476. obj = group[index[0]]
  477. if clipped(obj, frame_shape): #obj['clipped']:
  478. box = obj[2]
  479. # calculate a new region that will hopefully get the entire object
  480. region = calculate_region(frame_shape,
  481. box[0], box[1],
  482. box[2], box[3])
  483. tensor_input = create_tensor_input(frame, region)
  484. # run detection on new region
  485. refined_detections = object_detector.detect(tensor_input)
  486. frame_detections += 1
  487. for d in refined_detections:
  488. if filtered(d):
  489. continue
  490. box = d[2]
  491. size = region[2]-region[0]
  492. x_min = int((box[1] * size) + region[0])
  493. y_min = int((box[0] * size) + region[1])
  494. x_max = int((box[3] * size) + region[0])
  495. y_max = int((box[2] * size) + region[1])
  496. selected_objects.append((
  497. d[0],
  498. d[1],
  499. (x_min, y_min, x_max, y_max),
  500. region))
  501. refining = True
  502. else:
  503. selected_objects.append(obj)
  504. # set the detections list to only include top, complete objects
  505. # and new detections
  506. detections = selected_objects
  507. if refining:
  508. refine_count += 1
  509. # now that we have refined our detections, we need to track objects
  510. object_tracker.match_and_update(frame_time, detections)
  511. total_detections += frame_detections
  512. frame_times.append(datetime.datetime.now().timestamp()-start_frame)
  513. # if (frames >= 700 and frames <= 1635) or (frames >= 2500):
  514. # if (frames >= 700 and frames <= 1000):
  515. # if (frames >= 0):
  516. # # row1 = cv2.hconcat([gray, cv2.convertScaleAbs(avg_frame)])
  517. # # row2 = cv2.hconcat([frameDelta, thresh])
  518. # # cv2.imwrite(f"/lab/debug/output/{frames}.jpg", cv2.vconcat([row1, row2]))
  519. # # # cv2.imwrite(f"/lab/debug/output/resized-frame-{frames}.jpg", resized_frame)
  520. # # for region in motion_regions:
  521. # # cv2.rectangle(frame, (region[0], region[1]), (region[2], region[3]), (255,128,0), 2)
  522. # # for region in object_regions:
  523. # # cv2.rectangle(frame, (region[0], region[1]), (region[2], region[3]), (0,128,255), 2)
  524. # for region in merged_regions:
  525. # cv2.rectangle(frame, (region[0], region[1]), (region[2], region[3]), (0,255,0), 2)
  526. # for box in motion_boxes:
  527. # cv2.rectangle(frame, (box[0], box[1]), (box[2], box[3]), (255,0,0), 2)
  528. # for detection in detections:
  529. # box = detection[2]
  530. # draw_box_with_label(frame, box[0], box[1], box[2], box[3], detection[0], f"{detection[1]*100}%")
  531. # for obj in object_tracker.tracked_objects.values():
  532. # box = obj['box']
  533. # draw_box_with_label(frame, box[0], box[1], box[2], box[3], obj['label'], obj['id'], thickness=1, color=(0,0,255), position='bl')
  534. # cv2.putText(frame, str(total_detections), (10, 10), cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.5, color=(0, 0, 0), thickness=2)
  535. # cv2.putText(frame, str(frame_detections), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.5, color=(0, 0, 0), thickness=2)
  536. # cv2.imwrite(f"/lab/debug/output/frame-{frames}.jpg", frame)
  537. # break
  538. duration = datetime.datetime.now().timestamp()-start
  539. print(f"Processed {frames} frames for {duration:.2f} seconds and {(frames/duration):.2f} FPS.")
  540. print(f"Total detections: {total_detections}")
  541. print(f"Average frame processing time: {mean(frame_times)*1000:.2f}ms")
  542. if __name__ == '__main__':
  543. main()