detect-cme / app.py
broadfield-dev's picture
Create app.py
5a1c0ee verified
Raw History Blame
5.07 kB
import gradio as gr
import numpy as np
import cv2
from PIL import Image
import io
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
except Exception as e:
return None, f"Error loading GIF: {str(e)}"
def preprocess_frame(frame):
"""Preprocess a frame: apply Gaussian blur to reduce noise."""
return cv2.GaussianBlur(frame, (5, 5), 0)
def detect_circles(frame_diff, min_radius=20, max_radius=200):
"""Detect circles in a frame difference image using Hough Circle Transform."""
circles = cv2.HoughCircles(
frame_diff,
cv2.HOUGH_GRADIENT,
dp=1.2, # Inverse ratio of resolution
minDist=50, # Minimum distance between detected centers
param1=50, # Canny edge detector threshold
param2=30, # Accumulator threshold for circle detection
minRadius=min_radius,
maxRadius=max_radius
)
return circles
def analyze_gif(gif_file):
"""Analyze a GIF for growing concentric circles."""
try:
# Save uploaded GIF to temporary file
with open("temp.gif", "wb") as f:
f.write(gif_file.read())
# Extract frames
frames, error = extract_frames("temp.gif")
if error:
return error
if len(frames) < 2:
return "GIF must have at least 2 frames for analysis."
# Initialize results
results = []
circle_data = []
min_radius = 20
max_radius = min(max(frames[0].shape) // 2, 200) # Limit max radius based on 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=2.0, beta=0)
# Detect circles in the difference image
circles = detect_circles(frame_diff, min_radius, max_radius)
if circles is not None:
circles = np.round(circles[0, :]).astype("int")
for (x, y, r) in circles:
circle_data.append({
"frame": i + 1,
"center": (x, y),
"radius": r
})
# Optional: Save frame with detected circles for visualization
output_frame = cv2.cvtColor(frames[i + 1], cv2.COLOR_GRAY2RGB)
if circles is not None:
for (x, y, r) in circles:
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))
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))
)
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"),
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()