Esempio n. 1
0
/******************************************************************************
METHOD:  l8cfmask

PURPOSE:  the main routine for fmask in C

RETURN VALUE:
Type = int
Value           Description
-----           -----------
ERROR           An error occurred during processing of the l8cfmask
SUCCESS         Processing was successful

PROJECT:  Land Satellites Data System Science Research and Development (LSRD)
at the USGS EROS

HISTORY:
Date        Programmer       Reason
--------    ---------------  -------------------------------------
3/15/2013   Song Guo         Original Development
5/14/2013   Song Guo         Added in Polar Stereographic support
7/17/2013   Song Guo         Added in Map info 
8/15/2013   Song Guo         Modified to use TOA reflectance file 
                             as input instead of metadata file
Oct/2014    Ron Dilley       Modified to utilize the ESPA internal raw binary
                             file format

NOTES: type ./l8cfmask --help for information to run the code
******************************************************************************/
int
main (int argc, char *argv[])
{
    char errstr[MAX_STR_LEN];     /* error string */
    char *cptr = NULL;            /* pointer to the file extension */
    char *xml_name = NULL;        /* input XML filename */
    char directory[MAX_STR_LEN];  /* input/output data directory */
    char extension[MAX_STR_LEN];  /* input TOA file extension */
    int ib;                       /* band counters */
    Input_t *input = NULL;        /* input data and meta data */
    char envi_file[MAX_STR_LEN];  /* output ENVI file name */
    char scene_name[MAX_STR_LEN]; /* input data scene name */
    unsigned char **pixel_mask;   /* pixel mask */
    unsigned char **conf_mask;    /* confidence mask */
    int status;               /* return value from function call */
    float clear_ptm;          /* percent of clear-sky pixels */
    float t_templ = 0.0;      /* percentile of low background temperature */
    float t_temph = 0.0;      /* percentile of high background temperature */
    Output_t *output = NULL;  /* output structure and metadata */
    bool verbose;             /* verbose flag for printing messages */
    bool use_l8_cirrus;       /* should we use L8 cirrus cloud bit results? */
    int cldpix = 2;           /* Default buffer for cloud pixel dilate */
    int sdpix = 2;            /* Default buffer for shadow pixel dilate */
    float cloud_prob;         /* Default cloud probability */
    float sun_azi_temp = 0.0; /* Keep the original sun azimuth angle */
    int max_cloud_pixels; /* Maximum cloud pixel number in cloud division */
    Espa_internal_meta_t xml_metadata; /* XML metadata structure */
    Envi_header_t envi_hdr;            /* output ENVI header information */

    time_t now;
    time (&now);
    printf ("CFmask start_time=%s\n", ctime (&now));

    /* Read the command-line arguments, including the name of the input
       Landsat TOA reflectance product and the DEM */
    status = get_args (argc, argv, &xml_name, &cloud_prob, &cldpix,
                       &sdpix, &max_cloud_pixels, &use_l8_cirrus, &verbose);
    if (status != SUCCESS)
    {
        sprintf (errstr, "calling get_args");
        CFMASK_ERROR (errstr, "main");
    }

    /* Validate the input metadata file */
    if (validate_xml_file (xml_name) != SUCCESS)
    {                           /* Error messages already written */
        CFMASK_ERROR (errstr, "main");
    }

    /* Initialize the metadata structure */
    init_metadata_struct (&xml_metadata);

    /* Parse the metadata file into our internal metadata structure; also
       allocates space as needed for various pointers in the global and band
       metadata */
    if (parse_metadata (xml_name, &xml_metadata) != SUCCESS)
    {                           /* Error messages already written */
        CFMASK_ERROR (errstr, "main");
    }

    /* Verify supported satellites */
    if (strcmp (xml_metadata.global.satellite, "LANDSAT_8") != 0)
    {
        sprintf (errstr, "Unsupported satellite sensor");
        CFMASK_ERROR (errstr, "main");
    }

    /* Split the filename to obtain the directory, scene name, and extension */
    split_filename (xml_name, directory, scene_name, extension);
    if (verbose)
        printf ("directory, scene_name, extension=%s,%s,%s\n",
                directory, scene_name, extension);

    /* Open input file, read metadata, and set up buffers */
    input = OpenInput (&xml_metadata);
    if (input == NULL)
    {
        sprintf (errstr, "opening the TOA and brightness temp files in: %s",
                 xml_name);
        CFMASK_ERROR (errstr, "main");
    }

    if (verbose)
    {
        /* Print some info to show how the input metadata works */
        printf ("DEBUG: Number of input TOA bands: %d\n", input->nband);
        printf ("DEBUG: Number of input thermal bands: %d\n", 1);
        printf ("DEBUG: Number of input TOA lines: %d\n", input->size.l);
        printf ("DEBUG: Number of input TOA samples: %d\n", input->size.s);
        printf ("DEBUG: ACQUISITION_DATE.DOY is %d\n",
                input->meta.acq_date.doy);
        printf ("DEBUG: Fill value is %d\n", input->meta.fill);
        for (ib = 0; ib < input->nband; ib++)
        {
            printf ("DEBUG: Band %d-->\n", ib);
            printf ("DEBUG:   band satu_value_ref: %d\n",
                    input->meta.satu_value_ref[ib]);
            printf ("DEBUG:   band satu_value_max: %d\n",
                    input->meta.satu_value_max[ib]);
            printf ("DEBUG:   band gains: %f, band biases: %f\n",
                    input->meta.gain[ib], input->meta.bias[ib]);
        }
        printf ("DEBUG: Thermal Band (Band 10) -->\n");
        printf ("DEBUG:   therm_satu_value_ref: %d\n",
                input->meta.therm_satu_value_ref);
        printf ("DEBUG:   therm_satu_value_max: %d\n",
                input->meta.therm_satu_value_max);
        printf ("DEBUG:   therm_gain: %f, therm_bias: %f\n",
                input->meta.gain_th, input->meta.bias_th);

        printf ("DEBUG: SUN AZIMUTH is %f\n", input->meta.sun_az);
        printf ("DEBUG: SUN ZENITH is %f\n", input->meta.sun_zen);
    }

    /* If the scene is an ascending polar scene (flipped upside down), then
       the solar azimuth needs to be adjusted by 180 degrees.  The scene in
       this case would be north down and the solar azimuth is based on north
       being up clock-wise direction. Flip the south to be up will not change
       the actual sun location, with the below relations, the solar azimuth
       angle will need add in 180.0 for correct sun location */
    if (input->meta.ul_corner.is_fill &&
        input->meta.lr_corner.is_fill &&
        (input->meta.ul_corner.lat - input->meta.lr_corner.lat) < MINSIGMA)
    {
        /* Keep the original solar azimuth angle */
        sun_azi_temp = input->meta.sun_az;
        input->meta.sun_az += 180.0;
        if ((input->meta.sun_az - 360.0) > MINSIGMA)
            input->meta.sun_az -= 360.0;
        if (verbose)
            printf ("  Polar or ascending scene."
                    "  Readjusting solar azimuth by 180 degrees.\n"
                    "  New value: %f degrees\n", input->meta.sun_az);
    }

    /* Dynamic allocate the 2d mask memory */
    pixel_mask = (unsigned char **) allocate_2d_array (input->size.l,
                                                       input->size.s,
                                                       sizeof (unsigned char));
    if (pixel_mask == NULL)
    {
        sprintf (errstr, "Allocating pixel mask memory");
        CFMASK_ERROR (errstr, "main");
    }

    conf_mask = (unsigned char **) allocate_2d_array (input->size.l,
                                                      input->size.s,
                                                      sizeof (unsigned char));
    if (conf_mask == NULL)
    {
        sprintf (errstr, "Allocating confidence mask memory");
        CFMASK_ERROR (errstr, "main");
    }

    /* Initialize the mask to clear data */
    int row, col;
    for (row = 0; row < input->size.l; row++)
        for (col = 0; col < input->size.s; col++)
        {
            pixel_mask[row][col] = MASK_CLEAR_LAND;
            conf_mask[row][col] = CLOUD_CONFIDENCE_NONE;
        }

    /* Build the potential cloud, shadow, snow, water mask */
    status = potential_cloud_shadow_snow_mask (input, cloud_prob, &clear_ptm,
                                               &t_templ, &t_temph, pixel_mask,
                                               conf_mask, use_l8_cirrus,
                                               verbose);
    if (status != SUCCESS)
    {
        sprintf (errstr, "processing potential_cloud_shadow_snow_mask");
        CFMASK_ERROR (errstr, "main");
    }

    printf ("Pcloud done, starting cloud/shadow match\n");

    /* Build the final cloud shadow based on geometry matching and
       combine the final cloud, shadow, snow, water masks into fmask
       the pixel_mask is a bit mask as input and a value mask as output */
    status = object_cloud_shadow_match (input, clear_ptm, t_templ, t_temph,
                                        cldpix, sdpix, max_cloud_pixels,
                                        pixel_mask, verbose);
    if (status != SUCCESS)
    {
        sprintf (errstr, "processing object_cloud_and_shadow_match");
        CFMASK_ERROR (errstr, "main");
    }

    /* Reassign solar azimuth angle for output purpose if south up north
       down scene is involved */
    if (input->meta.ul_corner.is_fill &&
        input->meta.lr_corner.is_fill &&
        (input->meta.ul_corner.lat - input->meta.lr_corner.lat) < MINSIGMA)
        input->meta.sun_az = sun_azi_temp;

    /* Open the output file */
    output = OpenOutput (&xml_metadata, input);
    if (output == NULL)
    {                           /* error message already printed */
        sprintf (errstr, "Opening output file");
        CFMASK_ERROR (errstr, "main");
    }

    if (!PutOutput (output, pixel_mask))
    {
        sprintf (errstr, "Writing output fmask files\n");
        CFMASK_ERROR (errstr, "main");
    }

    /* Close the output file */
    if (!CloseOutput (output))
    {
        sprintf (errstr, "closing output file");
        CFMASK_ERROR (errstr, "main");
    }

    /* Create the ENVI header file this band */
    if (create_envi_struct (&output->metadata.band[0], &xml_metadata.global,
                            &envi_hdr) != SUCCESS)
    {
        sprintf (errstr, "Creating ENVI header structure.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Write the ENVI header */
    strcpy (envi_file, output->metadata.band[0].file_name);
    cptr = strchr (envi_file, '.');
    if (cptr == NULL)
    {
        sprintf (errstr, "error in ENVI header filename");
        CFMASK_ERROR (errstr, "main");
    }

    strcpy (cptr, ".hdr");
    if (write_envi_hdr (envi_file, &envi_hdr) != SUCCESS)
    {
        sprintf (errstr, "Writing ENVI header file.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Append the cfmask band to the XML file */
    if (append_metadata (output->nband, output->metadata.band, xml_name)
        != SUCCESS)
    {
        sprintf (errstr, "Appending spectral index bands to XML file.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Free the structure */
    if (!FreeOutput (output))
    {
        sprintf (errstr, "freeing output file structure");
        CFMASK_ERROR (errstr, "main");
    }

    output = OpenOutputConfidence (&xml_metadata, input);
    if (output == NULL)
    {                           /* error message already printed */
        sprintf (errstr, "Opening output file");
        CFMASK_ERROR (errstr, "main");
    }

    if (!PutOutput (output, conf_mask))
    {
        sprintf (errstr, "Writing output fmask files\n");
        CFMASK_ERROR (errstr, "main");
    }

    /* Close the output file */
    if (!CloseOutput (output))
    {
        sprintf (errstr, "closing output file");
        CFMASK_ERROR (errstr, "main");
    }

    /* Create the ENVI header file this band */
    if (create_envi_struct (&output->metadata.band[0], &xml_metadata.global,
                            &envi_hdr) != SUCCESS)
    {
        sprintf (errstr, "Creating ENVI header structure.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Write the ENVI header */
    strcpy (envi_file, output->metadata.band[0].file_name);
    cptr = strchr (envi_file, '.');
    if (cptr == NULL)
    {
        sprintf (errstr, "error in ENVI header filename");
        CFMASK_ERROR (errstr, "main");
    }

    strcpy (cptr, ".hdr");
    if (write_envi_hdr (envi_file, &envi_hdr) != SUCCESS)
    {
        sprintf (errstr, "Writing ENVI header file.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Append the cfmask band to the XML file */
    if (append_metadata (output->nband, output->metadata.band, xml_name)
        != SUCCESS)
    {
        sprintf (errstr, "Appending spectral index bands to XML file.");
        CFMASK_ERROR (errstr, "main");
    }

    /* Free the structure */
    if (!FreeOutput (output))
    {
        sprintf (errstr, "freeing output file structure");
        CFMASK_ERROR (errstr, "main");
    }

    /* Free the metadata structure */
    free_metadata (&xml_metadata);

    /* Free the pixel mask */
    status = free_2d_array ((void **) pixel_mask);
    if (status != SUCCESS)
    {
        sprintf (errstr, "Freeing pixel mask memory");
        CFMASK_ERROR (errstr, "main");
    }

    status = free_2d_array ((void **) conf_mask);
    if (status != SUCCESS)
    {
        sprintf (errstr, "Freeing confidence mask memory");
        CFMASK_ERROR (errstr, "main");
    }

    /* Close the input file and free the structure */
    CloseInput (input);
    FreeInput (input);

    free (xml_name);

    printf ("Processing complete.\n");
    time (&now);
    printf ("CFmask end_time=%s\n", ctime (&now));

    return SUCCESS;
}
Esempio n. 2
0
/*****************************************************************************
METHOD:  cfmask

PURPOSE:  The main routine for fmask written in C

RETURN VALUE: Type = int
    Value           Description
    -----           -----------
    ERROR           An error occurred during processing of cfmask
    SUCCESS         Processing was successful
*****************************************************************************/
int
main (int argc, char *argv[])
{
    char *FUNC_NAME = "main";
    char *ext = NULL;            /* pointer to the file extension */
    char *xml_name = NULL;       /* input XML filename */
    char envi_file[MAX_STR_LEN]; /* output ENVI file name */
    char temp_file[MAX_STR_LEN]; /* temp file name */

    int status;
    int band_index;

    bool verbose;            /* verbose flag for printing messages */
    bool use_cirrus;         /* should we use Cirrus during determination? */
    bool use_thermal;        /* should we use Thermal during determination? */

    Input_t *input = NULL;    /* input data and meta data */
    Output_t *output = NULL;  /* output structure and metadata */
    Espa_internal_meta_t xml_metadata; /* XML metadata structure */
    Envi_header_t envi_hdr;            /* output ENVI header information */

    unsigned char *pixel_mask = NULL; /* pixel mask */
    unsigned char *conf_mask = NULL;  /* confidence mask */

    float clear_ptm;          /* percent of clear-sky pixels */
    float t_templ = 0.0;      /* percentile of low background temperature */
    float t_temph = 0.0;      /* percentile of high background temperature */

    int cldpix = 2;           /* Default buffer for cloud pixel dilate */
    int sdpix = 2;            /* Default buffer for shadow pixel dilate */
    float cloud_prob;         /* Default cloud probability */
    float sun_azi_temp = 0.0; /* Keep the original sun azimuth angle */

    int pixel_count;
    int pixel_index;

    time_t now;
    time(&now);

    /* Read the command-line arguments, including the name of the input
       Landsat TOA reflectance product and the DEM */
    status = get_args(argc, argv, &xml_name, &cloud_prob, &cldpix,
                      &sdpix, &use_cirrus, &use_thermal, &verbose);
    if (status != SUCCESS)
    {
        RETURN_ERROR("calling get_args", FUNC_NAME, EXIT_FAILURE);
    }

    printf("CFmask start_time=%s\n", ctime(&now));

    /* Validate the input metadata file */
    if (validate_xml_file(xml_name) != SUCCESS)
    {
        RETURN_ERROR("XML validation error", FUNC_NAME, EXIT_FAILURE);
    }

    /* Initialize the metadata structure */
    init_metadata_struct(&xml_metadata);

    /* Parse the metadata file into our internal metadata structure; also
       allocates space as needed for various pointers in the global and band
       metadata */
    if (parse_metadata(xml_name, &xml_metadata) != SUCCESS)
    {
        RETURN_ERROR("XML parsing error", FUNC_NAME, EXIT_FAILURE);
    }

    /* Open input file, read metadata, and set up buffers */
    input = OpenInput(&xml_metadata, use_thermal);
    if (input == NULL)
    {
        RETURN_ERROR("opening input data specified in input XML",
                     FUNC_NAME, EXIT_FAILURE);
    }

    if (verbose)
    {
        /* Print some info to show how the input metadata works */
        printf("Number of input TOA bands: %d\n", input->num_toa_bands);
        printf("Number of input thermal bands: %d\n", 1);
        printf("Number of input TOA lines: %d\n", input->size.l);
        printf("Number of input TOA samples: %d\n", input->size.s);
        printf("Fill value is %d\n", input->meta.fill);
        for (band_index = 0; band_index < MAX_BAND_COUNT; band_index++)
        {
            printf("Band %d-->\n", band_index);
            if (input->satellite != IS_LANDSAT_8)
            {
                /* Landsat 8 doesn't have saturation issues */
                printf("  band satu_value_ref: %d\n",
                       input->meta.satu_value_ref[band_index]);
                printf("  band satu_value_max: %d\n",
                       input->meta.satu_value_max[band_index]);
            }
            printf("  band gain: %f, band bias: %f\n",
                   input->meta.gain[band_index], input->meta.bias[band_index]);
        }

        printf("SUN AZIMUTH is %f\n", input->meta.sun_az);
        printf("SUN ZENITH is %f\n", input->meta.sun_zen);
    }

    /* If the scene is an ascending polar scene (flipped upside down), then
       the solar azimuth needs to be adjusted by 180 degrees.  The scene in
       this case would be north down and the solar azimuth is based on north
       being up clock-wise direction. Flip the south to be up will not change
       the actual sun location, with the below relations, the solar azimuth
       angle will need add in 180.0 for correct sun location */
    if (input->meta.ul_corner.lat < input->meta.lr_corner.lat)
    {
        /* Keep the original solar azimuth angle */
        sun_azi_temp = input->meta.sun_az;
        input->meta.sun_az += 180.0;
        if (input->meta.sun_az > 360.0)
        {
            input->meta.sun_az -= 360.0;
        }
        if (verbose)
        {
            printf("Polar or ascending scene."
                    "  Readjusting solar azimuth by 180 degrees.\n"
                    "  New value: %f degrees\n", input->meta.sun_az);
        }
    }

    pixel_count = input->size.l * input->size.s;

    /* Dynamic allocate the 2d mask memory */
    pixel_mask = calloc(pixel_count, sizeof(unsigned char));
    if (pixel_mask == NULL)
    {
        RETURN_ERROR("Allocating pixel mask memory", FUNC_NAME, EXIT_FAILURE);
    }

    conf_mask = calloc(pixel_count, sizeof(unsigned char));
    if (conf_mask == NULL)
    {
        RETURN_ERROR("Allocating confidence mask memory",
                     FUNC_NAME, EXIT_FAILURE);
    }

    /* Initialize the mask to clear data */
    for (pixel_index = 0; pixel_index < pixel_count; pixel_index++)
    {
        pixel_mask[pixel_index] = CF_NO_BITS;
        conf_mask[pixel_index] = CLOUD_CONFIDENCE_NONE;
    }

    /* Build the potential cloud, shadow, snow, water mask */
    status = potential_cloud_shadow_snow_mask(input, cloud_prob, &clear_ptm,
                                              &t_templ, &t_temph, pixel_mask,
                                              conf_mask, use_cirrus,
                                              use_thermal, verbose);
    if (status != SUCCESS)
    {
        RETURN_ERROR("processing potential_cloud_shadow_snow_mask",
                     FUNC_NAME, EXIT_FAILURE);
    }
    printf("Potential Cloud Shadow: Done\n");

    /* Build the final cloud shadow based on geometry matching and
       combine the final cloud, shadow, snow, water masks into fmask
       the pixel_mask is a bit mask as input and a value mask as output */
    int data_count = 0;
    status = object_cloud_shadow_match(input, clear_ptm, t_templ, t_temph,
                                       cldpix, sdpix, pixel_mask, &data_count,
                                       use_thermal, verbose);
    if (status != SUCCESS)
    {
        RETURN_ERROR("processing object_cloud_and_shadow_match",
                     FUNC_NAME, EXIT_FAILURE);
    }
    printf("Object Cloud Shadow Matching: Done\n");

    /* Convert the pixel_mask to a value mask
       Also retrieve and report statistics */
    float clear_percent = 0; /* Percent of clear pixels in the image data */
    float cloud_percent = 0; /* Percent of cloud pixels in the image data */
    float cloud_shadow_percent = 0; /* Percent of cloud shadow pixels in the
                                       image data */
    float water_percent = 0; /* Percent of water pixels in the image data */
    float snow_percent = 0;  /* Percent of snow pixels in the image data */
    convert_and_generate_statistics(verbose, pixel_mask,
                                    input->size.l * input->size.s,
                                    data_count, &clear_percent,
                                    &cloud_percent, &cloud_shadow_percent,
                                    &water_percent, &snow_percent);
    printf("Statistics Generation: Done\n");

    /* Reassign solar azimuth angle for output purpose if south up north
       down scene is involved */
    if (input->meta.ul_corner.lat < input->meta.lr_corner.lat)
    {
        input->meta.sun_az = sun_azi_temp;
    }

    /* Open the output file */
    output = OpenOutputCFmask(&xml_metadata, input, clear_percent,
                              cloud_percent, cloud_shadow_percent,
                              water_percent, snow_percent);
    if (output == NULL)
    {
        RETURN_ERROR("Opening output file", FUNC_NAME, EXIT_FAILURE);
    }

    if (!PutOutput(output, pixel_mask))
    {
        RETURN_ERROR("Writing output fmask files", FUNC_NAME, EXIT_FAILURE);
    }

    /* Close the output file */
    if (!CloseOutput(output))
    {
        RETURN_ERROR("closing output file", FUNC_NAME, EXIT_FAILURE);
    }

    /* Create the ENVI header file this band */
    if (create_envi_struct(&output->metadata.band[0], &xml_metadata.global,
                           &envi_hdr) != SUCCESS)
    {
        RETURN_ERROR("Creating ENVI header structure.", FUNC_NAME,
                     EXIT_FAILURE);
    }

    /* Write the ENVI header */
    snprintf(temp_file, sizeof(temp_file), "%s",
             output->metadata.band[0].file_name);
    ext = strrchr(temp_file, '.');
    if (ext == NULL)
    {
        RETURN_ERROR("error in ENVI header filename", FUNC_NAME, EXIT_FAILURE);
    }

    ext[0] = '\0';
    snprintf(envi_file, sizeof(envi_file), "%s.hdr", temp_file);
    if (write_envi_hdr(envi_file, &envi_hdr) != SUCCESS)
    {
        RETURN_ERROR("Writing ENVI header file.", FUNC_NAME, EXIT_FAILURE);
    }

    /* Append the cfmask band to the XML file */
    if (append_metadata(output->nband, output->metadata.band, xml_name)
        != SUCCESS)
    {
        RETURN_ERROR("Appending spectral index bands to XML file.",
                     FUNC_NAME, EXIT_FAILURE);
    }

    /* Free the structure */
    if (!FreeOutput(output))
    {
        RETURN_ERROR("freeing output file structure", FUNC_NAME, EXIT_FAILURE);
    }

    output = OpenOutputConfidence(&xml_metadata, input);
    if (output == NULL)
    {
        RETURN_ERROR("Opening output file", FUNC_NAME, EXIT_FAILURE);
    }

    if (!PutOutput(output, conf_mask))
    {
        RETURN_ERROR("Writing output fmask files", FUNC_NAME, EXIT_FAILURE);
    }

    /* Close the output file */
    if (!CloseOutput(output))
    {
        RETURN_ERROR("closing output file", FUNC_NAME, EXIT_FAILURE);
    }

    /* Create the ENVI header file this band */
    if (create_envi_struct(&output->metadata.band[0], &xml_metadata.global,
                           &envi_hdr) != SUCCESS)
    {
        RETURN_ERROR("Creating ENVI header structure.", FUNC_NAME,
                     EXIT_FAILURE);
    }

    /* Write the ENVI header */
    snprintf(temp_file, sizeof(temp_file), "%s",
             output->metadata.band[0].file_name);
    ext = strrchr(temp_file, '.');
    if (ext == NULL)
    {
        RETURN_ERROR("error in ENVI header filename", FUNC_NAME, EXIT_FAILURE);
    }

    ext[0] = '\0';
    snprintf(envi_file, sizeof(envi_file), "%s.hdr", temp_file);
    if (write_envi_hdr(envi_file, &envi_hdr) != SUCCESS)
    {
        RETURN_ERROR("Writing ENVI header file.", FUNC_NAME, EXIT_FAILURE);
    }

    /* Append the cfmask band to the XML file */
    if (append_metadata(output->nband, output->metadata.band,
                        xml_name) != SUCCESS)
    {
        RETURN_ERROR("Appending spectral index bands to XML file.",
                     FUNC_NAME, EXIT_FAILURE);
    }

    /* Free the structure */
    if (!FreeOutput(output))
    {
        RETURN_ERROR("freeing output file structure", FUNC_NAME, EXIT_FAILURE);
    }

    /* Free the metadata structure */
    free_metadata(&xml_metadata);

    /* Free the pixel mask */
    free(pixel_mask);
    pixel_mask = NULL;
    free(conf_mask);
    conf_mask = NULL;

    /* Close the input file and free the structure */
    CloseInput(input);
    FreeInput(input);

    free(xml_name);
    xml_name = NULL;

    printf("Processing complete.\n");
    time(&now);
    printf("CFmask end_time=%s\n", ctime(&now));

    return SUCCESS;
}