示例#1
0
文件: HyspexPro.py 项目: EnSpec/hypro
def get_acquisition_time(dn_header_file, raw_imugps_file):
    """ Get Hyspex image acquistion time.

    Notes
    -----
    (1) This code is adapted from Brendan Heberlein ([email protected]).

    Parameters
    ----------
    header_file: str
        Hyspex DN image header filename.
    imugps_file: str
        Hyspex raw imugps filename.

    Returns
    -------
    when: datetime object
        Image acquisition time.
    """

    from datetime import datetime, timedelta
    from ENVI import read_envi_header

    import numpy as np

    header = read_envi_header(dn_header_file)
    week_start = datetime.strptime(f"{header['acquisition date']} 00:00:00",
                                   "%Y-%m-%d %H:%M:%S")
    week_seconds = np.loadtxt(raw_imugps_file)[:, 7].mean()
    epoch = datetime(1980, 1, 6, 0, 0)
    gps_week = (week_start - epoch).days // 7
    time_elapsed = timedelta(days=gps_week * 7, seconds=week_seconds)
    when = epoch + time_elapsed

    return when
示例#2
0
def plot_avg_rdn(avg_rdn_figure_file, avg_rdn_file):
    """ Plot average radiance to a figure.
    Arguments:
        avg_rdn_figure_file: str
            Average radiance figure filename.
        avg_rdn_file: str
            Average radiance filename.
    """

    if os.path.exists(avg_rdn_figure_file):
        logger.info('Save the average radiance spectra figure to %s.' %avg_rdn_figure_file)
        return

    from ENVI import read_envi_header
    header = read_envi_header(os.path.splitext(avg_rdn_file)[0]+'.hdr')
    avg_rdn = np.memmap(avg_rdn_file,
                        mode='r',
                        dtype='float32',
                        shape=(header['lines'],
                               header['samples'])) # shape=(bands, samples)
    wavelength = np.array([float(v) for v in header['waves'].split(',')])

    plt.figure(figsize=(10, 6))
    plt.plot(wavelength, avg_rdn, lw=1)
    plt.xlim(np.floor(wavelength.min()), np.ceil(wavelength.max()))
    plt.xlabel('Wavelength (nm)', fontsize=16)
    plt.ylabel(r'Radiance $(mW{\cdot}cm^{-2}{\cdot}{\mu}m^{-1}{\cdot}sr^{-1})$', fontsize=16)
    plt.xticks(fontsize=16)
    plt.yticks(fontsize=16)
    plt.savefig(avg_rdn_figure_file, dpi=1000)
    plt.close()
    avg_rdn.flush()
    del avg_rdn
    
    logger.info('Save the average radiance spectra figure to %s.' %avg_rdn_figure_file)
示例#3
0
def interpolate_detectors(sensor_dict, mode='bicurvilinear', *args, **kwargs):
    ''' Interpolate radiance over bad detectors. '''

    if mode == 'bicurvilinear':
        interpolate = interpolate_detectors__bicurvilinear_

    # Initialize radiance cube from disk
    hdr = read_envi_header(
        os.path.splitext(sensor_dict['raw_rdn_image_file'])[0] + '.hdr')
    rdn_image = np.memmap(sensor_dict['raw_rdn_image_file'],
                          shape=(hdr['lines'], hdr['bands'], hdr['samples']),
                          offset=hdr['header offset'],
                          mode='r+',
                          dtype='float32')  #, dtype='int16')

    ### TODO: Read dtype from header!

    # Load bad detectors registry from settings file
    bad_detectors = bad_detectors_from_hyspex_set(sensor_dict['setting_file'])

    # Interpolate over bad detectors
    logger.info('Do interpolation.')
    wavelengths = np.array(hdr['wavelength'], dtype=np.float64)

    interpolate(rdn_image, bad_detectors, wavelengths, *args, **kwargs)

    # Commit changes to disk
    del rdn_image
示例#4
0
文件: Figure.py 项目: EnSpec/hypro
def plot_angle_geometry(angle_geometry_figure_file, sca_image_file):
    """ Plot the sun-target-view geometry in a polar coordinate system.

    Parameters
    ----------
    angle_geometry_figure_file: str
        Angle geometry figure filename.
    sca_image_file: str
        Scan angle image filename.
    """

    if os.path.exists(angle_geometry_figure_file):
        logger.info('Save the angle geometry figure to %s.' %
                    angle_geometry_figure_file)
        return

    from ENVI import read_envi_header

    # Read SCA image data
    sca_header = read_envi_header(os.path.splitext(sca_image_file)[0] + '.hdr')
    sca_image = np.memmap(sca_image_file,
                          dtype='float32',
                          mode='r',
                          offset=0,
                          shape=(sca_header['bands'], sca_header['lines'],
                                 sca_header['samples']))

    # Scatter-plot view geometry
    plt.figure(figsize=(10, 10))
    ax = plt.subplot(111, projection='polar')
    ax.scatter(np.deg2rad(sca_image[1, ::10, ::10].flatten()),
               sca_image[0, ::10, ::10].flatten(),
               color='green',
               marker='.',
               s=10)
    sca_image.flush()
    del sca_image

    # Scatter-plot solar geometry
    ax.scatter(np.deg2rad(float(sca_header['sun azimuth'])),
               float(sca_header['sun zenith']),
               color='red',
               marker='*',
               s=500)
    ax.set_theta_direction(-1)
    ax.set_theta_zero_location('N')
    _, _ = ax.set_thetagrids([0, 45, 90, 135, 180, 225, 270, 315],
                             labels=('0 N', '45', '90 E', '135', '180 S',
                                     '225', '270 W', '315'))
    ax.tick_params(labelsize=20)
    plt.savefig(angle_geometry_figure_file, dpi=1000)
    plt.close()
    del sca_header, ax

    logger.info('Save the angle geometry figure to %s.' %
                angle_geometry_figure_file)
示例#5
0
def modify_hyspex_band(img, band, arr):
    src_header = read_envi_header(img + '.hdr')
    src_image = np.memmap(img,
                          dtype='int16',
                          mode='r+',
                          shape=(src_header['lines'], src_header['samples']),
                          offset=int(src_header['lines'] *
                                     src_header['samples'] * band * 16 / 8))
    src_image[:] = arr
    src_image.flush()
    del src_image, arr
示例#6
0
def plot_wvc_histogram(wvc_histogram_figure_file, water_vapor_column_image_file):
    """ Plot water vapor column histogram.
    wvc_histogram_figure_file: str
        Water vapor column histogram figure filename.
    water_vapor_column_image_file: str
        Water vapor column image filename.
    """

    if os.path.exists(wvc_histogram_figure_file):
        logger.info('Save water vapor column histogram figure to %s.' %wvc_histogram_figure_file)
        return

    from ENVI import read_envi_header

    # Read water vapor column image
    wvc_header = read_envi_header(os.path.splitext(water_vapor_column_image_file)[0]+'.hdr')
    wvc_image = np.memmap(water_vapor_column_image_file,
                          dtype='float32',
                          mode='r',
                          shape=(wvc_header['lines'], wvc_header['samples']))

    # Plot water vapor column histogram
    wvc_bins = np.arange(0, 51, 1)
    freq = []
    for bin_index in range(len(wvc_bins)-1):
        index = (wvc_image>=wvc_bins[bin_index])&(wvc_image<wvc_bins[bin_index+1])
        freq.append(np.sum(index)/wvc_image.size*100)
    freq = np.array(freq)
    freq_max = 100
    plt.figure(figsize=(10,6))
    plt.bar(wvc_bins[:-1], freq, width=1, color='darkgreen', edgecolor='black', linewidth=1)
    plt.vlines([wvc_image.mean()], 0, freq_max, color='darkred', lw=2, linestyles='solid', label=r'WVC$_{Mean}$')
    plt.vlines([wvc_image.mean()-2*wvc_image.std()], 0, freq_max, color='darkred', lw=2, linestyles='dotted', label=r'WVC$_{Mean}$-2WVC$_{SD}$')
    plt.vlines([wvc_image.mean()+2*wvc_image.std()], 0, freq_max, color='darkred', lw=2, linestyles='dashed', label=r'WVC$_{Mean}$+2WVC$_{SD}$')
    plt.xticks(ticks=np.arange(0, 51, 5), labels=np.arange(0, 51, 5), fontsize=20)
    plt.yticks(ticks=np.arange(0,101,10), labels=np.arange(0,101,10), fontsize=20)
    plt.xlabel('Water Vapor Column (mm)', fontsize=20)
    plt.ylabel('Relative Frequency (%)', fontsize=20)
    plt.xlim(0, 50)
    plt.ylim(0, freq_max)
    plt.legend(fontsize=20)
    plt.savefig(wvc_histogram_figure_file, dpi=1000)
    plt.close()
    
    wvc_image.flush()
    del wvc_image
    
    logger.info('Save water vapor column histogram figure to %s.' %wvc_histogram_figure_file)
示例#7
0
def process_sca_file(raw_sca_image_file, processed_sca_image_file):
    # Read raw scan angle data.
    raw_sca_header = read_envi_header(
        os.path.splitext(raw_sca_image_file)[0] + '.hdr')
    raw_sca_image = np.memmap(raw_sca_image_file,
                              mode='r',
                              dtype='float32',
                              shape=(raw_sca_header['bands'],
                                     raw_sca_header['lines'],
                                     raw_sca_header['samples']))

    # Initialize processed scan angle data.
    processed_sca_image = np.memmap(processed_sca_image_file,
                                    mode='w+',
                                    dtype='int16',
                                    shape=(raw_sca_header['bands'],
                                           raw_sca_header['lines'],
                                           raw_sca_header['samples']))
    # zenith*100
    angle = raw_sca_image[0, :, :] * 100
    angle[angle < 0] = 9100
    processed_sca_image[0, :, :] = angle.astype('int16')
    del angle

    # zenith*10
    angle = raw_sca_image[1, :, :] * 10
    angle[angle < 0] = -1
    processed_sca_image[1, :, :] = angle.astype('int16')
    del angle

    # Clear data
    processed_sca_image.flush()
    raw_sca_image.flush()
    del processed_sca_image, raw_sca_image

    # Write header
    raw_sca_header['description'] = 'SCA'
    raw_sca_header['data type'] = 2
    raw_sca_header['band names'] = [
        'Sensor Zenith [100*deg]', 'Sensor Azimuth [10*deg]'
    ]
    raw_sca_header['data ignore value'] = None
    write_envi_header(
        os.path.splitext(processed_sca_image_file)[0] + '.hdr', raw_sca_header)
def create_polygon(dir_in, outshp):
    if os.path.exists(outshp):
        print("Output file exists!")
        sys.exit(1)

    # Read IGM.
    igm_header = read_envi_header(glob.glob(dir_in + '*IGM.hdr')[0])
    igm_image = np.memmap(glob.glob(dir_in + '*IGM')[0],
                          dtype='float64',
                          mode='r',
                          offset=0,
                          shape=(2, igm_header['lines'],
                                 igm_header['samples']))
    lon = igm_image[0, :, :]
    lat = igm_image[1, :, :]
    igm_image.flush()
    del igm_image

    # concat outline lon points:
    # lons = list(lon[:,0].flatten()) +list(lon[-1,:].flatten()) + list(np.flip(lon[:,-1].flatten()))+  list(np.flip(lon[0,:].flatten()))
    lons = np.concatenate(
        (lon[:, 0].flatten(), lon[-1, :].flatten(),
         np.flip(lon[:, -1].flatten()), np.flip(lon[0, :].flatten())))
    # lats = list(lat[:,0].flatten()) +list(lat[-1,:].flatten()) + list(np.flip(lat[:,-1].flatten()))+  list(np.flip(lat[0,:].flatten()))
    lats = np.concatenate(
        (lat[:, 0].flatten(), lat[-1, :].flatten(),
         np.flip(lat[:, -1].flatten()), np.flip(lat[0, :].flatten())))

    # construct the geometry:
    ring = ogr.Geometry(ogr.wkbLinearRing)
    for i in range(0, len(lons)):
        ring.AddPoint(lons[i], lats[i])

    # Create polygon
    poly = ogr.Geometry(ogr.wkbPolygon)
    poly.AddGeometry(ring)
    return poly.ExportToWkt()
示例#9
0
def dn2rdn_Hyspex(rdn_image_file, dn_image_file, radio_cali_file,
                  acquisition_time):
    """ Do Hyspex radiometric calibration.

    Parameters
    ----------
    rdn_image_file: str
        Radiance image filename.
    dn_image_file: str
        Hyspex DN image filename.
    radio_cali_file: str
        Hyspex radiometric calibration coefficients filename.
    acquisition_time: datetime object
        Acquisition time.
    """

    if os.path.exists(rdn_image_file):
        logger.info('Write the radiance image to %s.' % rdn_image_file)
        return

    from ENVI import empty_envi_header, read_envi_header, write_envi_header

    # Read calibration coefficients
    radio_cali_header = read_envi_header(
        os.path.splitext(radio_cali_file)[0] + '.hdr')
    radio_cali_coeff = np.memmap(radio_cali_file,
                                 dtype='float64',
                                 mode='r',
                                 shape=(radio_cali_header['bands'],
                                        radio_cali_header['lines'],
                                        radio_cali_header['samples']))
    wavelengths = np.array(
        [float(v) for v in radio_cali_header['waves'].split(',')])
    fwhms = np.array([float(v) for v in radio_cali_header['fwhms'].split(',')])

    # Read DN image
    dn_header = read_envi_header(os.path.splitext(dn_image_file)[0] + '.hdr')
    dn_image = np.memmap(dn_image_file,
                         dtype='uint16',
                         mode='r',
                         offset=dn_header['header offset'],
                         shape=(dn_header['lines'], dn_header['bands'],
                                dn_header['samples']))

    # Get gain coefficients
    gain = radio_cali_coeff[0, :, :]  # shape=(bands, samples)

    # Do radiometric calibration
    info = 'Line (max=%d): ' % dn_header['lines']
    fid = open(rdn_image_file, 'wb')
    for from_line in range(0, dn_header['lines'], 500):
        info += '%d, ' % (from_line + 1)

        # Determine chunk size
        to_line = min(from_line + 500, dn_header['lines'])

        # Get offset coefficients
        if radio_cali_header['bands'] == 2:
            offset = radio_cali_coeff[1, :, :]  # shape=(bands, samples)
        else:
            background = np.stack([radio_cali_coeff[1, :, :]] *
                                  (to_line - from_line))
            backgroundLast = np.stack([radio_cali_coeff[2, :, :]] *
                                      (to_line - from_line))
            factor = np.arange(from_line, to_line) / dn_header['lines']
            offset = background + (
                backgroundLast - background
            ) * factor[:, np.newaxis,
                       np.newaxis]  # shape=(to_line-from_line, bands, samples)
            del background, backgroundLast, factor

        # Convert DN to radiance
        rdn = (dn_image[from_line:to_line, :, :].astype('float32') -
               offset) * gain  # shape=(to_line-from_line, bands, samples)

        # Write radiance to file
        fid.write(rdn.astype('float32').tostring())

        # Clear temporary data
        del rdn, to_line, offset
    fid.close()
    info += '%d, Done!' % dn_header['lines']
    logger.info(info)

    # Clear data
    dn_image.flush()
    radio_cali_coeff.flush()
    del gain, from_line
    del dn_image, radio_cali_coeff

    # Write header
    rdn_header = empty_envi_header()
    rdn_header['description'] = 'Hyspex radiance in mW/(cm2*um*sr)'
    rdn_header['file type'] = 'ENVI Standard'
    rdn_header['samples'] = dn_header['samples']
    rdn_header['lines'] = dn_header['lines']
    rdn_header['bands'] = dn_header['bands']
    rdn_header['byte order'] = 0
    rdn_header['header offset'] = 0
    rdn_header['interleave'] = 'bil'
    rdn_header['data type'] = 4
    rdn_header['wavelength'] = list(wavelengths)
    rdn_header['fwhm'] = list(fwhms)
    rdn_header['wavelength units'] = 'nm'
    rdn_header['default bands'] = dn_header['default bands']
    rdn_header['acquisition time'] = acquisition_time.strftime(
        '%Y-%m-%dT%H:%M:%S.%f')
    write_envi_header(os.path.splitext(rdn_image_file)[0] + '.hdr', rdn_header)
    del radio_cali_header, dn_header, rdn_header

    logger.info('Write the radiance image to %s.' % rdn_image_file)
