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

Ubuntu22.04 安装和使用标注工具labelImg

文章目录

  • 一、LabelImg 的安装及配置
    • 1. 安装
    • 2. 配置
  • 二、使用
    • 1. 基础操作介绍
    • 2. 创建自定义标签
      • 2.1 修改 predefined_classes.txt
      • 2.2 直接软件界面新增
    • 3. 图像标注
      • 3.1 重命名排序
      • 3.2 标注
      • 3.2 voc2yolo 格式转换

LabelImg 是一个用Python编写,并使用Qt作为其图形界面的图形图像注释工具。

一、LabelImg 的安装及配置

1. 安装

Python版本不同,安装方式也不同
1.1 Python 2 + Qt4

sudo apt-get install pyqt4-dev-tools
sudo pip install lxml
git clone https://github.com/tzutalin/labelImg.git
cd labelImg
make all						# 编译报错不用关注
python labelImg.py  #打开 labelImg
python labelImg.py [IMAGE_PATH] [PRE-DEFINED CLASS FILE]

1.2 Python 3 + Qt5

sudo apt-get install pyqt5-dev-tools
sudo pip3 install lxml
git clone https://github.com/tzutalin/labelImg.git
cd labelImg
make qt5py3
python3 labelImg.py  #打开 labelImg
python3 labelImg.py [IMAGE_PATH] [PRE-DEFINED CLASS FILE]

2. 配置

上述是使用命令行启动,如需图形界面触发,可参考如下方法:
2.1 新建 run_labelImg.sh

#!/bin/bash# <labelImg 软件包路径>/labelImg.py
python3 /home/gene/Documents/software/labelImg/labelImg.py

2.2 新建 labelImg.desktop

内容如下:

[Desktop Entry]
Version=1.0
Name=labelImg
Comment=labelImg
Exec=/home/gene/Documents/software/labelImg/run_labelImg.sh
Icon=/home/gene/Documents/software/labelImg/logo.png
StartupNotify=true
Terminal=false
Type=Application
Categories=Applications;

Exec 为上一步骤的 run_labelImg.sh
Icon 为软件打开后的 logo,可以找中意的图片代替(建议与软件相关);

2.3 图形化启动
拷贝到应用管理器使得系统识别。

sudo cp labelImg.desktop /usr/share/applications/

执行完上述操作,即可通过图形界面启动,或者图中搜索框搜索启动。

在这里插入图片描述

二、使用

1. 基础操作介绍

打开labelImg软件后,首先可以进行界面配置,将 view -> Auto Save modeview -> Display Labels 勾选,前者表示会自动保存(注意:最后一张需要手动保存), 后者表示标签可视化。
请添加图片描述
接着是界面左侧各操作按键的介绍:

  • Open:打开单张图片路径;
  • Open Dir :打开图片文件夹路径;
  • Change Save Dir : 标签保存路径;
  • Next Image: 下一张图片;
  • Prev Image:上一张图片;
  • Verify Image:可更改xml文件的内容;
  • Save:保存;
  • PascalVOC / Yolo / CreateML : 三种标签保存模式切换,推荐使用PascalVOC(方便可视化,yolo使用可以使用工具转换);

快捷键:

  • w :创建矩形框;
  • a :上一张图;
  • d :下一张图;
  • Ctrl+滚轮:放大与缩小;

2. 创建自定义标签

2.1 修改 predefined_classes.txt

一般来说 labelImg 会存在默认标签文件,保存在 labelImg/data/predefined_classes.txt 路径下,下面便是默认的标签:

dog
person
cat
tv
car
meatballs
marinara sauce
tomato soup
chicken noodle soup
french onion soup
chicken breast
ribs
pulled pork
hamburger
cavity

注意:每行一个标签,默认标签序号从上到下从0 开始。

现在修改文件内容,创建自己的预标签文件:

person
helmet
no_helmet

