mlbench123 commited on
Commit
ec13f98
·
verified ·
1 Parent(s): 2224ad2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -24
app.py CHANGED
@@ -70,6 +70,11 @@ class FingerCutOverlapError(Exception):
70
  def __init__(self, message="There was an overlap with fingercuts... Please try again to generate dxf."):
71
  super().__init__(message)
72
 
 
 
 
 
 
73
  # Global model variables for lazy loading
74
  paper_detector_global = None
75
  u2net_global = None
@@ -106,12 +111,16 @@ def get_paper_detector():
106
  if paper_detector_global is None:
107
  logger.info("Loading paper detector model...")
108
  if os.path.exists(paper_model_path):
109
- paper_detector_global = YOLO(paper_model_path)
 
 
 
 
 
110
  else:
111
  # Fallback to generic object detection for paper-like rectangles
112
- logger.warning("Using fallback paper detection")
113
  paper_detector_global = None
114
- logger.info("Paper detector loaded successfully")
115
  return paper_detector_global
116
 
117
  def get_u2net():
@@ -149,47 +158,70 @@ def detect_paper_contour(image: np.ndarray) -> Tuple[np.ndarray, float]:
149
  Detect paper in the image using contour detection as fallback
150
  Returns the paper contour and estimated scaling factor
151
  """
 
 
152
  # Convert to grayscale
153
  gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if len(image.shape) == 3 else image
154
 
155
- # Apply Gaussian blur
156
- blurred = cv2.GaussianBlur(gray, (5, 5), 0)
 
 
 
 
157
 
158
- # Edge detection
159
- edges = cv2.Canny(blurred, 50, 150)
 
 
 
 
 
 
160
 
161
  # Find contours
162
  contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
163
 
164
  # Filter contours by area and aspect ratio to find paper-like rectangles
165
  paper_contours = []
166
- min_area = (image.shape[0] * image.shape[1]) * 0.1 # At least 10% of image
 
 
167
 
168
  for contour in contours:
169
  area = cv2.contourArea(contour)
170
- if area > min_area:
171
  # Approximate contour to polygon
172
  epsilon = 0.02 * cv2.arcLength(contour, True)
173
  approx = cv2.approxPolyDP(contour, epsilon, True)
174
 
175
- # Check if it's roughly rectangular (4 corners)
176
  if len(approx) >= 4:
177
  # Calculate bounding rectangle
178
  rect = cv2.boundingRect(approx)
179
- aspect_ratio = rect[2] / rect[3] # width / height
 
180
 
181
  # Check if aspect ratio matches common paper ratios
182
  # A4: 1.414, A3: 1.414, US Letter: 1.294
183
- if 0.7 < aspect_ratio < 1.8: # Allow some tolerance
184
- paper_contours.append((contour, area, aspect_ratio))
 
 
 
 
 
185
 
186
  if not paper_contours:
187
- raise PaperNotDetectedError("Could not detect paper in the image")
 
188
 
189
- # Select the largest paper-like contour
190
- paper_contours.sort(key=lambda x: x[1], reverse=True)
191
  best_contour = paper_contours[0][0]
192
 
 
 
193
  return best_contour, 0.0 # Return 0.0 as placeholder scaling factor
194
 
195
  def detect_paper_bounds(image: np.ndarray, paper_size: str) -> Tuple[np.ndarray, float]:
@@ -201,13 +233,24 @@ def detect_paper_bounds(image: np.ndarray, paper_size: str) -> Tuple[np.ndarray,
201
 
202
  if paper_detector is not None:
203
  # Use trained model if available
204
- results = paper_detector.predict(image, conf=0.9)
205
- if not results or len(results) == 0 or len(results[0].boxes) == 0:
206
- logger.warning("Model detection failed, using fallback contour detection")
 
 
 
 
 
 
 
207
  return detect_paper_contour(image)
208
 
209
  # Get the largest detected paper
210
- boxes = results[0].cpu().boxes.xyxy
 
 
 
 
211
  largest_box = None
212
  max_area = 0
213
 
@@ -219,7 +262,8 @@ def detect_paper_bounds(image: np.ndarray, paper_size: str) -> Tuple[np.ndarray,
219
  largest_box = box
220
 
221
  if largest_box is None:
222
- raise PaperNotDetectedError("No paper detected by model")
 
223
 
224
  # Convert box to contour-like format
225
  x_min, y_min, x_max, y_max = map(int, largest_box)
@@ -230,8 +274,11 @@ def detect_paper_bounds(image: np.ndarray, paper_size: str) -> Tuple[np.ndarray,
230
  [[x_min, y_max]]
231
  ])
232
 
 
 
233
  else:
234
  # Use fallback contour detection
 
235
  paper_contour, _ = detect_paper_contour(image)
236
 
237
  # Calculate scaling factor based on paper size
@@ -241,7 +288,8 @@ def detect_paper_bounds(image: np.ndarray, paper_size: str) -> Tuple[np.ndarray,
241
 
242
  except Exception as e:
243
  logger.error(f"Error in paper detection: {e}")
244
- raise PaperNotDetectedError(f"Failed to detect paper: {str(e)}")
 
245
 
246
  def calculate_paper_scaling_factor(paper_contour: np.ndarray, paper_size: str) -> float:
247
  """
