ishanprogs commited on
Commit
aa47bc2
·
verified ·
1 Parent(s): a65d531

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -31
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py (Complete Final Version - Fixed resize_masks SyntaxError)
2
 
3
  import gradio as gr
4
  import torch
@@ -167,6 +167,7 @@ def classify_image_clip(image_pil):
167
  traceback.print_exc()
168
  return "Error during CLIP processing", {"Error": 1.0}
169
 
 
170
  def process_car_image(image_np_bgr, damage_threshold, part_threshold):
171
  """
172
  Runs damage and part segmentation (YOLOv8), calculates overlap, visualizes.
@@ -215,38 +216,21 @@ def process_car_image(image_np_bgr, damage_threshold, part_threshold):
215
  yolo_end_time = time.time()
216
  logger.info(f" YOLO predictions took {yolo_end_time - yolo_start_time:.2f}s")
217
 
218
- # --- 3. Resize Masks (Corrected Function Definition) ---
219
  def resize_masks(masks_tensor, target_h, target_w):
220
  """Resizes masks tensor to target H, W using CPU numpy and OpenCV."""
221
- masks_np_bool = masks_tensor.cpu().numpy().astype(bool) # Move to CPU *before* resizing
222
-
223
- # Handle empty tensor or already correct size
224
- if masks_np_bool.shape[0] == 0:
225
- return np.array([]) # Return empty numpy array
226
- if masks_np_bool.ndim == 3 and masks_np_bool.shape[1] == target_h and masks_np_bool.shape[2] == target_w:
227
- return masks_np_bool # Return if already correct size
228
-
229
- # Ensure masks_np_bool is 3D [N, H, W] even if only one mask
230
- if masks_np_bool.ndim == 2: # Handle case of single mask output [H, W]
231
- masks_np_bool = np.expand_dims(masks_np_bool, axis=0)
232
- logger.warning("Detected 2D mask input, expanding to 3D for resize loop.")
233
-
234
- # Check dimensions *before* logging resize message
235
- if masks_np_bool.ndim != 3:
236
- logger.error(f"Unexpected mask dimension: {masks_np_bool.ndim}. Expected 3D [N, H, W]. Cannot resize.")
237
- return np.array([]) # Return empty if shape is wrong
238
-
239
- # Proceed with resizing if necessary
240
- # logger.info(f"Resizing {masks_np_bool.shape[0]} masks from {masks_np_bool.shape[1:]} to {(target_h, target_w)}") # Optional verbose log
241
  resized_masks_list = []
242
  for i in range(masks_np_bool.shape[0]):
243
- mask = masks_np_bool[i] # Get the single [H, W] mask
244
- # Resize needs uint8
245
  mask_resized = cv2.resize(mask.astype(np.uint8), (target_w, target_h), interpolation=cv2.INTER_NEAREST)
246
- resized_masks_list.append(mask_resized.astype(bool)) # Append boolean mask
247
- return np.array(resized_masks_list) # Return numpy array [N, target_h, target_w]
248
 
249
- # --- Perform resizing ---
250
  resize_start_time = time.time()
251
  damage_masks_np = resize_masks(damage_masks_raw, img_h, img_w)
252
  part_masks_np = resize_masks(part_masks_raw, img_h, img_w)
@@ -259,10 +243,61 @@ def process_car_image(image_np_bgr, damage_threshold, part_threshold):
259
  overlap_start_time = time.time()
260
  if damage_masks_np.shape[0] > 0 and part_masks_np.shape[0] > 0:
261
  overlap_threshold = 0.4
262
- for i in range(len(damage_masks_np)):
263
- damage_mask = damage_masks_np[i]; damage_class_id = damage_classes_ids_cpu[i]; try: damage_name = DAMAGE_CLASSES[damage_class_id]; except IndexError: logger.warning(f"Invalid damage ID {damage_class_id} during overlap"); continue; damage_area = np.sum(damage_mask); if damage_area < 10: continue; max_overlap = 0; assigned_part_name = "Unknown / Outside Parts";
264
- for j in range(len(part_masks_np)): part_mask = part_masks_np[j]; part_class_id = part_classes_ids_cpu[j]; try: part_name = CAR_PART_CLASSES[part_class_id]; except IndexError: logger.warning(f"Invalid part ID {part_class_id} during overlap"); continue; intersection = np.logical_and(damage_mask, part_mask); overlap_ratio = np.sum(intersection) / damage_area if damage_area > 0 else 0; if overlap_ratio > max_overlap: max_overlap = overlap_ratio; if max_overlap >= overlap_threshold: assigned_part_name = part_name;
265
- assignment_desc = f"{damage_name} in {assigned_part_name}"; if assigned_part_name == "Unknown / Outside Parts": assignment_desc += f" (Overlap < {overlap_threshold*100:.0f}%)"; final_assignments.append(assignment_desc)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  elif damage_masks_np.shape[0] > 0: final_assignments.append(f"{len(damage_masks_np)} damages found, but no parts detected/matched above threshold {part_threshold}.")
267
  elif part_masks_np.shape[0] > 0: final_assignments.append(f"No damages detected above threshold {damage_threshold}.")
268
  else: final_assignments.append(f"No damages or parts detected above thresholds.")
 
1
+ # app.py (Complete Final Version - Fixed SyntaxError in overlap loop)
2
 
3
  import gradio as gr
4
  import torch
 
167
  traceback.print_exc()
168
  return "Error during CLIP processing", {"Error": 1.0}
169
 
170
+ # --- CORRECTED process_car_image Function ---
171
  def process_car_image(image_np_bgr, damage_threshold, part_threshold):
172
  """
