2024-05-16 15:43:12 -04:00
import os
import time
2024-05-29 13:49:28 -04:00
import calendar
2024-05-16 15:43:12 -04:00
import datetime
from collections import defaultdict
from multiprocessing import Queue , Process
import re
import warnings
import queue
2024-05-24 21:24:30 -04:00
import traceback
2024-05-16 15:43:12 -04:00
import tqdm
2024-05-25 03:10:46 -04:00
from PIL import Image , ImageDraw , ImageFont
2024-05-16 15:43:12 -04:00
import numpy as np
from matplotlib import pyplot as plt
from astropy . io import fits
import palettable
def lowpriority ( ) :
""" Set the priority of the process to below-normal. """
import sys
try :
sys . getwindowsversion ( )
except AttributeError :
isWindows = False
else :
isWindows = True
if isWindows :
# Based on:
# "Recipe 496767: Set Process Priority In Windows" on ActiveState
# http://code.activestate.com/recipes/496767/
2024-05-24 21:24:30 -04:00
import win32api , win32process , win32con # pywin32
2024-05-16 15:43:12 -04:00
pid = win32api . GetCurrentProcessId ( )
handle = win32api . OpenProcess ( win32con . PROCESS_ALL_ACCESS , True , pid )
win32process . SetPriorityClass ( handle , win32process . BELOW_NORMAL_PRIORITY_CLASS )
else :
import os
os . nice ( 1 )
def bin_ndarray ( ndarray , new_shape , operation = ' mean ' ) :
"""
Bins an ndarray in all axes based on the target shape , by summing or
averaging .
Number of output dimensions must match number of input dimensions and
new axes must divide old ones .
Example
- - - - - - -
>> > m = np . arange ( 0 , 100 , 1 ) . reshape ( ( 10 , 10 ) )
>> > n = bin_ndarray ( m , new_shape = ( 5 , 5 ) , operation = ' sum ' )
>> > print ( n )
[ [ 22 30 38 46 54 ]
[ 102 110 118 126 134 ]
[ 182 190 198 206 214 ]
[ 262 270 278 286 294 ]
[ 342 350 358 366 374 ] ]
"""
operation = operation . lower ( )
if not operation in [ ' sum ' , ' mean ' ] :
raise ValueError ( " Operation not supported. " )
if ndarray . ndim != len ( new_shape ) :
raise ValueError ( " Shape mismatch: {} -> {} " . format ( ndarray . shape ,
new_shape ) )
compression_pairs = [ ( d , c / / d ) for d , c in zip ( new_shape ,
ndarray . shape ) ]
flattened = [ l for p in compression_pairs for l in p ]
ndarray = ndarray . reshape ( flattened )
for i in range ( len ( new_shape ) ) :
op = getattr ( ndarray , operation )
ndarray = op ( - 1 * ( i + 1 ) )
return ndarray
def gamma_correct ( fun ) :
def wrapper ( * args , * * kwargs ) :
args = list ( args )
args [ 0 ] = np . power ( args [ 0 ] , 2.2 )
args [ 1 ] = np . power ( args [ 1 ] , 2.2 )
args = tuple ( args )
result = fun ( * args , * * kwargs )
return np . power ( result , 1 / 2.2 )
return wrapper
def clip_color ( fun ) :
def wrapper ( * args , * * kwargs ) :
return np . clip ( fun ( * args , * * kwargs ) , 0.0 , 1.0 )
return wrapper
# linear_srgb_matrix = np.array([[0.4124, 0.3576, 0.1805],
# [0.2126, 0.7152, 0.0722],
# [0.0193, 0.1192, 0.9505]])
# linear_srgb_matrix_inv = np.array([[ 3.2406, -1.5372, -0.4986],
# [-0.9689, 1.8758, 0.0415],
# [ 0.0557, -0.2040, 1.0570]])
# def linear_color_correction(fun):
# def wrapper(*args, **kwargs):
# args = list(args)
# inds = args[0] <= 0.04045
# ninds = args[0] > 0.04045
# for i in range(2):
# args[i][inds] = args[i][inds] / 12.92
# args[i][ninds] = np.power((args[i][ninds] + 0.055) / 1.055, 2.4)
# for x in range(args[i].shape[0]):
# for y in range(args[i].shape[1]):
# args[i][x,y,:] = np.matmul(linear_srgb_matrix, args[i][x,y,:])
# args = tuple(args)
# result = fun(*args, **kwargs)
# for x in range(result.shape[0]):
# for y in range(result.shape[1]):
# result[x,y,:] = np.matmul(linear_srgb_matrix_inv, result[x,y,:])
# inds = result <= 0.0031308
# ninds = result > 0.0031308
# result[inds] = result[inds] * 12.92
# result[ninds] = np.power(result[ninds], 1.0/2.4) * 1.055 - 0.055
# return result
# return wrapper
@gamma_correct
def composite_alpha_over ( F , B , alpha_F , alpha_B = 1 ) :
return ( F * alpha_F + B * alpha_B * ( 1 - alpha_F ) ) / ( alpha_F + alpha_B * ( 1 - alpha_F ) )
def composite_alpha_blend ( F , B , alpha ) :
return F * alpha + B * ( 1 - alpha )
def linear_burn ( F , B ) :
burn = F + B - 1
burn [ burn < 0.0 ] = 0.0
return burn
def difference ( F , B ) :
d = np . abs ( F - B )
if d . shape [ 2 ] == 4 : # Preserve alpha of base image
d [ : , : , 3 ] = B [ : , : , 3 ]
return d
@clip_color
def linear_light ( F , B ) :
result = np . zeros_like ( F )
inds = F < = 0.5
ninds = F > 0.5
result [ inds ] = B [ inds ] + 2.0 * F [ inds ] - 1
result [ ninds ] = 2.0 * ( F [ ninds ] - 0.5 ) + B [ ninds ]
return result
@clip_color
def hard_light ( F , B ) :
result = np . zeros_like ( F )
inds = B < 0.5
ninds = B > = 0.5
result [ inds ] = 2 * F [ inds ] * B [ inds ]
result [ ninds ] = 1 - ( 2 * ( 1 - F [ ninds ] ) * ( 1 - B [ ninds ] ) )
return result
@clip_color
def color_dodge ( F , B ) :
return B / ( 1.000001 - F )
@clip_color
def exclusion ( F , B ) :
d = F + B - 2 * F * B
if d . shape [ 2 ] == 4 : # Preserve alpha of base image
d [ : , : , 3 ] = B [ : , : , 3 ]
return d
@clip_color
def saturation ( img , R , G , B ) :
img [ : , : , 0 ] * = R
img [ : , : , 1 ] * = G
img [ : , : , 2 ] * = B
return img
@clip_color
def contrast ( img , c , b ) :
return ( img - 0.5 ) * c + 0.5 + b * c
def rgb_to_hsl ( img ) :
r = img [ : , : , 0 ]
g = img [ : , : , 1 ]
b = img [ : , : , 2 ]
cmax = np . copy ( r )
cmax [ g > cmax ] = g [ g > cmax ]
cmax [ b > cmax ] = b [ b > cmax ]
cmin = np . copy ( r )
cmin [ g < cmin ] = g [ g < cmin ]
cmin [ b < cmin ] = b [ b < cmin ]
delta = cmax - cmin
# Calc hue
hue = np . zeros_like ( r )
inds = cmax == r
with warnings . catch_warnings ( ) :
warnings . filterwarnings ( ' ignore ' )
hue [ inds ] = 60 * np . mod ( ( g [ inds ] - b [ inds ] ) / delta [ inds ] , 6 )
inds = cmax == g
hue [ inds ] = 60 * ( ( b [ inds ] - r [ inds ] ) / delta [ inds ] + 2 )
inds = cmax == b
hue [ inds ] = 60 * ( ( r [ inds ] - g [ inds ] ) / delta [ inds ] + 4 )
hue [ np . isnan ( hue ) ] = 0
hue [ hue < 0 ] = hue [ hue < 0 ] + 360 # Make negative hue values positive behind 360
# Calc lightness / luminance
luminance = ( cmax + cmin ) / 2.0
# Calc saturation
saturation = np . zeros_like ( r )
inds = delta != 0
saturation [ inds ] = delta [ inds ] / ( 1.0 - np . abs ( 2 * luminance [ inds ] - 1.0 ) )
# Multiply luminance and saturation by 100 to scale them to the appropriate range (0-100)
saturation * = 100.0
luminance * = 100.0
if img . shape [ 2 ] == 4 : # Preserve alpha of original image
return np . stack ( [ hue , saturation , luminance , img [ : , : , 3 ] ] , - 1 )
else :
return np . stack ( [ hue , saturation , luminance ] , - 1 )
def hsl_to_rgb ( img ) :
hue = img [ : , : , 0 ]
saturation = img [ : , : , 1 ]
luminance = img [ : , : , 2 ]
saturation / = 100.0
luminance / = 100.0
c = ( 1.0 - np . abs ( 2.0 * luminance - 1.0 ) ) * saturation
x = c * ( 1.0 - np . abs ( ( hue / 60.0 ) % 2.0 - 1.0 ) )
m = luminance - c / 2.0
r = np . zeros_like ( hue )
g = np . zeros_like ( hue )
b = np . zeros_like ( hue )
inds1 = np . logical_and ( 0.0 < = hue , hue < 60.0 )
r [ inds1 ] = c [ inds1 ]
g [ inds1 ] = x [ inds1 ]
inds2 = np . logical_and ( 60.0 < = hue , hue < 120.0 )
r [ inds2 ] = x [ inds2 ]
g [ inds2 ] = c [ inds2 ]
inds3 = np . logical_and ( 120.0 < = hue , hue < 180.0 )
g [ inds3 ] = c [ inds3 ]
b [ inds3 ] = x [ inds3 ]
inds4 = np . logical_and ( 180.0 < = hue , hue < 240.0 )
g [ inds4 ] = x [ inds4 ]
b [ inds4 ] = c [ inds4 ]
inds5 = np . logical_and ( 240.0 < = hue , hue < 300.0 )
r [ inds5 ] = x [ inds5 ]
b [ inds5 ] = c [ inds5 ]
inds6 = np . logical_and ( 240.0 < = hue , hue < 300.0 )
r [ inds6 ] = c [ inds6 ]
b [ inds6 ] = x [ inds6 ]
r + = m
g + = m
b + = m
if img . shape [ 2 ] == 4 : # Preserve alpha of original image
return np . stack ( [ r , g , b , img [ : , : , 3 ] ] , - 1 )
else :
return np . stack ( [ r , g , b ] , - 1 )
def generate_composite ( work_queue , result_queue ) :
2024-05-18 19:06:07 -04:00
lowpriority ( )
2024-05-16 15:43:12 -04:00
cmaps = [ palettable . cmocean . sequential . Ice_5 . mpl_colormap ,
palettable . cmocean . sequential . Ice_20 . mpl_colormap ,
palettable . cmocean . sequential . Turbid_5_r . mpl_colormap ,
palettable . cmocean . sequential . Turbid_20_r . mpl_colormap ,
2024-05-18 19:06:07 -04:00
plt . colormaps . get_cmap ( ' gist_heat ' ) ,
plt . colormaps . get_cmap ( ' afmhot ' ) ]
2024-05-16 15:43:12 -04:00
# Modify colormaps to start at perfect black (when they otherwise start at very dark colors)
for cmi in range ( 0 , 4 ) :
for c in [ ' red ' , ' green ' , ' blue ' ] :
for i in range ( 3 ) :
cmaps [ cmi ] . _segmentdata [ c ] [ 0 ] [ i ] = 0.0
# These values are used to map floating point radiance values to colors using the above color maps.
vmins = [ 0.050 , 0.05 , 00.100 , 00.10 , 00.100 , 00.1 ]
vmaxs = [ 8.000 , 8.00 , 20.000 , 30.00 , 40.000 , 90.0 ]
gammas = [ 0.375 , 0.40 , 00.425 , 00.45 , 00.475 , 00.5 ]
2024-05-24 21:24:30 -04:00
trimx = 64
trimy = 100
2024-05-25 03:10:46 -04:00
fnt1 = ImageFont . truetype ( " OpenSans-Regular.ttf " , size = 24 )
fnt2 = ImageFont . truetype ( " OpenSans-Regular.ttf " , size = 16 )
2024-05-16 15:43:12 -04:00
while True :
try :
job = work_queue . get ( )
if job is None :
result_queue . cancel_join_thread ( )
return
2024-05-24 21:24:30 -04:00
files_this_timestamp , timestamp , processed_images_dir , synthetic_data = job
2024-05-25 03:10:46 -04:00
synthetic_filepath = os . path . join ( processed_images_dir , f " Composite- { int ( timestamp ) } _s.jpg " )
normal_filepath = os . path . join ( processed_images_dir , f " Composite- { int ( timestamp ) } .jpg " )
filepath = None
2024-05-24 21:24:30 -04:00
if synthetic_data :
2024-05-25 03:10:46 -04:00
if os . path . isfile ( synthetic_filepath ) :
result_queue . put ( ( " Exists " , timestamp ) )
continue
filepath = synthetic_filepath
2024-05-24 21:24:30 -04:00
else :
2024-05-25 03:10:46 -04:00
if os . path . isfile ( synthetic_filepath ) :
os . remove ( synthetic_filepath )
if os . path . isfile ( normal_filepath ) :
result_queue . put ( ( " Exists " , timestamp ) )
continue
filepath = normal_filepath
2024-05-16 15:43:12 -04:00
base_imgs = [ ]
for i in range ( 6 ) :
2024-05-24 21:24:30 -04:00
raw_data = np . flip ( fits . getdata ( files_this_timestamp [ i ] ) , 0 ) [ trimy : - trimy , trimx : - trimx ] # Trim and reorient the image to match our final desired dimensions
2024-05-16 15:43:12 -04:00
raw_data [ raw_data < 0.0 ] = 0.0 # Remove non-zero data because it doesn't makes sense (supposed to be std Radiance)
2024-05-24 21:24:30 -04:00
base_imgs . append ( cmaps [ i ] ( np . clip ( ( raw_data - vmins [ i ] ) / vmaxs [ i ] , 0 , 1.0 ) * * gammas [ i ] ) [ : , : , : 3 ] )
2024-05-16 15:43:12 -04:00
# plt.figure(image_names[i])
# plt.imshow(base_imgs[-1])
# plt.figure("Initial Blend")
2024-05-24 21:24:30 -04:00
composite_image_data = composite_alpha_over ( base_imgs [ 4 ] , base_imgs [ 5 ] , 0.2 ) * * 0.5
2024-05-16 15:43:12 -04:00
# plt.imshow(composite_image_data)
# plt.figure("Linear Burn with 304")
composite_image_data = composite_alpha_over ( linear_burn ( base_imgs [ 5 ] , composite_image_data ) , composite_image_data , 0.60 )
# plt.imshow(composite_image_data)
# plt.figure("Light Ops with mid bands")
mix_img = composite_alpha_over ( exclusion ( base_imgs [ 3 ] , composite_image_data ) , composite_image_data , 0.95 )
2024-05-24 21:24:30 -04:00
mix_img = composite_alpha_over ( linear_light ( base_imgs [ 2 ] , mix_img ) , mix_img , 1.0 ) * * 0.75
2024-05-16 15:43:12 -04:00
composite_image_data = composite_alpha_over ( mix_img , composite_image_data , 0.5 )
# plt.imshow(composite_image_data)
# plt.figure("Mix in mid 131")
composite_image_data = composite_alpha_over ( base_imgs [ 2 ] , composite_image_data , 0.25 ) # Mix in a small amount of 131Å for the nice streamers
# plt.imshow(composite_image_data)
# plt.figure("Mid diff")
mix_img = difference ( base_imgs [ 3 ] , base_imgs [ 2 ] ) # 171Å - 131Å
# plt.imshow(mix_img)
# plt.figure("HSL ops with mid")
comp_hsl = rgb_to_hsl ( composite_image_data )
mix_img_hsl = rgb_to_hsl ( mix_img )
del mix_img
base_img2_hsl = rgb_to_hsl ( base_imgs [ 2 ] )
base_img3_hsl = rgb_to_hsl ( base_imgs [ 3 ] )
comp_hsl = np . copy ( comp_hsl )
comp_hsl [ : , : , 0 ] + = 0.025 * mix_img_hsl [ : , : , 0 ] # Rotate hue based on mix_1_hsl
del mix_img_hsl
comp_hsl [ : , : , 0 ] [ comp_hsl [ : , : , 0 ] > 360.0 ] - = 360.0
comp_hsl [ : , : , 1 ] - = 0.1 * base_img3_hsl [ : , : , 1 ] # Reduce saturation based on base_img3
comp_hsl [ : , : , 1 ] = np . clip ( comp_hsl [ : , : , 1 ] , 0.0 , 100.0 )
comp_hsl [ : , : , 2 ] + = 0.5 * base_img2_hsl [ : , : , 2 ] # Boost luminance based on base_img2
comp_hsl [ : , : , 1 ] = np . clip ( comp_hsl [ : , : , 2 ] , 0.0 , 100.0 )
composite_image_data = hsl_to_rgb ( comp_hsl )
del comp_hsl
composite_image_data = saturation ( composite_image_data , 1.0 , 0.8 , 0.1 ) # Remove some blue and green
# plt.imshow(composite_image_data)
# plt.figure("Hue shifted 131")
mix_img_hsl = rgb_to_hsl ( base_imgs [ 1 ] )
mix_img_hsl [ : , : , 0 ] - = 50
mix_img_hsl [ : , : , 0 ] [ mix_img_hsl [ : , : , 0 ] < 0.0 ] + = 360.0
mix_img = hsl_to_rgb ( mix_img_hsl )
del mix_img_hsl
# plt.imshow(mix_img)
# plt.figure("Hard Light with hue shifted 131")
composite_image_data = composite_alpha_over ( hard_light ( mix_img , composite_image_data ) , composite_image_data , 0.2 )
# plt.imshow(composite_image_data)
# plt.figure("Adjusted 094")
mix_img = saturation ( base_imgs [ 0 ] , 1.15 , 1.2 , 1.05 )
mix_img = contrast ( mix_img , 1.5 , 0.0 )
# plt.imshow(mix_img)
# plt.figure("Color Dodge with adjusted 094")
composite_image_data = composite_alpha_over ( color_dodge ( mix_img , composite_image_data ) , composite_image_data , 0.5 )
# plt.imshow(composite_image_data)
# plt.figure("Exclusion with adjusted 094")
composite_image_data = composite_alpha_over ( exclusion ( mix_img , composite_image_data ) , composite_image_data , 0.65 )
# plt.imshow(composite_image_data)
del mix_img
# plt.figure("Final Image")
composite_image_data = saturation ( composite_image_data , 1.0 , 1.1 , 1.2 )
composite_image_data = contrast ( composite_image_data , 1.20 , 0.00 )
# plt.imshow(composite_image_data)
# We have our final image
# plt.show()
# Now shrink the component images and assemble them alongside the composite.
2024-05-24 21:24:30 -04:00
new_dimx = composite_image_data . shape [ 1 ] / / 3
new_dimy = composite_image_data . shape [ 0 ] / / 3
2024-05-16 15:43:12 -04:00
# Enlarge the composite to fit the new images
2024-05-24 21:24:30 -04:00
composite_image_data = np . pad ( composite_image_data , ( ( 0 , 0 ) , ( new_dimx , new_dimx ) , ( 0 , 0 ) ) )
2024-05-16 15:43:12 -04:00
for i in range ( 6 ) :
2024-05-24 21:24:30 -04:00
img = base_imgs [ i ]
img = bin_ndarray ( img , ( new_dimy , new_dimx , 3 ) ) # Shrink down to 1/3 for assembly
2024-05-16 15:43:12 -04:00
img = contrast ( img , 1.25 , 0.0 )
2024-05-24 21:24:30 -04:00
xdimoff = i % 2 * ( composite_image_data . shape [ 1 ] - new_dimx )
ydimoff = i / / 2 * new_dimy
composite_image_data [ ydimoff : ydimoff + new_dimy , xdimoff : xdimoff + new_dimx , : ] = img
2024-05-16 15:43:12 -04:00
img = Image . fromarray ( ( 255 * composite_image_data ) . astype ( ' uint8 ' ) )
timestring = datetime . datetime . fromtimestamp ( timestamp , tz = datetime . UTC ) . strftime ( ' % Y- % m- %d % H: % M: % S ' )
2024-05-25 03:10:46 -04:00
ImageDraw . Draw ( img ) . text ( ( 602 , 15 ) , f " NOAA GOES Satellite SUVI Composite - { timestring } UTC " , ( 255 , 255 , 255 ) , font = fnt1 )
2024-05-16 15:43:12 -04:00
for i in range ( 6 ) : # Draw component angstrom labels
if i % 2 == 0 :
xdimtxtoff = 5
else :
xdimtxtoff = composite_image_data . shape [ 1 ] - 44
2024-05-25 03:10:46 -04:00
ydimtxtoff = i / / 2 * new_dimy + new_dimy / 2.0 - 14
ImageDraw . Draw ( img ) . text ( ( xdimtxtoff , ydimtxtoff ) , image_names [ i ] , font = fnt2 )
2024-05-24 21:24:30 -04:00
img . save ( filepath , quality = 95 )
2024-05-16 15:43:12 -04:00
result_queue . put ( ( " Created " , timestamp ) )
except KeyboardInterrupt :
return
except Exception as e :
2024-05-24 21:24:30 -04:00
traceback . print_exception ( e )
2024-05-16 15:43:12 -04:00
result_queue . put ( ( e , timestamp ) )
2024-05-24 21:24:30 -04:00
image_names = [ " 094A " , " 131A " , " 171A " , " 195A " , " 284A " , " 304A " ]
2024-05-16 15:43:12 -04:00
2024-05-24 21:24:30 -04:00
if __name__ == " __main__ " :
2024-06-04 17:18:51 -04:00
stored_fits_dirs = [ r " .. \ Data \ goes16 \ l2 \ data " , r " .. \ Data \ goes18 \ l2 \ data " ]
processed_images_dirs = [ r " .. \ composite \ goes16 " , r " .. \ composite \ goes18 " ]
starttime = calendar . timegm ( datetime . datetime ( 2023 , 1 , 1 , tzinfo = datetime . timezone . utc ) . timetuple ( ) )
stoptime = calendar . timegm ( datetime . datetime ( 2024 , 1 , 1 , tzinfo = datetime . timezone . utc ) . timetuple ( ) )
2024-05-24 21:24:30 -04:00
regex_filename = r " dr_suvi-l2-ci \ d {3} _g(16|18)_s \ S* \ _f.fits$ "
2024-05-18 19:06:07 -04:00
nworkers = 20
2024-05-25 03:10:46 -04:00
max_time_gap = 3
2024-06-04 17:18:51 -04:00
fill_missing_data = False
2024-05-24 21:24:30 -04:00
file_prefixes = [ " dr_suvi-l2-ci " + n [ : - 1 ] for n in image_names ]
2024-05-16 15:43:12 -04:00
# Testing
2024-05-18 19:06:07 -04:00
# stored_fits_dirs = [r"..\fits_test_2024"]
2024-05-16 15:43:12 -04:00
work_queue = Queue ( maxsize = nworkers )
result_queue = Queue ( )
workers = [ ]
for i in range ( nworkers ) :
p = Process ( target = generate_composite , args = ( work_queue , result_queue ) , daemon = True )
p . start ( )
workers . append ( p )
lowpriority ( )
ncreated = 0
nexists = 0
nfailed = 0
2024-05-25 03:10:46 -04:00
black_image = Image . fromarray ( np . zeros ( ( 1080 , 1920 , 3 ) , dtype = ' uint8 ' ) )
2024-05-16 15:43:12 -04:00
try :
2024-06-04 17:18:51 -04:00
for stored_fits_dir , processed_images_dir in zip ( stored_fits_dirs , processed_images_dirs ) :
files_by_timestamp = defaultdict ( list )
num_found_files = 0
2024-05-16 15:43:12 -04:00
os . makedirs ( processed_images_dir , exist_ok = True )
filename_tester = re . compile ( regex_filename )
print ( f " Searching for FITS files in: { stored_fits_dir } " )
for root , dirs , files in tqdm . tqdm ( os . walk ( stored_fits_dir ) , desc = " Searching " ) :
for f in files :
if filename_tester . match ( f ) :
file_parts = f . split ( " _ " )
2024-05-25 03:10:46 -04:00
# measurement = file_parts[1]
# sattelite = file_parts[2]
measure_end_time = int ( datetime . datetime . strptime ( file_parts [ 4 ] [ 1 : 16 ] + " +0000 " , " % Y % m %d T % H % M % S % z " ) . timestamp ( ) )
2024-05-29 13:49:28 -04:00
if ( measure_end_time > = starttime ) and ( measure_end_time < stoptime ) :
2024-06-04 17:18:51 -04:00
files_by_timestamp [ measure_end_time ] . append ( os . path . join ( root , f ) )
2024-05-29 13:49:28 -04:00
num_found_files + = 1
2024-05-16 15:43:12 -04:00
2024-06-04 17:18:51 -04:00
print ( f " Found { num_found_files } FITS files. Starting conversion. " )
if num_found_files == 0 :
exit ( 3 )
sorted_times = sorted ( list ( files_by_timestamp . keys ( ) ) )
min_time = sorted_times [ 0 ]
max_time = sorted_times [ - 1 ]
diff_times = np . diff ( sorted_times )
unique , counts = np . unique ( diff_times , return_counts = True )
interval = unique [ 0 ] # This is the amount of time between each sample in seconds
assert np . sum ( ( unique % interval ) > 0 ) == 0 # Ensure all our timestamps align perfectly with our interval
last_good_files = None
last_good_file_times = None
# Lets find the first and last timestamps in the sorted_times from our files which actually have a full set of 6/6 images available
# This check will prevent partially downloaded sets of data from generating composite images which have "filled in" data from detected gaps
# which would have been later filled with downloaded imagery.
f = None
l = None
for i , timestamp in enumerate ( sorted_times ) :
if len ( files_by_timestamp [ timestamp ] ) == 6 :
f = i
2024-05-24 21:24:30 -04:00
break
2024-06-04 17:18:51 -04:00
for i , timestamp in enumerate ( reversed ( sorted_times ) ) :
if len ( files_by_timestamp [ timestamp ] ) == 6 :
l = len ( sorted_times ) - 1 - i
break
assert f is not None # Check to make sure we found valid indices
assert l is not None
assert f != l
sorted_times = sorted_times [ f : l ] # Limit our composite image generation to only files within the valid range
last_good_files = files_by_timestamp [ sorted_times [ 0 ] ]
for timestamp in tqdm . tqdm ( sorted_times , desc = " Creating Composite Solar Images " ) :
if timestamp < min_time or timestamp > max_time :
continue
# Collect completed jobs and record completion status
while True :
try :
result = result_queue . get_nowait ( )
if result [ 0 ] == " Exists " :
nexists + = 1
elif result [ 0 ] == " Created " :
ncreated + = 1
2024-05-29 13:49:28 -04:00
else :
2024-06-04 17:18:51 -04:00
print ( f " A worker encountered an exception on job { result [ 1 ] } : { result [ 0 ] } " )
nfailed + = 1
except queue . Empty :
break
# Submit new jobs
files_this_timestamp = files_by_timestamp [ timestamp ]
files_this_timestamp = sorted ( files_this_timestamp )
if ( not len ( files_this_timestamp ) == 6 ) :
if fill_missing_data :
print ( f " Invalid or incomplete sensor records for { timestamp } - { len ( files_this_timestamp ) } /6 filling from last good data. " )
files_for_job = [ ]
for i , prefix in enumerate ( file_prefixes ) :
found = False
for f in files_this_timestamp :
filename = os . path . split ( f ) [ - 1 ]
if filename . startswith ( prefix ) :
files_for_job . append ( f )
last_good_files [ i ] = f
last_good_file_times [ i ] = timestamp
found = True
break
if not found : # We did not find this prefix, use the last good file
time_gap = ( timestamp - last_good_file_times [ i ] ) / / interval
if time_gap < = max_time_gap :
files_for_job . append ( last_good_files [ i ] )
else :
print ( f " Detected a gap of { time_gap } frames at { timestamp } " )
break
if len ( files_for_job ) == 6 :
work_queue . put ( ( files_for_job , timestamp , processed_images_dir , True ) )
else : # We have a complete file set, update the last_good_files
last_good_files = files_this_timestamp
last_good_file_times = [ timestamp for _ in last_good_files ]
work_queue . put ( ( files_this_timestamp , timestamp , processed_images_dir , False ) )
2024-05-16 15:43:12 -04:00
except KeyboardInterrupt :
print ( " Finishing current jobs and exiting " )
for _ in range ( nworkers ) :
try :
work_queue . put ( None , timeout = 10.0 )
except :
break
for w in workers :
2024-05-24 21:24:30 -04:00
# w.join(10.0)
w . join ( )
2024-05-16 15:43:12 -04:00
print ( f " Created { ncreated } | Already had { nexists } | Failed { nfailed } " )