예제 #1
0
num_images_group = np.size(t_group)
        
# Read the image

hdulist = fits.open(dir_images + '/' + file)
data = hdulist['PRIMARY'].data
stretch = astropy.visualization.PercentileInterval(10)  # PI(90) scales array to 5th .. 95th %ile. 

# Create the backplanes

(planes, descs) = hbt.compute_backplanes(dir_images + '/' + file)

am = planes['Ang_Metis']

dx_total =  ( t_group['dx_opnav'][index_image] )
dy_total =  ( t_group['dy_opnav'][index_image])

(dx_total, dy_total) =(52,70)
  
am_roll = np.roll(np.roll(am, -dy_total, axis=0), -dx_total, axis=1)

# Plot it

data_stretch = stretch(hbt.remove_sfit(data,degree=5))

plt.imshow(data_stretch)
plt.show()

plt.imshow( data_stretch * (am_roll > 0.0001))
plt.show()
예제 #2
0
if __name__ == '__main__':

    import astropy.visualization
    
    stretch_percent = 90    
    stretch = astropy.visualization.PercentileInterval(stretch_percent) # PI(90) scales to 5th..95th %ile.
    
    arr = hbt.nh_get_straylight_median(6,[120,121,122,123,124],do_sfit=True,  power=5)
    plt.imshow(stretch(arr))
    
    arr = hbt.nh_get_straylight_median(6,[120,121,122,123,124],do_sfit=True, power=10)
    plt.imshow(stretch(arr))

    arr = hbt.nh_get_straylight_median(6,[120,121,122,123,124],do_sfit=False, power=2)
    plt.imshow(arr)    
    
    arr = hbt.nh_get_straylight_median(6,[120,121,122,123,124],do_sfit=True, power=5)
    plt.imshow(arr)
    
    arr_bg = hbt.nh_get_straylight_median(7,[49,50,51],do_sfit=False)
    arr_image = hbt.nh_get_straylight_median(7,[52], do_sfit=False)
    
    plt.imshow(stretch(hbt.remove_sfit(arr_image - arr_bg,degree=1)))
    
    # Now try one for real, from 8/0-48 sequence.
    # There are a lot of ring artifacts left we wnt to get rid of.
    
    arr = hbt.nh_get_straylight_median(8,hbt.frange(0,48), do_sfit=True, power=5)
    plt.imshow(stretch(hbt.remove_sfit(arr,degree=1)))
    
예제 #3
0
# Read and plot the LHS image of a 1x3 mosaic of Gossamer ring images.
# This is a lot of lines of code, but most of it is just stacking images and scaling, which should be rewritten 
# into a function.

index_group = 6
index_image = hbt.frange(181,184).astype(int) # Which frame from the group do we extract?

image_stray = hbt.nh_get_straylight_median(6, hbt.frange(185,188)) # 

image_arr = np.zeros((1024, 1024, 4))
for i in range(4):
    image = hbt.read_lorri(t_group[index_image[i]]['Filename'], autozoom=True)
    image_arr[:,:,i] = image

image_sum = np.sum(image_arr,axis=2)
image_sum_sfit = hbt.remove_sfit(image_sum, 5)

(image_stray_scale,junk) = hbt.normalize_images(image_stray, image_sum_sfit)

final = image_sum_sfit - image_stray_scale
final = hbt.remove_brightest(final, 0.95, symmetric=True)
final = hbt.remove_sfit(final,5)

ax=plt.subplot(1,3,2)
name = (t_group[index_image[0]]['Shortname'] + ' .. ' + 
        t_group[index_image[3]]['Shortname']).replace('lor_', '').replace('_0x633_sci_1.fit','')
plt.title(name)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.imshow(final)
#plt.show()
예제 #4
0
#stop

image_stray = hbt.nh_get_straylight_median(index_group, hbt.frange(8,15))

nx = np.shape(image)[0]
ny = np.shape(image)[1]

#image_stray = '/Users/throop/data/NH_Jring/out/straylight_median_g7_n8..15_sfit5,5.pkl'

xvals = range(nx)
yvals = range(ny)
(x,y) = np.meshgrid(xvals,yvals)

stretch = astropy.visualization.PercentileInterval(90)  # PI(90) scales array to 5th .. 95th %ile. 

image_s5 = hbt.remove_sfit(image,5)

plt.rcParams['figure.figsize'] = 5,5

#plt.subplot(2,2,1)
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(x, y, stretch(image_stray), rstride=10, cstride=10, cmap=cm.coolwarm,
                       linewidth=0, antialiased=False)
plt.show()

#plt.subplot(2,2,2)
plt.imshow(stretch(image_stray))
plt.show()

#plt.subplot(2,2,3)
예제 #5
0
file_backplane = file_image.replace('.fit', '_planes.pkl')

file_short = file_image.replace('.fit', '').replace('_sci', '').replace('_opnav', '')[0:-8]

#==============================================================================
# Read image and all associated files
#==============================================================================

# Read the image and header

power_sfit = 5

im     = hbt.read_lorri(dir_images + file_image)
im     = hbt.lorri_destripe(im)
im     = hbt.remove_sfit(im, power_sfit)

header = hbt.get_image_header(dir_images + file_image)

# Load the WCS coords (so we can overlay the ring)

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    w = WCS(dir_images + file_image)

# Process headaer

dx_pix = header['NAXIS1']
dy_pix = header['NAXIS2']
et     = header['SPCSCET']
          'mpf_0299793106_0x539_sci_2.fit', # O_RING_DEP_MVICFRAME_202  
          'mpf_0308706966_0x539_sci_6.fit'] # O_RING_DEP_MVICFRAME_305
          
stretch_percent = 90

hdulist = []

stretch = astropy.visualization.PercentileInterval(stretch_percent)  # PI(90) scales to 5th..95th %ile. 

plt.set_cmap('Greys_r')
        
for file in files:
    hdulist.append(fits.open(dir + file))

    
plt.imshow(hbt.remove_sfit(stretch(hdulist[2]['PRIMARY'].data[0,:,:]),2))

# Load the mosaics

files2 = ['ringdep_mos_v1_wcs.fits', 'mvic_d202_mos_v1.fits', 'mvic_d211_mos_v1_wcs.fits', 
                'mvic_d305_sum_mos_v1_wcs.fits']    
hdulist2 = []

for file in files2:
    hdulist2.append(fits.open(dir + file))

for hdu in hdulist2:
    print("Size = ")

plt.show()
    
        plt.subplot(1, 4, 1)
        plt.imshow(stretch(image_raw))
        plt.title('stretch(image_raw)')

        plt.subplot(1, 4, 2)
        plt.imshow(stretch(im))
        plt.imshow(mask_objects, alpha=0.2, cmap='plasma')
        plt.title('stretch(image_raw), overlay w mask_objects')

        plt.subplot(1, 4, 3)
        plt.imshow(mask_objects)
        plt.title('mask_objects')
        plt.show()

        plt.subplot(1, 4, 1)
        plt.imshow(stretch(hbt.remove_sfit(image_raw, degree=5)),
                   cmap='plasma')
        plt.title('hbt.remove_sfit(image_raw)')

        plt.subplot(1, 4, 3)
        plt.imshow(stretch(
            hbt.remove_sfit(image_raw,
                            degree=5,
                            mask=np.logical_not(mask_objects))),
                   cmap='plasma')
        plt.title('hbt.remove_sfit(mask=mask_objects)')

        plt.show()

