Ejemplo n.º 1
0
def interp_atm_lut(atm_lut_file, WVC, VIS, VZA, RAA):
    """ Interpolate atmosphere look-up-table to different water vapor columns (WVC),
        visibilities (VIS), view zenith angles (VZA) and relative azimuth angles (RAA).

    Parameters
    ----------
    atm_lut_file: str
        Atmosphere look-up-table filename.
    WVC, VIS, VZA, RAA: list of floats
        Water vapor column, visibility, view zenith angles and relative azimuth angles.

    Returns
    -------
    WAVE: array
        Wavelengths of the atmosphere look-up-table radiance.
    lut_rdn: 2D array
        Interpolated path radiance (albedo=0.0, 0.5, 1.0).
    """

    from AtmLUT import read_binary_metadata, get_interp_range, combos

    # Read atmospheric lookup table grids
    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_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_WAVE = np.array([float(v) for v in atm_lut_metadata['WAVE']])

    # Read atmospheric lookup table data
    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)

    # Initialize interpolated radiance
    interp_rdn = np.zeros((len(WVC), len(atm_lut_WAVE)), dtype='float32')

    # Do interpolation
    for i in range(len(WVC)):
        wvc, vis, vza, raa = WVC[i], VIS[i], VZA[i], RAA[i]

        # Get interpolation ranges
        wvc_dict = get_interp_range(atm_lut_WVC, wvc)
        vis_dict = get_interp_range(atm_lut_VIS, vis)
        vza_dict = get_interp_range(atm_lut_VZA, vza)
        raa_dict = get_interp_range(atm_lut_RAA, raa)

        # Get combos
        index_combos = combos([
            list(wvc_dict.keys()),
            list(vis_dict.keys()),
            list(vza_dict.keys()),
            list(raa_dict.keys())
        ])

        # Update interpolated radiance
        for index_combo in index_combos:
            wvc_index, vis_index, vza_index, raa_index = index_combo
            interp_rdn[i, :] += atm_lut[
                1, wvc_index, vis_index, vza_index,
                raa_index, :] * wvc_dict[wvc_index] * vis_dict[
                    vis_index] * vza_dict[vza_index] * raa_dict[raa_index]
        del index_combo, index_combos

    # Clear atmosphere look-up table
    atm_lut.flush()
    del atm_lut

    return atm_lut_WAVE, interp_rdn
Ejemplo n.º 2
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'])
Ejemplo n.º 3
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)
Ejemplo n.º 4
0
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'])
Ejemplo n.º 5
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