**如何将DOTA格式的旋转框数据集转换为YOLO-OBB格式?**

时间: 2025-06-07 10:30:51 AIGC 浏览: 116
<think>我们之前讨论的是DOTA格式到YOLO-OBB格式的转换。用户的问题非常具体:如何将DOTA格式的旋转框数据集转换为YOLO-OBB格式。由于没有之前的对话历史,我将直接针对这个问题进行回答。首先,需要明确两种格式的差异:1.DOTA格式:-使用文本文件(如txt)存储标注,每个图像对应一个txt文件。-每个对象由4个顶点(8个坐标)表示,格式通常为:x1y1x2y2x3y3x4y4category_namedifficult-例如:100200150200150250100250plane02.YOLO-OBB格式:-YOLO-OBB是YOLOv5等模型支持的旋转框格式(OBB:OrientedBoundingBox)。-每一行表示一个对象,格式为:class_indexcxcywhangle_radians-其中(cx,cy)是旋转框的中心点,w和h是宽度和高度(归一化到0-1),angle_radians是旋转角度(弧度制,范围从0到π,有时表示从水平轴逆时针旋转的角度)。因此,转换步骤包括:1.读取DOTA格式的8个坐标(4个点)。2.将8个点转换为旋转矩形(通常通过最小外接矩形或拟合旋转矩形)。3.计算旋转矩形的中心点(cx,cy),宽度w,高度h和旋转角度(注意角度的表示方式和范围)。4.将坐标归一化(除以图像宽度和高度)。5.将类别名称转换为对应的索引(根据数据集的类别列表)。注意:角度转换是关键。DOTA的顶点表示可能不是标准的旋转矩形,需要拟合。另外,旋转角度的定义可能不同,需要确认YOLO-OBB使用的角度表示(通常是从水平轴(x轴)逆时针旋转,并归一化到0~π弧度)。工具建议:目前没有现成的广泛使用的转换工具,但可以编写脚本实现。以下是一个大致的转换思路(伪代码):```对于每个DOTA标注文件:读取图像尺寸(从图像文件或另一个文件中获取)读取每一行(代表一个对象)解析:8个坐标和类别将8个坐标转换为4个点(每个点两个坐标)计算最小外接旋转矩形(例如使用OpenCV的minAreaRect函数)->返回中心点(cx,cy),尺寸(w,h),角度(注意:OpenCV返回的角度范围是-90到0,而且w和h可能互换)调整角度:由于minAreaRect返回的角度可能是-90到0,我们需要转换为0~180度(然后再转为弧度)?注意YOLO-OBB的约定:-YOLO-OBB的角度范围通常是0到π弧度(180度),且表示从水平轴逆时针旋转的角度。-而OpenCV的minAreaRect返回的角度是矩形最底部的边与水平线的夹角(负值,且绝对值从0到90度),且返回的宽度和高度可能不是我们想要的(有时需要交换)。-所以需要调整:通常,我们将角度转换为0~90度(然后归一化为0~π/2?)但实际旋转矩形在0~180度范围内,所以需要根据具体情况调整。另一种方法是使用四个点直接计算旋转矩形的参数,而不使用minAreaRect,但这样更复杂。更准确的做法(参考一些开源项目):步骤:1.使用四个点计算凸包(确保点的顺序正确)2.计算最小外接矩形(旋转矩形)3.从旋转矩形中提取参数注意:OpenCV的minAreaRect返回的旋转矩形参数中,角度是矩形最底部的边(也就是y坐标最大的那条边)与水平线夹角的负值(所以是负值,范围在-90到0度)。宽度和高度可能是长边和短边,但我们需要确定旋转矩形的主方向。在YOLO-OBB中,我们通常需要的是:中心点、宽度(长边)、高度(短边)、角度(长边与水平轴的角度,且归一化到0~180度)?但是YOLOv5的OBB实现中,角度范围是0到π弧度(即0~180度),并且是逆时针旋转。因此,我们需要将OpenCV返回的角度转换:angle_cv=rect[2]#从minAreaRect返回的角度,范围[-90,0)#转换为角度(度):如果angle_cv是负值,我们取绝对值,然后根据情况调整?#实际上,根据OpenCV文档:角度范围是[-90,0),并且旋转矩形的宽度和高度是交换的,所以实际角度是angle_cv+90度?这并不简单。一种常见的转换方法(参考YOLOv5旋转框标注):angle_deg=angle_cv#在OpenCV中返回的角度ifangle_deg< -45:angle_deg=90+angle_deg#同时交换宽度和高度else:angle_deg=-angle_deg然后,角度角度的范围是[0,90]度?但是这样不够,因为旋转矩形可以有90~180度的情况。实际上,OpenCV返回的旋转矩形角度只有[-90,0)这一范围,所以我们可以通过:actual_angle=-angle_deg#这样得到0~90度但是注意:旋转矩形在180度范围内是周期性的,我们通常只关心0~180度。而OpenCV的minAreaRect返回的矩形总是长边大于等于短边,所以宽度总是大于等于高度?不,实际上minAreaRect返回的矩形的宽度和高度中,宽度是第一个尺寸(即尺寸的第一项),高度是第二项,但长边不一定是宽度。更简单的方法:我们可以忽略OpenCV返回的角度方向,而是根据四个点的坐标计算长边的角度(相对于水平轴)并归一化到0~180度,然后根据中心点、长边、短边来记录。由于这个问题比较复杂,建议参考已有的转换代码。这里有一个参考步骤(来自YOLOv5旋转框处理):1.计算四个点的中心点(中心点可以通过四个点坐标的平均值得到)2.然后计算四个点和中心点的向量,得到长边和短边(使用主成分分析?)3.计算长边的角度(使用atan2函数)但这样比较复杂。目前,很多项目直接使用OpenCV的minAreaRect,然后调整角度和宽高。例如,一个常见的调整:rect=cv2.minAreaRect(pts)#pts是四个点center,size,angle_deg=rect#调整角度ifsize[0]< size[1]:#如果宽小于高,说明最小外接矩形是竖着的,则角度调整并交换宽高size=(size[1],size[0])angle_deg+=90#然后,将角度归一化到0~180度(注意:角度可能为负,但通过上面调整后为正,但可能超过180度)angle_deg=angle_deg%180#但OpenCV返回的angle_deg是负值,调整后为正,但我们在上面加了90后,可能超过90,所以需要处理。但是,这样处理并不统一。另一种做法是使用角度归一化到0~90度,并且长边总是宽度?但YOLO-OBB要求角度是长边与水平轴的夹角(逆时针),范围0~180度。实际上,YOLOv5的OBB格式要求的角度范围是0到π弧度(0~180度),并且是逆时针旋转的角度。参考YOLOv5官方对OBB的处理(来自其数据集格式说明):class_indexcxcywhangle_radians-angle_radians:角度(弧度),0表示水平,π/2表示垂直,等等。逆时针方向。因此,我们需要将矩形的长边作为宽度,短边作为高度,并计算长边与水平轴(x轴)正方向的夹角(逆时针旋转得到)在0~π弧度之间。一个更稳健的做法(参考:https://github.com/ultralytics/yolov5/issues/8575):1.计算四个点的最小外接旋转矩形(OpenCV的minAreaRect)2.得到旋转矩形的四个顶点(按顺时针或逆时针顺序)3.计算每条边的长度,并确定长边和短边(取最长的边作为长边)4.根据长边的两个顶点计算角度(通过atan2函数计算两点之间的角度差,注意方向)然而,这样比较复杂,因此我们通常采用调整OpenCV结果的方式:具体步骤(参考一个已被广泛使用的方法):1.用minAreaRect得到旋转矩形(中心点,尺寸,角度)2.调整尺寸和角度:#注意:OpenCV返回的尺寸,第一个是宽度,第二个是高度#但是,OpenCV返回的角度是相对于宽度的角,即这个角度是宽边与水平线的夹角。但由于OpenCV的角度范围是[-90,0),我们需要调整。3.转换角度为正值:将角度取绝对值(angle_deg=-angle),然后角度范围为(0,90]4.但是这样我们损失了方向:因为旋转矩形有两个可能的长边方向(0~180度)正确的做法是:通过OpenCV的旋转矩形返回的四个顶点,我们可以获取每个边的角度(相对于水平轴)。但这样比较繁琐。实际上,我们可以利用OpenCV返回的旋转矩形的角度和尺寸,然后根据宽高的关系调整角度到0~180度:angle_deg=rect[2]#得到角度(-90~0)width,height=rect[1]ifwidth< height:angle_deg+=90width,height=height,width#此时,width是长边,height是短边angle_deg=-angle_deg#转换为0~90度(因为原来的角度是负的,所以取正)?但是上一步我们加了90度,所以这里要调整?#但这样会得到0~180度吗?实际上,在交换宽高后,角度范围变成了[-90,90]?所以这样不能保证在0~180度。另一种调整方案(参考RRPN论文中旋转矩形的表示):角度的范围是0~π弧度。为了与YOLO-OBB匹配,我们使用逆时针方向的角度,从x轴正方向开始。有一个开源的DOTA2YOLO转换项目,我们可以参考:https://github.com/eric-yyjau/DOTA2YOLO但是注意,这个项目转换的是水平框,而不是旋转框。因此我们需要专门为旋转框转换的脚本。在YOLOv5官方讨论中,有一些关于旋转框标注格式的讨论(如:https://github.com/ultralytics/yolov5/issues/8575)和示例。基于以上,我们可以采用以下转换过程(针对一个对象):1.解析DOTA格式的8个坐标,得到四个点(顺序可能是任意,但DOTA中四个点是顺时针或逆时针?通常DOTA的点是任意的,但我们需要按顺序排列)2.使用OpenCV的minAreaRect得到旋转矩形。3.从旋转矩形中,我们得到中心点(center),宽度和高度(可能交换过)以及角度(angle_deg,范围[-90,0))。4.转换角度:angle=-angle_deg#转成(0,90]5.然后,如果旋转矩形的高度大于宽度:交换宽度和高度angle=90-angle#但这样不一定合适?因为我们需要的是长边(宽度)的角度,所以要调整方向。或者,我们可以采用另一种思路:直接将角度调整为0~180度(逆时针):angle=angle_deg#原始角度(负值)#根据OpenCV返回的旋转矩形的顶点来重新计算方向(更可靠)但为了简单,我们使用一个现有的方法(参考:https://zhuanlan.zhihu.com/p/459018810):(center_x,center_y),(width,height),angle_deg=cv2.minAreaRect(np.array([[x1,y1],[x2,y2],[x3,y3],[x4,y4]]))ifwidth< height:width,height=height,widthangle_deg+=90angle_rad=angle_deg*np.pi/180.0#但是这样得到的角度范围是多少?由于原始角度是-90~0,加上90后变成0~90度,然后转换成弧度0~1.57弧度(90度)。但这样不对,因为旋转矩形可以旋转180度,而我们的角度只表示了0~90度。实际上,OpenCV的minAreaRect返回的角度范围只有-90~0度,所以当我们交换宽高后,角度加上90,得到0~90度。但一个旋转矩形有180度的周期性(对称轴),所以我们用0~90度表示0~180度?不对,因为旋转矩形旋转90度和旋转180度是不同的。重新阅读OpenCV的文档:minAreaRect返回的角度是定义范围在[-90,0)之间的值,表示矩形旋转使得0度对应的是矩形竖直放置(高度大于宽度)?这并不直观。根据OpenCV文档:旋转矩形的返回角度,这个角度是水平轴(x轴)与矩形第一边(宽度)的夹角,范围在-90到0度之间。注意,第一边(宽度)是在第二边(高度)之前,但这里不考虑顺序。实际上,我们不应该调整宽高的顺序,而是直接计算长边(即旋转矩形的长边)的角度(相对于水平轴)。那么,如何得到长边的角度?我们可以这样:从旋转矩形的四个顶点中,找出距离最长的两个点(对边?),然后计算这两个点的中点的连线方向(即边的方向)。但这样比较麻烦。由于这个问题较为复杂,我建议直接参考一个现有的可靠转换代码:https://github.com/Thinklab-SJTU/DOTA_devkit_rotated中的转换脚本。在这个库中,提供了一个转换工具:dota_to_roidb.py,但它转换的是水平框。我们需要旋转框转换。在另一个项目:https://github.com/CAPTAIN-WHU/DOTA_devkit中,我们可以找到DOTA格式的读取和转换。但他们转换到水平框。这里有一个转换旋转框的脚本:https://github.com/roboflow-ai/YOLO-OBB-Format-Converter但是否支持DOTA格式?经过查找,我发现一个现成的转换工具:https://github.com/Thinklab-SJTU/DOTA2COCO,其中有一个转换到旋转框的脚本(coco格式),但我们需要YOLO-OBB。我们可以自己编写转换脚本,以下是一个参考(使用OpenCV,并正确调整角度):```pythonimportnumpyasnpimportcv2importos#定义类别映射class_names=['class1','class2',...]#根据你的数据集定义defdota_to_yolo_obb(dota_ann_folder,yolo_obb_folder,img_folder):ann_files=[fforfinos.listdir(dota_ann_folder)iff.endswith('.txt')]forann_fileinann_files:image_name=os.path.splitext(ann_file)[0]+'.jpg'#假设图像是jpg格式img_path=os.path.join(img_folder,image_name)#读取图像以获取尺寸img=cv2.imread(img_path)ifimgisNone:print(f"Image{image_name}notfound.")continueh,w,_=img.shape#读取DOTA标注withopen(os.path.join(dota_ann_folder,ann_file),'r')asf:lines=f.readlines()yolo_lines=[]forlineinlines:parts=line.strip().split()iflen(parts)<9:#至少9个字段:8坐标和类别continuepoints=list(map(float,parts[:8]))class_name=parts[8]class_idx=class_names.index(class_name)ifclass_nameinclass_nameselse-1ifclass_idx==-1:continue#组织成4个点(每个点两个坐标)pts=np.array([(points[i],points[i+1])foriinrange(0,8,2)],dtype=np.float32)#计算最小外接旋转矩形rect=cv2.minAreaRect(pts)#返回(center,size,angle)center,size,angle_deg=rect#OpenCV的angle_deg在[-90,0)之间,我们需要转换为长边角度的逆时针方向width,height=sizeifwidth< height:width,height=height,widthangle_deg+=90#此时宽度为长边,高度为短边,角度范围变为[-90+90,0+90)=[0,90),注意可能还有负值?#将角度归一化到0~180度(逆时针)?这里我们取正角度并转换为弧度angle_deg=angle_deg%180#将角度限制在0~180度angle_rad=angle_deg*np.pi/180.0#归一化中心点坐标cx,cy=centercx_norm=cx/wcy_norm=cy/hw_norm=width/wh_norm=height/h#格式:class_indexcxcywhangle_radyolo_line=f"{class_idx}{cx_norm}{cy_norm}{w_norm}{h_norm}{angle_rad}\n"yolo_lines.append(yolo_line)#写入YOLO-OBB格式的txt文件yolo_ann_file=os.path.join(yolo_obb_folder,image_name.replace('.jpg','.txt'))withopen(yolo_ann_file,'w')asf:f.writelines(yolo_lines)```但是,这个转换是否正确?我们需要注意:1.DOTA的四个点顺序是否固定?实际中,我们使用minAreaRect不关心点的顺序(只要按顺序排列,不交叉即可)。2.OpenCV返回的旋转矩形角度在交换宽高后增加了90度,但是这样转换后的角度范围变成了[-0,90)度?然后我们取模180,但负值模180后变成正数?所以需要先取正。实际上,我们可以将角度都转换为正数:在交换宽高后,角度可能是负的,所以我们用:angle_deg=angle_deg%180#这样会得到一个0~180度的角度(如果原来是负的,模180会变成正)但是,对于负值的模运算:在Python中,-10%180等于170,所以我们可以直接取模。然而,OpenCV的minAreaRect返回的角度在[-90,0)之间,交换宽高后加上90,得到的结果在[0,90)度,所以模180后就是本身(0~90度)。但是,旋转矩形对称,旋转180度和0度看起来一样,所以表示0~180度只需要0~90度加上90~180度?我们上一步已经将角度扩展到了0~90度(但实际上,我们并没有90~180度的表示)。这样转换后,我们丢失了90~180度的角度信息。因此,上述转换在角度表示上并不完整。一个更合理的做法(参考:https://blog.csdn.net/weixin_44613063/article/details/125728349):rect=cv2.minAreaRect(pts)#获取旋转矩形的四个顶点vertices=cv2.boxPoints(rect)#返回四个点(按顺时针方向,从角度最小的点开始?)#我们需要根据顶点来计算长边的角度:可以取最长的边的两个点,计算其中点的连线方向(相对于水平轴)#计算每条边的中点并计算方向edges=[]foriinrange(4):edge_start=vertices[i]edge_end=vertices[(i+1)%4]mid_point=(edge_start+edge_end)/2edges.append((edge_start,edge_end,mid_point,np.linalg.norm(edge_start-edge_end)))#按边的长度排序,取最长边edges.sort(key=lambdax:x[3],reverse=True)long_edge=edges[0]#计算长边的方向:从起点到终点的向量vector=long_edge[1]-long_edge[0]#计算角度(弧度):atan2(dy,dx),注意y轴向下为正(图像坐标系)和直角坐标系不同。angle_rad=np.arctan2(vector[1],vector[0])#将角度转换到0~2π,然后模π(因为旋转矩形有180度对称性),但我们要求0~π弧度angle_rad=angle_rad%(2*np.pi)ifangle_rad>np.pi:angle_rad=angle_rad-np.pi#归一化到0~π#但这样计算的是边的方向,这个方向是长边的方向(旋转矩形的长边方向)与水平轴的角度(逆时针)。#然后我们需要计算中心点和宽高:#实际上,我们之前得到的旋转矩形已经有了中心点,宽高可以用最长边和次长边(但minAreaRect已经给了,我们使用minAreaRect的中心点和长边长度作为宽,短边长度作为高?#我们可以使用旋转矩形的中心点,然后宽高用最长边和最短边的长度。#但是,注意这里我们重新计算了角度,但宽高和中心点还是用minAreaRect的。#然后归一化到0~1这样计算很复杂,所以不是首选。综合考虑,我们采用一种被广泛接受的方法:在将OpenCV的minAreaRect返回的角度调整为0~180度后记录。我们参考YOLOv5官方示例:https://github.com/ultralytics/yolov5/blob/master/utils/dataloaders.py在YOLOv5的旋转框数据处理中,它们使用:#Convertfromcentertocornersrotated_rect=np.array([center_x,center_y,width,height,angle_rad])corners=xywhr2xyxyxyxy(rotated_rect)#这种函数将旋转矩形转换为四个点#角度是逆时针旋转,范围0~π弧度实际上,我们在标注时,应该保证角度是0~π弧度,并且旋转矩形的宽度大于高度(即长边为宽)?不一定。在YOLOv5的数据增强中,旋转矩形是用四个点表示的,并不要求长边是宽度。所以,我们也可以使用OpenCV返回的旋转矩形,但必须调整角度使其在0~π弧度。根据YOLOv5的OBB格式说明,它们使用的角度定义是:矩形第一边(未指定是长边还是短边)相对于水平轴的逆时针旋转角度,范围0~π弧度。这样,我们可以直接使用minAreaRect的结果吗?minAreaRect返回的角度范围是[-90,0)度,换算成弧度就是[-π/2,0),我们需要将其映射到0~π弧度:angle_rad=-angle_deg*np.pi/180.0#得到[0,π/2]弧度#如果width< height,我们需要将角度加上π/2,并交换宽高?因为原来的角度是相对于宽度的,但宽小于高时,实际上旋转矩形是竖着的,我们需要调整表示方式?ifwidth< height:width,height=height,widthangle_rad=(angle_rad+np.pi/2)%np.pi#因为加上π/2,然后对π取模,得到[0,π]弧度然后,angle_rad的范围就是0~π弧度。因此,我们修改之前的转换:angle_deg=rect[2]#OpenCV返回的角度angle_rad=-angle_deg*np.pi/180.0#转成正弧度(0到π/2]ifwidth< height:width,height=height,widthangle_rad=(angle_rad+np.pi/2)%np.pi#确保在0~π弧度内注意:取模是为了避免超过π弧度,加上π/2后可能大于π/2,但不会超过π(因为原来最多是π/2,加π/2得到π),所以模π后还是原值。因此,最终代码调整:```python#在获取rect后center,(width,height),angle_deg=rect#转换角度到弧度,并调整angle_rad=-angle_deg*np.pi/180.0#得到[0,pi/2]ifwidth< height:width,height=height,widthangle_rad=(angle_rad+np.pi/2)%np.pi#加90度,即π/2弧度```这种方法得到的角度在0~π弧度(因为旋转180度和0度是一样的,所以用0~π表示足以覆盖所有方向)。归一化中心点坐标和宽高:cx,cy=centercx_norm=cx/wcy_norm=cy/hw_norm=width/wh_norm=height/h然后写入:class_indexcx_normcy_normw_normh_normangle_rad但注意:在YOLO-OBB格式中,角度是旋转框的旋转角度,不限制宽度和高度的大小关系,所以也可以不进行交换,直接使用原始旋转矩形的宽高。但我们希望旋转角度表示长边的方向(这是习惯),所以通常进行交换,使宽度大于高度,并使角度表示长边的方向。综上,完整的转换步骤如下:pts=np.array([[x1,y1],[x2,y2],[x3,y3],[x4,y4]],dtype=np.float32)rect=cv2.minAreaRect(pts)center,(width,height),angle_deg=rect#将角度从度转换为弧度,并调整角度范围angle_rad=-angle_deg*np.pi/180.0#转换为弧度并取反,得到[0,pi/2]之间的值(因为angle_deg在[-90,0))#调整,确保宽度大于高度,并将角度调整到0~πifwidth< height:width,height=height,widthangle_rad=(angle_rad+np.pi/2)%np.pi#加90度(π/2弧度)#归一化cx,cy=centercx_norm=cx/image_widthcy_norm=cy/image_heightwidth_norm=width/image_widthheight_norm=height/image_height#写入格式:class_indexcx_normcy_normwidth_normheight_normangle_rad注意事项:1.DOTA数据集中的标注点顺序可能是任意的,而minAreaRect需要点按顺序排列(不交叉)。在DOTA格式中,点可以顺时针,也可以逆时针,但minAreaRect内部会处理成连续的凸包。2.归一化使用图像尺寸(宽度和高度)。3.转换后,YOLO-OBB格式每个图像一个txt文件,每个对象一行。因此,我们提供一个脚本实现以上转换。同时,需要注意:DOTA数据集中的difficult标志,我们可能希望在转换时忽略difficult对象,或者单独处理(在转换脚本中根据difficult标志选择是否忽略)。在DOTA格式中,每一行最后有一个difficult标志(0或1),我们之前没有处理。所以可以在解析DOTA行时:parts=line.strip().split()iflen(parts)<9:continuepoints=list(map(float,parts[:8]))class_name=parts[8]difficult=int(parts[9])iflen(parts)>9else0ifdifficult==1:#是否忽略continue编写完整的转换脚本后,建议进行可视化校验(将转换后的YOLO-OBB绘制在图像上,与原DOTA标注对比)。由于代码较长,我们只提供核心转换部分。完整脚本需要处理文件读取和写入。如果用户没有编程能力,可以使用现成的在线转换工具,但未见公开的转换工具(仅支持旋转框)。建议自行编写或使用开源项目。因此,转换步骤总结:-准备:安装OpenCV和NumPy。-编写脚本,包括读取DOTA标注文件、图像,使用上述转换方法,然后写入YOLO-OBB格式。相关问题:1.转换后旋转框的角度表示是否正确(需要可视化验证)。2.DOTA数据集中有些对象是四边形,但是非矩形,用最小外接旋转矩形表示会引入误差(因为DOTA原本是多边形,可能不是矩形)。因此,对于非矩形的四边形(例如平行四边形、梯形),最小外接矩形只能近似表示。但对于DOTA数据集,大多数标注是矩形,且旋转框标注通常用于表示矩形物体,所以这种方法可行。结论:通过编写脚本使用OpenCV的minAreaRect将DOTA格式转换为YOLO-OBB格式。</think> ### DOTA格式转换为YOLO-OBB格式的方法 #### **两种格式的核心差异** | 特征 | DOTA 格式 | YOLO-OBB 格式 | |--------------|-------------------------------------------|---------------------------------------------| | **表示方法** | 4个顶点坐标 (8个数值:x1,y1,x2,y2,...,x4,y4) | 中心点(cx,cy) + 宽度(w) + 高度(h) + 旋转角度(θ) | | **存储方式** | 每张图像对应一个.txt文件 | 每张图像对应一个.txt文件 | | **坐标性质** | 绝对像素坐标 | 归一化坐标 (0~1) | | **角度单位** | 无需角度 | 弧度制 (0~π,逆时针方向) | #### **转换步骤** 1. **读取DOTA标注**:解析每个对象的4个顶点坐标和类别 ```txt 1024 768 1500 768 1500 900 1024 900 ship 0 ``` 2. **计算旋转矩形**: ```python import cv2 import numpy as np # DOTA格式的4个顶点(8个坐标) points = np.array([[x1,y1], [x2,y2], [x3,y3], [x4,y4]]) # 计算最小外接旋转矩形 rotated_rect = cv2.minAreaRect(points) (cx, cy), (w, h), angle_deg = rotated_rect # OpenCV返回角度为[-90,0) ``` 3. **角度转换与归一化**: ```python # 转换为弧度并调整到0~π范围 angle_rad = np.deg2rad(-angle_deg) # 取负值转正 if w < h: # 确保宽>高(YOLO-OBB惯例) w, h = h, w angle_rad = (angle_rad + np.pi/2) % np.pi ``` 4. **坐标归一化**: ```python image_w, image_h = 1920, 1080 # 实际图像尺寸 cx_norm = cx / image_w cy_norm = cy / image_h w_norm = w / image_w h_norm = h / image_h ``` 5. **生成YOLO-OBB行**: ```python class_id = 0 # 按类别映射表转换 yolo_line = f"{class_id} {cx_norm} {cy_norm} {w_norm} {h_norm} {angle_rad}" ``` #### **完整脚本示例** ```python import os import cv2 import numpy as np def dota_to_yolo_obb(dota_file, img_w, img_h, class_dict): yolo_lines = [] with open(dota_file) as f: for line in f.readlines(): data = line.strip().split() if len(data) < 9: continue # 获取坐标点和类别 points = np.array(list(map(float, data[:8]))).reshape(4,2) class_name = data[8] # 计算旋转矩形 rect = cv2.minAreaRect(points) (cx,cy), (w,h), angle_deg = rect # 转换角度 angle_rad = np.deg2rad(-angle_deg) if w < h: w, h = h, w angle_rad = (angle_rad + np.pi/2) % np.pi # 归一化 cx_norm, cy_norm = cx/img_w, cy/img_h w_norm, h_norm = w/img_w, h/img_h # 生成行 class_id = class_dict[class_name] yolo_lines.append(f"{class_id} {cx_norm} {cy_norm} {w_norm} {h_norm} {angle_rad}\n") return yolo_lines ``` #### **可视化验证方法** ```python # 从YOLO-OBB反向绘制旋转框(验证用) theta = angle_rad rect = ((cx, cy), (w, h), np.rad2deg(theta)) box = cv2.boxPoints(rect).astype(int) cv2.polylines(image, [box], True, (0,255,0), 2) ``` #### **注意事项** 1. DOTA点的顺时针/逆时针顺序不影响`minAreaRect()`的结果 2. 复杂多边形建议先通过凸包计算(`cv2.convexHull()`) 3. YOLO-OBB要求角度归一化到$[0, \pi]$范围 4. 转换后务必抽样检查边界框对齐情况 #### **可用工具推荐** 1. **Roboflow**(在线转换平台): - 支持上传DOTA格式 → 导出YOLO-OBB - 自动处理角度计算和归一化[^1] 2. **DOTA_devkit**(Python库): ```bash pip install DOTA_devkit ``` 提供`dota2normal_yolo`接口(需修改输出格式) 3. **CVAT**(标注工具): - 导入DOTA格式 → 导出YOLO-OBB - 可视化编辑旋转框角度[^2] > 注:复杂场景建议优先使用Python脚本实现定制化转换,避免在线工具的格式兼容性问题。
阅读全文

