当前位置: 首页 > news >正文

使用YOLO系列txt目标检测标签的滑窗切割:批量处理图像和标签的实用工具

使用YOLO系列txt目标检测标签的滑窗切割:批量处理图像和标签的实用工具

    • 使用YOLO的TXT目标检测标签的滑窗切割:批量处理图像和标签的实用工具
      • 背景
      • 1. 代码概述
      • 2. 滑窗切割算法原理
        • 滑窗切割步骤:
        • 示例:
      • 3. **代码实现**
        • 1. **加载标签**
        • 2. **切割标签**
        • 3. **主函数**
      • 4. **如何使用该工具**
      • 4. **完整代码**

使用YOLO的TXT目标检测标签的滑窗切割:批量处理图像和标签的实用工具

背景

在计算机视觉领域,目标检测(Object Detection)是一个非常重要的任务。随着 YOLO(You Only Look Once)系列模型的普及,目标检测模型已经被广泛应用于各种实际场景中。对于目标检测任务,训练模型所需的标注数据至关重要。

当我们处理大规模图像数据集时,尤其是在图像的尺寸远大于模型输入尺寸时,往往需要使用 滑窗切割(Sliding Window)技术,将大图像分割成多个小块进行处理。这一过程不仅可以减小每次训练所需的计算资源,还能增强模型的鲁棒性。

本博客将介绍如何使用 YOLO 的TXT目标检测标签格式 对大图像进行滑窗切割,并确保标签的正确性。我们将逐步阐述该代码的工作原理、使用方法及其在目标检测中的实际意义。

1. 代码概述

该代码实现了对大图像及其对应标签的 滑窗切割,并确保切割后的标签正确地被裁剪并保存。它通过对图像和标签的逐块切割,将大图像分割成多个较小的图像块,同时调整标签的位置和大小,以符合新的图像尺寸。

主要步骤如下:

  1. 加载图像和标签:读取图片和标签文件,确保标签与图像对应。
  2. 滑窗切割:以给定的窗口大小和步长,对图像进行滑窗切割。
  3. 裁剪标签:对于每个切割窗口,检查标签是否位于窗口内,如果位于窗口内,调整标签坐标,并确保标签归一化。
  4. 保存切割后的图像和标签:将切割后的图像和标签保存到新的文件夹中。

2. 滑窗切割算法原理

滑窗切割是计算机视觉中常用的技术,通常用于:

  • 大图像分块:当图像尺寸过大时,模型输入尺寸无法处理整个图像,可以将其切割成小块进行逐块处理。
  • 多尺度检测:不同尺度的物体需要不同大小的窗口来检测。通过滑窗切割,能够在多个尺度上执行目标检测任务。
滑窗切割步骤:
  1. 指定窗口大小和步长:窗口大小和步长决定了滑窗的密集程度。步长越小,生成的窗口越多,计算量越大。窗口大小决定了每个块的输入尺寸。

  2. 标签裁剪:标签的裁剪是根据目标与滑窗的交集来进行的。每个标签会被裁剪到窗口内,并且坐标会被重新归一化到窗口的尺寸。

示例:
  • 窗口大小:640x640像素。
  • 横向步长:301像素。
  • 纵向步长:180像素。

对于每个标签,代码会检查它是否位于当前滑窗内,如果是,标签的位置和尺寸会被重新计算并保存。

3. 代码实现

1. 加载标签
def load_labels(label_file):"""加载YOLO的标签文件"""labels = []with open(label_file, 'r') as f:for line in f:parts = line.strip().split()cls = int(parts[0])  # 类别x_center, y_center, w, h = map(float, parts[1:])labels.append((cls, x_center, y_center, w, h))return labels

这段代码用于读取每个标签文件,并将其转换为包含类别和坐标的格式,方便后续处理。

