def __init__(self, config_filename):

        self.config = deepcopy(json_load_ascii(config_filename))

        self.modtran_config_filenames = self.config[
            'modtran_config_json_filenames']
        self.output_modtran_config_filenames = self.config[
            'output_modtran_config_filenames']
        self.year_for_HRRR_profiles_in_modtran = self.config[
            'year_for_HRRR_profiles_in_modtran']
        self.HRRR_data_library_path = self.config['HRRR_data_library_path']

        for modtran_config_filename, output_modtran_config_filename in \
                zip(self.modtran_config_filenames, self.output_modtran_config_filenames):

            template = deepcopy(
                json_load_ascii(modtran_config_filename)['MODTRAN'])

            prof_altitude_dict, prof_pressure_dict, prof_temperature_dict, prof_H2O_dict = self.create_profiles(
                template)

            template[0]['MODTRANINPUT']['ATMOSPHERE']['NLAYERS'] = len(
                prof_altitude_dict['PROFILE'])
            template[0]['MODTRANINPUT']['ATMOSPHERE']['NPROF'] = 4
            template[0]['MODTRANINPUT']['ATMOSPHERE']['PROFILES'] = [
                dict(prof_altitude_dict),
                dict(prof_pressure_dict),
                dict(prof_temperature_dict),
                dict(prof_H2O_dict)
            ]

            template_str = json.dumps({"MODTRAN": template})
            with open(output_modtran_config_filename, 'w') as f:
                f.write(template_str)

        return
def instrument_model(config):
    """."""

    hdr_template = """ENVI
    samples = {samples}
    lines   = {lines}
    bands   = 1
    header offset = 0
    file type = ENVI Standard
    data type = 4
    interleave = bsq
    byte order = 0
    """

    config = json_load_ascii(config, shell_replace=True)
    configdir, configfile = split(abspath(config))

    infile = expand_path(configdir, config['input_radiance_file'])
    outfile = expand_path(configdir, config['output_model_file'])
    flatfile = expand_path(configdir, config['output_flatfield_file'])
    uniformity_thresh = float(config['uniformity_threshold'])

    infile_hdr = infile + '.hdr'
    img = envi.open(infile_hdr, infile)
    inmm = img.open_memmap(interleave='bil', writable=False)
    X = np.array(inmm[:, :, :], dtype=np.float32)
    nr, nb, nc = X.shape

    FF, Xhoriz, Xhorizp, use_ff = _flat_field(X, uniformity_thresh)
    np.array(FF, dtype=np.float32).tofile(flatfile)
    with open(flatfile + '.hdr', 'w') as fout:
        fout.write(hdr_template.format(lines=nb, samples=nc))

    C, Xvert, Xvertp, use_C = _column_covariances(X, uniformity_thresh)
    cshape = (C.shape[0], C.shape[1]**2)
    out = np.array(C, dtype=np.float32).reshape(cshape)
    mdict = {
        'columns': out.shape[0],
        'bands': out.shape[1],
        'covariances': out,
        'Xvert': Xvert,
        'Xhoriz': Xhoriz,
        'Xvertp': Xvertp,
        'Xhorizp': Xhorizp,
        'use_ff': use_ff,
        'use_C': use_C
    }
    scipy.io.savemat(outfile, mdict)
示例#3
0
def generate_noise(config):
    """Add noise to a radiance spectrum or image."""

    config = json_load_ascii(config, shell_replace=True)
    configdir, configfile = split(abspath(config))

    infile = expand_path(configdir, config['input_radiance_file'])
    outfile = expand_path(configdir, config['output_radiance_file'])
    instrument = Instrument(config['instrument_model'])
    geom = Geometry()

    if infile.endswith('txt'):
        rdn, wl = load_spectrum(infile)
        Sy = instrument.Sy(rdn, geom)
        rdn_noise = rdn + np.random.multivariate_normal(
            np.zeros(rdn.shape), Sy)
        with open(outfile, 'w') as fout:
            for w, r in zip(wl, rdn_noise):
                fout.write('%8.5f %8.5f' % (w, r))
    else:
        raise ValueError("Image cubes not yet implemented.")
