Spaces:
Paused
Paused
Download app.py from broadfield-dev/detect-cme: direct link, hf CLI and curl.
- Browser
- Download file 5.07 kB
-
https://huggingface.co/spaces/broadfield-dev/detect-cme/resolve/e40096f9a6080884474d5bc3fc5cf34c3805a874/app.py
- Command line
-
hf download hf://spaces/broadfield-dev/detect-cme@e40096f9a6080884474d5bc3fc5cf34c3805a874/app.py
-
curl -L -o app.py https://huggingface.co/spaces/broadfield-dev/detect-cme/resolve/e40096f9a6080884474d5bc3fc5cf34c3805a874/app.py
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() |