Update app.py
Browse files
app.py
CHANGED
|
@@ -158,31 +158,19 @@ def get_transform_from_tif(tif_path):
|
|
| 158 |
crs = src.crs
|
| 159 |
return transform, crs
|
| 160 |
|
| 161 |
-
def mask_to_polygons(mask, transform,
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
Args:
|
| 166 |
-
mask (np.ndarray): Predicted mask (H, W) with class indices.
|
| 167 |
-
transform (Affine): Georeferencing transform from the input TIF.
|
| 168 |
-
target_class_id (int): The class ID to convert into polygons.
|
| 169 |
-
|
| 170 |
-
Returns:
|
| 171 |
-
gpd.GeoDataFrame: Geodataframe containing polygons for the target class.
|
| 172 |
-
"""
|
| 173 |
-
# Binary mask for the target class
|
| 174 |
-
binary_mask = (mask == target_class_id).astype(np.uint8)
|
| 175 |
|
| 176 |
-
# Extract
|
| 177 |
results = (
|
| 178 |
-
{'properties': {'class':
|
| 179 |
-
for
|
| 180 |
-
if
|
| 181 |
)
|
| 182 |
|
|
|
|
| 183 |
geoms = list(results)
|
| 184 |
-
|
| 185 |
-
# Return empty GeoDataFrame if no shapes found
|
| 186 |
if not geoms:
|
| 187 |
return gpd.GeoDataFrame(columns=['geometry'], geometry='geometry', crs='EPSG:4326')
|
| 188 |
|
|
@@ -191,21 +179,23 @@ def mask_to_polygons(mask, transform, target_class_id):
|
|
| 191 |
return gdf
|
| 192 |
|
| 193 |
|
| 194 |
-
|
| 195 |
-
|
|
|
|
| 196 |
mining_gdf = mining_gdf.to_crs(wiup_gdf.crs)
|
| 197 |
|
| 198 |
-
#
|
| 199 |
-
|
| 200 |
|
| 201 |
# Convert to metric CRS (e.g., UTM) to calculate area in m²
|
| 202 |
-
metric_crs = 'EPSG:4326' # UTM zone for SE Asia (adjust based on location)
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
|
|
|
| 207 |
|
| 208 |
-
return
|
| 209 |
|
| 210 |
# Streamlit App Title
|
| 211 |
st.title("Satellite Mining Segmentation: SAR + Optic Image Inference")
|
|
@@ -365,28 +355,19 @@ if st.button("Run Inference"):
|
|
| 365 |
transform, crs = get_transform_from_tif(optic_path)
|
| 366 |
|
| 367 |
# Convert mask to mining polygons
|
| 368 |
-
|
| 369 |
-
mining_gdf = mask_to_polygons(pred_label_mask, transform, target_class_id=1)
|
| 370 |
-
beach_gdf = mask_to_polygons(pred_label_mask, transform, target_class_id=2)
|
| 371 |
-
|
| 372 |
-
st.success(f"Non-mining polygons: {len(non_mining_gdf)}")
|
| 373 |
st.success(f"Mining polygons: {len(mining_gdf)}")
|
| 374 |
-
|
| 375 |
|
| 376 |
# Make sure WIUP and prediction are in same CRS
|
| 377 |
wiup_gdf = wiup_gdf.to_crs(mining_gdf.crs)
|
| 378 |
st.success(f"WIUP CRS: {wiup_gdf.crs}")
|
| 379 |
|
| 380 |
# Find area
|
| 381 |
-
|
| 382 |
-
mining_area, mining_area_gdf = calculate_area(mining_gdf, wiup_gdf)
|
| 383 |
-
beach_area, beach_area_gdf = calculate_area(beach_gdf, wiup_gdf)
|
| 384 |
|
| 385 |
# Display in Streamlit
|
| 386 |
-
st.success(f"
|
| 387 |
-
st.success(f" Mining Area : {mining_area/1e6:.2f} sq. km")
|
| 388 |
-
st.success(f" Beach Area : {beach_area/1e6:.2f} sq. km")
|
| 389 |
-
|
| 390 |
|
| 391 |
else:
|
| 392 |
st.warning("Please upload all three .tiff files to proceed.")
|
|
|
|
| 158 |
crs = src.crs
|
| 159 |
return transform, crs
|
| 160 |
|
| 161 |
+
def mask_to_polygons(mask, transform, mining_class_id=1):
|
| 162 |
+
# Create a binary mask for mining areas
|
| 163 |
+
mining_mask = (mask == mining_class_id).astype(np.uint8)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
|
| 165 |
+
# Extract shapes (polygons) from the mask
|
| 166 |
results = (
|
| 167 |
+
{'properties': {'class': 'mining_land'}, 'geometry': s}
|
| 168 |
+
for s, v in shapes(mining_mask, mask=None, transform=transform)
|
| 169 |
+
if v == 1 # Only keep areas marked as mining
|
| 170 |
)
|
| 171 |
|
| 172 |
+
# Create a GeoDataFrame from the polygon results
|
| 173 |
geoms = list(results)
|
|
|
|
|
|
|
| 174 |
if not geoms:
|
| 175 |
return gpd.GeoDataFrame(columns=['geometry'], geometry='geometry', crs='EPSG:4326')
|
| 176 |
|
|
|
|
| 179 |
return gdf
|
| 180 |
|
| 181 |
|
| 182 |
+
# Function to calculate illegal mining area (mining outside WIUP)
|
| 183 |
+
def calculate_illegal_area(mining_gdf, wiup_gdf):
|
| 184 |
+
# Ensure both are in the same CRS
|
| 185 |
mining_gdf = mining_gdf.to_crs(wiup_gdf.crs)
|
| 186 |
|
| 187 |
+
# Perform overlay operation: difference (mining area outside WIUP)
|
| 188 |
+
illegal_area_gdf = gpd.overlay(mining_gdf, wiup_gdf, how='difference')
|
| 189 |
|
| 190 |
# Convert to metric CRS (e.g., UTM) to calculate area in m²
|
| 191 |
+
metric_crs = ' EPSG:4326' # UTM zone for SE Asia (adjust based on location)
|
| 192 |
+
illegal_area_gdf = illegal_area_gdf.to_crs(metric_crs)
|
| 193 |
+
|
| 194 |
+
# Calculate the area in square meters
|
| 195 |
+
illegal_area_gdf['area_m2'] = illegal_area_gdf.geometry.area
|
| 196 |
+
total_illegal_area = illegal_area_gdf['area_m2'].sum()
|
| 197 |
|
| 198 |
+
return total_illegal_area, illegal_area_gdf
|
| 199 |
|
| 200 |
# Streamlit App Title
|
| 201 |
st.title("Satellite Mining Segmentation: SAR + Optic Image Inference")
|
|
|
|
| 355 |
transform, crs = get_transform_from_tif(optic_path)
|
| 356 |
|
| 357 |
# Convert mask to mining polygons
|
| 358 |
+
mining_gdf = mask_to_polygons(pred_label_mask, transform, mining_class_id=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
st.success(f"Mining polygons: {len(mining_gdf)}")
|
| 360 |
+
|
| 361 |
|
| 362 |
# Make sure WIUP and prediction are in same CRS
|
| 363 |
wiup_gdf = wiup_gdf.to_crs(mining_gdf.crs)
|
| 364 |
st.success(f"WIUP CRS: {wiup_gdf.crs}")
|
| 365 |
|
| 366 |
# Find area
|
| 367 |
+
mining_area, mining_area_gdf = calculate_illegal_area(mining_gdf, wiup_gdf)
|
|
|
|
|
|
|
| 368 |
|
| 369 |
# Display in Streamlit
|
| 370 |
+
st.success(f" Illegal Mining Area : {mining_area/1e6:.2f} sq. km")
|
|
|
|
|
|
|
|
|
|
| 371 |
|
| 372 |
else:
|
| 373 |
st.warning("Please upload all three .tiff files to proceed.")
|