Spaces:
Paused
Paused
File size: 6,402 Bytes
5a1c0ee 255303a 5a1c0ee 00694e7 de92129 00694e7 d84d480 00694e7 5a1c0ee de92129 5a1c0ee 34076e0 de92129 00694e7 5a1c0ee de92129 5a1c0ee 00694e7 5a1c0ee de92129 255303a 5a1c0ee 255303a 5a1c0ee 255303a 5a1c0ee 255303a 5a1c0ee de92129 5a1c0ee 34076e0 5a1c0ee de92129 5a1c0ee 34076e0 5a1c0ee 00694e7 de92129 5a1c0ee de92129 5a1c0ee de92129 34076e0 de92129 34076e0 de92129 5a1c0ee 34076e0 00694e7 34076e0 00694e7 34076e0 5a1c0ee 00694e7 5a1c0ee 00694e7 5a1c0ee 255303a 5a1c0ee 34076e0 5a1c0ee 00694e7 5a1c0ee | 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 | import gradio as gr
import numpy as np
import cv2
from PIL import Image
import matplotlib.pyplot as plt
def extract_frames(gif_path):
"""Extract frames from a GIF and return as a list of numpy arrays."""
try:
img = Image.open(gif_path)
frames = []
while True:
frame = img.convert('L') # Convert to grayscale
frames.append(np.array(frame))
try:
img.seek(img.tell() + 1)
except EOFError:
break
return frames, None
except Exception as e:
return None, f"Error loading GIF: {str(e)}"
def preprocess_frame(frame):
"""Preprocess a frame: isolate mid-to-light pixels and enhance circular patterns."""
# Apply Gaussian blur to reduce noise
blurred = cv2.GaussianBlur(frame, (9, 9), 0)
# Isolate mid-to-light pixels (intensity range 100–200 in grayscale)
lower_bound = 150
upper_bound = 255
mask = cv2.inRange(blurred, lower_bound, upper_bound)
# Apply morphological operation to enhance circular patterns
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
enhanced = cv2.dilate(mask, kernel, iterations=2)
return enhanced
def detect_circles(frame_diff, image_center, min_radius=20, max_radius=200):
"""Detect circles in a frame difference image, centered at the Sun."""
circles = cv2.HoughCircles(
frame_diff,
cv2.HOUGH_GRADIENT,
dp=1.5, # Resolution for better detection
minDist=100, # Prevent overlapping circles
param1=80, # Lower edge threshold to capture fainter edges
param2=15, # Lower accumulator threshold to detect faint circles
minRadius=min_radius,
maxRadius=max_radius
)
if circles is not None:
circles = np.round(circles[0, :]).astype("int")
# Filter circles: only keep those centered near the image center
filtered_circles = []
center_tolerance = 30 # Allow 30-pixel deviation from the center
for (x, y, r) in circles:
if (abs(x - image_center[0]) < center_tolerance and
abs(y - image_center[1]) < center_tolerance):
filtered_circles.append((x, y, r))
return filtered_circles if filtered_circles else None
return None
def analyze_gif(gif_file):
"""Analyze a GIF for growing concentric circles of mid-to-light pixels."""
try:
# Handle Gradio file input
gif_path = gif_file.name if hasattr(gif_file, 'name') else gif_file
# Extract frames
frames, error = extract_frames(gif_path)
if error:
return error, []
if len(frames) < 2:
return "GIF must have at least 2 frames for analysis.", []
# Determine the image center (Sun's position)
height, width = frames[0].shape
image_center = (width // 2, height // 2) # Assume Sun is at the center
# Initialize results
all_circle_data = [] # Store all detected circles
min_radius = 20
max_radius = min(height, width) // 2 # Limit max radius to half the image size
# Process frames and detect circles
for i in range(len(frames) - 1):
frame1 = preprocess_frame(frames[i])
frame2 = preprocess_frame(frames[i + 1])
# Compute absolute difference between consecutive frames
frame_diff = cv2.absdiff(frame2, frame1)
# Enhance contrast for the difference image
frame_diff = cv2.convertScaleAbs(frame_diff, alpha=3.0, beta=0)
# Detect circles centered at the Sun
circles = detect_circles(frame_diff, image_center, min_radius, max_radius)
if circles:
# Take the largest circle (most prominent CME feature)
largest_circle = max(circles, key=lambda c: c[2]) # Sort by radius
x, y, r = largest_circle
all_circle_data.append({
"frame": i + 1,
"center": (x, y),
"radius": r,
"output_frame": frames[i + 1] # Store the frame for visualization
})
# Filter frames where the circle is growing
growing_circle_data = []
if all_circle_data:
# Start with the first detection
growing_circle_data.append(all_circle_data[0])
for i in range(1, len(all_circle_data)):
# Compare radius with the last growing circle
if all_circle_data[i]["radius"] > growing_circle_data[-1]["radius"]:
growing_circle_data.append(all_circle_data[i])
# Generate output frames and report
results = []
report = "Analysis Report (as of 07:34 PM PDT, May 24, 2025):\n"
if growing_circle_data:
report += f"Detected {len(growing_circle_data)} frames with growing concentric circles of mid-to-light pixels:\n"
for c in growing_circle_data:
# Visualize the frame with detected circle
output_frame = cv2.cvtColor(c["output_frame"], cv2.COLOR_GRAY2RGB)
cv2.circle(output_frame, (c["center"][0], c["center"][1]), c["radius"], (0, 255, 0), 2)
# Convert to PIL Image for Gradio
output_frame = Image.fromarray(output_frame)
results.append(output_frame)
report += f"Frame {c['frame']}: Center at {c['center']}, Radius {c['radius']} pixels\n"
report += "\nConclusion: Growing concentric circles of mid-to-light pixels detected, indicative of a potential Earth-directed CME."
else:
report += "No growing concentric circles of mid-to-light pixels detected. CME may not be Earth-directed."
return report, results
except Exception as e:
return f"Error during analysis: {str(e)}", []
# Gradio interface
iface = gr.Interface(
fn=analyze_gif,
inputs=gr.File(label="Upload Solar GIF", file_types=[".gif"]),
outputs=[
gr.Textbox(label="Analysis Report"),
gr.Gallery(label="Frames with Growing Circles")
],
title="Solar CME Detection",
description="Upload a GIF of solar images to detect growing concentric circles of mid-to-light pixels indicative of Earth-directed coronal mass ejections (CMEs)."
)
if __name__ == "__main__":
iface.launch() |