88 个测试覆盖配置继承、注册表、GPU 守卫、YOLO 格式解析、数据集、模型形状、 标签模式、聚类原语与 SimCLR 损失,以及 train.py / eval.py / 聚类脚本的入口接线。 含两条回归测试: - PCA 白化放大噪声方向导致簇塌陷(默认 whiten=False 的依据) - classes 写成 data.yaml 路径时 num_classes 必须是类别数而非路径字符串长度 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
119 lines
3.9 KiB
Python
119 lines
3.9 KiB
Python
"""YOLO 标注格式的解析与写出。"""
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
pytest.importorskip("numpy")
|
|
|
|
import numpy as np # noqa: E402
|
|
|
|
from datasets.yolo import ( # noqa: E402
|
|
denormalize,
|
|
format_label_lines,
|
|
label_path_for,
|
|
list_images,
|
|
load_classes,
|
|
parse_label_file,
|
|
write_data_yaml,
|
|
)
|
|
|
|
|
|
def test_label_path_swaps_images_for_labels():
|
|
img_dir = Path("/data/film/images/train")
|
|
assert label_path_for(img_dir / "0001.png", img_dir) == Path("/data/film/labels/train/0001.txt")
|
|
|
|
|
|
def test_label_path_keeps_subdirs_and_honours_explicit_dir():
|
|
img_dir = Path("/data/film/images/train")
|
|
assert label_path_for(img_dir / "cam1/0001.png", img_dir) == Path(
|
|
"/data/film/labels/train/cam1/0001.txt"
|
|
)
|
|
assert label_path_for(img_dir / "0001.png", img_dir, Path("/tmp/lab")) == Path("/tmp/lab/0001.txt")
|
|
|
|
|
|
def test_parse_center_format(tmp_path):
|
|
path = tmp_path / "a.txt"
|
|
path.write_text("0 0.5 0.5 0.4 0.2\n# 注释\n\n1 0.25 0.25 0.1 0.1\n", encoding="utf-8")
|
|
|
|
boxes, labels = parse_label_file(path)
|
|
|
|
assert labels.tolist() == [0, 1]
|
|
assert boxes[0].tolist() == pytest.approx([0.3, 0.4, 0.7, 0.6])
|
|
assert denormalize(boxes, 100, 200)[0].tolist() == pytest.approx([30.0, 80.0, 70.0, 120.0])
|
|
|
|
|
|
def test_missing_file_means_no_defect(tmp_path):
|
|
boxes, labels = parse_label_file(tmp_path / "nope.txt")
|
|
assert boxes.shape == (0, 4) and labels.shape == (0,)
|
|
|
|
|
|
def test_polygon_rows_fall_back_to_bounding_box(tmp_path):
|
|
path = tmp_path / "a.txt"
|
|
path.write_text("2 0.1 0.1 0.4 0.2 0.3 0.5\n", encoding="utf-8")
|
|
|
|
boxes, labels = parse_label_file(path)
|
|
|
|
assert labels.tolist() == [2]
|
|
assert boxes[0].tolist() == pytest.approx([0.1, 0.1, 0.4, 0.5])
|
|
|
|
|
|
def test_coordinates_are_clamped_and_degenerate_rows_dropped(tmp_path):
|
|
path = tmp_path / "a.txt"
|
|
path.write_text("0 0.5 0.5 2.0 2.0\n0 0.5 0.5 0.0 0.0\n", encoding="utf-8")
|
|
|
|
boxes, labels = parse_label_file(path)
|
|
|
|
assert len(labels) == 1
|
|
assert boxes[0].tolist() == pytest.approx([0.0, 0.0, 1.0, 1.0])
|
|
|
|
|
|
def test_malformed_rows_report_file_and_line(tmp_path):
|
|
path = tmp_path / "a.txt"
|
|
path.write_text("0 0.5 0.5 0.4\n", encoding="utf-8")
|
|
with pytest.raises(ValueError, match=r"a\.txt:1"):
|
|
parse_label_file(path)
|
|
|
|
path.write_text("scratch 0.5 0.5 0.4 0.2\n", encoding="utf-8")
|
|
with pytest.raises(ValueError, match="不是合法的 YOLO 标注行"):
|
|
parse_label_file(path)
|
|
|
|
|
|
def test_class_id_range_is_checked(tmp_path):
|
|
path = tmp_path / "a.txt"
|
|
path.write_text("3 0.5 0.5 0.4 0.2\n", encoding="utf-8")
|
|
with pytest.raises(ValueError, match="越界"):
|
|
parse_label_file(path, num_classes=2)
|
|
|
|
|
|
def test_format_lines_roundtrips_pixel_boxes(tmp_path):
|
|
boxes = np.array([[10.0, 20.0, 50.0, 60.0]])
|
|
lines = format_label_lines(boxes, [1], width=100, height=200)
|
|
assert lines == ["1 0.300000 0.200000 0.400000 0.200000"]
|
|
|
|
path = tmp_path / "a.txt"
|
|
path.write_text("\n".join(lines), encoding="utf-8")
|
|
parsed, labels = parse_label_file(path)
|
|
assert labels.tolist() == [1]
|
|
assert denormalize(parsed, 100, 200)[0].tolist() == pytest.approx([10.0, 20.0, 50.0, 60.0])
|
|
|
|
|
|
def test_data_yaml_roundtrip(tmp_path):
|
|
pytest.importorskip("yaml")
|
|
path = write_data_yaml(tmp_path / "data.yaml", ["scratch", "bubble"], root=tmp_path)
|
|
assert load_classes(path) == ["scratch", "bubble"]
|
|
assert load_classes(["a", "b"]) == ["a", "b"]
|
|
|
|
|
|
def test_list_images_supports_dir_and_list_file(tmp_path):
|
|
images = tmp_path / "images"
|
|
images.mkdir()
|
|
for name in ("b.png", "a.jpg", "notes.txt"):
|
|
(images / name).write_bytes(b"")
|
|
|
|
assert [p.name for p in list_images(images)] == ["a.jpg", "b.png"]
|
|
|
|
list_file = tmp_path / "train.txt"
|
|
list_file.write_text("images/b.png\nimages/a.jpg\n", encoding="utf-8")
|
|
assert [p.name for p in list_images(images, list_file)] == ["b.png", "a.jpg"]
|