108 lines
2.4 KiB
Python
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()
|