Spaces:
Sleeping
Sleeping
muhammadhamza-stack
commited on
Commit
·
fd5039f
1
Parent(s):
9a2f47d
refine the gradio ui
Browse files- app.py +128 -220
- Test20.jpg → data/Test20.jpg +0 -0
- Test21.jpg → data/Test21.jpg +0 -0
- requirements.txt +4 -2
app.py
CHANGED
|
@@ -58,7 +58,7 @@ class FingerCutOverlapError(Exception):
|
|
| 58 |
"""There was an overlap with fingercuts... Please try again to generate dxf."""
|
| 59 |
pass
|
| 60 |
# ---------------------
|
| 61 |
-
# Global Model Initialization with caching and print statements
|
| 62 |
# ---------------------
|
| 63 |
print("Loading YOLOWorld model...")
|
| 64 |
start_time = time.time()
|
|
@@ -110,7 +110,7 @@ transform_image_global = transforms.Compose([
|
|
| 110 |
])
|
| 111 |
|
| 112 |
# ---------------------
|
| 113 |
-
#
|
| 114 |
# ---------------------
|
| 115 |
def unload_and_reload_models():
|
| 116 |
global drawer_detector_global, reference_detector_global, birefnet_global, u2net_global
|
|
@@ -708,198 +708,6 @@ def draw_single_polygon(poly, image_rgb, scaling_factor, image_height, color=(0,
|
|
| 708 |
pts_px = np.array(pts_px, dtype=np.int32)
|
| 709 |
cv2.polylines(image_rgb, [pts_px], isClosed=True, color=color, thickness=thickness, lineType=cv2.LINE_AA)
|
| 710 |
|
| 711 |
-
# def draw_and_pad(polygons_inch, scaling_factor,boundary_polygon, padding=50,
|
| 712 |
-
# color=(0,0,255), thickness=2):
|
| 713 |
-
# """
|
| 714 |
-
# - polygons_inch: list of Shapely Polygons in inch units (already including boundary).
|
| 715 |
-
# - scaling_factor: inches per pixel.
|
| 716 |
-
# - padding: padding in pixels.
|
| 717 |
-
# """
|
| 718 |
-
# all_x = []
|
| 719 |
-
# all_y = []
|
| 720 |
-
# pixel_polys = []
|
| 721 |
-
|
| 722 |
-
# # 1) Convert to pixel coords and collect bounds
|
| 723 |
-
# for poly in polygons_inch:
|
| 724 |
-
# coords = list(poly.exterior.coords)
|
| 725 |
-
# pts = []
|
| 726 |
-
# for x_in, y_in in coords:
|
| 727 |
-
# px = int(round(x_in / scaling_factor))
|
| 728 |
-
# py = int(round(y_in / scaling_factor))
|
| 729 |
-
# pts.append([px, py])
|
| 730 |
-
# all_x.append(px)
|
| 731 |
-
# all_y.append(py)
|
| 732 |
-
# pixel_polys.append(np.array(pts, dtype=np.int32))
|
| 733 |
-
|
| 734 |
-
# # 2) Compute canvas size
|
| 735 |
-
|
| 736 |
-
# min_x, max_x = min(all_x), max(all_x)
|
| 737 |
-
# min_y, max_y = min(all_y), max(all_y)
|
| 738 |
-
# width = max_x - min_x + 1
|
| 739 |
-
# height = max_y - min_y + 1
|
| 740 |
-
|
| 741 |
-
# # 3) Create blank white canvas
|
| 742 |
-
# canvas = 255 * np.ones((height, width, 3), dtype=np.uint8)
|
| 743 |
-
|
| 744 |
-
# # 4) Draw each polygon, flipping y within the local box
|
| 745 |
-
# for pts in pixel_polys:
|
| 746 |
-
# # Offset so min corner is (0,0)
|
| 747 |
-
# pts_off = pts - np.array([[min_x, min_y]])
|
| 748 |
-
# # Flip y: new_y = height-1 - old_y
|
| 749 |
-
# pts_off[:,1] = (height - 1) - pts_off[:,1]
|
| 750 |
-
# cv2.polylines(canvas, [pts_off], isClosed=True,
|
| 751 |
-
# color=color, thickness=thickness, lineType=cv2.LINE_AA)
|
| 752 |
-
|
| 753 |
-
# # 5) Pad the canvas
|
| 754 |
-
|
| 755 |
-
# padded = cv2.copyMakeBorder(
|
| 756 |
-
# canvas,
|
| 757 |
-
# top=padding, bottom=padding,
|
| 758 |
-
# left=padding, right=padding,
|
| 759 |
-
# borderType=cv2.BORDER_CONSTANT,
|
| 760 |
-
# value=[255,255,255]
|
| 761 |
-
# )
|
| 762 |
-
# return padded
|
| 763 |
-
|
| 764 |
-
# import numpy as np
|
| 765 |
-
# import cv2
|
| 766 |
-
|
| 767 |
-
# def draw_and_pad(polygons_inch, scaling_factor, boundary_polygon, padding=50,
|
| 768 |
-
# color=(0,0,255), thickness=2):
|
| 769 |
-
# """
|
| 770 |
-
# - polygons_inch: list of Shapely Polygons in inch units.
|
| 771 |
-
# - scaling_factor: inches per pixel.
|
| 772 |
-
# - boundary_polygon: the Shapely boundary polygon, or None.
|
| 773 |
-
# - padding: base padding in pixels.
|
| 774 |
-
# """
|
| 775 |
-
# all_x, all_y = [], []
|
| 776 |
-
# pixel_polys = []
|
| 777 |
-
|
| 778 |
-
# # 1) Convert to pixel coords and collect bounds
|
| 779 |
-
# for poly in polygons_inch:
|
| 780 |
-
# coords = list(poly.exterior.coords)
|
| 781 |
-
# pts = []
|
| 782 |
-
# for x_in, y_in in coords:
|
| 783 |
-
# px = int(round(x_in / scaling_factor))
|
| 784 |
-
# py = int(round(y_in / scaling_factor))
|
| 785 |
-
# pts.append([px, py])
|
| 786 |
-
# all_x.append(px)
|
| 787 |
-
# all_y.append(py)
|
| 788 |
-
# pixel_polys.append(np.array(pts, dtype=np.int32))
|
| 789 |
-
|
| 790 |
-
# # 2) Compute canvas size
|
| 791 |
-
# min_x, max_x = min(all_x), max(all_x)
|
| 792 |
-
# min_y, max_y = min(all_y), max(all_y)
|
| 793 |
-
# width = max_x - min_x + 1
|
| 794 |
-
# height = max_y - min_y + 1
|
| 795 |
-
|
| 796 |
-
# # 3) Create blank white canvas
|
| 797 |
-
# canvas = 255 * np.ones((height, width, 3), dtype=np.uint8)
|
| 798 |
-
|
| 799 |
-
# # 4) Draw each polygon, flipping y within the local box
|
| 800 |
-
# for pts in pixel_polys:
|
| 801 |
-
# pts_off = pts - np.array([[min_x, min_y]])
|
| 802 |
-
# pts_off[:,1] = (height - 1) - pts_off[:,1]
|
| 803 |
-
# cv2.polylines(canvas, [pts_off], isClosed=True,
|
| 804 |
-
# color=color, thickness=thickness, lineType=cv2.LINE_AA)
|
| 805 |
-
|
| 806 |
-
# # 5) Decide padding amounts
|
| 807 |
-
# if boundary_polygon is not None:
|
| 808 |
-
# top = bottom = left = right = padding
|
| 809 |
-
# else:
|
| 810 |
-
# # Double the padding if there's no boundary, to avoid clipping
|
| 811 |
-
# top = bottom = left = right = padding * 2
|
| 812 |
-
|
| 813 |
-
# # 6) Pad the canvas
|
| 814 |
-
# padded = cv2.copyMakeBorder(
|
| 815 |
-
# canvas,
|
| 816 |
-
# top=top, bottom=bottom,
|
| 817 |
-
# left=left, right=right,
|
| 818 |
-
# borderType=cv2.BORDER_CONSTANT,
|
| 819 |
-
# value=[255,255,255]
|
| 820 |
-
# )
|
| 821 |
-
# return padded
|
| 822 |
-
|
| 823 |
-
import numpy as np
|
| 824 |
-
import cv2
|
| 825 |
-
|
| 826 |
-
# def draw_and_pad(polygons_inch, scaling_factor, boundary_polygon, padding=50,
|
| 827 |
-
# color=(0, 0, 255), thickness=2):
|
| 828 |
-
# """
|
| 829 |
-
# Draws Shapely Polygons (in inch units) on a white canvas.
|
| 830 |
-
|
| 831 |
-
# When boundary_polygon is None, the computed bounds are expanded by the padding value
|
| 832 |
-
# so that the drawn contours are not clipped at the edges after adding the final padding.
|
| 833 |
-
|
| 834 |
-
# Arguments:
|
| 835 |
-
# polygons_inch: list of Shapely Polygons in inch units (already including boundary).
|
| 836 |
-
# scaling_factor: inches per pixel.
|
| 837 |
-
# boundary_polygon: the Shapely boundary polygon, or None.
|
| 838 |
-
# padding: padding in pixels.
|
| 839 |
-
# color: color of the drawn polylines (in BGR format).
|
| 840 |
-
# thickness: line thickness.
|
| 841 |
-
|
| 842 |
-
# Returns:
|
| 843 |
-
# padded: an image (numpy array) of the drawn polygons with an external white border.
|
| 844 |
-
# """
|
| 845 |
-
# all_x = []
|
| 846 |
-
# all_y = []
|
| 847 |
-
# pixel_polys = []
|
| 848 |
-
|
| 849 |
-
# # 1) Convert each polygon to pixel coordinates and compute overall bounds.
|
| 850 |
-
# for poly in polygons_inch:
|
| 851 |
-
# coords = list(poly.exterior.coords)
|
| 852 |
-
# pts = []
|
| 853 |
-
# for x_in, y_in in coords:
|
| 854 |
-
# px = int(round(x_in / scaling_factor))
|
| 855 |
-
# py = int(round(y_in / scaling_factor))
|
| 856 |
-
# pts.append([px, py])
|
| 857 |
-
# all_x.append(px)
|
| 858 |
-
# all_y.append(py)
|
| 859 |
-
# pixel_polys.append(np.array(pts, dtype=np.int32))
|
| 860 |
-
|
| 861 |
-
# # 2) Compute the basic canvas size from the polygon bounds.
|
| 862 |
-
# min_x, max_x = min(all_x), max(all_x)
|
| 863 |
-
# min_y, max_y = min(all_y), max(all_y)
|
| 864 |
-
|
| 865 |
-
# # If no boundary polygon is provided, expand the bounds to add margin
|
| 866 |
-
# # so that later when we pad externally, the contours do not get clipped.
|
| 867 |
-
# if boundary_polygon is None:
|
| 868 |
-
# min_x -= padding
|
| 869 |
-
# max_x += padding
|
| 870 |
-
# min_y -= padding
|
| 871 |
-
# max_y += padding
|
| 872 |
-
|
| 873 |
-
# width = max_x - min_x + 1
|
| 874 |
-
# height = max_y - min_y + 1
|
| 875 |
-
|
| 876 |
-
# # 3) Create a blank white canvas.
|
| 877 |
-
# canvas = 255 * np.ones((height, width, 3), dtype=np.uint8)
|
| 878 |
-
|
| 879 |
-
# # 4) Draw each polygon, flipping the y-coordinates to match image coordinates.
|
| 880 |
-
# for pts in pixel_polys:
|
| 881 |
-
# # Offset so the minimum corner becomes (0,0) on canvas.
|
| 882 |
-
# pts_off = pts - np.array([[min_x, min_y]])
|
| 883 |
-
# # Flip y: image coordinates have (0,0) at the top-left.
|
| 884 |
-
# pts_off[:, 1] = (height - 1) - pts_off[:, 1]
|
| 885 |
-
# cv2.polylines(canvas, [pts_off], isClosed=True,
|
| 886 |
-
# color=color, thickness=thickness, lineType=cv2.LINE_AA)
|
| 887 |
-
|
| 888 |
-
# # 5) Finally, add external padding on all sides.
|
| 889 |
-
# padded = cv2.copyMakeBorder(
|
| 890 |
-
# canvas,
|
| 891 |
-
# top=padding, bottom=padding,
|
| 892 |
-
# left=padding, right=padding,
|
| 893 |
-
# borderType=cv2.BORDER_CONSTANT,
|
| 894 |
-
# value=[255, 255, 255]
|
| 895 |
-
# )
|
| 896 |
-
|
| 897 |
-
# return padded
|
| 898 |
-
|
| 899 |
-
import numpy as np
|
| 900 |
-
import cv2
|
| 901 |
-
from shapely.geometry import Polygon
|
| 902 |
-
|
| 903 |
import numpy as np
|
| 904 |
import cv2
|
| 905 |
from shapely.geometry import Polygon
|
|
@@ -1274,39 +1082,139 @@ def predict(
|
|
| 1274 |
str(scaling_factor)
|
| 1275 |
)
|
| 1276 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1277 |
# ---------------------
|
| 1278 |
# Gradio Interface
|
| 1279 |
# ---------------------
|
| 1280 |
if __name__ == "__main__":
|
| 1281 |
os.makedirs("./outputs", exist_ok=True)
|
|
|
|
| 1282 |
def gradio_predict(img, offset, offset_unit, finger_clearance, add_boundary, boundary_length, boundary_width, annotation_text):
|
| 1283 |
try:
|
| 1284 |
return predict(img, offset, offset_unit, finger_clearance, add_boundary, boundary_length, boundary_width, annotation_text)
|
| 1285 |
except Exception as e:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1286 |
return None, None, None, None, f"Error: {str(e)}"
|
| 1287 |
-
|
| 1288 |
-
|
| 1289 |
-
|
| 1290 |
-
|
| 1291 |
-
|
| 1292 |
-
|
| 1293 |
-
|
| 1294 |
-
gr.
|
| 1295 |
-
gr.
|
| 1296 |
-
gr.
|
| 1297 |
-
gr.
|
| 1298 |
-
|
| 1299 |
-
|
| 1300 |
-
|
| 1301 |
-
|
| 1302 |
-
|
| 1303 |
-
gr.
|
| 1304 |
-
|
| 1305 |
-
|
| 1306 |
-
|
| 1307 |
-
|
| 1308 |
-
|
| 1309 |
-
|
| 1310 |
-
|
| 1311 |
-
|
| 1312 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
"""There was an overlap with fingercuts... Please try again to generate dxf."""
|
| 59 |
pass
|
| 60 |
# ---------------------
|
| 61 |
+
# Global Model Initialization with caching and print statements (Original code preserved)
|
| 62 |
# ---------------------
|
| 63 |
print("Loading YOLOWorld model...")
|
| 64 |
start_time = time.time()
|
|
|
|
| 110 |
])
|
| 111 |
|
| 112 |
# ---------------------
|
| 113 |
+
# Helper Functions (Preserved as is)
|
| 114 |
# ---------------------
|
| 115 |
def unload_and_reload_models():
|
| 116 |
global drawer_detector_global, reference_detector_global, birefnet_global, u2net_global
|
|
|
|
| 708 |
pts_px = np.array(pts_px, dtype=np.int32)
|
| 709 |
cv2.polylines(image_rgb, [pts_px], isClosed=True, color=color, thickness=thickness, lineType=cv2.LINE_AA)
|
| 710 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 711 |
import numpy as np
|
| 712 |
import cv2
|
| 713 |
from shapely.geometry import Polygon
|
|
|
|
| 1082 |
str(scaling_factor)
|
| 1083 |
)
|
| 1084 |
|
| 1085 |
+
|
| 1086 |
+
# ---------------------
|
| 1087 |
+
# Documentation Strings for Gradio
|
| 1088 |
+
# ---------------------
|
| 1089 |
+
|
| 1090 |
+
QUICK_START = """
|
| 1091 |
+
## 1. Quick Start Guide: From Photo to DXF Cut File
|
| 1092 |
+
This application converts a single photograph of tools in a drawer/tray into a ready-to-use DXF file for CNC cutting foam inserts.
|
| 1093 |
+
|
| 1094 |
+
1. **Preparation**: Place your tools in a drawer and include a **reference coin** (assumed to be 0.955 inches / 24.25mm wide, e.g., a US Quarter). Ensure clear, even lighting.
|
| 1095 |
+
2. **Upload**: Upload the image.
|
| 1096 |
+
3. **Configure**: Adjust the `Offset Value` (clearance around the tool) and select options like `Finger Clearance` and `Boundary`.
|
| 1097 |
+
4. **Run**: Click the **"Submit"** button (automatically generated by Gradio).
|
| 1098 |
+
5. **Download**: Review the `Outlines` and `Output Image`, then download the final **DXF file** for your cutting machine.
|
| 1099 |
+
"""
|
| 1100 |
+
|
| 1101 |
+
INPUT_EXPLANATION = """
|
| 1102 |
+
## 2. Expected Inputs
|
| 1103 |
+
|
| 1104 |
+
### Image Requirements
|
| 1105 |
+
* **Reference Object is Mandatory:** The system *must* detect a specific reference object (default: coin roughly 0.955 inches wide) to correctly calculate the real-world scale (inches/pixel).
|
| 1106 |
+
* **Placement:** The image should be taken from directly above the drawer (minimal perspective distortion).
|
| 1107 |
+
* **Lighting:** Clear, shadow-free lighting is crucial for object detection and masking.
|
| 1108 |
+
|
| 1109 |
+
### Processing Parameters
|
| 1110 |
+
|
| 1111 |
+
| Parameter | Purpose | Default Value | Guidance for Non-Tech Users |
|
| 1112 |
+
| :--- | :--- | :--- | :--- |
|
| 1113 |
+
| **Offset Value** | The amount of space (clearance) added around the tool profile. This is the buffer to ensure the tool fits easily into the foam cutout. | 0.075 | Increase this value for thicker tools or if you need looser cutouts. |
|
| 1114 |
+
| **Offset Unit** | Specifies whether the offset value is in millimeters or inches. | inches | Match this to your preferred measurement system. |
|
| 1115 |
+
| **Add Finger Clearance?** | Adds a small circular cutout to the side of the tool profile to allow easy removal using a finger. | Yes | Recommended for most tools. |
|
| 1116 |
+
| **Add Rectangular Boundary?**| Defines the exterior rectangular shape of the entire foam insert (the boundary of the drawer). | Yes | Required if you need a rectangular outer boundary for cutting. |
|
| 1117 |
+
| **Boundary Length/Width** | The actual dimensions of the drawer/tray area for the foam insert. | 50.0 (in the selected unit) | Must be larger than the combined area of all tools plus margins. |
|
| 1118 |
+
| **Annotation** | Text to engrave/cut into the foam, typically placed at the top of the boundary box. (Max 20 chars) | (Empty) | Use this to label the tray (e.g., "Wrench Set"). |
|
| 1119 |
+
"""
|
| 1120 |
+
|
| 1121 |
+
OUTPUT_EXPLANATION = """
|
| 1122 |
+
## 3. Expected Outputs
|
| 1123 |
+
|
| 1124 |
+
| Output Field | Description | Purpose |
|
| 1125 |
+
| :--- | :--- | :--- |
|
| 1126 |
+
| **Output Image** | The original image overlaid with the detected tool outlines (including offset and finger clearance). | Visual confirmation that the detection and sizing are correct relative to the original photo. |
|
| 1127 |
+
| **Outlines of Objects**| A clean, scaled, white-background image showing only the outlines (blue lines). This visually represents the final geometry exported to the DXF. | Final quality check for shape and spacing before CNC cutting. |
|
| 1128 |
+
| **DXF file** | The final vector file containing the tool outlines, boundary box, and text annotation (if included). | Ready to upload to CNC machine software (AutoCAD DXF R2000 format). |
|
| 1129 |
+
| **Mask** | The binary image used internally to generate the tool contours after applying the offset value. | Technical output for debugging segmentation issues. |
|
| 1130 |
+
| **Scaling Factor**| The calculated real-world scale (inches per pixel) determined by the reference coin. | Confirmation of measurement accuracy. Values close to 0.7 are common for phone photos. |
|
| 1131 |
+
"""
|
| 1132 |
+
|
| 1133 |
+
|
| 1134 |
# ---------------------
|
| 1135 |
# Gradio Interface
|
| 1136 |
# ---------------------
|
| 1137 |
if __name__ == "__main__":
|
| 1138 |
os.makedirs("./outputs", exist_ok=True)
|
| 1139 |
+
|
| 1140 |
def gradio_predict(img, offset, offset_unit, finger_clearance, add_boundary, boundary_length, boundary_width, annotation_text):
|
| 1141 |
try:
|
| 1142 |
return predict(img, offset, offset_unit, finger_clearance, add_boundary, boundary_length, boundary_width, annotation_text)
|
| 1143 |
except Exception as e:
|
| 1144 |
+
# Handle specific known errors for clearer user feedback
|
| 1145 |
+
if isinstance(e, (DrawerNotDetectedError, ReferenceBoxNotDetectedError)):
|
| 1146 |
+
gr.Warning(f"Processing Error: {str(e)} Please check image quality and coin placement.")
|
| 1147 |
+
elif isinstance(e, (BoundaryOverlapError, TextOverlapError)):
|
| 1148 |
+
gr.Error(f"Boundary/Text Placement Error: {str(e)}")
|
| 1149 |
+
else:
|
| 1150 |
+
gr.Error(f"An unexpected error occurred: {str(e)}")
|
| 1151 |
+
|
| 1152 |
return None, None, None, None, f"Error: {str(e)}"
|
| 1153 |
+
|
| 1154 |
+
with gr.Blocks(title="Tool Cutout DXF Generator") as demo:
|
| 1155 |
+
gr.Markdown("<h1 style='text-align: center;'> AI-Powered Tool Cutout DXF Generator </h1>")
|
| 1156 |
+
gr.Markdown("Convert a photo of your tool drawer into precise CNC-ready vector files.")
|
| 1157 |
+
|
| 1158 |
+
# 1. Guidelines & Instructions
|
| 1159 |
+
with gr.Accordion(" Tips & User Guide", open=False):
|
| 1160 |
+
gr.Markdown(QUICK_START)
|
| 1161 |
+
gr.Markdown("---")
|
| 1162 |
+
gr.Markdown(INPUT_EXPLANATION)
|
| 1163 |
+
gr.Markdown("---")
|
| 1164 |
+
gr.Markdown(OUTPUT_EXPLANATION)
|
| 1165 |
+
|
| 1166 |
+
# 2. Main Interface Setup
|
| 1167 |
+
|
| 1168 |
+
with gr.Row():
|
| 1169 |
+
with gr.Column():
|
| 1170 |
+
gr.Markdown("## Step 1: Upload Image and Configure Parameters")
|
| 1171 |
+
input_image = gr.Image(label="Input Image", type="numpy")
|
| 1172 |
+
|
| 1173 |
+
with gr.Column():
|
| 1174 |
+
gr.Markdown("## Step 2: Configure Parameters")
|
| 1175 |
+
offset_value = gr.Number(label="Offset value for Mask", value=0.075)
|
| 1176 |
+
offset_unit = gr.Dropdown(label="Offset Unit", choices=["mm", "inches"], value="inches")
|
| 1177 |
+
finger_clearance = gr.Dropdown(label="Add Finger Clearance?", choices=["Yes", "No"], value="Yes")
|
| 1178 |
+
add_boundary = gr.Dropdown(label="Add Rectangular Boundary?", choices=["Yes", "No"], value="Yes")
|
| 1179 |
+
boundary_length = gr.Number(label="Boundary Length", value=50.0, precision=2)
|
| 1180 |
+
boundary_width = gr.Number(label="Boundary Width", value=50.0, precision=2)
|
| 1181 |
+
annotation_text = gr.Textbox(label="Annotation (max 20 chars)", max_length=20, placeholder="Type up to 20 characters")
|
| 1182 |
+
|
| 1183 |
+
gr.Markdown("## Step 3: Click Generate DXF & Previews")
|
| 1184 |
+
submit_button = gr.Button("Generate DXF & Previews", variant="primary")
|
| 1185 |
+
|
| 1186 |
+
# 3. Outputs
|
| 1187 |
+
gr.Markdown("## Outputs")
|
| 1188 |
+
|
| 1189 |
+
with gr.Row():
|
| 1190 |
+
output_img = gr.Image(format="png", label="Output Image (Tools overlaid with outlines)")
|
| 1191 |
+
outlines_color = gr.Image(format="png", label="Outlines of Objects (Final DXF Geometry Preview)")
|
| 1192 |
+
|
| 1193 |
+
with gr.Row():
|
| 1194 |
+
dxf_file = gr.File(label="DXF file")
|
| 1195 |
+
with gr.Column():
|
| 1196 |
+
scaling_factor_display = gr.Textbox(label="Scaling Factor (inches/pixel)")
|
| 1197 |
+
dilated_mask = gr.Image(label="Mask (Technical Preview)")
|
| 1198 |
+
|
| 1199 |
+
# 4. Examples
|
| 1200 |
+
gr.Markdown("---")
|
| 1201 |
+
gr.Markdown("## Example Data (Click a row to load the image and parameters)")
|
| 1202 |
+
gr.Examples(
|
| 1203 |
+
examples=[
|
| 1204 |
+
["data/Test20.jpg", 0.075, "inches", "Yes", "No", 300.0, 200.0, "MyTool"],
|
| 1205 |
+
["data/Test21.jpg", 0.075, "inches", "Yes", "Yes", 300.0, 200.0, "Tool2"]
|
| 1206 |
+
],
|
| 1207 |
+
inputs=[input_image, offset_value, offset_unit, finger_clearance, add_boundary, boundary_length, boundary_width, annotation_text],
|
| 1208 |
+
outputs=[output_img, outlines_color, dxf_file, dilated_mask, scaling_factor_display],
|
| 1209 |
+
fn=gradio_predict,
|
| 1210 |
+
cache_examples=False,
|
| 1211 |
+
)
|
| 1212 |
+
|
| 1213 |
+
# Event Handler
|
| 1214 |
+
submit_button.click(
|
| 1215 |
+
fn=gradio_predict,
|
| 1216 |
+
inputs=[input_image, offset_value, offset_unit, finger_clearance, add_boundary, boundary_length, boundary_width, annotation_text],
|
| 1217 |
+
outputs=[output_img, outlines_color, dxf_file, dilated_mask, scaling_factor_display]
|
| 1218 |
+
)
|
| 1219 |
+
|
| 1220 |
+
demo.launch(share=True)
|
Test20.jpg → data/Test20.jpg
RENAMED
|
File without changes
|
Test21.jpg → data/Test21.jpg
RENAMED
|
File without changes
|
requirements.txt
CHANGED
|
@@ -1,11 +1,13 @@
|
|
| 1 |
transformers
|
| 2 |
ultralytics==8.3.9
|
| 3 |
ezdxf
|
| 4 |
-
gradio
|
| 5 |
pydantic==2.10.6
|
| 6 |
kornia
|
| 7 |
timm
|
| 8 |
einops
|
| 9 |
shapely
|
| 10 |
gevent==22.10.2
|
| 11 |
-
matplotlib
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
transformers
|
| 2 |
ultralytics==8.3.9
|
| 3 |
ezdxf
|
|
|
|
| 4 |
pydantic==2.10.6
|
| 5 |
kornia
|
| 6 |
timm
|
| 7 |
einops
|
| 8 |
shapely
|
| 9 |
gevent==22.10.2
|
| 10 |
+
matplotlib
|
| 11 |
+
numpy<2
|
| 12 |
+
gradio==3.50.2
|
| 13 |
+
gradio-client==0.6.1
|