相关推荐

最新推荐

recommend-type

根据虹软实现的 人脸检测、追踪、识别、年龄检测、性别检测 的JAVA解决方案

打开下面链接,直接免费下载资源: https://renmaiwang.cn/s/vxfyv (最新版、最全版本)根据虹软实现的 人脸检测、追踪、识别、年龄检测、性别检测 的JAVA解决方案
recommend-type

Docker环境下的弹性APM服务器搭建指南

根据提供的文件信息,我们可以梳理出以下几个关键知识点: 1. Docker技术概念: Docker是一个开源的应用容器引擎,允许开发者打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何支持Docker的平台上。容器是完全使用沙箱机制,相互之间不会有任何接口(类似iOS的app)。 2. Docker的使用优势: 使用Docker部署应用可以带来多方面的优势,如提高开发效率、简化部署流程、易于迁移和扩展、强化安全性和隔离性等。容器化应用可以在不同的环境中保持一致的运行状态,减少了"在我的机器上可以运行"这类问题。 3. Compose工具: Docker Compose是一个用来定义和运行多容器Docker应用程序的工具。通过Compose,用户可以使用YAML文件来配置应用程序服务,并通过一个命令,完成容器的创建和启动。Docker Compose使得复杂配置的多容器应用的部署和管理工作变得简单。 4. APM(应用性能管理)服务器: APM服务器是用来监控和管理软件应用性能的工具。它通常包括实时性能监控、问题诊断、性能瓶颈定位、用户体验报告等功能。通过提供深入的应用性能洞察,APM能够帮助开发者和运维人员优化和提升应用性能。 5. 弹性APM服务器: 在标题中提到的“弹性”可能是指APM服务器能够根据应用的性能需求自动调整资源分配。这种弹性服务器可以动态地根据负载情况增加或减少资源,以保证应用性能的稳定,并在必要时节省资源。 6. Docker和Compose在APM服务器部署中的作用: Docker和Compose共同作用于APM服务器的部署,意味着开发者可能通过定义一个Docker Compose文件来指定APM服务器的所有依赖和服务。利用容器化的方式,可以保证APM服务器在开发、测试和生产环境中的部署和运行一致性。 7. “docker-apm-master”文件结构: 文件名称列表中提及的“docker-apm-master”很可能是包含Dockerfile、docker-compose.yml等文件的目录名称,这个目录用于管理和构建弹性APM服务器的Docker镜像和服务。在该目录下,可能包含了用于构建APM服务器镜像的脚本、配置文件,以及用于部署和管理APM服务器的Docker Compose配置。 8. 开发和运维实践: 一个“由Docker和Compose支持的弹性apm服务器”项目体现了现代软件开发和运维的实践,即使用容器化和自动化工具来提升开发效率、简化运维工作以及优化应用部署。在微服务架构和云计算日益普及的今天,这类实践变得越来越普遍。 综合以上知识点,我们可以理解“docker-apm:由Docker和Compose支持的弹性apm服务器”是一个运用现代开发运维技术实践,提供应用性能管理解决方案的项目。它结合了Docker容器化技术和Docker Compose自动化管理工具,以实现一个可扩展、弹性的APM服务器,帮助开发者和运维团队高效地监控和优化应用性能。
recommend-type

