silversurfer343/ComputerVisionProject
0
1import streamlit as st
2import cv2
3import numpy as np
4from skimage.segmentation import slic, mark_boundaries, find_boundaries
5from skimage.util import img_as_float
6from skimage import measure
7from PIL import Image
8import io
9import torch
10import torchvision
11from torchvision.models.segmentation import deeplabv3_resnet50
12import matplotlib.pyplot as plt
13import os
14from pathlib import Path
15from scipy import stats
16from scipy.ndimage import binary_fill_holes
17from skimage.morphology import remove_small_objects, remove_small_holes
18from skimage.measure import label, regionprops
19from sklearn.mixture import GaussianMixture
20from sklearn.preprocessing import StandardScaler
21from huggingface_hub import hf_hub_download
22
23def main():
24 st.set_page_config(page_title="Wall Crack Detection", layout="wide")
25 st.title("🧱 Wall Detection & Segmentation")
26
27 # Create tabs for different methods
28 tab1, tab2 = st.tabs(["Traditional CV Approach", "Deep Learning Approach"])
29
30 @st.cache_data
31 def load_image(image_file):
32 # Convert uploaded file to a cv2 image
33 file_bytes = np.asarray(bytearray(image_file.read()), dtype=np.uint8)
34 img = cv2.imdecode(file_bytes, 1)
35 return img
36
37 def process_image_traditional(img):
38 # Make copies for later overlays
39 img_orig = img.copy()
40
41 # Convert to grayscale
42 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
43
44 # -------------------------------
45 # Harris Corner Detection
46 # -------------------------------
47 harris = cv2.cornerHarris(np.float32(gray), blockSize=2, ksize=3, k=0.04)
48 harris = cv2.dilate(harris, None)
49 harris_img = img.copy()
50 harris_img[harris > 0.01 * harris.max()] = [255, 0, 0] # Mark corners in red
51
52 # Create binary mask for harris corners for comparison
53 harris_mask = np.zeros_like(gray, dtype=np.uint8)
54 harris_mask[harris > 0.01 * harris.max()] = 255
55
56 # -------------------------------
57 # Canny + Morphological Closing
58 # -------------------------------
59 edges = cv2.Canny(gray, 50, 120)
60 kernel = np.ones((3, 3), np.uint8)
61 closed_edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
62
63 # -------------------------------
64 # HoughLinesP Detection
65 # -------------------------------
66 hough_img = img.copy()
67 hough_mask = np.zeros_like(gray, dtype=np.uint8)
68 lines = cv2.HoughLinesP(closed_edges, rho=1, theta=np.pi/180,
69 threshold=50, minLineLength=28, maxLineGap=15)
70 if lines is not None:
71 for line in lines:
72 x1, y1, x2, y2 = line[0]
73 cv2.line(hough_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
74 cv2.line(hough_mask, (x1, y1), (x2, y2), 255, 2)
75
76 # -------------------------------
77 # Region-based Segmentation (Contours)
78 # -------------------------------
79 contours, _ = cv2.findContours(closed_edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
80 region_seg_img = img.copy()
81 region_mask = np.zeros_like(gray, dtype=np.uint8)
82 for cnt in contours:
83 area = cv2.contourArea(cnt)
84 if area > 50: # adjust the area threshold as needed
85 x, y, w, h = cv2.boundingRect(cnt)
86 cv2.rectangle(region_seg_img, (x, y), (x + w, y + h), (255, 0, 0), 2)
87 cv2.rectangle(region_mask, (x, y), (x + w, y + h), 255, 2)
88
89 # -------------------------------
90 # Crack Detection Logic (Hough + Region)
91 # -------------------------------
92 hough_detected = lines is not None and len(lines) > 0
93 region_detected = False
94 for cnt in contours:
95 if cv2.contourArea(cnt) > 100: # more stringent area threshold for detection
96 region_detected = True
97 break
98 traditional_prediction = 1 if (hough_detected or region_detected) else 0
99
100 return harris_img, closed_edges, hough_img, region_seg_img, harris_mask, hough_mask, region_mask, traditional_prediction
101
102 def process_image_slic(img):
103 # Make a copy of the original image
104 img_orig = img.copy()
105
106 # First, perform the same basic processing for Hough detection:
107 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
108 edges = cv2.Canny(gray, 50, 120)
109 kernel = np.ones((3, 3), np.uint8)
110 closed_edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
111 lines1 = cv2.HoughLinesP(closed_edges, rho=1, theta=np.pi/180,
112 threshold=50, minLineLength=28, maxLineGap=15)
113 hough_detected = lines1 is not None and len(lines1) > 0
114
115 # Now, perform SLIC segmentation on the original image.
116 # Convert image to RGB float (as required by skimage)
117 img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
118 img_float = img_as_float(img_rgb)
119
120 # Run SLIC with chosen parameters; adjust n_segments for granularity
121 segments = slic(img_float, n_segments=200, compactness=10, sigma=1, start_label=1)
122
123 # Mark boundaries for visualization
124 slic_img = mark_boundaries(img_float, segments)
125
126 # Extract boundaries from the SLIC segmentation
127 boundaries = find_boundaries(segments, mode='outer').astype(np.uint8) * 255
128
129 # Run Hough Transform on the boundary mask
130 hough_lines_img = img.copy()
131 slic_mask = np.zeros_like(gray, dtype=np.uint8)
132 lines2 = cv2.HoughLinesP(boundaries, rho=1, theta=np.pi/180,
133 threshold=50, minLineLength=28, maxLineGap=15)
134
135 if lines2 is not None:
136 for line in lines2:
137 x1, y1, x2, y2 = line[0]
138 cv2.line(hough_lines_img, (x1, y1), (x2, y2), (0, 0, 255), 2)
139 cv2.line(slic_mask, (x1, y1), (x2, y2), 255, 2)
140
141 slic_detected = lines2 is not None and len(lines2) > 0
142
143 # Union logic: if either Hough on closed_edges or on SLIC boundaries detects a crack, return 1.
144 prediction = 1 if (hough_detected or slic_detected) else 0
145
146 return slic_img, hough_lines_img, slic_mask, prediction
147
148 # GMM segmentation function
149 def apply_gmm_binary_mask(image, k=2):
150 image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
151 gray = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2GRAY)
152
153 pixels = gray.reshape(-1, 1)
154 scaler = StandardScaler()
155 pixels_scaled = scaler.fit_transform(pixels)
156
157 gmm = GaussianMixture(n_components=k, random_state=42)
158 labels = gmm.fit_predict(pixels_scaled)
159 labels_image = labels.reshape(gray.shape)
160
161 class_means = [pixels[labels == i].mean() for i in range(k)]
162 crack_class = np.argmin(class_means)
163
164 binary_mask = (labels_image == crack_class).astype(np.uint8) * 255
165 return image_rgb, binary_mask
166
167 # Function to get DeepLabV3 model
168 @st.cache_resource
169 def load_dl_model():
170 # Initialize the DeepLabV3 model
171 model = torchvision.models.segmentation.deeplabv3_resnet50(pretrained=False)
172 # Modify the classifier to output 1 class (binary segmentation)
173 model.classifier[4] = torch.nn.Conv2d(256, 1, kernel_size=(1, 1), stride=(1, 1))
174
175 # Load pretrained weights
176 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
177 model_path = os.path.join(os.path.dirname(__file__), "deepnetv3.pth")
178
179 try:
180 # First try loading from local file
181 if os.path.exists(model_path):
182 model.load_state_dict(torch.load(model_path, map_location=device), strict=False)
183 else:
184 # If local file doesn't exist, try downloading from Hugging Face
185 model_path = hf_hub_download(
186 repo_id="silversurfer343/deepnet", # Replace with your actual username
187 filename="deepnetv3.pth"
188 )
189 model.load_state_dict(torch.load(model_path, map_location=device), strict=False)
190
191 model.to(device)
192 model.eval()
193 return model, device, True
194 except Exception as e:
195 st.error(f"Error loading model: {e}")
196 return model, device, False
197
198 # Function to preprocess image for deep learning
199 def preprocess_image_dl(img):
200 # Resize image to 512x512 (or the size expected by your model)
201 img_resized = cv2.resize(img, (512, 512))
202 # Convert BGR to RGB
203 img_rgb = cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB)
204 # Convert to tensor and normalize
205 img_tensor = torch.from_numpy(img_rgb).float().permute(2, 0, 1) / 255.0
206 # Add batch dimension
207 img_batch = img_tensor.unsqueeze(0)
208 return img_batch, img_rgb
209
210 # Function to predict mask using the pretrained model
211 def predict_mask(model, img_tensor, device, threshold=0.5):
212 img_tensor = img_tensor.to(device)
213 with torch.no_grad():
214 output = model(img_tensor)['out']
215 pred = torch.sigmoid(output)
216 pred_bin = (pred > threshold).float()
217 return pred_bin
218
219 # ------------------- Refinement Methods -------------------
220
221 # 1. SLIC-based Refinement
222 def generate_superpixels(image, n_segments=300, compactness=10):
223 # image must be in HWC format and float
224 if image.dtype != np.float32:
225 image = image.astype(np.float32) / 255.0
226
227 superpixels = slic(image, n_segments=n_segments, compactness=compactness, start_label=0)
228 return superpixels
229
230 def refine_with_superpixels(pred_mask, superpixels):
231 refined_mask = np.zeros_like(pred_mask)
232
233 for label in np.unique(superpixels):
234 region_mask = (superpixels == label)
235 majority_vote = stats.mode(pred_mask[region_mask].flatten(), keepdims=False).mode
236 refined_mask[region_mask] = majority_vote
237
238 return refined_mask
239
240 # 2. Region-based Split and Merge Refinement
241 def region_based_split_and_merge(image, pred_mask, split_variance_thresh=0.01, merge_similarity_thresh=0.1, min_region_size=50):
242 image_gray = cv2.cvtColor((image * 255).astype(np.uint8), cv2.COLOR_RGB2GRAY)
243 labeled_mask = label(pred_mask)
244
245 region_map = np.zeros_like(pred_mask)
246
247 # Region Splitting
248 label_counter = 1
249 for region in regionprops(labeled_mask):
250 coords = region.coords
251 region_intensity = image_gray[tuple(zip(*coords))]
252 region_variance = np.var(region_intensity)
253
254 if region_variance > split_variance_thresh and len(coords) > 10:
255 # split into 2 by k-means clustering
256 pixels = np.array([image[coord[0], coord[1]] for coord in coords])
257 pixels = np.float32(pixels)
258
259 if len(pixels) > 1: # Ensure there are enough pixels for clustering
260 try:
261 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
262 _, labels, _ = cv2.kmeans(pixels, 2, None, criteria, 3, cv2.KMEANS_RANDOM_CENTERS)
263 labels = labels.flatten()
264 for i, coord in enumerate(coords):
265 region_map[coord[0], coord[1]] = label_counter + labels[i]
266 label_counter += 2
267 except:
268 # Fallback if kmeans fails
269 for coord in coords:
270 region_map[coord[0], coord[1]] = label_counter
271 label_counter += 1
272 else:
273 for coord in coords:
274 region_map[coord[0], coord[1]] = label_counter
275 label_counter += 1
276 else:
277 for coord in coords:
278 region_map[coord[0], coord[1]] = label_counter
279 label_counter += 1
280
281 # Region Merging
282 final_map = region_map.copy()
283 merged = set()
284 for label1 in np.unique(region_map):
285 if label1 == 0 or label1 in merged:
286 continue
287
288 mask1 = (region_map == label1)
289 if np.sum(mask1) == 0: # Skip empty regions
290 continue
291
292 mean1 = np.mean(image[mask1], axis=0)
293
294 for label2 in np.unique(region_map):
295 if label2 == 0 or label2 == label1 or label2 in merged:
296 continue
297
298 mask2 = (region_map == label2)
299 if np.sum(mask2) == 0: # Skip empty regions
300 continue
301
302 mean2 = np.mean(image[mask2], axis=0)
303
304 # Merge if similar color mean
305 if np.linalg.norm(mean1 - mean2) < merge_similarity_thresh:
306 final_map[mask2] = label1
307 merged.add(label2)
308
309 # Morphological Cleaning
310 binary_mask = final_map > 0
311 binary_mask = binary_fill_holes(binary_mask)
312 binary_mask = remove_small_objects(binary_mask, min_size=min_region_size)
313 binary_mask = remove_small_holes(binary_mask, area_threshold=min_region_size)
314
315 return binary_mask.astype(np.uint8)
316
317 # 3. Mean Shift Refinement
318 def generate_mean_shift_segments(image, spatial_radius=21, color_radius=51, quantization_level=16):
319 # Convert image to uint8 [0,255] and then to BGR
320 image_uint8 = (image * 255).astype(np.uint8)
321 image_bgr = cv2.cvtColor(image_uint8, cv2.COLOR_RGB2BGR)
322
323 # Apply mean shift filtering
324 filtered_bgr = cv2.pyrMeanShiftFiltering(image_bgr, spatial_radius, color_radius)
325 filtered_rgb = cv2.cvtColor(filtered_bgr, cv2.COLOR_BGR2RGB)
326
327 # Quantize colors
328 quantized = (filtered_rgb // quantization_level) * quantization_level
329
330 # Flatten to combine channels; cast to uint32 to avoid overflow
331 flat = quantized.reshape(-1, 3).astype(np.uint32)
332 flat_int = flat[:, 0] * 256 * 256 + flat[:, 1] * 256 + flat[:, 2]
333 quantized_int = flat_int.reshape(quantized.shape[0], quantized.shape[1])
334
335 # Label connected components
336 segments = label(quantized_int, connectivity=1)
337 return segments
338
339 def refine_with_mean_shift(pred_mask, segments):
340 refined_mask = np.zeros_like(pred_mask)
341 unique_segments = np.unique(segments)
342
343 for seg_val in unique_segments:
344 region = (segments == seg_val)
345 majority_label = np.mean(pred_mask[region]) > 0.5
346 refined_mask[region] = majority_label
347
348 return refined_mask
349
350 # Function to visualize the prediction with refinements
351 def visualize_prediction_with_refinements(image_tensor, pred_bin, slic_refined, region_refined, mean_shift_refined, alpha=0.6):
352 # Convert tensors to numpy arrays
353 image_np = image_tensor.squeeze().permute(1, 2, 0).numpy()
354 pred_np = pred_bin.squeeze().cpu().numpy()
355
356 # Create overlays
357 overlay_original = create_overlay(image_np, pred_np, alpha)
358 overlay_slic = create_overlay(image_np, slic_refined, alpha)
359 overlay_region = create_overlay(image_np, region_refined, alpha)
360 overlay_mean_shift = create_overlay(image_np, mean_shift_refined, alpha)
361
362 # Create figure for visualization
363 fig, axs = plt.subplots(2, 4, figsize=(18, 9))
364
365 # Original Image and Predicted Mask
366 axs[0, 0].imshow(image_np)
367 axs[0, 0].set_title('Original Image')
368 axs[0, 1].imshow(pred_np, cmap='gray')
369 axs[0, 1].set_title('Predicted Mask')
370
371 # Refined Masks
372 axs[0, 2].imshow(slic_refined, cmap='gray')
373 axs[0, 2].set_title('SLIC Refined')
374 axs[0, 3].imshow(region_refined, cmap='gray')
375 axs[0, 3].set_title('Region-based Refined')
376
377 # Mean Shift Refined and Overlays
378 axs[1, 0].imshow(mean_shift_refined, cmap='gray')
379 axs[1, 0].set_title('Mean Shift Refined')
380 axs[1, 1].imshow(overlay_original)
381 axs[1, 1].set_title('Original Overlay')
382 axs[1, 2].imshow(overlay_slic)
383 axs[1, 2].set_title('SLIC Overlay')
384 axs[1, 3].imshow(overlay_mean_shift)
385 axs[1, 3].set_title('Mean Shift Overlay')
386
387 for ax in axs.flat:
388 ax.axis('off')
389
390 plt.tight_layout()
391
392 # Convert plot to image
393 buf = io.BytesIO()
394 plt.savefig(buf, format='png', dpi=150)
395 buf.seek(0)
396 plt.close(fig)
397
398 return buf
399
400 def create_overlay(image, mask, alpha=0.6):
401 # Create green mask overlay
402 overlay = image.copy()
403 green_mask = np.zeros_like(image)
404 green_mask[..., 1] = 1 # Green channel
405 overlay_mask = np.where(mask[..., None] > 0, green_mask, 0)
406 overlay = (1 - alpha) * image + alpha * overlay_mask
407 overlay = np.clip(overlay, 0, 1)
408 return overlay
409
410 # Function to visualize traditional CV methods comparison
411 def visualize_traditional_cv_comparison(img_rgb, harris_mask, hough_mask, region_mask, slic_mask, gmm_mask, alpha=0.6):
412 # Create figure for visualization
413 fig, axs = plt.subplots(2, 3, figsize=(18, 12))
414
415 # Original Image
416 axs[0, 0].imshow(img_rgb)
417 axs[0, 0].set_title('Original Image')
418
419 # Harris Corners Mask
420 axs[0, 1].imshow(harris_mask, cmap='gray')
421 axs[0, 1].set_title('Harris Corners Mask')
422
423 # Hough Lines Mask
424 axs[0, 2].imshow(hough_mask, cmap='gray')
425 axs[0, 2].set_title('Hough Lines Mask')
426
427 # Region Mask
428 axs[1, 0].imshow(region_mask, cmap='gray')
429 axs[1, 0].set_title('Region-based Mask')
430
431 # SLIC Mask
432 axs[1, 1].imshow(slic_mask, cmap='gray')
433 axs[1, 1].set_title('SLIC Mask')
434
435 # GMM Mask
436 axs[1, 2].imshow(gmm_mask, cmap='gray')
437 axs[1, 2].set_title('GMM Mask')
438
439 for ax in axs.flat:
440 ax.axis('off')
441
442 plt.tight_layout()
443
444 # Convert plot to image
445 buf = io.BytesIO()
446 plt.savefig(buf, format='png', dpi=150)
447 buf.seek(0)
448 plt.close(fig)
449
450 return buf
451
452 def visualize_traditional_overlays(img_rgb, harris_mask, hough_mask, region_mask, slic_mask, gmm_mask, alpha=0.6):
453 # Create overlays for each mask
454 harris_overlay = create_overlay(img_rgb, harris_mask/255, alpha)
455 hough_overlay = create_overlay(img_rgb, hough_mask/255, alpha)
456 region_overlay = create_overlay(img_rgb, region_mask/255, alpha)
457 slic_overlay = create_overlay(img_rgb, slic_mask/255, alpha)
458 gmm_overlay = create_overlay(img_rgb, gmm_mask/255, alpha)
459
460 # Create figure for visualization
461 fig, axs = plt.subplots(2, 3, figsize=(18, 12))
462
463 # Original Image
464 axs[0, 0].imshow(img_rgb)
465 axs[0, 0].set_title('Original Image')
466
467 # Harris Corners Overlay
468 axs[0, 1].imshow(harris_overlay)
469 axs[0, 1].set_title('Harris Corners Overlay')
470
471 # Hough Lines Overlay
472 axs[0, 2].imshow(hough_overlay)
473 axs[0, 2].set_title('Hough Lines Overlay')
474
475 # Region Overlay
476 axs[1, 0].imshow(region_overlay)
477 axs[1, 0].set_title('Region-based Overlay')
478
479 # SLIC Overlay
480 axs[1, 1].imshow(slic_overlay)
481 axs[1, 1].set_title('SLIC Overlay')
482
483 # GMM Overlay
484 axs[1, 2].imshow(gmm_overlay)
485 axs[1, 2].set_title('GMM Overlay')
486
487 for ax in axs.flat:
488 ax.axis('off')
489
490 plt.tight_layout()
491
492 # Convert plot to image
493 buf = io.BytesIO()
494 plt.savefig(buf, format='png', dpi=150)
495 buf.seek(0)
496 plt.close(fig)
497
498 return buf
499
500 # ------------- Streamlit UI -------------
501 with tab1:
502 st.header("Traditional Computer Vision Approach")
503
504 uploaded_file = st.file_uploader("Upload a wall image (JPG/PNG/JPEG):", type=["jpg","png","jpeg"], key="cv_uploader")
505
506 if uploaded_file is not None:
507 img = load_image(uploaded_file)
508
509 # Process the image through our traditional pipeline
510 harris_img, closed_edges, hough_img, region_seg_img, harris_mask, hough_mask, region_mask, traditional_prediction = process_image_traditional(img)
511
512 # Process the image using SLIC
513 slic_img, slic_hough_img, slic_mask, slic_prediction = process_image_slic(img)
514
515 # Process the image using GMM
516 image_rgb, gmm_mask = apply_gmm_binary_mask(img, 2)
517
518 # GMM Crack Detection Logic
519 crack_pixel_ratio = np.sum(gmm_mask == 255) / gmm_mask.size
520 gmm_prediction = 1 if crack_pixel_ratio > 0.01 else 0 # 1% threshold
521
522 st.subheader("Uploaded Image")
523 st.image(cv2.cvtColor(img, cv2.COLOR_BGR2RGB), channels="RGB", use_column_width=True)
524
525 # Display traditional CV outputs
526 st.subheader("Traditional Computer Vision Results")
527 col1, col2, col3 = st.columns(3)
528
529 with col1:
530 st.image(cv2.cvtColor(harris_img, cv2.COLOR_BGR2RGB), caption="Harris Corner Detection", use_column_width=True)
531 st.image(closed_edges, caption="Canny + Morphological Closing", use_column_width=True, channels="GRAY")
532
533 with col2:
534 st.image(cv2.cvtColor(hough_img, cv2.COLOR_BGR2RGB), caption="Crack Lines via HoughLinesP", use_column_width=True)
535 st.image(cv2.cvtColor(region_seg_img, cv2.COLOR_BGR2RGB), caption="Region-based Segmentation", use_column_width=True)
536
537 with col3:
538 # Final Crack Classification
539 if traditional_prediction == 1:
540 st.error("⚠️ Crack Detected! (Traditional)")
541 else:
542 st.success("✅ No Crack Detected (Traditional)")
543
544 # Display SLIC outputs
545 st.subheader("SLIC-based Segmentation Results")
546 col1, col2 = st.columns(2)
547
548 with col1:
549 st.image(slic_img, caption="SLIC Segmentation", use_column_width=True)
550
551 with col2:
552 # SLIC Crack Classification
553 if slic_prediction == 1:
554 st.error("⚠️ Crack Detected! (SLIC + Hough)")
555 else:
556 st.success("✅ No Crack Detected (SLIC + Hough)")
557
558 # GMM Results
559 st.subheader("GMM-Based Segmentation")
560 col1, col2 = st.columns(2)
561
562 with col1:
563 st.image(image_rgb, caption="Original (RGB)", use_column_width=True)
564 with col2:
565 st.image(gmm_mask, caption="GMM Crack Mask", use_column_width=True, channels="GRAY")
566
567 # Display GMM result in Streamlit
568 if gmm_prediction == 1:
569 st.error("⚠️ Crack Detected! (GMM Segmentation)")
570 else:
571 st.success("✅ No Crack Detected (GMM Segmentation)")
572
573 # New: Detailed Refinement Results and Performance Comparison
574 st.subheader("Detailed Method Comparison")
575
576 # Visualization of all masks
577 img_rgb_viz = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
578 mask_comparison = visualize_traditional_cv_comparison(
579 img_rgb_viz,
580 harris_mask,
581 hough_mask,
582 region_mask,
583 slic_mask,
584 gmm_mask
585 )
586 st.image(mask_comparison, caption="Comparison of Detection Masks", use_column_width=True)
587
588
589 # Performance Comparison
590 st.subheader("Performance Comparison")
591
592 # Calculate pixel coverage for each method
593 harris_coverage = np.sum(harris_mask > 0) / harris_mask.size * 100
594 hough_coverage = np.sum(hough_mask > 0) / hough_mask.size * 100
595 region_coverage = np.sum(region_mask > 0) / region_mask.size * 100
596 slic_coverage = np.sum(slic_mask > 0) / slic_mask.size * 100
597 gmm_coverage = np.sum(gmm_mask > 0) / gmm_mask.size * 100
598
599 data = {
600 'Method': ['Harris Corners', 'Hough Lines', 'Region-based', 'SLIC', 'GMM'],
601 'Crack Coverage (%)': [
602 harris_coverage,
603 hough_coverage,
604 region_coverage,
605 slic_coverage,
606 gmm_coverage
607 ]
608 }
609
610 st.write("Crack Coverage Percentage by Method:")
611 st.bar_chart(data, x='Method', y='Crack Coverage (%)')
612
613 # Method Analysis
614 st.subheader("Method Analysis")
615
616 col1, col2 = st.columns(2)
617
618 with col1:
619 st.markdown("**Strongest Detection Methods**")
620 methods = ['Harris Corners', 'Hough Lines', 'Region-based', 'SLIC', 'GMM']
621 coverages = [harris_coverage, hough_coverage, region_coverage, slic_coverage, gmm_coverage]
622 max_idx = coverages.index(max(coverages))
623
624 st.info(f"**{methods[max_idx]}** shows the highest crack coverage at **{coverages[max_idx]:.2f}%**")
625
626 if max(coverages) > 1.0:
627 st.warning("Significant crack pattern detected in the image")
628 else:
629 st.success("Minimal crack patterns detected in the image")
630
631 with col2:
632 st.markdown("**Detection Confidence**")
633
634 # Count positive detections
635 detection_count = sum([
636 1 if harris_coverage > 0.5 else 0,
637 1 if hough_coverage > 0.5 else 0,
638 1 if region_coverage > 0.5 else 0,
639 1 if slic_coverage > 0.5 else 0,
640 1 if gmm_coverage > 0.5 else 0
641 ])
642 confidence = (detection_count / 5) * 100
643
644 st.progress(confidence / 100)
645 st.write(f"Detection Confidence: {confidence:.1f}%")
646
647 if confidence > 60:
648 st.error("High confidence crack detection - further inspection recommended")
649 elif confidence > 20:
650 st.warning("Medium confidence crack detection - monitoring recommended")
651 else:
652 st.success("Low confidence crack detection - likely safe condition")
653
654 # Final Analysis Summary
655 st.subheader("Analysis Summary")
656 st.markdown(f"""
657 * **Traditional CV Methods**: {'Detected cracks' if traditional_prediction == 1 else 'No cracks detected'}
658 * **SLIC-Based Methods**: {'Detected cracks' if slic_prediction == 1 else 'No cracks detected'}
659 * **GMM-Based Methods**: {'Detected cracks' if gmm_prediction == 1 else 'No cracks detected'}
660 * **Overall Detection Confidence**: {confidence:.1f}%
661 """)
662
663 # Add recommendations based on detection results
664 if confidence > 40:
665 st.error("""
666 **Recommendations**:
667 - Consider professional inspection of the wall
668 - Monitor crack development over time
669 - Check for water damage or structural issues nearby
670 """)
671 elif confidence > 10:
672 st.warning("""
673 **Recommendations**:
674 - Monitor the area periodically
675 - Take reference photos for comparison over time
676 - Check again after extreme weather conditions
677 """)
678 else:
679 st.success("""
680 **Recommendations**:
681 - No immediate action required
682 - Include in regular building maintenance checks
683 """)
684
685 with tab2:
686 st.header("Deep Learning Approach with Refinement Methods")
687
688 uploaded_file_dl = st.file_uploader("Upload a wall image (JPG/PNG/JPEG):", type=["jpg","png","jpeg"], key="dl_uploader")
689
690 # Load the pretrained model
691 model, device, model_loaded = load_dl_model()
692
693 if not model_loaded:
694 st.warning("Pretrained model could not be loaded. Deep learning analysis may not be accurate.")
695
696 if uploaded_file_dl is not None:
697 with st.spinner("Processing image with DeepLabV3 model and applying refinements..."):
698 img_dl = load_image(uploaded_file_dl)
699
700 # Preprocess image
701 img_tensor, img_rgb = preprocess_image_dl(img_dl)
702
703 # Predict mask
704 pred_bin = predict_mask(model, img_tensor, device)
705 pred_np = pred_bin.squeeze().cpu().numpy()
706
707 # Apply SLIC-based refinement
708 superpixels = generate_superpixels(img_rgb, n_segments=300, compactness=10)
709 slic_refined = refine_with_superpixels(pred_np, superpixels)
710
711 # Apply Region-based Split and Merge refinement
712 region_refined = region_based_split_and_merge(img_rgb, pred_np)
713
714 # Apply Mean Shift refinement
715 mean_shift_segments = generate_mean_shift_segments(img_rgb)
716 mean_shift_refined = refine_with_mean_shift(pred_np, mean_shift_segments)
717
718 # Visualize all results
719 vis_buf = visualize_prediction_with_refinements(
720 img_tensor.cpu(),
721 pred_bin.cpu(),
722 slic_refined,
723 region_refined,
724 mean_shift_refined
725 )
726
727 # Display results
728 st.image(vis_buf, caption="DeepLabV3 Segmentation Results with Refinement Methods", use_column_width=True)
729
730 # Show individual refinement results
731 st.subheader("Detailed Refinement Results")
732
733 col1, col2, col3 = st.columns(3)
734
735 with col1:
736 st.markdown("**SLIC-based Refinement**")
737 st.info("Combines segmentation with majority voting inside superpixels")
738 crack_ratio_slic = np.sum(slic_refined) / slic_refined.size
739 if crack_ratio_slic > 0.01:
740 st.error("⚠️ Crack Detected! (SLIC Refinement)")
741 else:
742 st.success("✅ No Crack Detected (SLIC Refinement)")
743
744 with col2:
745 st.markdown("**Region-based Refinement**")
746 st.info("Uses split & merge based on color & texture variance")
747 crack_ratio_region = np.sum(region_refined) / region_refined.size
748 if crack_ratio_region > 0.01:
749 st.error("⚠️ Crack Detected! (Region Refinement)")
750 else:
751 st.success("✅ No Crack Detected (Region Refinement)")
752
753 with col3:
754 st.markdown("**Mean Shift Refinement**")
755 st.info("Uses adaptive bandwidth clustering for natural segmentation")
756 crack_ratio_ms = np.sum(mean_shift_refined) / mean_shift_refined.size
757 if crack_ratio_ms > 0.01:
758 st.error("⚠️ Crack Detected! (Mean Shift Refinement)")
759 else:
760 st.success("✅ No Crack Detected (Mean Shift Refinement)")
761
762 # Display comparison of methods
763 st.subheader("Performance Comparison")
764
765 # Calculate crack ratios for each method
766 crack_ratio_original = np.sum(pred_np) / pred_np.size
767
768 data = {
769 'Method': ['Original DL', 'SLIC', 'Region-based', 'Mean Shift'],
770 'Crack Coverage (%)': [
771 crack_ratio_original * 100,
772 crack_ratio_slic * 100,
773 crack_ratio_region * 100,
774 crack_ratio_ms * 100
775 ]
776 }
777
778 st.write("Crack Coverage Percentage by Method:")
779 st.bar_chart(data, x='Method', y='Crack Coverage (%)')
780
781 # Add new detailed analysis and recommendations section
782 st.subheader("Deep Learning Model Analysis")
783
784 # Calculate weighted ensemble prediction
785 weights = {
786 'Original': 0.25,
787 'SLIC': 0.25,
788 'Region': 0.25,
789 'MeanShift': 0.25
790 }
791
792 ensemble_score = (
793 weights['Original'] * crack_ratio_original +
794 weights['SLIC'] * crack_ratio_slic +
795 weights['Region'] * crack_ratio_region +
796 weights['MeanShift'] * crack_ratio_ms
797 ) * 100
798
799 st.write(f"Weighted Ensemble Score: {ensemble_score:.2f}%")
800
801 # Display confidence gauge
802 st.progress(min(ensemble_score/10, 1.0)) # Cap at 100%
803
804 if ensemble_score > 5:
805 st.error("⚠️ High confidence crack detection (Deep Learning)")
806 st.markdown("""
807 **Model Analysis**:
808 - The deep learning model has detected significant crack patterns with high confidence
809 - Multiple refinement methods confirm the detection
810 - Detailed inspection is recommended
811 """)
812 elif ensemble_score > 1:
813 st.warning("⚠️ Medium confidence crack detection (Deep Learning)")
814 st.markdown("""
815 **Model Analysis**:
816 - The model has detected potential crack patterns with moderate confidence
817 - Some refinement methods confirm the detection
818 - Further monitoring is recommended
819 """)
820 else:
821 st.success("✅ Low/No crack detection (Deep Learning)")
822 st.markdown("""
823 **Model Analysis**:
824 - The model found minimal or no crack patterns
825 - Refinement methods confirm the absence of significant cracks
826 - The wall appears to be in good condition
827 """)
828
829 # Method comparison and analysis
830 st.subheader("Method Effectiveness Analysis")
831
832 # Determine the most sensitive method
833 methods = ['Original DL', 'SLIC', 'Region-based', 'Mean Shift']
834 ratios = [crack_ratio_original, crack_ratio_slic, crack_ratio_region, crack_ratio_ms]
835 most_sensitive_idx = np.argmax(ratios)
836
837 st.info(f"Most sensitive method: **{methods[most_sensitive_idx]}** with {ratios[most_sensitive_idx]*100:.2f}% coverage")
838
839 # Calculate agreement between methods
840 agreement_count = sum([1 for r in ratios if r > 0.01])
841 agreement_percentage = (agreement_count / len(ratios)) * 100
842
843 st.write(f"Method agreement: {agreement_percentage:.1f}% ({agreement_count}/{len(ratios)} methods agree)")
844
845 # Model confidence explanation
846 st.subheader("Confidence Explanation")
847
848 col1, col2 = st.columns(2)
849 with col1:
850 st.markdown("""
851 **How confidence is calculated:**
852 - Original model prediction (25%)
853 - SLIC refinement results (25%)
854 - Region-based refinement (25%)
855 - Mean Shift refinement (25%)
856 """)
857
858 with col2:
859 st.markdown("""
860 **Interpreting the results:**
861 - >5%: Significant crack detected
862 - 1-5%: Potential crack detected
863 - <1%: No significant crack detected
864 """)
865
866 # Final recommendation section
867 st.subheader("Final Recommendations")
868
869 if ensemble_score > 5:
870 st.error("""
871 **Professional Assessment Recommended:**
872 - Schedule a structural inspection
873 - Document the crack pattern and location
874 - Monitor for changes in size or pattern
875 - Check for water infiltration or other damage
876 """)
877 elif ensemble_score > 1:
878 st.warning("""
879 **Monitoring Recommended:**
880 - Take reference photos for future comparison
881 - Check the area after extreme weather conditions
882 - Monitor for growth or pattern changes
883 - Consider applying crack sealant if stable
884 """)
885 else:
886 st.success("""
887 **No Immediate Action Required:**
888 - Include in regular building maintenance inspections
889 - Re-analyze if visible changes occur
890 - Consider this area low priority for repairs
891 """)
892
893if __name__ == "__main__":
894 main()