示例#10
0
def orthorectify_dem(ortho_dem_image_file, igm_image_file, glt_image_file):
    """ Do orthorectifications on DEM.
    Arguments:
        ortho_dem_image_file: str
            Orthorectified DEM image filename.
        igm_image_file: str
            IGM image filename.
        glt_image_file: str
            Geographic look-up table image filename.
    """

    if os.path.exists(ortho_dem_image_file):
        logger.info('Write the geo-rectified DEM image to %s.' %ortho_dem_image_file)
        return

    from ENVI import empty_envi_header, read_envi_header, write_envi_header

    # Read IGM image (the third band is DEM).
    igm_header = read_envi_header(igm_image_file+'.hdr')
    igm_image = np.memmap(igm_image_file,
                          dtype='float64',
                          mode='r',
                          shape=(igm_header['bands'],
                                 igm_header['lines'],
                                 igm_header['samples']))
    del igm_header

    # Read GLT image.
    glt_header = read_envi_header(glt_image_file+'.hdr')
    glt_image = np.memmap(glt_image_file,
                          dtype=np.int32,
                          mode='r',
                          shape=(glt_header['bands'],
                                 glt_header['lines'],
                                 glt_header['samples']))

    # Get spatial indices.
    I, J = np.where((glt_image[0,:,:]>=0)&(glt_image[1,:,:]>=0))
    ortho_dem_image = np.zeros((glt_header['lines'], glt_header['samples']), dtype='float32')

    # Orthorectify DEM.
    fid = open(ortho_dem_image_file, 'wb')
    ortho_dem_image[:,:] = -1000.0
    ortho_dem_image[I,J] = igm_image[2, glt_image[0,I,J], glt_image[1,I,J]]
    fid.write(ortho_dem_image.tostring())
    fid.close()
    del ortho_dem_image
    igm_image.flush()
    glt_image.flush()
    del igm_image, glt_image
    
    # Write header.
    ortho_dem_header = empty_envi_header()
    ortho_dem_header['description'] = 'Geo-rectified DEM, in [m]'
    ortho_dem_header['file type'] = 'ENVI Standard'
    ortho_dem_header['samples'] = glt_header['samples']
    ortho_dem_header['lines'] = glt_header['lines']
    ortho_dem_header['bands'] = 1
    ortho_dem_header['byte order'] = 0
    ortho_dem_header['header offset'] = 0
    ortho_dem_header['data ignore value'] = -1000.0
    ortho_dem_header['interleave'] = 'bsq'
    ortho_dem_header['data type'] = 4
    ortho_dem_header['map info'] = glt_header['map info']
    ortho_dem_header['coordinate system string'] = glt_header['coordinate system string']
    write_envi_header(ortho_dem_image_file+'.hdr', ortho_dem_header)
    del glt_header, ortho_dem_header

    logger.info('Write the geo-rectified DEM image to %s.' %ortho_dem_image_file)
示例#11
0
def orthorectify_rdn(ortho_rdn_image_file, rdn_image_file, glt_image_file):
    """ Do orthorectifications on radiance.
    Arguments:
        ortho_rdn_image_file: str
            Orthorectified radiance filename.
        rdn_image_file: str
            Radiance image filename.
        glt_image_file: str
            Geographic look-up table image filename.
    """

    if os.path.exists(ortho_rdn_image_file):
        logger.info('Write the geo-rectified radiance image to %s.' %ortho_rdn_image_file)
        return

    from ENVI import empty_envi_header, read_envi_header, write_envi_header

    # Read radiance image.
    rdn_header = read_envi_header(rdn_image_file+'.hdr')
    rdn_image = np.memmap(rdn_image_file,
                          dtype='float32',
                          mode='r',
                          shape=(rdn_header['lines'],
                                 rdn_header['bands'],
                                 rdn_header['samples']))

    # Read GLT image.
    glt_header = read_envi_header(glt_image_file+'.hdr')
    glt_image = np.memmap(glt_image_file,
                          dtype=np.int32,
                          mode='r',
                          offset=0,
                          shape=(glt_header['bands'],
                                 glt_header['lines'],
                                 glt_header['samples']))

    # Get spatial indices.
    I, J = np.where((glt_image[0,:,:]>=0)&(glt_image[1,:,:]>=0))
    ortho_image = np.zeros((glt_header['lines'], glt_header['samples']), dtype='float32')

    # Orthorectify radiance.
    fid = open(ortho_rdn_image_file, 'wb')
    info = 'Band (max=%d): ' %rdn_header['bands']
    for band in range(rdn_header['bands']):
        if band%20==0:
            info += '%d, ' %(band+1)
        ortho_image[:,:] = 0.0
        ortho_image[I,J] = rdn_image[glt_image[0,I,J], band, glt_image[1,I,J]]
        fid.write(ortho_image.tostring())
    fid.close()
    info += 'Done! %s' %rdn_header['bands']
    logger.info(info)
    del ortho_image
    rdn_image.flush()
    glt_image.flush()
    del rdn_image, glt_image
    
    # Write header.
    ortho_rdn_header = empty_envi_header()
    ortho_rdn_header['description'] = 'Geo-rectified radiance, in [mW/(cm2*um*sr)]'
    ortho_rdn_header['file type'] = 'ENVI Standard'
    ortho_rdn_header['samples'] = glt_header['samples']
    ortho_rdn_header['lines'] = glt_header['lines']
    ortho_rdn_header['bands'] = rdn_header['bands']
    ortho_rdn_header['byte order'] = 0
    ortho_rdn_header['header offset'] = 0
    ortho_rdn_header['interleave'] = 'bsq'
    ortho_rdn_header['data type'] = 4
    ortho_rdn_header['wavelength'] = rdn_header['wavelength']
    ortho_rdn_header['fwhm'] = rdn_header['fwhm']
    ortho_rdn_header['wavelength units'] = 'nm'
    ortho_rdn_header['default bands'] = rdn_header['default bands']
    ortho_rdn_header['acquisition time'] = rdn_header['acquisition time']
    ortho_rdn_header['map info'] = glt_header['map info']
    ortho_rdn_header['coordinate system string'] = glt_header['coordinate system string']
    write_envi_header(ortho_rdn_image_file+'.hdr', ortho_rdn_header)
    del glt_header, rdn_header, ortho_rdn_header

    logger.info('Write the geo-rectified radiance image to %s.' %ortho_rdn_image_file)
示例#12
0
def orthorectify_sca(ortho_sca_image_file, sca_image_file, glt_image_file):
    """ Do orthorectifications on SCA.
    Arguments:
        ortho_sca_image_file: str
            Orthorectified SCA image filename.
        sca_image_file: str
            SCA image filename.
        glt_image_file: str
            Geographic look-up table image filename.
    """

    if os.path.exists(ortho_sca_image_file):
        logger.info('Write the geo-rectified SCA image to %s.' %ortho_sca_image_file)
        return

    from ENVI import empty_envi_header, read_envi_header, write_envi_header

    # Read SCA image.
    sca_header = read_envi_header(os.path.splitext(sca_image_file)[0]+'.hdr')

    # Read GLT image.
    glt_header = read_envi_header(os.path.splitext(glt_image_file)[0]+'.hdr')
    glt_image = np.memmap(glt_image_file,
                          dtype=np.int32,
                          mode='r',
                          shape=(glt_header['bands'],
                                 glt_header['lines'],
                                 glt_header['samples']))

    # Get spatial indices.
    I, J = np.where((glt_image[0,:,:]>=0)&(glt_image[1,:,:]>=0))
    ortho_sca_image = np.zeros((glt_header['lines'], glt_header['samples']), dtype='float32')

    # Orthorectify SCA.
    fid = open(ortho_sca_image_file, 'wb')
    for band in range(sca_header['bands']):
        ortho_sca_image[:,:] = -1000.0
        offset = sca_header['header offset']++4*band*sca_header['lines']*sca_header['samples']
        sca_image = np.memmap(sca_image_file,
                              dtype='float32',
                              mode='r',
                              offset=offset,
                              shape=(sca_header['lines'], sca_header['samples']))
        ortho_sca_image[I,J] = sca_image[glt_image[0,I,J], glt_image[1,I,J]]
        fid.write(ortho_sca_image.tostring())
        sca_image.flush()
        del sca_image
    fid.close()
    del ortho_sca_image
    glt_image.flush()
    del glt_image
    
    # Write header
    ortho_sca_header = empty_envi_header()
    ortho_sca_header['description'] = 'Geo-rectified SCA, in [deg]'
    ortho_sca_header['file type'] = 'ENVI Standard'
    ortho_sca_header['samples'] = glt_header['samples']
    ortho_sca_header['lines'] = glt_header['lines']
    ortho_sca_header['bands'] = sca_header['bands']
    ortho_sca_header['byte order'] = 0
    ortho_sca_header['header offset'] = 0
    ortho_sca_header['data ignore value'] = -1000.0
    ortho_sca_header['interleave'] = 'bsq'
    ortho_sca_header['data type'] = 4
    ortho_sca_header['band names'] = sca_header['band names']
    ortho_sca_header['sun zenith'] = sca_header['sun zenith']
    ortho_sca_header['sun azimuth'] = sca_header['sun azimuth']
    ortho_sca_header['map info'] = glt_header['map info']
    ortho_sca_header['coordinate system string'] = glt_header['coordinate system string']
    write_envi_header(ortho_sca_image_file+'.hdr', ortho_sca_header)
    del glt_header, sca_header, ortho_sca_header

    logger.info('Write the geo-rectified SCA image to %s.' %ortho_sca_image_file)
示例#13
0
def merge_rdn(merged_image_file, mask_file, sensors):
    """ Merge radiance images.
    Arguments:
        merged_image_file: str
            Merged radiance image filename.
        mask_file: str
            Background mask filename.
        sensors: dict
            Sensor dictionaries.
    """

    if os.path.exists(merged_image_file):
        logger.info('Write the merged refletance image to %s.' %
                    merged_image_file)
        return

    from ENVI import empty_envi_header, read_envi_header, write_envi_header

    # Read mask.
    mask_header = read_envi_header(os.path.splitext(mask_file)[0] + '.hdr')
    mask_image = np.memmap(mask_file,
                           mode='r',
                           dtype='bool',
                           shape=(mask_header['lines'],
                                  mask_header['samples']))

    # Get the map upper-left coordinates and pixel sizes of VNIR and SWIR images.
    ulx, uly, pixel_size = float(mask_header['map info'][3]), float(
        mask_header['map info'][4]), float(mask_header['map info'][5])

    # Determine regular map grids.
    x, y = np.meshgrid(ulx + np.arange(mask_header['samples']) * pixel_size,
                       uly - np.arange(mask_header['lines']) * pixel_size)
    del ulx, uly, pixel_size

    # Read radiance header and image.
    header_dict = dict()
    image_file_dict = dict()
    bands_waves_fwhms = []
    for sensor_index, sensor_dict in sensors.items():
        tmp_header = read_envi_header(
            os.path.splitext(sensor_dict['ortho_rdn_image_file'])[0] + '.hdr')
        for band in range(tmp_header['bands']):
            bands_waves_fwhms.append([
                '%s_%d' % (sensor_index, band), tmp_header['wavelength'][band],
                tmp_header['fwhm'][band]
            ])
        header_dict[sensor_index] = tmp_header
        image_file_dict[sensor_index] = sensor_dict['ortho_rdn_image_file']
    bands_waves_fwhms.sort(key=lambda x: x[1])

    # Merge images.
    wavelengths = []
    fwhms = []
    fid = open(merged_image_file, 'wb')
    for v in bands_waves_fwhms:
        # Determine which sensor, band to read.
        sensor_index, band = v[0].split('_')
        band = int(band)
        wavelengths.append(v[1])
        fwhms.append(v[2])
        header = header_dict[sensor_index]
        image_file = image_file_dict[sensor_index]

        # Write image.
        if ((v[1] >= 1339.0) &
            (v[1] <= 1438.0)) | ((v[1] >= 1808.0) &
                                 (v[1] <= 1978.0)) | (v[1] >= 2467.0):
            # resampled_image = np.zeros(x.shape)
            resampled_image = np.zeros(x.shape,
                                       dtype='int16')  # in int, by Ting
        else:
            offset = header[
                'header offset'] + 2 * band * header['lines'] * header[
                    'samples']  # in bytes, int16 consists 2 bytes
            rdn_image = np.memmap(
                image_file,
                # dtype='float32',
                dtype='int16',  # in int, by Ting
                mode='r',
                offset=offset,
                shape=(header['lines'], header['samples']))
            resampled_image = resample_ortho_rdn(
                np.copy(rdn_image) /
                1000,  # divided by 1000 to convert back to original rdn
                float(header_dict[sensor_index]['map info'][3]),
                float(header_dict[sensor_index]['map info'][4]),
                float(header_dict[sensor_index]['map info'][5]),
                x,
                y)
            # resampled_image[mask_image] = 0.0
            resampled_image = resampled_image * 1000  # times 1000 to convert to int, by Ting
            resampled_image[mask_image] = 0  # in int, by Ting
            rdn_image.flush()
            del rdn_image

        # fid.write(resampled_image.astype('float32').tostring())
        fid.write(resampled_image.astype(
            'int16').tostring())  # write in int ,by Ting
        del resampled_image
    fid.close()
    del header_dict, image_file_dict, x, y
    mask_image.flush()
    del mask_image

    # Write header.
    header = empty_envi_header()
    header['description'] = 'Merged radiance, in [mW/(cm2*um*sr)]'
    header['file type'] = 'ENVI Standard'
    header['samples'] = mask_header['samples']
    header['lines'] = mask_header['lines']
    header['bands'] = len(wavelengths)
    header['byte order'] = 0
    header['header offset'] = 0
    header['interleave'] = 'bsq'
    # header['data type'] = 4
    header['data type'] = 2  # in int, by Ting
    header['wavelength'] = wavelengths
    header['fwhm'] = fwhms
    header['wavelength units'] = 'nm'
    header['acquisition time'] = tmp_header['acquisition time']
    header['map info'] = mask_header['map info']
    header['coordinate system string'] = mask_header[
        'coordinate system string']
    write_envi_header(os.path.splitext(merged_image_file)[0] + '.hdr', header)
    del header, tmp_header

    logger.info('Write the merged refletance image to %s.' % merged_image_file)
示例#14
0
def main():
    # dir_in = r'Z:\townsenduser-rw\HyspexPro\Output\Cheesehead_V3\CHEESEHEAD_20190626\CHEESEHEAD_20190626_02_quicklooks'
    dir_in = r'Z:\townsenduser-rw\HyspexPro\Output\Cheesehead_V3\CHEESEHEAD_20190806'
    dest_dir = dir_in + '/Deshift'
    img = '/CHEESEHEAD_20190806_03_Refl'
    # ref_img = dir_in + '/Merge' + img
    dest_file = dest_dir + img
    header = read_envi_header(dir_in + '/Merge' + img + '.hdr')
    band_vnir, band_swir, idx_vnir, idx_swir = band_select(header)
    # hyspex_extract(dir_in + '/Merge' + img, [186], dest_dir + img + '_refband')
    # hyspex_extract(dir_in + '/Merge' + img, [185], dest_dir + img + '_swir_test')

    ref_img = dest_dir + img + '_refband'
    tgt_img = dest_dir + img + '_swir_test'

    grid_ls = [350]  #[300, 350, 400, 450]
    windows = [150]  #[128, 150, 264, 280]

    for i in range(len(grid_ls)):

        out = dest_dir + '/shifted_local_03_{}_{}'.format(
            grid_ls[i], windows[i])
        g = grid_ls[i]
        w = windows[i]
        kwargs = kwargs = {
            'grid_res': g,
            'window_size': (w, w),
            'path_out': out,
            'nodata': (0, 0),
            'r_b4match': 1,
            's_b4match': 1,
            'max_iter': 10,
            'max_shift': 10,
            # 'min_reliability': 25,
            # 'tieP_filter_level': 1,
            'resamp_alg_calc': 'nearest',
            'q': False
        }

        dest_img = dest_file + '_deshift'

        # calculate the shift info
        try:
            CRL = COREG_LOCAL(ref_img, tgt_img, **kwargs)
            CRL.calculate_spatial_shifts()
            # CRL.correct_shifts()
        except:
            continue

        # apply the shift to a single band:
        band = 185
        # shift_arg = {
        #         'band2process': int(band + 1),
        #         'resamp_alg': 'nearest',
        #         'nodata': 0,
        #         'align_grids': True,
        #         'q': True
        #     }
        result = DESHIFTER(dir_in + '/Merge' + img,
                           CRL.coreg_info,
                           band2process=int(band + 1),
                           align_grids=True,
                           match_gsd=True,
                           nodata=0,
                           resamp_alg='cubic',
                           q=True).correct_shifts()
        modify_hyspex_band(dest_img, band, result['arr_shifted'])
示例#15
0
def make_quicklook(quicklook_figure_file, rdn_image_file, glt_image_file):
    """ Make a RGB quicklook image.
    Arguments:
        quicklook_figure_file: str
            Geo-rectified quicklook figure filename.
        rdn_image_file: str
            Radiance image filename, in BIL format.
        glt_image_file: str
            GLT image filename.
    """

    if os.path.exists(quicklook_figure_file):
        logger.info('Save the quicklook figure to %s.' % quicklook_figure_file)
        return

    from ENVI import read_envi_header
    from Spectra import get_closest_wave

    # Read radiance image data.
    rdn_header = read_envi_header(os.path.splitext(rdn_image_file)[0] + '.hdr')
    rdn_image = np.memmap(
        rdn_image_file,
        # dtype='float32',
        dtype='int16',  # to int, by Ting
        mode='r',
        shape=(rdn_header['lines'], rdn_header['bands'],
               rdn_header['samples']))

    # Get RGB bands.
    if rdn_header['default bands'] is not None:
        rgb_bands = rdn_header['default bands']
    else:
        rgb_bands = []
        wave, _ = get_closest_wave(rdn_header['wavelength'], 450)
        if abs(wave - 450) < 10:
            for rgb_wave in [680, 550, 450]:
                _, band = get_closest_wave(rdn_header['wavelength'], rgb_wave)
                rgb_bands.append(band)
        else:
            for rgb_wave in [1220, 1656, 2146]:
                _, band = get_closest_wave(rdn_header['wavelength'], rgb_wave)
                rgb_bands.append(band)
        del band, wave

    # Read GLT image.
    glt_header = read_envi_header(os.path.splitext(glt_image_file)[0] + '.hdr')
    glt_image = np.memmap(glt_image_file,
                          dtype=np.int32,
                          mode='r',
                          shape=(2, glt_header['lines'],
                                 glt_header['samples']))

    # Write RGB image.
    driver = gdal.GetDriverByName('GTiff')
    ds = driver.Create(quicklook_figure_file, glt_header['samples'],
                       glt_header['lines'], 3, gdal.GDT_Byte)
    ds.SetGeoTransform(
        (float(glt_header['map info'][3]), float(glt_header['map info'][5]), 0,
         float(glt_header['map info'][4]), 0,
         -float(glt_header['map info'][6])))
    ds.SetProjection(glt_header['coordinate system string'])
    image = np.zeros((glt_header['lines'], glt_header['samples']),
                     dtype='uint8')
    I, J = np.where((glt_image[0, :, :] >= 0) & (glt_image[1, :, :] >= 0))
    for band_index, rgb_band in enumerate(rgb_bands):
        image[:, :] = 0
        tmp_image = linear_percent_stretch(rdn_image[:, rgb_band, :])
        image[I, J] = tmp_image[glt_image[0, I, J], glt_image[1, I, J]]
        ds.GetRasterBand(band_index + 1).WriteArray(image)
        del tmp_image
    glt_image.flush()
    rdn_image.flush()
    del glt_image, rdn_image
    ds = None
    del I, J, glt_header, rdn_header, image

    logger.info('Save the quicklook figure to %s.' % quicklook_figure_file)
