utils.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import base64
  2. import colorsys
  3. import re
  4. import socket
  5. import traceback
  6. from enum import Enum
  7. from functools import reduce
  8. import requests
  9. from PIL import ImageDraw, ImageFont, Image
  10. from keras import backend as K
  11. import numpy as np
  12. import cv2
  13. def compose(*funcs):
  14. """Compose arbitrarily many functions, evaluated left to right.
  15. Reference: https://mathieularose.com/function-composition-in-python/
  16. """
  17. if funcs:
  18. return reduce(lambda f, g: lambda *a, **kw: g(f(*a, **kw)), funcs)
  19. else:
  20. raise ValueError('Composition of empty sequence not supported.')
  21. def box_iou(b1, b2):
  22. """Return iou tensor
  23. Parameters
  24. ----------
  25. b1: tensor, shape=(i1,...,iN, 4), xywh
  26. b2: tensor, shape=(j, 4), xywh
  27. Returns
  28. -------
  29. iou: tensor, shape=(i1,...,iN, j)
  30. """
  31. # Expand dim to apply broadcasting.
  32. b1 = K.expand_dims(b1, -2)
  33. b1_xy = b1[..., :2]
  34. b1_wh = b1[..., 2:4]
  35. b1_wh_half = b1_wh/2.
  36. b1_mins = b1_xy - b1_wh_half
  37. b1_maxes = b1_xy + b1_wh_half
  38. # Expand dim to apply broadcasting.
  39. b2 = K.expand_dims(b2, 0)
  40. b2_xy = b2[..., :2]
  41. b2_wh = b2[..., 2:4]
  42. b2_wh_half = b2_wh/2.
  43. b2_mins = b2_xy - b2_wh_half
  44. b2_maxes = b2_xy + b2_wh_half
  45. intersect_mins = K.maximum(b1_mins, b2_mins)
  46. intersect_maxes = K.minimum(b1_maxes, b2_maxes)
  47. intersect_wh = K.maximum(intersect_maxes - intersect_mins, 0.)
  48. intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]
  49. b1_area = b1_wh[..., 0] * b1_wh[..., 1]
  50. b2_area = b2_wh[..., 0] * b2_wh[..., 1]
  51. iou = intersect_area / (b1_area + b2_area - intersect_area)
  52. return iou
  53. def get_classes(classes_path):
  54. """loads the classes"""
  55. with open(classes_path) as f:
  56. class_names = f.readlines()
  57. class_names = [c.strip() for c in class_names]
  58. return class_names
  59. def get_anchors(anchors_path):
  60. """loads the anchors from a file"""
  61. with open(anchors_path) as f:
  62. anchors = f.readline()
  63. anchors = [float(x) for x in anchors.split(',')]
  64. return np.array(anchors).reshape(-1, 2)
  65. def get_colors(number, bright=True):
  66. """
  67. Generate random colors for drawing bounding boxes.
  68. To get visually distinct colors, generate them in HSV space then
  69. convert to RGB.
  70. """
  71. if number <= 0:
  72. return []
  73. brightness = 1.0 if bright else 0.7
  74. hsv_tuples = [(x / number, 1., brightness)
  75. for x in range(number)]
  76. colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))
  77. colors = list(
  78. map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)),
  79. colors))
  80. np.random.seed(10101) # Fixed seed for consistent colors across runs.
  81. np.random.shuffle(colors) # Shuffle colors to decorrelate adjacent classes.
  82. np.random.seed(None) # Reset seed to default.
  83. return colors
  84. labelType = Enum('labelType', ('LABEL_TOP_OUTSIDE',
  85. 'LABEL_BOTTOM_OUTSIDE',
  86. 'LABEL_TOP_INSIDE',
  87. 'LABEL_BOTTOM_INSIDE',))
  88. def draw_boxes(image, out_boxes, out_classes, out_scores, class_names, colors):
  89. font = ImageFont.truetype(font='yolo_data/FiraMono-Medium.otf',
  90. size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32'))
  91. thickness = (image.size[0] + image.size[1]) // 300
  92. box_list = []
  93. for i, c in reversed(list(enumerate(out_classes))):
  94. predicted_class = class_names[c]
  95. box = out_boxes[i]
  96. score = out_scores[i]
  97. label = '{} {:.2f}'.format(predicted_class, score)
  98. draw = ImageDraw.Draw(image)
  99. label_size = draw.textsize(label, font)
  100. top, left, bottom, right = box
  101. top = max(0, np.floor(top + 0.5).astype('int32'))
  102. left = max(0, np.floor(left + 0.5).astype('int32'))
  103. bottom = min(image.size[1], np.floor(bottom + 0.5).astype('int32'))
  104. right = min(image.size[0], np.floor(right + 0.5).astype('int32'))
  105. # print(label, (left, top), (right, bottom))
  106. box_list.append([(left, top), (right, bottom)])
  107. if top - label_size[1] >= 0:
  108. text_origin = np.array([left, top - label_size[1]])
  109. else:
  110. text_origin = np.array([left, top + 1])
  111. # My kingdom for a good redistributable image drawing library.
  112. for i in range(thickness):
  113. draw.rectangle(
  114. [left + i, top + i, right - i, bottom - i],
  115. outline=colors[c])
  116. draw.rectangle(
  117. [tuple(text_origin), tuple(text_origin + label_size)],
  118. fill=colors[c])
  119. draw.text(text_origin, label, fill=(0, 0, 0), font=font)
  120. del draw
  121. return image, box_list
  122. def draw_label(image, text, color, coords, label_type=labelType.LABEL_TOP_OUTSIDE):
  123. font = cv2.FONT_HERSHEY_PLAIN
  124. font_scale = 1.
  125. (text_width, text_height) = cv2.getTextSize(text, font, fontScale=font_scale, thickness=1)[0]
  126. padding = 5
  127. rect_height = text_height + padding * 2
  128. rect_width = text_width + padding * 2
  129. (x, y) = coords
  130. if label_type == labelType.LABEL_TOP_OUTSIDE or label_type == labelType.LABEL_BOTTOM_INSIDE:
  131. cv2.rectangle(image, (x, y), (x + rect_width, y - rect_height), color, cv2.FILLED)
  132. cv2.putText(image, text, (x + padding, y - text_height + padding), font,
  133. fontScale=font_scale,
  134. color=(255, 255, 255),
  135. lineType=cv2.LINE_AA)
  136. else:
  137. # LABEL_BOTTOM_OUTSIDE or LABEL_TOP_INSIDE
  138. cv2.rectangle(image, (x, y), (x + rect_width, y + rect_height), color, cv2.FILLED)
  139. cv2.putText(image, text, (x + padding, y + text_height + padding), font,
  140. fontScale=font_scale,
  141. color=(255, 255, 255),
  142. lineType=cv2.LINE_AA)
  143. return image
  144. def pil_resize(image_np, height, width):
  145. image_pil = Image.fromarray(cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB))
  146. image_pil = image_pil.resize((int(width), int(height)), Image.BICUBIC)
  147. image_np = cv2.cvtColor(np.asarray(image_pil), cv2.COLOR_RGB2BGR)
  148. return image_np
  149. def pil_resize_a(image_np, height, width):
  150. image_pil = Image.fromarray(cv2.cvtColor(image_np, cv2.COLOR_BGRA2RGBA))
  151. image_pil = image_pil.resize((int(width), int(height)), Image.BICUBIC)
  152. image_np = cv2.cvtColor(np.asarray(image_pil), cv2.COLOR_RGBA2BGRA)
  153. return image_np
  154. def np2pil(image_np):
  155. image_pil = Image.fromarray(cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB))
  156. return image_pil
  157. def pil2np(image_pil):
  158. image_np = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
  159. return image_np
  160. def np2pil_a(image_np):
  161. image_pil = Image.fromarray(cv2.cvtColor(image_np, cv2.COLOR_BGRA2RGBA))
  162. return image_pil
  163. def pil2np_a(image_pil):
  164. image_np = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGBA2BGRA)
  165. return image_np
  166. def pil_rotate(image_np, angle, fill_color=(255, 255, 255)):
  167. image_pil = np2pil(image_np)
  168. image_rotate = image_pil.rotate(angle, expand=False, fillcolor=fill_color)
  169. image_np = pil2np(image_rotate)
  170. return image_np
  171. def pil_rotate_a(image_np, angle):
  172. image_pil = np2pil_a(image_np)
  173. image_rotate = image_pil.rotate(angle, expand=False, fillcolor=(255, 255, 255))
  174. image_np = pil2np_a(image_rotate)
  175. return image_np
  176. def request_post(url, param, time_out=1000, use_zlib=False):
  177. fails = 0
  178. text = None
  179. while True:
  180. try:
  181. if fails >= 1:
  182. break
  183. headers = {'content-type': 'application/json'}
  184. # result = requests.post(url, data=param, timeout=time_out)
  185. session = requests.Session()
  186. result = session.post(url, data=param, timeout=time_out)
  187. if result.status_code == 200:
  188. text = result.text
  189. break
  190. else:
  191. print('result.status_code', result.status_code)
  192. print('result.text', result.text)
  193. fails += 1
  194. continue
  195. except socket.timeout:
  196. fails += 1
  197. print('timeout! fail times:', fails)
  198. except:
  199. fails += 1
  200. print('fail! fail times:', fails)
  201. traceback.print_exc()
  202. return text
  203. def np2bytes(image_np):
  204. # numpy转为可序列化的string
  205. success, img_encode = cv2.imencode(".jpg", image_np)
  206. # numpy -> bytes
  207. img_bytes = img_encode.tobytes()
  208. return img_bytes
  209. def bytes2np(_b):
  210. try:
  211. # 二进制数据流转np.ndarray [np.uint8: 8位像素]
  212. image_np = cv2.imdecode(np.frombuffer(_b, np.uint8), cv2.IMREAD_COLOR)
  213. # 将rgb转为bgr
  214. # image_np = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)
  215. return image_np
  216. except cv2.error as e:
  217. if "src.empty()" in str(e):
  218. print("bytes2np image is empty!")
  219. return None
  220. except:
  221. traceback.print_exc()
  222. return None
  223. def base64_decode(b64, is_test=False):
  224. # b64 = re.sub(r"\r\n", "\n", b64)
  225. missing_padding = 4 - len(b64) % 4
  226. if missing_padding:
  227. b64 += '=' * missing_padding
  228. if is_test:
  229. return base64.b64decode(b64), b64
  230. return base64.b64decode(b64)