###
#    method = 'String'
def nh_jring_process_image(image_raw,
                           method,
                           vars,
                           index_group=-1,
                           index_image=-1,
                           mask_sfit=None):
    """
    Return image with stray light removed. Flux is preserved and no clipping is done.

    Parameters
    -----
    
    image_raw: 
        NumPy array with the data image.

    method:    
        Method of background subtraction, which can be 'Next', 'Prev', 'Polynomial', 'String', 'None', 
        'Grp Num Frac Pow', etc.
        
        In general 'String' is the most flexible, and recommended.
        It can be things like "5/0-10 r3 *2 mask_7_10':
            
        - Make a median of Group 5, Images 0-10
        - Rotate them all by 270 degrees
        - Scale it to the data image
        - Multiply background image by 2
        - Subtract data - background
        - Remove a 5th degree polynomial from the result [always done, regardless]
        - Load the Photoshop-created mask file "mask_7_10" and incorporate via a tuple
        - Return final result

    vars:
        The argument to the 'method'. Can be an exponent, a file number, a string, etc -- arbitrary, as needed. 
             
    index_group:
        Index of current image. Not used except for Next/Prev.

    index_image:
        Index of current group. Not used except for Next/Prev.
        
    mask_sfit:
        An optional mask to be applied when doing the sfit. Ony pixels with True will be used.
        This is to mask out satellites, stars, CRs, etc. so they don't affect the sfit().
             
    """

    stretch_percent = 90
    stretch = astropy.visualization.PercentileInterval(
        stretch_percent)  # PI(90) scales to 5th..95th %ile.

    # Load the arrays with all of the filenames

    dir_out = '/Users/throop/Data/NH_Jring/out/'

    file_pickle = dir_out + 'nh_jring_read_params_571.pkl'  # Filename to get filenames, etc.

    lun = open(file_pickle, 'rb')
    t = pickle.load(lun)
    lun.close()

    # Initialize variables

    DO_MASK = False  # We set this based on whether a mask is passed in or not

    dir_mask_stray = dir_out.replace(
        'out', 'masks')  # Directory where the mask files are

    # Process the group names. Some of this is duplicated logic -- depends on how we want to use it.

    groups = astropy.table.unique(t, keys=(['Desc']))['Desc']

    if (index_group != -1):  # Only do this if we actually passed a group in
        groupmask = (t['Desc'] == groups[index_group])
        t_group = t[groupmask]

        # Look up the filename, in case we need it.

        file_image = t_group['Filename'][index_image]

    if (method == 'Previous'):
        file_prev = t_group['Filename'][index_image - 1]
        #            print "file =      " + filename
        print("file_prev = " + file_prev)
        image_bg = hbt.read_lorri(file_prev, frac_clip=1.0, bg_method='None')
        image_fg = image_raw
        image = image_fg - image_bg
        image_processed = image

    if (method == 'Next'):
        file_next = t_group['Filename'][index_image + 1]
        image_bg = hbt.read_lorri(file_next,
                                  frac_clip=1.0,
                                  bg_method='None',
                                  autozoom=True)
        image_fg = image_raw
        image = image_fg - image_bg
        image_processed = image

    if (method == 'Median'):  # XXX not working yet
        file_prev = t_group['Filename'][index_image - 1]
        image_bg = hbt.read_lorri(file_prev, frac_clip=1.0, bg_method='None')
        image_fg = image_raw
        image = image_fg - image_bg
        image_processed = image

    if (method == 'Polynomial'):

        power = vars
        image = image_raw - hbt.sfit(
            image_raw, power)  # Look up the exponenent and apply it
        image_processed = image

    if (
            method == 'Grp Num Frac Pow'
    ):  # Specify to subtract a specified group#/image#, mult factor, and sfit power.
        # I thought this would be useful, but it turns out we usually need to subtract
        # a median of multiple images -- not just one -- so this is not very useful.
        # Plus, the best power is usually 5, and the best frac can be calc'd
        # with a linfit.

        if (np.size(vars) == 0):  # If no args passed, just plot the image
            power = 0
            frac = 0
            image = image_raw

        if (np.size(vars) == 1):  # One variable: interpret as exponent
            power = float(vars[0])
            frac = 0
            image = image_raw
            image_bg = hbt.sfit(image, power, mask=mask_sfit)
            image = image - image_bg

        if (np.size(vars) == 2
            ):  # Two variables: interpret as group num and file num
            (grp, num) = vars
            frac = 1
            power = 0

        if (
                np.size(vars)
        ) == 3:  # Three variables: interpret as group num, file num, fraction
            (grp, num, frac) = vars
            power = 0

        if (np.size(vars) == 4
            ):  # Four variables: Group num, File num, Fraction, Exponent
            (grp, num, frac, power) = vars

        if int(np.size(vars)) in [2, 3, 4]:

            grp = int(grp)
            num = int(num)
            frac = float(frac)
            power = int(power)

            print("group={}, num={}, frac={}".format(grp, num, frac))
            #            print "Group = {}, num{}, Name = {}".format(name_group, num, name)

            name_group = groups[grp]
            groupmask = t['Desc'] == name_group
            group_tmp = t[groupmask]
            filename_bg = group_tmp['Filename'][num]

            image_fg = image_raw
            image_bg = hbt.read_lorri(filename_bg,
                                      frac_clip=1,
                                      bg_method='None')

            image = image_fg - float(frac) * image_bg
            image = image - hbt.sfit(image, power, mask=mask_sfit)

        image_processed = image

# =============================================================================
# Do method 'None' (trivial)
# =============================================================================

    if (method == 'None'):

        image_processed = image = image_raw

#==============================================================================
# Do method 'String'. Complicated, but most useful.
#
# Parse a string like "6/112-6/129", or "129", or "6/114", or "124-129"
#        or "6/123 - 129" or "6/123-129 r1 *0.5 p4 mask_7_12"
#                  or "".
#
# Except for the group and image number, the order of thse does not matter.
#==============================================================================
####
# As of 8-July-2017, this is the one I will generally use for most purposes.
#
# 'String' does this:
#   o Subtract the bg image made by combining the named frames, and rotating and scaling as requested (optional)
#   o Apply a mask file (optional)
#   o Subtract a polynomial (optional). ** As of 17-Nov-2017, sfit is applied only to masked pixels, not full image.
#
####

    if (method == 'String'):

        str = vars

        # =============================================================================
        #          Parse any rotation angle -- written as "r90" -- and remove from the string
        # =============================================================================

        angle_rotate_deg = 0

        match = re.search('(r[0-9]+)', str)
        if match:
            angle_rotate_deg = int(match.group(0).replace(
                'r', ''))  # Extract the rotation angle
            str = str.replace(match.group(0),
                              '')  # Remove the whole phrase from string

            if (
                    np.abs(angle_rotate_deg)
            ) <= 10:  # Allow value to be passed as (1,2,3) or (90, 180, 270)
                angle_rotate_deg *= 90

        # Determine how much the bg frame should be scaled, to match the data frame. This is just a multiplicative
        # factor that very crudely accomodates for differences in phase angle, exptime, etc.

