File size: 6,119 Bytes
c436e12
 
72d2178
c436e12
5cf1972
72d2178
 
75b4a12
fc232c8
72d2178
 
 
 
 
fc232c8
 
75b4a12
fc232c8
 
 
 
 
 
 
 
75b4a12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72d2178
 
75b4a12
 
 
 
 
 
72d2178
 
 
fc232c8
 
75b4a12
 
 
 
 
fc232c8
75b4a12
 
 
 
fc232c8
75b4a12
72d2178
 
 
 
 
 
 
75b4a12
5cf1972
75b4a12
72d2178
75b4a12
9444004
c436e12
75b4a12
 
72d2178
75b4a12
72d2178
 
 
 
c436e12
75b4a12
c436e12
 
 
 
 
 
 
75b4a12
c436e12
 
 
75b4a12
c436e12
 
75b4a12
c436e12
72d2178
c436e12
75b4a12
fc232c8
 
75b4a12
 
fc232c8
75b4a12
 
fc232c8
 
c436e12
75b4a12
72d2178
 
fc232c8
 
 
72d2178
 
 
 
75b4a12
 
fc232c8
 
9444004
75b4a12
9444004
75b4a12
9444004
75b4a12
9444004
 
5cf1972
9444004
 
5cf1972
 
75b4a12
 
9444004
75b4a12
c436e12
75b4a12
 
 
c436e12
5cf1972
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import cv2
import numpy as np
from PIL import Image, ImageDraw
import gradio as gr

def classify_pipe_material(image_np):
    """
    Classify overall material based on image brightness.
    Brighter images (mean intensity > 130) are assumed to be Plastic; otherwise, Metal.
    """
    gray = cv2.cvtColor(image_np, cv2.COLOR_RGB2GRAY)
    mean_intensity = np.mean(gray)
    return "Plastic" if mean_intensity > 130 else "Metal"

def detect_rust(roi):
    """
    Detect rust by checking for reddish-brown hues in the ROI.
    """
    hsv_roi = cv2.cvtColor(roi, cv2.COLOR_RGB2HSV)
    lower_rust = np.array([5, 50, 50])
    upper_rust = np.array([25, 255, 255])
    mask = cv2.inRange(hsv_roi, lower_rust, upper_rust)
    rust_ratio = np.count_nonzero(mask) / float(roi.shape[0] * roi.shape[1])
    return rust_ratio

def detect_mold(roi):
    """
    Detect mold by looking for greenish hues, which may indicate fungal growth.
    """
    hsv_roi = cv2.cvtColor(roi, cv2.COLOR_RGB2HSV)
    lower_mold = np.array([35, 50, 20])
    upper_mold = np.array([85, 255, 120])
    mask = cv2.inRange(hsv_roi, lower_mold, upper_mold)
    mold_ratio = np.count_nonzero(mask) / float(roi.shape[0] * roi.shape[1])
    return mold_ratio

def detect_water_damage(roi):
    """
    Detect water damage by checking for discoloration typical of stains (dark brownish-yellow).
    """
    hsv_roi = cv2.cvtColor(roi, cv2.COLOR_RGB2HSV)
    lower_water = np.array([5, 50, 50])
    upper_water = np.array([20, 200, 150])
    mask = cv2.inRange(hsv_roi, lower_water, upper_water)
    water_ratio = np.count_nonzero(mask) / float(roi.shape[0] * roi.shape[1])
    return water_ratio

def classify_defect(roi):
    """
    Classify the defect using a combination of color and texture heuristics.
    Priority is given to color cues:
      - "Rust": reddish-brown
      - "Mold": greenish
      - "Water Damage": discoloration from water stains
    Then geometric/texture analysis is used to differentiate "Crack" and "Corrosion."
    """
    area = roi.shape[0] * roi.shape[1]
    std_intensity = np.std(roi)
    
    rust_ratio = detect_rust(roi)
    mold_ratio = detect_mold(roi)
    water_ratio = detect_water_damage(roi)
    
    # Check for color-based defects first.
    if rust_ratio > 0.25:
        return "Rust"
    elif mold_ratio > 0.2:
        return "Mold"
    elif water_ratio > 0.2:
        return "Water Damage"
    
    # Then use size and intensity variation for structural defects.
    if area < 5000 and std_intensity > 50:
        return "Crack"
    elif area >= 5000 and std_intensity > 40:
        return "Corrosion"
    else:
        return "Other Defect"

def detect_infrastructure_issues(image: Image.Image):
    try:
        # Convert the PIL image to a NumPy array (RGB)
        image_np = np.array(image)
        annotated = image.copy()  # For drawing annotations
        draw = ImageDraw.Draw(annotated)
        
        # Classify the overall pipe (or structure) material
        overall_material = classify_pipe_material(image_np)
        
        # Enhance image for defect detection:
        gray = cv2.cvtColor(image_np, cv2.COLOR_RGB2GRAY)
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
        enhanced = clahe.apply(gray)
        blurred = cv2.GaussianBlur(enhanced, (5, 5), 0)
        
        # Adaptive thresholding highlights potential defect areas
        thresh = cv2.adaptiveThreshold(
            blurred, 255,
            cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
            cv2.THRESH_BINARY_INV,
            11, 2
        )
        
        # Morphological closing to consolidate defect regions
        kernel = np.ones((3, 3), np.uint8)
        morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
        
        # Edge detection to identify defect boundaries
        edges = cv2.Canny(morph, 50, 150)
        
        # Extract contours as candidate defect regions
        contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        detections = []
        
        # Define distinct colors for each defect type
        colors = {
            "Rust": "orange",
            "Mold": "purple",
            "Water Damage": "blue",
            "Crack": "red",
            "Corrosion": "cyan",
            "Other Defect": "gray"
        }
        
        for cnt in contours:
            if cv2.contourArea(cnt) < 100:  # Filter out noise
                continue
            x, y, w, h = cv2.boundingRect(cnt)
            roi = image_np[y:y+h, x:x+w]
            if roi.size == 0:
                continue
            defect_type = classify_defect(roi)
            detection_info = f"{defect_type} at ({x}, {y}, {w}, {h})"
            detections.append(detection_info)
            
            # Draw bounding box with defect-specific color
            box_color = colors.get(defect_type, "gray")
            draw.rectangle([x, y, x+w, y+h], outline=box_color, width=2)
            draw.text((x, y-10), defect_type, fill=box_color)
        
        # Create a textual summary
        if detections:
            summary = f"Overall Material: {overall_material}\nDetected Issues:\n" + "\n".join(detections)
        else:
            summary = f"Overall Material: {overall_material}\nNo significant defects detected."
        
        return annotated, summary
    except Exception as e:
        print("Error during detection:", e)
        return image, f"Error: {e}"

iface = gr.Interface(
    fn=detect_infrastructure_issues,
    inputs=gr.Image(type="pil", label="Upload an Infrastructure Image"),
    outputs=[gr.Image(label="Annotated Image"), gr.Textbox(label="Detection Summary")],
    title="Comprehensive Home Infrastructure Defect Detector",
    description=(
        "Upload an image of a pipe or any home infrastructure (walls, floors, etc.) to detect defects. "
        "This tool identifies issues such as Rust (orange), Mold (purple), Water Damage (blue), Cracks (red), "
        "and Corrosion (cyan), and returns both an annotated image and a detailed summary."
    )
)

if __name__ == "__main__":
    iface.launch()