plt_旋转标记框_绘制在图片上

前情提要:旋转标记框,较之前普通的标记框可以更好的贴合目标物体。

  • 参考链接:https://blog.csdn.net/qq_36449741/article/details/107366835

需求:将旋转的标记框绘制在图片上。

  • 普通标记框可以通过  patches.Rectangle()方法进行绘制,虽然该方法有一个旋转角度的参数,但该方法的旋转方式是通过:以标记框左上角的标记点为中心进行旋转的,而不是以标记框的中心坐标进行旋转。

实现思路:

  • 尝试了使用 patches.Rectangle()的旋转角度参数进行绘制,但怎么转换旋转后的坐标暂时没有搞清楚,故采取别的方式。
  • patches.Rectangle()的目的是绘制矩形框,而矩形框可以由 4 条线段组成(相邻的线段互相垂直),那当然可以通过绘制 4 条线段的方式组成对应的标记框
    链接:https://blog.csdn.net/weixin_47873308/article/details/113059744 ,中给出了 “ 一个点以另一个点为中心,计算旋转后的坐标公式 ”,故可以通过公式,得出旋转后矩形框 4 个点的坐标,进而绘制出对应矩形框。
    • 旋转公式具体如下
       
    • 1 假设对图片上任意点(x,y),绕一个坐标点(rx0,ry0)逆时针旋转a角度后的新的坐标设为(x0, y0),有公式:
      2 x0= (x - rx0)*cos(a) - (y - ry0)*sin(a) + rx0 
      3 y0= (x - rx0)*sin(a) + (y - ry0)*cos(a) + ry0 

原图:

将标记框标注在图像后的结果:

  • 黄色标记框为旋转角度为 0 的标记框,红色标记框为以标记框中心为旋转中心,旋转之后的标记框

 具体实现代码如下:

  1 ''' 参考链接 :https://blog.csdn.net/weixin_47873308/article/details/113059744
  2 假设对图片上任意点(x,y),绕一个坐标点(rx0,ry0)逆时针旋转a角度后的新的坐标设为(x0, y0),有公式:
  3 x0= (x - rx0)*cos(a) - (y - ry0)*sin(a) + rx0
  4 y0= (x - rx0)*sin(a) + (y - ry0)*cos(a) + ry0
  5 '''
  6 
  7 import math
  8 import xml.dom.minidom
  9 import matplotlib.pyplot as plt
 10 from matplotlib.image import imread
 11 import matplotlib.patches as patches
 12 from pylab import *
 13 mpl.rcParams['font.sans-serif']=['SimHei']
 14 mpl.rcParams['axes.unicode_minus']=False
 15 
 16 ### 一个点以另一个点为中心旋转一定的角度
 17 def point_rotate(x, y, cx, cy, angle):
 18     x0 = (x - cx) * cos(angle) - (y - cy) * sin(angle) + cx
 19     y0 = (x - cx) * sin(angle) + (y - cy) * cos(angle) + cy
 20     return x0, y0
 21 
 22 ### 绘制旋转标记框,通过四条线段组成矩形的方式进行绘制
 23 # 传入 c_x, c_y, w, h  通过绘制四条直线画出标记框
 24 def draw_bbox_by_lines(currentAxis, box, box_color):
 25     print(box)
 26     angle, cx, cy = box[0], box[1], box[2]
 27     # 计算出旋转前的各坐标
 28     x1, y1 = box[1]-box[3]/2, box[2]-box[4]/2   # 左上
 29     x2, y2 = box[1]+box[3]/2, box[2]-box[4]/2   # 右上
 30     x3, y3 = box[1]-box[3]/2, box[2]+box[4]/2   # 左下
 31     x4, y4 = box[1]+box[3]/2, box[2]+box[4]/2   # 右下
 32     # 以标记框中心为旋转中心,旋转后标记框各点的坐标
 33     x1, y1 = point_rotate(x1, y1, cx, cy, angle)
 34     x2, y2 = point_rotate(x2, y2, cx, cy, angle)
 35     x3, y3 = point_rotate(x3, y3, cx, cy, angle)
 36     x4, y4 = point_rotate(x4, y4, cx, cy, angle)
 37     # 绘制直线,通过4条直线完成标记框的绘制
 38     currentAxis.plot([x1, x2], [y1, y2], color = box_color)
 39     currentAxis.plot([x1, x3], [y1, y3], color = box_color)
 40     currentAxis.plot([x4, x2], [y4, y2], color = box_color)
 41     currentAxis.plot([x4, x3], [y4, y3], color = box_color)
 42 
 43 # 定义画矩形框的函数
 44 def draw_rectangle(currentAxis, bbox, edgecolor='y', facecolor='r', fill=False, linestyle='-'):
 45     # 坐标格式为 xmin ymin w h 。 可以不是 int 类型
 46     my_angle, xmin, ymin, w, h = bbox[0], bbox[1], bbox[2], bbox[3], bbox[4]
 47     print('****************', my_angle, '********************') # my_angle/math.pi*180
 48     rect = patches.Rectangle( (xmin, ymin), w, h, angle=my_angle, linewidth=1, edgecolor=edgecolor, facecolor=facecolor, fill=fill, linestyle=linestyle)
 49     currentAxis.add_patch(rect)
 50 
 51 ###  返回 xml文件中的标签名,和标记框数据
 52 # 输入:xml 路径
 53 # 返回值:一个列表,
 54 #           列表中每个元素为一个标记框的信息:标签名称, [旋转角度 坐标值]
 55 def get_bbox(xml_path):
 56     # print('xml 文件名称\t ', xml_path)
 57     box_list = []
 58     # 打开 xml文档
 59     DOMTree = xml.dom.minidom.parse(xml_path)
 60     # 得到文档元素对象
 61     collection = DOMTree.documentElement
 62     ### 文件夹名称
 63     folder_name = collection.getElementsByTagName("folder")[0].childNodes[0].data
 64     # print('文件夹名称\t', folder_name)
 65     # 文件名
 66     filenamelist = collection.getElementsByTagName("filename")[0].childNodes[0].data
 67     # print('文件名\t\t', filenamelist)
 68     # 文件路径
 69     file_path = collection.getElementsByTagName("path")[0].childNodes[0].data
 70     # print('文件路径\t\t', file_path)
 71     # 图像 size
 72     file_size = collection.getElementsByTagName("size")
 73     file_width = file_size[0].getElementsByTagName('width')[0].childNodes[0].data
 74     file_height = file_size[0].getElementsByTagName('height')[0].childNodes[0].data
 75     file_depth = file_size[0].getElementsByTagName('depth')[0].childNodes[0].data
 76     # print('图片尺寸\t\t宽 {}, 高 {}, 通道数 {}'.format(file_width, file_height, file_depth))
 77     ### 标记框信息
 78     objectlist = collection.getElementsByTagName("object")
 79     for objects in objectlist:
 80         # print('==============标记框信息==============')
 81         # 标记框类型
 82         box_type = objects.getElementsByTagName('type')[0].childNodes[0].data
 83         # print('标记框类型 \t', box_type)
 84         # 标签名称
 85         box_name = objects.getElementsByTagName('name')[0].childNodes[0].data
 86         # print('标记框类别名\t', box_name)
 87         ### 旋转标记框 , 最终结果统一为:xmin  ymin  w  h  angle
 88         if box_type == 'robndbox':
 89             bndbox = objects.getElementsByTagName('robndbox')
 90             for box in bndbox:
 91                 c_x = float(box.getElementsByTagName('cx')[0].childNodes[0].data)
 92                 c_y = float(box.getElementsByTagName('cy')[0].childNodes[0].data)
 93                 w = float(box.getElementsByTagName('w')[0].childNodes[0].data)
 94                 h = float(box.getElementsByTagName('h')[0].childNodes[0].data)
 95                 angle = float(box.getElementsByTagName('angle')[0].childNodes[0].data)
 96                 # bbox = [angle, c_x-w/2, c_y-h/2, w, h]
 97                 bbox = [angle, c_x, c_y, w, h]
 98                 # print('旋转标记框,角度+坐标值:', bbox)
 99         ### 普通标记框