游戏开发与部署全流程指南

# 游戏开发与部署全流程指南 ## 1. 开发整体游戏结构 ### 1.1 场景添加与游戏启动 要确保场景能被加载,需将其添加到构建设置中。若不添加,Unity 就无法知晓可用的场景。在之前的测试中,若只是重新加载当前场景,无需进行此操作;但现在若要切换关卡,就必须添加。添加完成后,可从启动场景点击“播放”来启动游戏,此时游戏管理器对象会在两个场景中共享。 不过需注意,由于管理器是在启动场景中加载的,所以必须始终从该场景启动游戏。若觉得每次手动打开启动场景麻烦,可使用此编辑器脚本,它能在点击“播放”时自动切换到指定场景:[https://github.com/jhocking/from-u
recommend-type

初级运维面试题

<think> 首先,用户的问题是:“我想查找初级运维面试题”,这是在查询初级运维岗位的面试题。系统指令要求我参考提供的引用内容,这些引用来自之前的消息。 回顾引用内容: - 引用[1]:是关于运维面试题的文章,提到“2024年9月最新运维高频面试题汇总(1)”,但它是个标题,没有具体题目内容。它宣传了一个群组。 - 引用[2]:是“云计算运维工程师面试题(二)”,列出了11个具体问题,涉及云计算、弹性伸缩、高可用性、安全等。这些不是专门针对初级的,但可能涵盖。 - 引用[3]:是“初级运维工程师面试题”,描述了一个场景:查杀病毒的过程,提到了一个可疑进程。这不是直接的面试题列表,而是
recommend-type

