visualize.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. # Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from __future__ import division
  15. import os
  16. import cv2
  17. import numpy as np
  18. from PIL import Image, ImageDraw, ImageFile
  19. ImageFile.LOAD_TRUNCATED_IMAGES = True
  20. import math
  21. def visualize_box_mask(im, results, labels, threshold=0.5):
  22. """
  23. Args:
  24. im (str/np.ndarray): path of image/np.ndarray read by cv2
  25. results (dict): include 'boxes': np.ndarray: shape:[N,6], N: number of box,
  26. matix element:[class, score, x_min, y_min, x_max, y_max]
  27. MaskRCNN's results include 'masks': np.ndarray:
  28. shape:[N, im_h, im_w]
  29. labels (list): labels:['class1', ..., 'classn']
  30. threshold (float): Threshold of score.
  31. Returns:
  32. im (PIL.Image.Image): visualized image
  33. """
  34. if isinstance(im, str):
  35. im = Image.open(im).convert('RGB')
  36. elif isinstance(im, np.ndarray):
  37. im = Image.fromarray(im)
  38. if 'masks' in results and 'boxes' in results and len(results['boxes']) > 0:
  39. im = draw_mask(
  40. im, results['boxes'], results['masks'], labels, threshold=threshold)
  41. if 'boxes' in results and len(results['boxes']) > 0:
  42. im = draw_box(im, results['boxes'], labels, threshold=threshold)
  43. if 'segm' in results:
  44. im = draw_segm(
  45. im,
  46. results['segm'],
  47. results['label'],
  48. results['score'],
  49. labels,
  50. threshold=threshold)
  51. return im
  52. def get_color_map_list(num_classes):
  53. """
  54. Args:
  55. num_classes (int): number of class
  56. Returns:
  57. color_map (list): RGB color list
  58. """
  59. color_map = num_classes * [0, 0, 0]
  60. for i in range(0, num_classes):
  61. j = 0
  62. lab = i
  63. while lab:
  64. color_map[i * 3] |= (((lab >> 0) & 1) << (7 - j))
  65. color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j))
  66. color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j))
  67. j += 1
  68. lab >>= 3
  69. color_map = [color_map[i:i + 3] for i in range(0, len(color_map), 3)]
  70. return color_map
  71. def draw_mask(im, np_boxes, np_masks, labels, threshold=0.5):
  72. """
  73. Args:
  74. im (PIL.Image.Image): PIL image
  75. np_boxes (np.ndarray): shape:[N,6], N: number of box,
  76. matix element:[class, score, x_min, y_min, x_max, y_max]
  77. np_masks (np.ndarray): shape:[N, im_h, im_w]
  78. labels (list): labels:['class1', ..., 'classn']
  79. threshold (float): threshold of mask
  80. Returns:
  81. im (PIL.Image.Image): visualized image
  82. """
  83. color_list = get_color_map_list(len(labels))
  84. w_ratio = 0.4
  85. alpha = 0.7
  86. im = np.array(im).astype('float32')
  87. clsid2color = {}
  88. expect_boxes = (np_boxes[:, 1] > threshold) & (np_boxes[:, 0] > -1)
  89. np_boxes = np_boxes[expect_boxes, :]
  90. np_masks = np_masks[expect_boxes, :, :]
  91. im_h, im_w = im.shape[:2]
  92. np_masks = np_masks[:, :im_h, :im_w]
  93. for i in range(len(np_masks)):
  94. clsid, score = int(np_boxes[i][0]), np_boxes[i][1]
  95. mask = np_masks[i]
  96. if clsid not in clsid2color:
  97. clsid2color[clsid] = color_list[clsid]
  98. color_mask = clsid2color[clsid]
  99. for c in range(3):
  100. color_mask[c] = color_mask[c] * (1 - w_ratio) + w_ratio * 255
  101. idx = np.nonzero(mask)
  102. color_mask = np.array(color_mask)
  103. im[idx[0], idx[1], :] *= 1.0 - alpha
  104. im[idx[0], idx[1], :] += alpha * color_mask
  105. return Image.fromarray(im.astype('uint8'))
  106. def draw_box(im, np_boxes, labels, threshold=0.5):
  107. """
  108. Args:
  109. im (PIL.Image.Image): PIL image
  110. np_boxes (np.ndarray): shape:[N,6], N: number of box,
  111. matix element:[class, score, x_min, y_min, x_max, y_max]
  112. labels (list): labels:['class1', ..., 'classn']
  113. threshold (float): threshold of box
  114. Returns:
  115. im (PIL.Image.Image): visualized image
  116. """
  117. draw_thickness = min(im.size) // 320
  118. draw = ImageDraw.Draw(im)
  119. clsid2color = {}
  120. color_list = get_color_map_list(len(labels))
  121. expect_boxes = (np_boxes[:, 1] > threshold) & (np_boxes[:, 0] > -1)
  122. np_boxes = np_boxes[expect_boxes, :]
  123. for dt in np_boxes:
  124. clsid, bbox, score = int(dt[0]), dt[2:], dt[1]
  125. if clsid not in clsid2color:
  126. clsid2color[clsid] = color_list[clsid]
  127. color = tuple(clsid2color[clsid])
  128. if len(bbox) == 4:
  129. xmin, ymin, xmax, ymax = bbox
  130. print('class_id:{:d}, confidence:{:.4f}, left_top:[{:.2f},{:.2f}],'
  131. 'right_bottom:[{:.2f},{:.2f}]'.format(
  132. int(clsid), score, xmin, ymin, xmax, ymax))
  133. # draw bbox
  134. draw.line(
  135. [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin),
  136. (xmin, ymin)],
  137. width=draw_thickness,
  138. fill=color)
  139. elif len(bbox) == 8:
  140. x1, y1, x2, y2, x3, y3, x4, y4 = bbox
  141. draw.line(
  142. [(x1, y1), (x2, y2), (x3, y3), (x4, y4), (x1, y1)],
  143. width=2,
  144. fill=color)
  145. xmin = min(x1, x2, x3, x4)
  146. ymin = min(y1, y2, y3, y4)
  147. # draw label
  148. text = "{} {:.4f}".format(labels[clsid], score)
  149. tw, th = draw.textsize(text)
  150. draw.rectangle(
  151. [(xmin + 1, ymin - th), (xmin + tw + 1, ymin)], fill=color)
  152. draw.text((xmin + 1, ymin - th), text, fill=(255, 255, 255))
  153. return im
  154. def draw_segm(im,
  155. np_segms,
  156. np_label,
  157. np_score,
  158. labels,
  159. threshold=0.5,
  160. alpha=0.7):
  161. """
  162. Draw segmentation on image
  163. """
  164. mask_color_id = 0
  165. w_ratio = .4
  166. color_list = get_color_map_list(len(labels))
  167. im = np.array(im).astype('float32')
  168. clsid2color = {}
  169. np_segms = np_segms.astype(np.uint8)
  170. for i in range(np_segms.shape[0]):
  171. mask, score, clsid = np_segms[i], np_score[i], np_label[i]
  172. if score < threshold:
  173. continue
  174. if clsid not in clsid2color:
  175. clsid2color[clsid] = color_list[clsid]
  176. color_mask = clsid2color[clsid]
  177. for c in range(3):
  178. color_mask[c] = color_mask[c] * (1 - w_ratio) + w_ratio * 255
  179. idx = np.nonzero(mask)
  180. color_mask = np.array(color_mask)
  181. idx0 = np.minimum(idx[0], im.shape[0] - 1)
  182. idx1 = np.minimum(idx[1], im.shape[1] - 1)
  183. im[idx0, idx1, :] *= 1.0 - alpha
  184. im[idx0, idx1, :] += alpha * color_mask
  185. sum_x = np.sum(mask, axis=0)
  186. x = np.where(sum_x > 0.5)[0]
  187. sum_y = np.sum(mask, axis=1)
  188. y = np.where(sum_y > 0.5)[0]
  189. x0, x1, y0, y1 = x[0], x[-1], y[0], y[-1]
  190. cv2.rectangle(im, (x0, y0), (x1, y1),
  191. tuple(color_mask.astype('int32').tolist()), 1)
  192. bbox_text = '%s %.2f' % (labels[clsid], score)
  193. t_size = cv2.getTextSize(bbox_text, 0, 0.3, thickness=1)[0]
  194. cv2.rectangle(im, (x0, y0), (x0 + t_size[0], y0 - t_size[1] - 3),
  195. tuple(color_mask.astype('int32').tolist()), -1)
  196. cv2.putText(
  197. im,
  198. bbox_text, (x0, y0 - 2),
  199. cv2.FONT_HERSHEY_SIMPLEX,
  200. 0.3, (0, 0, 0),
  201. 1,
  202. lineType=cv2.LINE_AA)
  203. return Image.fromarray(im.astype('uint8'))
  204. def get_color(idx):
  205. idx = idx * 3
  206. color = ((37 * idx) % 255, (17 * idx) % 255, (29 * idx) % 255)
  207. return color
  208. def visualize_pose(imgfile,
  209. results,
  210. visual_thresh=0.6,
  211. save_name='pose.jpg',
  212. save_dir='output',
  213. returnimg=False,
  214. ids=None):
  215. try:
  216. import matplotlib.pyplot as plt
  217. import matplotlib
  218. plt.switch_backend('agg')
  219. except Exception as e:
  220. print('Matplotlib not found, please install matplotlib.'
  221. 'for example: `pip install matplotlib`.')
  222. raise e
  223. skeletons, scores = results['keypoint']
  224. skeletons = np.array(skeletons)
  225. kpt_nums = 17
  226. if len(skeletons) > 0:
  227. kpt_nums = skeletons.shape[1]
  228. if kpt_nums == 17: #plot coco keypoint
  229. EDGES = [(0, 1), (0, 2), (1, 3), (2, 4), (3, 5), (4, 6), (5, 7), (6, 8),
  230. (7, 9), (8, 10), (5, 11), (6, 12), (11, 13), (12, 14),
  231. (13, 15), (14, 16), (11, 12)]
  232. else: #plot mpii keypoint
  233. EDGES = [(0, 1), (1, 2), (3, 4), (4, 5), (2, 6), (3, 6), (6, 7), (7, 8),
  234. (8, 9), (10, 11), (11, 12), (13, 14), (14, 15), (8, 12),
  235. (8, 13)]
  236. NUM_EDGES = len(EDGES)
  237. colors = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0], [85, 255, 0], [0, 255, 0], \
  238. [0, 255, 85], [0, 255, 170], [0, 255, 255], [0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], \
  239. [170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85]]
  240. cmap = matplotlib.cm.get_cmap('hsv')
  241. plt.figure()
  242. img = cv2.imread(imgfile) if type(imgfile) == str else imgfile
  243. color_set = results['colors'] if 'colors' in results else None
  244. if 'bbox' in results and ids is None:
  245. bboxs = results['bbox']
  246. for j, rect in enumerate(bboxs):
  247. xmin, ymin, xmax, ymax = rect
  248. color = colors[0] if color_set is None else colors[color_set[j] %
  249. len(colors)]
  250. cv2.rectangle(img, (xmin, ymin), (xmax, ymax), color, 1)
  251. canvas = img.copy()
  252. for i in range(kpt_nums):
  253. for j in range(len(skeletons)):
  254. if skeletons[j][i, 2] < visual_thresh:
  255. continue
  256. if ids is None:
  257. color = colors[i] if color_set is None else colors[color_set[j]
  258. %
  259. len(colors)]
  260. else:
  261. color = get_color(ids[j])
  262. cv2.circle(
  263. canvas,
  264. tuple(skeletons[j][i, 0:2].astype('int32')),
  265. 2,
  266. color,
  267. thickness=-1)
  268. to_plot = cv2.addWeighted(img, 0.3, canvas, 0.7, 0)
  269. fig = matplotlib.pyplot.gcf()
  270. stickwidth = 2
  271. for i in range(NUM_EDGES):
  272. for j in range(len(skeletons)):
  273. edge = EDGES[i]
  274. if skeletons[j][edge[0], 2] < visual_thresh or skeletons[j][edge[
  275. 1], 2] < visual_thresh:
  276. continue
  277. cur_canvas = canvas.copy()
  278. X = [skeletons[j][edge[0], 1], skeletons[j][edge[1], 1]]
  279. Y = [skeletons[j][edge[0], 0], skeletons[j][edge[1], 0]]
  280. mX = np.mean(X)
  281. mY = np.mean(Y)
  282. length = ((X[0] - X[1])**2 + (Y[0] - Y[1])**2)**0.5
  283. angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1]))
  284. polygon = cv2.ellipse2Poly((int(mY), int(mX)),
  285. (int(length / 2), stickwidth),
  286. int(angle), 0, 360, 1)
  287. if ids is None:
  288. color = colors[i] if color_set is None else colors[color_set[j]
  289. %
  290. len(colors)]
  291. else:
  292. color = get_color(ids[j])
  293. cv2.fillConvexPoly(cur_canvas, polygon, color)
  294. canvas = cv2.addWeighted(canvas, 0.4, cur_canvas, 0.6, 0)
  295. if returnimg:
  296. return canvas
  297. save_name = os.path.join(
  298. save_dir, os.path.splitext(os.path.basename(imgfile))[0] + '_vis.jpg')
  299. plt.imsave(save_name, canvas[:, :, ::-1])
  300. print("keypoint visualize image saved to: " + save_name)
  301. plt.close()
  302. def visualize_attr(im, results, boxes=None, is_mtmct=False):
  303. if isinstance(im, str):
  304. im = Image.open(im)
  305. im = np.ascontiguousarray(np.copy(im))
  306. im = cv2.cvtColor(im, cv2.COLOR_RGB2BGR)
  307. else:
  308. im = np.ascontiguousarray(np.copy(im))
  309. im_h, im_w = im.shape[:2]
  310. text_scale = max(0.5, im.shape[0] / 3000.)
  311. text_thickness = 1
  312. line_inter = im.shape[0] / 40.
  313. for i, res in enumerate(results):
  314. if boxes is None:
  315. text_w = 3
  316. text_h = 1
  317. elif is_mtmct:
  318. box = boxes[i] # multi camera, bbox shape is x,y, w,h
  319. text_w = int(box[0]) + 3
  320. text_h = int(box[1])
  321. else:
  322. box = boxes[i] # single camera, bbox shape is 0, 0, x,y, w,h
  323. text_w = int(box[2]) + 3
  324. text_h = int(box[3])
  325. for text in res:
  326. text_h += int(line_inter)
  327. text_loc = (text_w, text_h)
  328. cv2.putText(
  329. im,
  330. text,
  331. text_loc,
  332. cv2.FONT_ITALIC,
  333. text_scale, (0, 255, 255),
  334. thickness=text_thickness)
  335. return im
  336. def visualize_action(im,
  337. mot_boxes,
  338. action_visual_collector=None,
  339. action_text="",
  340. video_action_score=None,
  341. video_action_text=""):
  342. im = cv2.imread(im) if isinstance(im, str) else im
  343. im_h, im_w = im.shape[:2]
  344. text_scale = max(1, im.shape[1] / 400.)
  345. text_thickness = 2
  346. if action_visual_collector:
  347. id_action_dict = {}
  348. for collector, action_type in zip(action_visual_collector, action_text):
  349. id_detected = collector.get_visualize_ids()
  350. for pid in id_detected:
  351. id_action_dict[pid] = id_action_dict.get(pid, [])
  352. id_action_dict[pid].append(action_type)
  353. for mot_box in mot_boxes:
  354. # mot_box is a format with [mot_id, class, score, xmin, ymin, w, h]
  355. if mot_box[0] in id_action_dict:
  356. text_position = (int(mot_box[3] + mot_box[5] * 0.75),
  357. int(mot_box[4] - 10))
  358. display_text = ', '.join(id_action_dict[mot_box[0]])
  359. cv2.putText(im, display_text, text_position,
  360. cv2.FONT_HERSHEY_PLAIN, text_scale, (0, 0, 255), 2)
  361. if video_action_score:
  362. cv2.putText(
  363. im,
  364. video_action_text + ': %.2f' % video_action_score,
  365. (int(im_w / 2), int(15 * text_scale) + 5),
  366. cv2.FONT_ITALIC,
  367. text_scale, (0, 0, 255),
  368. thickness=text_thickness)
  369. return im
  370. def visualize_vehicleplate(im, results, boxes=None):
  371. if isinstance(im, str):
  372. im = Image.open(im)
  373. im = np.ascontiguousarray(np.copy(im))
  374. im = cv2.cvtColor(im, cv2.COLOR_RGB2BGR)
  375. else:
  376. im = np.ascontiguousarray(np.copy(im))
  377. im_h, im_w = im.shape[:2]
  378. text_scale = max(1.0, im.shape[0] / 400.)
  379. text_thickness = 2
  380. line_inter = im.shape[0] / 40.
  381. for i, res in enumerate(results):
  382. if boxes is None:
  383. text_w = 3
  384. text_h = 1
  385. else:
  386. box = boxes[i]
  387. text = res
  388. if text == "":
  389. continue
  390. text_w = int(box[2])
  391. text_h = int(box[5] + box[3])
  392. text_loc = (text_w, text_h)
  393. cv2.putText(
  394. im,
  395. "LP: " + text,
  396. text_loc,
  397. cv2.FONT_ITALIC,
  398. text_scale, (0, 255, 255),
  399. thickness=text_thickness)
  400. return im
  401. def draw_press_box_lanes(im, np_boxes, labels, threshold=0.5):
  402. """
  403. Args:
  404. im (PIL.Image.Image): PIL image
  405. np_boxes (np.ndarray): shape:[N,6], N: number of box,
  406. matix element:[class, score, x_min, y_min, x_max, y_max]
  407. labels (list): labels:['class1', ..., 'classn']
  408. threshold (float): threshold of box
  409. Returns:
  410. im (PIL.Image.Image): visualized image
  411. """
  412. if isinstance(im, str):
  413. im = Image.open(im).convert('RGB')
  414. elif isinstance(im, np.ndarray):
  415. im = Image.fromarray(im)
  416. draw_thickness = min(im.size) // 320
  417. draw = ImageDraw.Draw(im)
  418. clsid2color = {}
  419. color_list = get_color_map_list(len(labels))
  420. if np_boxes.shape[1] == 7:
  421. np_boxes = np_boxes[:, 1:]
  422. expect_boxes = (np_boxes[:, 1] > threshold) & (np_boxes[:, 0] > -1)
  423. np_boxes = np_boxes[expect_boxes, :]
  424. for dt in np_boxes:
  425. clsid, bbox, score = int(dt[0]), dt[2:], dt[1]
  426. if clsid not in clsid2color:
  427. clsid2color[clsid] = color_list[clsid]
  428. color = tuple(clsid2color[clsid])
  429. if len(bbox) == 4:
  430. xmin, ymin, xmax, ymax = bbox
  431. # draw bbox
  432. draw.line(
  433. [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin),
  434. (xmin, ymin)],
  435. width=draw_thickness,
  436. fill=(0, 0, 255))
  437. elif len(bbox) == 8:
  438. x1, y1, x2, y2, x3, y3, x4, y4 = bbox
  439. draw.line(
  440. [(x1, y1), (x2, y2), (x3, y3), (x4, y4), (x1, y1)],
  441. width=2,
  442. fill=color)
  443. xmin = min(x1, x2, x3, x4)
  444. ymin = min(y1, y2, y3, y4)
  445. # draw label
  446. text = "{}".format(labels[clsid])
  447. tw, th = draw.textsize(text)
  448. draw.rectangle(
  449. [(xmin + 1, ymax - th), (xmin + tw + 1, ymax)], fill=color)
  450. draw.text((xmin + 1, ymax - th), text, fill=(0, 0, 255))
  451. return im
  452. def visualize_vehiclepress(im, results, threshold=0.5):
  453. results = np.array(results)
  454. labels = ['violation']
  455. im = draw_press_box_lanes(im, results, labels, threshold=threshold)
  456. return im
  457. def visualize_lane(im, lanes):
  458. if isinstance(im, str):
  459. im = Image.open(im).convert('RGB')
  460. elif isinstance(im, np.ndarray):
  461. im = Image.fromarray(im)
  462. draw_thickness = min(im.size) // 320
  463. draw = ImageDraw.Draw(im)
  464. if len(lanes) > 0:
  465. for lane in lanes:
  466. draw.line(
  467. [(lane[0], lane[1]), (lane[2], lane[3])],
  468. width=draw_thickness,
  469. fill=(0, 0, 255))
  470. return im
  471. def visualize_vehicle_retrograde(im, mot_res, vehicle_retrograde_res):
  472. if isinstance(im, str):
  473. im = Image.open(im).convert('RGB')
  474. elif isinstance(im, np.ndarray):
  475. im = Image.fromarray(im)
  476. draw_thickness = min(im.size) // 320
  477. draw = ImageDraw.Draw(im)
  478. lane = vehicle_retrograde_res['fence_line']
  479. if lane is not None:
  480. draw.line(
  481. [(lane[0], lane[1]), (lane[2], lane[3])],
  482. width=draw_thickness,
  483. fill=(0, 0, 0))
  484. mot_id = vehicle_retrograde_res['output']
  485. if mot_id is None or len(mot_id) == 0:
  486. return im
  487. if mot_res is None:
  488. return im
  489. np_boxes = mot_res['boxes']
  490. if np_boxes is not None:
  491. for dt in np_boxes:
  492. if dt[0] not in mot_id:
  493. continue
  494. bbox = dt[3:]
  495. if len(bbox) == 4:
  496. xmin, ymin, xmax, ymax = bbox
  497. # draw bbox
  498. draw.line(
  499. [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin),
  500. (xmin, ymin)],
  501. width=draw_thickness,
  502. fill=(0, 255, 0))
  503. # draw label
  504. text = "retrograde"
  505. tw, th = draw.textsize(text)
  506. draw.rectangle(
  507. [(xmax + 1, ymin - th), (xmax + tw + 1, ymin)],
  508. fill=(0, 255, 0))
  509. draw.text((xmax + 1, ymin - th), text, fill=(0, 255, 0))
  510. return im