计算DOTA文件的IOU

背景

在目标检测任务中,评估不同对象之间的重叠情况是至关重要的,而IOU(Intersection Over Union)是衡量这种重叠程度的重要指标。本文将介绍如何编写一个Python脚本,通过并行化处理DOTA格式的标注文件,统计同类别对象之间的IOU超过某个阈值的对数。

代码功能

本文代码的核心功能包括:

  1. 解析DOTA格式标注文件,提取对象类别和多边形坐标。
  2. 计算同类对象之间的IOU,并统计超过设定阈值的情况。
  3. 使用多进程并行化处理,提高对大规模数据的处理效率。
  4. 将最终的结果保存为CSV格式,方便后续分析。

完整代码

import os
import logging
from shapely.geometry import Polygon
import numpy as np
from itertools import combinations
from concurrent.futures import ProcessPoolExecutor, as_completed
import argparse
import pandas as pd# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s] %(message)s',handlers=[logging.StreamHandler()]
)def parse_args():"""解析命令行参数。"""parser = argparse.ArgumentParser(description='进行标注文件的IOU分析,统计同类别对象之间的重叠数量。')parser.add_argument('--anno_folder', type=str, required=True, help='标注文件夹路径')parser.add_argument('--output_csv', type=str, default='iou_overlap_results.csv', help='输出CSV文件路径')parser.add_argument('--iou_threshold', type=float, default=0.01, help='IOU阈值,超过此值视为重叠')parser.add_argument('--num_workers', type=int, default=None, help='并行处理的进程数,默认为CPU核心数')parser.add_argument('--by_class', action='store_true', help='是否按类别统计重叠数量')return parser.parse_args()def parse_annotation_file(file_path, class_map=None):"""解析标注文件,提取对象的类别和多边形坐标。参数:file_path (str): 标注文件的路径。class_map (set, optional): 需要筛选的类别集合。默认为None,表示不筛选。返回:list of tuples: 每个元组包含类别和对应的Shapely多边形。"""objects = []try:with open(file_path, 'r') as file:for line_num, line in enumerate(file, 1):parts = line.strip().split()if len(parts) < 9:logging.warning(f"{file_path} 第{line_num}行格式不正确,跳过。")continuetry:# 假设坐标为前8个元素,类别为第9个元素coords = list(map(float, parts[:8]))dota_type = parts[8]if class_map and dota_type not in class_map:continue# 将坐标转换为Shapely多边形polygon = Polygon(np.array(coords).reshape(-1, 2))if not polygon.is_valid:logging.warning(f"{file_path} 第{line_num}行的多边形无效,跳过。")continueobjects.append((dota_type, polygon))except ValueError as ve:logging.error(f"{file_path} 第{line_num}行坐标转换错误: {ve}")except Exception as e:logging.error(f"读取文件 {file_path} 时发生错误: {e}")return objectsdef compute_iou(poly1, poly2):"""计算两个多边形的IOU。参数:poly1 (Polygon): 第一个多边形。poly2 (Polygon): 第二个多边形。返回:float: 两个多边形的IOU值。"""intersection = poly1.intersection(poly2).areaunion = poly1.union(poly2).areaif union == 0:return 0return intersection / uniondef analyze_file(file_path, by_class=False, class_map=None, iou_threshold=0.01):"""分析单个标注文件,统计同类别对象之间的IOU超过阈值的对数。参数:file_path (str): 标注文件的路径。by_class (bool, optional): 是否按类别统计。默认为False。class_map (set, optional): 需要筛选的类别集合。默认为None,表示所有类别。iou_threshold (float, optional): IOU阈值。默认为0.01。返回:list of dicts: 每个字典包含文件名、类别(如果按类别统计)和重叠对数。"""filename = os.path.basename(file_path)objects = parse_annotation_file(file_path, class_map)results = []if by_class:# 按类别分组class_dict = {}for dota_type, polygon in objects:class_dict.setdefault(dota_type, []).append(polygon)for dota_type, polygons in class_dict.items():overlap_count = 0num_objects = len(polygons)if num_objects < 2:# 少于两个对象,无需比较results.append({'filename': filename,'class': dota_type,'overlap_count': 0})continue# 使用组合生成所有可能的对象对for poly1, poly2 in combinations(polygons, 2):iou = compute_iou(poly1, poly2)if iou > iou_threshold:overlap_count += 1results.append({'filename': filename,'class': dota_type,'overlap_count': overlap_count})else:# 不按类别,统计所有对象之间的重叠polygons = [polygon for _, polygon in objects]overlap_count = 0num_objects = len(polygons)if num_objects >= 2:for poly1, poly2 in combinations(polygons, 2):iou = compute_iou(poly1, poly2)if iou > iou_threshold:overlap_count += 1results.append({'filename': filename,'overlap_count': overlap_count})return resultsdef main():args = parse_args()anno_folder = args.anno_folderoutput_csv = args.output_csviou_threshold = args.iou_thresholdnum_workers = args.num_workersby_class = args.by_class# 定义类别映射,如果需要筛选特定类别,可以在这里修改# 例如:class_map = {'embankment_dota', 'gravity_dota'}class_map = None  # 设置为None表示分析所有类别# class_map = {'embankment_dota'}  # 只分析 'embankment_dota' 类别# 获取所有标注文件all_files = [os.path.join(anno_folder, f) for f in os.listdir(anno_folder) if os.path.isfile(os.path.join(anno_folder, f))]logging.info(f"找到 {len(all_files)} 个标注文件。")# 准备并行处理results = []with ProcessPoolExecutor(max_workers=num_workers) as executor:future_to_file = {executor.submit(analyze_file, file_path, by_class, class_map, iou_threshold): file_path for file_path in all_files}for future in as_completed(future_to_file):file_path = future_to_file[future]try:file_results = future.result()results.extend(file_results)logging.info(f"完成分析文件: {os.path.basename(file_path)}")except Exception as exc:logging.error(f"分析文件 {os.path.basename(file_path)} 时发生异常: {exc}")# 将结果写入CSVif results:df = pd.DataFrame(results)df.to_csv(output_csv, index=False)logging.info(f"分析结果已保存到 {output_csv}")else:logging.warning("没有生成任何分析结果。")if __name__ == '__main__':main()