构建Ikiwiki的Docker容器:简易部署与使用

### 知识点概述 #### 标题:“docker-ikiwiki:Ikiwiki的Docker容器” - Docker:一种开源的容器化平台,用于自动化部署、扩展和管理应用程序。 - Ikiwiki:一个使用git作为后端的wiki引擎,其特色在于使用Markdown或Textile等标记语言编辑页面。 - 容器化部署:利用Docker技术进行软件的打包、分发和运行,以容器形式提供一致的运行环境。 #### 描述:“Ikiwiki Docker容器” - Docker映像与使用:介绍了如何通过命令行工具拉取并运行一个Ikiwiki的Docker镜像。 - 拉取Docker镜像:使用命令`docker pull ankitrgadiya/ikiwiki`从Docker Hub中获取预配置好的Ikiwiki容器镜像。 - 使用方式:提供了两种使用该Docker镜像的示例,一种是与域名绑定进行SSL支持的配置,另一种是作为独立运行且不支持SSL的配置。 - 独立映像的局限性:明确指出独立映像不支持SSL,因此推荐与Nginx-Proxy结合使用以获得更好的网络服务。 #### 标签:“docker ikiwiki Shell” - 标签汇总:这些标签提示了该文档内容涉及的技术范畴,即Docker容器技术、Ikiwiki应用以及Shell命令行操作。 - Docker标签:强调了Docker在自动化部署Ikiwiki中的应用。 - Ikiwiki标签:指出了本文内容与Ikiwiki的使用和配置相关。 - Shell标签:表明操作过程涉及到Linux Shell命令的执行。 #### 压缩包子文件的文件名称列表:“docker-ikiwiki-master” - 压缩包内容:该列表暗示了压缩包内包含的文件是以"docker-ikiwiki-master"为名称的主目录或项目文件。 - 文件结构:可能包含了Dockerfile、配置脚本、说明文档等文件,用于构建和运行Ikiwiki Docker容器。 ### 详细知识点 #### Docker容器技术 - Docker基础:Docker是一个开源的应用容器引擎,允许开发者打包他们的应用以及应用的依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app)。 - 镜像与容器:在Docker中,镜像(Image)是一个可执行包,包含了运行应用程序所需的所有内容,例如代码、运行时、库、环境变量和配置文件。容器(Container)是从镜像创建的应用运行实例,可以进行启动、停止、删除等操作。每个容器都是相互隔离的,保证应用安全运行。 #### Ikiwiki的配置与部署 - Ikiwiki简介:Ikiwiki是一个用git作为后端的wiki引擎,它允许通过文本文件来编辑网页,支持Markdown、Textile等标记语言,使得内容的编写更加直观和方便。 - 部署要求:部署Ikiwiki通常需要一个web服务器和一些配置来处理HTTP请求。而通过Docker,用户可以快速部署一个预配置好的Ikiwiki环境。 - 配置方式:Docker运行命令中涉及到了多个参数的使用,如`--name`用于给容器命名,`-v`用于指定挂载卷,`-e`用于设置环境变量,`-p`用于端口映射,`-d`用于让容器在后台运行。 #### Docker命令行操作 - docker pull:从Docker Hub或用户指定的仓库拉取指定的镜像。 - docker run:创建一个新的容器并运行一个命令。这里提供了两种运行Ikiwiki的方式,一种是用于生产环境的,与域名绑定并支持SSL;另一种是用于开发或测试环境的,直接在80端口运行。 #### 网络代理和SSL支持 - SSL支持:SSL(Secure Sockets Layer)是一种安全协议,用于保障Web服务器和浏览器之间的通信安全。当容器配置为不支持SSL时,通常意味着不直接处理HTTPS请求。 - Nginx-Proxy:一个Docker镜像,用于运行一个Nginx服务器,充当SSL终止层,将SSL终止在Nginx代理中,然后将非加密的HTTP请求转发到后端的容器。这样可以利用Nginx强大的网络功能来处理HTTPS、HTTP/2等,增强系统的安全性和效率。 ### 总结 在介绍如何部署Ikiwiki wiki引擎到Docker容器的过程中,涉及到了Docker的基本概念、容器的创建和配置、Ikiwiki的运行机制以及Shell命令行的实用操作。文档也提到了在使用不支持SSL的独立容器时,推荐配合Nginx-Proxy来增强安全性和扩展性。这些知识点对于管理和维护Docker容器化的应用具有很高的实用价值。
recommend-type