# =============================================================================
#          Parse any stray multiplication factor -- written as "*3" -- and remove from the string
#          Multiplicative factor is used to scale the stray light image to be removed, up and down (e.g., up by 3x)
# =============================================================================

        factor_stray_default = 1  # Define the default multiplicative factor

        factor_stray = factor_stray_default

        match = re.search(
            '(\*[0-9.]+)', str
        )  # Match   *3   *0.4   etc  [where '*' is literal, not wildcard]
        if match:
            factor_stray = float(match.group(0).replace(
                '*', ''))  # Extract the multiplicative factor
            str = str.replace(match.group(0),
                              '').strip()  # Remove phrase from the string

# =============================================================================
#          Parse any mask file -- written as "mask_7_0" -- and remove from the string
# =============================================================================
#
# This mask is a fixed pattern, read from a file, for stray light etc.
# It is *not* for stars or satellites, which are calculated separately.
#
# To make these mask files, the steps are...

# Maskfile is using same name structure as for the summed bg straylight images -- e.g., 8/0-48.
#
# True = good pixel. False = bad.

        file_mask_stray = None

        match = re.search('(mask[0-9a-z._\-]+)', str)

        print("Str = {}, dir_mask_stray = {}".format(str, dir_mask_stray))

        if match:
            file_mask_stray = dir_mask_stray + match.group(
                0) + '.png'  # Create the filename
            DO_MASK = True

            str = str.replace(match.group(0),
                              '').strip()  # Remove the phrase from the string

# =============================================================================
#          Parse any polynomial exponent -- written as 'p5'
#          This is the polynomial removed *after* subtracting Image - Stray
# =============================================================================

        poly_after_default = 0  # Define the default polynomial to subtract.
        # I could do 5, or I could do 0.

        poly_after = poly_after_default

        match = re.search('(p[0-9]+)', str)
        if match:
            poly_after = int(match.group(0).replace(
                'p', ''))  # Extract the polynomal exponent
            str = str.replace(match.group(0),
                              '').strip()  # Remove the phrase from the string

# =============================================================================
#          Now parse the rest of the string
# =============================================================================

# The only part that is left is 0, 1, or 2 integers, which specify the stray light file to extract
# They must be in the form "7/12-15", or "7/12" or "12"

        str2 = str.replace('-', ' ').replace('/', ' ').replace(
            'None', '')  # Get rid of any punctuation

        vars = np.array(
            str2.split(), dtype=int
        )  # With no arguments, split() breaks at any set of >0 whitespace chars.

        # =============================================================================
        # Now load the appropriate stray light image, based on the number of arguments passed
        # =============================================================================

        do_sfit_stray = False  # Flag: When constructing the straylight median file, do we subtract polynomial, or not?
        #
        # Usually we want False. ie, want to do:
        #  out = remove_sfit(raw - stray)
        #      not
        #  out = remove_sfit(raw) - remove_sfit(stray)

        if (np.size(vars) == 0):  #  "<no arguments>"
            image = image_raw
            image_processed = image
            image_stray = 0 * image

        if (np.size(vars) == 1):  #  "12" -- image number
            image_stray = hbt.nh_get_straylight_median(
                index_group, [int(vars[0])],
                do_sfit=do_sfit_stray)  # "122" -- assume current group

        if (np.size(vars) == 2):  #  "7-12" -- image range
            image_stray = hbt.nh_get_straylight_median(
                index_group,
                hbt.frange(int(vars[0]), int(vars[1])).astype('int'),
                do_sfit=do_sfit_stray)  # "122-129"
            # -- assume current group

        if (np.size(vars) == 3):  #  "7/12-20" -- group, plus image range
            image_stray = hbt.nh_get_straylight_median(
                int(vars[0]),
                hbt.frange(vars[1], vars[2]).astype('int'),
                do_sfit=do_sfit_stray)  # "5/122 - 129"

        if (np.size(vars) == 4
            ):  #  "7/12 - 7/20"  (very wordy -- don't use this)
            image_stray = hbt.nh_get_straylight_median(
                int(vars[0]),
                hbt.frange(vars[1], vars[3]).astype('int'),
                do_sfit=do_sfit_stray)  # "6/122 - 6/129"

# Adjust the stray image to be same size as original.
# Sometimes we'll have a 4x4 we want to use as stray model for 1x1 image -- this allows that.
# When we resize it, also adjust the flux (e.g., by factor of 16).

        dx_stray = hbt.sizex(image_stray)
        dx_im = hbt.sizex(image_raw)
        ratio = dx_im / dx_stray

        if (dx_stray < dx_im):
            image_stray = scipy.ndimage.zoom(image_stray, ratio) / (
                ratio**2)  # Enlarge the stray image

        if (dx_stray > dx_im):
            image_stray = scipy.ndimage.zoom(image_stray, ratio) / (
                ratio**2)  # Shrink the stray image

#=============================================================================
# Now that we have parsed the string, do the image processing
#=============================================================================

# Load the Photoshop stray mask file, if it exists. Otherwise, make a blank mask of True.

        if file_mask_stray:

            try:
                mask_stray = imread(
                    file_mask_stray
                ) > 128  # Read file. Mask PNG file is 0-255. Convert to boolean.

                print("Reading mask file {}".format(file_mask_stray))

                if (
                        len(np.shape(mask_stray)) > 2
                ):  # If Photoshop saved multiple planes, then just take first
                    mask_stray = mask_stray[:, :, 0]

            except IOError:  # If mask file is missing
                print("Stray light mask file {} not found".format(
                    file_mask_stray))

        else:
            mask_stray = np.ones(np.shape(image_raw), dtype=bool)

# Load the object mask. This masks out stars and satellites, which should not have sfit applied to them.

        file_objects = os.path.basename(file_image).replace(
            '.fit', '_objects.txt')
        mask_objects = nh_jring_mask_from_objectlist(file_objects)
        mask_objects = np.logical_not(
            mask_objects)  # Make so True = good pixel

        # Merge the two masks together

        mask = np.logical_and(
            mask_objects, mask_stray)  # Output good if inputs are both good

        # Rotate the stray light image, if that has been requested
        # [this probably doesn't work, but that's fine -- I didn't end up using this.]

        image_stray = np.rot90(
            image_stray, angle_rotate_deg /
            90)  # np.rot90() takes 1, 2, 3, 4 = 90, 180, 270, 360.

        # Subract the final background image from the data image

        image_processed = image_raw - factor_stray * image_stray

        #        print("Removing bg. factor = {}, angle = {}".format(factor_stray, angle_rotate_deg))

        # Apply the mask: convert any False pixels to NaN in prep for the sfit

        image_masked = image_processed.copy()
        image_masked[mask == False] = math.nan

        frac_good = np.sum(mask) / (np.prod(np.shape(mask)))

        print("Applying mask, fraction good = {}".format(frac_good))

        # Remove a polynomial from the result. This is where the mask comes into play.
        # XXX NB: I think the logic here could be cleaned up. sfit() now allows a mask= argument ,
        #         but it must not have when I wrote this code.

        sfit_masked = hbt.sfit(image_masked, poly_after)

        image_processed = image_processed - sfit_masked

        print("Removing sfit {}".format(poly_after))

        # Plot the masks and sfits, for diagnostics

        do_plot_masks = False
        if do_plot_masks:

            plt.subplot(1, 3, 1)
            plt.imshow(stretch(mask))
            plt.title('mask')
            plt.subplot(1, 3, 2)
            plt.imshow(stretch(image_masked))
            plt.title('image_masked')
            plt.subplot(1, 3, 3)
            plt.imshow(sfit_masked)
            plt.title('sfit_masked')
            plt.show()