2. 切割标签
def save_cut_labels(window_x, window_y, window_size, img_width, img_height, labels):"""根据滑窗切割标签,并确保标签正确裁剪"""new_labels = []for cls, x_center, y_center, w, h in labels:# 将归一化坐标转换为像素坐标x_center_px = x_center * img_widthy_center_px = y_center * img_heightw_px = w * img_widthh_px = h * img_height# 计算标签与当前窗口的交集区域intersection_x1 = max(x_center_px - w_px / 2, window_x)intersection_y1 = max(y_center_px - h_px / 2, window_y)intersection_x2 = min(x_center_px + w_px / 2, window_x + window_size)intersection_y2 = min(y_center_px + h_px / 2, window_y + window_size)# 如果标签和窗口相交if intersection_x1 < intersection_x2 and intersection_y1 < intersection_y2:# 计算交集区域的宽高和中心坐标intersection_w = intersection_x2 - intersection_x1intersection_h = intersection_y2 - intersection_y1intersection_x_center = (intersection_x1 + intersection_x2) / 2intersection_y_center = (intersection_y1 + intersection_y2) / 2# 将交集区域的坐标归一化normalized_x_center = (intersection_x_center - window_x) / window_sizenormalized_y_center = (intersection_y_center - window_y) / window_sizenormalized_w = intersection_w / window_sizenormalized_h = intersection_h / window_size# 生成新的标签new_labels.append(f"{cls} {normalized_x_center} {normalized_y_center} {normalized_w} {normalized_h}")return new_labels

该函数根据当前窗口的位置,裁剪标签,并将裁剪后的标签归一化到当前窗口大小。

3. 主函数
def main():image_folder = 'images'  # 输入图片文件夹label_folder = 'labels'  # 输入标签文件夹output_image_folder = 'output_images'output_label_folder = 'output_labels'if not os.path.exists(output_image_folder):os.makedirs(output_image_folder)if not os.path.exists(output_label_folder):os.makedirs(output_label_folder)image_files = sorted(os.listdir(image_folder))label_files = sorted(os.listdir(label_folder))window_size = 640  # 滑窗大小step_x = 301  # 横向步长step_y = 180  # 纵向步长# 遍历所有图片和标签文件for image_file, label_file in zip(image_files, label_files):# 读取图片image_path = os.path.join(image_folder, image_file)image = cv2.imread(image_path)img_height, img_width, _ = image.shape# 读取对应的标签label_path = os.path.join(label_folder, label_file)labels = load_labels(label_path)# 计算横向和纵向可以切割的窗口数量num_windows_x = (img_width - window_size) // step_x + 1num_windows_y = (img_height - window_size) // step_y + 1# 遍历所有切割窗口for i in range(num_windows_x):for j in range(num_windows_y):window_x = i * step_xwindow_y = j * step_y# 获取当前窗口内的标签windowed_labels = save_cut_labels(window_x, window_y, window_size, img_width, img_height, labels)if windowed_labels:  # 如果窗口内有标签# 保存切割后的图片windowed_image = image[window_y:window_y + window_size, window_x:window_x + window_size]output_image_path = os.path.join(output_image_folder, f"{os.path.splitext(image_file)[0]}_window_{i}_{j}.jpg")cv2.imwrite(output_image_path, windowed_image)# 保存切割后的标签output_label_path = os.path.join(output_label_folder, f"{os.path.splitext(label_file)[0]}_window_{i}_{j}.txt")with open(output_label_path, 'w') as f:for label in windowed_labels:f.write(label + '\n')

4. 如何使用该工具

  1. 准备工作

    • 将你的图片和标签放在 images/labels/ 文件夹中。
    • 确保标签格式为 YOLOv5 格式,即每行包含 class_id x_center y_center width height(所有值均为归一化形式)。
  2. 运行脚本

    • 运行上述代码,程序将自动读取图片和标签,进行滑窗切割,并将每个切割后的图像和标签保存到新的文件夹中。
  3. 输出结果

    • 切割后的图像会保存在 output_images/ 文件夹中。
    • 切割后的标签会保存在 output_labels/ 文件夹中,标签内容与原标签一致,只是经过裁

4. 完整代码

