Spaces:
Configuration error
Configuration error
Download pca_module.py from DrAmar/Remote_sensing: direct link, hf CLI and curl.
- Browser
- Download file 7.67 kB
-
https://huggingface.co/spaces/DrAmar/Remote_sensing/resolve/main/pca_module.py
- Command line
-
hf download hf://spaces/DrAmar/Remote_sensing/pca_module.py
-
curl -L -o pca_module.py https://huggingface.co/spaces/DrAmar/Remote_sensing/resolve/main/pca_module.py
7.67 kB
| import os | |
| import numpy as np | |
| import geopandas as gpd | |
| import pandas as pd | |
| import rasterio | |
| from rasterio.features import shapes | |
| from sklearn.decomposition import PCA | |
| import logging | |
| from tqdm import tqdm | |
| from shapely.geometry import shape | |
| # Configure logging | |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') | |
| # Choose one of the following band mappings based on selected bands for PCA | |
| band_mapping_case1 = {2: 0, 4: 1, 5: 2, 6: 3} # For Bands 2, 4, 5, and 6 | |
| band_mapping_case2 = {2: 0, 5: 1, 6: 2, 7: 3} # For Bands 2, 5, 6, and 7 | |
| # Choose band mapping (adjust as needed) | |
| band_mapping = band_mapping_case1 | |
| # Function to read specific band file | |
| def read_band_file(input_directory, scene_name, band_index): | |
| band_filename = f"{scene_name}_SR_B{band_index}.TIF" | |
| band_path = os.path.join(input_directory, band_filename) | |
| if not os.path.exists(band_path): | |
| logging.error(f"File not found: {band_path}") | |
| return None | |
| with rasterio.open(band_path) as src: | |
| return src.read(1), src.transform, src.crs | |
| # Function to stack bands for PCA | |
| def stack_bands(scene_name, input_directory, output_directory, bands): | |
| stacked_bands, transform, crs = [], None, None | |
| for band_index in bands: | |
| result = read_band_file(input_directory, scene_name, band_index) | |
| if result is not None: | |
| band_data, transform, crs = result | |
| stacked_bands.append(band_data) | |
| else: | |
| logging.warning(f"Skipping band {band_index} for scene {scene_name} due to missing file.") | |
| if len(stacked_bands) == len(bands): | |
| stacked_array = np.stack(stacked_bands, axis=-1) | |
| stacked_path = os.path.join(output_directory, f"{scene_name}_stacked.tif") | |
| with rasterio.open(stacked_path, 'w', driver='GTiff', height=stacked_array.shape[0], | |
| width=stacked_array.shape[1], count=len(bands), dtype=stacked_array.dtype, | |
| crs=crs, transform=transform) as dst: | |
| for i in range(len(bands)): | |
| dst.write(stacked_array[..., i], i + 1) | |
| logging.info(f"Stacked bands saved at: {stacked_path}") | |
| return stacked_path | |
| else: | |
| logging.warning(f"Could not stack all bands for scene: {scene_name}") | |
| return None | |
| # Function to perform PCA on stacked bands | |
| def perform_pca(stacked_path): | |
| with rasterio.open(stacked_path) as src: | |
| data = src.read() | |
| reshaped_data = data.reshape(data.shape[0], -1).T | |
| pca = PCA(n_components=min(data.shape[0], data.shape[2])) | |
| pca_results = pca.fit_transform(reshaped_data) | |
| loadings_df = pd.DataFrame(pca.components_.T, columns=[f'PC{i + 1}' for i in range(pca.n_components_)]) | |
| return pca_results.reshape(data.shape[1], data.shape[2], -1), loadings_df | |
| # Function to analyze principal components | |
| def analyze_principal_components(loadings, criteria): | |
| selected_pcs = {} | |
| for mineral, bands in criteria.items(): | |
| found = False | |
| for pc_index in range(loadings.shape[0]): | |
| if all((loadings[pc_index, band_mapping[band_index]] > 0 if sign == '+' else loadings[pc_index, band_mapping[band_index]] < 0) | |
| for band_index, sign in bands.items() if band_index in band_mapping): | |
| selected_pcs[mineral] = pc_index + 1 | |
| logging.info(f"Selected PC for {mineral} is PC{pc_index + 1}.") | |
| found = True | |
| break | |
| if not found: | |
| logging.warning(f"No suitable PC found for {mineral}.") | |
| return selected_pcs | |
| # Function to log PCA statistics | |
| def log_pca_stats(loadings_df, selected_pcs, output_directory, scene_name): | |
| stats = [] | |
| for mineral, pc_index in selected_pcs.items(): | |
| pc_column = loadings_df[f'PC{pc_index}'] | |
| mean_val = pc_column.mean() | |
| std_val = pc_column.std() | |
| logging.info(f"{mineral} - PC{pc_index}: Mean={mean_val}, Std Dev={std_val}") | |
| stats.append({ | |
| 'Mineral': mineral, | |
| 'Selected_PC': f'PC{pc_index}', | |
| 'Mean_Loading': mean_val, | |
| 'Std_Dev_Loading': std_val, | |
| **{f'Band_{col}': loadings_df.iloc[band_mapping[col], pc_index - 1] for col in band_mapping.keys()} | |
| }) | |
| stats_df = pd.DataFrame(stats) | |
| stats_csv_path = os.path.join(output_directory, f"{scene_name}_PCA_Loading_Stats.csv") | |
| stats_df.to_csv(stats_csv_path, index=False) | |
| logging.info(f"PCA loading values and statistics saved at: {stats_csv_path}") | |
| # Function to apply thresholds and convert to vector | |
| def apply_threshold_and_convert_to_vector(data, mean, std_dev, threshold_levels, output_shapefile_base, transform, crs): | |
| for label, multiplier in threshold_levels.items(): | |
| threshold = mean + multiplier * std_dev | |
| mask = data > threshold | |
| with rasterio.Env(): | |
| shapes_gen = shapes(mask.astype(np.uint8), transform=transform) | |
| polygons = [shape(geom) for geom, val in shapes_gen if val == 1] | |
| shapefile_path = f"{output_shapefile_base}_{label}.shp" | |
| gdf = gpd.GeoDataFrame(geometry=polygons, crs=crs) | |
| gdf.to_file(shapefile_path) | |
| logging.info(f"Shapefile created for threshold {label} at: {shapefile_path}") | |
| # Main function to process a single scene | |
| def process_single_scene(scene_name, input_directory, output_directory, mineral_criteria, threshold_levels): | |
| bands = [2, 4, 5, 6] if band_mapping == band_mapping_case1 else [2, 5, 6, 7] | |
| stacked_path = stack_bands(scene_name, input_directory, output_directory, bands) | |
| if stacked_path is not None: | |
| with rasterio.open(stacked_path) as src: | |
| transform, crs = src.transform, src.crs | |
| pca_results, loadings_df = perform_pca(stacked_path) | |
| selected_pcs = analyze_principal_components(loadings_df.values, mineral_criteria) | |
| log_pca_stats(loadings_df, selected_pcs, output_directory, scene_name) | |
| for mineral, pc_index in selected_pcs.items(): | |
| pc_data = pca_results[:, :, pc_index - 1] | |
| mean, std_dev = np.mean(pc_data), np.std(pc_data) | |
| shapefile_path_base = os.path.join(output_directory, f"{scene_name}_{mineral}") | |
| apply_threshold_and_convert_to_vector(pc_data, mean, std_dev, threshold_levels, shapefile_path_base, transform, crs) | |
| # Batch process function for all files in the directory | |
| def batch_process_scenes(input_directory, output_directory, mineral_criteria, threshold_levels): | |
| processed_scenes = set() | |
| tif_files = [file for file in os.listdir(input_directory) if file.endswith('.TIF')] | |
| for file in tqdm(tif_files, desc="Processing scenes"): | |
| scene_name = "_".join(file.split("_")[:-2]) | |
| if scene_name not in processed_scenes: | |
| process_single_scene(scene_name, input_directory, output_directory, mineral_criteria, threshold_levels) | |
| processed_scenes.add(scene_name) | |
| # Main execution block for standalone use | |
| if __name__ == "__main__": | |
| input_directory = r'D:\RS\Arabian Shield\Complied data\Data for BR\segregated_files\SR' | |
| output_directory = r'D:\RS\Arabian Shield\Complied data\Processed_PCA' | |
| mineral_criteria = { | |
| 'Iron_Oxides': {2: '+', 4: '-'}, | |
| 'Hydroxyl_Alteration': {2: '+', 5: '+', 6: '-', 7: '+'}, | |
| 'Limonite': {2: '+', 5: '-'} | |
| } | |
| threshold_levels = {'98%': 3, '95%': 2, '92%': 1} | |
| batch_process_scenes(input_directory, output_directory, mineral_criteria, threshold_levels) | |
| logging.info("Processing completed!") | |