# =============================================================================
# END OF CASE STATEMENT FOR METHODS
# =============================================================================

# Remove a small bias offset between odd and even rows ('jailbars')
# This might be better done before the sfit(), but in reality probably doesn't make a difference.

    image_processed = hbt.lorri_destripe(image_processed)

    # If requested: plot the image, and the background that I remove.
    # Plot to Python console, not the GUI.

    # Test stretching here
    # We use astropy's stretching here, rather than matplotlib's norm= keyword. The basic idea of both of these
    # is the same, but I know that astropy has a percentile stretch available.

    DO_DIAGNOSTIC = False

    if (DO_DIAGNOSTIC):

        stretch = astropy.visualization.PercentileInterval(
            90)  # PI(90) scales array to 5th .. 95th %ile

        plt.rcParams['figure.figsize'] = 16, 6

        # Column 1: raw image

        im = image_raw

        im = hbt.remove_sfit(im, degree=5)
        plt.subplot(1, 3, 1)  # vertical, horizontal, index
        plt.imshow(stretch(hbt.remove_sfit(im, degree=5)))
        plt.title('remove_sfit(image_raw, degree=5), mean=' +
                  hbt.trunc(np.mean(im), 3))
        plt.colorbar()

        #       Column 2: Stray only. This will throw an error if we haven't read in a stray light file -- just ignore it.

        plt.subplot(1, 3, 2)
        try:
            plt.imshow(stretch(hbt.remove_sfit(
                image_stray,
                degree=5)))  # This won't do much since it is already applied
        except UnboundLocalError:
            print("No stray light to subtract")

        plt.title('remove_sfit(stray_norm, degree=5), mean=' +
                  hbt.trunc(np.mean(im), 3))

        # Column 3: raw - stray

        plt.subplot(1, 3, 3)

        try:
            im = hbt.remove_sfit(image_raw - image_stray, degree=5)
            plt.imshow(stretch(im))
        except UnboundLocalError:
            print("No stray light to subtract")

        plt.title('remove_sfit(image_raw - image_stray, degree=5), med ' +
                  hbt.trunc(np.median(im), 3))

        plt.show()

# Now return the array. If we have a mask, then we return it too, as a tuple

    if (DO_MASK):  # If we loaded a mask
        return (image_processed, mask)
    else:
        return image_processed
    images = []
    
    for index_image in index_imagesets_i:
        t_image = t_group[index_image]  # Grab this, read-only, since we use it a lot.
        file_in = t_image['Filename']
        
        image = hbt.read_lorri(file_in, frac_clip = 1., bg_method = 'None')  # Read the image
    
        image = hbt.lorri_destripe(image)
        
        images.append(image)                                                 # Stuff this image onto the end of a list.
 
    images = np.array(images)                                     # Now we have a 3D Numpy array with all the image data
    images_sum = np.sum(images,axis=0)                                       # And sum down into a 2D array.
    
    images_proc = stretch(hbt.remove_sfit(images_sum, degree=power))
        
#    plt.imshow(images_sum)
#    plt.title("{}/{}-{} RAW".format(index_group, index_start, index_end))
#    plt.show()
    
    plt.imshow(images_proc)
    plt.title("{}/{}-{} - sfit({})".format(index_group, index_start, index_end, power))
    plt.show()
    
    imsave(file_out, images_proc)
#    
#    lun = open(file_out, 'wb')      # binary mode is important
#    w = png.Writer(1024, 1024, greyscale=True)
#    w.write(lun, 255*image_proc)
#    lun.close()
예제 #10
0
    print(f'Position of LORRI center in Jup coords: {pt_closest_jup_jup}')
    print(f'Position of LORRI center: Horiz = {dist_jup_center_horizontal_rj:.2}, ' + 
          f'Vert = {dist_jup_center_vertical_rj:.2}')
   
    # Set the 'extent', which is the axis values for the X and Y axes for an imshow().
    # We set the y range to be the same as X, to convince python to keep the pixels square.
    
    extent = [dist_jup_center_rj_range[0], dist_jup_center_rj_range[1],\
              dist_jup_center_rj_range[0], dist_jup_center_rj_range[1]]
        
    # Plot the image, sfit subtracted
    
    plt.subplot(2,2,2)
    degree=5
    im_sum /= len(index_images)  # We have just summed, so now divide so as to preserve DN of original.
    im_sum_s = hbt.remove_sfit(im_sum,degree=degree)  # _s = sfit subtracted
    plt.imshow(stretch(im_sum_s), origin='lower', extent=extent)
#    plt.gca().get_xaxis().set_visible(False)
    plt.gca().get_yaxis().set_visible(False)
    plt.title(f'{index_group}/{np.amin(index_images)}-{np.amax(index_images)} - p{degree}')

    # Plot the image, with better subtraction
    
    plt.subplot(2,2,3)
    
    method = 'String'
    vars = '6/63-70 p5'
#    index_image = 124
#    index_group = 6
    im_process = hbt.nh_jring_process_image(im_sum, method, vars, 
                                     index_group=index_group, index_image=index_image, mask_sfit=None)
def nh_create_straylight_median(index_group, index_files, do_fft=False, do_sfit=True, power=5):
    
    
    """
    This takes a set of related observations, and creates a median image of all of them,
    in a form useful for straylight removal.
    For now this routine is hard-coded to NH J-ring, but it could be generalized later.
    
    This routine actually does the calculation. 
    
    For a user, should call NH_GET_STRAYLIGHT_MEDIAN, rather than the current function, which will avoid having
    to deal with filenames.
    
    Although the filename is called 'median', this function uses different techniques to calculate the
    best background image, including percentiles, means, etc -- not just a median.
    
    This routine does not use a photoshop mask. That can be used elsewhere.
    
    This routine also does not use an object mask. But, other stray light processing parts do that. 
    
    """