示例#4
0
def surface_model(config_path: str,
                  wavelength_path: str = None,
                  output_path: str = None) -> None:
    """The surface model tool contains everything you need to build basic
    multicomponent (i.e. colleciton of Gaussian) surface priors for the
    multicomponent surface model.

    Args:
        config_path: path to a JSON formatted surface model configuration
        wavelength_path: optional path to a three-column wavelength file, 
           overriding the configuration file settings
        output_path: optional path to the destination .mat file, overriding
           the configuration file settings
    Returns:
        None
    """

    # Load configuration JSON into a local dictionary
    configdir, _ = os.path.split(os.path.abspath(config_path))
    config = json_load_ascii(config_path, shell_replace=True)

    # Determine top level parameters
    for q in ['output_model_file', 'sources', 'normalize', 'wavelength_file']:
        if q not in config:
            raise ValueError("Missing parameter: %s" % q)
    if wavelength_path is not None:
        wavelength_file = wavelength_path
    else:
        wavelength_file = expand_path(configdir, config['wavelength_file'])
    if output_path is not None:
        outfile = output_path
    else:
        outfile = expand_path(configdir, config['output_model_file'])
    normalize = config['normalize']
    reference_windows = config['reference_windows']

    # load wavelengths file, and change units to nm if needed
    q = np.loadtxt(wavelength_file)
    if q.shape[1] > 2:
        q = q[:, 1:]
    if q[0, 0] < 100:
        q = q * 1000.0
    wl = q[:, 0]
    nchan = len(wl)

    # build global reference windows
    refwl = []
    for wi, window in enumerate(reference_windows):
        active_wl = np.logical_and(wl >= window[0], wl < window[1])
        refwl.extend(wl[active_wl])
    normind = np.array([np.argmin(abs(wl - w)) for w in refwl])
    refwl = np.array(refwl, dtype=float)

    # create basic model template
    model = {
        'normalize': normalize,
        'wl': wl,
        'means': [],
        'covs': [],
        'attribute_means': [],
        'attribute_covs': [],
        'attributes': [],
        'refwl': refwl
    }

    # each "source" (i.e. spectral library) is treated separately
    for si, source_config in enumerate(config['sources']):

        # Determine source parameters
        for q in [
                'input_spectrum_files', 'windows', 'n_components', 'windows'
        ]:
            if q not in source_config:
                raise ValueError('Source %i is missing a parameter: %s' %
                                 (si, q))

        # Determine whether we should synthesize our own mixtures
        if 'mixtures' in source_config:
            mixtures = source_config['mixtures']
        elif 'mixtures' in config:
            mixtures = config['mixtures']
        else:
            mixtures = 0

        # open input files associated with this source
        infiles = [
            expand_path(configdir, fi)
            for fi in source_config['input_spectrum_files']
        ]

        # associate attributes, if they exist. These will not be used
        # in the retrieval, but can be used in post-analysis
        if 'input_attribute_files' in source_config:
            infiles_attributes = [
                expand_path(configdir, fi)
                for fi in source_config['input_attribute_files']
            ]
            if len(infiles_attributes) != len(infiles):
                raise IndexError('spectrum / attribute file mismatch')
        else:
            infiles_attributes = [
                None for fi in source_config['input_spectrum_files']
            ]

        ncomp = int(source_config['n_components'])
        windows = source_config['windows']

        # load spectra
        spectra, attributes = [], []
        for infile, attribute_file in zip(infiles, infiles_attributes):

            rfl = envi.open(envi_header(infile), infile)
            nl, nb, ns = [
                int(rfl.metadata[n]) for n in ('lines', 'bands', 'samples')
            ]
            swl = np.array([float(f) for f in rfl.metadata['wavelength']])

            # Maybe convert to nanometers
            if swl[0] < 100:
                swl = swl * 1000.0

            # Load library and adjust interleave, if needed
            rfl_mm = rfl.open_memmap(interleave='bip', writable=False)
            x = np.array(rfl_mm[:, :, :])
            x = x.reshape(nl * ns, nb)

            # import spectra and resample
            for x1 in x:
                p = scipy.interpolate.interp1d(swl,
                                               x1,
                                               kind='linear',
                                               bounds_error=False,
                                               fill_value='extrapolate')
                spectra.append(p(wl))

            # Load attributes
            if attribute_file is not None:

                attr = envi.open(envi_header(attribute_file), attribute_file)
                nla, nba, nsa = [
                    int(attr.metadata[n])
                    for n in ('lines', 'bands', 'samples')
                ]

                # Load library and adjust interleave, if needed
                attr_mm = attr.open_memmap(interleave='bip', writable=False)
                x = np.array(attr_mm[:, :, :])
                x = x.reshape(nla * nsa, nba)
                model['attributes'] = attr.metadata['band names']

                # import spectra and resample
                for x1 in x:
                    attributes.append(x1)

        if len(attributes) > 0 and len(attributes) != len(spectra):
            raise IndexError('Mismatch in number of spectra vs. attributes')

        # calculate mixtures, if needed
        if len(attributes) > 0 and mixtures > 0:
            raise ValueError('Synthetic mixtures w/ attributes is not advised')

        n = float(len(spectra))
        nmix = int(n * mixtures)
        for mi in range(nmix):
            s1, m1 = spectra[int(np.random.rand() * n)], np.random.rand()
            s2, m2 = spectra[int(np.random.rand() * n)], 1.0 - m1
            spectra.append(m1 * s1 + m2 * s2)

        # Lists to arrays
        spectra = np.array(spectra)
        attributes = np.array(attributes)

        # Flag bad data
        use = np.all(np.isfinite(spectra), axis=1)
        spectra = spectra[use, :]
        if len(attributes) > 0:
            attributes = attributes[use, :]

        # Accumulate total list of window indices
        window_idx = -np.ones((nchan), dtype=int)
        for wi, win in enumerate(windows):
            active_wl = np.logical_and(wl >= win['interval'][0],
                                       wl < win['interval'][1])
            window_idx[active_wl] = wi

        # Two step model generation.  First step is k-means clustering.
        # This is more "stable" than Expectation Maximization with an
        # unconstrained covariance matrix
        kmeans = KMeans(init='k-means++', n_clusters=ncomp, n_init=10)
        kmeans.fit(spectra)
        Z = kmeans.predict(spectra)

        # Build a combined dataset of attributes and spectra
        if len(attributes) > 0:
            spectra_attr = np.concatenate((spectra, attributes), axis=1)

        # Now fit the full covariance for each component
        for ci in range(ncomp):

            m = np.mean(spectra[Z == ci, :], axis=0)
            C = np.cov(spectra[Z == ci, :], rowvar=False)
            if len(attributes) > 0:
                m_attr = np.mean(spectra_attr[Z == ci, :], axis=0)
                C_attr = np.cov(spectra_attr[Z == ci, :], rowvar=False)

            for i in range(nchan):
                window = windows[window_idx[i]]

                # Each spectral interval, or window, is constructed
                # using one of several rules.  We can draw the covariance
                # directly from the data...
                if window['correlation'] == 'EM':
                    C[i, i] = C[i, i] + float(window['regularizer'])

                # Alternatively, we can use a band diagonal form,
                # a Gaussian process that promotes local smoothness.
                elif window['correlation'] == 'GP':
                    width = float(window['gp_width'])
                    magnitude = float(window['gp_magnitude'])
                    kernel = scipy.stats.norm.pdf((wl - wl[i]) / width)
                    kernel = kernel / kernel.sum() * magnitude
                    C[i, :] = kernel
                    C[:, i] = kernel
                    C[i, i] = C[i, i] + float(window['regularizer'])

                # To minimize bias, leave the channels independent
                # and uncorrelated
                elif window['correlation'] == 'decorrelated':
                    ci = C[i, i]
                    C[:, i] = 0
                    C[i, :] = 0
                    C[i, i] = ci + float(window['regularizer'])

                else:
                    raise ValueError('I do not recognize the method ' +
                                     window['correlation'])

            # Normalize the component spectrum if desired
            if normalize == 'Euclidean':
                z = np.sqrt(np.sum(pow(m[normind], 2)))
            elif normalize == 'RMS':
                z = np.sqrt(np.mean(pow(m[normind], 2)))
            elif normalize == 'None':
                z = 1.0
            else:
                raise ValueError('Unrecognized normalization: %s\n' %
                                 normalize)
            m = m / z
            C = C / (z**2)

            model['means'].append(m)
            model['covs'].append(C)

            if len(attributes) > 0:
                model['attribute_means'].append(m_attr)
                model['attribute_covs'].append(C_attr)

    model['means'] = np.array(model['means'])
    model['covs'] = np.array(model['covs'])
    model['attribute_means'] = np.array(model['attribute_means'])
    model['attribute_covs'] = np.array(model['attribute_covs'])

    scipy.io.savemat(outfile, model)