注意:修改 预标签文件后,记得重启 labelImg 软件才能识别。

在这里插入图片描述

2.2 直接软件界面新增

直接在软件里 创建矩形框,然后手动输入类别,如下图所示,添加新标签house
在这里插入图片描述

进行后续图片标注时可以看到,标签 house 依旧可以使用:

在这里插入图片描述

有了方便的方法二,为啥还要有方法一嘞
个人理解原因是:方法二并没有修改 predefined_classes.txt 文件,导致下次软件打开,方法二添加的预标签不存在,而标签需要顺序,方便后续管理。所以应该把常见的标签使用方法一进行永久保存,只有临时性的少数标签使用方法二标注。

3. 图像标注

3.1 重命名排序

首先,为方便管理需要保证数据集的文件名从1开始递增进行重命名,img_sort.sh 脚本 内容如下:

#!/bin/bash# 图片重命名排序脚本
# 用法: ./img_sort.sh /path/to/image/folder [padding_option] [start_number]
# padding_option: 0 - 使用动态前导零补齐 (默认)
#                 1 - 不使用前导零补齐
# start_number:   起始编号 (默认为1)# 检查是否提供了文件夹路径参数
if [ $# -eq 0 ]; thenecho "错误: 请提供文件夹路径作为参数"echo "用法: $0 /path/to/image/folder [padding_option] [start_number]"echo "padding_option: 0 - 使用动态前导零补齐 (默认)"echo "                1 - 不使用前导零补齐"echo "start_number:   起始编号 (默认为1)"exit 1
fi# 获取输入的文件夹路径
INPUT_DIR="$1"# 获取填充选项 (默认为0 - 使用动态前导零补齐)
PADDING_OPTION=${2:-0}# 获取起始编号 (默认为1)
START_NUMBER=${3:-1}# 验证填充选项
if [ "$PADDING_OPTION" != "0" ] && [ "$PADDING_OPTION" != "1" ]; thenecho "错误: 填充选项必须是 0 或 1"echo "0 - 使用动态前导零补齐"echo "1 - 不使用前导零补齐"exit 1
fi# 验证起始编号是否为数字
if ! [[ "$START_NUMBER" =~ ^[0-9]+$ ]]; thenecho "错误: 起始编号必须是正整数"exit 1
fi# 检查文件夹是否存在
if [ ! -d "$INPUT_DIR" ]; thenecho "错误: 文件夹 '$INPUT_DIR' 不存在"exit 1
fi# 创建输出文件夹路径 (在原文件夹名后添加 _sort)
OUTPUT_DIR="${INPUT_DIR%/}_sort"# 如果输出文件夹已存在,询问用户是否覆盖
if [ -d "$OUTPUT_DIR" ]; thenecho "警告: 输出文件夹 '$OUTPUT_DIR' 已存在"read -p "是否覆盖? (y/N): " -n 1 -rechoif [[ ! $REPLY =~ ^[Yy]$ ]]; thenecho "操作已取消"exit 1fi# 删除现有输出文件夹rm -rf "$OUTPUT_DIR"
fi# 创建输出文件夹
mkdir -p "$OUTPUT_DIR"# 检查文件夹是否创建成功
if [ ! -d "$OUTPUT_DIR" ]; thenecho "错误: 无法创建输出文件夹 '$OUTPUT_DIR'"exit 1
fi# 支持的图片扩展名
IMAGE_EXTS=("jpg" "jpeg" "png" "gif" "bmp" "tiff" "webp")# 首先统计图片数量
# echo "正在统计图片数量..."
IMAGE_COUNT=0
while IFS= read -r -d '' FILE; doEXTENSION=$(echo "${FILE##*.}" | tr '[:upper:]' '[:lower:]')if [[ " ${IMAGE_EXTS[@]} " =~ " ${EXTENSION} " ]]; thenIMAGE_COUNT=$((IMAGE_COUNT + 1))fi
done < <(find "$INPUT_DIR" -maxdepth 1 -type f -print0 2>/dev/null)if [ $IMAGE_COUNT -eq 0 ]; thenecho "在指定文件夹中没有找到图片文件"exit 1
fiecho "找到 $IMAGE_COUNT 张图片"# 计算需要的位数(基于图片总数和起始编号,仅当使用前导零时)
if [ "$PADDING_OPTION" = "0" ]; then# 计算最大可能的数字(起始编号 + 图片数量 - 1)MAX_NUMBER=$((START_NUMBER + IMAGE_COUNT - 1))PADDING_LENGTH=${#MAX_NUMBER}echo "使用动态前导零补齐,位数: $PADDING_LENGTH (最大编号: $MAX_NUMBER)"
elseecho "不使用前导零补齐"
fi# 计数器从起始编号开始
COUNT=$START_NUMBER# 处理所有图片文件
echo "开始处理图片..."
PROCESSED_COUNT=0
while IFS= read -r -d '' FILE; doEXTENSION=$(echo "${FILE##*.}" | tr '[:upper:]' '[:lower:]')# 检查是否为图片文件if [[ " ${IMAGE_EXTS[@]} " =~ " ${EXTENSION} " ]]; then# 构建新文件名if [ "$PADDING_OPTION" = "0" ]; then# 使用动态前导零FORMATTED_NUMBER=$(printf "%0${PADDING_LENGTH}d" "$COUNT")NEW_FILENAME="${FORMATTED_NUMBER}.${EXTENSION}"else# 不使用前导零NEW_FILENAME="${COUNT}.${EXTENSION}"fiNEW_PATH="$OUTPUT_DIR/$NEW_FILENAME"# 复制文件到新位置cp "$FILE" "$NEW_PATH"echo "已复制: $(basename "$FILE") -> $NEW_FILENAME"# 增加计数器COUNT=$((COUNT + 1))PROCESSED_COUNT=$((PROCESSED_COUNT + 1))fi
done < <(find "$INPUT_DIR" -maxdepth 1 -type f -print0 2>/dev/null | sort -z)echo "处理完成!"
echo "所有图片已重命名并保存到: $OUTPUT_DIR"
echo "共处理了 $PROCESSED_COUNT 张图片,从 $START_NUMBER 开始编号"

img_sort.sh 脚本 使用命令

# 默认 重命名用0补齐
./img_sort.sh ./helmet_test/image/# 显示 重命名用0补齐
./img_sort.sh ./helmet_test/image/ 0# 显示 重命名不用0补齐
./img_sort.sh ./helmet_test/image/ 1# 显示 重命名用0补齐,且从数字29开始递增命名
./img_sort.sh ./helmet_test/image/ 0  29

3.2 标注

标注过程如下:
① 建议在 数据集同目录创建 annotations文件夹,用于保存标签;
② 打开 labelImg软件,配置如下:
  a) 添加数据集文件夹目录;b) 添加标签保存目录; c) 设置标签格式为 PascalVOC 模式。
