【目标检测】大图包括标签切分,并转换成txt格式
前言
遥感图像比较大,通常需要切分成小块再进行训练,之前写过一篇关于大图裁切和拼接的文章【目标检测】图像裁剪/标签可视化/图像拼接处理脚本,不过当时的工作流是先将大图切分成小图,再在小图上进行标注,于是就不考虑标签变换的问题。
最近项目遇到的问题是,一批大图已经做好标注,需要将其裁切,同时标签也要进行同步裁切。本文讲解如何实现这一需求,同时将labelimg直出的xml格式标签转换成yolov5等模型需要的txt标签。
图片裁剪
图片裁剪还是沿用了一套之前博文提到的编码规则,即将图片裁成1280x1280的图像块,裁剪后通过文件名来标记图像块在原始图像中的位置。
import configparser
import shutil
import yaml
import os.path
from pathlib import Path
from PIL import Image
from tqdm import tqdmrootdir = r"E:\Dataset\数据集\可见光数据\原始未裁剪\img"
savedir = r'E:\Dataset\数据集\可见光数据\裁剪后数据\img' # 保存图片文件夹dis = 1280
leap = 1280def main():# 创建输出文件夹if Path(savedir).exists():shutil.rmtree(savedir)os.mkdir(savedir)num_dir = len(os.listdir(rootdir)) # 得到文件夹下数量num = 0for parent, dirnames, filenames in os.walk(rootdir): # 遍历每一张图片filenames.sort()for filename in tqdm(filenames):currentPath = os.path.join(parent, filename)suffix = currentPath.split('.')[-1]if suffix == 'jpg' or suffix == 'png' or suffix == 'JPG' or suffix == 'PNG':img = Image.open(currentPath)width = img.size[0]height = img.size[1]i = j = 0for i in range(0, width, leap):for j in range(0, height, leap):box = (i, j, i + dis, j + dis)image = img.crop(box) # 图像裁剪image.save(savedir + '/' + filename.split(suffix)[0][:-1] + "__" + str(i) + "__" + str(j) + ".jpg")if __name__ == '__main__':main()
标签裁剪
标签读取
首先需要通过lxml库对xml格式的数据进行解析,主要提取两个信息,1是目标类别,2是目标bbox坐标。
通过递归形式,将xml转换成字典形式,然后就可以获取到需要的信息。
def parse_xml_to_dict(xml):"""将xml文件解析成字典形式"""if len(xml) == 0: # 遍历到底层,直接返回tag对应的信息return {xml.tag: xml.text}result = {}for child in xml:child_result = parse_xml_to_dict(child) # 递归遍历标签信息if child.tag != 'object':result[child.tag] = child_result[child.tag]else:if child.tag not in result:result[child.tag] = []result[child.tag].append(child_result[child.tag])return {xml.tag: result}def main():xml_path = r"label.xml"with open(xml_path, encoding="utf-8") as fid:xml_str = fid.read()xml = etree.fromstring(xml_str)data = parse_xml_to_dict(xml)["annotation"]for obj in data["object"]:# 获取每个object的box信息xmin = float(obj["bndbox"]["xmin"])xmax = float(obj["bndbox"]["xmax"])ymin = float(obj["bndbox"]["ymin"])ymax = float(obj["bndbox"]["ymax"])class_name = obj["name"]
标签位置重置
由于图像裁剪成小的图像块,标签也要转换成图像块对应的bbox。不过,对于裁剪的图像,存在的一个问题是,如果标签被切分成两半,该如何进行处理。
下面是我的处理思路,通过对图像块的位置编码,可以分成四种情况。
第一种情况,标签四个角全在图像块中,此时不用做过多处理。
(下图仅为示意,实际尺寸比例未精确,黑色为bbox,红色为切割线)

第二种情况,标签被左右裁开。此时,将左右两部分都当作一个label分给相应的图像块。

第三种情况,标签被上下裁开。此时,将上下两部分都当作一个label分给相应的图像块。

第四种情况,标签被四块裁开,此时,每一块都过于细小,对于小目标而言,这种情况比较少见,因此舍弃该标签。