#     o index_group:   What set of observations, grouped by 'Desc' field. Integer.
#     o index_files:   Numpy array list of the files. Cannot be a scalar.
#     o do_fft:        Flag. For the final step, do we use an FFT? [NOT IMPLEMENTED]
#     o do_sfit:       Flag. Do we use an sfit (ie, polynomial fit)?
#     o power:         Exponent for sfit, to be applied at end and subtracted
#   
#     This routine returns the array itself, and a recommended base filename. It does not write it to disk.

    do_debug = True
    
    do_destripe = True   # Flag: Do we apply LORRI de-striping to each frame?
    
    do_normalize = True
    
    stretch = astropy.visualization.PercentileInterval(90)  # PI(90) scales array to 5th .. 95th %ile

    if do_debug:
        print("nh_create_straylight_median: {}/{}-{}".format(index_group, index_files[0], index_files[-1]))
        
    file_pickle = '/Users/throop/Data/NH_Jring/out/nh_jring_read_params_571.pkl' # Filename to read to get filenames, etc.
    
    lun = open(file_pickle, 'rb')
    t = pickle.load(lun)
    lun.close()

    # Process the group names. Some of this is duplicated logic -- depends on how we want to use it.

    groups = astropy.table.unique(t, keys=(['Desc']))['Desc']
        
    groupmask = (t['Desc'] == groups[index_group])
    t_group = t[groupmask]	
    
    # Create the output arrays
   
    num_files = np.size(index_files)
    
    header = hbt.get_image_header(t_group['Filename'][index_files[0]])
    
    # Get dimensions of first frame in the series
    
    dx_0 = header['NAXIS1']
    dy_0 = header['NAXIS2']
    
    print("For image 0, dx={}".format(dx_0))
    
    frame_arr      = np.zeros((num_files, dx_0, dy_0))
    frame_sfit_arr = np.zeros((num_files, dx_0, dy_0))
    frame_ffit_arr = np.zeros((num_files, dx_0, dy_0))
    
    # Read frames in to make a median
     
    for i,n in enumerate(index_files):
        file = t_group['Filename'][n] # Look up filename
        print("Reading: " + file)
        frame = hbt.read_lorri(file,frac_clip = 1)

        if do_destripe:
            frame = hbt.lorri_destripe(frame)
            
        if (hbt.sizex(frame) != dx_0):
            print("For image {}, dx={}, dy={}".format(i, hbt.sizex(frame), hbt.sizey(frame)))

            print("Error: mixed sizes in this straylight series. Aborting.")
#            raise ValueError('MultipleSizes')
            
            if (np.shape(frame)[0] == 256):
#            
#    Resize the image to 1024x1024, if it is a 4x4 frame. 
#    scipy.misc.imresize should do this, and has various interpolation schemes, but truncates to integer (?!)
#    because it is designed for images. So instead, use scipy.ndimage.zoom, which uses cubic spline.
#        
                frame2 = scipy.ndimage.zoom(frame, 4)
                frame = frame2
                
            if (np.shape(frame)[0] == 1024):
                frame2 = scipy.ndimage.zoom(frame, 0.25)
                frame = frame2
                
        frame_arr[i,:,:] = frame	
        
#        frame_sfit_arr[i,:,:] = frame - hbt.sfit(frame, power)  # Calculate the sfit to each individual frame
#        frame_ffit_arr[i,:,:] = frame - hbt.ffit(frame)
    
    # Now take the median!
    
    frame_med      = np.median(frame_arr, axis=0)

    # Now normalize if necessary
    
    if do_normalize:
        frame_arr_norm = frame_arr.copy()
        
        for i in range(num_files):
            (frame_arr_norm[i], r) = hbt.normalize_images(frame_arr[i], frame_med)
                    
        frame_arr = frame_arr_norm

    # Make a plot of the mean and median of each file, to see how the normalization worked
    
        mean_arr = np.zeros(num_files)
        median_arr = np.zeros(num_files)
        for i in range(num_files):
            mean_arr[i] = np.nanmean(frame_arr[i])
            median_arr[i] = np.nanmedian(frame_arr[i])
        plt.plot(mean_arr, label='mean')
        plt.plot(median_arr, label='median')
        plt.legend()
        plt.show()

    # Take the median, again
        
        frame_med      = np.median(frame_arr, axis=0)

            
    # Now that we have the median for each pixel... take the median of pixels below the median.
    
    frame_arr_step_2 = frame_arr.copy()
    for j in range(hbt.sizex(frame_arr)):
        frame_arr_step_2[j][frame_arr_step_2[j] > frame_med] = np.nan
    
    frame_med_step_2 = np.nanmedian(frame_arr_step_2, axis=0)
#
#    frame_arr_step_3 = frame_arr_step_2.copy()
#    for j in range(hbt.sizex(frame_arr)):
#        frame_arr_step_3[j][frame_arr_step_3[j] > frame_med_step_2] = np.nan
#    
#    frame_med_step_3 = np.nanmedian(frame_arr_step_2, axis=0)
#    
#    frame_med_step3  = np.percentile(frame_arr, 10, axis=0)
#    
#    hbt.figsize(25,25)
#    plt.subplot(1,3,1)
#    plt.imshow(stretch(hbt.remove_sfit(frame_med,degree=5)), cmap='plasma')
#    plt.title('Median')
#    
#    plt.subplot(1,3,2)
#    plt.imshow(stretch(hbt.remove_sfit(frame_med_step_2,degree=5)), cmap='plasma')
#    plt.title('Median of pixels < median')
#    
#    plt.subplot(1,3,3)
#    plt.imshow(stretch(hbt.remove_sfit(frame_med_step_3,degree=5)), cmap='plasma')
#    plt.title('Median of pixels < pixels < median')
#    plt.show()
#    hbt.figsize()
    
    #%%%
    
    ######
    # Now just do some experimentation. I am just trying a bunch of things, to try to get the best signal here.
    # In theory a median will work well. The problem is that this just doesn't exclude all the ring.
    # So I tried taking 30th percentile. Works better. 
    # Tried taking 1st percentile (ie, faintest value at each pixel). But this still had a ring, dammit! 
    # I think it must be because a few images are anomalously faint, throwing off the statistics.
    #
    # The method right here uses a very ad-hoc percentile method. There is no reason it should work. 
    # I've just found that it does.
    ######
    
    hbt.figsize(10,10)   
    frame_percentile  = np.percentile(frame_arr, 99, axis=0)
    plt.imshow(stretch(hbt.remove_sfit(frame_percentile,degree=5)), cmap='plasma')

    # Take the mmedians at a bunch of different percentiles
    
    frame_p10  = np.percentile(frame_arr, 10, axis=0)
    frame_p20  = np.percentile(frame_arr, 20, axis=0)
    frame_p30  = np.percentile(frame_arr, 30, axis=0)
    frame_p40  = np.percentile(frame_arr, 40, axis=0)
    frame_p50  = np.percentile(frame_arr, 50, axis=0)
    frame_p60  = np.percentile(frame_arr, 60, axis=0)
    frame_p70  = np.percentile(frame_arr, 70, axis=0)
    frame_p80  = np.percentile(frame_arr, 80, axis=0)
    frame_p90  = np.percentile(frame_arr, 90, axis=0)
    
    frames_pall = np.array([frame_p10, frame_p20, frame_p30, frame_p40, frame_p50, frame_p60, frame_p70, frame_p80])
    frames_pall = np.array([frame_p10, frame_p20])

    # Mean these frames all together
    
    frames_pall_med = np.mean(frames_pall,axis=0)
    
    plt.imshow(stretch(hbt.remove_sfit(frames_pall_med)))
    plt.title('frames_pall_med')
    plt.show()

    frame_med = frames_pall_med
    
    # Convolve the output array with a gaussian, to smooth it
    # *** Did a test. Doing this gaussian convolution to smooth it makes no difference.

    do_smooth = True  # Smooth the output array with a gaussian kernel?
    
    if do_smooth:
        
        kernel = Gaussian2DKernel(3)
        frame_med_convolve = astropy.convolution.convolve(frame_med,kernel, boundary='extend')
        
        # Repair the edges, by copying data back from the orignal source image, pre-convolution
        # Copy from a few kernel widths away, to be sure we get it all.
        
        frame_med_convolve[0:12,:] = frame_med[0:12,:]
        frame_med_convolve[-12:,:] = frame_med[-12:,:]
        frame_med_convolve[:,0:12] = frame_med[:,0:12]
        frame_med_convolve[:,-12:] = frame_med[:,-12:]
        
        # Save into the ouput array
    
        frame_med = frame_med_convolve
    