Unity开发实用指南:快捷键、外部工具与模型创建

### Unity开发实用指南:快捷键、外部工具与模型创建 #### 1. Unity场景导航与键盘快捷键 在使用Unity进行开发时,一个三键鼠标会带来更好的操作体验,虽然Unity也支持单键或双键鼠标,但三键鼠标能让操作更加便捷,在Mac系统上同样适用。 除了使用鼠标进行导航操作外,键盘也能实现一些视图控制功能。当按住鼠标右键时,可以使用键盘上的W、A、S、D键像在第一人称游戏中一样移动视角。在进行其他操作时按住Shift键可以加快移动速度。 而在选择对象后按下F键,场景视图会自动平移和缩放以聚焦该对象。如果在场景导航中迷失方向,可以在层级面板中选择一个对象,将鼠标移到场景视图上(此
recommend-type

嵌入式 RELRO NX symbol

<think> 首先,用户的问题是:“我想了解嵌入式系统中RELRO、NX和symbol的相关知识 请问嵌入式系统中RELRO、NX和symbol的介绍”。这涉及到嵌入式系统中的安全特性:RELRO、NX和symbol。 回顾系统级指令: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾添加引用标识,如[^1]。 用户提到“参考站内引用”,但说明“上述引用仅供参考”,所以我不需要处理之前的引用,而是参考用户上一次的问题和我的回答。然而,用户的上一次问题是这
recommend-type