100         elif box_type == 'bndbox':
101             bndbox = objects.getElementsByTagName('bndbox')
102             for box in bndbox:
103                 xmin = int(box.getElementsByTagName('xmin')[0].childNodes[0].data)
104                 ymin = int(box.getElementsByTagName('ymin')[0].childNodes[0].data)
105                 xmax = int(box.getElementsByTagName('xmax')[0].childNodes[0].data)
106                 ymax = int(box.getElementsByTagName('ymax')[0].childNodes[0].data)
107                 bbox = [0.0, xmin, ymin, xmax-xmin, ymax-ymin]
108                 # print('普通标记框,参数: ', bbox)
109         # 标记框类型(bndbox、robndbox),标记框名称,标记框信息
110         box_list.append([box_name, bbox])
111     return box_list
112 
113 
114 # 自定义函数,输入图像和 gtbox
115 def draw_bbox(img_path, bboxes, img_save_path):
116     img = imread(img_path)
117     plt.figure(num=1)       # 使用同一张画布,最终只会展示最后一张图片
118     plt.axis('off')
119     plt.imshow(img)
120     currentAxis = plt.gca()
121     # 绘制矩形框, 挨个画,最终在图片上一起展示
122     for box in bboxes:
123         box_name = box[0]   # 标记框名称
124         bbox = box[1]       # 标记框信息
125         ### 旋转角度为 0 的标记框使用  patches.Rectangle 的方式进行绘制
126         if bbox[0] == 0:
127             draw_rectangle(currentAxis, bbox, edgecolor='y')
128             plt.scatter(bbox[1]+bbox[3]/2, bbox[2]+bbox[4]/2, s=30, c='r', alpha=1) # 绘制中心点坐标
129         # 旋转角度不为 0 ,通过直线组成标记框的方式进行绘制
130         else:
131             draw_bbox_by_lines(currentAxis, bbox, 'r')
132             plt.scatter(bbox[1], bbox[2], s=30, c='r', alpha=1)                     # 绘制中心点坐标
133             ### 绘制没有旋转的标记框
134             bbox2 = [bbox[0], bbox[1]-bbox[3]/2, bbox[2]-bbox[4]/2, bbox[3], bbox[4]]
135             draw_rectangle(currentAxis, bbox2, edgecolor='y')
136 
137 
138 
139 
140             print()
141         ### 标记框对应的标签
142         plt.text(bbox[1]+3, bbox[2]+13, box_name, fontsize=8, color='yellow')
143 
144     plt.savefig(img_save_path, bbox_inches='tight', pad_inches=0, dpi=500)
145     plt.show()
146     plt.close()
147 
148 
149 image_path = '../data/2.jpg'
150 xml_path = '../data/2.xml'
151 img_save_path = '../data/22_labeled.jpg'
152 
153 bboxes = get_bbox(xml_path)
154 # print(bboxes)
155 draw_bbox(image_path, bboxes, img_save_path)
原文地址:https://www.cnblogs.com/lyj0123/p/15741652.html