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()