import os
import cv2def load_labels(label_path):"""加载YOLOv5标签文件"""labels = []with open(label_path, 'r') as f:for line in f.readlines():parts = line.strip().split()cls = int(parts[0])  # 类别x_center = float(parts[1])  # x中心y_center = float(parts[2])  # y中心w = float(parts[3])  # 宽度h = float(parts[4])  # 高度labels.append([cls, x_center, y_center, w, h])return labelsdef save_cut_labels(window_x, window_y, window_size, img_width, img_height, labels):"""根据滑窗切割标签,并确保标签正确裁剪"""new_labels = []for cls, x_center, y_center, w, h in labels:# 将归一化坐标转换为像素坐标x_center_px = x_center * img_widthy_center_px = y_center * img_heightw_px = w * img_widthh_px = h * img_height# 计算标签与当前窗口的交集区域intersection_x1 = max(x_center_px - w_px / 2, window_x)intersection_y1 = max(y_center_px - h_px / 2, window_y)intersection_x2 = min(x_center_px + w_px / 2, window_x + window_size)intersection_y2 = min(y_center_px + h_px / 2, window_y + window_size)# 如果标签和窗口相交if intersection_x1 < intersection_x2 and intersection_y1 < intersection_y2:# 计算交集区域的宽高和中心坐标intersection_w = intersection_x2 - intersection_x1intersection_h = intersection_y2 - intersection_y1intersection_x_center = (intersection_x1 + intersection_x2) / 2intersection_y_center = (intersection_y1 + intersection_y2) / 2# 将交集区域的坐标归一化normalized_x_center = (intersection_x_center - window_x) / window_sizenormalized_y_center = (intersection_y_center - window_y) / window_sizenormalized_w = intersection_w / window_sizenormalized_h = intersection_h / window_size# 生成新的标签new_labels.append(f"{cls} {normalized_x_center} {normalized_y_center} {normalized_w} {normalized_h}")# 如果没有标签,返回空列表return new_labelsdef main():image_folder = 'images'  # 输入图片文件夹label_folder = 'labels'  # 输入标签文件夹output_image_folder = 'output_images'output_label_folder = 'output_labels'if not os.path.exists(output_image_folder):os.makedirs(output_image_folder)if not os.path.exists(output_label_folder):os.makedirs(output_label_folder)image_files = sorted(os.listdir(image_folder))label_files = sorted(os.listdir(label_folder))window_size = 640  # 滑窗大小step_x = 301  # 横向步长step_y = 180  # 纵向步长# 遍历所有图片和标签文件for image_file, label_file in zip(image_files, label_files):# 读取图片image_path = os.path.join(image_folder, image_file)image = cv2.imread(image_path)img_height, img_width, _ = image.shape# 读取对应的标签label_path = os.path.join(label_folder, label_file)labels = load_labels(label_path)# 计算横向和纵向可以切割的窗口数量num_windows_x = (img_width - window_size) // step_x + 1num_windows_y = (img_height - window_size) // step_y + 1# 遍历所有切割窗口for i in range(num_windows_x):for j in range(num_windows_y):window_x = i * step_xwindow_y = j * step_y# 获取当前窗口内的标签windowed_labels = save_cut_labels(window_x, window_y, window_size, img_width, img_height, labels)# 如果标签列表为空,说明此窗口没有标签,跳过该窗口if not windowed_labels:continue# 保存切割后的图片windowed_image = image[window_y:window_y + window_size, window_x:window_x + window_size]output_image_path = os.path.join(output_image_folder, f"{os.path.splitext(image_file)[0]}_window_{i}_{j}.jpg")cv2.imwrite(output_image_path, windowed_image)# 保存切割后的标签output_label_path = os.path.join(output_label_folder, f"{os.path.splitext(label_file)[0]}_window_{i}_{j}.txt")with open(output_label_path, 'w') as f:for label in windowed_labels:f.write(label + '\n')if __name__ == "__main__":main()

相关文章:

