Image Segmentation
PyTorch
English
painting-vision-robotics-kit
semantic-segmentation
robotics
edge-ai
construction-ai
autonomous-painting
wall-painting-robot
paint-coverage-estimation
building-facade
drywall
skirting-detection
window-detection
lidar
depth-validation
segformer
deeplabv3
Eval Results (legacy)
Upload robot_planner.py with huggingface_hub
Browse files- robot_planner.py +37 -3
robot_planner.py
CHANGED
|
@@ -30,6 +30,7 @@ Example::
|
|
| 30 |
from __future__ import annotations
|
| 31 |
|
| 32 |
import argparse
|
|
|
|
| 33 |
import json
|
| 34 |
from pathlib import Path
|
| 35 |
|
|
@@ -303,14 +304,16 @@ def count_passes(segments, orientation):
|
|
| 303 |
|
| 304 |
|
| 305 |
def build_plan(paintable, keepout, stroke_width, overlap, margin, orientation, corners, mm_per_unit,
|
| 306 |
-
boundary_tolerance, trim_width=None, trim_overlap=None, already_painted=None):
|
|
|
|
|
|
|
| 307 |
height, width = paintable.shape
|
| 308 |
-
plane_size, to_plane, to_image = (width, height), None, None
|
| 309 |
if corners is not None:
|
| 310 |
corners = np.asarray(corners, float)
|
| 311 |
edge_w = max(np.linalg.norm(corners[1] - corners[0]), np.linalg.norm(corners[2] - corners[3]))
|
| 312 |
edge_h = max(np.linalg.norm(corners[3] - corners[0]), np.linalg.norm(corners[2] - corners[1]))
|
| 313 |
-
plane_size = (max(1, int(round(edge_w))), max(1, int(round(edge_h))))
|
| 314 |
to_plane = homography(corners, [(0, 0), (plane_size[0], 0), (plane_size[0], plane_size[1]), (0, plane_size[1])])
|
| 315 |
to_image = np.linalg.inv(to_plane)
|
| 316 |
paintable = warp_mask(paintable, to_plane, plane_size)
|
|
@@ -484,6 +487,11 @@ def main():
|
|
| 484 |
source.add_argument("--paintable", type=Path, help="binary paintable-wall mask PNG from predict.py")
|
| 485 |
source.add_argument("--mask", type=Path, help="semantic mask PNG; paintable/keep-out are derived from it")
|
| 486 |
parser.add_argument("--keepout", type=Path, help="binary keep-out mask PNG (used with --paintable)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
parser.add_argument("--corners", type=corners_arg,
|
| 488 |
help='four wall corners "TL TR BR BL" as "x,y x,y x,y x,y"')
|
| 489 |
parser.add_argument("--stroke-width", type=float, default=230.0, help="roller/pass width in plane units")
|
|
@@ -522,6 +530,31 @@ def main():
|
|
| 522 |
paintable = np.isin(semantic, WALL_CLASSES)
|
| 523 |
keepout = np.isin(semantic, KEEPOUT_CLASSES)
|
| 524 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 525 |
already = None
|
| 526 |
if args.state:
|
| 527 |
loaded = load_state(args.state)
|
|
@@ -539,6 +572,7 @@ def main():
|
|
| 539 |
|
| 540 |
plan, debug = build_plan(paintable, keepout, args.stroke_width, args.overlap, args.margin,
|
| 541 |
args.orientation, args.corners, args.mm_per_unit, args.boundary_tolerance,
|
|
|
|
| 542 |
trim_width=args.trim_width, trim_overlap=args.trim_overlap, already_painted=already)
|
| 543 |
rendered = json.dumps(plan, indent=2)
|
| 544 |
print(rendered)
|
|
|
|
| 30 |
from __future__ import annotations
|
| 31 |
|
| 32 |
import argparse
|
| 33 |
+
import sys
|
| 34 |
import json
|
| 35 |
from pathlib import Path
|
| 36 |
|
|
|
|
| 304 |
|
| 305 |
|
| 306 |
def build_plan(paintable, keepout, stroke_width, overlap, margin, orientation, corners, mm_per_unit,
|
| 307 |
+
boundary_tolerance, trim_width=None, trim_overlap=None, already_painted=None, plane_size=None):
|
| 308 |
+
"""``plane_size`` (units wide, units high) sets the true wall-plane aspect; without it the
|
| 309 |
+
longest image edges are used, which is only right for a near-frontal view."""
|
| 310 |
height, width = paintable.shape
|
| 311 |
+
given_size, plane_size, to_plane, to_image = plane_size, (width, height), None, None
|
| 312 |
if corners is not None:
|
| 313 |
corners = np.asarray(corners, float)
|
| 314 |
edge_w = max(np.linalg.norm(corners[1] - corners[0]), np.linalg.norm(corners[2] - corners[3]))
|
| 315 |
edge_h = max(np.linalg.norm(corners[3] - corners[0]), np.linalg.norm(corners[2] - corners[1]))
|
| 316 |
+
plane_size = given_size or (max(1, int(round(edge_w))), max(1, int(round(edge_h))))
|
| 317 |
to_plane = homography(corners, [(0, 0), (plane_size[0], 0), (plane_size[0], plane_size[1]), (0, plane_size[1])])
|
| 318 |
to_image = np.linalg.inv(to_plane)
|
| 319 |
paintable = warp_mask(paintable, to_plane, plane_size)
|
|
|
|
| 487 |
source.add_argument("--paintable", type=Path, help="binary paintable-wall mask PNG from predict.py")
|
| 488 |
source.add_argument("--mask", type=Path, help="semantic mask PNG; paintable/keep-out are derived from it")
|
| 489 |
parser.add_argument("--keepout", type=Path, help="binary keep-out mask PNG (used with --paintable)")
|
| 490 |
+
parser.add_argument("--measurements", type=Path,
|
| 491 |
+
help="predict.py --json output: plan inside a measured face's paint region at true size; "
|
| 492 |
+
"plane units are then --unit-mm millimetres and widths/margins are in mm")
|
| 493 |
+
parser.add_argument("--face", type=int, default=0, help="which measured face to plan (with --measurements)")
|
| 494 |
+
parser.add_argument("--unit-mm", type=float, default=10.0, help="plane resolution with --measurements")
|
| 495 |
parser.add_argument("--corners", type=corners_arg,
|
| 496 |
help='four wall corners "TL TR BR BL" as "x,y x,y x,y x,y"')
|
| 497 |
parser.add_argument("--stroke-width", type=float, default=230.0, help="roller/pass width in plane units")
|
|
|
|
| 530 |
paintable = np.isin(semantic, WALL_CLASSES)
|
| 531 |
keepout = np.isin(semantic, KEEPOUT_CLASSES)
|
| 532 |
|
| 533 |
+
plane_size = None
|
| 534 |
+
if args.measurements:
|
| 535 |
+
data = json.loads(args.measurements.read_text(encoding="utf-8"))
|
| 536 |
+
faces = (data.get("measurements") or data).get("faces") or []
|
| 537 |
+
if not 0 <= args.face < len(faces):
|
| 538 |
+
parser.error(f"{args.measurements} has {len(faces)} measured face(s); --face {args.face} is not one")
|
| 539 |
+
face = faces[args.face]
|
| 540 |
+
open_ends = [side for side, kind in face["ends"].items() if kind == "frame"]
|
| 541 |
+
if open_ends:
|
| 542 |
+
print(f"warning: the wall continues past the image on the {' and '.join(open_ends)}; "
|
| 543 |
+
f"this plan covers only the visible part and has no stop there", file=sys.stderr)
|
| 544 |
+
doubtful = [k for k, v in face.get("stop_line_trust", {}).items() if v == "conflict"]
|
| 545 |
+
if doubtful:
|
| 546 |
+
print(f"warning: {', '.join(doubtful)} stop line(s) disagree with the segmentation; verify before painting",
|
| 547 |
+
file=sys.stderr)
|
| 548 |
+
# Plan in true wall-plane millimetres: ceiling line to top of skirting, end to end.
|
| 549 |
+
args.corners = [tuple(p) for p in face["paint_region_corners_px"]]
|
| 550 |
+
args.mm_per_unit = args.unit_mm
|
| 551 |
+
plane_size = (max(1, round(face["width_ft"] * 304.8 / args.unit_mm)),
|
| 552 |
+
max(1, round(face["paint_height_ft"] * 304.8 / args.unit_mm)))
|
| 553 |
+
args.stroke_width /= args.unit_mm
|
| 554 |
+
args.margin /= args.unit_mm
|
| 555 |
+
if args.trim_width:
|
| 556 |
+
args.trim_width /= args.unit_mm
|
| 557 |
+
|
| 558 |
already = None
|
| 559 |
if args.state:
|
| 560 |
loaded = load_state(args.state)
|
|
|
|
| 572 |
|
| 573 |
plan, debug = build_plan(paintable, keepout, args.stroke_width, args.overlap, args.margin,
|
| 574 |
args.orientation, args.corners, args.mm_per_unit, args.boundary_tolerance,
|
| 575 |
+
plane_size=plane_size,
|
| 576 |
trim_width=args.trim_width, trim_overlap=args.trim_overlap, already_painted=already)
|
| 577 |
rendered = json.dumps(plan, indent=2)
|
| 578 |
print(rendered)
|