/****************************************************************************** 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; }
/***************************************************************************** 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; }