使用YOLO系列txt目标检测标签的滑窗切割:批量处理图像和标签的实用工具

使用YOLO系列txt目标检测标签的滑窗切割&#xff1a;批量处理图像和标签的实用工具 使用YOLO的TXT目标检测标签的滑窗切割&#xff1a;批量处理图像和标签的实用工具背景1. 代码概述2. 滑窗切割算法原理滑窗切割步骤&#xff1a;示例&#xff1a; 3. **代码实现**1. **加载标签…...

架构10-可观测性

零、文章目录 架构10-可观测性 1、可观测性 &#xff08;1&#xff09;可观测性的背景 **历史沿革&#xff1a;**可观测性最初由匈牙利数学家鲁道夫卡尔曼提出&#xff0c;用于线性动态控制系统。后来&#xff0c;该概念被引入到计算机科学中。**现代意义&#xff1a;**在分…...

git管理Unity项目的正确方式

git管理Unity项目的正确打开方式 前言&#xff1a;对于刚开始git进行unity项目管理的时候&#xff0c;我采取的方式是全部文件上传&#xff0c;文件数量太多以及上传太大&#xff0c;我尝试过一下几个方法&#xff1a; 利用git的LFS大文件进行传方式&#xff0c;可行但比较麻…...

openssl使用哈希算法生成随机密钥

文章目录 一、openssl中随机数函数**OpenSSL 随机数函数概览**1. **核心随机数函数** **常用函数详解**1. RAND_bytes2. RAND_priv_bytes3. RAND_seed 和 RAND_add4. RAND_status **随机数生成器的熵池****常见用例****注意事项** 二、使用哈希算法生成随机的密钥 一、openssl中…...

将word里自带公式编辑器编辑的公式转换成用mathtype编辑的格式

文章目录 将word里自带公式编辑器编辑的公式转换成用mathtype编辑的格式MathType安装问题MathType30天试用延期MathPage.wll文件找不到问题 将word里自带公式编辑器编辑的公式转换成用mathtype编辑的格式 word自带公式编辑器编辑的公式格式&#xff1a; MathType编辑的格式&a…...

校园失物招领系统基于 SpringBoot:点亮校园归还遗失物之光

2系统开发环境 2.1vue技术 Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。 [5] 与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第…...

dhcpd服务器的配置与管理(超详细!!!)

前提条件&#xff1a; &#xff08;1&#xff09;虚拟机能够联网&#xff08;如果nat模式不能联网的看另一期&#xff09; CentOS7 NAT模式不能联网-CSDN博客 &#xff08;2&#xff09;系统是Centos8&#xff0c;因为下载的dhcp-server软件包版本和Centos7不匹配,如果你能成…...

Qml之基本控件

一.Qml常用控件 1.Text(显示普通文本和富文本) 1.1显示普通文本&#xff1a; Window { visible: true width: 320 height: 240 title: qsTr("Hello World") Text { text: "Hello World!" font.family: "Helvetica" font.pointSize: 24 color:…...

【Java从入门到放弃 之 Stream API】

Java Stream API Stream API行为参数化传递代码Lambda表达式Lambda 表达式的语法方法引用 Lambda 表达式的实际应用集合操作并发编程 Lambda 表达式的注意事项总结 Stream API Java8提供了一个全新的API - Stream。引入这个Stream的主要目的&#xff0c;一个是可以支持更好的并…...

Ruby On Rails 笔记1——Rails 入门

突然想跟着官方文档把Ruby On Rails过一遍&#xff0c;把一些有用的记下来就可以一直看了&#xff0c;do它! https://guides.rubyonrails.org/v7.2/ 注&#xff1a;官网是英文文档&#xff0c;我自己翻译了一下&#xff0c;不确保完全准确&#xff0c;只供自己学习开发使用。 …...

高效开发 Python Web 应用:FastAPI 数据验证与响应体设计

