添加状态分类和液面分割

This commit is contained in:
琉璃月光
2025-09-01 14:14:18 +08:00
parent 6e553f6a20
commit ad52ab9125
2379 changed files with 102501 additions and 1465 deletions

109
yolo11_point/check.py Normal file
View File

@ -0,0 +1,109 @@
import os
import glob
def is_point_in_bbox(px, py, x_center, y_center, w, h, tolerance=1e-3):
"""
判断归一化坐标下的点 (px, py) 是否在 bbox 范围内
允许微小越界(如浮点误差)
"""
x_min = x_center - w / 2 - tolerance
x_max = x_center + w / 2 + tolerance
y_min = y_center - h / 2 - tolerance
y_max = y_center + h / 2 + tolerance
return x_min <= px <= x_max and y_min <= py <= y_max
def check_txt_file(txt_path, img_dir, delete=True):
"""
检查一个 .txt 文件:
- 每行1 个目标1 个 bbox + 3 个关键点)
- 每个关键点格式:(x, y, v) → v 是可见性,我们只关心 x, y
- 如果任意 1 个关键点超出其 bbox → 标记为无效,删除 txt + jpg
"""
try:
with open(txt_path, 'r') as f:
lines = f.readlines()
except Exception as e:
print(f"⚠️ 无法读取文件 {txt_path}: {e}")
return False
for line_num, line in enumerate(lines):
parts = line.strip().split()
if not parts or len(parts) < 5:
continue # 跳过空行或无效行
try:
# 解析 bbox (归一化)
class_id = int(parts[0])
x_c = float(parts[1])
y_c = float(parts[2])
w = float(parts[3])
h = float(parts[4])
# 检查 bbox 合法性
if not (0 <= x_c <= 1 and 0 <= y_c <= 1 and 0 < w <= 1 and 0 < h <= 1):
print(f"❌ 行 {line_num+1}: bbox 值非法")
if delete:
return True # 触发删除
# 提取 3 个关键点 (每 3 个值一组: x, y, v)
keypoints = parts[5:]
if len(keypoints) != 9:
print(f"❌ 行 {line_num+1}: 关键点数量不为 3 (实际: {len(keypoints)//3})")
if delete:
return True
for i in range(3):
kx = float(keypoints[i*3])
ky = float(keypoints[i*3 + 1])
# kv = int(keypoints[i*3 + 2]) # v 可见性,此处不用
if not is_point_in_bbox(kx, ky, x_c, y_c, w, h):
print(f"🚨 行 {line_num+1}: 关键点 {i+1} ({kx:.4f}, {ky:.4f}) 超出 bbox [{x_c-w/2:.4f}, {x_c+w/2:.4f}] x [{y_c-h/2:.4f}, {y_c+h/2:.4f}]")
if delete:
return True # 只要有一个越界,立即决定删除
except Exception as e:
print(f"❌ 行 {line_num+1} 解析出错: {e}")
if delete:
return True
return False # 无需删除
def clean_invalid_files(labels_dir, images_dir, delete=True):
"""
遍历 labels_dir 下所有 .txt 文件,检查并删除异常文件
"""
txt_files = glob.glob(os.path.join(labels_dir, "*.txt"))
deleted_count = 0
print(f"🔍 开始检查 {len(txt_files)} 个标注文件...")
for txt_file in txt_files:
base_name = os.path.splitext(os.path.basename(txt_file))[0]
img_file = os.path.join(images_dir, base_name + ".jpg")
should_delete = check_txt_file(txt_file, images_dir, delete=delete)
if should_delete:
print(f"💥 异常文件: {base_name}.txt")
try:
if os.path.exists(txt_file):
os.remove(txt_file)
print(f"🗑️ 已删除: {txt_file}")
if os.path.exists(img_file):
os.remove(img_file)
print(f"🗑️ 已删除: {img_file}")
deleted_count += 1
except Exception as e:
print(f"❌ 删除文件时出错: {e}")
print(f"\n✅ 检查完成!共删除 {deleted_count} 组异常文件 (txt + jpg)")
# =============== 用户配置区 ===============
LABELS_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/yolodataset/point1/labels_keypoints"
IMAGES_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/yolodataset/point1/f11" # 请确保这是图片所在目录
DELETE_FILES = True # 设为 False 可先预览哪些文件会被删
# ========================================
if __name__ == "__main__":
clean_invalid_files(LABELS_DIR, IMAGES_DIR, delete=DELETE_FILES)

112
yolo11_point/check_point.py Normal file
View File