代码详解

接下来我们将详细解释该脚本的每个部分。

1. 配置日志和命令行参数解析

我们首先配置了日志系统,以便记录运行时的相关信息。日志系统可以帮助我们实时跟踪程序的执行状态和潜在问题。

import logginglogging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s] %(message)s',handlers=[logging.StreamHandler()]
)

然后,定义了命令行参数解析函数 parse_args(),用于接收用户输入的文件夹路径、IOU阈值、输出文件路径、并行进程数等参数:

def parse_args():parser = argparse.ArgumentParser(description='进行标注文件的IOU分析,统计同类别对象之间的重叠数量。')parser.add_argument('--anno_folder', type=str, required=True, help='标注文件夹路径')parser.add_argument('--output_csv', type=str, default='iou_overlap_results.csv', help='输出CSV文件路径')parser.add_argument('--iou_threshold', type=float, default=0.01, help='IOU阈值,超过此值视为重叠')parser.add_argument('--num_workers', type=int, default=None, help='并行处理的进程数,默认为CPU核心数')parser.add_argument('--by_class', action='store_true', help='是否按类别统计重叠数量')return parser.parse_args()

2. 解析DOTA格式文件

DOTA标注文件包含多个对象的坐标和类别,通常以文本行的形式存储。我们通过 parse_annotation_file() 函数读取文件内容,并提取每个对象的类别和多边形坐标。

def parse_annotation_file(file_path, class_map=None):"""解析标注文件,提取对象的类别和多边形坐标。参数:file_path (str): 标注文件的路径。class_map (set, optional): 需要筛选的类别集合。默认为None,表示不筛选。返回:list of tuples: 每个元组包含类别和对应的Shapely多边形。"""objects = []try:with open(file_path, 'r') as file:for line_num, line in enumerate(file, 1):parts = line.strip().split()if len(parts) < 9:logging.warning(f"{file_path} 第{line_num}行格式不正确,跳过。")continuetry:# 假设坐标为前8个元素,类别为第9个元素coords = list(map(float, parts[:8]))dota_type = parts[8]if class_map and dota_type not in class_map:continue# 将坐标转换为Shapely多边形polygon = Polygon(np.array(coords).reshape(-1, 2))if not polygon.is_valid:logging.warning(f"{file_path} 第{line_num}行的多边形无效,跳过。")continueobjects.append((dota_type, polygon))except ValueError as ve:logging.error(f"{file_path} 第{line_num}行坐标转换错误: {ve}")except Exception as e:logging.error(f"读取文件 {file_path} 时发生错误: {e}")return objects

通过这个函数,我们可以将文件中的每个对象转化为一个Shapely库支持的多边形对象,方便后续计算IOU。

3. 计算IOU

IOU(交并比)的计算公式如下:

IOU = \frac{\text{Intersection Area}}{\text{Union Area}}

我们利用Shapely库中的 intersection()union() 方法来计算两个多边形的交集和并集面积。

def compute_iou(poly1, poly2):intersection = poly1.intersection(poly2).areaunion = poly1.union(poly2).areaif union == 0:return 0return intersection / union

4. 文件分析与并行化处理

analyze_file() 函数用于分析单个标注文件,统计同类别对象之间的IOU超过设定阈值的对数。支持按类别统计或整体统计。

