ammariii08 commited on
Commit
c427817
·
verified ·
1 Parent(s): 39e7a09

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -33
app.py CHANGED
@@ -20,6 +20,7 @@ from transformers import AutoModelForImageSegmentation
20
  from torchvision import transforms
21
  from scalingtestupdated import calculate_scaling_factor
22
  from shapely.geometry import Polygon, Point, MultiPolygon, GeometryCollection
 
23
  from scipy.interpolate import splprep, splev
24
  from scipy.ndimage import gaussian_filter1d
25
  from u2net import U2NETP
@@ -260,60 +261,119 @@ def extract_outlines(binary_image: np.ndarray) -> (np.ndarray, list):
260
  # ---------------------
261
  # Functions for Finger Cut Clearance
262
  # ---------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  def union_tool_and_circle(tool_polygon: Polygon, center_inch, circle_diameter=1.0):
264
- radius = circle_diameter / 2.0
265
- circle_poly = Point(center_inch).buffer(radius, resolution=64)
266
- union_poly = tool_polygon.union(circle_poly)
267
- return union_poly
 
 
 
 
 
 
 
 
 
268
 
269
  def build_tool_polygon(points_inch):
270
- return Polygon(points_inch)
 
 
 
 
 
 
 
271
 
272
  def polygon_to_exterior_coords(poly: Polygon):
273
- if poly.geom_type == "GeometryCollection":
 
274
  return []
 
275
  if poly.geom_type == "MultiPolygon":
276
  biggest = max(poly.geoms, key=lambda g: g.area)
277
  poly = biggest
278
- if not poly.exterior:
 
279
  return []
 
280
  return list(poly.exterior.coords)
281
 
282
  def place_finger_cut_randomly(tool_polygon, points_inch, existing_centers, all_polygons, circle_diameter=1.0, min_gap=0.25, max_attempts=30):
283
  import random
284
  needed_center_distance = circle_diameter + min_gap
285
  radius = circle_diameter / 2.0
 
 
 
 
 
286
  for _ in range(max_attempts):
287
  idx = random.randint(0, len(points_inch) - 1)
288
  cx, cy = points_inch[idx]
289
- too_close = False
290
- for (ex_x, ex_y) in existing_centers:
291
- if np.hypot(cx - ex_x, cy - ex_y) < needed_center_distance:
292
- too_close = True
293
- break
294
  if too_close:
295
  continue
296
- circle_poly = Point((cx, cy)).buffer(radius, resolution=64)
297
- union_poly = tool_polygon.union(circle_poly)
298
- overlap_with_others = False
299
- too_close_to_others = False
300
- for poly in all_polygons:
301
- if union_poly.intersects(poly):
302
- overlap_with_others = True
303
- break
304
- if circle_poly.buffer(min_gap).intersects(poly):
305
- too_close_to_others = True
306
- break
307
- if overlap_with_others or too_close_to_others:
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  continue
309
- existing_centers.append((cx, cy))
310
- return union_poly, (cx, cy)
311
  print("Warning: Could not place a finger cut circle meeting all spacing requirements.")
312
  return None, None
313
 
314
- # ---------------------
315
- # DXF Spline and Boundary Functions
316
- # ---------------------
317
  def save_dxf_spline(inflated_contours, scaling_factor, height, finger_clearance=False):
318
  degree = 3
319
  closed = True
