import cv2 import numpy as np from typing import List, Tuple, Dict, Any def detect_cracks_and_holes(frame: np.ndarray) -> Tuple[List[Dict[str, Any]], np.ndarray]: """ Detect cracks and holes in the frame using edge detection and contour analysis. Args: frame: Input frame as a numpy array. Returns: Tuple of (list of detections, annotated frame). """ # Convert to grayscale gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # Apply Gaussian blur to reduce noise blurred = cv2.GaussianBlur(gray, (5, 5), 0) # Edge detection using Canny edges = cv2.Canny(blurred, 50, 150) # Dilate edges to connect nearby edges kernel = np.ones((3, 3), np.uint8) dilated = cv2.dilate(edges, kernel, iterations=1) # Find contours contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) detections = [] for i, contour in enumerate(contours): # Calculate the area of the contour area = cv2.contourArea(contour) if area < 100: # Ignore small contours continue # Get bounding box x, y, w, h = cv2.boundingRect(contour) x_min, y_min, x_max, y_max = x, y, x + w, y + h # Determine if it's a crack or hole based on shape and area perimeter = cv2.arcLength(contour, True) circularity = 4 * np.pi * area / (perimeter * perimeter) if perimeter > 0 else 0 # Classify as hole if more circular, crack if elongated dtype = "hole" if circularity > 0.5 else "crack" label = f"{dtype.capitalize()} {i+1}" # Determine severity based on area severity = "Severe" if area > 1000 else "Moderate" if area > 500 else "Mild" detections.append({ "box": [x_min, y_min, x_max, y_max], "label": label, "type": dtype, "severity": severity }) return detections, frame