示例#16
0
def calculate_sca(sca_image_file, imugps_file, igm_image_file, sun_angles):
    """ Create a scan angle (SCA) image.
    Arguments:
        sca_image_file: str
            Scan angle filename.
        imu_gps_file: str
            IMU/GPS filename.
        igm_image_file: str
            IGM image filename.
        sun_angles: list
            Sun angles [sun zenith, sun azimuth], in degrees.
    """

    if os.path.exists(sca_image_file):
        logger.info('Write the SCA to %s.' % sca_image_file)
        return

    from ENVI import empty_envi_header, read_envi_header, write_envi_header

    # Read IGM data.
    igm_header = read_envi_header(os.path.splitext(igm_image_file)[0] + '.hdr')
    igm_image = np.memmap(igm_image_file,
                          dtype='float64',
                          mode='r',
                          offset=0,
                          shape=(igm_header['bands'], igm_header['lines'],
                                 igm_header['samples']))

    # Read GPS data.
    imugps = np.loadtxt(imugps_file)  # ID, X, Y, Z, R, P, H, ...

    # Calculate sensor angles.
    DX = igm_image[0, :, :] - np.expand_dims(imugps[:, 1], axis=1)
    DY = igm_image[1, :, :] - np.expand_dims(imugps[:, 2], axis=1)
    DZ = igm_image[2, :, :] - np.expand_dims(imugps[:, 3], axis=1)

    igm_image.flush()
    del imugps, igm_image

    view_zenith = np.abs(np.arctan(np.sqrt(DX**2 + DY**2) / DZ))

    index = view_zenith >= np.deg2rad(40.0)
    if np.any(index):
        view_zenith[index] = np.deg2rad(39.0)
    del index

    view_azimuth = np.arcsin(np.abs(DX) / np.sqrt(DX**2 + DY**2))

    index = (DX > 0) & (DY < 0)
    view_azimuth[index] = np.pi - view_azimuth[index]
    index = (DX < 0) & (DY < 0)
    view_azimuth[index] = np.pi + view_azimuth[index]
    index = (DX < 0) & (DY > 0)
    view_azimuth[index] = 2 * np.pi - view_azimuth[index]
    del DX, DY, DZ, index

    # Save scan angles.
    fid = open(sca_image_file, 'wb')
    fid.write(np.rad2deg(view_zenith).astype('float32').tostring())
    fid.write(np.rad2deg(view_azimuth).astype('float32').tostring())
    fid.close()
    del view_zenith, view_azimuth

    # Write scan angle header file.
    sca_header = empty_envi_header()
    sca_header['description'] = 'SCA [deg]'
    sca_header['file type'] = 'ENVI Standard'
    sca_header['samples'] = igm_header['samples']
    sca_header['lines'] = igm_header['lines']
    sca_header['bands'] = 2
    sca_header['byte order'] = 0
    sca_header['header offset'] = 0
    sca_header['interleave'] = 'bsq'
    sca_header['data type'] = 4
    sca_header['band names'] = ['Sensor Zenith [deg]', 'Sensor Azimuth [deg]']
    sca_header['sun zenith'] = sun_angles[0]
    sca_header['sun azimuth'] = sun_angles[1]
    sca_header_file = os.path.splitext(sca_image_file)[0] + '.hdr'
    write_envi_header(sca_header_file, sca_header)
    del sca_header

    logger.info('Write the SCA to %s.' % sca_image_file)
示例#17
0
def pre_classification(pre_class_image_file,
                       rdn_image_file,
                       sun_zenith,
                       distance,
                       background_mask_file=None):
    """ Pre-classify the image.

    Notes
    -----
    (1) The classification algorithm used here is from ATCOR.

    Parameters
    ----------
    pre_class_image_file: str
        Pre-classification image filename.
    rdn_image_file: str
        Radiance image filename, either BIL or BSQ.
    sun_zenith: float
        Sun zenith angle in degrees.
    distance: float
        Sun-Earth distance.
    background_mask_file: str
        Background mask filename.
    """

    if os.path.exists(pre_class_image_file):
        logger.info('Write the pre-classification map to %s.' %
                    (pre_class_image_file))
        return

    from ENVI import empty_envi_header, read_envi_header, write_envi_header
    from Spectra import get_closest_wave, resample_solar_flux

    # Read radiance image data
    rdn_header = read_envi_header(os.path.splitext(rdn_image_file)[0] + '.hdr')
    if rdn_header['interleave'].lower() == 'bil':
        rdn_image = np.memmap(rdn_image_file,
                              dtype='float32',
                              mode='r',
                              shape=(rdn_header['lines'], rdn_header['bands'],
                                     rdn_header['samples']))
    elif rdn_header['interleave'].lower() == 'bsq':
        rdn_image = np.memmap(rdn_image_file,
                              dtype='float32',
                              mode='r',
                              shape=(rdn_header['bands'], rdn_header['lines'],
                                     rdn_header['samples']))
    else:
        logger.error('Cannot read radiance data from %s format file.' %
                     rdn_header['interleave'])

    # Read solar flux
    solar_flux = resample_solar_flux(solar_flux_file, rdn_header['wavelength'],
                                     rdn_header['fwhm'])
    cos_sun_zenith = np.cos(np.deg2rad(sun_zenith))
    d2 = distance**2

    # Initialize classification
    pre_class_image = np.memmap(pre_class_image_file,
                                dtype='uint8',
                                mode='w+',
                                shape=(rdn_header['lines'],
                                       rdn_header['samples']))
    pre_class_image[:, :] = 0

    # Define VNIR sensor wavelengths
    blue_wave, blue_band = get_closest_wave(rdn_header['wavelength'], 470)
    green_wave, green_band = get_closest_wave(rdn_header['wavelength'], 550)
    red_wave, red_band = get_closest_wave(rdn_header['wavelength'], 660)
    nir_wave, nir_band = get_closest_wave(rdn_header['wavelength'], 850)

    # Define SWIR sensor wavelengths
    cirrus_wave, cirrus_band = get_closest_wave(rdn_header['wavelength'], 1380)
    swir1_wave, swir1_band = get_closest_wave(rdn_header['wavelength'], 1650)
    swir2_wave, swir2_band = get_closest_wave(rdn_header['wavelength'], 2200)

    # Determine the sensor type
    if_vnir = abs(blue_wave - 470) < 20 and abs(green_wave - 550) < 20 and abs(
        red_wave - 660) < 20 and abs(nir_wave - 850) < 20
    if_swir = abs(cirrus_wave - 1380) < 20 and abs(
        swir1_wave - 1650) < 20 and abs(swir2_wave - 2200) < 20
    if_whole = if_vnir and if_swir

    # Do classification
    if if_whole:
        # Calculate the reflectance at different bands
        if rdn_header['interleave'].lower() == 'bil':
            blue_refl = rdn_image[:, blue_band, :] * np.pi * d2 / (
                solar_flux[blue_band] * cos_sun_zenith)
            green_refl = rdn_image[:, green_band, :] * np.pi * d2 / (
                solar_flux[green_band] * cos_sun_zenith)
            red_refl = rdn_image[:, red_band, :] * np.pi * d2 / (
                solar_flux[red_band] * cos_sun_zenith)
            nir_refl = rdn_image[:, nir_band, :] * np.pi * d2 / (
                solar_flux[nir_band] * cos_sun_zenith)

            cirrus_refl = rdn_image[:, cirrus_band, :] * np.pi * d2 / (
                solar_flux[cirrus_band] * cos_sun_zenith)
            swir1_refl = rdn_image[:, swir1_band, :] * np.pi * d2 / (
                solar_flux[swir1_band] * cos_sun_zenith)
            swir2_refl = rdn_image[:, swir2_band, :] * np.pi * d2 / (
                solar_flux[swir2_band] * cos_sun_zenith)
        else:
            blue_refl = rdn_image[blue_band, :, :] * np.pi * d2 / (
                solar_flux[blue_band] * cos_sun_zenith)
            green_refl = rdn_image[green_band, :, :] * np.pi * d2 / (
                solar_flux[green_band] * cos_sun_zenith)
            red_refl = rdn_image[red_band, :, :] * np.pi * d2 / (
                solar_flux[red_band] * cos_sun_zenith)
            nir_refl = rdn_image[nir_band, :, :] * np.pi * d2 / (
                solar_flux[nir_band] * cos_sun_zenith)

            cirrus_refl = rdn_image[cirrus_band, :, :] * np.pi * d2 / (
                solar_flux[cirrus_band] * cos_sun_zenith)
            swir1_refl = rdn_image[swir1_band, :, :] * np.pi * d2 / (
                solar_flux[swir1_band] * cos_sun_zenith)
            swir2_refl = rdn_image[swir2_band, :, :] * np.pi * d2 / (
                solar_flux[swir2_band] * cos_sun_zenith)
        rdn_image.flush()
        del rdn_image

        # Calculate NDSI
        ndsi = (green_refl - swir1_refl) / (green_refl + swir1_refl + 1e-10)

        # Water
        water = (nir_refl < 0.05) & (swir1_refl < 0.03)

        # Land
        land = ~water

        # Shadow
        shadow = (water | land) & (green_refl < 0.01)
        land = land & (~shadow)
        water = water & (~shadow)

        # Cloud over land
        Tc = 0.20
        cloud_over_land = land & (blue_refl > Tc) & (red_refl > 0.15) & (
            nir_refl < red_refl * 2.0) & (nir_refl > red_refl * 0.8) & (
                nir_refl > swir1_refl) & (ndsi < 0.7)
        land = land & (~cloud_over_land)

        # Cloud over water
        cloud_over_water = water & (blue_refl < 0.40) & (blue_refl > 0.20) & (
            green_refl < blue_refl) & (nir_refl < green_refl) & (
                swir1_refl < 0.15) & (ndsi < 0.20)
        water = water & (~cloud_over_water)

        # Cloud shadow
        cloud_shadow = (water | land) & (nir_refl < 0.12) & (
            nir_refl > 0.04) & (swir1_refl < 0.20)
        land = land & (~cloud_shadow)
        water = water & (~cloud_shadow)

        # Snow & ice
        snow_ice = land & (((blue_refl > 0.22) & (ndsi > 0.60)) |
                           ((green_refl > 0.22) & (ndsi > 0.25) &
                            (swir2_refl < 0.5 * green_refl)))
        land = land & (~snow_ice)

        # Cirrus over land & water
        thin_cirrus = (cirrus_refl < 0.015) & (cirrus_refl > 0.010)
        medium_cirrus = (cirrus_refl < 0.025) & (cirrus_refl >= 0.015)
        thick_cirrus = (cirrus_refl < 0.040) & (cirrus_refl >= 0.025)

        thin_cirrus_over_land = land & thin_cirrus
        land = land & (~thin_cirrus_over_land)

        medium_cirrus_over_land = land & medium_cirrus
        land = land & (~medium_cirrus_over_land)

        thick_cirrus_over_land = land & thick_cirrus
        land = land & (~thick_cirrus_over_land)

        thin_cirrus_over_water = water & thin_cirrus
        water = water & (~thin_cirrus_over_water)

        medium_cirrus_over_water = water & thin_cirrus
        water = water & (~medium_cirrus_over_water)

        thick_cirrus_over_water = water & thin_cirrus
        water = water & (~thick_cirrus_over_water)

        cirrus_cloud = (water
                        | land) & (cirrus_refl < 0.050) & (cirrus_refl > 0.040)
        land = land & (~cirrus_cloud)
        water = water & (~cirrus_cloud)

        thick_cirrus_cloud = (water | land) & (cirrus_refl > 0.050)
        land = land & (~thick_cirrus_cloud)
        water = water & (~thick_cirrus_cloud)

        # Haze over water
        T2 = 0.04
        T1 = 0.12
        thin_haze_over_water = water & (nir_refl >= T1) & (nir_refl <= 0.06)
        water = water & (~thin_haze_over_water)
        medium_haze_over_water = water & (nir_refl >= 0.06) & (nir_refl <= T2)
        water = water & (~medium_haze_over_water)

        # Assign class values
        pre_class_image[shadow] = 1
        pre_class_image[thin_cirrus_over_water] = 2
        pre_class_image[medium_cirrus_over_water] = 3
        pre_class_image[thick_cirrus_over_water] = 4
        pre_class_image[land] = 5
        pre_class_image[snow_ice] = 7
        pre_class_image[thin_cirrus_over_land] = 8
        pre_class_image[medium_cirrus_over_land] = 9
        pre_class_image[thick_cirrus_over_land] = 10
        pre_class_image[thin_haze_over_water] = 13
        pre_class_image[medium_haze_over_water] = 14
        pre_class_image[cloud_over_land] = 15
        pre_class_image[cloud_over_water] = 16
        pre_class_image[water] = 17
        pre_class_image[cirrus_cloud] = 18
        pre_class_image[thick_cirrus_cloud] = 19
        pre_class_image[cloud_shadow] = 22

        # Clear data
        del shadow, thin_cirrus_over_water, medium_cirrus_over_water
        del thick_cirrus_over_water, land, snow_ice
        del thin_cirrus_over_land, medium_cirrus_over_land, thick_cirrus_over_land
        del thin_haze_over_water, medium_haze_over_water, cloud_over_land
        del cloud_over_water, water, cirrus_cloud
        del thick_cirrus_cloud, cloud_shadow

    elif if_vnir:
        # Calculate the reflectance at different bands
        if rdn_header['interleave'].lower() == 'bil':
            blue_refl = rdn_image[:, blue_band, :] * np.pi * d2 / (
                solar_flux[blue_band] * cos_sun_zenith)
            green_refl = rdn_image[:, green_band, :] * np.pi * d2 / (
                solar_flux[green_band] * cos_sun_zenith)
            red_refl = rdn_image[:, red_band, :] * np.pi * d2 / (
                solar_flux[red_band] * cos_sun_zenith)
            nir_refl = rdn_image[:, nir_band, :] * np.pi * d2 / (
                solar_flux[nir_band] * cos_sun_zenith)
        else:
            blue_refl = rdn_image[blue_band, :, :] * np.pi * d2 / (
                solar_flux[blue_band] * cos_sun_zenith)
            green_refl = rdn_image[green_band, :, :] * np.pi * d2 / (
                solar_flux[green_band] * cos_sun_zenith)
            red_refl = rdn_image[red_band, :, :] * np.pi * d2 / (
                solar_flux[red_band] * cos_sun_zenith)
            nir_refl = rdn_image[nir_band, :, :] * np.pi * d2 / (
                solar_flux[nir_band] * cos_sun_zenith)
        rdn_image.flush()
        del rdn_image

        # Water
        water = nir_refl < 0.05

        # Land
        land = ~water

        # Shadow
        shadow = (water | land) & (green_refl < 0.01)
        land = land & (~shadow)
        water = water & (~shadow)

        # Cloud over land
        Tc = 0.20
        cloud_over_land = land & (blue_refl > Tc) & (red_refl > 0.15) & (
            nir_refl < red_refl * 2.0) & (nir_refl > red_refl * 0.8)
        land = land & (~cloud_over_land)

        # Cloud over water
        cloud_over_water = water & (blue_refl < 0.40) & (blue_refl > 0.20) & (
            green_refl < blue_refl) & (nir_refl < green_refl)
        water = water & (~cloud_over_water)

        # Cloud shadow
        cloud_shadow = (water | land) & (nir_refl < 0.12) & (nir_refl > 0.04)
        land = land & (~cloud_shadow)
        water = water & (~cloud_shadow)

        # Haze over water
        T2 = 0.04
        T1 = 0.12
        thin_haze_over_water = water & (nir_refl >= T1) & (nir_refl <= 0.06)
        water = water & (~thin_haze_over_water)
        medium_haze_over_water = water & (nir_refl >= 0.06) & (nir_refl <= T2)
        water = water & (~medium_haze_over_water)

        # Assign class values
        pre_class_image[shadow] = 1
        pre_class_image[land] = 5
        pre_class_image[thin_haze_over_water] = 13
        pre_class_image[medium_haze_over_water] = 14
        pre_class_image[cloud_over_land] = 15
        pre_class_image[cloud_over_water] = 16
        pre_class_image[water] = 17
        pre_class_image[cloud_shadow] = 22

        # Clear data
        del shadow, land, thin_haze_over_water
        del medium_haze_over_water, cloud_over_land, cloud_over_water
        del water, cloud_shadow

    elif if_swir:
        # Calculate the reflectance at different bands
        if rdn_header['interleave'] == 'bil':
            cirrus_refl = rdn_image[:, cirrus_band, :] * np.pi * d2 / (
                solar_flux[cirrus_band] * cos_sun_zenith)
            swir1_refl = rdn_image[:, swir1_band, :] * np.pi * d2 / (
                solar_flux[swir1_band] * cos_sun_zenith)
            swir2_refl = rdn_image[:, swir2_band, :] * np.pi * d2 / (
                solar_flux[swir2_band] * cos_sun_zenith)
        else:
            cirrus_refl = rdn_image[cirrus_band, :, :] * np.pi * d2 / (
                solar_flux[cirrus_band] * cos_sun_zenith)
            swir1_refl = rdn_image[swir1_band, :, :] * np.pi * d2 / (
                solar_flux[swir1_band] * cos_sun_zenith)
            swir2_refl = rdn_image[swir2_band, :, :] * np.pi * d2 / (
                solar_flux[swir2_band] * cos_sun_zenith)
        rdn_image.flush()
        del rdn_image

        # Water
        water = swir1_refl < 0.03

        # Land
        land = ~water

        # Cirrus over land & water
        thin_cirrus = (cirrus_refl < 0.015) & (cirrus_refl > 0.010)
        medium_cirrus = (cirrus_refl < 0.025) & (cirrus_refl >= 0.015)
        thick_cirrus = (cirrus_refl < 0.040) & (cirrus_refl >= 0.025)

        thin_cirrus_over_land = land & thin_cirrus
        land = land & (~thin_cirrus_over_land)

        medium_cirrus_over_land = land & medium_cirrus
        land = land & (~medium_cirrus_over_land)

        thick_cirrus_over_land = land & thick_cirrus
        land = land & (~thick_cirrus_over_land)

        thin_cirrus_over_water = water & thin_cirrus
        water = water & (~thin_cirrus_over_water)

        medium_cirrus_over_water = water & thin_cirrus
        water = water & (~medium_cirrus_over_water)

        thick_cirrus_over_water = water & thin_cirrus
        water = water & (~thick_cirrus_over_water)

        cirrus_cloud = (water
                        | land) & (cirrus_refl < 0.050) & (cirrus_refl > 0.040)
        land = land & (~cirrus_cloud)
        water = water & (~cirrus_cloud)

        thick_cirrus_cloud = (water | land) & (cirrus_refl > 0.050)
        land = land & (~thick_cirrus_cloud)
        water = water & (~thick_cirrus_cloud)

        # Assign class values
        pre_class_image[thin_cirrus_over_water] = 2
        pre_class_image[medium_cirrus_over_water] = 3
        pre_class_image[thick_cirrus_over_water] = 4
        pre_class_image[land] = 5
        pre_class_image[thin_cirrus_over_land] = 8
        pre_class_image[medium_cirrus_over_land] = 9
        pre_class_image[thick_cirrus_over_land] = 10
        pre_class_image[water] = 17
        pre_class_image[cirrus_cloud] = 18
        pre_class_image[thick_cirrus_cloud] = 19

        # Clear data
        del thin_cirrus_over_water, medium_cirrus_over_water, thick_cirrus_over_water,
        del land, thin_cirrus_over_land, medium_cirrus_over_land, thick_cirrus_over_land
        del water, cirrus_cloud, thick_cirrus_cloud

    else:
        logger.error(
            'Cannot find appropriate wavelengths to do pre-classification.')

    # Apply background mask
    if background_mask_file is not None:
        bg_mask_header = read_envi_header(
            os.path.splitext(background_mask_file)[0] + '.hdr')
        bg_mask_image = np.memmap(background_mask_file,
                                  mode='r',
                                  dtype='bool',
                                  shape=(bg_mask_header['lines'],
                                         bg_mask_header['samples']))
        pre_class_image[bg_mask_image] = 0
        bg_mask_image.flush()
        del bg_mask_image

    # Clear data
    pre_class_image.flush()
    del pre_class_image

    # Write header
    pre_class_header = empty_envi_header()
    pre_class_header['description'] = 'Pre-classification'
    pre_class_header['file type'] = 'ENVI Standard'
    pre_class_header['samples'] = rdn_header['samples']
    pre_class_header['lines'] = rdn_header['lines']
    pre_class_header['classes'] = 23
    pre_class_header['bands'] = 1
    pre_class_header['byte order'] = 0
    pre_class_header['header offset'] = 0
    pre_class_header['interleave'] = 'bsq'
    pre_class_header['data type'] = 1
    pre_class_header['class names'] = [
        '0: geocoded background', '1: shadow', '2: thin cirrus (water)',
        '3: medium cirrus (water)', '4: thick cirrus (water)',
        '5: land (clear)', '6: saturated', '7: snow/ice',
        '8: thin cirrus (land)', '9: medium cirrus (land)',
        '10: thick cirrus (land)', '11: thin haze (land)',
        '12: medium haze (land)', '13: thin haze/glint (water)',
        '14: med. haze/glint (water)', '15: cloud (land)', '16: cloud (water)',
        '17: water', '18: cirrus cloud', '19: cirrus cloud (thick)',
        '20: bright', '21: topogr. shadow', '22: cloud shadow'
    ]
    pre_class_header['class lookup'] = [
        80, 80, 80, 0, 0, 0, 0, 160, 250, 0, 200, 250, 0, 240, 250, 180, 100,
        40, 200, 0, 0, 250, 250, 250, 240, 240, 170, 225, 225, 150, 210, 210,
        120, 250, 250, 100, 230, 230, 80, 80, 100, 250, 130, 130, 250, 180,
        180, 180, 160, 160, 240, 0, 0, 250, 180, 180, 100, 160, 160, 100, 200,
        200, 200, 40, 40, 40, 50, 50, 50
    ]

    pre_class_header['map info'] = rdn_header['map info']
    pre_class_header['coordinate system string'] = rdn_header[
        'coordinate system string']
    write_envi_header(
        os.path.splitext(pre_class_image_file)[0] + '.hdr', pre_class_header)

    logger.info('Write the pre-classification map to %s.' %
                (pre_class_image_file))
