util.py 8.2 KB

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