#    frame_med = frame_p20

    #%%%

    frame_med_sfit = hbt.remove_sfit(frame_med, degree=power) # Take median using sfit images
    
#    frame_ffit_med = np.median(frame_ffit_arr, axis=0) # Take median using fft  images
    
    # Do a very small removal of hot pixels. Between 0.99 and 0.999 seems to be fine. Make as small 
    # as possible, but something is often necessary
    
#    frame_sfit_med = hbt.remove_brightest(frame_sfit_med, 0.999)
#    frame_ffit_med = hbt.remove_brightest(frame_sfit_med, 0.999)
#    
    file_base = hbt.nh_create_straylight_median_filename(index_group, index_files, do_fft=do_fft, do_sfit=do_sfit, 
                                                     power=power)
                                                     
    if (do_fft): 
        raise ValueError('Sorry, do_ffit not implemented')
        return -1
    
    if (do_sfit):
        return (frame_med_sfit, file_base)
    
    else:
        return (frame_med, file_base)   
    if (do_fft): 
        raise ValueError('Sorry, do_ffit not implemented')
        return -1
    
    if (do_sfit):
        return (frame_med_sfit, file_base)
    
    else:
        return (frame_med, file_base)   

# =============================================================================
# End of Function
# =============================================================================
    
if (__name__ == '__main__'):
    
    stretch = astropy.visualization.PercentileInterval(90)  # PI(90) scales array to 5th .. 95th %ile

    print("Testing...")
    
    index_group = 8
    index_files = hbt.frange(0,47)
    do_fft      = False
    power       = 5
    do_sfit     = False
    
    (arr,mask) = nh_create_straylight_median(index_group, index_files, do_fft=do_fft, do_sfit=do_sfit, power=power)
 
    plt.set_cmap('plasma')
    plt.imshow(stretch(hbt.remove_sfit(arr, degree=5)))
    
##### 

# Just a scratch spot to test star search, etc.

from photutils import daofind
from astropy.stats import sigma_clipped_stats
from photutils import find_peaks

DO_TEST = False

if (DO_TEST):
    
    num = 200 # Number of brightest objects to keep
    
    image_s = hbt.remove_sfit(image,4)
    
    mean, median, std = sigma_clipped_stats(image, sigma=3.0, iters=5)
        
    sources = daofind(image, fwhm=2.0, threshold=2.*std)
    
    threshold = median + (10.0 * std) # Ten sigma
    tbl = find_peaks(image, threshold, box_size=5)
    
    sources.sort('flux')  # Sort in-place
    tbl.sort('peak_value')
        
    if (num > 0):  
        index_start = -num
    else:
        index_start = 0
def nh_jring_process_image(image_raw, method, vars, index_group=-1, index_image=-1, mask_sfit=None):
    
    """
    Return image with stray light removed. Flux is preserved and no clipping is done.

    Parameters
    -----
    
    image_raw: 
        NumPy array with the data image.

    method:    
        Method of background subtraction, which can be 'Next', 'Prev', 'Polynomial', 'String', 'None', 
        'Grp Num Frac Pow', etc.
        
        In general 'String' is the most flexible, and recommended.
        It can be things like "5/0-10 r3 *2 mask_7_10':
            
        - Make a median of Group 5, Images 0-10
        - Rotate them all by 270 degrees
        - Scale it to the data image
        - Multiply background image by 2
        - Subtract data - background
        - Remove a 5th degree polynomial from the result [always done, regardless]
        - Load the Photoshop-created mask file "mask_7_10" and incorporate via a tuple
        - Return final result

    vars:
        The argument to the 'method'. Can be an exponent, a file number, a string, etc -- arbitrary, as needed. 
             
    index_group:
        Index of current image. Not used except for Next/Prev.

    index_image:
        Index of current group. Not used except for Next/Prev.
        
    mask_sfit:
        An optional mask to be applied when doing the sfit. Ony pixels with True will be used.
        This is to mask out satellites, stars, CRs, etc. so they don't affect the sfit().
             
    """

    stretch_percent = 90    
    stretch = astropy.visualization.PercentileInterval(stretch_percent) # PI(90) scales to 5th..95th %ile.


# Load the arrays with all of the filenames

    dir_out = '/Users/throop/Data/NH_Jring/out/'

    file_pickle = dir_out + 'nh_jring_read_params_571.pkl' # Filename to get filenames, etc.
    
    lun = open(file_pickle, 'rb')
    t = pickle.load(lun)
    lun.close()

    # Initialize variables
    
    DO_MASK = False  # We set this based on whether a mask is passed in or not

    dir_mask_stray = dir_out.replace('out','masks')   # Directory where the mask files are
    
    # Process the group names. Some of this is duplicated logic -- depends on how we want to use it.

    groups = astropy.table.unique(t, keys=(['Desc']))['Desc']


    
    if (index_group != -1):  # Only do this if we actually passed a group in
        groupmask = (t['Desc'] == groups[index_group])
        t_group = t[groupmask]	

        # Look up the filename, in case we need it.
    
        file_image = t_group['Filename'][index_image]
        
    if (method == 'Previous'):
        file_prev = t_group['Filename'][index_image-1]
#            print "file =      " + filename
        print("file_prev = " + file_prev)
        image_bg = hbt.read_lorri(file_prev, frac_clip = 1.0, bg_method = 'None')
        image_fg = image_raw
        image = image_fg - image_bg
        image_processed = image

    if (method == 'Next'):
        file_next = t_group['Filename'][index_image+1]
        image_bg = hbt.read_lorri(file_next, frac_clip = 1.0, bg_method = 'None', autozoom=True)
        image_fg = image_raw
        image = image_fg - image_bg
        image_processed = image
        
    if (method == 'Median'): # XXX not working yet
        file_prev = t_group['Filename'][index_image-1]
        image_bg = hbt.read_lorri(file_prev, frac_clip = 1.0, bg_method = 'None')
        image_fg = image_raw
        image = image_fg - image_bg
        image_processed = image

    if (method == 'Polynomial'):
        
        power = vars
        image = image_raw - hbt.sfit(image_raw, power) # Look up the exponenent and apply it
        image_processed = image
                                            
    if (method == 'Grp Num Frac Pow'):  # Specify to subtract a specified group#/image#, mult factor, and sfit power.
                                        # I thought this would be useful, but it turns out we usually need to subtract
                                        # a median of multiple images -- not just one -- so this is not very useful.
                                        # Plus, the best power is usually 5, and the best frac can be calc'd
                                        # with a linfit.
            
        if (np.size(vars) == 0): # If no args passed, just plot the image
            power = 0
            frac  = 0
            image = image_raw

        if (np.size(vars) == 1): # One variable: interpret as exponent
            power = float(vars[0])
            frac  = 0
            image = image_raw
            image_bg = hbt.sfit(image, power, mask=mask_sfit)
            image = image - image_bg
            
        if (np.size(vars) == 2): # Two variables: interpret as group num and file num
            (grp, num) = vars
            frac  = 1
            power = 0
            
        if (np.size(vars)) == 3: # Three variables: interpret as group num, file num, fraction
            (grp, num, frac) = vars
            power = 0
            
        if (np.size(vars) == 4): # Four variables: Group num, File num, Fraction, Exponent
            (grp, num, frac, power) = vars
             
        if int(np.size(vars)) in [2,3,4]:
           
            grp = int(grp)
            num = int(num)
            frac = float(frac)
            power = int(power)
            
            print("group={}, num={}, frac={}".format(grp, num, frac))