示例#18
0
def estimate_vis(vis_file, ddv_file, atm_lut_file, rdn_file, sca_file,
                 background_mask_file):
    """ Estimate visibility.
    Arguments:
        vis_file: str
            Visibility map filename.
        ddv_file: str
            Dark dense vegetation map filename.
        atm_lut_file: str
            Atmospheric lookup table filename.
        rdn_file: str
            Radiance filename.
        sca_file: str
            Scan angle filename.
        background_mask_file:
            Background mask filename.
    """

    if os.path.exists(ddv_file) and os.path.exists(vis_file):
        logger.info('Write the visibility map to %s.' % vis_file)
        logger.info('Write the DDV map to %s.' % ddv_file)
        return

    from ENVI import read_envi_header, empty_envi_header, write_envi_header
    from AtmLUT import read_binary_metadata
    from Spectra import get_closest_wave
    from AtmCorr import atm_corr_band

    # Read radiance header.
    rdn_header = read_envi_header(os.path.splitext(rdn_file)[0] + '.hdr')

    # Find VNIR and SWIR sensor wavelengths.
    red_wave, red_band = get_closest_wave(rdn_header['wavelength'], 660)
    nir_wave, nir_band = get_closest_wave(rdn_header['wavelength'], 850)
    swir1_wave, swir1_band = get_closest_wave(rdn_header['wavelength'], 1650)
    swir2_wave, swir2_band = get_closest_wave(rdn_header['wavelength'], 2130)

    # Determine the sensor type.
    if_vnir = abs(red_wave - 660) < 20 and abs(nir_wave - 850) < 20
    if_swir = abs(swir1_wave - 1650) < 20 or abs(swir2_wave - 2130) < 20

    if if_vnir and if_swir:
        logger.info(
            'Both VNIR and SWIR bands are used for estimating visibility.')
    elif if_vnir:
        logger.info('Only VNIR bands are used for estimating visibility.')
    elif if_swir:
        logger.info(
            'Only SWIR bands are available, a constant visibility (23 km) is used.'
        )
    else:
        logger.error(
            'Cannot find appropriate bands for estimating visibility.')

    # Read atmospheric lookup table.
    atm_lut_metadata = read_binary_metadata(atm_lut_file + '.meta')
    atm_lut_metadata['shape'] = tuple(
        [int(v) for v in atm_lut_metadata['shape']])
    atm_lut_RHO = np.array([float(v) for v in atm_lut_metadata['RHO']])
    atm_lut_WVC = np.array([float(v) for v in atm_lut_metadata['WVC']])
    atm_lut_VIS = np.array([float(v) for v in atm_lut_metadata['VIS']])
    atm_lut_VZA = np.array([float(v) for v in atm_lut_metadata['VZA']])
    atm_lut_RAA = np.array([float(v) for v in atm_lut_metadata['RAA']])

    atm_lut = np.memmap(atm_lut_file,
                        dtype=atm_lut_metadata['dtype'],
                        mode='r',
                        shape=atm_lut_metadata['shape']
                        )  # shape = (RHO, WVC, VIS, VZA, RAA, WAVE)

    # Read radiance image.
    rdn_image = np.memmap(
        rdn_file,
        # dtype='float32',
        dtype='int16',  # in int, by Ting
        mode='r',
        shape=(rdn_header['bands'], rdn_header['lines'],
               rdn_header['samples']))

    # Read VZA and RAA image.
    sca_header = read_envi_header(os.path.splitext(sca_file)[0] + '.hdr')
    saa = float(sca_header['sun azimuth'])
    sca_image = np.memmap(sca_file,
                          dtype='float32',
                          offset=0,
                          shape=(sca_header['bands'], sca_header['lines'],
                                 sca_header['samples']))
    # vza
    vza_image = np.copy(sca_image[0, :, :])
    # raa
    raa_image = saa - sca_image[1, :, :]
    raa_image[raa_image < 0] += 360.0
    raa_image[raa_image > 180] = 360.0 - raa_image[raa_image > 180]
    raa_image[raa_image == 180] = 179.0
    # Constrain RAA within bounds of ALT
    raa_max = atm_lut_RAA.max()
    raa_image[raa_image > raa_max] = raa_max - 5e-2
    # clear data
    sca_image.flush()
    del sca_header, saa, sca_image

    # Set visibility and water vapor column values.
    metadata = read_binary_metadata(atm_lut_file + '.meta')
    tmp_wvc_image = np.ones(
        vza_image.shape) * atm_db_wvc_lut[metadata['atm_mode']]
    tmp_vis_image = np.ones(vza_image.shape) * 23
    del metadata

    # Read background mask.
    bg_header = read_envi_header(
        os.path.splitext(background_mask_file)[0] + '.hdr')
    bg_mask = np.memmap(background_mask_file,
                        dtype='bool',
                        mode='r',
                        shape=(bg_header['lines'], bg_header['samples']))

    # Calculate NDVI.
    red_refl = atm_corr_band(
        atm_lut_WVC,
        atm_lut_VIS,
        atm_lut_VZA,
        atm_lut_RAA,
        atm_lut[..., red_band],
        tmp_wvc_image,
        tmp_vis_image,
        vza_image,
        raa_image,
        rdn_image[red_band, :, :] /
        1000,  # divided by 1000 to convert to original rdn, by Ting
        bg_mask)

    nir_refl = atm_corr_band(atm_lut_WVC, atm_lut_VIS, atm_lut_VZA,
                             atm_lut_RAA, atm_lut[...,
                                                  nir_band], tmp_wvc_image,
                             tmp_vis_image, vza_image, raa_image,
                             rdn_image[nir_band, :, :] / 1000, bg_mask)
    ndvi = (nir_refl - red_refl) / (nir_refl + red_refl + 1e-10)
    vis_image = np.zeros((rdn_header['lines'], rdn_header['samples']))

    if if_vnir and if_swir:
        # Decide which SWIR band to use.
        if abs(swir2_wave - 2130) < 20:
            swir_wave = swir2_wave
            swir_band = swir2_band
            swir_refl_upper_bounds = [0.05, 0.10, 0.12]
            red_swir_ratio = 0.50
        else:
            swir_wave = swir1_wave
            swir_band = swir1_band
            swir_refl_upper_bounds = [0.10, 0.15, 0.18]
            red_swir_ratio = 0.25

        # Calculate swir refletance.
        swir_refl = atm_corr_band(
            atm_lut_WVC,
            atm_lut_VIS,
            atm_lut_VZA,
            atm_lut_RAA,
            atm_lut[..., swir_band],
            tmp_wvc_image,
            tmp_vis_image,
            vza_image,
            raa_image,
            rdn_image[swir_band, :, :] /
            1000,  # divided by 1000 to convert to original rdn, by Ting
            bg_mask)

        # Get DDV mask.
        for swir_refl_upper_bound in swir_refl_upper_bounds:
            ddv_mask = (ndvi > 0.10) & (swir_refl < swir_refl_upper_bound) & (
                swir_refl > 0.01)
            percentage = np.sum(ddv_mask[~bg_mask]) / np.sum(~bg_mask)
            if percentage > 0.02:
                logger.info(
                    'The SWIR wavelength %.2f is used for detecting dark dense vegetation.'
                    % swir_wave)
                logger.info('The SWIR reflectance upper boundary is %.2f.' %
                            swir_refl_upper_bound)
                logger.info('The number of DDV pixels is %.2f%%.' %
                            (percentage * 100))
                break
        # Estimate Visibility.
        rows, columns = np.where(ddv_mask)

        # Estimate red reflectance.
        red_refl[rows, columns] = red_swir_ratio * swir_refl[rows, columns]
        del swir_refl
        for row, column in zip(rows, columns):
            interp_rdn = interp_atm_lut(atm_lut_RHO, atm_lut_WVC, atm_lut_VZA,
                                        atm_lut_RAA, atm_lut[..., red_band],
                                        red_refl[row, column],
                                        tmp_wvc_image[row, column],
                                        vza_image[row,
                                                  column], raa_image[row,
                                                                     column])
            vis_image[row, column] = np.interp(
                rdn_image[red_band, row, column] / 1000, interp_rdn[::-1],
                atm_lut_VIS[::-1]
            )  # divided by 1000 to convert to original rdn, by Ting
        rdn_image.flush()
        del rdn_image
        logger.info(
            'Visibility [km] statistics: min=%.2f, max=%.2f, avg=%.2f, sd=%.2f.'
            % (vis_image[ddv_mask].min(), vis_image[ddv_mask].max(),
               vis_image[ddv_mask].mean(), vis_image[ddv_mask].std()))

        # Fill gaps with average values.
        vis_image[~ddv_mask] = vis_image[ddv_mask].mean()
        vis_image[bg_mask] = -1000.0

        # Write the visibility data.
        fid = open(vis_file, 'wb')
        fid.write(vis_image.astype('float32').tostring())
        fid.close()
        del vis_image

        vis_header = empty_envi_header()
        vis_header['description'] = 'Visibility [km]'
        vis_header['samples'] = rdn_header['samples']
        vis_header['lines'] = rdn_header['lines']
        vis_header['bands'] = 1
        vis_header['byte order'] = 0
        vis_header['header offset'] = 0
        vis_header['interleave'] = 'bsq'
        vis_header['data ignore value'] = -1000.0
        vis_header['data type'] = 4
        vis_header['map info'] = rdn_header['map info']
        vis_header['coordinate system string'] = rdn_header[
            'coordinate system string']
        write_envi_header(os.path.splitext(vis_file)[0] + '.hdr', vis_header)
        logger.info('Write the visibility image to %s.' % vis_file)

        # Write the DDV data.
        fid = open(ddv_file, 'wb')
        fid.write(ddv_mask.tostring())
        fid.close()
        del ddv_mask

        ddv_header = empty_envi_header()
        ddv_header[
            'description'] = 'DDV mask (1: Dark dense vegetaion; 0: non-dark dense vegetation)'
        ddv_header['samples'] = rdn_header['samples']
        ddv_header['lines'] = rdn_header['lines']
        ddv_header['bands'] = 1
        ddv_header['byte order'] = 0
        ddv_header['header offset'] = 0
        ddv_header['interleave'] = 'bsq'
        ddv_header['data type'] = 1
        ddv_header['map info'] = rdn_header['map info']
        ddv_header['coordinate system string'] = rdn_header[
            'coordinate system string']
        write_envi_header(os.path.splitext(ddv_file)[0] + '.hdr', ddv_header)

        logger.info('Write the DDV mask to %s.' % ddv_file)
    elif if_vnir:
        pass
    elif if_swir:
        pass

    # Clear data
    del ndvi, red_refl, nir_refl, rdn_header