@ -0,0 +1,112 @@
import os
import glob
def validate_and_delete(labels_dir, images_dir, delete=True):
"""
检查 labels_dir 中所有 .txt 文件:
- 每行必须是 14 个字段1+4+9
- 坐标在 [0,1] 范围内
- 关键点 v 值在 {0,1,2}
- 任意错误 → 删除 txt + jpg
"""
txt_files = sorted(glob.glob(os.path.join(labels_dir, "*.txt")))
print(f"🔍 开始检查 {len(txt_files)} 个标注文件...\n")
deleted_count = 0
for txt_file in txt_files:
base_name = os.path.splitext(os.path.basename(txt_file))[0]
img_file = os.path.join(images_dir, base_name + ".jpg")
invalid = False # 标记是否异常
try:
with open(txt_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
except Exception as e:
print(f"❌ 无法读取文件: {txt_file} | 错误: {e}")
invalid = True
else:
if not lines:
print(f"❌ 文件为空: {txt_file}")
invalid = True
else:
for line_idx, line in enumerate(lines):
parts = line.strip().split()
if len(parts) == 0:
continue # 跳过空行
# 检查字段数量
if len(parts) != 14:
print(f"🚨 {base_name}.txt | 第 {line_idx+1} 行: 字段数错误 ({len(parts)} != 14)")
invalid = True
break
try:
values = [float(x) for x in parts]
# 检查 NaN
if any(x != x for x in values):
print(f"🚨 {base_name}.txt | 第 {line_idx+1} 行: 包含 NaN")
invalid = True
break
# 检查 class_id
cls = int(values[0])
if cls < 0:
print(f"🚨 {base_name}.txt | 第 {line_idx+1} 行: class_id {cls} < 0")
invalid = True
break
# 检查 bbox
x_c, y_c, w, h = values[1:5]
if not (0 <= x_c <= 1 and 0 <= y_c <= 1 and 0 < w <= 1 and 0 < h <= 1):
print(f"🚨 {base_name}.txt | 第 {line_idx+1} 行: bbox 越界 (x_c={x_c:.4f}, y_c={y_c:.4f}, w={w:.4f}, h={h:.4f})")
invalid = True
break
# 检查 3 个关键点
kpts = values[5:]
for i in range(3):
kx = kpts[i*3]
ky = kpts[i*3+1]
v = kpts[i*3+2]
if not (0 <= kx <= 1 and 0 <= ky <= 1):
print(f"🚨 {base_name}.txt | 第 {line_idx+1} 行: 关键点 {i+1} 坐标越界 (kx={kx:.4f}, ky={ky:.4f})")
invalid = True
break
if v not in (0, 1, 2):
print(f"🚨 {base_name}.txt | 第 {line_idx+1} 行: 关键点 {i+1} v={v},应为 0/1/2")
invalid = True
break
if invalid:
break # 跳出关键点循环
except ValueError as e:
print(f"🚨 {base_name}.txt | 第 {line_idx+1} 行: 数据格式错误 -> {parts}")
invalid = True
break
# === 执行删除 ===
if invalid:
try:
if os.path.exists(txt_file):
os.remove(txt_file)
print(f"🗑️ 已删除: {txt_file}")
if os.path.exists(img_file):
os.remove(img_file)
print(f"🗑️ 已删除: {img_file}")
deleted_count += 1
print(f"💥 已清理: {base_name}\n")
except Exception as e:
print(f"❌ 删除文件失败 {txt_file}{img_file}: {e}\n")
print(f"\n✅ 检查完成!共删除 {deleted_count} 组异常文件 (txt + jpg)")
# =============== 用户配置区 ===============
LABELS_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/yolodataset/point1/labels_keypoints"
IMAGES_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/yolodataset/point1/f11"
DELETE_FILES = True # 设为 False 可预览哪些文件会被删
# ✅ 必须添加这一行才能运行!
validate_and_delete(LABELS_DIR, IMAGES_DIR, delete=DELETE_FILES)
# ========================================

View File

@ -47,7 +47,7 @@ def delete_json_files(folder_path, recursive=True, confirm=True):
print(f"\n🎉 删除完成!共删除 {deleted_count} 个 .json 文件。")
# ================== 用户配置区 ==================
FOLDER_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/yolodataset/point/folder_end" # ✅ 修改为你要删除 JSON 文件的文件夹路径
FOLDER_PATH = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/yolodataset/point1/f11" # ✅ 修改为你要删除 JSON 文件的文件夹路径
RECURSIVE = True # True: 删除所有子文件夹中的 .jsonFalse: 只删除当前文件夹
CONFIRM_BEFORE_DELETE = True # 是否每次删除前确认(推荐开启)

View File

@ -152,12 +152,12 @@ def labelme_to_yolo_keypoints_batch(json_dir, output_dir, target_box_label="J1",
# ================== 用户配置区 ==================
JSON_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/3/folder_end"
OUTPUT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/3/labels_keypoints"
JSON_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/yolodataset/point1/f11"
OUTPUT_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/yolodataset/point1/labels_keypoints"
TARGET_BOX_LABEL = "J1"
CLASS_ID = 0
CLASS_ID = 1
IMG_SHAPE = (1440, 2506) # (height, width)
KEYPOINTS_PER_INSTANCE = 4
KEYPOINTS_PER_INSTANCE = 3
# ================== 执行转换 ==================
if __name__ == "__main__":

21
yolo11_point/trains.py Normal file
View File

@ -0,0 +1,21 @@
import os
labels_dir = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/seg/dataset2/labels"
for txt_file in os.listdir(labels_dir):
if not txt_file.endswith(".txt"):
continue
path = os.path.join(labels_dir, txt_file)
lines = open(path, "r").readlines()
new_lines = []
for line in lines:
parts = line.strip().split()
if not parts:
continue
# 修改类别 ID1 -> 0
if parts[0] == "1":
parts[0] = "0"
new_lines.append(" ".join(parts))
open(path, "w").write("\n".join(new_lines))
print(f"✅ 已处理: {txt_file}")