Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -368,6 +368,20 @@ def detect_paper_bounds(image: np.ndarray, paper_size: str, output_unit: str = "
|
|
| 368 |
# Use fallback contour detection
|
| 369 |
logger.info("Using fallback contour detection for paper")
|
| 370 |
paper_contour, _ = detect_paper_contour(image, output_unit)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 371 |
|
| 372 |
# Calculate scaling factor based on paper size with proper units
|
| 373 |
scaling_factor = calculate_paper_scaling_factor(paper_contour, paper_size, output_unit)
|
|
@@ -529,24 +543,20 @@ def mask_paper_area_in_image(image: np.ndarray, paper_contour: np.ndarray) -> np
|
|
| 529 |
return masked_image
|
| 530 |
|
| 531 |
def exclude_paper_area(mask: np.ndarray, paper_contour: np.ndarray, expansion_factor: float = 1.2) -> np.ndarray:
|
| 532 |
-
"""
|
| 533 |
-
Remove paper area from the mask to focus only on objects using soft boundaries
|
| 534 |
-
"""
|
| 535 |
# Create paper mask
|
| 536 |
paper_mask = np.zeros(mask.shape[:2], dtype=np.uint8)
|
| 537 |
cv2.fillPoly(paper_mask, [paper_contour], 255)
|
| 538 |
|
| 539 |
-
#
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
eroded_paper_mask = cv2.erode(paper_mask, kernel, iterations=2)
|
| 543 |
|
| 544 |
-
|
| 545 |
-
|
| 546 |
|
| 547 |
-
#
|
| 548 |
-
|
| 549 |
-
result_mask = cv2.morphologyEx(result_mask, cv2.MORPH_CLOSE, small_kernel, iterations=1)
|
| 550 |
|
| 551 |
return result_mask
|
| 552 |
|
|
@@ -964,51 +974,31 @@ def predict_with_paper(image, paper_size, offset, offset_unit, finger_clearance=
|
|
| 964 |
raise gr.Error(f"Error processing image: {str(e)}")
|
| 965 |
|
| 966 |
try:
|
| 967 |
-
#
|
| 968 |
-
|
|
|
|
| 969 |
|
| 970 |
-
|
| 971 |
-
|
| 972 |
-
|
| 973 |
-
|
| 974 |
-
|
| 975 |
-
|
| 976 |
-
|
| 977 |
-
|
| 978 |
-
|
| 979 |
-
|
| 980 |
-
if not results or len(results) == 0 or not hasattr(results[0], 'boxes') or len(results[0].boxes) == 0:
|
| 981 |
-
logger.warning("No objects detected by YOLOv8, proceeding with full image")
|
| 982 |
-
cropped_image = image
|
| 983 |
-
crop_offset = (0, 0)
|
| 984 |
-
else:
|
| 985 |
-
boxes = results[0].boxes.xyxy.cpu().numpy()
|
| 986 |
-
confidences = results[0].boxes.conf.cpu().numpy()
|
| 987 |
-
|
| 988 |
-
# Filter out very large boxes (likely paper/background)
|
| 989 |
-
image_area = image.shape[0] * image.shape[1]
|
| 990 |
-
valid_boxes = []
|
| 991 |
-
|
| 992 |
-
for i, box in enumerate(boxes):
|
| 993 |
-
x_min, y_min, x_max, y_max = box
|
| 994 |
-
box_area = (x_max - x_min) * (y_max - y_min)
|
| 995 |
-
# Keep boxes that are 5% to 40% of image area
|
| 996 |
-
if 0.001 * image_area < box_area < 0.6 * image_area:
|
| 997 |
-
valid_boxes.append((i, confidences[i]))
|
| 998 |
-
|
| 999 |
-
if not valid_boxes:
|
| 1000 |
-
logger.warning("No valid objects detected, proceeding with full image")
|
| 1001 |
-
cropped_image = image
|
| 1002 |
-
crop_offset = (0, 0)
|
| 1003 |
-
else:
|
| 1004 |
-
# Get highest confidence valid box
|
| 1005 |
-
best_idx = max(valid_boxes, key=lambda x: x[1])[0]
|
| 1006 |
-
x_min, y_min, x_max, y_max = map(int, boxes[best_idx])
|
| 1007 |
|
| 1008 |
-
# Remove background
|
| 1009 |
-
orig_size = image.shape[:2]
|
| 1010 |
objects_mask = remove_bg(cropped_image)
|
| 1011 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1012 |
|
| 1013 |
# Resize mask to match cropped region and place back in original image space
|
| 1014 |
full_mask = np.zeros((orig_size[0], orig_size[1]), dtype=np.uint8)
|
|
|
|
| 368 |
# Use fallback contour detection
|
| 369 |
logger.info("Using fallback contour detection for paper")
|
| 370 |
paper_contour, _ = detect_paper_contour(image, output_unit)
|
| 371 |
+
|
| 372 |
+
# After getting paper_contour, expand it
|
| 373 |
+
rect = cv2.boundingRect(paper_contour)
|
| 374 |
+
expansion = int(min(rect[2], rect[3]) * 0.1) # Expand by 10%
|
| 375 |
+
|
| 376 |
+
x, y, w, h = rect
|
| 377 |
+
expanded_contour = np.array([
|
| 378 |
+
[[max(0, x - expansion), max(0, y - expansion)]],
|
| 379 |
+
[[min(image.shape[1], x + w + expansion), max(0, y - expansion)]],
|
| 380 |
+
[[min(image.shape[1], x + w + expansion), min(image.shape[0], y + h + expansion)]],
|
| 381 |
+
[[max(0, x - expansion), min(image.shape[0], y + h + expansion)]]
|
| 382 |
+
])
|
| 383 |
+
|
| 384 |
+
paper_contour = expanded_contour
|
| 385 |
|
| 386 |
# Calculate scaling factor based on paper size with proper units
|
| 387 |
scaling_factor = calculate_paper_scaling_factor(paper_contour, paper_size, output_unit)
|
|
|
|
| 543 |
return masked_image
|
| 544 |
|
| 545 |
def exclude_paper_area(mask: np.ndarray, paper_contour: np.ndarray, expansion_factor: float = 1.2) -> np.ndarray:
|
| 546 |
+
"""Less aggressive paper area exclusion"""
|
|
|
|
|
|
|
| 547 |
# Create paper mask
|
| 548 |
paper_mask = np.zeros(mask.shape[:2], dtype=np.uint8)
|
| 549 |
cv2.fillPoly(paper_mask, [paper_contour], 255)
|
| 550 |
|
| 551 |
+
# Instead of eroding, slightly expand the paper mask
|
| 552 |
+
rect = cv2.boundingRect(paper_contour)
|
| 553 |
+
expansion = max(10, int(min(rect[2], rect[3]) * 0.02)) # 2% expansion
|
|
|
|
| 554 |
|
| 555 |
+
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (expansion, expansion))
|
| 556 |
+
expanded_paper_mask = cv2.dilate(paper_mask, kernel, iterations=1)
|
| 557 |
|
| 558 |
+
# Keep objects within expanded paper area
|
| 559 |
+
result_mask = cv2.bitwise_and(mask, expanded_paper_mask)
|
|
|
|
| 560 |
|
| 561 |
return result_mask
|
| 562 |
|
|
|
|
| 974 |
raise gr.Error(f"Error processing image: {str(e)}")
|
| 975 |
|
| 976 |
try:
|
| 977 |
+
# Get paper bounds with expansion
|
| 978 |
+
rect = cv2.boundingRect(paper_contour)
|
| 979 |
+
expansion = max(20, int(min(rect[2], rect[3]) * 0.05)) # 5% expansion
|
| 980 |
|
| 981 |
+
x, y, w, h = rect
|
| 982 |
+
x_min = max(0, x - expansion)
|
| 983 |
+
y_min = max(0, y - expansion)
|
| 984 |
+
x_max = min(image.shape[1], x + w + expansion)
|
| 985 |
+
y_max = min(image.shape[0], y + h + expansion)
|
| 986 |
+
|
| 987 |
+
# Process the expanded paper area
|
| 988 |
+
cropped_image = image[y_min:y_max, x_min:x_max]
|
| 989 |
+
crop_offset = (x_min, y_min)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 990 |
|
| 991 |
+
# Remove background
|
|
|
|
| 992 |
objects_mask = remove_bg(cropped_image)
|
| 993 |
+
|
| 994 |
+
# Place back in full image space
|
| 995 |
+
full_mask = np.zeros((image.shape[0], image.shape[1]), dtype=np.uint8)
|
| 996 |
+
full_mask[y_min:y_max, x_min:x_max] = objects_mask
|
| 997 |
+
|
| 998 |
+
# Light filtering only - don't exclude paper area aggressively
|
| 999 |
+
# Just remove small noise
|
| 1000 |
+
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
|
| 1001 |
+
objects_mask = cv2.morphologyEx(full_mask, cv2.MORPH_OPEN, kernel)
|
| 1002 |
|
| 1003 |
# Resize mask to match cropped region and place back in original image space
|
| 1004 |
full_mask = np.zeros((orig_size[0], orig_size[1]), dtype=np.uint8)
|