示例#19
0
def resample_rdn(resampled_rdn_image_file, raw_rdn_image_file,
                 smile_effect_file):
    """ Resample radiance spectra.

    Parameters
    ----------
    resampled_rdn_image_file: str
        Resampled radiance image filename.
    raw_rdn_image_file: str
        Raw radiance image filename.
    smile_effect_file: str
        Smile effect filename.
    """

    if os.path.exists(resampled_rdn_image_file):
        logger.info('Write the spectrally resampled radiance image to %s.' %
                    resampled_rdn_image_file)
        return

    from ENVI import read_envi_header, write_envi_header
    from scipy import interpolate

    # Read raw radiance image
    raw_rdn_header = read_envi_header(
        os.path.splitext(raw_rdn_image_file)[0] + '.hdr')
    raw_rdn_image = np.memmap(raw_rdn_image_file,
                              dtype='float32',
                              mode='r',
                              shape=(raw_rdn_header['lines'],
                                     raw_rdn_header['bands'],
                                     raw_rdn_header['samples']))

    # Read spectral center wavelengths & bandwidths
    smile_effect_header = read_envi_header(
        os.path.splitext(smile_effect_file)[0] + '.hdr')
    smile_effect_data = np.memmap(smile_effect_file,
                                  dtype='float32',
                                  mode='r',
                                  shape=(smile_effect_header['bands'],
                                         smile_effect_header['lines'],
                                         smile_effect_header['samples']))

    # Do spectral interpolation
    info = 'Line (max=%d): ' % smile_effect_header['lines']
    fid = open(resampled_rdn_image_file, 'wb')
    for from_line in range(0, raw_rdn_header['lines'], 500):
        info += '%d, ' % (from_line + 1)
        # Determine chunk size
        to_line = min(from_line + 500, raw_rdn_header['lines'])

        # Initialize
        rdn = np.zeros((to_line - from_line, raw_rdn_header['bands'],
                        raw_rdn_header['samples']
                        ))  # shape=(from_line:to_line, bands, samples)

        # Do spectral interpolation
        for sample in range(raw_rdn_header['samples']):
            # NOTE: No NaN values may be present in the input!
            f = interpolate.interp1d(smile_effect_data[0, :, sample],
                                     raw_rdn_image[from_line:to_line, :,
                                                   sample],
                                     kind='cubic',
                                     fill_value='extrapolate',
                                     axis=1)
            rdn[:, :, sample] = f(raw_rdn_header['wavelength'])
            del f

        # Write interpolated radiance to file
        fid.write(rdn.astype('float32').tostring())
        del rdn, to_line
    fid.close()
    info += '%d, Done!' % smile_effect_header['lines']
    logger.info(info)

    # Clear data
    del smile_effect_data
    raw_rdn_image.flush()
    del raw_rdn_image

    # Write header
    write_envi_header(
        os.path.splitext(resampled_rdn_image_file)[0] + '.hdr', raw_rdn_header)
    del raw_rdn_header

    logger.info('Write the spectrally resampled radiance image to %s.' %
                resampled_rdn_image_file)
示例#20
0
def plot_image_area(image_area_figure_file, dem_image_file, igm_image_file,
                    imugps_file):
    """ Plot image area (DEM is used as the background).
    Arguments:
        image_area_figure_file: str
            Image area figure filename.
        dem_image_file: str
            DEM image filename.
        igm_image_file: str
            IGM image filename.
        imugps_file: str
            IMUGPS filename.
    """

    if os.path.exists(image_area_figure_file):
        logger.info('Save the image-area figure to %s.' %
                    image_area_figure_file)
        return

    from ENVI import read_envi_header

    # Read DEM.
    ds = gdal.Open(dem_image_file, gdal.GA_ReadOnly)
    dem_image = ds.GetRasterBand(1).ReadAsArray()
    dem_geotransform = ds.GetGeoTransform()
    ds = None

    # Read IGM.
    igm_header = read_envi_header(os.path.splitext(igm_image_file)[0] + '.hdr')
    igm_image = np.memmap(igm_image_file,
                          dtype='float64',
                          mode='r',
                          offset=0,
                          shape=(2, igm_header['lines'],
                                 igm_header['samples']))
    cols = (igm_image[0, :, :] - dem_geotransform[0]) / dem_geotransform[1]
    rows = (igm_image[1, :, :] - dem_geotransform[3]) / dem_geotransform[5]
    igm_image.flush()
    del igm_image

    # Read IMUGPS
    imugps = np.loadtxt(imugps_file)

    # Make a plot
    plt.figure(figsize=(10, 10.0 * dem_image.shape[0] / dem_image.shape[1]))
    plt.imshow(dem_image,
               cmap='gray',
               vmin=dem_image.min(),
               vmax=dem_image.max())
    del dem_image
    plt.plot(cols[:, 0],
             rows[:, 0],
             '-',
             color='lime',
             lw=2,
             label='Image Area')
    plt.plot(cols[:, -1], rows[:, -1], '-', color='lime', lw=2)
    plt.plot(cols[0, :], rows[0, :], '-', color='lime', lw=2)
    plt.plot(cols[-1, :], rows[-1, :], '-', color='lime', lw=2)
    cols = (imugps[:, 1] - dem_geotransform[0]) / dem_geotransform[1]
    rows = (imugps[:, 2] - dem_geotransform[3]) / dem_geotransform[5]
    plt.plot(cols, rows, '--', color='red', lw=2, label='Flight')
    plt.scatter(cols[0], rows[0], c='navy', s=20, label='Start')
    plt.scatter(cols[-1], rows[-1], c='orange', s=20, label='End')
    plt.xticks([])
    plt.yticks([])
    plt.legend(fontsize=5)
    plt.savefig(image_area_figure_file, bbox_inches='tight')
    plt.close()
    del cols, rows, imugps

    logger.info('Save the image-area figure to %s.' % image_area_figure_file)
示例#21
0
def hyspex_extract(src_img, bands, out_img):
    """
    extract certain bands from hyspex images (note: images are in BSQ)
    :param src_img:
    :param out_img:
    :param bands: list; bands to be extracted, starting from 0
    :return:
    """

    src_header = read_envi_header(src_img + '.hdr')
    # src_image = np.memmap(src_img,
    #                       dtype='int16',
    #                       mode='r',
    #                       offset=src_header['header offset'],
    #                       shape=(src_header['lines'],
    #                              src_header['samples'],
    #                              src_header['bands'],))

    # Create empty array for the output image:
    # out = np.memmap(out_img, dtype='int16',
    #                 mode='w+',
    #                 shape=(src_header['lines'],
    #                        src_header['samples'],
    #                        len(bands))
    #                 )
    # Read the given bands:
    i = 0
    with open(out_img, 'wb') as outfile:
        for b in bands:
            print('Now reading band {}'.format(b))
            src_image = np.memmap(
                src_img,
                dtype='int16',
                mode='r',
                shape=(src_header['lines'], src_header['samples']),
                offset=int(src_header['lines'] * src_header['samples'] * b *
                           16 / 8))

            # # Create empty array for the output image:
            # out = np.memmap(out_img, dtype='int16',
            #                 mode='w+',
            #                 shape=(src_header['lines'],
            #                        src_header['samples']),
            #                 offset=int(src_header['lines'] * src_header['samples'] * i * 16 / 8))

            # out[:, :, i] = src_image[:]
            outfile.write(src_image[:])
            del src_image

        i = i + 1
    outfile.close()
    # flush memory:
    # out.flush()

    # src_image.flush()

    # Modify the header for out_image:
    tgt_header = src_header
    # number of bands:
    tgt_header['bands'] = len(bands)
    # wavelength:
    wvl = [src_header['wavelength'][b] for b in bands]
    tgt_header['wavelength'] = wvl

    # fwhm:
    fwhm = [src_header['fwhm'][b] for b in bands]
    tgt_header['fwhm'] = fwhm
    write_envi_header(out_img + '.hdr', tgt_header)
示例#22
0
def average_rdn(avg_rdn_file, rdn_image_file, sca_image_file,
                pre_class_image_file):
    """ Average radiance along each column.

    Parameters
    ----------
    avg_rdn_file: str
        Average radiance data filename.
    rdn_image_file: 3D array
        Radiance image filename, in BIL format.
    sca_image_file: 3D array
        Scan angle image filename, in BSQ format.
    pre_class_image_file: str
        Pre-classification image filename.
    """

    if os.path.exists(avg_rdn_file):
        logger.info('Write the averaged radiance data to %s.' % avg_rdn_file)
        return

    from ENVI import empty_envi_header, read_envi_header, write_envi_header

    # Read radiance image data
    rdn_header = read_envi_header(os.path.splitext(rdn_image_file)[0] + '.hdr')
    rdn_image = np.memmap(rdn_image_file,
                          dtype='float32',
                          mode='r',
                          shape=(rdn_header['lines'], rdn_header['bands'],
                                 rdn_header['samples']))

    # Read classification map
    pre_class_header = read_envi_header(
        os.path.splitext(pre_class_image_file)[0] + '.hdr')
    pre_class_image = np.memmap(pre_class_image_file,
                                dtype='uint8',
                                mode='r',
                                shape=(pre_class_header['lines'],
                                       pre_class_header['samples']))
    mask = pre_class_image == 5  # 5: land (clear)
    pre_class_image.flush()
    del pre_class_header, pre_class_image

    # Average radiance along each column
    fid = open(avg_rdn_file, 'wb')
    info = 'Band (max=%d): ' % rdn_header['bands']
    for band in range(rdn_header['bands']):
        if band % 20 == 0:
            info += '%d, ' % (band + 1)

        # Build temporary mask
        tmp_mask = mask & (rdn_image[:, band, :] > 0.0)

        # Average radiance
        rdn = np.ma.array(rdn_image[:, band, :],
                          mask=~tmp_mask).mean(axis=0)  # shape = (samples,1)

        # Interpolate bad radiance values
        for bad_sample in np.where(rdn.mask)[0]:
            if bad_sample == 0:
                rdn[bad_sample] = rdn[bad_sample + 1]
            elif bad_sample == rdn_header['samples'] - 1:
                rdn[bad_sample] = rdn[bad_sample - 1]
            else:
                rdn[bad_sample] = (rdn[bad_sample - 1] +
                                   rdn[bad_sample + 1]) / 2.0

        # Write average radiance data to file
        fid.write(rdn.data.astype('float32'))

        # Clear data
        del tmp_mask, rdn
    fid.close()
    info += str(rdn_header['bands'])
    logger.info(info)
    rdn_image.flush()
    del rdn_image

    # Read scan angles
    sca_header = read_envi_header(os.path.splitext(sca_image_file)[0] + '.hdr')
    sca_image = np.memmap(sca_image_file,
                          dtype='float32',
                          mode='r',
                          shape=(sca_header['bands'], sca_header['lines'],
                                 sca_header['samples']))
    saa = float(sca_header['sun azimuth'])

    # Average scan angles
    avg_vza = np.ma.array(sca_image[0, :, :], mask=~mask).mean(axis=0)
    avg_vaa = np.ma.array(sca_image[1, :, :], mask=~mask).mean(axis=0)
    avg_raa = saa - avg_vaa
    avg_raa[avg_raa < 0] += 360.0
    avg_raa[avg_raa > 180] = 360.0 - avg_raa[avg_raa > 180]
    sca_image.flush()
    del sca_image

    # Write header
    avg_rdn_header = empty_envi_header()
    avg_rdn_header['description'] = 'Averaged radiance in [mW/(cm2*um*sr)]'
    avg_rdn_header['samples'] = rdn_header['samples']
    avg_rdn_header['lines'] = rdn_header['bands']
    avg_rdn_header['bands'] = 1
    avg_rdn_header['byte order'] = 0
    avg_rdn_header['header offset'] = 0
    avg_rdn_header['interleave'] = 'bsq'
    avg_rdn_header['data type'] = 4
    avg_rdn_header['waves'] = rdn_header['wavelength']
    avg_rdn_header['fwhms'] = rdn_header['fwhm']
    avg_rdn_header['VZA'] = list(avg_vza)
    avg_rdn_header['RAA'] = list(avg_raa)
    write_envi_header(avg_rdn_file + '.hdr', avg_rdn_header)

    logger.info('Write the averaged radiance data to %s.' % avg_rdn_file)
示例#23
0
def build_glt(glt_image_file, igm_image_file, pixel_size, map_crs):
    """ Create a geographic lookup table (GLT) image.
    Notes:
        (1) This code is adapted from Adam Chlus's ([email protected]) script.
        (2) The GLT image consists of two bands:
                Band 0: Sample Lookup:
                    Pixel values indicate the column number of the pixel
                    in the input geometry file that belongs at the given Y
                    location in the output image.
                Band 1: Line Lookup:
                    Pixel values indicate the row number of the pixel
                    in the input geometry file that belongs at the given X
                    location in the output image.
        (3) For more details about GLT, refer to https://www.harrisgeospatial.com/
            docs/GeoreferenceFromInputGeometry.html.
    Arguments:
        glt_image_file: str
            Geographic look-up table filename.
        igm_image_file: str
            Input geometry filename.
        pixel_size: float
            Output image pixel size.
        map_crs: osr object
            GLT image map coordinate system.
    """

    if os.path.exists(glt_image_file):
        logger.info('Write the GLT to %s.' % glt_image_file)
        return

    from ENVI import empty_envi_header, read_envi_header, write_envi_header

    # Read IGM.
    igm_header = read_envi_header(os.path.splitext(igm_image_file)[0] + '.hdr')
    igm_image = np.memmap(igm_image_file,
                          dtype='float64',
                          mode='r',
                          offset=0,
                          shape=(igm_header['bands'], igm_header['lines'],
                                 igm_header['samples']))

    # Estimate output spatial extent.
    X_Min = igm_image[0, :, :].min()
    X_Max = igm_image[0, :, :].max()
    Y_Min = igm_image[1, :, :].min()
    Y_Max = igm_image[1, :, :].max()
    X_Min = np.floor(X_Min / pixel_size) * pixel_size - pixel_size
    X_Max = np.ceil(X_Max / pixel_size) * pixel_size + pixel_size
    Y_Min = np.floor(Y_Min / pixel_size) * pixel_size - pixel_size
    Y_Max = np.ceil(Y_Max / pixel_size) * pixel_size + pixel_size
    igm_image.flush()
    del igm_image

    # Build VRT for IGM.
    igm_vrt_file = os.path.splitext(igm_image_file)[0] + '.vrt'
    igm_vrt = open(igm_vrt_file, 'w')
    igm_vrt.write('<VRTDataset rasterxsize="%s" rasterysize="%s">\n' %
                  (igm_header['samples'], igm_header['lines']))
    for band in range(igm_header['bands']):
        igm_vrt.write('\t<VRTRasterBand dataType="%s" band="%s">\n' %
                      ("Float64", band + 1))
        igm_vrt.write('\t\t<SimpleSource>\n')
        igm_vrt.write(
            '\t\t\t<SourceFilename relativeToVRT="1">%s</SourceFilename>\n' %
            igm_image_file.replace('&', '&amp;'))
        igm_vrt.write('\t\t\t<SourceBand>%s</SourceBand>\n' % (band + 1))
        igm_vrt.write(
            '\t\t\t<SourceProperties RasterXSize="%s" RasterYSize="%s" DataType="%s" BlockXSize="%s" BlockYSize="%s" />\n'
            % (igm_header['samples'], igm_header['lines'], "Float64",
               igm_header['samples'], 1))
        igm_vrt.write(
            '\t\t\t<SrcRect xOff="0" yOff="0" xSize="%s" ySize="%s" />\n' %
            (igm_header['samples'], igm_header['lines']))
        igm_vrt.write(
            '\t\t\t<DstRect xOff="0" yOff="0" xSize="%s" ySize="%s" />\n' %
            (igm_header['samples'], igm_header['lines']))
        igm_vrt.write("\t\t</SimpleSource>\n")
        igm_vrt.write("\t</VRTRasterBand>\n")
    igm_vrt.write("</VRTDataset>\n")
    igm_vrt.close()

    # Make IGM index image.
    index_image_file = igm_image_file + '_Index'
    index_image = np.memmap(index_image_file,
                            dtype='int32',
                            mode='w+',
                            offset=0,
                            shape=(2, igm_header['lines'],
                                   igm_header['samples']))
    index_image[0, :, :], index_image[1, :, :] = np.mgrid[
        0:igm_header['lines'], 0:igm_header['samples']]
    index_image.flush()
    del index_image

    index_header = empty_envi_header()
    index_header['description'] = 'IGM Image Index'
    index_header['samples'] = igm_header['samples']
    index_header['lines'] = igm_header['lines']
    index_header['bands'] = 2
    index_header['byte order'] = 0
    index_header['header offset'] = 0
    index_header['interleave'] = 'bsq'
    index_header['data type'] = 3
    index_header['band names'] = ['Image Row', 'Image Column']
    index_header_file = os.path.splitext(index_image_file)[0] + '.hdr'
    write_envi_header(index_header_file, index_header)

    # Build VRT for IGM Index.
    index_vrt_file = os.path.splitext(index_image_file)[0] + '.vrt'
    index_vrt = open(index_vrt_file, 'w')
    index_vrt.write('<VRTDataset rasterxsize="%s" rasterysize="%s">\n' %
                    (index_header['samples'], index_header['lines']))
    index_vrt.write('\t<Metadata Domain = "GEOLOCATION">\n')
    index_vrt.write('\t\t<mdi key="X_DATASET">%s</mdi>\n' %
                    igm_image_file.replace('&', '&amp;'))
    index_vrt.write('\t\t<mdi key="X_BAND">1</mdi>\n')
    index_vrt.write('\t\t<mdi key="Y_DATASET">%s</mdi>\n' %
                    igm_image_file.replace('&', '&amp;'))
    index_vrt.write('\t\t<mdi key="Y_BAND">2</mdi>\n')
    index_vrt.write('\t\t<mdi key="PIXEL_OFFSET">0</mdi>\n')
    index_vrt.write('\t\t<mdi key="LINE_OFFSET">0</mdi>\n')
    index_vrt.write('\t\t<mdi key="PIXEL_STEP">1</mdi>\n')
    index_vrt.write('\t\t<mdi key="LINE_STEP">1</mdi>\n')
    index_vrt.write('\t</Metadata>\n')
    for band in range(index_header['bands']):
        index_vrt.write('\t<VRTRasterBand dataType="%s" band="%s">\n' %
                        ("Int16", band + 1))
        index_vrt.write('\t\t<SimpleSource>\n')
        index_vrt.write(
            '\t\t\t<SourceFilename relativeToVRT="1">%s</SourceFilename>\n' %
            index_image_file.replace('&', '&amp;'))
        index_vrt.write('\t\t\t<SourceBand>%s</SourceBand>\n' % (band + 1))
        index_vrt.write(
            '\t\t\t<SourceProperties RasterXSize="%s" RasterYSize="%s" DataType="%s" BlockXSize="%s" BlockYSize="%s" />\n'
            % (index_header['samples'], index_header['lines'], "Int32",
               index_header['samples'], 1))
        index_vrt.write(
            '\t\t\t<SrcRect xOff="0" yOff="0" xSize="%s" ySize="%s" />\n' %
            (index_header['samples'], index_header['lines']))
        index_vrt.write(
            '\t\t\t<DstRect xOff="0" yOff="0" xSize="%s" ySize="%s" />\n' %
            (index_header['samples'], index_header['lines']))
        index_vrt.write("\t\t</SimpleSource>\n")
        index_vrt.write("\t</VRTRasterBand>\n")
    index_vrt.write("</VRTDataset>\n")
    index_vrt.close()

    # Build GLT.
    tmp_glt_image_file = glt_image_file + '.tif'
    tmp_glt_image = gdal.Warp(tmp_glt_image_file,
                              index_vrt_file,
                              outputBounds=(X_Min, Y_Min, X_Max, Y_Max),
                              xRes=pixel_size,
                              yRes=pixel_size,
                              resampleAlg='near',
                              dstSRS=map_crs.ExportToProj4(),
                              dstNodata=-1,
                              multithread=True,
                              geoloc=True)
    del tmp_glt_image

    # Convert the .tif file to ENVI format.
    ds = gdal.Open(tmp_glt_image_file, gdal.GA_ReadOnly)
    lines = ds.RasterYSize
    samples = ds.RasterXSize
    glt_image = np.memmap(glt_image_file,
                          dtype='int32',
                          mode='w+',
                          offset=0,
                          shape=(2, lines, samples))
    for band in range(ds.RasterCount):
        glt_image[band, :, :] = ds.GetRasterBand(band + 1).ReadAsArray()
    glt_image.flush()
    del glt_image
    ds = None

    # Write GLT header.
    glt_header = empty_envi_header()
    glt_header['description'] = 'GLT'
    glt_header['file type'] = 'ENVI Standard'
    glt_header['samples'] = samples
    glt_header['lines'] = lines
    glt_header['bands'] = 2
    glt_header['byte order'] = 0
    glt_header['header offset'] = 0
    glt_header['interleave'] = 'bsq'
    glt_header['data type'] = 3
    glt_header['data ignore value'] = -1
    glt_header['band names'] = ['Image Row', 'Image Column']
    glt_header['coordinate system string'] = map_crs.ExportToWkt()
    glt_header['map info'] = [
        map_crs.GetAttrValue('projcs').replace(',', ''), 1, 1, X_Min, Y_Max,
        pixel_size, pixel_size, ' ', ' ',
        map_crs.GetAttrValue('datum'),
        map_crs.GetAttrValue('unit')
    ]
    if map_crs.GetAttrValue('PROJECTION').lower() == 'transverse_mercator':
        glt_header['map info'][7] = map_crs.GetUTMZone()
        if Y_Max > 0.0:
            glt_header['map info'][8] = 'North'
        else:
            glt_header['map info'][8] = 'South'
    glt_header_file = os.path.splitext(glt_image_file)[0] + '.hdr'
    write_envi_header(glt_header_file, glt_header)

    # Remove temporary files.
    os.remove(index_image_file)
    os.remove(index_vrt_file)
    os.remove(index_header_file)
    os.remove(igm_vrt_file)
    os.remove(tmp_glt_image_file)

    logger.info('Write the GLT to %s.' % glt_image_file)