def analyze_file(file_path, by_class=False, class_map=None, iou_threshold=0.01):"""分析单个标注文件,统计同类别对象之间的IOU超过阈值的对数。参数:file_path (str): 标注文件的路径。by_class (bool, optional): 是否按类别统计。默认为False。class_map (set, optional): 需要筛选的类别集合。默认为None,表示所有类别。iou_threshold (float, optional): IOU阈值。默认为0.01。返回:list of dicts: 每个字典包含文件名、类别(如果按类别统计)和重叠对数。"""filename = os.path.basename(file_path)objects = parse_annotation_file(file_path, class_map)results = []if by_class:# 按类别分组class_dict = {}for dota_type, polygon in objects:class_dict.setdefault(dota_type, []).append(polygon)for dota_type, polygons in class_dict.items():overlap_count = 0num_objects = len(polygons)if num_objects < 2:# 少于两个对象,无需比较results.append({'filename': filename,'class': dota_type,'overlap_count': 0})continue# 使用组合生成所有可能的对象对for poly1, poly2 in combinations(polygons, 2):iou = compute_iou(poly1, poly2)if iou > iou_threshold:overlap_count += 1results.append({'filename': filename,'class': dota_type,'overlap_count': overlap_count})else:# 不按类别,统计所有对象之间的重叠polygons = [polygon for _, polygon in objects]overlap_count = 0num_objects = len(polygons)if num_objects >= 2:for poly1, poly2 in combinations(polygons, 2):iou = compute_iou(poly1, poly2)if iou > iou_threshold:overlap_count += 1results.append({'filename': filename,'overlap_count': overlap_count})return results

推荐工具

在本文代码中,我们使用了以下Python库,它们在处理几何计算、多进程处理、文件解析等方面发挥了重要作用。如果你对这些库不太熟悉,可以通过以下链接获取更多信息和文档。

  1. Shapely - 进行几何对象的构造和操作,比如多边形的交集、并集等计算。

    • 官方文档:Shapely Documentation
    • 安装方法:pip install shapely
  2. NumPy - 科学计算库,用于处理数值数组。在这里,我们用它来将多边形的坐标转换为二维数组。

    • 官方文档:NumPy Documentation
    • 安装方法:pip install numpy
  3. itertools - Python标准库中的组合工具,用于生成多边形配对,计算它们之间的IOU。

    • 官方文档:itertools Documentation
  4. concurrent.futures - Python标准库中的并发工具,用于多进程并行处理标注文件。

    • 官方文档:concurrent.futures Documentation

结论

通过这篇博客,我们详细介绍了如何使用Python并行化处理DOTA格式的标注文件,并统计对象之间的IOU重叠情况。该脚本不仅具有较强的灵活性(支持按类别或整体统计),还充分利用多进程加速大数据量的处理。希望这篇博客能够帮助你在实际项目中更高效地处理和分析目标检测任务中的标注文件。

---

希望这篇博客对您有所帮助,如果您喜欢这篇文章,请点赞或关注,我会持续分享更多实用的 Python 技术内容!

---

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/452714.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3 解决背景图与窗口留有间隙的问题

需要实现一个登录界面&#xff0c;login.vue的代码如下&#xff1a; <script> import { ref } from vue;export default {setup() {return {};}, }; </script><template><div id"login-container" class"login-container"><di…

线程的同步

文章目录 线程的同步同步&#xff1a;条件变量&#xff1a;pthread_cond_init():pthread_cond_wait()pthread_cond_signalpthread_cond_broadcast cp问题伪唤醒 信号量**多线程的互斥用信号量**&#xff1a;**单线程的互斥用锁**&#xff1a; 线程的同步 同步&#xff1a; 让…

Cesium 影像加载的TileReplacementQueue技术

本文以分析QuadtreePrimitive及相关影像内容&#xff0c;讨论一些流程和方法。影像和地形是Cesium的基础内容&#xff0c;但是有时候感觉这部分的加载和渲染效率并不高。 TileReplacementQueue是一个非常神奇的类&#xff0c;我自己研究了小半天。虽然结构简单&#xff0c;但是…

ACH支付详解,北美电商为何偏爱这一方式

ACH支付在北美广泛应用&#xff0c;低成本、可逆、安全、便捷。ZohoBooks财务管理软件支持ACH&#xff0c;可自动化处理收付款&#xff0c;提高效率并减少错误。适合北美电商客户使用&#xff0c;支持多货币和税务法规。 一、什么是ACH支付&#xff1f; ACH支付是一种通过名为…

②PROFINET转ModbusTCP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 PROFINET 转 Modbus TCP &#xff08;接上一章&#xff09; 配置使用 与 PROFINET 主站进行组态说明 这里介绍与西门子 PLC 的…