高效开发 Python Web 应用&#xff1a;FastAPI 数据验证与响应体设计 目录 &#x1f9d1;‍&#x1f4bb; FastAPI 的数据验证系统与 Pydantic 模型&#x1f4e6; 响应体与模型&#xff1a;定义响应数据的最佳实践&#x1f504; 响应模型与查询参数的结合&#xff1a;增强灵活…...

基于“开源 2+1 链动 O2O 商城小程序”的门店拉新策略与流程设计

摘要&#xff1a;在数字化商业浪潮席卷之下&#xff0c;实体门店面临着激烈的市场竞争&#xff0c;如何高效拉新成为关乎门店生存与发展的关键问题。本文聚焦于“开源 21 链动 O2O 商城小程序”&#xff0c;深入探讨结合多种手段的门店拉新策略及详细流程设计。通过剖析到店扫码…...

33.5 remote实战项目之设计prometheus数据源的结构

本节重点介绍 : 项目要求 通过remote read读取prometheus中的数据通过remote write向prometheus中写入数据 准备工作 新建项目 prome_remote_read_write设计prometheus 数据源的结构初始化 项目要求 通过remote read读取prometheus中的数据通过remote write向prometheus中写…...

微服务springboot详细解析(一)

目录 1.Spring概述 2.什么是SpringBoot&#xff1f; 3.第一个SpringBoot程序 4.配置参数优先级 5.springboot自动装配原理 6.SpringBootApplication&SpringApplication.run 7.ConfigurationProperties(prefix "") 8.Validated数据校验 29、聊聊该如何写一…...

深入探讨Go语言中的双向链表

简介 双向链表是链表家族中的一种高级结构&#xff0c;每个节点不仅指向下一个节点&#xff0c;还指向上一个节点。今天&#xff0c;我们将学习如何在Go语言中实现和操作这种灵活的数据结构。 双向链表的优缺点 优点&#xff1a; 可以从任一方向遍历链表&#xff0c;灵活性高…...

Fastapi + vue3 自动化测试平台---移动端App自动化篇

概述 好久写文章了&#xff0c;专注于新框架&#xff0c;新UI界面的实践&#xff0c;废话不多说&#xff0c;开搞 技术架构 后端&#xff1a; Fastapi Airtest multiprocessing 前端&#xff1a; 基于 Vue3、Vite、TypeScript、Pinia、Pinia持久化插件、Unocss 和 Elemen…...

ElasticSearch easy-es 聚合函数 group by 混合写法求Top N 词云 分词

1.将用户访问记录表数据同步到ES&#xff0c;并且分词&#xff0c;获取用户访问最多前十条词语。 Elasticsearch、Easy-es 快速入门 SearchAfterPage分页 若依前后端分离 Ruoyi-Vue SpringBoot 使用结巴分词器 <!-- 分词器--><dependency><groupId>com.hua…...

在 ASP.NET C# Web API 中实现 Serilog 以增强请求和响应的日志记录

介绍 日志记录是任何 Web 应用程序的关键方面。它有助于调试、性能监控和了解用户交互。在 ASP.NET C# 中&#xff0c;集成 Serilog 作为记录请求和响应&#xff08;包括传入和传出的数据&#xff09;的中间件可以显著提高 Web API 的可观察性和故障排除能力。 在过去的几周里&…...

2024年顶级小型语言模型前15名

本文&#xff0c;我们将深入了解2024年备受瞩目的十五款小型语言模型&#xff08;SLMs&#xff09;&#xff0c;它们分别是Llama 3.1 8B、Gemma2、Qwen 2、Mistral Nemo、Phi-3.5等。这些SLMs以其精巧的体积和高效率著称&#xff0c;它们不需要依赖庞大的服务器资源&#xff0c…...

精通 Python 网络安全(一)

前言 最近&#xff0c;Python 开始受到越来越多的关注&#xff0c;最新的 Python 更新添加了许多可用于执行关键任务的包。我们的主要目标是帮助您利用 Python 包来检测和利用漏洞&#xff0c;并解决网络挑战。 本书将首先带您了解与网络和安全相关的 Python 脚本和库。然后&…...

