util.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. from abc import ABC, abstractmethod
  2. import datetime
  3. import time
  4. import signal
  5. import traceback
  6. import collections
  7. import numpy as np
  8. import cv2
  9. import threading
  10. import matplotlib.pyplot as plt
  11. import hashlib
  12. from multiprocessing import shared_memory
  13. from typing import AnyStr
  14. def draw_box_with_label(frame, x_min, y_min, x_max, y_max, label, info, thickness=2, color=None, position='ul'):
  15. if color is None:
  16. color = (0,0,255)
  17. display_text = "{}: {}".format(label, info)
  18. cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), color, thickness)
  19. font_scale = 0.5
  20. font = cv2.FONT_HERSHEY_SIMPLEX
  21. # get the width and height of the text box
  22. size = cv2.getTextSize(display_text, font, fontScale=font_scale, thickness=2)
  23. text_width = size[0][0]
  24. text_height = size[0][1]
  25. line_height = text_height + size[1]
  26. # set the text start position
  27. if position == 'ul':
  28. text_offset_x = x_min
  29. text_offset_y = 0 if y_min < line_height else y_min - (line_height+8)
  30. elif position == 'ur':
  31. text_offset_x = x_max - (text_width+8)
  32. text_offset_y = 0 if y_min < line_height else y_min - (line_height+8)
  33. elif position == 'bl':
  34. text_offset_x = x_min
  35. text_offset_y = y_max
  36. elif position == 'br':
  37. text_offset_x = x_max - (text_width+8)
  38. text_offset_y = y_max
  39. # make the coords of the box with a small padding of two pixels
  40. textbox_coords = ((text_offset_x, text_offset_y), (text_offset_x + text_width + 2, text_offset_y + line_height))
  41. cv2.rectangle(frame, textbox_coords[0], textbox_coords[1], color, cv2.FILLED)
  42. cv2.putText(frame, display_text, (text_offset_x, text_offset_y + line_height - 3), font, fontScale=font_scale, color=(0, 0, 0), thickness=2)
  43. def calculate_region(frame_shape, xmin, ymin, xmax, ymax, multiplier=2):
  44. # size is larger than longest edge
  45. size = int(max(xmax-xmin, ymax-ymin)*multiplier)
  46. # dont go any smaller than 300
  47. if size < 300:
  48. size = 300
  49. # if the size is too big to fit in the frame
  50. if size > min(frame_shape[0], frame_shape[1]):
  51. size = min(frame_shape[0], frame_shape[1])
  52. # x_offset is midpoint of bounding box minus half the size
  53. x_offset = int((xmax-xmin)/2.0+xmin-size/2.0)
  54. # if outside the image
  55. if x_offset < 0:
  56. x_offset = 0
  57. elif x_offset > (frame_shape[1]-size):
  58. x_offset = (frame_shape[1]-size)
  59. # y_offset is midpoint of bounding box minus half the size
  60. y_offset = int((ymax-ymin)/2.0+ymin-size/2.0)
  61. # if outside the image
  62. if y_offset < 0:
  63. y_offset = 0
  64. elif y_offset > (frame_shape[0]-size):
  65. y_offset = (frame_shape[0]-size)
  66. return (x_offset, y_offset, x_offset+size, y_offset+size)
  67. def yuv_region_2_rgb(frame, region):
  68. height = frame.shape[0]//3*2
  69. width = frame.shape[1]
  70. # make sure the size is a multiple of 4
  71. size = (region[3] - region[1])//4*4
  72. x1 = region[0]
  73. y1 = region[1]
  74. uv_x1 = x1//2
  75. uv_y1 = y1//4
  76. uv_width = size//2
  77. uv_height = size//4
  78. u_y_start = height
  79. v_y_start = height + height//4
  80. two_x_offset = width//2
  81. yuv_cropped_frame = np.zeros((size+size//2, size), np.uint8)
  82. # y channel
  83. yuv_cropped_frame[0:size, 0:size] = frame[y1:y1+size, x1:x1+size]
  84. # u channel
  85. yuv_cropped_frame[size:size+uv_height, 0:uv_width] = frame[uv_y1+u_y_start:uv_y1+u_y_start+uv_height, uv_x1:uv_x1+uv_width]
  86. yuv_cropped_frame[size:size+uv_height, uv_width:size] = frame[uv_y1+u_y_start:uv_y1+u_y_start+uv_height, uv_x1+two_x_offset:uv_x1+two_x_offset+uv_width]
  87. # v channel
  88. yuv_cropped_frame[size+uv_height:size+uv_height*2, 0:uv_width] = frame[uv_y1+v_y_start:uv_y1+v_y_start+uv_height, uv_x1:uv_x1+uv_width]
  89. yuv_cropped_frame[size+uv_height:size+uv_height*2, uv_width:size] = frame[uv_y1+v_y_start:uv_y1+v_y_start+uv_height, uv_x1+two_x_offset:uv_x1+two_x_offset+uv_width]
  90. return cv2.cvtColor(yuv_cropped_frame, cv2.COLOR_YUV2RGB_I420)
  91. def intersection(box_a, box_b):
  92. return (
  93. max(box_a[0], box_b[0]),
  94. max(box_a[1], box_b[1]),
  95. min(box_a[2], box_b[2]),
  96. min(box_a[3], box_b[3])
  97. )
  98. def area(box):
  99. return (box[2]-box[0] + 1)*(box[3]-box[1] + 1)
  100. def intersection_over_union(box_a, box_b):
  101. # determine the (x, y)-coordinates of the intersection rectangle
  102. intersect = intersection(box_a, box_b)
  103. # compute the area of intersection rectangle
  104. inter_area = max(0, intersect[2] - intersect[0] + 1) * max(0, intersect[3] - intersect[1] + 1)
  105. if inter_area == 0:
  106. return 0.0
  107. # compute the area of both the prediction and ground-truth
  108. # rectangles
  109. box_a_area = (box_a[2] - box_a[0] + 1) * (box_a[3] - box_a[1] + 1)
  110. box_b_area = (box_b[2] - box_b[0] + 1) * (box_b[3] - box_b[1] + 1)
  111. # compute the intersection over union by taking the intersection
  112. # area and dividing it by the sum of prediction + ground-truth
  113. # areas - the interesection area
  114. iou = inter_area / float(box_a_area + box_b_area - inter_area)
  115. # return the intersection over union value
  116. return iou
  117. def clipped(obj, frame_shape):
  118. # if the object is within 5 pixels of the region border, and the region is not on the edge
  119. # consider the object to be clipped
  120. box = obj[2]
  121. region = obj[4]
  122. if ((region[0] > 5 and box[0]-region[0] <= 5) or
  123. (region[1] > 5 and box[1]-region[1] <= 5) or
  124. (frame_shape[1]-region[2] > 5 and region[2]-box[2] <= 5) or
  125. (frame_shape[0]-region[3] > 5 and region[3]-box[3] <= 5)):
  126. return True
  127. else:
  128. return False
  129. class EventsPerSecond:
  130. def __init__(self, max_events=1000):
  131. self._start = None
  132. self._max_events = max_events
  133. self._timestamps = []
  134. def start(self):
  135. self._start = datetime.datetime.now().timestamp()
  136. def update(self):
  137. if self._start is None:
  138. self.start()
  139. self._timestamps.append(datetime.datetime.now().timestamp())
  140. # truncate the list when it goes 100 over the max_size
  141. if len(self._timestamps) > self._max_events+100:
  142. self._timestamps = self._timestamps[(1-self._max_events):]
  143. def eps(self, last_n_seconds=10):
  144. if self._start is None:
  145. self.start()
  146. # compute the (approximate) events in the last n seconds
  147. now = datetime.datetime.now().timestamp()
  148. seconds = min(now-self._start, last_n_seconds)
  149. return len([t for t in self._timestamps if t > (now-last_n_seconds)]) / seconds
  150. def print_stack(sig, frame):
  151. traceback.print_stack(frame)
  152. def listen():
  153. signal.signal(signal.SIGUSR1, print_stack)
  154. class FrameManager(ABC):
  155. @abstractmethod
  156. def create(self, name, size) -> AnyStr:
  157. pass
  158. @abstractmethod
  159. def get(self, name, timeout_ms=0):
  160. pass
  161. @abstractmethod
  162. def close(self, name):
  163. pass
  164. @abstractmethod
  165. def delete(self, name):
  166. pass
  167. class DictFrameManager(FrameManager):
  168. def __init__(self):
  169. self.frames = {}
  170. def create(self, name, size) -> AnyStr:
  171. mem = bytearray(size)
  172. self.frames[name] = mem
  173. return mem
  174. def get(self, name, shape):
  175. mem = self.frames[name]
  176. return np.ndarray(shape, dtype=np.uint8, buffer=mem)
  177. def close(self, name):
  178. pass
  179. def delete(self, name):
  180. del self.frames[name]
  181. class SharedMemoryFrameManager(FrameManager):
  182. def __init__(self):
  183. self.shm_store = {}
  184. def create(self, name, size) -> AnyStr:
  185. shm = shared_memory.SharedMemory(name=name, create=True, size=size)
  186. self.shm_store[name] = shm
  187. return shm.buf
  188. def get(self, name, shape):
  189. if name in self.shm_store:
  190. shm = self.shm_store[name]
  191. else:
  192. shm = shared_memory.SharedMemory(name=name)
  193. self.shm_store[name] = shm
  194. return np.ndarray(shape, dtype=np.uint8, buffer=shm.buf)
  195. def close(self, name):
  196. if name in self.shm_store:
  197. self.shm_store[name].close()
  198. del self.shm_store[name]
  199. def delete(self, name):
  200. if name in self.shm_store:
  201. self.shm_store[name].close()
  202. self.shm_store[name].unlink()
  203. del self.shm_store[name]