对应代码:
xmin_index = int(xmin / leap)
xmax_index = int(xmax / leap)
ymin_index = int(ymin / leap)
ymax_index = int(ymax / leap)xmin = xmin % leap
xmax = xmax % leap
ymin = ymin % leap
ymax = ymax % leap# 第一种情况,两个点在相同的图像块中
if xmin_index == xmax_index and ymin_index == ymax_index:info = xml2txt(xmin, xmax, ymin, ymax, class_name, img_width, img_height)file_name = img_name + "__" + str(xmin_index * leap) + "__" + str(ymin_index * leap) + ".txt"write_txt(info, file_name)
# 第二种情况,目标横跨左右两幅图
elif xmin_index + 1 == xmax_index and ymin_index == ymax_index:# 保存左半目标info = xml2txt(xmin, leap, ymin, ymax, class_name, img_width, img_height)file_name = img_name + "__" + str(xmin_index * leap) + "__" + str(ymax_index * leap) + ".txt"write_txt(info, file_name)# 保存右半目标info = xml2txt(0, xmax, ymin, ymax, class_name, img_width, img_height)file_name = img_name + "__" + str(xmax_index * leap) + "__" + str(ymax_index * leap) + ".txt"write_txt(info, file_name)
# 第三种情况,目标纵跨上下两幅图
elif xmin_index == xmax_index and ymin_index + 1 == ymax_index:# 保存上半目标info = xml2txt(xmin, xmax, ymin, leap, class_name, img_width, img_height)file_name = img_name + "__" + str(xmin_index * leap) + "__" + str(ymin_index * leap) + ".txt"write_txt(info, file_name)# 保存下半目标info = xml2txt(xmin, xmax, 0, ymax, class_name, img_width, img_height)file_name = img_name + "__" + str(xmin_index * leap) + "__" + str(ymax_index * leap) + ".txt"write_txt(info, file_name)
标签转换成txt格式
xml格式是 xmin,ymin,xmax,ymax,对应左上角和左下角矩形框的全局像素点坐标。
txt格式是 class, xcenter, ycenter, w, h, 对应中心点和bbox的宽和高,不过该坐标是相对坐标,这里转换时需要除以小图的宽高。
相关代码:
def xml2txt(xmin, xmax, ymin, ymax, class_name, img_width, img_height):# 类别索引class_index = class_dict.index(class_name)# 将box信息转换到yolo格式xcenter = xmin + (xmax - xmin) / 2ycenter = ymin + (ymax - ymin) / 2w = xmax - xminh = ymax - ymin# 绝对坐标转相对坐标,保存6位小数xcenter = round(xcenter / img_width, 6)ycenter = round(ycenter / img_height, 6)w = round(w / img_width, 6)h = round(h / img_height, 6)info = [str(i) for i in [class_index, xcenter, ycenter, w, h]]return info
完整代码
最后附上批量处理的完整代码:
import os
from tqdm import tqdm
from lxml import etreexml_file_path = "E:/Dataset/数据集/可见光数据/原始未裁剪/labels"
output_txt_path = "E:/Dataset/数据集/可见光数据/裁剪后数据/labels"class_dict = ['class1', 'class2']
leap = 1280def parse_xml_to_dict(xml):"""将xml文件解析成字典形式"""if len(xml) == 0: # 遍历到底层,直接返回tag对应的信息return {xml.tag: xml.text}result = {}for child in xml:child_result = parse_xml_to_dict(child) # 递归遍历标签信息if child.tag != 'object':result[child.tag] = child_result[child.tag]else:if child.tag not in result:result[child.tag] = []result[child.tag].append(child_result[child.tag])return {xml.tag: result}def xml2txt(xmin, xmax, ymin, ymax, class_name, img_width, img_height):# 类别索引class_index = class_dict.index(class_name)# 将box信息转换到yolo格式xcenter = xmin + (xmax - xmin) / 2ycenter = ymin + (ymax - ymin) / 2w = xmax - xminh = ymax - ymin# 绝对坐标转相对坐标,保存6位小数xcenter = round(xcenter / img_width, 6)ycenter = round(ycenter / img_height, 6)w = round(w / img_width, 6)h = round(h / img_height, 6)info = [str(i) for i in [class_index, xcenter, ycenter, w, h]]return infodef write_txt(info, file_name):with open(file_name, encoding="utf-8", mode="a") as f:# 若文件不为空,添加换行if os.path.getsize(file_name):f.write("\n" + " ".join(info))else:f.write(" ".join(info))def main():for xml_file in os.listdir(xml_file_path):with open(os.path.join(xml_file_path, xml_file), encoding="utf-8") as fid:xml_str = fid.read()xml = etree.fromstring(xml_str)data = parse_xml_to_dict(xml)["annotation"]# img_height = int(data["size"]["height"])# img_width = int(data["size"]["width"])img_height = leapimg_width = leapimg_name = xml_file[:-4]for obj in data["object"]:# 获取每个object的box信息xmin = float(obj["bndbox"]["xmin"])xmax = float(obj["bndbox"]["xmax"])ymin = float(obj["bndbox"]["ymin"])ymax = float(obj["bndbox"]["ymax"])class_name = obj["name"]xmin_index = int(xmin / leap)xmax_index = int(xmax / leap)ymin_index = int(ymin / leap)ymax_index = int(ymax / leap)xmin = xmin % leapxmax = xmax % leapymin = ymin % leapymax = ymax % leap# 第一种情况,两个点在相同的图像块中if xmin_index == xmax_index and ymin_index == ymax_index:info = xml2txt(xmin, xmax, ymin, ymax, class_name, img_width, img_height)file_name = output_txt_path + "/" + img_name + "__" + str(xmin_index * leap) + "__" + str(ymin_index * leap) + ".txt"write_txt(info, file_name)# 第二种情况,目标横跨左右两幅图elif xmin_index + 1 == xmax_index and ymin_index == ymax_index:# 保存左半目标info = xml2txt(xmin, leap, ymin, ymax, class_name, img_width, img_height)file_name = output_txt_path + "/" + img_name + "__" + str(xmin_index * leap) + "__" + str(ymax_index * leap) + ".txt"write_txt(info, file_name)# 保存右半目标info = xml2txt(0, xmax, ymin, ymax, class_name, img_width, img_height)file_name = output_txt_path + "/" + img_name + "__" + str(xmax_index * leap) + "__" + str(ymax_index * leap) + ".txt"write_txt(info, file_name)# 第三种情况,目标纵跨上下两幅图elif xmin_index == xmax_index and ymin_index + 1 == ymax_index:# 保存上半目标info = xml2txt(xmin, xmax, ymin, leap, class_name, img_width, img_height)file_name = output_txt_path + "/" + img_name + "__" + str(xmin_index * leap) + "__" + str(ymin_index * leap) + ".txt"write_txt(info, file_name)# 保存下半目标info = xml2txt(xmin, xmax, 0, ymax, class_name, img_width, img_height)file_name = output_txt_path + "/" + img_name + "__" + str(xmin_index * leap) + "__" + str(ymax_index * leap) + ".txt"write_txt(info, file_name)if __name__ == "__main__":main()相关文章:
【目标检测】大图包括标签切分,并转换成txt格式
前言 遥感图像比较大,通常需要切分成小块再进行训练,之前写过一篇关于大图裁切和拼接的文章【目标检测】图像裁剪/标签可视化/图像拼接处理脚本,不过当时的工作流是先将大图切分成小图,再在小图上进行标注,于是就不考…...
gitlab登录出现的Invalid login or password问题
前提 我是在一个项目里创建的gitlab账号,想在别的项目里登录或者官网登录发现怎么都登陆不上 原因 在GitLab中,有两种不同的账号类型:项目账号和个人账号(官网账号)。 项目账号:项目账号是在特定GitLab…...
git本地创建分支并推送到远程
1. 创建本地分支并切换到该分支 比如我创建dev分支。git checkout -b相当于把两条命令git branch 分支名、git checkout分支名合成一条,来实现一条命令新建分支切换分支。 git checkout -b dev 2. 将dev分支推送到远程 -u参数与--set-upstream这一串是一个意思&am…...
手机待办事项app哪个好?
手机是日常很多人随身携带的设备,手机除了拥有通讯功能外,还能帮助大家高效管理日常工作,借助手机上的待办事项提醒APP可以快速地帮助大家规划日常事务,提高工作的效率。 过去,我也曾经在寻找一款能够将工作任务清晰罗…...
容器运行elasticsearch安装ik分词非root权限安装报错问题
有些应用默认不允许root用户运行,来确保应用的安全性,这也会导致我们使用docker run后一些操作问题,用es安装ik分词器举例(es版本8.9.0,analysis-ik版本8.9.0) 1. 容器启动elasticsearch 如挂载方式&…...
UE4游戏客户端开发进阶学习指南
前言 两年多前写过一篇入门指南,教大家在短时间内快速入门UE4的使用,在知乎被很多人收藏了。如今鸡佬使用UE快三年了,是时候更新一下进阶版本的学习指南。本文对于读者的要求: 有一定的C基础已经入门UE,能够用蓝图和…...
javaee SpringMVC 乱码问题解决
方法一 在web.xml文件中注册过滤器 <!-- 注册过滤器 设置编码 --><filter><filter-name>CharacterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param&…...
用ChatGPT做数据分析,提升10倍工作效率
目录 写报告分析框架报告框架指标体系设计 Excel 写报告 分析框架 拿到一个专题不知道怎么做?没关系,用ChatGPT列一下框架。 以上分析框架挺像那么回事,如果没思路的话,问问ChatGPT能起到找灵感的作用。 报告框架 报告的框架…...
【Pytorch笔记】4.梯度计算
深度之眼官方账号 - 01-04-mp4-计算图与动态图机制 前置知识:计算图 可以参考我的笔记: 【学习笔记】计算机视觉与深度学习(2.全连接神经网络) 计算图 以这棵计算图为例。这个计算图中,叶子节点为x和w。 import torchw torch.tensor([1.]…...
浏览器安装vue调试工具
下载扩展程序文件 下载链接:链接: 下载连接网盘地址, 提取码: 0u46,里面有两个crx,一个适用于vue2,一个适用于vue3,可根据vue版本选择不同的调试工具 crx安装扩展程序不成功,将文件改为rar文件然后解压 安装…...
C/C++学习 -- RSA算法
概述 RSA算法是一种广泛应用于数据加密与解密的非对称加密算法。它由三位数学家(Rivest、Shamir和Adleman)在1977年提出,因此得名。RSA算法的核心原理是基于大素数的数学问题的难解性,利用两个密钥来完成加密和解密操作。 特点 …...
基于若依ruoyi-nbcio支持flowable流程增加自定义业务表单(一)
因为需要支持自定义业务表单的相关流程,所以需要建立相应的关联表 1、首先先建表wf_custom_form -- ---------------------------- -- Table structure for wf_custom_form -- ---------------------------- DROP TABLE IF EXISTS wf_custom_form; CREATE TABLE wf…...
面试经典 150 题 1 —(数组 / 字符串)— 88. 合并两个有序数组
88. 合并两个有序数组 方法一: class Solution { public:void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {for(int i 0; i<n;i){nums1[mi] nums2[i];}sort(nums1.begin(),nums1.end());} };方法二: clas…...
【大数据 | 综合实践】大数据技术基础综合项目 - 基于GitHub API的数据采集与分析平台
🤵♂️ 个人主页: AI_magician 📡主页地址: 作者简介:CSDN内容合伙人,全栈领域优质创作者。 👨💻景愿:旨在于能和更多的热爱计算机的伙伴一起成长!!&…...
超高频RFID模具精细化生产管理方案
近二十年来,我国的模具行业经历了快速发展的阶段,然而,模具行业作为一个传统、复杂且竞争激烈的行业,企业往往以订单为导向,每个订单都需要进行新产品的开发,从客户需求分析、结构确定、报价、设计、物料准…...
FP-Growth算法全解析:理论基础与实战指导
目录 一、简介什么是频繁项集?什么是关联规则挖掘?FP-Growth算法与传统方法的对比Apriori算法Eclat算法 FP树:心脏部分 二、算法原理FP树的结构构建FP树第一步:扫描数据库并排序第二步:构建树 挖掘频繁项集优化&#x…...
Jmeter 分布式压测,你的系统能否承受高负载?
你可以使用 JMeter 来模拟高并发秒杀场景下的压力测试。这里有一个例子,它模拟了同时有 5000 个用户,循环 10 次的情况。 请求默认配置 token 配置 秒杀接口 结果分析 但是,实际企业中,这种压测方式根本不满足实际需求。下…...
什么是浮动密封?
浮动密封也称为机械面密封或双锥密封,是一种用于各种行业和应用的特殊类型的密封装置。它旨在提供有效的密封和保护,防止污染物的进入以及旋转设备中润滑剂或液体的润滑剂泄漏。 浮动密封件由相同的金属环组成,这些金属环称为密封环…...
浅析前端单元测试
对于前端来说,测试主要是对HTML、CSS、JavaScript进行测试,以确保代码的正常运行。 常见的测试有单元测试、集成测试、端到端(e2e)的测试。 单元测试:对程序中最小可测试单元进行测试。我们可以类比对汽车的测试&…...
线上mysql表字段加不了Fail to get MDL on replica during DDL synchronize,排查记录
某天接近业务高峰期想往表里加字段加不了,报错:Fail to get MDL on replica during DDL synchronize 遂等到业务空闲时操作、还是加不了, 最后怀疑是相关表被锁了,或者有事务一直进行(可能这俩是一个意思)&…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...
基于stm32F10x 系列微控制器的智能电子琴(附完整项目源码、详细接线及讲解视频)
注:文章末尾网盘链接中自取成品使用演示视频、项目源码、项目文档 所用硬件:STM32F103C8T6、无源蜂鸣器、44矩阵键盘、flash存储模块、OLED显示屏、RGB三色灯、面包板、杜邦线、usb转ttl串口 stm32f103c8t6 面包板 …...
Java中栈的多种实现类详解
Java中栈的多种实现类详解:Stack、LinkedList与ArrayDeque全方位对比 前言一、Stack类——Java最早的栈实现1.1 Stack类简介1.2 常用方法1.3 优缺点分析 二、LinkedList类——灵活的双端链表2.1 LinkedList类简介2.2 常用方法2.3 优缺点分析 三、ArrayDeque类——高…...
