detect-cme / app.py
broadfield-dev's picture
Update app.py
de92129 verified
Raw History Blame
6.17 kB
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: apply Gaussian blur and adaptive thresholding."""
# Apply Gaussian blur to reduce noise
blurred = cv2.GaussianBlur(frame, (9, 9), 0)
# Adaptive thresholding to enhance lighter regions (CME features)
thresh = cv2.adaptiveThreshold(
blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2
)
return thresh
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, # Increase resolution for better detection
minDist=100, # Prevent overlapping circles
param1=100, # Higher edge threshold to reduce noise
param2=20, # 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 centered at the Sun."""
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
results = []
circle_data = []
min_radius = 20
max_radius = min(height, width) // 2 # Limit max radius to half the image size
# Process frames
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 lighter pixels
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
circle_data.append({
"frame": i + 1,
"center": (x, y),
"radius": r
})
# Visualize the frame with detected circle
output_frame = cv2.cvtColor(frames[i + 1], cv2.COLOR_GRAY2RGB)
if circles:
x, y, r = largest_circle
cv2.circle(output_frame, (x, y), r, (0, 255, 0), 2)
# Convert to PIL Image for Gradio
output_frame = Image.fromarray(output_frame)
results.append(output_frame)
# Analyze circle data for growth
report = "Analysis Report:\n"
if circle_data:
radii = [c["radius"] for c in circle_data]
centers = [c["center"] for c in circle_data]
frames_with_circles = [c["frame"] for c in circle_data]
# Check if radii are increasing over frames
is_growing = all(radii[i] < radii[i + 1] for i in range(len(radii) - 1)) if len(radii) > 1 else False
center_consistent = all(
abs(centers[i][0] - centers[0][0]) < 20 and
abs(centers[i][1] - centers[0][1]) < 20
for i in range(1, len(centers))
) if len(centers) > 1 else False
report += f"Detected {len(circle_data)} circles across frames.\n"
for c in circle_data:
report += f"Frame {c['frame']}: Center at {c['center']}, Radius {c['radius']} pixels\n"
if is_growing and center_consistent:
report += "\nConclusion: Growing concentric circles detected, indicative of a potential Earth-directed CME."
else:
report += "\nConclusion: Detected circles, but growth pattern or center consistency does not confirm a clear CME."
else:
report += "No concentric circles detected."
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 Detected Circles")
],
title="Solar CME Detection",
description="Upload a GIF of solar images to detect growing concentric circles indicative of Earth-directed coronal mass ejections (CMEs)."
)
if __name__ == "__main__":
iface.launch()