@@ -732,6 +780,9 @@ def make_square(img: np.ndarray):
732
  def predict_with_paper(image, paper_size, offset, offset_unit, edge_radius, finger_clearance=False):
733
  """Main prediction function using paper as reference"""
734
 
 
 
 
735
  if offset_unit == "inches":
736
  offset *= 25.4
737
 
@@ -743,15 +794,18 @@ def predict_with_paper(image, paper_size, offset, offset_unit, edge_radius, fing
743
 
744
  try:
745
  # Detect paper bounds and calculate scaling factor
 
746
  paper_contour, scaling_factor = detect_paper_bounds(image, paper_size)
747
- logger.info(f"Paper detected with scaling factor: {scaling_factor:.4f} mm/px")
748
 
749
- except PaperNotDetectedError as e:
 
750
  return (
751
  None, None, None, None,
752
  f"Error: {str(e)}"
753
  )
754
  except Exception as e:
 
755
  raise gr.Error(f"Error processing image: {str(e)}")
756
 
757
  try:
 
70
  def __init__(self, message="There was an overlap with fingercuts... Please try again to generate dxf."):
71
  super().__init__(message)
72
 
73
+ class ReferenceBoxNotDetectedError(Exception):
74
+ """Raised when reference box/paper cannot be detected"""
75
+ def __init__(self, message="Reference box not detected"):
76
+ super().__init__(message)
77
+
78
  # Global model variables for lazy loading
79
  paper_detector_global = None
80
  u2net_global = None
 
111
  if paper_detector_global is None:
112
  logger.info("Loading paper detector model...")
113
  if os.path.exists(paper_model_path):
114
+ try:
115
+ paper_detector_global = YOLO(paper_model_path)
116
+ logger.info("Paper detector loaded successfully")
117
+ except Exception as e:
118
+ logger.error(f"Failed to load paper detector: {e}")
119
+ paper_detector_global = None
120
  else:
121
  # Fallback to generic object detection for paper-like rectangles
122
+ logger.warning("Paper model file not found, using fallback detection")
123
  paper_detector_global = None
 
124
  return paper_detector_global
125
 
126
  def get_u2net():
 
158
  Detect paper in the image using contour detection as fallback
159
  Returns the paper contour and estimated scaling factor
160
  """
161
+ logger.info("Using contour-based paper detection")
162
+
163
  # Convert to grayscale
164
  gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if len(image.shape) == 3 else image
165
 
166
+ # Apply bilateral filter to reduce noise while preserving edges
167
+ filtered = cv2.bilateralFilter(gray, 9, 75, 75)
168
+
169
+ # Apply adaptive threshold
170
+ thresh = cv2.adaptiveThreshold(filtered, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
171
+ cv2.THRESH_BINARY, 11, 2)
172
 
173
+ # Edge detection with multiple thresholds
174
+ edges1 = cv2.Canny(filtered, 50, 150)
175
+ edges2 = cv2.Canny(filtered, 30, 100)
176
+ edges = cv2.bitwise_or(edges1, edges2)
177
+
178
+ # Morphological operations to connect broken edges
179
+ kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
180
+ edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
181
 
182
  # Find contours
183
  contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
184
 
185
  # Filter contours by area and aspect ratio to find paper-like rectangles
186
  paper_contours = []
187
+ image_area = image.shape[0] * image.shape[1]
188
+ min_area = image_area * 0.15 # At least 15% of image
189
+ max_area = image_area * 0.95 # At most 95% of image
190
 
191
  for contour in contours:
192
  area = cv2.contourArea(contour)
193
+ if min_area < area < max_area:
194
  # Approximate contour to polygon
195
  epsilon = 0.02 * cv2.arcLength(contour, True)
196
  approx = cv2.approxPolyDP(contour, epsilon, True)
197
 
198
+ # Check if it's roughly rectangular (4 corners) or close to it
199
  if len(approx) >= 4:
200
  # Calculate bounding rectangle
201
  rect = cv2.boundingRect(approx)
202
+ w, h = rect[2], rect[3]
203
+ aspect_ratio = w / h if h > 0 else 0
204
 
205
  # Check if aspect ratio matches common paper ratios
206
  # A4: 1.414, A3: 1.414, US Letter: 1.294
207
+ if 0.6 < aspect_ratio < 2.0: # More lenient tolerance
208
+ # Check if contour area is close to bounding rect area (rectangularity)
209
+ rect_area = w * h
210
+ if rect_area > 0:
211
+ extent = area / rect_area
212
+ if extent > 0.7: # At least 70% rectangular
213
+ paper_contours.append((contour, area, aspect_ratio, extent))
214
 
215
  if not paper_contours:
216
+ logger.error("No paper-like contours found")
217
+ raise ReferenceBoxNotDetectedError("Could not detect paper in the image using contour detection")
218
 
219
+ # Select the best paper contour based on area and rectangularity
220
+ paper_contours.sort(key=lambda x: (x[1] * x[3]), reverse=True) # Sort by area * extent
221
  best_contour = paper_contours[0][0]
222
 
223
+ logger.info(f"Paper detected using contours: area={paper_contours[0][1]}, aspect_ratio={paper_contours[0][2]:.2f}")
224
+
225
  return best_contour, 0.0 # Return 0.0 as placeholder scaling factor
226
 
227
  def detect_paper_bounds(image: np.ndarray, paper_size: str) -> Tuple[np.ndarray, float]:
 
233
 
234
  if paper_detector is not None:
235
  # Use trained model if available
236
+ # FIXED: Add verbose=False to suppress prints, and use proper confidence threshold
237
+ results = paper_detector.predict(image, conf=0.3, verbose=False) # Lower confidence threshold
238
+
239
+ if not results or len(results) == 0:
240
+ logger.warning("No results from paper detector")
241
+ return detect_paper_contour(image)
242
+
243
+ # Check if boxes exist and are not empty
244
+ if not hasattr(results[0], 'boxes') or results[0].boxes is None or len(results[0].boxes) == 0:
245
+ logger.warning("No boxes detected by model, using fallback contour detection")
246
  return detect_paper_contour(image)
247
 
248
  # Get the largest detected paper
249
+ boxes = results[0].boxes.xyxy.cpu().numpy() # Convert to numpy
250
+ if len(boxes) == 0:
251
+ logger.warning("Empty boxes detected, using fallback")
252
+ return detect_paper_contour(image)
253
+
254
  largest_box = None
255
  max_area = 0
256
 
 
262
  largest_box = box
263
 
264
  if largest_box is None:
265
+ logger.warning("No valid paper box found, using fallback")
266
+ return detect_paper_contour(image)
267
 
268
  # Convert box to contour-like format
269
  x_min, y_min, x_max, y_max = map(int, largest_box)
 
274
  [[x_min, y_max]]
275
  ])
276
 
277
+ logger.info(f"Paper detected by model: {x_min},{y_min} to {x_max},{y_max}")
278
+
279
  else:
280
  # Use fallback contour detection
281
+ logger.info("Using fallback contour detection for paper")
282
  paper_contour, _ = detect_paper_contour(image)
283
 
284
  # Calculate scaling factor based on paper size
 
288
 
289
  except Exception as e:
290
  logger.error(f"Error in paper detection: {e}")
291
+ # Instead of raising PaperNotDetectedError, raise ReferenceBoxNotDetectedError
292
+ raise ReferenceBoxNotDetectedError(f"Failed to detect paper: {str(e)}")
293
 
294
  def calculate_paper_scaling_factor(paper_contour: np.ndarray, paper_size: str) -> float:
295
  """
 
780
  def predict_with_paper(image, paper_size, offset, offset_unit, edge_radius, finger_clearance=False):
781
  """Main prediction function using paper as reference"""
782
 
783
+ logger.info(f"Starting prediction with image shape: {image.shape}")
784
+ logger.info(f"Paper size: {paper_size}, Edge radius: {edge_radius}")
785
+
786
  if offset_unit == "inches":
787
  offset *= 25.4
788
 
 
794
 
795
  try:
796
  # Detect paper bounds and calculate scaling factor
797
+ logger.info("Starting paper detection...")
798
  paper_contour, scaling_factor = detect_paper_bounds(image, paper_size)
799
+ logger.info(f"Paper detected successfully with scaling factor: {scaling_factor:.4f} mm/px")
800
 
801
+ except ReferenceBoxNotDetectedError as e:
802
+ logger.error(f"Paper detection failed: {e}")
803
  return (
804
  None, None, None, None,
805
  f"Error: {str(e)}"
806
  )
807
  except Exception as e:
808
+ logger.error(f"Unexpected error in paper detection: {e}")
809
  raise gr.Error(f"Error processing image: {str(e)}")
810
 
811
  try: