Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -331,10 +331,13 @@ def validate_single_object(mask: np.ndarray, paper_contour: np.ndarray) -> None:
|
|
| 331 |
# Find contours of objects within paper bounds
|
| 332 |
contours, _ = cv2.findContours(masked_objects, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
| 333 |
|
| 334 |
-
|
|
|
|
|
|
|
| 335 |
min_area = 1000 # Minimum area threshold
|
| 336 |
-
|
| 337 |
-
|
|
|
|
| 338 |
if len(significant_contours) == 0:
|
| 339 |
raise NoObjectDetectedError()
|
| 340 |
elif len(significant_contours) > 1:
|
|
@@ -398,7 +401,7 @@ def remove_bg(image: np.ndarray) -> np.ndarray:
|
|
| 398 |
logger.error(f"Error in BiRefNet background removal: {e}")
|
| 399 |
raise
|
| 400 |
|
| 401 |
-
def exclude_paper_area(mask: np.ndarray, paper_contour: np.ndarray, expansion_factor: float = 1.
|
| 402 |
"""
|
| 403 |
Remove paper area from the mask to focus only on objects
|
| 404 |
"""
|
|
@@ -777,7 +780,7 @@ def make_square(img: np.ndarray):
|
|
| 777 |
|
| 778 |
return padded
|
| 779 |
|
| 780 |
-
def predict_with_paper(image, paper_size, offset,
|
| 781 |
"""Main prediction function using paper as reference"""
|
| 782 |
|
| 783 |
logger.info(f"Starting prediction with image shape: {image.shape}")
|
|
@@ -832,10 +835,7 @@ def predict_with_paper(image, paper_size, offset, offset_unit, edge_radius, fing
|
|
| 832 |
raise gr.Error(f"Error in object detection: {str(e)}")
|
| 833 |
|
| 834 |
# Apply edge rounding if specified
|
| 835 |
-
|
| 836 |
-
rounded_mask = round_edges(objects_mask, edge_radius, scaling_factor)
|
| 837 |
-
else:
|
| 838 |
-
rounded_mask = objects_mask.copy()
|
| 839 |
|
| 840 |
# Apply dilation for offset
|
| 841 |
if offset > 0:
|
|
@@ -902,21 +902,14 @@ def predict_with_paper(image, paper_size, offset, offset_unit, edge_radius, fing
|
|
| 902 |
f"Scale: {scaling_factor:.4f} mm/px | Paper: {paper_size}"
|
| 903 |
)
|
| 904 |
|
| 905 |
-
def predict_full_paper(image, paper_size,
|
| 906 |
-
"""
|
| 907 |
-
Full prediction function with paper reference and flexible outputs
|
| 908 |
-
Returns DXF + conditionally selected additional outputs
|
| 909 |
-
"""
|
| 910 |
-
radius = fillet_value_mm if enable_fillet == "On" else 0
|
| 911 |
finger_flag = "On" if enable_finger_cut == "On" else "Off"
|
| 912 |
|
| 913 |
# Always get all outputs from predict_with_paper
|
| 914 |
ann, outlines, dxf_path, mask, scale_info = predict_with_paper(
|
| 915 |
image,
|
| 916 |
paper_size,
|
| 917 |
-
offset=
|
| 918 |
-
offset_unit="mm",
|
| 919 |
-
edge_radius=radius,
|
| 920 |
finger_clearance=finger_flag,
|
| 921 |
)
|
| 922 |
|
|
@@ -963,22 +956,14 @@ if __name__ == "__main__":
|
|
| 963 |
)
|
| 964 |
|
| 965 |
with gr.Group():
|
| 966 |
-
gr.Markdown("###
|
| 967 |
-
|
| 968 |
-
choices=["On", "Off"],
|
| 969 |
-
value="Off",
|
| 970 |
-
label="Enable Edge Rounding",
|
| 971 |
-
interactive=True
|
| 972 |
-
)
|
| 973 |
-
|
| 974 |
-
fillet_value_mm = gr.Slider(
|
| 975 |
minimum=0,
|
| 976 |
-
maximum=
|
| 977 |
step=1,
|
| 978 |
-
value=
|
| 979 |
-
label="
|
| 980 |
-
|
| 981 |
-
interactive=True
|
| 982 |
)
|
| 983 |
|
| 984 |
with gr.Group():
|
|
@@ -1011,10 +996,6 @@ if __name__ == "__main__":
|
|
| 1011 |
outlines_image = gr.Image(label="Outlines", visible=False)
|
| 1012 |
mask_image = gr.Image(label="Mask", visible=False)
|
| 1013 |
|
| 1014 |
-
# Dynamic visibility updates
|
| 1015 |
-
def toggle_fillet(choice):
|
| 1016 |
-
return gr.update(visible=(choice == "On"))
|
| 1017 |
-
|
| 1018 |
def update_outputs_visibility(selected):
|
| 1019 |
return [
|
| 1020 |
gr.update(visible="Annotated Image" in selected),
|
|
@@ -1022,12 +1003,6 @@ if __name__ == "__main__":
|
|
| 1022 |
gr.update(visible="Mask" in selected)
|
| 1023 |
]
|
| 1024 |
|
| 1025 |
-
# Event handlers
|
| 1026 |
-
enable_fillet.change(
|
| 1027 |
-
fn=toggle_fillet,
|
| 1028 |
-
inputs=enable_fillet,
|
| 1029 |
-
outputs=fillet_value_mm
|
| 1030 |
-
)
|
| 1031 |
|
| 1032 |
output_options.change(
|
| 1033 |
fn=update_outputs_visibility,
|
|
@@ -1040,8 +1015,7 @@ if __name__ == "__main__":
|
|
| 1040 |
inputs=[
|
| 1041 |
input_image,
|
| 1042 |
paper_size,
|
| 1043 |
-
|
| 1044 |
-
fillet_value_mm,
|
| 1045 |
enable_finger_cut,
|
| 1046 |
output_options
|
| 1047 |
],
|
|
|
|
| 331 |
# Find contours of objects within paper bounds
|
| 332 |
contours, _ = cv2.findContours(masked_objects, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
| 333 |
|
| 334 |
+
|
| 335 |
+
# Filter out very small contours (noise) and paper-sized contours
|
| 336 |
+
image_area = mask.shape[0] * mask.shape[1]
|
| 337 |
min_area = 1000 # Minimum area threshold
|
| 338 |
+
max_area = image_area * 0.5 # Maximum 50% of image area (to exclude paper detection)
|
| 339 |
+
significant_contours = [c for c in contours if min_area < cv2.contourArea(c) < max_area]
|
| 340 |
+
|
| 341 |
if len(significant_contours) == 0:
|
| 342 |
raise NoObjectDetectedError()
|
| 343 |
elif len(significant_contours) > 1:
|
|
|
|
| 401 |
logger.error(f"Error in BiRefNet background removal: {e}")
|
| 402 |
raise
|
| 403 |
|
| 404 |
+
def exclude_paper_area(mask: np.ndarray, paper_contour: np.ndarray, expansion_factor: float = 1.2) -> np.ndarray:
|
| 405 |
"""
|
| 406 |
Remove paper area from the mask to focus only on objects
|
| 407 |
"""
|
|
|
|
| 780 |
|
| 781 |
return padded
|
| 782 |
|
| 783 |
+
def predict_with_paper(image, paper_size, offset, finger_clearance=False):
|
| 784 |
"""Main prediction function using paper as reference"""
|
| 785 |
|
| 786 |
logger.info(f"Starting prediction with image shape: {image.shape}")
|
|
|
|
| 835 |
raise gr.Error(f"Error in object detection: {str(e)}")
|
| 836 |
|
| 837 |
# Apply edge rounding if specified
|
| 838 |
+
rounded_mask = objects_mask.copy()
|
|
|
|
|
|
|
|
|
|
| 839 |
|
| 840 |
# Apply dilation for offset
|
| 841 |
if offset > 0:
|
|
|
|
| 902 |
f"Scale: {scaling_factor:.4f} mm/px | Paper: {paper_size}"
|
| 903 |
)
|
| 904 |
|
| 905 |
+
def predict_full_paper(image, paper_size, offset_value_mm, enable_finger_cut, selected_outputs):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 906 |
finger_flag = "On" if enable_finger_cut == "On" else "Off"
|
| 907 |
|
| 908 |
# Always get all outputs from predict_with_paper
|
| 909 |
ann, outlines, dxf_path, mask, scale_info = predict_with_paper(
|
| 910 |
image,
|
| 911 |
paper_size,
|
| 912 |
+
offset=offset_value_mm,
|
|
|
|
|
|
|
| 913 |
finger_clearance=finger_flag,
|
| 914 |
)
|
| 915 |
|
|
|
|
| 956 |
)
|
| 957 |
|
| 958 |
with gr.Group():
|
| 959 |
+
gr.Markdown("### Contour Offset")
|
| 960 |
+
offset_value_mm = gr.Slider(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 961 |
minimum=0,
|
| 962 |
+
maximum=50,
|
| 963 |
step=1,
|
| 964 |
+
value=0,
|
| 965 |
+
label="Offset (mm)",
|
| 966 |
+
info="Expand contours outward by this amount"
|
|
|
|
| 967 |
)
|
| 968 |
|
| 969 |
with gr.Group():
|
|
|
|
| 996 |
outlines_image = gr.Image(label="Outlines", visible=False)
|
| 997 |
mask_image = gr.Image(label="Mask", visible=False)
|
| 998 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 999 |
def update_outputs_visibility(selected):
|
| 1000 |
return [
|
| 1001 |
gr.update(visible="Annotated Image" in selected),
|
|
|
|
| 1003 |
gr.update(visible="Mask" in selected)
|
| 1004 |
]
|
| 1005 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1006 |
|
| 1007 |
output_options.change(
|
| 1008 |
fn=update_outputs_visibility,
|
|
|
|
| 1015 |
inputs=[
|
| 1016 |
input_image,
|
| 1017 |
paper_size,
|
| 1018 |
+
offset_value_mm,
|
|
|
|
| 1019 |
enable_finger_cut,
|
| 1020 |
output_options
|
| 1021 |
],
|