File size: 5,545 Bytes
95700ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import cv2
import numpy as np
from matplotlib import pyplot as plt
import gradio as gr

def load_image(image_path):
    image = cv2.imread(image_path)
    if image is None:
        print(f"Error: Unable to load image at {image_path}")
        return None
    return image
def filter_sky_contours(mask, image_height, min_area=8000, max_area=None, height_ratio=0.2):
    """
    Filter contours that are likely to be the sky based on area and position.
    
    Parameters:
    - mask: Binary mask where the sky is white and the rest is black.
    - image_height: Height of the original image.
    - min_area: Minimum area of a contour to be considered as sky.
    - max_area: Maximum area of a contour to be considered as sky.
    - height_ratio: The ratio of the image height where we expect the sky to be located.
    
    Returns:
    - sky_mask: Refined binary mask with filtered sky regions.
    """
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    sky_mask = np.zeros_like(mask)
    for contour in contours:
        area = cv2.contourArea(contour)
        x, y, w, h = cv2.boundingRect(contour)
        aspect_ratio = w / float(h)

        convex_hull_area = cv2.contourArea(cv2.convexHull(contour))
        if convex_hull_area > 0:  # Ensure the denominator is not zero
            smoothness = area / convex_hull_area
        else:
            smoothness = 1  # Or some other default value that makes sense for your application

        if (area > min_area) and (max_area is None or area < max_area) and (y < image_height * height_ratio):
            if aspect_ratio > 1 and smoothness > 0.5:  # Adjust thresholds as necessary
                cv2.drawContours(sky_mask, [contour], -1, (255), thickness=cv2.FILLED)
    return sky_mask
def detect_edges(image):
    # Convert to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # Apply bilateral filter to preserve edges while reducing noise
    bilateral = cv2.bilateralFilter(gray, d=9, sigmaColor=75, sigmaSpace=75)
    
    # Use a combination of Sobel filters to find gradients
    sobelx = cv2.Sobel(bilateral, cv2.CV_64F, 1, 0, ksize=3)
    sobely = cv2.Sobel(bilateral, cv2.CV_64F, 0, 1, ksize=3)
    
    # Combine the gradients
    gradient_magnitude = cv2.magnitude(sobelx, sobely)
    gradient_magnitude = np.uint8(gradient_magnitude)
    
    # Apply thresholding to get binary result
    _, edge_binary = cv2.threshold(gradient_magnitude, 20, 255, cv2.THRESH_BINARY)
    
    # Optionally, you can apply dilation followed by erosion to close gaps
    kernel = np.ones((3,3), np.uint8)
    edge_dilated = cv2.dilate(edge_binary, kernel, iterations=1)
    edge_processed = cv2.erode(edge_dilated, kernel, iterations=1)
    
    return edge_processed



def adaptive_threshold_sky(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
                                 cv2.THRESH_BINARY,21,2)

def refine_mask(mask):
    kernel = np.ones((35,35), np.uint8)
    return cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)

def segment_sky(image):
    # Detect edges.
    edges = detect_edges(image)

    # Invert the edges.
    edges_inv = cv2.bitwise_not(edges)

    # Create a color mask using adaptive thresholding.
    color_mask = adaptive_threshold_sky(image)

    # Combine the color mask with the inverted edges to get an initial sky mask.
    combined_mask = cv2.bitwise_and(color_mask, edges_inv)

    # Filter contours that are likely to be the sky and refine the mask.
    refined_mask_contour = filter_sky_contours(combined_mask, image.shape[0])

    # Optionally, you can further refine the mask with morphological operations if needed.
    refined_mask_morph = refine_mask(refined_mask_contour)

    # Apply the final refined mask to segment the sky.
    segmented_sky = cv2.bitwise_and(image, image, mask=refined_mask_morph)
    contours, _ = cv2.findContours(refined_mask_morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    return segmented_sky, contours


def display_results_with_contours(original, segmented, edges, contours):
    # Create a copy of the original image to draw contours on.
    image_with_contours = original.copy()
    cv2.drawContours(image_with_contours, contours, -1, (0, 255, 0), 3)

    plt.figure(figsize=(12, 8))

    plt.subplot(1, 4, 1)
    plt.imshow(cv2.cvtColor(original, cv2.COLOR_BGR2RGB))
    plt.title('Original Image')

    plt.subplot(1, 4, 2)
    plt.imshow(edges, cmap='gray')
    plt.title('Edges')

    plt.subplot(1, 4, 3)
    plt.imshow(cv2.cvtColor(segmented, cv2.COLOR_BGR2RGB))
    plt.title('Segmented Sky')

    plt.subplot(1, 4, 4)
    plt.imshow(cv2.cvtColor(image_with_contours, cv2.COLOR_BGR2RGB))
    plt.title('Contours')

    plt.show()

def process_and_display(image):
    # Convert the uploaded PIL image to an OpenCV format
    image = np.array(image)
    image = image[:, :, :3]  # Ensure the image is in RGB format
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

    # Segment the sky
    segmented_sky, _ = segment_sky(image)

    # Prepare the segmented sky image for display in Gradio
    segmented_rgb = cv2.cvtColor(segmented_sky, cv2.COLOR_BGR2RGB)

    return segmented_rgb

# Set up the Gradio interface
iface = gr.Interface(
    fn=process_and_display,
    inputs=gr.Image(),
    outputs=gr.Image(label="Segmented Sky"),
    title="Sky Pixel Identification",
    description="Upload an image to see the segmented sky."
)

# Run the app
iface.launch()