示例#24
0
def merge_dem_sca(background_mask_file, merged_dem_file, merged_sca_file,
                  sensors):
    """ Merge DEM and SCA images.
    Arguments:
        background_mask_file: str
            Background mask filename.
        merged_dem_file: str
            Merged DEM file.
        merged_sca_file: str
            Merged SCA file.
        sensors: dict
            Sensor dictionaries.
    """

    if os.path.exists(background_mask_file) and os.path.exists(
            merged_dem_file) and os.path.exists(merged_sca_file):
        logger.info('Write the background mask to %s.' % background_mask_file)
        logger.info('Write the merged DEM image to %s.' % merged_dem_file)
        logger.info('Write the merged SCA image to %s.' % merged_sca_file)
        return

    from ENVI import empty_envi_header, read_envi_header, write_envi_header
    """
        Get spatial extent and pixel size.
    """
    ulx, uly, lrx, lry, pixel_size = -np.inf, np.inf, np.inf, -np.inf, np.inf
    for sensor_index, sensor_dict in sensors.items():
        tmp_header = read_envi_header(
            os.path.splitext(sensor_dict['ortho_dem_image_file'])[0] + '.hdr')
        tmp_ulx, tmp_uly = float(tmp_header['map info'][3]), float(
            tmp_header['map info'][4])
        tmp_pixel_size = float(tmp_header['map info'][5])
        tmp_lrx, tmp_lry = tmp_ulx + tmp_header[
            'samples'] * tmp_pixel_size, tmp_uly - tmp_header[
                'lines'] * tmp_pixel_size
        if tmp_ulx > ulx:
            ulx = tmp_ulx
        if tmp_uly < uly:
            uly = tmp_uly
        if tmp_lrx < lrx:
            lrx = tmp_lrx
        if tmp_lry > lry:
            lry = tmp_lry
        if tmp_pixel_size < pixel_size:
            pixel_size = tmp_pixel_size
        del tmp_ulx, tmp_uly, tmp_pixel_size, tmp_lrx, tmp_lry

    # Process at 2x the smallest pixel size
    pixel_size = 2 * pixel_size

    # Find lower right corners of first CRS-aligned pixels at the
    #  upper left & lower right corners which are fully in the image
    ulx, uly = np.ceil(ulx / pixel_size +
                       1) * pixel_size, np.floor(uly / pixel_size -
                                                 1) * pixel_size
    lrx, lry = np.floor(lrx / pixel_size -
                        1) * pixel_size, np.ceil(lry / pixel_size +
                                                 1) * pixel_size
    map_info = [
        tmp_header['map info'][0], 1, 1, ulx, uly, pixel_size, pixel_size
    ] + tmp_header['map info'][7:]
    crs = tmp_header['coordinate system string']

    del tmp_header

    logger.info('The spatial range and pixel size of merged images:')
    logger.info('Map x = %.2f - %.2f' % (ulx, lrx))
    logger.info('Map y = %.2f - %.2f' % (lry, uly))
    logger.info('Pixel size = %.2f' % pixel_size)
    """
        Determine regular map grids.
    """
    x, y = np.meshgrid(np.arange(ulx, lrx, pixel_size),
                       np.arange(uly, lry, -pixel_size))
    mask = np.full(x.shape, True, dtype='bool')
    """
        Build a background mask.
    """
    # Use DEM and SCA images to build this mask.
    for sensor_index, sensor_dict in sensors.items():
        # Use dem.
        tmp_header = read_envi_header(
            os.path.splitext(sensor_dict['ortho_dem_image_file'])[0] + '.hdr')
        tmp_image = np.memmap(sensor_dict['ortho_dem_image_file'],
                              dtype='float32',
                              mode='r',
                              shape=(tmp_header['lines'],
                                     tmp_header['samples']))
        tmp_ulx, tmp_uly = float(tmp_header['map info'][3]), float(
            tmp_header['map info'][4])
        tmp_pixel_size = float(tmp_header['map info'][5])
        resampled_image = resample_ortho_dem(np.copy(tmp_image), tmp_ulx,
                                             tmp_uly, tmp_pixel_size, x, y)
        mask = mask & (resampled_image > 0.0)

        # Use sca.
        tmp_header = read_envi_header(
            os.path.splitext(sensor_dict['ortho_sca_image_file'])[0] + '.hdr')
        tmp_image = np.memmap(sensor_dict['ortho_sca_image_file'],
                              dtype='float32',
                              mode='r',
                              shape=(tmp_header['bands'], tmp_header['lines'],
                                     tmp_header['samples']))
        tmp_ulx, tmp_uly = float(tmp_header['map info'][3]), float(
            tmp_header['map info'][4])
        tmp_pixel_size = float(tmp_header['map info'][5])
        resampled_image = resample_ortho_sca(np.copy(tmp_image[0, :, :]),
                                             tmp_ulx, tmp_uly, tmp_pixel_size,
                                             x, y)
        mask = mask & (resampled_image > 0.0)

        # Clear data.
        del tmp_header, tmp_ulx, tmp_uly, tmp_pixel_size, resampled_image
        tmp_image.flush()
        del tmp_image

    mask = ~mask  # 1: background pixels; 0: non-background pixels.

    # Write the mask to a file.
    fid = open(background_mask_file, 'wb')
    fid.write(mask.tostring())
    fid.close()

    # Write the mask header to a file.
    header = empty_envi_header()
    header[
        'description'] = 'Background mask (0: non-background; 1: background)'
    header['file type'] = 'ENVI Standard'
    header['samples'] = mask.shape[1]
    header['lines'] = mask.shape[0]
    header['bands'] = 1
    header['byte order'] = 0
    header['header offset'] = 0
    header['interleave'] = 'bsq'
    header['data type'] = 1
    header['map info'] = map_info
    header['coordinate system string'] = crs
    write_envi_header(
        os.path.splitext(background_mask_file)[0] + '.hdr', header)
    del header

    logger.info('Write the background mask to %s.' % background_mask_file)
    """
        Merge DEM.
    """
    # Read the first DEM.
    sensor_index = list(sensors.keys())[0]
    sensor_dict = sensors[sensor_index]
    raw_header = read_envi_header(
        os.path.splitext(sensor_dict['ortho_dem_image_file'])[0] + '.hdr')
    raw_image = np.memmap(sensor_dict['ortho_dem_image_file'],
                          dtype='float32',
                          mode='r',
                          shape=(raw_header['lines'], raw_header['samples']))
    resampled_image = resample_ortho_dem(np.copy(raw_image),
                                         float(raw_header['map info'][3]),
                                         float(raw_header['map info'][4]),
                                         float(raw_header['map info'][5]), x,
                                         y)
    resampled_image[mask] = -1000.0

    # Write the merged DEM to a file.
    fid = open(merged_dem_file, 'wb')
    fid.write(resampled_image.astype('float32').tostring())
    fid.close()
    del raw_header, sensor_index, sensor_dict, resampled_image
    raw_image.flush()
    del raw_image

    # Write the merged DEM header to a file.
    header = empty_envi_header()
    header['description'] = 'Merged DEM, in [m]'
    header['file type'] = 'ENVI Standard'
    header['samples'] = mask.shape[1]
    header['lines'] = mask.shape[0]
    header['bands'] = 1
    header['byte order'] = 0
    header['header offset'] = 0
    header['interleave'] = 'bsq'
    header['data type'] = 4
    header['data ignore value'] = -1000.0
    header['map info'] = map_info
    header['coordinate system string'] = crs
    write_envi_header(os.path.splitext(merged_dem_file)[0] + '.hdr', header)
    del header

    logger.info('Write the merged DEM image to %s.' % merged_dem_file)
    """
        Merge SCA.
    """
    # Read the first SCA.
    sensor_index = list(sensors.keys())[0]
    sensor_dict = sensors[sensor_index]
    raw_header = read_envi_header(
        os.path.splitext(sensor_dict['ortho_sca_image_file'])[0] + '.hdr')
    raw_image = np.memmap(sensor_dict['ortho_sca_image_file'],
                          dtype='float32',
                          mode='r',
                          shape=(raw_header['bands'], raw_header['lines'],
                                 raw_header['samples']))

    # Write data.
    fid = open(merged_sca_file, 'wb')
    for band in range(raw_header['bands']):
        resampled_image = resample_ortho_sca(np.copy(raw_image[band, :, :]),
                                             float(raw_header['map info'][3]),
                                             float(raw_header['map info'][4]),
                                             float(raw_header['map info'][5]),
                                             x, y)
        resampled_image[mask] = -1000.0
        fid.write(resampled_image.astype('float32').tostring())
        del resampled_image
    fid.close()
    del sensor_index, sensor_dict
    raw_image.flush()
    del raw_image

    # Write header.
    header = empty_envi_header()
    header['description'] = 'Merged SCA, in [deg]'
    header['file type'] = 'ENVI Standard'
    header['samples'] = mask.shape[1]
    header['lines'] = mask.shape[0]
    header['bands'] = 2
    header['byte order'] = 0
    header['header offset'] = 0
    header['interleave'] = 'bsq'
    header['data type'] = 4
    header['band names'] = ['Sensor Zenith [deg]', 'Sensor Azimuth [deg]']
    header['sun azimuth'] = raw_header['sun azimuth']
    header['sun zenith'] = raw_header['sun zenith']
    header['data ignore value'] = -1000.0
    header['map info'] = map_info
    header['coordinate system string'] = crs
    write_envi_header(os.path.splitext(merged_sca_file)[0] + '.hdr', header)
    del raw_header, header
    logger.info('Write the merged SCA image to %s.' % merged_sca_file)
示例#25
0
def plot_smile_effect(smile_effect_at_atm_features_figure_file,
                      smile_effect_at_atm_features_file):
    """ Plot smile effects at different atmosphere absorption features.
    Arguments:
        smile_effect_figure_file: str
            Smile effect figure filename.
        smile_effect_at_atm_features_file: str
            Smile effect at atm features filename.
    """

    if os.path.exists(smile_effect_at_atm_features_figure_file):
        logger.info(
            'Save the smile effect at atmosphere absorption features figure to %s.'
            % smile_effect_at_atm_features_figure_file)
        return

    from ENVI import read_envi_header

    header = read_envi_header(
        os.path.splitext(smile_effect_at_atm_features_file)[0] + '.hdr')
    center_waves = [
        float(v) for v in header['spectral center wavelengths'].split(',')
    ]
    fwhms = [float(v) for v in header['spectral bandwiths'].split(',')]
    shifts = np.memmap(smile_effect_at_atm_features_file,
                       dtype='float32',
                       mode='r',
                       shape=(header['bands'], header['lines'],
                              header['samples']))
    sample_indices = np.arange(header['samples'])
    x_lim = [1, header['samples']]
    if x_lim[1] - x_lim[0] > 1000:
        x_ticks = np.arange(x_lim[0], x_lim[1] + 200, 200)
    else:
        x_ticks = np.arange(x_lim[0], x_lim[1] + 50, 50)
    fig, axs = plt.subplots(nrows=3, ncols=3, figsize=(20, 20))
    for index, ax in enumerate(axs.flatten()):
        if index >= header['lines']:
            ax.axis('off')
            continue
        # Title
        ax.set_title(r'$\lambda$=%.2fnm, FWHM=%.2fnm' %
                     (center_waves[index], fwhms[index]),
                     fontsize=10)
        # Left y-axis
        y = np.copy(shifts[0, index, :])
        i = np.abs(y - y.mean()) > 3 * y.std()
        y[i] = np.nan
        ax.plot(sample_indices,
                y,
                '-',
                color='blue',
                alpha=0.3,
                lw=1,
                label=r'$\Delta\lambda$')
        p = np.poly1d(np.polyfit(sample_indices[~i], y[~i], 6))
        ax.plot(sample_indices, p(sample_indices), '-', color='blue', lw=4)
        y_lim = ax.get_ylim()
        y_lim = [np.floor(y_lim[0] / 0.5) * 0.5, np.ceil(y_lim[1] / 0.5) * 0.5]
        if y_lim[1] - y_lim[0] <= 1.0:
            y_ticks = np.arange(y_lim[0], y_lim[1] + 0.25, 0.25)
        elif y_lim[1] - y_lim[0] <= 3.0:
            y_ticks = np.arange(y_lim[0], y_lim[1] + 0.50, 0.50)
        else:
            y_ticks = np.arange(y_lim[0], y_lim[1] + 1.00, 1.00)
        y_lim = [y_ticks[0].min(), y_ticks[-1].max()]
        ax.set_yticks(y_ticks)
        y_ticklabels = ['%.2f' % v for v in y_ticks]
        ax.set_yticklabels(y_ticklabels, fontsize=10, color='blue')
        ax.set_ylim(y_lim)
        ax.set_xlim(x_lim)
        # Right y-axis
        twin_ax = ax.twinx()
        y = np.copy(shifts[1, index, :])
        i = np.abs(y - y.mean()) > 3 * y.std()
        y[i] = np.nan
        twin_ax.plot(sample_indices,
                     y,
                     '-',
                     color='red',
                     alpha=0.3,
                     lw=1,
                     label=r'$\Delta$FWHM')
        p = np.poly1d(np.polyfit(sample_indices[~i], y[~i], 6))
        twin_ax.plot(sample_indices, p(sample_indices), '-', color='red', lw=4)
        y_lim = twin_ax.get_ylim()
        y_lim = [np.floor(y_lim[0] / 0.5) * 0.5, np.ceil(y_lim[1] / 0.5) * 0.5]
        if y_lim[1] - y_lim[0] <= 1.0:
            y_ticks = np.arange(y_lim[0], y_lim[1] + 0.01, 0.25)
        elif y_lim[1] - y_lim[0] <= 3.0:
            y_ticks = np.arange(y_lim[0], y_lim[1] + 0.50, 0.50)
        else:
            y_ticks = np.arange(y_lim[0], y_lim[1] + 1.00, 1.00)
        y_lim = [y_ticks[0].min(), y_ticks[-1].max()]
        twin_ax.set_yticks(y_ticks)
        y_ticklabels = ['%.2f' % v for v in y_ticks]
        twin_ax.set_yticklabels(y_ticklabels, fontsize=10, color='red')
        twin_ax.set_xlim(x_lim)
        twin_ax.set_ylim(y_lim)
        twin_ax.set_xticks(x_ticks)
        twin_ax.set_xticklabels(x_ticks, fontsize=10)
        # X-axis
        ax.set_xticks(x_ticks)
        ax.set_xticklabels(x_ticks, fontsize=10)
        ax.set_xlabel('Across-track Pixel', fontsize=10)
        if index == 0:
            lines, labels = ax.get_legend_handles_labels()
            lines2, labels2 = twin_ax.get_legend_handles_labels()
            ax.legend(lines + lines2, labels + labels2)
    fig.savefig(smile_effect_at_atm_features_figure_file,
                dpi=600,
                bbox_inches='tight')
    plt.close()
    del fig, ax, axs, header
    shifts.flush()
    del shifts

    logger.info(
        'Save smile effect at atmosphere absorption features figure to %s.' %
        smile_effect_at_atm_features_figure_file)
