Spaces:
Sleeping
Sleeping
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()
|