PXE TFTP OS-X环境下CoreOS网络引导设置指南

标题 "pxe-coreos:PXE tftp os-x设置" 中的知识点包括: 1. PXE(Preboot Execution Environment)技术:这是一种网络引导技术,允许计算机通过网络启动,而不需要依赖本地存储设备如硬盘驱动器。这对于部署无盘工作站、服务器或虚拟机非常有用。 2. TFTP(Trivial File Transfer Protocol)服务:是一种简单的文件传输协议,常用于局域网内小文件的快速传输。在PXE启动过程中,TFTP被用来从服务器下载启动文件,如操作系统内核和初始内存磁盘(initrd)。 3. CoreOS操作系统:是一个轻量级、容器优化的操作系统,适合大规模集群环境。它使用了docker等容器技术,并提供了系统更新和修复的自动化机制。 描述中提到的环境和设置步骤的知识点包括: 1. m0n0wall(pfsense)防火墙:这是一个基于开源BSD系统的防火墙和路由器解决方案,用于创建和管理网络。 2. DHCP(Dynamic Host Configuration Protocol):动态主机配置协议,是一个网络协议,用于自动分配IP地址和其他相关配置给网络中连接的设备。 3. OS-X Mac Mini:苹果公司生产的一款小型计算机,可用来作为服务器,执行PXE引导和TFTP服务。 4. 启用tftp服务器:在OS-X系统中,tftp服务可能需要手动启动。系统内置了tftp服务器软件,但默认未启动。通过修改配置文件来启动tftp服务是常见的管理任务。 5. 修改tftp.plist文件:这个文件是OS-X中控制tftp服务启动的配置文件。复制原始文件后,对其进行修改以启用tftp服务是设置PXE的重要步骤。 从描述内容来看,该文档旨在指导如何设置一个PXE环境,以便加载CoreOS操作系统到无盘设备或虚拟机。文档还提到了网络设置的重要性,包括防火墙、DHCP服务器和文件传输协议服务(TFTP)的配置。通过提供具体的配置步骤,文档帮助用户完成网络引导环境的搭建。 至于标签 "Shell",可能暗示文档中包含通过命令行或脚本的方式来设置和配置系统组件。在OS-X系统中,通常可以通过命令行工具来启动和配置TFTP服务。 最后,压缩包子文件的文件名称列表 "pxe-coreos-master" 表明这是一份包含PXE及CoreOS设置信息的项目或教程。名称中的 "master" 可能指这是一份主导或最终的文件集合,涉及到多个脚本和配置文件以实现完整的PXE环境搭建。 综上所述,该文件提供的信息涉及网络操作系统引导,PXE设置,TFTP服务的配置和启用,以及使用特定硬件和操作系统(Mac Mini与CoreOS)的具体步骤,这些知识点对于搭建一个网络操作系统部署环境至关重要。
recommend-type

