You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

258 lines
11KB

  1. import cv2
  2. import numpy as np
  3. from PIL import Image, ImageDraw, ImageFont
  4. import unicodedata
  5. FONT_PATH = "../AIlib2/conf/platech.ttf"
  6. zhFont = ImageFont.truetype(FONT_PATH, 20, encoding="utf-8")
  7. def get_label_array(color=None, label=None, font=None, fontSize=40, unify=False):
  8. if unify:
  9. x, y, width, height = font.getbbox("标") # 统一数组大小
  10. else:
  11. x, y, width, height = font.getbbox(label)
  12. text_image = np.zeros((height, width, 3), dtype=np.uint8)
  13. text_image = Image.fromarray(text_image)
  14. draw = ImageDraw.Draw(text_image)
  15. draw.rectangle((0, 0, width, height), fill=tuple(color))
  16. draw.text((0, -1), label, fill=(255, 255, 255), font=font)
  17. im_array = np.asarray(text_image)
  18. # scale = fontSize / height
  19. # im_array = cv2.resize(im_array, (0, 0), fx=scale, fy=scale)
  20. scale = height / fontSize
  21. im_array = cv2.resize(im_array, (0, 0), fx=scale, fy=scale)
  22. return im_array
  23. def get_label_arrays(labelNames, colors, fontSize=40, fontPath="platech.ttf"):
  24. font = ImageFont.truetype(fontPath, fontSize, encoding='utf-8')
  25. label_arraylist = [get_label_array(colors[i % 20], label_name, font, fontSize) for i, label_name in
  26. enumerate(labelNames)]
  27. return label_arraylist
  28. def get_label_array_dict(colors, fontSize=40, fontPath="platech.ttf"):
  29. font = ImageFont.truetype(fontPath, fontSize, encoding='utf-8')
  30. all_chinese_characters = []
  31. for char in range(0x4E00, 0x9FFF + 1): # 中文
  32. chinese_character = chr(char)
  33. if unicodedata.category(chinese_character) == 'Lo':
  34. all_chinese_characters.append(chinese_character)
  35. for char in range(0x0041, 0x005B): # 大写字母
  36. all_chinese_characters.append(chr(char))
  37. for char in range(0x0061, 0x007B): # 小写字母
  38. all_chinese_characters.append(chr(char))
  39. for char in range(0x0030, 0x003A): # 数字
  40. all_chinese_characters.append(chr(char))
  41. zh_dict = {}
  42. for code in all_chinese_characters:
  43. arr = get_label_array(colors[2], code, font, fontSize, unify=True)
  44. zh_dict[code] = arr
  45. return zh_dict
  46. def xywh2xyxy(box):
  47. if not isinstance(box[0], (list, tuple, np.ndarray)):
  48. xc, yc, w, h = int(box[0]), int(box[1]), int(box[2]), int(box[3])
  49. bw, bh = int(w / 2), int(h / 2)
  50. lt, yt, rt, yr = xc - bw, yc - bh, xc + bw, yc + bh
  51. box = [(lt, yt), (rt, yt), (rt, yr), (lt, yr)]
  52. return box
  53. def xywh2xyxy2(param):
  54. if not isinstance(param[0], (list, tuple, np.ndarray)):
  55. xc, yc, x2, y2 = int(param[0]), int(param[1]), int(param[2]), int(param[3])
  56. return [(xc, yc), (x2, yc), (x2, y2), (xc, y2)], float(param[4]), int(param[5])
  57. # bw, bh = int(w / 2), int(h / 2)
  58. # lt, yt, rt, yr = xc - bw, yc - bh, xc + bw, yc + bh
  59. # return [(lt, yt), (rt, yt), (rt, yr), (lt, yr)]
  60. return np.asarray(param[0][0:4], np.int32), float(param[1]), int(param[2])
  61. def draw_painting_joint(box, img, label_array, score=0.5, color=None, config=None, isNew=False):
  62. # 识别问题描述图片的高、宽
  63. lh, lw = label_array.shape[0:2]
  64. # 图片的长度和宽度
  65. imh, imw = img.shape[0:2]
  66. box = xywh2xyxy(box)
  67. # 框框左上的位置
  68. x0, y1 = box[0][0], box[0][1]
  69. # if score_location == 'leftTop':
  70. # x0, y1 = box[0][0], box[0][1]
  71. # # 框框左下的位置
  72. # elif score_location == 'leftBottom':
  73. # x0, y1 = box[3][0], box[3][1]
  74. # else:
  75. # x0, y1 = box[0][0], box[0][1]
  76. # x1 框框左上x位置 + 描述的宽
  77. # y0 框框左上y位置 - 描述的高
  78. x1, y0 = x0 + lw, y1 - lh
  79. # 如果y0小于0, 说明超过上边框
  80. if y0 < 0:
  81. y0 = 0
  82. # y1等于文字高度
  83. y1 = y0 + lh
  84. # 如果y1框框的高大于图片高度
  85. if y1 > imh:
  86. # y1等于图片高度
  87. y1 = imh
  88. # y0等于y1减去文字高度
  89. y0 = y1 - lh
  90. # 如果x0小于0
  91. if x0 < 0:
  92. x0 = 0
  93. x1 = x0 + lw
  94. if x1 > imw:
  95. x1 = imw
  96. x0 = x1 - lw
  97. # box_tl = max(int(round(imw / 1920 * 3)), 1) or round(0.002 * (imh + imw) / 2) + 1
  98. '''
  99. 1. img(array) 为ndarray类型(可以为cv.imread)直接读取的数据
  100. 2. box(array):为所画多边形的顶点坐标
  101. 3. 所画四边形是否闭合,通常为True
  102. 4. color(tuple):BGR三个通道的值
  103. 5. thickness(int):画线的粗细
  104. 6. shift:顶点坐标中小数的位数
  105. '''
  106. tl = config[0]
  107. box1 = np.asarray(box, np.int32)
  108. cv2.polylines(img, [box1], True, color, tl)
  109. img[y0:y1, x0:x1, :] = label_array
  110. pts_cls = [(x0, y0), (x1, y1)]
  111. # 把英文字符score画到类别旁边
  112. # tl = max(int(round(imw / 1920 * 3)), 1) or round(0.002 * (imh + imw) / 2) + 1
  113. label = ' %.2f' % score
  114. # tf = max(tl, 1)
  115. # fontScale = float(format(imw / 1920 * 1.1, '.2f')) or tl * 0.33
  116. # fontScale = tl * 0.33
  117. '''
  118. 1. text:要计算大小的文本内容,类型为字符串。
  119. 2. fontFace:字体类型,例如cv2.FONT_HERSHEY_SIMPLEX等。
  120. 3. fontScale:字体大小的缩放因子,例如1.2表示字体大小增加20%。
  121. 4. thickness:文本线条的粗细,以像素为单位。
  122. 5. (text_width, text_height):给定文本在指定字体、字体大小、线条粗细下所占用的像素宽度和高度。
  123. '''
  124. # t_size = cv2.getTextSize(label, 0, fontScale=fontScale, thickness=tf)[0]
  125. t_size = (config[1], config[2])
  126. # if socre_location=='leftTop':
  127. p1, p2 = (pts_cls[1][0], pts_cls[0][1]), (pts_cls[1][0] + t_size[0], pts_cls[1][1])
  128. '''
  129. 1. img:要绘制矩形的图像
  130. 2. pt1:矩形框的左上角坐标,可以是一个包含两个整数的元组或列表,例如(x1, y1)或[x1, y1]。
  131. 3. pt2:矩形框的右下角坐标,可以是一个包含两个整数的元组或列表,例如(x2, y2)或[x2, y2]。
  132. 4. color:矩形框的颜色,可以是一个包含三个整数的元组或列表,例如(255, 0, 0)表示蓝色,或一个标量值,例如255表示白色。颜色顺序为BGR。
  133. 5. thickness:线条的粗细,以像素为单位。如果为负值,则表示要绘制填充矩形。默认值为1。
  134. 6. lineType:线条的类型,可以是cv2.LINE_AA表示抗锯齿线条,或cv2.LINE_4表示4连通线条,或cv2.LINE_8表示8连通线条。默认值为cv2.LINE_8。
  135. 7. shift:坐标点小数点位数。默认值为0。
  136. '''
  137. cv2.rectangle(img, p1, p2, color, -1, cv2.LINE_AA)
  138. p3 = pts_cls[1][0], pts_cls[1][1] - (lh - t_size[1]) // 2
  139. '''
  140. 1. img:要在其上绘制文本的图像
  141. 2. text:要绘制的文本内容,类型为字符串
  142. 3. org:文本起始位置的坐标,可以是一个包含两个整数的元组或列表,例如(x, y)或[x, y]。
  143. 4. fontFace:字体类型,例如cv2.FONT_HERSHEY_SIMPLEX等。
  144. 5. fontScale:字体大小的缩放因子,例如1.2表示字体大小增加20%。
  145. 6. color:文本的颜色,可以是一个包含三个整数的元组或列表,例如(255, 0, 0)表示蓝色,或一个标量值,例如255表示白色。颜色顺序为BGR。
  146. 7. thickness:文本线条的粗细,以像素为单位。默认值为1。
  147. 8. lineType:线条的类型,可以是cv2.LINE_AA表示抗锯齿线条,或cv2.LINE_4表示4连通线条,或cv2.LINE_8表示8连通线条。默认值为cv2.LINE_8。
  148. 9. bottomLeftOrigin:文本起始位置是否为左下角。如果为True,则文本起始位置为左下角,否则为左上角。默认值为False。
  149. '''
  150. if isNew:
  151. cv2.putText(img, label, p3, 0, config[3], [0, 0, 0], thickness=config[4], lineType=cv2.LINE_AA)
  152. else:
  153. cv2.putText(img, label, p3, 0, config[3], [225, 255, 255], thickness=config[4], lineType=cv2.LINE_AA)
  154. return img, box
  155. # 动态标签
  156. def draw_name_joint(box, img, label_array_dict, score=0.5, color=None, config=None, name=""):
  157. label_array = None
  158. for zh in name:
  159. if zh in label_array_dict:
  160. if label_array is None:
  161. label_array = label_array_dict[zh]
  162. else:
  163. label_array = np.concatenate((label_array,label_array_dict[zh]), axis= 1)
  164. # 识别问题描述图片的高、宽
  165. if label_array is None:
  166. lh, lw = 0, 0
  167. else:
  168. lh, lw = label_array.shape[0:2]
  169. # 图片的长度和宽度
  170. imh, imw = img.shape[0:2]
  171. box = xywh2xyxy(box)
  172. # 框框左上的位置
  173. x0, y1 = box[0][0], box[0][1]
  174. x1, y0 = x0 + lw, y1 - lh
  175. # 如果y0小于0, 说明超过上边框
  176. if y0 < 0:
  177. y0 = 0
  178. # y1等于文字高度
  179. y1 = y0 + lh
  180. # 如果y1框框的高大于图片高度
  181. if y1 > imh:
  182. # y1等于图片高度
  183. y1 = imh
  184. # y0等于y1减去文字高度
  185. y0 = y1 - lh
  186. # 如果x0小于0
  187. if x0 < 0:
  188. x0 = 0
  189. x1 = x0 + lw
  190. if x1 > imw:
  191. x1 = imw
  192. x0 = x1 - lw
  193. tl = config[0]
  194. box1 = np.asarray(box, np.int32)
  195. cv2.polylines(img, [box1], True, color, tl)
  196. if label_array is not None:
  197. img[y0:y1, x0:x1, :] = label_array
  198. pts_cls = [(x0, y0), (x1, y1)]
  199. # 把英文字符score画到类别旁边
  200. # tl = max(int(round(imw / 1920 * 3)), 1) or round(0.002 * (imh + imw) / 2) + 1
  201. label = ' %.2f' % score
  202. t_size = (config[1], config[2])
  203. # if socre_location=='leftTop':
  204. p1, p2 = (pts_cls[1][0], pts_cls[0][1]), (pts_cls[1][0] + t_size[0], pts_cls[1][1])
  205. cv2.rectangle(img, p1, p2, color, -1, cv2.LINE_AA)
  206. p3 = pts_cls[1][0], pts_cls[1][1] - (lh - t_size[1]) // 2
  207. cv2.putText(img, label, p3, 0, config[3], [225, 255, 255], thickness=config[4], lineType=cv2.LINE_AA)
  208. return img, box
  209. def filterBox(det0, det1, pix_dis):
  210. # det0为 (m1, 11) 矩阵
  211. # det1为 (m2, 12) 矩阵
  212. if len(det0.shape) == 1:
  213. det0 = det0[np.newaxis,...]
  214. if len(det1.shape) == 1:
  215. det1 = det1[np.newaxis,...]
  216. det1 = det1[...,0:11].copy()
  217. m, n = det0.size, det1.size
  218. if not m:
  219. return det0
  220. # 在det0的列方向加一个元素flag代表该目标框中心点是否在之前目标框内(0代表不在,其他代表在)
  221. flag = np.zeros([len(det0), 1])
  222. det0 = np.concatenate([det0, flag], axis=1)
  223. det0_copy = det0.copy()
  224. # det1_copy = det1.copy()
  225. if not n:
  226. return det0
  227. # det0转成 (m1, m2, 12) 的矩阵
  228. # det1转成 (m1, m2, 12) 的矩阵
  229. # det0与det1在第3维方向上拼接(6 + 7 = 13)
  230. det0 = det0[:, np.newaxis, :].repeat(det1.shape[0], 1)
  231. det1 = det1[np.newaxis, ...].repeat(det0.shape[0], 0)
  232. joint_det = np.concatenate((det1, det0), axis=2)
  233. # 分别求det0和det1的x1, y1, x2, y2(水平框的左上右下角点)
  234. x1, y1, x2, y2 = joint_det[..., 0], joint_det[..., 1], joint_det[..., 4], joint_det[..., 5]
  235. x3, y3, x4, y4 = joint_det[..., 11], joint_det[..., 12], joint_det[..., 15], joint_det[..., 16]
  236. x2_c, y2_c = (x1+x2)//2, (y1+y2)//2
  237. x_c, y_c = (x3+x4)//2, (y3+y4)//2
  238. dis = (x2_c - x_c)**2 + (y2_c - y_c)**2
  239. mask = (joint_det[..., 9] == joint_det[..., 20]) & (dis <= pix_dis**2)
  240. # 类别相同 & 中心点在上一帧的框内 判断为True
  241. res = np.sum(mask, axis=1)
  242. det0_copy[..., -1] = res
  243. return det0_copy