#            print "Group = {}, num{}, Name = {}".format(name_group, num, name)

            name_group = groups[grp]
            groupmask = t['Desc'] == name_group
            group_tmp = t[groupmask]
            filename_bg = group_tmp['Filename'][num]
                                    
            image_fg = image_raw
            image_bg = hbt.read_lorri(filename_bg, frac_clip = 1, bg_method = 'None')
            
            image = image_fg - float(frac) * image_bg                
            image = image - hbt.sfit(image, power, mask=mask_sfit)
            
        image_processed = image

# =============================================================================
# Do method 'None' (trivial)
# =============================================================================
                  
    if (method == 'None'):
        
        image_processed = image = image_raw

#==============================================================================
# Do method 'String'. Complicated, but most useful.
#
# Parse a string like "6/112-6/129", or "129", or "6/114", or "124-129" 
#        or "6/123 - 129" or "6/123-129 r1 *0.5 p4 mask_7_12"
#                  or "".
#
# Except for the group and image number, the order of thse does not matter.        
#==============================================================================
####        
# As of 8-July-2017, this is the one I will generally use for most purposes.
#        
# 'String' does this:
#   o Subtract the bg image made by combining the named frames, and rotating and scaling as requested (optional)
#   o Apply a mask file (optional)
#   o Subtract a polynomial (optional). ** As of 17-Nov-2017, sfit is applied only to masked pixels, not full image.
#        
####
        
    if (method == 'String'):

        str = vars

# =============================================================================
#          Parse any rotation angle -- written as "r90" -- and remove from the string
# =============================================================================
                
        angle_rotate_deg = 0
        
        match = re.search('(r[0-9]+)', str)
        if match:
            angle_rotate_deg = int(match.group(0).replace('r', ''))   # Extract the rotation angle
            str = str.replace(match.group(0), '')                     # Remove the whole phrase from string 
            
            if (np.abs(angle_rotate_deg)) <= 10:      # Allow value to be passed as (1,2,3) or (90, 180, 270)
                angle_rotate_deg *= 90

        # Determine how much the bg frame should be scaled, to match the data frame. This is just a multiplicative
        # factor that very crudely accomodates for differences in phase angle, exptime, etc.
        
# =============================================================================
#          Parse any stray multiplication factor -- written as "*3" -- and remove from the string
#          Multiplicative factor is used to scale the stray light image to be removed, up and down (e.g., up by 3x)
# =============================================================================
        
        factor_stray_default    = 1    # Define the default multiplicative factor
        
        factor_stray            = factor_stray_default
        
        match = re.search('(\*[0-9.]+)', str) # Match   *3   *0.4   etc  [where '*' is literal, not wildcard]
        if match:
            factor_stray = float(match.group(0).replace('*', ''))  # Extract the multiplicative factor
            str = str.replace(match.group(0), '').strip()          # Remove phrase from the string                       

# =============================================================================
#          Parse any mask file -- written as "mask_7_0" -- and remove from the string
# =============================================================================
# 
# This mask is a fixed pattern, read from a file, for stray light etc.
# It is *not* for stars or satellites, which are calculated separately.
#
# To make these mask files, the steps are...

# Maskfile is using same name structure as for the summed bg straylight images -- e.g., 8/0-48.            
#         
# True = good pixel. False = bad.
        
        file_mask_stray = None
                
        match = re.search('(mask[0-9a-z._\-]+)', str)
        
        print("Str = {}, dir_mask_stray = {}".format(str, dir_mask_stray))
        
        if match: 
            file_mask_stray = dir_mask_stray + match.group(0) + '.png'    # Create the filename
            DO_MASK = True
            
            str = str.replace(match.group(0), '').strip()     # Remove the phrase from the string
    
# =============================================================================
#          Parse any polynomial exponent -- written as 'p5'
#          This is the polynomial removed *after* subtracting Image - Stray
# =============================================================================
        
        poly_after_default = 0              # Define the default polynomial to subtract.
                                            # I could do 5, or I could do 0.
                                            
        poly_after         = poly_after_default 
        
        match = re.search('(p[0-9]+)', str)
        if match:
            poly_after = int(match.group(0).replace('p', ''))  # Extract the polynomal exponent
            str = str.replace(match.group(0), '').strip()      # Remove the phrase from the string
            
# =============================================================================
#          Now parse the rest of the string
# =============================================================================

# The only part that is left is 0, 1, or 2 integers, which specify the stray light file to extract
# They must be in the form "7/12-15", or "7/12" or "12"
            
        str2 = str.replace('-', ' ').replace('/', ' ').replace('None', '') # Get rid of any punctuation

        vars = np.array(str2.split(), dtype=int)  # With no arguments, split() breaks at any set of >0 whitespace chars.

# =============================================================================
# Now load the appropriate stray light image, based on the number of arguments passed
# =============================================================================

        do_sfit_stray = False  # Flag: When constructing the straylight median file, do we subtract polynomial, or not?
                               #
                               # Usually we want False. ie, want to do:
                               #  out = remove_sfit(raw - stray)
                               #      not
                               #  out = remove_sfit(raw) - remove_sfit(stray)
                               
        if (np.size(vars) == 0):             #  "<no arguments>"
            image           = image_raw
            image_processed = image
            image_stray     = 0 * image
                    
        if (np.size(vars) == 1):             #  "12" -- image number
            image_stray = hbt.nh_get_straylight_median(index_group, [int(vars[0])],
                                                      do_sfit=do_sfit_stray)  # "122" -- assume current group
            
        if (np.size(vars) == 2):             #  "7-12" -- image range
            image_stray = hbt.nh_get_straylight_median(index_group, 
                                                      hbt.frange(int(vars[0]), int(vars[1])).astype('int'),
                                                      do_sfit=do_sfit_stray)  # "122-129"
                                                                                        # -- assume current group
 
        if (np.size(vars) == 3):             #  "7/12-20" -- group, plus image range
            image_stray = hbt.nh_get_straylight_median(int(vars[0]), 
                                                      hbt.frange(vars[1], vars[2]).astype('int'), 
                                                      do_sfit=do_sfit_stray) # "5/122 - 129"
            
        if (np.size(vars) == 4):             #  "7/12 - 7/20"  (very wordy -- don't use this)
            image_stray = hbt.nh_get_straylight_median(int(vars[0]), 
                                                      hbt.frange(vars[1], vars[3]).astype('int'), 
                                                      do_sfit=do_sfit_stray) # "6/122 - 6/129"