Lavalink插件开发从入门到精通:自定义音频源完整指南

Lavalink插件开发从入门到精通&#xff1a;自定义音频源完整指南 【免费下载链接】Lavalink Standalone audio sending node based on Lavaplayer. 项目地址: https://gitcode.com/gh_mirrors/la/Lavalink Lavalink是一个基于Lavaplayer的独立音频发送节点&#xff0c;通…...

从CRUD到AI大神:小白程序员5个月逆袭之路(收藏版)

本文分享了作者从传统CRUD工程师转型为AI应用工程师的心路历程。通过实战先行、深入学习、项目巩固三个阶段&#xff0c;作者逐步掌握了AI模型开发、部署和服务化能力&#xff0c;并成功开发了多个AI应用项目。文章强调实践导向的学习方法&#xff0c;建议程序员利用AI工具提升…...

使用Taotoken后Keil5项目代码审查效率的直观提升

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 使用Taotoken后Keil5项目代码审查效率的直观提升 1. 背景与需求 在嵌入式开发领域&#xff0c;代码审查是保证软件质量的关键环节…...

标准化封装国产电源:钡特电源 VB50-24S24LD 与金升阳 URB2424LD-50WR3 同属工业高可靠

在工业电子系统设计中&#xff0c;工业 DC-DC 电源模块作为核心供电单元&#xff0c;其标准化程度、稳定性及适配性直接影响设备整体可靠性与研发效率。钡特电源 VB50-24S24LD 与金升阳 URB2424LD-50WR3 作为 50W 级国产工业 DC-DC 代表产品&#xff0c;均采用国际标准封装引脚…...

py每日spider案例之netease搜索接口获取

import requestsheaders = {"accept": "application/json, text/plain, */*","accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7","cache-control": "no-cache",...

LeetCode 88:合并两个有序数组 | 双指针从后向前求解

LeetCode 88&#xff1a;合并两个有序数组 | 双指针从后向前求解 引言 合并两个有序数组&#xff08;Merge Sorted Array&#xff09;是 LeetCode 第 88 题&#xff0c;难度为 Easy&#xff0c;但却是双指针法应用的经典案例。题目要求将两个已排序的数组 nums1 和 nums2 合并…...

MySQL事务与锁机制深度解析

摘要&#xff1a;事务与锁是 MySQL 并发控制的两大基石。本文从 ACID 四大特性出发&#xff0c;深入讲解 InnoDB 的 MVCC 多版本并发控制机制、四种隔离级别下的并发问题、七种锁类型&#xff08;从表锁到行锁、间隙锁、Next-Key 锁&#xff09;&#xff0c;以及死锁的产生原因…...

【大模型12步学习路线 · 第12步 · ①原理篇】多模态 LLM + Multimodal RAG 全景:从 Qwen3-VL 到 ColPali / ColQwen2.5,让 LLM看懂Spec

【大模型12步学习路线 第12步 ①原理篇】多模态 LLM + Multimodal RAG 全景:从 Qwen3-VL 到 ColPali / ColQwen2.5,让 LLM"看懂"Spec 时序图 系列定位:「大模型正确学习顺序」12 步系列 第 12 步 多模态 的 ①原理篇 —— 最后一步,Veri-Copilot v1.0 大结局。 前…...

企业内训系统集成AI问答时采用Taotoken的成本控制实践

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 企业内训系统集成AI问答时采用Taotoken的成本控制实践 应用场景类&#xff0c;设想一个企业开发内训知识库系统的场景&#xff0c;…...

告别IBus!在Ubuntu 22.04上为Fcitx5安装搜狗输入法并设置自启动的完整流程

在Ubuntu 22.04上深度配置Fcitx5与搜狗输入法的现代输入方案 对于追求高效输入的Linux用户而言&#xff0c;输入法框架的选择往往决定了日常使用的流畅度体验。传统IBus框架虽然预装在大多数发行版中&#xff0c;但在中文输入场景下常显力不从心——词库更新滞后、云输入支持有…...