示例#26
0
def estimate_wvc(wvc_file, rdn_file, sensors, sun_zenith, distance,
                 background_mask_file):
    """ Estimate water vapor column.

    Parameters
    ----------
    wvc_file: str
        Water vapor column image filename.
    rdn_file: str
        Radiance image filename.
    sensors: dict
         Sensors.
    mask_file: str
        Mask image filename.
    sun_zenith: float
        Sun zenith angle.
    distance: float
        Earth-to-sun distance.
    """

    if os.path.exists(wvc_file):
        logger.info('Save the WVC image to %s.' % (wvc_file))
        return

    from ENVI import read_envi_header, empty_envi_header, write_envi_header
    from Spectra import get_closest_wave, resample_solar_flux
    import json

    # Read radiance image
    rdn_header = read_envi_header(rdn_file + '.hdr')
    rdn_image = np.memmap(rdn_file,
                          dtype='float32',
                          mode='r',
                          shape=(rdn_header['bands'], rdn_header['lines'],
                                 rdn_header['samples']))
    # Read boundary mask
    bg_mask_header = read_envi_header(background_mask_file + '.hdr')
    bg_mask_image = np.memmap(background_mask_file,
                              mode='r',
                              dtype='bool',
                              shape=(bg_mask_header['lines'],
                                     bg_mask_header['samples']))

    # Mask out dark pixels
    good_mask_image = np.full((rdn_header['lines'], rdn_header['samples']),
                              True)
    solar_flux = resample_solar_flux(solar_flux_file, rdn_header['wavelength'],
                                     rdn_header['fwhm'])
    cos_sun_zenith = np.cos(np.deg2rad(sun_zenith))
    d2 = distance**2
    # If reflectance at 850 nm is less than 0.01, mask out these pixels
    wave, band = get_closest_wave(rdn_header['wavelength'], 470)
    if abs(wave - 470) < 20:
        refl = rdn_image[band, :, :] * np.pi * d2 / (solar_flux[band] *
                                                     cos_sun_zenith)
        good_mask_image &= (refl > 0.01)
        del refl
    # If reflectance at 850 nm is less than 0.10, mask out these pixels
    wave, band = get_closest_wave(rdn_header['wavelength'], 850)
    if abs(wave - 850) < 20:
        refl = rdn_image[band, :, :] * np.pi * d2 / (solar_flux[band] *
                                                     cos_sun_zenith)
        good_mask_image &= (refl > 0.10)
        del refl
    # If reflectance at 1600 nm is less than 0.10, mask out these pixels
    wave, band = get_closest_wave(rdn_header['wavelength'], 1600)
    if abs(wave - 1600) < 20:
        refl = rdn_image[band, :, :] * np.pi * d2 / (solar_flux[band] *
                                                     cos_sun_zenith)
        good_mask_image &= (refl > 0.10)
        del refl
    del wave, band, solar_flux, cos_sun_zenith, d2

    # Estimate water vapor columns
    wvc_image = np.zeros((rdn_header['lines'], rdn_header['samples']))
    for sensor_index, sensor_dict in sensors.items():
        # Read water vapor column models
        wvc_model_file = sensor_dict['wvc_model_file']
        wvc_models = json.load(open(wvc_model_file, 'r'))

        # Estimate WVC
        for wvc_model in wvc_models.values():
            # Find band indices
            bands = []
            for wave in wvc_model['wavelength']:
                _, band = get_closest_wave(rdn_header['wavelength'], wave)
                bands.append(band)
                del band

            # Calculate ratios
            ratio_image = rdn_image[bands[1], :, :] / (
                1e-10 + rdn_image[bands[0], :, :] * wvc_model['weight'][0] +
                rdn_image[bands[2], :, :] * wvc_model['weight'][1])

            # Calculate water vapor columns
            wvc_image[good_mask_image] += np.interp(
                ratio_image[good_mask_image], wvc_model['ratio'],
                wvc_model['wvc']).astype('float32')

        # Clear data
        del ratio_image, bands, wvc_model
    rdn_image.flush()
    del rdn_image

    # Average
    wvc_image /= len(sensors)

    # Read solar flux
    wvc_image[~good_mask_image] = wvc_image[good_mask_image].mean()
    logger.info(
        'WVC [mm] statistics: min=%.2f, max=%.2f, avg=%.2f, sd=%.2f.' %
        (wvc_image[good_mask_image].min(), wvc_image[good_mask_image].max(),
         wvc_image[good_mask_image].mean(), wvc_image[good_mask_image].std()))
    wvc_image[bg_mask_image] = -1000.0
    del bg_mask_image, good_mask_image

    # Save water vapor column
    fid = open(wvc_file, 'wb')
    fid.write(wvc_image.astype('float32').tostring())
    fid.close()

    # Write header
    wvc_header = empty_envi_header()
    wvc_header['description'] = 'Water vapor column [mm]'
    wvc_header['file type'] = 'ENVI Standard'
    wvc_header['bands'] = 1
    wvc_header['lines'] = rdn_header['lines']
    wvc_header['samples'] = rdn_header['samples']
    wvc_header['file type'] = 'ENVI Standard'
    wvc_header['interleave'] = 'bsq'
    wvc_header['byte order'] = 0
    wvc_header['data type'] = 4
    wvc_header['data ignore value'] = -1000.0
    wvc_header['map info'] = rdn_header['map info']
    wvc_header['coordinate system string'] = rdn_header[
        'coordinate system string']
    write_envi_header(wvc_file + '.hdr', wvc_header)
    del wvc_header, rdn_header

    logger.info('Save the WVC image to %s.' % (wvc_file))
示例#27
0
def atm_corr_image(flight_dict):
    """ Do atmospheric corrections on the whole image.
    Arguments:
        flight_dict: dict
            Flight dictionary.
    """
    if os.path.exists(flight_dict['refl_file']):
        logger.info('Write the reflectance image to %s.' %
                    flight_dict['refl_file'])
        return

    from ENVI import read_envi_header, write_envi_header
    from AtmLUT import read_binary_metadata

    # Read radiance image.
    rdn_header = read_envi_header(
        os.path.splitext(flight_dict['merged_rdn_file'])[0] + '.hdr')
    #    rdn_image = np.memmap(flight_dict['merged_rdn_file'],
    #                          dtype='float32',
    #                          mode='r',
    #                          shape=(rdn_header['bands'],
    #                                 rdn_header['lines'],
    #                                 rdn_header['samples']))

    # Read atmospheric lookup table.
    atm_lut_metadata = read_binary_metadata(
        flight_dict['resampled_atm_lut_file'] + '.meta')
    atm_lut_metadata['shape'] = tuple(
        [int(v) for v in atm_lut_metadata['shape']])

    atm_lut_WVC = np.array([float(v) for v in atm_lut_metadata['WVC']])
    atm_lut_VIS = np.array([float(v) for v in atm_lut_metadata['VIS']])
    atm_lut_VZA = np.array([float(v) for v in atm_lut_metadata['VZA']])
    atm_lut_RAA = np.array([float(v) for v in atm_lut_metadata['RAA']])

    atm_lut = np.memmap(flight_dict['resampled_atm_lut_file'],
                        dtype=atm_lut_metadata['dtype'],
                        mode='r',
                        shape=atm_lut_metadata['shape']
                        )  # shape = (RHO, WVC, VIS, VZA, RAA, WAVE)

    # Read VZA and RAA image.
    sca_header = read_envi_header(
        os.path.splitext(flight_dict['merged_sca_file'])[0] + '.hdr')
    saa = float(sca_header['sun azimuth'])
    sca_image = np.memmap(flight_dict['merged_sca_file'],
                          dtype='float32',
                          shape=(sca_header['bands'], sca_header['lines'],
                                 sca_header['samples']))
    # vza
    vza_image = np.copy(sca_image[0, :, :])
    # raa
    raa_image = saa - sca_image[1, :, :]
    raa_image[raa_image < 0] += 360.0
    raa_image[raa_image > 180] = 360.0 - raa_image[raa_image > 180]
    # clear data
    sca_image.flush()
    del sca_header, saa
    del sca_image

    # Read wvc and vis image.
    wvc_header = read_envi_header(
        os.path.splitext(flight_dict['wvc_file'])[0] + '.hdr')
    tmp_wvc_image = np.memmap(flight_dict['wvc_file'],
                              mode='r',
                              dtype='float32',
                              shape=(wvc_header['lines'],
                                     wvc_header['samples']))
    wvc_image = np.copy(tmp_wvc_image)

    vis_header = read_envi_header(
        os.path.splitext(flight_dict['vis_file'])[0] + '.hdr')
    tmp_vis_image = np.memmap(flight_dict['vis_file'],
                              dtype='float32',
                              mode='r',
                              shape=(vis_header['lines'],
                                     vis_header['samples']))
    vis_image = np.copy(tmp_vis_image)
    tmp_wvc_image.flush()
    tmp_vis_image.flush()
    del wvc_header, vis_header
    del tmp_vis_image, tmp_wvc_image

    # Read background mask.
    bg_header = read_envi_header(
        os.path.splitext(flight_dict['background_mask_file'])[0] + '.hdr')
    bg_mask = np.memmap(flight_dict['background_mask_file'],
                        dtype='bool',
                        mode='r',
                        shape=(bg_header['lines'], bg_header['samples']))
    idx = ~bg_mask

    max_WVC = atm_lut_WVC.max()
    max_VIS = atm_lut_VIS.max()
    max_VZA = atm_lut_VZA.max()
    max_RAA = atm_lut_RAA.max()

    wvc_image[wvc_image >= max_WVC] = max_WVC - 0.1
    vis_image[vis_image >= max_VIS] = max_VIS - 0.1
    vza_image[vza_image >= max_VZA] = max_VZA - 0.1
    raa_image[raa_image >= max_RAA] = max_RAA - 0.1

    del max_WVC, max_VIS, max_VZA, max_RAA

    # remove outliers in wvc and vis.
    wvc = wvc_image[idx]
    avg_wvc = wvc.mean()
    std_wvc = wvc.std()
    index = (np.abs(wvc_image - avg_wvc) > 2 * std_wvc) & (idx)
    wvc_image[index] = avg_wvc
    del wvc

    vis = vis_image[idx]
    avg_vis = vis.mean()
    std_vis = vis.std()
    index = (np.abs(vis_image - avg_vis) > 2 * std_vis) & (idx)
    vis_image[index] = avg_vis
    del vis
    del index

    del idx

    fid = open(flight_dict['refl_file'], 'wb')
    # Do atmosphere correction.
    info = 'Bands = '
    for band in range(rdn_header['bands']):
        if band % 20 == 0:
            info += '%d, ' % (band + 1)
        if (rdn_header['wavelength'][band] >= 1340.0
                and rdn_header['wavelength'][band] <= 1440.0) or (
                    rdn_header['wavelength'][band] >= 1800.0
                    and rdn_header['wavelength'][band] <= 1980.0
                ) or rdn_header['wavelength'][band] >= 2460.0:
            # fid.write(np.zeros((rdn_header['lines'], rdn_header['samples'])).astype('float32').tostring())
            fid.write(
                np.zeros((rdn_header['lines'], rdn_header['samples']
                          )).astype('int16').tostring())  # in int, by Ting
        else:
            # offset = rdn_header['header offset']+4*band*rdn_header['lines']*rdn_header['samples']# in bytes
            offset = rdn_header[
                'header offset'] + 2 * band * rdn_header['lines'] * rdn_header[
                    'samples']  # in bytes, int16 consists 2 bytes, by Ting
            rdn_image = np.memmap(
                flight_dict['merged_rdn_file'],
                # dtype='float32',
                dtype='int16',  # in int, by Ting
                mode='r',
                offset=offset,
                shape=(rdn_header['lines'], rdn_header['samples']))

            refl = atm_corr_band(
                atm_lut_WVC,
                atm_lut_VIS,
                atm_lut_VZA,
                atm_lut_RAA,
                np.copy(atm_lut[..., band]),
                wvc_image,
                vis_image,
                vza_image,
                raa_image,
                rdn_image /
                1000,  # divided by 1000 to convert back to original rdn, by Ting.
                bg_mask)
            refl = refl * 10000  # reflectance times 10000 to convert to int, by Ting
            # fid.write(refl.astype('float32').tostring())
            fid.write(refl.astype('int16').tostring())  # save in int, by Ting
            rdn_image.flush()
            del refl, rdn_image

    fid.close()
    info += '%d, Done!' % band
    logger.info(info)

    # Clear data
    del wvc_image, vis_image, vza_image, raa_image
    atm_lut.flush()
    bg_mask.flush()
    del atm_lut, bg_mask

    rdn_header['description'] = 'Reflectance [0-1]'
    write_envi_header(
        os.path.splitext(flight_dict['refl_file'])[0] + '.hdr', rdn_header)
    logger.info('Write the reflectance image to %s.' %
                flight_dict['refl_file'])
示例#28
0
def build_wvc_model(wvc_model_file, atm_lut_file, rdn_header_file, vis=40):
    """ Build water vapor models.

    Parameters
    ----------
    wvc_model_file: str
        Water vapor column model filename.
    atm_lut_file: str
        Atmosphere lookup table file.
    rdn_header_file: str
        Radiance header filename.
    vis: float
        Visibility in [km].
    """

    if os.path.exists(wvc_model_file):
        logger.info('Write WVC models to %s.' % wvc_model_file)
        return

    from AtmLUT import read_binary_metadata, get_interp_range
    from ENVI import read_envi_header
    from Spectra import get_closest_wave, resample_spectra
    import json

    # Get sensor wavelengths & FWHMs
    header = read_envi_header(rdn_header_file)
    sensor_waves = np.array(header['wavelength'])
    sensor_fwhms = np.array(header['fwhm'])

    # Read atmospheric lookup table (ALT) metadata
    metadata = read_binary_metadata(atm_lut_file + '.meta')
    metadata['shape'] = tuple([int(v) for v in metadata['shape']])
    atm_lut_WVC = np.array([float(v) for v in metadata['WVC']])
    atm_lut_VIS = np.array([float(v) for v in metadata['VIS']])
    atm_lut_VZA = np.array([float(v) for v in metadata['VZA']])
    atm_lut_RAA = np.array([float(v) for v in metadata['RAA']])
    atm_lut_WAVE = np.array([float(v) for v in metadata['WAVE']])
    # VZA index
    vza_index = np.where(atm_lut_VZA == min(atm_lut_VZA))[0][-1]
    # RAA index
    raa_index = np.where(atm_lut_RAA == min(atm_lut_RAA))[0][-1]

    # Load atmospheric lookup table
    atm_lut = np.memmap(
        atm_lut_file, dtype='float32', mode='r',
        shape=metadata['shape'])  # shape=(RHO, WVC, VIS, VZA, RAA, WAVE)

    # Subtract path radiance
    atm_lut_rdn = atm_lut[1, :, vza_index, raa_index, :] - atm_lut[
        0, :, vza_index, raa_index, :]  # shape=(WVC, VIS, WAVE)
    atm_lut.flush()
    del atm_lut

    # VIS interpolation range
    vis_dict = get_interp_range(atm_lut_VIS, vis)

    # Do interpolation
    interp_rdn = np.zeros((len(atm_lut_WVC), len(atm_lut_WAVE)))
    for vis_index, vis_delta in vis_dict.items():
        interp_rdn += atm_lut_rdn[:, vis_index, :] * vis_delta
    del atm_lut_rdn, vis_index, vis_delta

    # Build WVC models
    model_waves = [(890, 940, 1000), (1070, 1130, 1200)]
    wvc_model = dict()
    for waves in model_waves:
        # Get model wavelengths
        wave_1, wave_2, wave_3 = waves
        left_wave, left_band = get_closest_wave(sensor_waves, wave_1)
        middle_wave, middle_band = get_closest_wave(sensor_waves, wave_2)
        right_wave, right_band = get_closest_wave(sensor_waves, wave_3)

        # Build the model
        if np.abs(left_wave - wave_1) < 20 and np.abs(
                middle_wave - wave_2) < 20 and np.abs(right_wave -
                                                      wave_3) < 20:
            model = dict()

            # Bands
            model['band'] = [int(left_band), int(middle_band), int(right_band)]

            # Wavelengths
            model['wavelength'] = [left_wave, middle_wave, right_wave]

            # Weights
            left_weight = (right_wave - middle_wave) / (right_wave - left_wave)
            right_weight = (middle_wave - left_wave) / (right_wave - left_wave)
            model['weight'] = [left_weight, right_weight]

            # Ratios & WVCs
            resampled_rdn = resample_spectra(interp_rdn, atm_lut_WAVE,
                                             sensor_waves[model['band']],
                                             sensor_fwhms[model['band']])
            ratio = resampled_rdn[:, 1] / (left_weight * resampled_rdn[:, 0] +
                                           right_weight * resampled_rdn[:, 2])
            index = np.argsort(ratio)
            model['ratio'] = list(ratio[index])
            model['wvc'] = list(atm_lut_WVC[index])

            # Save model parameters
            wvc_model['WVC_Model_%d' % wave_2] = model

            # Clear data
            del left_weight, right_weight, resampled_rdn, ratio, index, model
    del interp_rdn

    # Save WVC models to a .JSON file
    with open(wvc_model_file, 'w') as fid:
        json.dump(wvc_model, fid, indent=4)
    logger.info('Write WVC models to %s.' % wvc_model_file)