# Adjust the stray image to be same size as original. 
# Sometimes we'll have a 4x4 we want to use as stray model for 1x1 image -- this allows that.
# When we resize it, also adjust the flux (e.g., by factor of 16).

        dx_stray = hbt.sizex(image_stray)
        dx_im    = hbt.sizex(image_raw)
        ratio    = dx_im / dx_stray
        
        if (dx_stray < dx_im):
            image_stray = scipy.ndimage.zoom(image_stray, ratio) / (ratio**2)    # Enlarge the stray image

        if (dx_stray > dx_im):
            image_stray = scipy.ndimage.zoom(image_stray, ratio) / (ratio**2)   # Shrink the stray image

#=============================================================================
# Now that we have parsed the string, do the image processing
#=============================================================================

# Load the Photoshop stray mask file, if it exists. Otherwise, make a blank mask of True.
        
        if file_mask_stray:

            try:
                mask_stray = imread(file_mask_stray) > 128      # Read file. Mask PNG file is 0-255. Convert to boolean.
        
                print("Reading mask file {}".format(file_mask_stray))
                
                if (len(np.shape(mask_stray)) > 2):          # If Photoshop saved multiple planes, then just take first
                    mask_stray = mask_stray[:,:,0]
                    
            except IOError:                                   # If mask file is missing
                print("Stray light mask file {} not found".format(file_mask_stray))
            
        else:
            mask_stray = np.ones(np.shape(image_raw),dtype=bool)

# Load the object mask. This masks out stars and satellites, which should not have sfit applied to them.
       
        file_objects = os.path.basename(file_image).replace('.fit', '_objects.txt')
        mask_objects = nh_jring_mask_from_objectlist(file_objects)
        mask_objects = np.logical_not(mask_objects)   # Make so True = good pixel
        
# Merge the two masks together
        
        mask = np.logical_and(mask_objects, mask_stray)  # Output good if inputs are both good

# Rotate the stray light image, if that has been requested 
# [this probably doesn't work, but that's fine -- I didn't end up using this.]

        image_stray = np.rot90(image_stray, angle_rotate_deg/90)  # np.rot90() takes 1, 2, 3, 4 = 90, 180, 270, 360.
        
# Subract the final background image from the data image
        
        image_processed = image_raw - factor_stray * image_stray    

#        print("Removing bg. factor = {}, angle = {}".format(factor_stray, angle_rotate_deg))
        
# Apply the mask: convert any False pixels to NaN in prep for the sfit
            
        image_masked                = image_processed.copy()
        image_masked[mask == False] = math.nan
        
        frac_good = np.sum(mask) / (np.prod(np.shape(mask)))
        
        print("Applying mask, fraction good = {}".format(frac_good))
                
# Remove a polynomial from the result. This is where the mask comes into play.
# XXX NB: I think the logic here could be cleaned up. sfit() now allows a mask= argument ,
#         but it must not have when I wrote this code.        
        
        sfit_masked = hbt.sfit(image_masked, poly_after)
        
        image_processed = image_processed - sfit_masked

        print("Removing sfit {}".format(poly_after))

        # Plot the masks and sfits, for diagnostics 
        
        do_plot_masks = False
        if do_plot_masks:
            
            plt.subplot(1,3,1)
            plt.imshow(stretch(mask))
            plt.title('mask')
            plt.subplot(1,3,2)
            plt.imshow(stretch(image_masked))
            plt.title('image_masked')
            plt.subplot(1,3,3)
            plt.imshow(sfit_masked)
            plt.title('sfit_masked')
            plt.show()
        
# =============================================================================
# END OF CASE STATEMENT FOR METHODS
# =============================================================================

# Remove a small bias offset between odd and even rows ('jailbars')
# This might be better done before the sfit(), but in reality probably doesn't make a difference.

    image_processed = hbt.lorri_destripe(image_processed)
    
# If requested: plot the image, and the background that I remove. 
# Plot to Python console, not the GUI.

# Test stretching here
# We use astropy's stretching here, rather than matplotlib's norm= keyword. The basic idea of both of these 
# is the same, but I know that astropy has a percentile stretch available.

    DO_DIAGNOSTIC = False
    
    if (DO_DIAGNOSTIC):

        stretch = astropy.visualization.PercentileInterval(90)  # PI(90) scales array to 5th .. 95th %ile

        plt.rcParams['figure.figsize'] = 16,6

# Column 1: raw image

        im = image_raw

        im = hbt.remove_sfit(im, degree=5)
        plt.subplot(1,3,1) # vertical, horizontal, index
        plt.imshow(stretch(hbt.remove_sfit(im, degree=5)))
        plt.title('remove_sfit(image_raw, degree=5), mean=' + hbt.trunc(np.mean(im),3))
        plt.colorbar()
        
#       Column 2: Stray only. This will throw an error if we haven't read in a stray light file -- just ignore it.
        
        plt.subplot(1,3,2)
        try:
            plt.imshow(stretch(hbt.remove_sfit(image_stray, degree=5))) # This won't do much since it is already applied
        except UnboundLocalError:
            print("No stray light to subtract")
        
        plt.title('remove_sfit(stray_norm, degree=5), mean=' + hbt.trunc(np.mean(im),3))

        # Column 3: raw - stray

        plt.subplot(1,3,3)

        try:
            im = hbt.remove_sfit(image_raw - image_stray,degree=5)
            plt.imshow(stretch(im))
        except UnboundLocalError:  
            print("No stray light to subtract")
        
        plt.title('remove_sfit(image_raw - image_stray, degree=5), med ' + hbt.trunc(np.median(im),3))
        
        plt.show()

# Now return the array. If we have a mask, then we return it too, as a tuple
        
    if (DO_MASK): # If we loaded a mask
        return (image_processed, mask)
    else:
        return image_processed
        plt.subplot(1,4,1)
        plt.imshow(stretch(image_raw))
        plt.title('stretch(image_raw)')

        plt.subplot(1,4,2)
        plt.imshow(stretch(im))
        plt.imshow(mask_objects, alpha=0.2, cmap='plasma')
        plt.title('stretch(image_raw), overlay w mask_objects')
        
        plt.subplot(1,4,3)
        plt.imshow(mask_objects)
        plt.title('mask_objects')
        plt.show()

        plt.subplot(1,4,1)
        plt.imshow(stretch(hbt.remove_sfit(image_raw,degree=5)), cmap='plasma')
        plt.title('hbt.remove_sfit(image_raw)')

        plt.subplot(1,4,3)
        plt.imshow(stretch(hbt.remove_sfit(image_raw,degree=5, mask=np.logical_not(mask_objects))), cmap='plasma')
        plt.title('hbt.remove_sfit(mask=mask_objects)')
        
        plt.show()
        
        
###
#    method = 'String'
#    index_group = 5
#    index_image = 2
#    file = '/Users/throop/data/NH_Jring/data/jupiter/level2/lor/all/lor_0034676524_0x630_sci_1_opnav.fit'
#    image_raw = hbt.read_lorri(file)