File size: 16,835 Bytes
40f6cf9
 
 
 
38db28c
 
 
 
 
074018f
38db28c
 
 
 
 
 
 
c65a642
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38db28c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
074018f
f7475f7
 
074018f
 
 
 
 
 
f7475f7
38db28c
 
40f6cf9
c65a642
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a3d8e5
c65a642
 
e07265e
c65a642
 
 
0a3d8e5
c65a642
 
 
 
 
0a3d8e5
c65a642
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
074018f
ae35d73
 
 
c65a642
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96caf47
 
c65a642
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40f6cf9
f7475f7
38db28c
 
3302095
 
 
 
c30417d
 
3302095
c30417d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
074018f
2df0af8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
074018f
c65a642
38db28c
 
074018f
 
 
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
import gradio as gr
import cv2
import numpy as np
from scipy.spatial import distance
import requests
from bs4 import BeautifulSoup
from datetime import datetime, timedelta
import urllib.request
from PIL import Image
import io# Function to fetch image filenames from SDO website for today and yesterday
def get_sdo_images():
    base_url = "https://sdo.gsfc.nasa.gov/assets/img/browse/"
    today = datetime.now()
    yesterday = today - timedelta(days=1)
    dates = [today, yesterday]
    image_list = []

    for date in dates:
        date_str = date.strftime("%Y/%m/%d")
        url = f"{base_url}{date_str}/"
        try:
            response = requests.get(url)
            response.raise_for_status()
            soup = BeautifulSoup(response.text, 'html.parser')
            for link in soup.find_all('a'):
                href = link.get('href')
                if href and href.endswith('_1024_HMIBC.jpg'):
                    filename = href.split('/')[-1].replace('_1024_HMIBC.jpg', '')
                    image_list.append((filename, f"{url}{href}"))
        except requests.RequestException as e:
            print(f"Error fetching images for {date_str}: {e}")
    
    image_list.sort(reverse=True, key=lambda x: x[0])
    filenames = [item[0] for item in image_list]
    url_map = {item[0]: item[1] for item in image_list}
    return filenames, url_map

# Function to fetch the selected image
def fetch_image(selected_image, url_map):
    if not selected_image:
        return None
    image_url = url_map.get(selected_image)
    if not image_url:
        return None
    try:
        with urllib.request.urlopen(image_url) as response:
            img_data = response.read()
        img = Image.open(io.BytesIO(img_data))
        return img
    except Exception as e:
        print(f"Error fetching image {selected_image}: {e}")
        return None# Function to analyze the solar image with adjustable parameters
