ishans24 commited on
Commit
b4f8060
·
verified ·
1 Parent(s): b4c714f

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +372 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,374 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import tensorflow as tf
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ from PIL import Image
6
+ from pathlib import Path
7
+ import io
8
+
9
+ from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation, \
10
+ MaxPool2D, UpSampling2D, concatenate, \
11
+ Input, Conv2DTranspose, MaxPooling2D, \
12
+ Dropout, BatchNormalization
13
+ from tensorflow.keras.models import Model
14
+
15
+
16
+ # ===========================
17
+ # Model Architecture
18
+ # ===========================
19
+
20
+ def conv2d_block(input_tensor, n_filters, kernel_size=3, batchnorm=True, sublayers=2):
21
+ '''In case batchnorm=False "if" statement will be skipped and in amount of "sublayers" convolutional layers will be created.'''
22
+ for idx in range(sublayers):
23
+ conv = Conv2D(filters=n_filters, kernel_size=(kernel_size, kernel_size),
24
+ kernel_initializer="he_normal", padding="same")(input_tensor if idx == 0 else conv)
25
+ if batchnorm:
26
+ normalized = BatchNormalization()(conv)
27
+ conv = Activation("relu")(conv)
28
+ return conv
29
+
30
+
31
+ def conv2d_transpose_block(input_tensor, concatenate_tensor, n_filters, kernel_size=3, strides=2, transpose=False):
32
+ if transpose:
33
+ conv = Conv2DTranspose(n_filters, (kernel_size, kernel_size),
34
+ strides=(strides, strides), padding='same')(input_tensor)
35
+ else:
36
+ conv = Conv2D(n_filters, (kernel_size, kernel_size), activation='relu', padding='same',
37
+ kernel_initializer='he_normal')(UpSampling2D(size=(kernel_size, kernel_size))(input_tensor))
38
+ conv = Activation("relu")(conv)
39
+ concatenation = concatenate([conv, concatenate_tensor])
40
+ return concatenation
41
+
42
+
43
+ def build_unet(input_shape=(512, 512, 3), filters=[16, 32, 64, 128, 256], batchnorm=True, transpose=False,
44
+ dropout_flag=False):
45
+ conv_dict = dict()
46
+ inputs = Input(input_shape)
47
+ dropout_rate = 0.5
48
+
49
+ for idx, n_filters in enumerate(filters[:-1]):
50
+ conv = conv2d_block(inputs if n_filters == filters[0] else max_pool,
51
+ n_filters=n_filters, kernel_size=3,
52
+ batchnorm=batchnorm)
53
+ max_pool = MaxPooling2D((2, 2))(conv)
54
+ conv_dict[f"conv2d_{idx + 1}"] = conv
55
+
56
+ conv_middle = conv2d_block(max_pool, n_filters=filters[-1], kernel_size=3, batchnorm=batchnorm)
57
+
58
+ for idx, n_filters in enumerate(reversed(filters[:-1])):
59
+ concatenation = conv2d_transpose_block(conv_middle if idx == 0 else conv,
60
+ conv_dict[f"conv2d_{len(conv_dict) - idx}"],
61
+ n_filters, kernel_size=2, strides=2, transpose=transpose)
62
+ conv = conv2d_block(concatenation, n_filters=n_filters, kernel_size=3,
63
+ batchnorm=batchnorm)
64
+
65
+ outputs = Conv2D(3, (1, 1), activation='softmax')(conv)
66
+ model = Model(inputs=inputs, outputs=outputs)
67
+ return model
68
+
69
+
70
+ # ===========================
71
+ # Model Loading (Cached)
72
+ # ===========================
73
+
74
+ @st.cache_resource
75
+ def load_model():
76
+ """Load the pre-trained U-Net model"""
77
+ model = build_unet(input_shape=(512, 512, 3),
78
+ filters=[2 ** i for i in range(5, int(np.log2(2048) + 1))],
79
+ batchnorm=False, transpose=False, dropout_flag=False)
80
+
81
+ weights_path = Path("src/forest-cover-model-v1.h5")
82
+ model.load_weights(str(weights_path))
83
+ return model
84
+
85
+
86
+ # ===========================
87
+ # Prediction Function
88
+ # ===========================
89
+
90
+ def predict_and_calculate(image, model):
91
+ """Process image and calculate forest cover"""
92
+ # Resize and normalize
93
+ img = image.convert("RGB")
94
+ img = img.resize((512, 512))
95
+ img_np = np.array(img) / 255.0
96
+
97
+ # Predict
98
+ pred = model.predict(img_np[np.newaxis, ...], verbose=0)[0]
99
+ pred_class = np.argmax(pred, axis=-1)
100
+
101
+ # Forest class = 0
102
+ forest_mask = (pred_class == 0).astype(np.uint8)
103
+
104
+ # Calculate forest cover percentage
105
+ total_pixels = forest_mask.size
106
+ forest_pixels = np.sum(forest_mask)
107
+ forest_cover = (forest_pixels / total_pixels) * 100
108
+
109
+ return img_np, forest_mask, forest_cover
110
+
111
+
112
+ def create_visualization(image_np, forest_mask, forest_cover):
113
+ """Create visualization with original, mask, and overlay"""
114
+ # Create colored mask
115
+ color_mask = np.zeros((512, 512, 3), dtype=np.uint8)
116
+ color_mask[forest_mask == 1] = [0, 255, 0] # GREEN = Forest
117
+
118
+ # Create overlay
119
+ overlay = image_np.copy()
120
+ overlay[forest_mask == 1] = overlay[forest_mask == 1] * 0.5 + np.array([0, 1, 0]) * 0.5
121
+
122
+ # Create matplotlib figure
123
+ fig, axes = plt.subplots(1, 3, figsize=(15, 5))
124
+
125
+ axes[0].imshow(image_np)
126
+ axes[0].set_title("Original Image", fontsize=12, fontweight='bold')
127
+ axes[0].axis("off")
128
+
129
+ axes[1].imshow(color_mask)
130
+ axes[1].set_title("Forest Cover Mask", fontsize=12, fontweight='bold')
131
+ axes[1].axis("off")
132
+
133
+ axes[2].imshow(overlay)
134
+ axes[2].set_title("Overlay Visualization", fontsize=12, fontweight='bold')
135
+ axes[2].axis("off")
136
+
137
+ plt.tight_layout()
138
+ return fig
139
+
140
+
141
+ # ===========================
142
+ # Streamlit App Configuration
143
+ # ===========================
144
+
145
+ st.set_page_config(
146
+ page_title="Forest Cover Assessment System",
147
+ page_icon="🌲",
148
+ layout="wide",
149
+ initial_sidebar_state="expanded"
150
+ )
151
+
152
+ # Custom CSS for professional government theme
153
+ st.markdown("""
154
+ <style>
155
+ .main-header {
156
+ font-size: 42px;
157
+ font-weight: bold;
158
+ color: #1e3a5f;
159
+ text-align: center;
160
+ padding: 20px 0;
161
+ border-bottom: 3px solid #2e5c8a;
162
+ margin-bottom: 30px;
163
+ }
164
+ .sub-header {
165
+ font-size: 18px;
166
+ color: #4a4a4a;
167
+ text-align: center;
168
+ margin-bottom: 40px;
169
+ }
170
+ .metric-container {
171
+ background-color: #f0f4f8;
172
+ padding: 20px;
173
+ border-radius: 10px;
174
+ border-left: 5px solid #2e5c8a;
175
+ margin: 10px 0;
176
+ }
177
+ .metric-value {
178
+ font-size: 36px;
179
+ font-weight: bold;
180
+ color: #1e3a5f;
181
+ }
182
+ .metric-label {
183
+ font-size: 14px;
184
+ color: #666;
185
+ text-transform: uppercase;
186
+ }
187
+ .footer {
188
+ text-align: center;
189
+ color: #666;
190
+ padding: 20px 0;
191
+ margin-top: 50px;
192
+ border-top: 1px solid #ddd;
193
+ font-size: 12px;
194
+ }
195
+ .info-box {
196
+ background-color: #e8f4f8;
197
+ padding: 15px;
198
+ border-radius: 5px;
199
+ border-left: 4px solid #2196F3;
200
+ margin: 10px 0;
201
+ }
202
+ </style>
203
+ """, unsafe_allow_html=True)
204
+
205
+
206
+ # ===========================
207
+ # Main Application
208
+ # ===========================
209
+
210
+ def main():
211
+ # Header
212
+ st.markdown('<div class="main-header">Forest Cover Assessment System</div>', unsafe_allow_html=True)
213
+ st.markdown(
214
+ '<div class="sub-header">Advanced Satellite Imagery Analysis for Forest Conservation & Monitoring</div>',
215
+ unsafe_allow_html=True)
216
+
217
+ # Sidebar
218
+ with st.sidebar:
219
+ st.image("https://via.placeholder.com/250x80/1e3a5f/ffffff?text=Forest+Division", use_container_width=True)
220
+ st.markdown("### About This System")
221
+ st.markdown("""
222
+ This system utilizes deep learning technology to analyze satellite
223
+ imagery and accurately assess forest cover percentages in designated areas.
224
+
225
+ **Key Features:**
226
+ - Automated forest detection
227
+ - High-precision segmentation
228
+ - Multi-image batch processing
229
+ - Visual overlay analysis
230
+
231
+ **Supported Formats:**
232
+ - PNG, JPG, JPEG
233
+ - Recommended: High-resolution satellite imagery
234
+ """)
235
+
236
+ st.markdown("---")
237
+ st.markdown("**System Status:** Operational")
238
+ st.markdown("**Model Version:** v1.0")
239
+
240
+ # Information box
241
+ st.markdown("""
242
+ <div class="info-box">
243
+ <strong>Instructions:</strong> Upload one or more satellite images (PNG, JPG, JPEG) to analyze forest cover.
244
+ The system will process each image and provide detailed forest coverage metrics along with visual overlays.
245
+ </div>
246
+ """, unsafe_allow_html=True)
247
+
248
+ # File uploader
249
+ uploaded_files = st.file_uploader(
250
+ "Upload Satellite Images",
251
+ type=["png", "jpg", "jpeg"],
252
+ accept_multiple_files=True,
253
+ help="Select one or more images for forest cover analysis"
254
+ )
255
+
256
+ if uploaded_files:
257
+ st.markdown("---")
258
+ st.markdown(f"### Analysis Results ({len(uploaded_files)} image(s) uploaded)")
259
+
260
+ # Load model
261
+ with st.spinner("Loading AI model..."):
262
+ model = load_model()
263
+
264
+ # Process each uploaded image
265
+ total_forest_cover = 0
266
+
267
+ for idx, uploaded_file in enumerate(uploaded_files, 1):
268
+ st.markdown(f"#### Image {idx}: {uploaded_file.name}")
269
+
270
+ # Load image
271
+ image = Image.open(uploaded_file)
272
+
273
+ # Process image
274
+ with st.spinner(f"Processing {uploaded_file.name}..."):
275
+ img_np, forest_mask, forest_cover = predict_and_calculate(image, model)
276
+ total_forest_cover += forest_cover
277
+
278
+ # Display metrics
279
+ col1, col2, col3 = st.columns(3)
280
+
281
+ with col1:
282
+ st.markdown(f"""
283
+ <div class="metric-container">
284
+ <div class="metric-label">Forest Coverage</div>
285
+ <div class="metric-value">{forest_cover:.2f}%</div>
286
+ </div>
287
+ """, unsafe_allow_html=True)
288
+
289
+ with col2:
290
+ st.markdown(f"""
291
+ <div class="metric-container">
292
+ <div class="metric-label">Non-Forest Area</div>
293
+ <div class="metric-value">{100 - forest_cover:.2f}%</div>
294
+ </div>
295
+ """, unsafe_allow_html=True)
296
+
297
+ with col3:
298
+ forest_pixels = np.sum(forest_mask)
299
+ total_pixels = forest_mask.size
300
+ st.markdown(f"""
301
+ <div class="metric-container">
302
+ <div class="metric-label">Forest Pixels</div>
303
+ <div class="metric-value">{forest_pixels:,}/{total_pixels:,}</div>
304
+ </div>
305
+ """, unsafe_allow_html=True)
306
+
307
+ # Create and display visualization
308
+ fig = create_visualization(img_np, forest_mask, forest_cover)
309
+ st.pyplot(fig)
310
+ plt.close(fig)
311
+
312
+ # Add status indicator
313
+ if forest_cover >= 70:
314
+ status = "Excellent Forest Coverage"
315
+ color = "#4CAF50"
316
+ elif forest_cover >= 40:
317
+ status = "Moderate Forest Coverage"
318
+ color = "#FF9800"
319
+ else:
320
+ status = "Low Forest Coverage - Attention Required"
321
+ color = "#F44336"
322
+
323
+ st.markdown(f"""
324
+ <div style="background-color: {color}20; padding: 10px; border-radius: 5px; border-left: 4px solid {color};">
325
+ <strong>Assessment: {status}</strong>
326
+ </div>
327
+ """, unsafe_allow_html=True)
328
+
329
+ st.markdown("---")
330
+
331
+ # Summary statistics
332
+ if len(uploaded_files) > 1:
333
+ avg_forest_cover = total_forest_cover / len(uploaded_files)
334
+ st.markdown("### Summary Statistics")
335
+
336
+ summary_col1, summary_col2, summary_col3 = st.columns(3)
337
+
338
+ with summary_col1:
339
+ st.metric("Total Images Processed", len(uploaded_files))
340
+
341
+ with summary_col2:
342
+ st.metric("Average Forest Coverage", f"{avg_forest_cover:.2f}%")
343
+
344
+ with summary_col3:
345
+ st.metric("Total Forest Coverage", f"{total_forest_cover:.2f}%")
346
+
347
+ else:
348
+ # Show example information when no files uploaded
349
+ st.info("Please upload satellite images to begin forest cover analysis")
350
+
351
+ # Optional: Display example images if they exist
352
+ example_dir = Path("src")
353
+ example_images = list(example_dir.glob("image-*.png"))
354
+
355
+ if example_images:
356
+ with st.expander("View Example Images"):
357
+ cols = st.columns(min(3, len(example_images)))
358
+ for idx, img_path in enumerate(example_images[:3]):
359
+ with cols[idx]:
360
+ img = Image.open(img_path)
361
+ st.image(img, caption=img_path.name, use_container_width=True)
362
+
363
+ # Footer
364
+ st.markdown("""
365
+ <div class="footer">
366
+ <strong>Forest Cover Assessment System</strong> | Powered by Deep Learning & Satellite Imagery Analysis<br>
367
+ For official use by authorized government personnel only<br>
368
+ © 2026 Forest Conservation Division
369
+ </div>
370
+ """, unsafe_allow_html=True)
371
+
372
 
373
+ if __name__ == "__main__":
374
+ main()