agrobot_base/Python/yolov8-seg/convert_labels.py

108 lines
2.4 KiB
Python

from pathlib import Path
import math
EPS_DIST = 1e-4 # distância mínima entre pontos
MIN_AREA = 1e-6 # área mínima do polígono
def clamp(v: float) -> float:
# remove -0.0 e garante [0,1]
v = 0.0 if abs(v) < 1e-12 else v
return max(0.0, min(1.0, v))
def polygon_area(pts):
# fórmula do polígono (shoelace)
area = 0.0
for i in range(len(pts)):
x1, y1 = pts[i]
x2, y2 = pts[(i + 1) % len(pts)]
area += x1 * y2 - x2 * y1
return abs(area) * 0.5
def dist(a, b):
return math.hypot(a[0] - b[0], a[1] - b[1])
def dedupe_and_simplify(pts):
# remove pontos duplicados / muito próximos
clean = []
for p in pts:
if not clean or dist(p, clean[-1]) > EPS_DIST:
clean.append(p)
# fecha polígono se necessário
if len(clean) > 2 and dist(clean[0], clean[-1]) < EPS_DIST:
clean.pop()
return clean
def convert_label_file(path: Path):
text = path.read_text().strip()
if not text:
print(f"[WARN] {path.name} vazio, pulando.")
return
new_lines = []
for line in text.splitlines():
parts = line.strip().split()
if len(parts) < 7:
continue # menos que 3 pontos
cls_id = int(float(parts[0]))
nums = list(map(float, parts[1:]))
if len(nums) % 2 != 0:
continue
pts = []
for x, y in zip(nums[0::2], nums[1::2]):
pts.append((clamp(x), clamp(y)))
pts = dedupe_and_simplify(pts)
if len(pts) < 3:
continue
area = polygon_area(pts)
if area < MIN_AREA:
continue
flat = []
for x, y in pts:
flat.append(f"{x:.6f}")
flat.append(f"{y:.6f}")
new_lines.append(" ".join([str(cls_id)] + flat))
if not new_lines:
print(f"[WARN] {path.name} ficou sem polígonos válidos.")
return
# backup
backup = path.with_suffix(path.suffix + ".bak")
if not backup.exists():
backup.write_text(text)
path.write_text("\n".join(new_lines) + "\n")
print(f"[OK] Corrigido: {path.name}")
def main():
root = Path(".").resolve()
for sub in ["labels/train", "labels/val"]:
d = root / sub
if not d.exists():
continue
for txt in sorted(d.glob("*.txt")):
convert_label_file(txt)
if __name__ == "__main__":
main()