大数据-180 Elasticsearch - 原理剖析 索引写入与近实时搜索

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Docker-Consul概述以及集群环境搭建

文章目录 一、Docker consul概述二、consul 部署1.consul服务器2.registrator服务器&#xff08;客户端&#xff09;2.consul-template&#xff08;在consul服务器&#xff09;3.consul 多节点 一、Docker consul概述 容器服务更新与发现&#xff1a;先发现再更新&#xff0c;…

leetcode289:生命游戏

根据 百度百科 &#xff0c; 生命游戏 &#xff0c;简称为 生命 &#xff0c;是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子的面板&#xff0c;每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态&#xff1a; 1 即为 活细胞 &am…

ClickFix攻击活动升级:可通过虚假谷歌会议画面传播恶意软件

最近&#xff0c;研究人员报告了一种新的 ClickFix 攻击活动&#xff0c;主要通过诱骗用户访问显示虚假连接错误的欺诈性 谷歌会议的页面&#xff0c;继而借此传播信息窃取恶意软件&#xff0c;主要针对 Windows 和 macOS 操作系统。 ClickFix是网络安全公司Proofpoint在5月份…

016集——c# 实现CAD类库 与窗体的交互(CAD—C#二次开发入门)

第一步&#xff1a;搭建CAD类库dll开发环境。 第二步&#xff1a;添加窗体 第三步&#xff1a;添加控件 第四步&#xff1a;双击控件&#xff0c;在控件点击方法内输入代码 第五步&#xff1a;在主程序内实例化新建的form类&#xff0c;并弹窗form窗体 第六步&#xff1a;CAD命…

第五届人工智能与教育国际学术会议(ICAIE 2024)

文章目录 一、会议详情二、重要信息三、大会介绍四、出席嘉宾五、征稿主题六、咨询 一、会议详情 二、重要信息 大会官网&#xff1a;https://ais.cn/u/vEbMBz提交检索&#xff1a;EI Compendex、IEEE Xplore、Scopus 三、大会介绍 第五届人工智能与教育国际学术会议&#x…

学习虚幻C++开发日志——TSet

TSet 官方文档&#xff1a;虚幻引擎中的Set容器 | 虚幻引擎 5.5 文档 | Epic Developer Community (epicgames.com) TSet 是通过对元素求值的可覆盖函数&#xff0c;使用数据值本身作为键&#xff0c;而不是将数据值与独立的键相关联。 默认情况下&#xff0c;TSet 不支持重…

大数据-168 Elasticsearch 单机云服务器部署运行 详细流程

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

基于stm32的4G模块点灯实验

led模块功能封装 #include "led.h" #include "sys.h"//初始化GPIO函数 void led_init(void) {GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOB_CLK_ENABLE();//调用GPIO初始化函数gpio_initstruct.Pin GPIO_PIN_8 | GPIO_PIN_9;gpio_inits…

Linux基本指令一眼看懂(简洁表示)

首先先声明是简单表示&#xff0c;如果要全指令有链接 1. ls 指令 ls [选项] [文件/目录]常用选项: -l: 以长格式列出文件和目录的详细信息。 -a: 显示所有文件&#xff0c;包括隐藏文件&#xff08;以.开头的文件&#xff09;。 -h: 以人类可读的格式显示文件大小。 示例: …

基于stm32的esp8266的WIFI控制风扇实验

实验案例&#xff37;&#xff29;&#xff26;&#xff29;控制风扇 项目需求 电脑通过esp8266模块远程遥控风扇。 项目框图 ​ 风扇模块封装 #include "sys.h" #include "fan.h"void fan_init(void) {GPIO_InitTypeDef gpio_initstruct;//打开时钟…

数据库知识点整理

DDL DDL-数据库操作 show databases ------------ 查看所有数据库 select database(); ----------查看当前数据库 create database 数据库名&#xff1b;---- 创建数据库 use 数据库名&#xff1b; --------------使用数据库 drop database 数据库名&#xff1b;--…

day02_计算机常识丶第一个程序丶注释丶关键字丶标识符

计算机常识 计算机如何存储数据 计算机世界中只有二进制。那么在计算机中存储和运算的所有数据都要转为二进制。包括数字、字符、图片、声音、视频等。 进制 进制也就是进位计数制&#xff0c;是人为定义的带进位的计数方法 实例&#xff1a; // 在java 中 可以使用不同…

[PHP]Undefined index错误只针对数组

1、示例一 <?php $a null; var_dump($a[name]); 结果&#xff1a;无报错 2、示例二 <?php $a []; var_dump($a[name]);结果&#xff1a;报错

【JavaEE初阶】深入理解网络编程—使用UDP协议API实现回显服务器

前言 &#x1f31f;&#x1f31f;本期讲解关于TCP/UDP协议的原理理解~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不…