visualizer.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. # Copyright (c) 2024 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. import numpy as np
  15. import PIL
  16. from PIL import Image, ImageDraw, ImageFont
  17. from ......utils import logging
  18. from ......utils.deps import function_requires_deps, is_dep_available
  19. from ......utils.fonts import PINGFANG_FONT
  20. if is_dep_available("pycocotools"):
  21. from pycocotools.coco import COCO
  22. def colormap(rgb=False):
  23. """
  24. Get colormap
  25. The code of this function is copied from https://github.com/facebookresearch/Detectron/blob/main/detectron/\
  26. utils/colormap.py
  27. """
  28. color_list = np.array(
  29. [
  30. 0xFF,
  31. 0x00,
  32. 0x00,
  33. 0xCC,
  34. 0xFF,
  35. 0x00,
  36. 0x00,
  37. 0xFF,
  38. 0x66,
  39. 0x00,
  40. 0x66,
  41. 0xFF,
  42. 0xCC,
  43. 0x00,
  44. 0xFF,
  45. 0xFF,
  46. 0x4D,
  47. 0x00,
  48. 0x80,
  49. 0xFF,
  50. 0x00,
  51. 0x00,
  52. 0xFF,
  53. 0xB2,
  54. 0x00,
  55. 0x1A,
  56. 0xFF,
  57. 0xFF,
  58. 0x00,
  59. 0xE5,
  60. 0xFF,
  61. 0x99,
  62. 0x00,
  63. 0x33,
  64. 0xFF,
  65. 0x00,
  66. 0x00,
  67. 0xFF,
  68. 0xFF,
  69. 0x33,
  70. 0x00,
  71. 0xFF,
  72. 0xFF,
  73. 0x00,
  74. 0x99,
  75. 0xFF,
  76. 0xE5,
  77. 0x00,
  78. 0x00,
  79. 0xFF,
  80. 0x1A,
  81. 0x00,
  82. 0xB2,
  83. 0xFF,
  84. 0x80,
  85. 0x00,
  86. 0xFF,
  87. 0xFF,
  88. 0x00,
  89. 0x4D,
  90. ]
  91. ).astype(np.float32)
  92. color_list = color_list.reshape((-1, 3))
  93. if not rgb:
  94. color_list = color_list[:, ::-1]
  95. return color_list.astype("int32")
  96. def font_colormap(color_index):
  97. """
  98. Get font color according to the index of colormap
  99. """
  100. dark = np.array([0x14, 0x0E, 0x35])
  101. light = np.array([0xFF, 0xFF, 0xFF])
  102. light_indexs = [0, 3, 4, 8, 9, 13, 14, 18, 19]
  103. if color_index in light_indexs:
  104. return light.astype("int32")
  105. else:
  106. return dark.astype("int32")
  107. @function_requires_deps("pycocotools")
  108. def draw_bbox(image, coco_info: "COCO", img_id):
  109. """
  110. Draw bbox on image
  111. """
  112. try:
  113. image_info = coco_info.loadImgs(img_id)[0]
  114. font_size = int(0.024 * int(image_info["width"])) + 2
  115. except:
  116. font_size = 12
  117. font = ImageFont.truetype(PINGFANG_FONT.path, font_size, encoding="utf-8")
  118. image = image.convert("RGB")
  119. draw = ImageDraw.Draw(image)
  120. image_size = image.size
  121. width = int(max(image_size) * 0.005)
  122. catid2color = {}
  123. catid2fontcolor = {}
  124. catid_num_dict = {}
  125. color_list = colormap(rgb=True)
  126. annotations = coco_info.loadAnns(coco_info.getAnnIds(imgIds=img_id))
  127. for ann in annotations:
  128. catid = ann["category_id"]
  129. catid_num_dict[catid] = catid_num_dict.get(catid, 0) + 1
  130. for i, (catid, _) in enumerate(
  131. sorted(catid_num_dict.items(), key=lambda x: x[1], reverse=True)
  132. ):
  133. if catid not in catid2color:
  134. color_index = i % len(color_list)
  135. catid2color[catid] = color_list[color_index]
  136. catid2fontcolor[catid] = font_colormap(color_index)
  137. for ann in annotations:
  138. catid, bbox = ann["category_id"], ann["bbox"]
  139. color = tuple(catid2color[catid])
  140. font_color = tuple(catid2fontcolor[catid])
  141. if len(bbox) == 4:
  142. # draw bbox
  143. xmin, ymin, w, h = bbox
  144. xmax = xmin + w
  145. ymax = ymin + h
  146. draw.line(
  147. [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin), (xmin, ymin)],
  148. width=width,
  149. fill=color,
  150. )
  151. elif len(bbox) == 8:
  152. x1, y1, x2, y2, x3, y3, x4, y4 = bbox
  153. draw.line(
  154. [(x1, y1), (x2, y2), (x3, y3), (x4, y4), (x1, y1)],
  155. width=width,
  156. fill=color,
  157. )
  158. xmin = min(x1, x2, x3, x4)
  159. ymin = min(y1, y2, y3, y4)
  160. else:
  161. logging.info("Error: The shape of bbox must be [M, 4] or [M, 8]!")
  162. # draw label
  163. label = coco_info.loadCats(catid)[0]["name"]
  164. text = "{}".format(label)
  165. if tuple(map(int, PIL.__version__.split("."))) <= (10, 0, 0):
  166. tw, th = draw.textsize(text, font=font)
  167. else:
  168. left, top, right, bottom = draw.textbbox((0, 0), text, font)
  169. tw, th = right - left, bottom - top
  170. if ymin < th:
  171. draw.rectangle([(xmin, ymin), (xmin + tw + 4, ymin + th + 1)], fill=color)
  172. draw.text((xmin + 2, ymin - 2), text, fill=font_color, font=font)
  173. else:
  174. draw.rectangle([(xmin, ymin - th), (xmin + tw + 4, ymin + 1)], fill=color)
  175. draw.text((xmin + 2, ymin - th - 2), text, fill=font_color, font=font)
  176. return image
  177. @function_requires_deps("pycocotools")
  178. def draw_mask(image, coco_info: "COCO", img_id):
  179. """
  180. Draw mask on image
  181. """
  182. mask_color_id = 0
  183. w_ratio = 0.4
  184. alpha = 0.6
  185. color_list = colormap(rgb=True)
  186. img_array = np.array(image).astype("float32")
  187. h, w = img_array.shape[:2]
  188. annotations = coco_info.loadAnns(coco_info.getAnnIds(imgIds=img_id))
  189. for ann in annotations:
  190. segm = ann["segmentation"]
  191. if not segm:
  192. continue
  193. import pycocotools.mask as mask_util
  194. rles = mask_util.frPyObjects(segm, h, w)
  195. rle = mask_util.merge(rles)
  196. mask = mask_util.decode(rle) * 255
  197. color_mask = color_list[mask_color_id % len(color_list), 0:3]
  198. mask_color_id += 1
  199. for c in range(3):
  200. color_mask[c] = color_mask[c] * (1 - w_ratio) + w_ratio * 255
  201. idx = np.nonzero(mask)
  202. img_array[idx[0], idx[1], :] *= 1.0 - alpha
  203. img_array[idx[0], idx[1], :] += alpha * color_mask
  204. return Image.fromarray(img_array.astype("uint8"))