【目标检测】大图包括标签切分,并转换成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()

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

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

相关文章

Elasticsearch:使用 Langchain 和 OpenAI 进行问答

这款交互式 jupyter notebook 使用 Langchain 将虚构的工作场所文档拆分为段落 (chunks),并使用 OpenAI 将这些段落转换为嵌入并将其存储到 Elasticsearch 中。然后,当我们提出问题时,我们从向量存储中检索相关段落,并使用 langch…

Google SGE 正在添加人工智能图像生成器,现已推出:从搜索中的生成式 AI 中获取灵感的新方法

🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…

1688拍立淘接口,按图搜索1688商品接口,图片搜索商品接口,1688API接口

按图搜索1688商品的方法如下: 打开1688平台,点击首页右上角的搜索框,进入搜索页面。 点击搜索框右侧的相机图标,选择“拍照”或“相册”,上传你想要搜索的图片。 等待图片上传完成,系统会自动识别图片中的…

基于Effect的组件设计 | 京东云技术团队

Effect的概念起源 从输入输出的角度理解Effect https://link.excalidraw.com/p/readonly/KXAy7d2DlnkM8X1yps6L 编程中的Effect起源于函数式编程中纯函数的概念 纯函数是指在相同的输入下,总是产生相同的输出,并且没有任何副作用(side effect)的函数。…

Qt QMultiMap

QMultiMap 文章目录 QMultiMap摘要QMultiMapQMultiMap 特点代码示例 关键字: Qt、 QMultiMap、 容器、 键值、 键值重复 摘要 今天在观摩小伙伴撸代码的时候,突然听到了QMultiMap自己使用Qt开发这么就,竟然都不知道,所以趁没…

sentinel的启动与运行

首先我们github下载sentinel Releases alibaba/Sentinel (github.com) 下载好了后输入命令让它运行即可,使用cmd窗口输入一下命令即可 java -Dserver.port8089 -jar sentinel-dashboard-1.8.6.jar 账号密码默认都是sentinel 启动成功后登录进去效果如下

ABAP 采购组 条目 Z001 不存在T161内-请检查输入

背景:在ALV报表更改PR采购组 做法:ALV报表取出PR相关数据,直接将采购组列设置为可编辑,然后设置按钮更改逻辑。 操作:将采购组值更新(从原来500改为600),然后点更改功能按钮&#xf…

汽车一键启动点火开关按键一键启动按钮型号规格

汽车点火开关/移动管家一键启动按键/汽车改装引擎启动按钮型号:YD828溥款开关 一键启动按钮(适用于配套启动主机使用或原车一键启动开关更换) 1.适合配套专用板板安装 2.开孔器开孔安装 3.原车钥匙位安装 外观:黑色 按钮上有3种不…

SpringBoot 前端406 后端Could not find acceptable representation

原因:返回对象没有get方法,无法转成JSON格式

Elasticsearch:什么是检索增强生成 - RAG?

在人工智能的动态格局中,检索增强生成(Retrieval Augmented Generation - RAG)已经成为游戏规则的改变者,彻底改变了我们生成文本和与文本交互的方式。 RAG 使用大型语言模型 (LLMs) 等工具将信息检索的能力与自然语言生成无缝结合…

Linux系统下centos中在线添加硬盘后不重启在线扩容linux系统目录不重启系统

Centos7 在线添加硬盘不重启系统 CentOS 7在线添加新磁盘,无需重启 现有环境基本都是线下server以及线上虚拟机等,几乎都支持热插拔,热扩容,所以在线添加新磁盘就尤为重要,这样可以无需中断当前服务或进程也可对其进行添加硬盘操作。 1.添加硬盘: 虚拟机在线状态下对其进行添加…

centos下安装配置redis7

1、找个目录下载安装包 sudo wget https://download.redis.io/release/redis-7.0.0.tar.gz 2、将tar.gz包解压至指定目录下 sudo mkdir /home/redis sudo tar -zxvf redis-7.0.0.tar.gz -C /home/redis 3、安装gcc-c yum install gcc-c 4、切换到redis-7.0.0目录下 5、修改…

【RabbitMQ】docker rabbitmq集群 docker搭建rabbitmq集群

docker rabbitmq集群 docker搭建rabbitmq集群 RabbitMQ提供了两种常用的集群模式 1.普通集群模式 2.镜像集群模式 普通集群模式只能同步主节点上的交换机和队列信息,但对于队列中的消息不做同步,主节点宕机也不能进行切换(故障转移&#xff…

【Python】PaddleOCR文字识别国产之光 从安装到pycharm中测试 (保姆级图文)

目录 官方项目地址Python环境搭建(也就是使用Anaconda的python)1. 安装Anaconda1. 打开终端并创建conda环境 安装PaddlePaddle(CPU演示)安装PaddleOCR whl包如果安装shapely库报错(我没有报错,其他类似库安…

Pygame中将鼠标形状设置为图片2-2

3 编写主程序 在主程序中,首先创建屏幕并且完成一些准备工作,之后在while循环中不断更新sprite实例即可。 3.1 创建屏幕及准备工作 创建屏幕及准备工作的代码如图5所示。 图5 创建屏幕及准备工作 其中,第20行代码调用pygame.mouse模块中的…

pycharm设置pyuic和pyrcc

pyuic设置 适合任何虚拟环境,直接用虚拟环境的python解决一切。。。 E:\anaconda3\envs\qt5\python.exe-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py$FileDir$pyrcc设置 E:\anaconda3\envs\qt5\python.exe-m PyQt5.pyrcc_main $FileName$ -o…

《机器学习》第5章 神经网络

文章目录 5.1 神经元模型5.2 感知机与多层网络5.3 误差逆传播算法5.4 全局最小与局部最小5.5 其他常见神经网络RBF网络ART网络SOM网络级联相关网络Elman网络Boltzmann机 5.6 深度学习 5.1 神经元模型 神经网络是由具有适应性的简单单元组成的广泛并行互连的网络,它…

什么是MTU(Maximum Transmission Unit)?

最大传输单元MTU(Maximum Transmission Unit,MTU),是指网络能够传输的最大数据包大小,以字节为单位。MTU的大小决定了发送端一次能够发送报文的最大字节数。如果MTU超过了接收端所能够承受的最大值,或者是超…

基于安卓android微信小程序宠物交易小程序

运行环境 开发语言:Java 框架:ssm JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 小程序框架&…

Spring-事务源码解析2

上一篇文章我们介绍了事务开启注解EnableTransactionManagement源码解析《Spring-事务源码解析1》 里面提到了2个关键组件,这里我们分析下Spring如何利用这2个组件来给Bean创建代理对象。 文章待整理 先说下执行流程,当请求进来的时候,会执…