③ 标注图像,标注1张,自动保存标签到 annotations文件夹,如下所示:

在这里插入图片描述
内容格式如下:

<annotation><folder>image_sort</folder><filename>01.jpeg</filename><path>/home/gene/project/helmet_test/image_sort/01.jpeg</path><source><database>Unknown</database></source><size><width>265</width><height>180</height><depth>3</depth></size><segmented>0</segmented><object><name>person</name><pose>Unspecified</pose><truncated>1</truncated><difficult>0</difficult><bndbox><xmin>79</xmin><ymin>1</ymin><xmax>206</xmax><ymax>179</ymax></bndbox></object>
</annotation>

3.2 voc2yolo 格式转换

若需将 PascalVOC 格式转成 yolo 格式,则新建 voc2yolo.py 文件,执行如下指令进行转换:

# 单参数: 待转换voc标签文件夹路径
# 默认类别:参考 DEFAULT_CLASSES = ['Ball', 'Post', 'L', 'T', 'X']
python3 voc2yolo.py ./Labels# 双参数: 待转换voc标签文件夹路径  标签路径
python3 voc2yolo.py ./Labels /home/gene/Documents/software/labelImg/data/predefined_classes.txt

voc2yolo.py 文件内容如下:

#!/usr/bin/env python3
"""
Pascal VOC 转 YOLO 格式转换脚本
用法: python3 voc2yolo.py [输入文件夹] [类别文件(可选)] [输出文件夹(可选)]
示例: python3 voc2yolo.py ./Labels  # 使用默认类别python3 voc2yolo.py ./Labels /path/to/predefined_classes.txt  # 使用指定类别文件
"""import os
import sys
import xml.etree.ElementTree as ET
from pathlib import Path# 默认类别列表
DEFAULT_CLASSES = ['Ball', 'Post', 'L', 'T', 'X']def parse_args():"""解析命令行参数"""if len(sys.argv) < 2:print("错误: 参数不足")print("用法: python3 voc2yolo.py [输入文件夹] [类别文件(可选)] [输出文件夹(可选)]")print("示例: python3 voc2yolo.py ./Labels  # 使用默认类别")print("示例: python3 voc2yolo.py ./Labels /path/to/predefined_classes.txt")sys.exit(1)input_dir = sys.argv[1]# 处理可选参数class_file = Noneoutput_dir = os.path.join(os.path.dirname(input_dir), "Labels_yolo")if len(sys.argv) > 2:# 检查第二个参数是类别文件还是输出目录if os.path.isfile(sys.argv[2]):class_file = sys.argv[2]if len(sys.argv) > 3:output_dir = sys.argv[3]else:# 第二个参数可能是输出目录output_dir = sys.argv[2]return input_dir, class_file, output_dirdef load_classes(class_file):"""从文件加载类别列表,如果没有提供文件则使用默认类别"""if class_file is None:print("使用默认类别:", DEFAULT_CLASSES)return DEFAULT_CLASSESclasses = []try:with open(class_file, 'r') as f:for line in f:class_name = line.strip()if class_name:  # 跳过空行classes.append(class_name)except FileNotFoundError:print(f"警告: 类别文件 '{class_file}' 不存在,使用默认类别")return DEFAULT_CLASSESexcept Exception as e:print(f"读取类别文件时出错: {e},使用默认类别")return DEFAULT_CLASSESif not classes:print("警告: 类别文件为空,使用默认类别")return DEFAULT_CLASSESprint(f"从文件加载了 {len(classes)} 个类别: {', '.join(classes)}")return classesdef convert_voc_to_yolo(xml_file, classes, output_dir):"""转换单个VOC XML文件到YOLO格式"""try:tree = ET.parse(xml_file)root = tree.getroot()except Exception as e:print(f"解析XML文件 {xml_file} 时出错: {e}")return# 获取图像尺寸size = root.find('size')if size is None:print(f"警告: {xml_file} 中没有找到尺寸信息,跳过此文件")returnwidth = int(size.find('width').text)height = int(size.find('height').text)if width <= 0 or height <= 0:print(f"警告: {xml_file} 中的图像尺寸无效,跳过此文件")return# 准备输出文件xml_name = Path(xml_file).stemoutput_file = os.path.join(output_dir, f"{xml_name}.txt")yolo_annotations = []# 处理每个对象for obj in root.findall('object'):# 获取类别class_name = obj.find('name').textif class_name not in classes:print(f"警告: 在文件 {xml_file} 中发现未定义类别 '{class_name}',跳过此对象")continueclass_id = classes.index(class_name)# 获取边界框bbox = obj.find('bndbox')if bbox is None:print(f"警告: 在文件 {xml_file} 中发现没有边界框的对象,跳过")continuexmin = float(bbox.find('xmin').text)ymin = float(bbox.find('ymin').text)xmax = float(bbox.find('xmax').text)ymax = float(bbox.find('ymax').text)# 转换为YOLO格式 (中心点x, 中心点y, 宽度, 高度)x_center = (xmin + xmax) / 2.0 / widthy_center = (ymin + ymax) / 2.0 / heightw = (xmax - xmin) / widthh = (ymax - ymin) / height# 确保值在0-1范围内x_center = max(0, min(1, x_center))y_center = max(0, min(1, y_center))w = max(0, min(1, w))h = max(0, min(1, h))yolo_annotations.append(f"{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}")# 写入输出文件try:with open(output_file, 'w') as f:for annotation in yolo_annotations:f.write(annotation + '\n')except Exception as e:print(f"写入文件 {output_file} 时出错: {e}")def main():"""主函数"""input_dir, class_file, output_dir = parse_args()# 检查输入目录if not os.path.isdir(input_dir):print(f"错误: 输入目录 '{input_dir}' 不存在")sys.exit(1)# 加载类别classes = load_classes(class_file)# 创建输出目录os.makedirs(output_dir, exist_ok=True)print(f"输出目录: {output_dir}")# 处理所有XML文件xml_files = [f for f in os.listdir(input_dir) if f.endswith('.xml')]print(f"找到 {len(xml_files)} 个XML文件")for i, xml_file in enumerate(xml_files):xml_path = os.path.join(input_dir, xml_file)print(f"处理文件 {i+1}/{len(xml_files)}: {xml_file}")convert_voc_to_yolo(xml_path, classes, output_dir)print("转换完成!")if __name__ == "__main__":main()