示例#29
0
def detect_smile_effect(sensor_dict, atm_lut_file):
    """ Detect smile effect.

    Parameters
    ----------
    sensor_dict: dict
        Sensor configurations.
    atm_lut_file: str
        Raw atmosphere lookup table filename.
    """

    if os.path.exists(sensor_dict['smile_effect_at_atm_features_file']
                      ) and os.path.exists(sensor_dict['smile_effect_file']):
        logger.info(
            'Write smile effect at atmosphere aborption features to %s.' %
            sensor_dict['smile_effect_at_atm_features_file'])
        logger.info('Write smile effect to %s.' %
                    sensor_dict['smile_effect_file'])
        return

    from ENVI import empty_envi_header, read_envi_header, write_envi_header
    from Spectra import get_closest_wave
    from scipy import optimize, interpolate
    import json

    # Read averaged radiance
    header = read_envi_header(sensor_dict['avg_rdn_file'] + '.hdr')
    sensor_rdn = np.memmap(sensor_dict['avg_rdn_file'],
                           mode='r',
                           dtype='float32',
                           shape=(header['lines'],
                                  header['samples']))  # shape=(bands, samples)
    samples = header['samples']
    sensor_wave = np.array([float(v) for v in header['waves'].split(',')])
    sensor_fwhm = np.array([float(v) for v in header['fwhms'].split(',')])

    tmp_vza = header['VZA'].split(',')
    tmp_raa = header['RAA'].split(',')
    vza = []
    raa = []
    for i in range(samples):
        try:
            vza.append(float(tmp_vza[i]))
            raa.append(float(tmp_raa[i]))
        except:
            vza.append(np.nan)
            raa.append(np.nan)
            logger.info('No spectrum for column: %s.' % (i + 1))
    del tmp_vza, tmp_raa
    del header
    vza = np.array(vza)
    raa = np.array(raa)
    if np.all(np.isnan(vza)) or np.all(np.isnan(raa)):
        raise IOError(
            "Cannot detect smile effects since all columns do not have spectra."
        )

    nonnan_index = np.where((~np.isnan(vza)) & (~np.isnan(vza)))[0]
    vza = np.interp(np.arange(samples), nonnan_index, vza[nonnan_index])
    raa = np.interp(np.arange(samples), nonnan_index, raa[nonnan_index])

    # Assign visibility
    vis = [40] * samples

    # Estimate water vapor column
    logger.info('Estimate WVC from averaged radiance spectra.')
    wvc_models = json.load(open(sensor_dict['wvc_model_file'], 'r'))
    wvc = np.zeros(samples)
    for model in wvc_models.values():
        ratio = sensor_rdn[model['band'][1], :] / (
            sensor_rdn[model['band'][0], :] * model['weight'][0] +
            sensor_rdn[model['band'][2], :] * model['weight'][1])
        wvc += np.interp(ratio, model['ratio'], model['wvc'])
        del ratio
    wvc /= len(wvc_models)
    wvc_isnan = np.isnan(wvc)  # Look for NaN values
    if np.any(wvc_isnan):
        logger.info('WVC could not be estimated for some columns. '
                    'Missing values will be interpolated.')
        interpolate_values(wvc,
                           wvc_isnan)  # Replace NaNs with interpolated values
    logger.info('WVC [mm] statistics: min=%.2f, max=%.2f, avg=%.2f, sd=%.2f.' %
                (wvc.min(), wvc.max(), wvc.mean(), wvc.std()))
    del wvc_models, model

    # Interpolate atmospheric lookup table
    logger.info('Interpolate atmospheric look-up table.')
    atm_lut_wave, atm_lut_rdn = interp_atm_lut(atm_lut_file, wvc, vis, vza,
                                               raa)  # shape=(samples, bands)
    del wvc, vis, vza, raa

    # Detect smile effects at atmospheric absorption features
    logger.info('Detect smile effects at atmosphere absorption features.')
    shifts = []
    band_indices = []
    n_features = 0
    for wave, wave_range in absorption_features.items():
        # Get sensor band range
        sensor_wave0, sensor_band0 = get_closest_wave(sensor_wave,
                                                      wave_range[0])
        sensor_wave1, sensor_band1 = get_closest_wave(sensor_wave,
                                                      wave_range[1])
        center_wave, band_index = get_closest_wave(sensor_wave, wave)

        # Check if continue
        if abs(sensor_wave0 - wave_range[0]) > 20 or abs(sensor_wave1 -
                                                         wave_range[1]) > 20:
            continue

        # Get LUT band range
        _, atm_lut_band0 = get_closest_wave(atm_lut_wave, sensor_wave0 - 20)
        _, atm_lut_band1 = get_closest_wave(atm_lut_wave, sensor_wave1 + 20)

        # Optimize
        logger.info(
            'Absorption feature center wavelength and range [nm] = %d: %d-%d.'
            % (wave, wave_range[0], wave_range[1]))
        x = []
        for sample in range(samples):
            p = optimize.minimize(
                cost_fun, [0, 0],
                method='BFGS',
                args=(sensor_wave[sensor_band0:sensor_band1 + 1],
                      sensor_fwhm[sensor_band0:sensor_band1 + 1],
                      sensor_rdn[sensor_band0:sensor_band1 + 1, sample],
                      atm_lut_wave[atm_lut_band0:atm_lut_band1],
                      atm_lut_rdn[sample, atm_lut_band0:atm_lut_band1]))
            x.append(p.x)
        x = np.array(x)

        # Do linear interpolation for invalid values
        tmp_index = ~(np.abs(x[:, 0]) > 10.0)
        x[:, 0] = np.interp(np.arange(samples),
                            np.arange(samples)[tmp_index], x[tmp_index, 0])
        x[:, 1] = np.interp(np.arange(samples),
                            np.arange(samples)[tmp_index], x[tmp_index, 1])
        del tmp_index

        # Append shifts
        shifts.append(x)
        band_indices.append(band_index)
        n_features += 1

    # Clear data
    del p, x, atm_lut_rdn
    sensor_rdn.flush()
    del sensor_rdn

    # Reshape data
    shifts = np.dstack(shifts).astype('float32').swapaxes(0, 1).swapaxes(1, 2)

    # Write shifts to binary file
    fid = open(sensor_dict['smile_effect_at_atm_features_file'], 'wb')
    fid.write(shifts[0, :, :].tostring())  # Shift in wavelength
    fid.write(shifts[1, :, :].tostring())  # Shift in fwhm
    fid.close()

    # Write header
    header = empty_envi_header()
    header[
        'description'] = 'Smile effects detected at atmosphere absorption features'
    header['file type'] = 'ENVI Standard'
    header['bands'] = 2
    header['lines'] = n_features
    header['samples'] = samples
    header['interleave'] = 'bsq'
    header['byte order'] = 0
    header['data type'] = 4
    header['spectral center wavelengths'] = list(sensor_wave[band_indices])
    header['spectral bandwiths'] = list(sensor_fwhm[band_indices])
    header['band indices'] = band_indices
    write_envi_header(
        sensor_dict['smile_effect_at_atm_features_file'] + '.hdr', header)
    del header
    logger.info('Write smile effect at atmosphere absorption features to %s.' %
                sensor_dict['smile_effect_at_atm_features_file'])

    # Do interpolation
    fid = open(sensor_dict['smile_effect_file'], 'wb')
    x = np.arange(samples)
    y = sensor_wave[band_indices]
    x_new = x
    y_new = sensor_wave

    # Center wavelength
    z = np.zeros((shifts.shape[1], shifts.shape[2]), dtype='float64')
    for feature in range(shifts.shape[1]):
        p = np.poly1d(np.polyfit(x, shifts[0, feature, :], 4))
        z[feature, :] = p(x)
    f = interpolate.interp2d(x, y, z, kind='cubic')
    z_new = f(x_new, y_new) + np.expand_dims(sensor_wave, axis=1)
    fid.write(z_new.astype('float32').tostring())

    # Bandwidth
    z = np.zeros((shifts.shape[1], shifts.shape[2]), dtype='float64')
    for feature in range(shifts.shape[1]):
        p = np.poly1d(np.polyfit(x, shifts[1, feature, :], 5))
        z[feature, :] = p(x)
    f = interpolate.interp2d(x, y, z, kind='cubic')
    z_new = f(x_new, y_new) + np.expand_dims(sensor_fwhm, axis=1)
    fid.write(z_new.astype('float32').tostring())
    fid.close()
    del f, x, y, z, z_new

    # Write header
    header = empty_envi_header()
    header[
        'description'] = 'Spectral Center Wavelengths and Spectral Bandwidths'
    header['file type'] = 'ENVI Standard'
    header['bands'] = 2
    header['lines'] = len(y_new)
    header['samples'] = len(x_new)
    header['interleave'] = 'bsq'
    header['byte order'] = 0
    header['data type'] = 4
    write_envi_header(sensor_dict['smile_effect_file'] + '.hdr', header)
    del header, x_new, y_new

    logger.info('Write smile effect to %s.' % sensor_dict['smile_effect_file'])
示例#30
0
文件: AtmCorr.py 项目: EnSpec/hypro
def atm_corr_image(flight_dict):
    """ Whole-image atmospheric correction.

    Parameters
    ----------
    flight_dict: dict
        Flight dictionary.
    """

    if os.path.exists(flight_dict['refl_file']):
        logger.info('Write the reflectance image to %s.' %
                    flight_dict['refl_file'])
        return

    from ENVI import read_envi_header, write_envi_header
    from AtmLUT import read_binary_metadata

    # Read radiance image header
    rdn_header = read_envi_header(
        os.path.splitext(flight_dict['merged_rdn_file'])[0] + '.hdr')

    # Read atmospheric lookup table
    atm_lut_metadata = read_binary_metadata(
        flight_dict['resampled_atm_lut_file'] + '.meta')
    atm_lut_metadata['shape'] = tuple(
        [int(v) for v in atm_lut_metadata['shape']])

    atm_lut_WVC = np.array([float(v) for v in atm_lut_metadata['WVC']])
    atm_lut_VIS = np.array([float(v) for v in atm_lut_metadata['VIS']])
    atm_lut_VZA = np.array([float(v) for v in atm_lut_metadata['VZA']])
    atm_lut_RAA = np.array([float(v) for v in atm_lut_metadata['RAA']])

    atm_lut = np.memmap(flight_dict['resampled_atm_lut_file'],
                        dtype=atm_lut_metadata['dtype'],
                        mode='r',
                        shape=atm_lut_metadata['shape']
                        )  # shape=(RHO, WVC, VIS, VZA, RAA, WAVE)

    # Read scan angles (SCA) image
    sca_header = read_envi_header(
        os.path.splitext(flight_dict['merged_sca_file'])[0] + '.hdr')
    saa = float(sca_header['sun azimuth'])
    sca_image = np.memmap(flight_dict['merged_sca_file'],
                          dtype='float32',
                          shape=(sca_header['bands'], sca_header['lines'],
                                 sca_header['samples']))
    # View zenith angle (VZA)
    vza_image = np.copy(sca_image[0, :, :])
    # Relative azimuth angle (RAA)
    raa_image = saa - sca_image[1, :, :]
    raa_image[raa_image < 0] += 360.0
    raa_image[raa_image > 180] = 360.0 - raa_image[raa_image > 180]

    # Clear data
    sca_image.flush()
    del sca_header, saa
    del sca_image

    # Read WVC & VIS images
    wvc_header = read_envi_header(
        os.path.splitext(flight_dict['wvc_file'])[0] + '.hdr')
    tmp_wvc_image = np.memmap(flight_dict['wvc_file'],
                              mode='r',
                              dtype='float32',
                              shape=(wvc_header['lines'],
                                     wvc_header['samples']))
    wvc_image = np.copy(tmp_wvc_image)

    vis_header = read_envi_header(
        os.path.splitext(flight_dict['vis_file'])[0] + '.hdr')
    tmp_vis_image = np.memmap(flight_dict['vis_file'],
                              dtype='float32',
                              mode='r',
                              shape=(vis_header['lines'],
                                     vis_header['samples']))
    vis_image = np.copy(tmp_vis_image)
    tmp_wvc_image.flush()
    tmp_vis_image.flush()
    del wvc_header, vis_header
    del tmp_vis_image, tmp_wvc_image

    # Read background mask
    bg_header = read_envi_header(
        os.path.splitext(flight_dict['background_mask_file'])[0] + '.hdr')
    bg_mask = np.memmap(flight_dict['background_mask_file'],
                        dtype='bool',
                        mode='r',
                        shape=(bg_header['lines'], bg_header['samples']))

    idx = ~bg_mask

    max_WVC = atm_lut_WVC.max()
    max_VIS = atm_lut_VIS.max()
    max_VZA = atm_lut_VZA.max()
    max_RAA = atm_lut_RAA.max()

    # Enforce cutoffs in ALT parameters
    wvc_image[wvc_image >= max_WVC] = max_WVC - 0.1
    vis_image[vis_image >= max_VIS] = max_VIS - 0.1
    vza_image[vza_image >= max_VZA] = max_VZA - 0.1
    raa_image[raa_image >= max_RAA] = max_RAA - 0.1

    del max_WVC, max_VIS, max_VZA, max_RAA

    # Remove outliers in WVC and VIS
    wvc = wvc_image[idx]
    avg_wvc = wvc.mean()
    std_wvc = wvc.std()
    wvc_image[(np.abs(wvc_image - avg_wvc) > 2 * std_wvc)
              & (~bg_mask)] = avg_wvc
    del wvc

    vis = vis_image[idx]
    avg_vis = vis.mean()
    std_vis = vis.std()
    vis_image[(np.abs(vis_image - avg_vis) > 2 * std_vis)
              & (~bg_mask)] = avg_vis
    del vis

    del idx

    fid = open(flight_dict['refl_file'], 'wb')
    # Do atmospheric correction
    info = 'Bands = '
    for band in range(rdn_header['bands']):
        if band % 20 == 0:
            info += '%d, ' % (band + 1)
        if (rdn_header['wavelength'][band] >= 1340.0
                and rdn_header['wavelength'][band] <= 1440.0) or (
                    rdn_header['wavelength'][band] >= 1800.0
                    and rdn_header['wavelength'][band] <= 1980.0
                ) or rdn_header['wavelength'][band] >= 2460.0:
            fid.write(
                np.zeros((rdn_header['lines'],
                          rdn_header['samples'])).astype('float32').tostring())
        else:
            offset = rdn_header['header offset'] + 4 * band * rdn_header[
                'lines'] * rdn_header['samples']  # in bytes
            rdn_image = np.memmap(flight_dict['merged_rdn_file'],
                                  dtype='float32',
                                  mode='r',
                                  offset=offset,
                                  shape=(rdn_header['lines'],
                                         rdn_header['samples']))
            refl = atm_corr_band(atm_lut_WVC, atm_lut_VIS,
                                 atm_lut_VZA, atm_lut_RAA,
                                 np.copy(atm_lut[...,
                                                 band]), wvc_image, vis_image,
                                 vza_image, raa_image, rdn_image, bg_mask)
            fid.write(refl.astype('float32').tostring())
            rdn_image.flush()
            del refl, rdn_image

    fid.close()
    info += '%d, Done!' % band
    logger.info(info)

    # Clear data
    del wvc_image, vis_image, vza_image, raa_image
    atm_lut.flush()
    bg_mask.flush()
    del atm_lut, bg_mask

    rdn_header['description'] = 'Reflectance [0-1]'
    write_envi_header(
        os.path.splitext(flight_dict['refl_file'])[0] + '.hdr', rdn_header)
    logger.info('Write the reflectance image to %s.' %
                flight_dict['refl_file'])