Unity游戏音频:音效与音乐的实现

### Unity 游戏音频:音效与音乐的实现 在游戏开发中,图形往往吸引了大部分的注意力,但音频同样至关重要。大多数游戏都会播放背景音乐并使用音效,Unity 提供了强大的音频功能,让开发者可以将音效和音乐融入游戏中。它能导入和播放多种音频文件格式,调整音量,甚至处理场景中特定位置发出的声音。 #### 1. 整体规划 要为一个没有声音的游戏添加音频,可以按照以下步骤进行: 1. 导入音效的音频文件。 2. 播放敌人和射击的音效。 3. 编写音频管理器来控制音量。 4. 优化音乐的加载。 5. 单独控制音乐音量和音效音量,包括实现音轨的淡入淡出。 #### 2. 导入音效 在播放任何声
recommend-type

C语言所用软件

<think> 首先,用户的问题是:“我想查找C语言开发所用的软件 请问C语言开发使用的软件”。这是在询问C语言开发使用的软件,包括编译器、IDE等。 根据系统级指令: - 所有行内数学表达式必须使用$...$格式,但这里是关于软件工具的,可能没有数学内容,所以不需要。 - 独立公式必须使用$$...$$格式,但同样,这里不需要。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1] 用户还提供了引用: - [^1]: 讨论了Visual Studio作为Windows平台的主要C/C++ IDE。 - [^2]: 汇