@@ -323,26 +383,43 @@ def save_dxf_spline(inflated_contours, scaling_factor, height, finger_clearance=
323
  msp = doc.modelspace()
324
  finger_cut_centers = []
325
  final_polygons_inch = []
 
326
  for contour in inflated_contours:
327
  try:
328
  resampled_contour = resample_contour(contour)
329
- points_inch = [(x * scaling_factor, (height - y) * scaling_factor) for x, y in resampled_contour]
 
 
330
  if len(points_inch) < 3:
331
  continue
 
332
  if np.linalg.norm(np.array(points_inch[0]) - np.array(points_inch[-1])) > 1e-2:
333
  points_inch.append(points_inch[0])
 
334
  tool_polygon = build_tool_polygon(points_inch)
 
 
 
335
  if finger_clearance:
336
- union_poly, center = place_finger_cut_randomly(tool_polygon, points_inch, finger_cut_centers, final_polygons_inch, circle_diameter=1.0, min_gap=0.25, max_attempts=30)
 
 
 
 
337
  if union_poly is not None:
338
  tool_polygon = union_poly
 
339
  exterior_coords = polygon_to_exterior_coords(tool_polygon)
340
  if len(exterior_coords) < 3:
341
  continue
 
342
  msp.add_spline(exterior_coords, degree=degree, dxfattribs={"layer": "TOOLS"})
343
  final_polygons_inch.append(tool_polygon)
344
- except ValueError as e:
345
- print(f"Skipping contour: {e}")
 
 
 
346
  return doc, final_polygons_inch
347
 
348
  def add_rectangular_boundary(doc, polygons_inch, boundary_length, boundary_width, offset_unit, annotation_text="", image_height_in=None, image_width_in=None):
 
20
  from torchvision import transforms
21
  from scalingtestupdated import calculate_scaling_factor
22
  from shapely.geometry import Polygon, Point, MultiPolygon, GeometryCollection
23
+ from shapely.validation import make_valid
24
  from scipy.interpolate import splprep, splev
25
  from scipy.ndimage import gaussian_filter1d
26
  from u2net import U2NETP
 
261
  # ---------------------
262
  # Functions for Finger Cut Clearance
263
  # ---------------------
264
+ def validate_and_fix_geometry(geom):
265
+ """Ensure geometry is valid and convert to Polygon if possible"""
266
+ if geom is None or geom.is_empty:
267
+ return None
268
+
269
+ if not geom.is_valid:
270
+ geom = make_valid(geom)
271
+
272
+ # Handle GeometryCollections by extracting the largest valid polygon
273
+ if geom.geom_type == "GeometryCollection":
274
+ polygons = [g for g in geom.geoms if g.geom_type in ['Polygon', 'MultiPolygon']]
275
+ if not polygons:
276
+ return None
277
+ # Get the largest polygon by area
278
+ largest = max(polygons, key=lambda g: g.area)
279
+ geom = largest
280
+
281
+ # Convert MultiPolygon to single Polygon using convex hull if needed
282
+ if geom.geom_type == "MultiPolygon":
283
+ geom = geom.convex_hull
284
+
285
+ if geom.geom_type != 'Polygon':
286
+ return None
287
+
288
+ return geom
289
+
290
  def union_tool_and_circle(tool_polygon: Polygon, center_inch, circle_diameter=1.0):
291
+ try:
292
+ radius = circle_diameter / 2.0
293
+ circle_poly = Point(center_inch).buffer(radius, resolution=64)
294
+ tool_polygon = validate_and_fix_geometry(tool_polygon)
295
+ if tool_polygon is None:
296
+ return None, None
297
+
298
+ union_poly = tool_polygon.union(circle_poly)
299
+ union_poly = validate_and_fix_geometry(union_poly)
300
+ return union_poly, center_inch
301
+ except Exception as e:
302
+ print(f"Error in union_tool_and_circle: {str(e)}")
303
+ return None, None
304
 
305
  def build_tool_polygon(points_inch):
306
+ try:
307
+ if len(points_inch) < 3:
308
+ return None
309
+ poly = Polygon(points_inch)
310
+ return validate_and_fix_geometry(poly)
311
+ except Exception as e:
312
+ print(f"Error building tool polygon: {str(e)}")
313
+ return None
314
 
315
  def polygon_to_exterior_coords(poly: Polygon):
316
+ poly = validate_and_fix_geometry(poly)
317
+ if poly is None:
318
  return []
319
+
320
  if poly.geom_type == "MultiPolygon":
321
  biggest = max(poly.geoms, key=lambda g: g.area)
322
  poly = biggest
323
+
324
+ if not hasattr(poly, 'exterior') or not poly.exterior:
325
  return []
326
+
327
  return list(poly.exterior.coords)
328
 
329
  def place_finger_cut_randomly(tool_polygon, points_inch, existing_centers, all_polygons, circle_diameter=1.0, min_gap=0.25, max_attempts=30):
330
  import random
331
  needed_center_distance = circle_diameter + min_gap
332
  radius = circle_diameter / 2.0
333
+
334
+ tool_polygon = validate_and_fix_geometry(tool_polygon)
335
+ if tool_polygon is None:
336
+ return None, None
337
+
338
  for _ in range(max_attempts):
339
  idx = random.randint(0, len(points_inch) - 1)
340
  cx, cy = points_inch[idx]
341
+
342
+ # Check distance to existing centers
343
+ too_close = any(np.hypot(cx - ex_x, cy - ex_y) < needed_center_distance
344
+ for (ex_x, ex_y) in existing_centers)
 
345
  if too_close:
346
  continue
347
+
348
+ try:
349
+ circle_poly = Point((cx, cy)).buffer(radius, resolution=64)
350
+ union_poly = tool_polygon.union(circle_poly)
351
+ union_poly = validate_and_fix_geometry(union_poly)
352
+
353
+ if union_poly is None:
354
+ continue
355
+
356
+ # Check for overlaps
357
+ overlap = False
358
+ for poly in all_polygons:
359
+ if union_poly.intersects(poly):
360
+ overlap = True
361
+ break
362
+ if circle_poly.buffer(min_gap).intersects(poly):
363
+ overlap = True
364
+ break
365
+
366
+ if not overlap:
367
+ existing_centers.append((cx, cy))
368
+ return union_poly, (cx, cy)
369
+
370
+ except Exception as e:
371
+ print(f"Error placing finger cut: {str(e)}")
372
  continue
373
+
 
374
  print("Warning: Could not place a finger cut circle meeting all spacing requirements.")
375
  return None, None
376
 
 
 
 
377
  def save_dxf_spline(inflated_contours, scaling_factor, height, finger_clearance=False):
378
  degree = 3
379
  closed = True
 
383
  msp = doc.modelspace()
384
  finger_cut_centers = []
385
  final_polygons_inch = []
386
+
387
  for contour in inflated_contours:
388
  try:
389
  resampled_contour = resample_contour(contour)
390
+ points_inch = [(x * scaling_factor, (height - y) * scaling_factor)
391
+ for x, y in resampled_contour]
392
+
393
  if len(points_inch) < 3:
394
  continue
395
+
396
  if np.linalg.norm(np.array(points_inch[0]) - np.array(points_inch[-1])) > 1e-2:
397
  points_inch.append(points_inch[0])
398
+
399
  tool_polygon = build_tool_polygon(points_inch)
400
+ if tool_polygon is None:
401
+ continue
402
+
403
  if finger_clearance:
404
+ union_poly, center = place_finger_cut_randomly(
405
+ tool_polygon, points_inch, finger_cut_centers,
406
+ final_polygons_inch, circle_diameter=1.0,
407
+ min_gap=0.25, max_attempts=30
408
+ )
409
  if union_poly is not None:
410
  tool_polygon = union_poly
411
+
412
  exterior_coords = polygon_to_exterior_coords(tool_polygon)
413
  if len(exterior_coords) < 3:
414
  continue
415
+
416
  msp.add_spline(exterior_coords, degree=degree, dxfattribs={"layer": "TOOLS"})
417
  final_polygons_inch.append(tool_polygon)
418
+
419
+ except Exception as e:
420
+ print(f"Skipping contour due to error: {str(e)}")
421
+ continue
422
+
423
  return doc, final_polygons_inch
424
 
425
  def add_rectangular_boundary(doc, polygons_inch, boundary_length, boundary_width, offset_unit, annotation_text="", image_height_in=None, image_width_in=None):