待续 。。。

http://www.dtcms.com/a/351664.html

相关文章:

  • GZ-CTF平台pwn题目部署
  • GitHub 热榜项目 - 日榜(2025-08-26)
  • word批量修改交叉引用颜色
  • 【RAGFlow代码详解-28】部署和基础设施
  • 国标28181 国标视频平台
  • 四、Python 脚本常用模块(续)
  • Linux虚拟机ansible部署
  • 机器视觉学习-day04-形态学变换
  • Spring Boot 与传统 Spring:从 WAR 到可执行 JAR,颠覆性的部署哲学
  • MEMS陀螺定向短节与传统陀螺工具的区别?
  • 永磁同步电机无速度算法--传统脉振方波注入法(1)
  • 图片生成视频软件深度评测:浅谈视频音频提取技术
  • Boris FX Samplitude Suite 2025.0.0 音频录制/编辑和母带处理
  • 不增加 GPU,首 Token 延迟下降 50%|LLM 服务负载均衡的新实践
  • 如何基于阿里云OpenSearch LLM搭建智能客服平台
  • ssc37x平台的音频应用demo
  • 160.在 Vue3 中用 OpenLayers 解决国内 OpenStreetMap 地图加载不出来的问题
  • Mamba-HoME:面向3D医学影像分割的层次化专家混合新框架
  • 蓝思科技半年净利超11亿,蓝思成绩单怎么分析?
  • 为什么选择爱普生TG5032CFN温补晶振—可穿戴设备?
  • Pycharm
  • 前端漏洞(上)- JSONHijacking 漏洞
  • LangGraph-2-Demo
  • 基于goofys挂载s3到ubuntu, 没用s3fs因为它兼容性不太好
  • Stream流中的Map与flatMap的区别
  • 《从裸机到 GPU 共享:一步一步在 Kubernetes 上部署 NVIDIA GPU Operator + KAI Scheduler》
  • Python训练营打卡Day44-通道注意力(SE注意力)
  • 数字IC前端设计——前仿篇(VCS,DVE,Verdi)
  • 【Redis 进阶】-----哨兵 Sentinel(重点理解流程和原理)
  • 构建智能提示词工程师:LangGraph 的自动化提示词生成流程