173
  Runs damage and part segmentation (YOLOv8), calculates overlap, visualizes.
 
216
  yolo_end_time = time.time()
217
  logger.info(f" YOLO predictions took {yolo_end_time - yolo_start_time:.2f}s")
218
 
219
+ # --- 3. Resize Masks ---
220
  def resize_masks(masks_tensor, target_h, target_w):
221
  """Resizes masks tensor to target H, W using CPU numpy and OpenCV."""
222
+ masks_np_bool = masks_tensor.cpu().numpy().astype(bool)
223
+ if masks_np_bool.shape[0] == 0: return np.array([])
224
+ if masks_np_bool.ndim == 3 and masks_np_bool.shape[1] == target_h and masks_np_bool.shape[2] == target_w: return masks_np_bool
225
+ if masks_np_bool.ndim == 2: masks_np_bool = np.expand_dims(masks_np_bool, axis=0)
226
+ if masks_np_bool.ndim != 3: logger.error(f"Unexpected mask dim: {masks_np_bool.ndim}"); return np.array([])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  resized_masks_list = []
228
  for i in range(masks_np_bool.shape[0]):
229
+ mask = masks_np_bool[i]
 
230
  mask_resized = cv2.resize(mask.astype(np.uint8), (target_w, target_h), interpolation=cv2.INTER_NEAREST)
231
+ resized_masks_list.append(mask_resized.astype(bool))
232
+ return np.array(resized_masks_list)
233
 
 
234
  resize_start_time = time.time()
235
  damage_masks_np = resize_masks(damage_masks_raw, img_h, img_w)
236
  part_masks_np = resize_masks(part_masks_raw, img_h, img_w)
 
243
  overlap_start_time = time.time()
244
  if damage_masks_np.shape[0] > 0 and part_masks_np.shape[0] > 0:
245
  overlap_threshold = 0.4
246
+ # *** CORRECTED OVERLAP LOOP SYNTAX ***
247
+ for i in range(len(damage_masks_np)): # Iterate through each detected damage
248
+ damage_mask = damage_masks_np[i]
249
+ damage_class_id = damage_classes_ids_cpu[i]
250
+
251
+ # Try getting damage name, skip if ID is invalid
252
+ try:
253
+ damage_name = DAMAGE_CLASSES[damage_class_id]
254
+ except IndexError:
255
+ logger.warning(f"Invalid damage ID {damage_class_id} found during overlap check. Skipping this damage.")
256
+ continue # Go to the next damage mask
257
+
258
+ # Check damage area (only if name was valid)
259
+ damage_area = np.sum(damage_mask)
260
+ if damage_area < 10: # Skip tiny masks
261
+ continue
262
+
263
+ # Initialize for finding best overlapping part for this damage
264
+ max_overlap = 0
265
+ assigned_part_name = "Unknown / Outside Parts" # Default
266
+
267
+ # Inner loop for parts
268
+ for j in range(len(part_masks_np)):
269
+ part_mask = part_masks_np[j]
270
+ part_class_id = part_classes_ids_cpu[j]
271
+ try:
272
+ part_name = CAR_PART_CLASSES[part_class_id]
273
+ except IndexError:
274
+ logger.warning(f"Invalid part ID {part_class_id} during overlap check.")
275
+ continue # Skip this part
276
+
277
+ intersection = np.logical_and(damage_mask, part_mask)
278
+ overlap_ratio = np.sum(intersection) / damage_area if damage_area > 0 else 0
279
+
280
+ if overlap_ratio > max_overlap:
281
+ max_overlap = overlap_ratio
282
+ # Assign based on threshold condition within the inner loop
283
+ if max_overlap >= overlap_threshold:
284
+ assigned_part_name = part_name
285
+ else:
286
+ # If max overlap is < threshold even for best part, keep default
287
+ assigned_part_name = "Unknown / Outside Parts"
288
+
289
+
290
+ # Append assignment result after checking all parts for the current damage
291
+ assignment_desc = f"{damage_name} in {assigned_part_name}"
292
+ if assigned_part_name == "Unknown / Outside Parts" and max_overlap > 0: # Add detail if there was *some* overlap
293
+ assignment_desc += f" (Max Overlap < {overlap_threshold*100:.0f}%)"
294
+ elif assigned_part_name == "Unknown / Outside Parts":
295
+ assignment_desc += " (No overlap)" # Clarify if zero overlap
296
+
297
+ final_assignments.append(assignment_desc)
298
+ # *** END OF CORRECTED OVERLAP LOOP ***
299
+
300
+ # Handle cases with no damages or no parts
301
  elif damage_masks_np.shape[0] > 0: final_assignments.append(f"{len(damage_masks_np)} damages found, but no parts detected/matched above threshold {part_threshold}.")
302
  elif part_masks_np.shape[0] > 0: final_assignments.append(f"No damages detected above threshold {damage_threshold}.")
303
  else: final_assignments.append(f"No damages or parts detected above thresholds.")