| import numpy as np | |
| import cv2 | |
| from skimage.morphology import skeletonize | |
| def crack_metrics(mask: np.ndarray): | |
| # mask: [H,W] 0/1 | |
| area_px = int(mask.sum()) | |
| # largest connected component | |
| num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(mask.astype(np.uint8), connectivity=8) | |
| largest_cc = 0 | |
| if num_labels > 1: | |
| # ignore background label 0 | |
| largest_cc = int(stats[1:, cv2.CC_STAT_AREA].max()) | |
| # length estimate via skeletonization | |
| skel = skeletonize(mask.astype(bool)) | |
| length_px = int(skel.sum()) | |
| return { | |
| "area_px": area_px, | |
| "length_px": length_px, | |
| "largest_component_px": largest_cc, | |
| } | |
| def severity_from_metrics(m): | |
| # Tune these thresholds on your validation set | |
| area = m["area_px"] | |
| length = m["length_px"] | |
| largest = m["largest_component_px"] | |
| score = 0 | |
| if area > 1500: score += 1 | |
| if area > 6000: score += 1 | |
| if length > 600: score += 1 | |
| if length > 2000: score += 1 | |
| if largest > 2500: score += 1 | |
| if score >= 4: | |
| return "High" | |
| if score >= 2: | |
| return "Medium" | |
| return "Low" |