def analyze_solar_image(
    image,
    yellow_hue_min=20, yellow_hue_max=30, yellow_sat_min=100, yellow_val_min=100,
    grey_hue_max=180, grey_sat_max=30, grey_val_min=50, grey_val_max=200,
    blue_hue_min=90, blue_hue_max=150, blue_sat_min=50, blue_val_min=30,
    red_hue_min1=0, red_hue_max1=15, red_hue_min2=155, red_hue_max2=180,
    red_sat_min=50, red_val_min=30,
    flare_dist_threshold=50, min_area_threshold=10, darkness_weight=0.5
):
    if image is None:
        return None, "No image selected or image could not be loaded."

    image = np.array(image)
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    
    yellow_lower = np.array([yellow_hue_min, yellow_sat_min, yellow_val_min])
    yellow_upper = np.array([yellow_hue_max, 255, 255])
    grey_lower = np.array([0, 0, grey_val_min])
    grey_upper = np.array([grey_hue_max, grey_sat_max, grey_val_max])
    blue_lower = np.array([blue_hue_min, blue_sat_min, blue_val_min])
    blue_upper = np.array([blue_hue_max, 255, 255])
    red_lower1 = np.array([red_hue_min1, red_sat_min, red_val_min])
    red_lower2 = np.array([red_hue_min2, red_sat_min, red_val_min])
    red_upper1 = np.array([red_hue_max1, 255, 255])
    red_upper2 = np.array([red_hue_max2, 255, 255])
    
    mask_yellow = cv2.inRange(hsv_image, yellow_lower, yellow_upper)
    mask_grey = cv2.inRange(hsv_image, grey_lower, grey_upper)
    mask_blue = cv2.inRange(hsv_image, blue_lower, blue_upper)
    mask_red1 = cv2.inRange(hsv_image, red_lower1, red_upper1)
    mask_red2 = cv2.inRange(hsv_image, red_lower2, red_upper2)
    mask_red = cv2.bitwise_or(mask_red1, mask_red2)
    
    blue_contours, _ = cv2.findContours(mask_blue, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    red_contours, _ = cv2.findContours(mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    sunspots = []
    used_blue = set()
    used_red = set()
    
    for i, bc in enumerate(blue_contours):
        if i in used_blue:
            continue
        bc_area = cv2.contourArea(bc)
        if bc_area < min_area_threshold:
            continue
    
        bc_centroid = None
        M = cv2.moments(bc)
        if M["m00"] != 0:
            bcX = int(M["m10"] / M["m00"])
            bcY = int(M["m01"] / M["m00"])
            bc_centroid = (bcX, bcY)
    
        if bc_centroid is None:
            continue
    
        sunspot_contours = [bc]
        used_blue.add(i)
    
        for j, rc in enumerate(red_contours):
            if j in used_red:
                continue
            rc_area = cv2.contourArea(rc)
            if rc_area < min_area_threshold:
                continue
    
            M = cv2.moments(rc)
            if M["m00"] != 0:
                rcX = int(M["m10"] / M["m00"])
                rcY = int(M["m01"] / M["m00"])
                rc_centroid = (rcX, rcY)
                dist = distance.euclidean(bc_centroid, rc_centroid)
                if dist < flare_dist_threshold:
                    sunspot_contours.append(rc)
                    used_red.add(j)
    
        for k, bc2 in enumerate(blue_contours):
            if k in used_blue or k == i:
                continue
            bc2_area = cv2.contourArea(bc2)
            if bc2_area < min_area_threshold:
                continue
    
            M = cv2.moments(bc2)
            if M["m00"] != 0:
                bc2X = int(M["m10"] / M["m00"])
                bc2Y = int(M["m01"] / M["m00"])
                bc2_centroid = (bc2X, bc2Y)
                dist = distance.euclidean(bc_centroid, bc2_centroid)
                if dist < flare_dist_threshold:
                    sunspot_contours.append(bc2)
                    used_blue.add(k)
    
        for j, rc in enumerate(red_contours):
            if j in used_red:
                continue
            rc_area = cv2.contourArea(rc)
            if rc_area < min_area_threshold:
                continue
    
            M = cv2.moments(rc)
            if M["m00"] != 0:
                rcX = int(M["m10"] / M["m00"])
                rcY = int(M["m01"] / M["m00"])
                rc_centroid = (rcX, rcY)
                dist = distance.euclidean(bc_centroid, rc_centroid)
                if dist < flare_dist_threshold:
                    sunspot_contours.append(rc)
                    used_red.add(j)
    
        sunspots.append(sunspot_contours)
    
    for j, rc in enumerate(red_contours):
        if j in used_red:
            continue
        rc_area = cv2.contourArea(rc)
        if rc_area >= min_area_threshold:
            sunspots.append([rc])
            used_red.add(j)
    
    sunspot_image = image.copy()
    report = "Solar Image Analysis Report\n\n"
    overall_flare_risk = "Low"
    overall_flare_percentage = 0
    mixed_sunspot_count = 0
    
    for i, sunspot_contours in enumerate(sunspots):
        all_points = np.concatenate(sunspot_contours)
        x, y, w, h = cv2.boundingRect(all_points)
        cv2.rectangle(sunspot_image, (x, y), (x+w, y+h), (0, 255, 0), 2)
        cX, cY = x + w//2, y + h//2
    
        sunspot_region = np.zeros_like(mask_blue)
        cv2.drawContours(sunspot_region, sunspot_contours, -1, 255, thickness=cv2.FILLED)
    
        blue_in_sunspot = cv2.bitwise_and(mask_blue, sunspot_region)
        red_in_sunspot = cv2.bitwise_and(mask_red, sunspot_region)
        blue_contours_in_sunspot, _ = cv2.findContours(blue_in_sunspot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        red_contours_in_sunspot, _ = cv2.findContours(red_in_sunspot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
        blue_area = sum(cv2.contourArea(bc) for bc in blue_contours_in_sunspot)
        red_area = sum(cv2.contourArea(rc) for rc in red_contours_in_sunspot)
    
        blue_darkness = 0
        red_darkness = 0
        if blue_contours_in_sunspot:
            blue_mask = np.zeros_like(hsv_image[..., 2])
            cv2.drawContours(blue_mask, blue_contours_in_sunspot, -1, 255, thickness=cv2.FILLED)
            blue_values = hsv_image[..., 2][blue_mask == 255]
            if len(blue_values) > 0:
                blue_darkness = 255 - np.mean(blue_values)
        if red_contours_in_sunspot:
            red_mask = np.zeros_like(hsv_image[..., 2])
            cv2.drawContours(red_mask, red_contours_in_sunspot, -1, 255, thickness=cv2.FILLED)
            red_values = hsv_image[..., 2][red_mask == 255]
            if len(red_values) > 0:
                red_darkness = 255 - np.mean(red_values)
    
        blue_centroids = []
        red_centroids = []
        min_dist = float('inf')
        mixing_score = 0
        for bc in blue_contours_in_sunspot:
            M = cv2.moments(bc)
            if M["m00"] != 0:
                bcX = int(M["m10"] / M["m00"])
                bcY = int(M["m01"] / M["m00"])
                blue_centroids.append((bcX, bcY))
    
        for rc in red_contours_in_sunspot:
            M = cv2.moments(rc)
            if M["m00"] != 0:
                rcX = int(M["m10"] / M["m00"])
                rcY = int(M["m01"] / M["m00"])
                red_centroids.append((rcX, rcY))
    
        if blue_centroids and red_centroids:
            for bc in blue_centroids:
                for rc in red_centroids:
                    dist = distance.euclidean(bc, rc)
                    min_dist = min(min_dist, dist)
            mixing_score = 1000 / (min_dist + 1)
    
        darkness_score = (blue_darkness + red_darkness) / 2 if blue_darkness and red_darkness else max(blue_darkness, red_darkness)
        flare_risk_percentage = 0
        risk_level = "Low"
    
        if blue_centroids and red_centroids:
            flare_risk_score = (1 - darkness_weight) * (flare_dist_threshold - min_dist) / flare_dist_threshold + darkness_weight * (darkness_score / 255)
            flare_risk_percentage = flare_risk_score * 100
            mixed_sunspot_count += 1
            overall_flare_percentage = (overall_flare_percentage * (mixed_sunspot_count - 1) + flare_risk_percentage) / mixed_sunspot_count
            if flare_risk_percentage >= 70:
                risk_level = "High"
                overall_flare_risk = "High"
            elif flare_risk_percentage >= 40:
                risk_level = "Medium"
                if overall_flare_risk != "High":
                    overall_flare_risk = "Medium"
            else:
                if overall_flare_risk not in ["High", "Medium"]:
                    overall_flare_risk = "Low"
        else:
            flare_risk_percentage = 0
            risk_level = "Low"
    
        line_height = 30
        y = 0
        lines = [f"Sunspot {i+1}", f"Risk: {risk_level}, {flare_risk_percentage:.1f}%"]
        for line in lines:
            cv2.putText(sunspot_image, line, (cX, cY + y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
            y += line_height
    
        for bc in blue_contours_in_sunspot:
            cv2.drawContours(sunspot_image, [bc], -1, (255, 0, 0), 2)
        for rc in red_contours_in_sunspot:
            cv2.drawContours(sunspot_image, [rc], -1, (0, 0, 255), 2)
    
        report += f"Sunspot {i+1}:\n"
        report += f"  Blue Area: {blue_area:.2f} pixels\n"
        report += f"  Red Area: {red_area:.2f} pixels\n"
        report += f"  Minimum Distance Between Blue and Red: {min_dist:.2f} pixels\n"
        report += f"  Mixing Score (Higher = Closer): {mixing_score:.2f}\n"
        report += f"  Blue Darkness (Higher = Darker): {blue_darkness:.2f}\n"
        report += f"  Red Darkness (Higher = Darker): {red_darkness:.2f}\n"
        report += f"  Flare Risk: {risk_level}, {flare_risk_percentage:.1f}%\n\n"
    
    if mixed_sunspot_count == 0:
        overall_flare_percentage = 0
        overall_flare_risk = "Low"
    report += f"Overall Flare Risk: {overall_flare_risk}, {overall_flare_percentage:.1f}%\n"
    
    sunspot_image = cv2.cvtColor(sunspot_image, cv2.COLOR_BGR2RGB)
    return sunspot_image, report

# Gradio interface with adjustable controls
def create_interface():
    with gr.Blocks() as interface:
        # State to store image options and URL map
        image_options = gr.State(value=[])
        url_map = gr.State(value={})

        gr.Markdown("# Solar Image Analysis for Sunspots and Flare Prediction")
        gr.Markdown("Select a solar image from the dropdown or upload your own to detect sunspots, identify blue and red regions, and predict solar flare potential based on magnetic mixing. Adjust the sliders to fine-tune detection.")
        
        with gr.Sidebar(open=False):
            yellow_hue_min = gr.Slider(minimum=0, maximum=180, value=20, label="Yellow Hue Min")
            yellow_hue_max = gr.Slider(minimum=0, maximum=180, value=30, label="Yellow Hue Max")
            yellow_sat_min = gr.Slider(minimum=0, maximum=255, value=100, label="Yellow Saturation Min")
            yellow_val_min = gr.Slider(minimum=0, maximum=255, value=100, label="Yellow Value Min")
            grey_hue_max = gr.Slider(minimum=0, maximum=180, value=180, label="Grey Hue Max")
            grey_sat_max = gr.Slider(minimum=0, maximum=255, value=30, label="Grey Saturation Max")
            grey_val_min = gr.Slider(minimum=0, maximum=255, value=50, label="Grey Value Min")
            grey_val_max = gr.Slider(minimum=0, maximum=255, value=200, label="Grey Value Max")
            blue_hue_min = gr.Slider(minimum=0, maximum=180, value=90, label="Blue Hue Min")
            blue_hue_max = gr.Slider(minimum=0, maximum=180, value=150, label="Blue Hue Max")
            blue_sat_min = gr.Slider(minimum=0, maximum=255, value=50, label="Blue Saturation Min")
            blue_val_min = gr.Slider(minimum=0, maximum=255, value=30, label="Blue Value Min")
            red_hue_min1 = gr.Slider(minimum=0, maximum=180, value=0, label="Red Hue Min 1")
            red_hue_max1 = gr.Slider(minimum=0, maximum=180, value=15, label="Red Hue Max 1")
            red_hue_min2 = gr.Slider(minimum=0, maximum=180, value=100, label="Red Hue Min 2")
            red_hue_max2 = gr.Slider(minimum=0, maximum=180, value=180, label="Red Hue Max 2")
            red_sat_min = gr.Slider(minimum=0, maximum=255, value=50, label="Red Saturation Min")
            red_val_min = gr.Slider(minimum=0, maximum=255, value=30, label="Red Value Min")
            flare_dist_threshold = gr.Slider(minimum=0, maximum=200, value=50, label="Flare Distance Threshold (pixels)")
            min_area_threshold = gr.Slider(minimum=0, maximum=100, value=1, label="Minimum Area Threshold (pixels)")
            darkness_weight = gr.Slider(minimum=0, maximum=1, value=0.5, label="Darkness Weight in Flare Risk (0 to 1)")
        
        with gr.Row():
            with gr.Column():
                update_button = gr.Button("Update Image List")
                image_dropdown = gr.Dropdown(label="Select SDO Image (YYYYMMDD_HHMMSS)", choices=[])
                fetch_button = gr.Button("Fetch Selected Image")
                image_input = gr.Image(type="pil", label="Solar Image")
                analyze_button = gr.Button("Analyze Image")
            
            with gr.Column():
                analyzed_image = gr.Image(type="pil", label="Analyzed Image")
                report = gr.Textbox(label="Analysis Report")

        # Function to update dropdown choices
        def update_image_list():
            filenames, new_url_map = get_sdo_images()
            return gr.update(choices=filenames), new_url_map
    
        # Function to handle fetch button
        def fetch_and_update(selected_image, url_map):
            img = fetch_image(selected_image, url_map)
            return img
    
        # Update image list on button click
        update_button.click(
            fn=update_image_list,
            inputs=None,
            outputs=[image_dropdown, url_map]
        )
    
        # Update image list on app load
        interface.load(
            fn=update_image_list,
            inputs=None,
            outputs=[image_dropdown, url_map]
        )
    
        # Fetch button
        fetch_button.click(
            fn=fetch_and_update,
            inputs=[image_dropdown, url_map],
            outputs=image_input
        )
    
        # Analyze button
        analyze_button.click(
            fn=analyze_solar_image,
            inputs=[
                image_input,
                yellow_hue_min, yellow_hue_max, yellow_sat_min, yellow_val_min,
                grey_hue_max, grey_sat_max, grey_val_min, grey_val_max,
                blue_hue_min, blue_hue_max, blue_sat_min, blue_val_min,
                red_hue_min1, red_hue_max1, red_hue_min2, red_hue_max2,
                red_sat_min, red_val_min,
                flare_dist_threshold, min_area_threshold, darkness_weight
            ],
            outputs=[analyzed_image, report]
        )

    return interface

# Launch the interface
interface = create_interface()
interface.launch()