diff --git a/src/coastseg/common.py b/src/coastseg/common.py index 57e32d5c..f3d144e6 100644 --- a/src/coastseg/common.py +++ b/src/coastseg/common.py @@ -3373,10 +3373,10 @@ def create_roi_settings( dates = settings["dates"] for roi in selected_rois["features"]: roi_id = str(roi["properties"]["id"]) + polygon = roi["geometry"]["coordinates"] sitename = ( "" if date_str == "" else "ID_" + str(roi_id) + "_datetime" + date_str ) - polygon = roi["geometry"]["coordinates"] inputs_dict = { "dates": dates, "sat_list": sat_list, diff --git a/src/coastseg/extracted_shoreline.py b/src/coastseg/extracted_shoreline.py index db8503d5..a9d4903d 100644 --- a/src/coastseg/extracted_shoreline.py +++ b/src/coastseg/extracted_shoreline.py @@ -559,6 +559,8 @@ def process_satellite_image( Returns: dict: A dictionary containing the extracted shoreline and cloud cover percentage. """ + logger = logging.getLogger('extract_shorelines_logger') + # get image date date = filename[:19] # get the filenames for each of the tif files (ms, pan, qa) @@ -592,15 +594,17 @@ def process_satellite_image( # compute cloud_cover percentage (with no data pixels) cloud_cover_combined = get_cloud_cover_combined(cloud_mask) if cloud_cover_combined > 0.99: # if 99% of cloudy pixels in image skip - logger.info(f"cloud_cover_combined > 0.99 : {filename} ") + logger.info(f"cloud cover and no data covererage exceeds 99% skipping : {filename} ") return None # compute cloud cover percentage (without no data pixels) cloud_cover = get_cloud_cover(cloud_mask, im_nodata) + logger.info(f"{satname} {date} cloud cover : {cloud_cover:.2%}") # skip image if cloud cover is above user-defined threshold if cloud_cover > settings["cloud_thresh"]: - logger.info(f"Cloud thresh exceeded for {filename}") + logger.info(f"Cloud thresh exceeded {cloud_cover} > {settings['cloud_thresh']} skipping {filename}") return None + logger.info(f"The cloud cover was : {cloud_cover}") # calculate a buffer around the reference shoreline (if any has been digitised) # buffer is dilated by 5 pixels ref_shoreline_buffer = SDS_shoreline.create_shoreline_buffer( @@ -612,7 +616,7 @@ def process_satellite_image( npz_file = find_matching_npz(filename, session_path) # logger.info(f"npz_file: {npz_file}") if npz_file is None: - logger.warning(f"npz file not found for {filename}") + logger.warning(f"Npz file not found. Skipping {filename}") return None # get the labels for water and land @@ -1310,7 +1314,7 @@ def find_shoreline( except Exception as e: logger.error(f"{e}\nCould not map shoreline for this image: {filename}") return None - # print(f"Settings used by process_shoreline: {settings}") + # process the water contours into a shoreline shoreline = SDS_shoreline.process_shoreline( contours, cloud_mask_adv, im_nodata, georef, image_epsg, settings @@ -1345,6 +1349,7 @@ def extract_shorelines_with_dask( Returns: dict: A dictionary containing the extracted shorelines for each satellite. """ + logger = logging.getLogger('extract_shorelines_logger') sitename = settings["inputs"]["sitename"] filepath_data = settings["inputs"]["filepath"] @@ -1358,6 +1363,7 @@ def extract_shorelines_with_dask( # get the list of files that were sorted as 'good' filtered_files = get_filtered_files_dict(good_folder, "npz", sitename) + # keep only the metadata for the files that were sorted as 'good' metadata = edit_metadata(metadata, filtered_files) @@ -1874,9 +1880,11 @@ def create_extracted_shorelines_from_session( Returns: - object: The Extracted_Shoreline class instance. """ + # validate input parameters are not empty and are of the correct type self._validate_input_params(roi_id, shoreline, roi_settings, settings) + logger = logging.getLogger('extract_shorelines_logger') logger.info(f"Extracting shorelines for ROI id: {roi_id}") # read model settings from session path @@ -1893,7 +1901,7 @@ def create_extracted_shorelines_from_session( # read model card from downloaded models path downloaded_models_dir = common.get_downloaded_models_dir() downloaded_models_path = os.path.join(downloaded_models_dir, model_type) - logger.info( + logger.info( f"Searching for model card in downloaded_models_path: {downloaded_models_path}" ) model_card_path = file_utilities.find_file_by_regex( @@ -1925,10 +1933,8 @@ def create_extracted_shorelines_from_session( ) # Check and log 'reference_shoreline' if it exists ref_sl = self.shoreline_settings.get("reference_shoreline", np.array([])) - if isinstance(ref_sl, np.ndarray): - logger.info(f"reference_shoreline.shape: {ref_sl.shape}") logger.info( - f"Number of 'reference_shoreline': {len(ref_sl)} for ROI {roi_id}" + f"Length of 'reference_shoreline': {len(ref_sl)} for ROI {roi_id}" ) # gets metadata used to extract shorelines metadata = get_metadata(self.shoreline_settings["inputs"]) @@ -1983,6 +1989,7 @@ def create_extracted_shorelines_from_session( extracted_shorelines_dict = remove_duplicates( extracted_shorelines_dict ) # removes duplicates (images taken on the same date by the same satellite + extracted_shorelines_dict = remove_inaccurate_georef( extracted_shorelines_dict, 10 ) # remove inaccurate georeferencing (set threshold to 10 m) diff --git a/src/coastseg/zoo_model.py b/src/coastseg/zoo_model.py index 5ae6ac90..cfa30d92 100644 --- a/src/coastseg/zoo_model.py +++ b/src/coastseg/zoo_model.py @@ -48,6 +48,36 @@ logger = logging.getLogger(__name__) +def setup_logger( + folder, + base_filename="download_report", + log_format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", +): + # Determine the next available log file number + i = 1 + while True: + log_filename = f"{base_filename}{i}.txt" if i > 1 else f"{base_filename}.txt" + log_filepath = os.path.join(folder, log_filename) + if not os.path.exists(log_filepath): + break + i += 1 + + # Create a custom logger + logger = logging.getLogger("extract_shorelines_logger") + logger.setLevel(logging.INFO) # Log all levels of messages + + # Create handlers + file_handler = logging.FileHandler(log_filepath) + file_handler.setLevel(logging.INFO) + + # Create formatters and add it to handlers + log_format = logging.Formatter(log_format) + file_handler.setFormatter(log_format) + + # Add handlers to the logger + logger.addHandler(file_handler) + + return logger def filter_no_data_pixels(files: list[str], percent_no_data: float = 0.50) -> list[str]: def percentage_of_black_pixels(img: "PIL.Image") -> float: @@ -77,12 +107,12 @@ def has_image_files(file_list, extensions): if file.endswith(".jpg") or file.endswith(".jpeg") or file.endswith(".png"): img = Image.open(file) percentage = percentage_of_black_pixels(img) + if percentage <= percent_no_data: valid_images.append(file) return valid_images - def get_files_to_download( available_files: List[dict], filenames: List[str], model_id: str, model_path: str ) -> dict: @@ -788,9 +818,11 @@ def extract_shorelines_with_unet( shoreline_extraction_area_path (str, optional): The path to the shoreline extraction area. Defaults to "". Returns: None - """ - logger.info(f"extract_shoreline_settings: {settings}") - + """ + # logger.info("extract_shoreline_settings: %s", settings) + + + # save the selected model session settings["model_session_path"] = session_path self.set_settings(**settings) @@ -802,7 +834,18 @@ def extract_shorelines_with_unet( new_session_path.mkdir(parents=True, exist_ok=True) + + logger = setup_logger( + new_session_path, + "extract_shorelines_report", + log_format="%(levelname)s - %(message)s", + ) + # by default assume no ROI + roi_id = None + logger.info(f"Extract Shorelines Settings: {settings}") + # roi_settings = get_roi_settings_from_config(session_path) + # load the ROI settings from the config file try: config = file_utilities.load_json_data_from_file( @@ -824,13 +867,13 @@ def extract_shorelines_with_unet( raise Exception( f"The roi ID {roi_id} did not exist is the config.json \n config.json: {config}" ) - logger.info(f"roi_settings: {roi_settings}") - + + logger.info(f"Settings of the ROI having shorelines extracted: {roi_settings}") # read ROI from config geojson file config_geojson_location = file_utilities.find_file_recursively( session_path, "config_gdf.geojson" ) - logger.info(f"config_geojson_location: {config_geojson_location}") + logger.info(f"Location of config_gdf.geojson: {config_geojson_location}") config_gdf = geodata_processing.read_gpd_file(config_geojson_location) roi_gdf = config_gdf[config_gdf["id"] == roi_id] if roi_gdf.empty: