mouadenna commited on
Commit
54c7430
·
verified ·
1 Parent(s): 49d38c8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -73
app.py CHANGED
@@ -7,111 +7,105 @@ from rasterio.windows import Window
7
  from tqdm.auto import tqdm
8
  import io
9
  import zipfile
10
-
11
- # Assuming you have these functions defined elsewhere
12
- import torch
13
- import numpy as np
14
- from PIL import Image
15
  import albumentations as albu
16
  import segmentation_models_pytorch as smp
17
  from albumentations.pytorch.transforms import ToTensorV2
18
-
 
 
 
19
 
20
 
21
  DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
22
  ENCODER = 'se_resnext50_32x4d'
23
  ENCODER_WEIGHTS = 'imagenet'
24
 
 
25
  # Load and prepare the model
26
- best_model = torch.load('deeplabv3+ v15.pth', map_location=DEVICE)
27
- best_model.eval().float()
 
 
 
 
 
 
 
28
 
29
  def to_tensor(x, **kwargs):
30
- return x.astype('float32')#.transpose(2, 0, 1)
 
31
 
32
  # Preprocessing
33
  preprocessing_fn = smp.encoders.get_preprocessing_fn(ENCODER, ENCODER_WEIGHTS)
34
 
 
35
  def get_preprocessing():
36
  _transform = [
37
  albu.Resize(512, 512),
38
  albu.Lambda(image=preprocessing_fn),
39
  albu.Lambda(image=to_tensor, mask=to_tensor),
40
  ToTensorV2(),
41
- #albu.Normalize(mean=MEAN,std=STD)
42
-
43
  ]
44
  return albu.Compose(_transform)
45
 
46
 
47
  preprocess = get_preprocessing()
48
 
 
49
  @torch.no_grad()
50
  def process_and_predict(image, model):
51
- # Convert PIL Image to numpy array if necessary
52
  if isinstance(image, Image.Image):
53
  image = np.array(image)
54
-
55
- # Ensure image is 3-channel
56
  if image.ndim == 2:
57
  image = np.stack([image] * 3, axis=-1)
58
  elif image.shape[2] == 4:
59
  image = image[:, :, :3]
60
-
61
- # Apply preprocessing
62
  preprocessed = preprocess(image=image)['image']
63
- #preprocessed=torch.tensor(preprocessed)
64
- # Add batch dimension and move to device
65
  input_tensor = preprocessed.unsqueeze(0).to(DEVICE)
66
 
67
- print(input_tensor.shape)
68
- # Predict
69
  mask = model(input_tensor)
70
  mask = torch.sigmoid(mask)
71
  mask = (mask > 0.6).float()
72
-
73
- # Convert to PIL Image
74
  mask_image = Image.fromarray((mask.squeeze().cpu().numpy() * 255).astype(np.uint8))
75
-
76
- return mask_image
77
 
78
- #example
79
- def main(image_path):
80
- image = Image.open(image_path)
81
- mask = process_and_predict(image, best_model)
82
- return mask
83
 
84
 
85
  def extract_tiles(map_file, model, tile_size=512, overlap=0, batch_size=4):
86
  tiles = []
87
-
88
  with rasterio.open(map_file) as src:
89
  height = src.height
90
  width = src.width
91
-
92
  effective_tile_size = tile_size - overlap
93
-
94
  for y in tqdm(range(0, height, effective_tile_size)):
95
  for x in range(0, width, effective_tile_size):
96
  batch_images = []
97
  batch_metas = []
98
-
99
  for i in range(batch_size):
100
  curr_y = y + (i * effective_tile_size)
101
  if curr_y >= height:
102
  break
103
-
104
  window = Window(x, curr_y, tile_size, tile_size)
105
  out_image = src.read(window=window)
106
-
107
  if out_image.shape[0] == 1:
108
  out_image = np.repeat(out_image, 3, axis=0)
109
  elif out_image.shape[0] != 3:
110
  raise ValueError("The number of channels in the image is not supported")
111
-
112
  out_image = np.transpose(out_image, (1, 2, 0))
113
  tile_image = Image.fromarray(out_image.astype(np.uint8))
114
-
115
  out_meta = src.meta.copy()
116
  out_meta.update({
117
  "driver": "GTiff",
@@ -120,34 +114,66 @@ def extract_tiles(map_file, model, tile_size=512, overlap=0, batch_size=4):
120
  "transform": rasterio.windows.transform(window, src.transform)
121
  })
122
  tile_image = np.array(tile_image)
123
-
124
  preprocessed_tile = preprocess(image=tile_image)['image']
125
  batch_images.append(preprocessed_tile)
126
  batch_metas.append(out_meta)
127
-
128
  if not batch_images:
129
  break
130
-
131
- # Concatenate batch images
132
  batch_tensor = torch.cat([img.unsqueeze(0).to(DEVICE) for img in batch_images], dim=0)
133
- # Perform inference on the batch
134
  with torch.no_grad():
135
  batch_masks = model(batch_tensor.to(DEVICE))
136
-
137
  batch_masks = torch.sigmoid(batch_masks)
138
  batch_masks = (batch_masks > 0.6).float()
139
-
140
- # Process each mask in the batch
141
  for j, mask_tensor in enumerate(batch_masks):
142
- mask_resized = torch.nn.functional.interpolate(mask_tensor.unsqueeze(0), size=(tile_size, tile_size), mode='bilinear', align_corners=False).squeeze(0)
143
-
 
 
144
  mask_array = mask_resized.squeeze().cpu().numpy()
145
-
146
  if mask_array.any() == 1:
147
  tiles.append([mask_array, batch_metas[j]])
148
-
149
  return tiles
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  def main():
152
  st.title("TIF File Processor")
153
 
@@ -156,38 +182,45 @@ def main():
156
  if uploaded_file is not None:
157
  st.write("File uploaded successfully!")
158
 
159
- # Process button
160
  if st.button("Process File"):
161
  st.write("Processing...")
162
-
163
- # Save the uploaded file temporarily
164
  with open("temp.tif", "wb") as f:
165
  f.write(uploaded_file.getbuffer())
166
-
167
- # Process the file
168
  best_model.float()
169
  tiles = extract_tiles("temp.tif", best_model, tile_size=512, overlap=15, batch_size=4)
170
-
171
  st.write("Processing complete!")
172
 
173
- # Prepare zip file for download
174
- zip_buffer = io.BytesIO()
175
- with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zip_file:
176
- for i, (mask_array, meta) in enumerate(tiles):
177
- # Save each tile as a separate TIF file
178
- with rasterio.open(f"tile_{i}.tif", 'w', **meta) as dst:
179
- dst.write(mask_array, 1)
180
-
181
- # Add the tile to the zip file
182
- zip_file.write(f"tile_{i}.tif")
183
-
184
- # Offer the zip file for download
185
- st.download_button(
186
- label="Download processed tiles",
187
- data=zip_buffer.getvalue(),
188
- file_name="processed_tiles.zip",
189
- mime="application/zip"
190
- )
 
 
 
 
 
 
 
 
 
 
191
 
192
  if __name__ == "__main__":
193
  main()
 
7
  from tqdm.auto import tqdm
8
  import io
9
  import zipfile
10
+ import os
 
 
 
 
11
  import albumentations as albu
12
  import segmentation_models_pytorch as smp
13
  from albumentations.pytorch.transforms import ToTensorV2
14
+ import geopandas as gpd
15
+ from shapely.geometry import shape
16
+ from shapely.ops import unary_union
17
+ from rasterio.features import shapes
18
 
19
 
20
  DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
21
  ENCODER = 'se_resnext50_32x4d'
22
  ENCODER_WEIGHTS = 'imagenet'
23
 
24
+
25
  # Load and prepare the model
26
+ @st.cache_resource
27
+ def load_model():
28
+ model = torch.load(r'C:\Users\MOUAD\Documents\PV panels\application\deeplabv3+ v15.pth', map_location=DEVICE)
29
+ model.eval().float()
30
+ return model
31
+
32
+
33
+ best_model = load_model()
34
+
35
 
36
  def to_tensor(x, **kwargs):
37
+ return x.astype('float32')
38
+
39
 
40
  # Preprocessing
41
  preprocessing_fn = smp.encoders.get_preprocessing_fn(ENCODER, ENCODER_WEIGHTS)
42
 
43
+
44
  def get_preprocessing():
45
  _transform = [
46
  albu.Resize(512, 512),
47
  albu.Lambda(image=preprocessing_fn),
48
  albu.Lambda(image=to_tensor, mask=to_tensor),
49
  ToTensorV2(),
 
 
50
  ]
51
  return albu.Compose(_transform)
52
 
53
 
54
  preprocess = get_preprocessing()
55
 
56
+
57
  @torch.no_grad()
58
  def process_and_predict(image, model):
 
59
  if isinstance(image, Image.Image):
60
  image = np.array(image)
61
+
 
62
  if image.ndim == 2:
63
  image = np.stack([image] * 3, axis=-1)
64
  elif image.shape[2] == 4:
65
  image = image[:, :, :3]
66
+
 
67
  preprocessed = preprocess(image=image)['image']
 
 
68
  input_tensor = preprocessed.unsqueeze(0).to(DEVICE)
69
 
 
 
70
  mask = model(input_tensor)
71
  mask = torch.sigmoid(mask)
72
  mask = (mask > 0.6).float()
73
+
 
74
  mask_image = Image.fromarray((mask.squeeze().cpu().numpy() * 255).astype(np.uint8))
 
 
75
 
76
+ return mask_image
 
 
 
 
77
 
78
 
79
  def extract_tiles(map_file, model, tile_size=512, overlap=0, batch_size=4):
80
  tiles = []
81
+
82
  with rasterio.open(map_file) as src:
83
  height = src.height
84
  width = src.width
85
+
86
  effective_tile_size = tile_size - overlap
87
+
88
  for y in tqdm(range(0, height, effective_tile_size)):
89
  for x in range(0, width, effective_tile_size):
90
  batch_images = []
91
  batch_metas = []
92
+
93
  for i in range(batch_size):
94
  curr_y = y + (i * effective_tile_size)
95
  if curr_y >= height:
96
  break
97
+
98
  window = Window(x, curr_y, tile_size, tile_size)
99
  out_image = src.read(window=window)
100
+
101
  if out_image.shape[0] == 1:
102
  out_image = np.repeat(out_image, 3, axis=0)
103
  elif out_image.shape[0] != 3:
104
  raise ValueError("The number of channels in the image is not supported")
105
+
106
  out_image = np.transpose(out_image, (1, 2, 0))
107
  tile_image = Image.fromarray(out_image.astype(np.uint8))
108
+
109
  out_meta = src.meta.copy()
110
  out_meta.update({
111
  "driver": "GTiff",
 
114
  "transform": rasterio.windows.transform(window, src.transform)
115
  })
116
  tile_image = np.array(tile_image)
117
+
118
  preprocessed_tile = preprocess(image=tile_image)['image']
119
  batch_images.append(preprocessed_tile)
120
  batch_metas.append(out_meta)
121
+
122
  if not batch_images:
123
  break
124
+
 
125
  batch_tensor = torch.cat([img.unsqueeze(0).to(DEVICE) for img in batch_images], dim=0)
 
126
  with torch.no_grad():
127
  batch_masks = model(batch_tensor.to(DEVICE))
128
+
129
  batch_masks = torch.sigmoid(batch_masks)
130
  batch_masks = (batch_masks > 0.6).float()
131
+
 
132
  for j, mask_tensor in enumerate(batch_masks):
133
+ mask_resized = torch.nn.functional.interpolate(mask_tensor.unsqueeze(0),
134
+ size=(tile_size, tile_size), mode='bilinear',
135
+ align_corners=False).squeeze(0)
136
+
137
  mask_array = mask_resized.squeeze().cpu().numpy()
138
+
139
  if mask_array.any() == 1:
140
  tiles.append([mask_array, batch_metas[j]])
141
+
142
  return tiles
143
 
144
+
145
+
146
+
147
+
148
+
149
+ def create_vector_mask(tiles, output_path):
150
+ all_polygons = []
151
+ for mask_array, meta in tiles:
152
+ # Ensure mask is binary
153
+ mask_array = (mask_array > 0).astype(np.uint8)
154
+
155
+ # Get shapes from the mask
156
+ mask_shapes = list(shapes(mask_array, mask=mask_array, transform=meta['transform']))
157
+
158
+ # Convert shapes to Shapely polygons
159
+ polygons = [shape(geom) for geom, value in mask_shapes if value == 1]
160
+
161
+ all_polygons.extend(polygons)
162
+ # Perform union of all polygons
163
+ union_polygon = unary_union(all_polygons)
164
+ # Create a GeoDataFrame
165
+ gdf = gpd.GeoDataFrame({'geometry': [union_polygon]}, crs=meta['crs'])
166
+ # Save to file
167
+ gdf.to_file(output_path)
168
+
169
+ # Calculate area in square meters
170
+ area_m2 = gdf.to_crs(epsg=3857).area.sum()
171
+ # Convert to hectares
172
+ area_ha = area_m2 / 10000
173
+
174
+ return gdf, area_ha
175
+
176
+
177
  def main():
178
  st.title("TIF File Processor")
179
 
 
182
  if uploaded_file is not None:
183
  st.write("File uploaded successfully!")
184
 
 
185
  if st.button("Process File"):
186
  st.write("Processing...")
187
+
 
188
  with open("temp.tif", "wb") as f:
189
  f.write(uploaded_file.getbuffer())
190
+
 
191
  best_model.float()
192
  tiles = extract_tiles("temp.tif", best_model, tile_size=512, overlap=15, batch_size=4)
193
+
194
  st.write("Processing complete!")
195
 
196
+ output_path = "output_mask.shp"
197
+ result_gdf, area_ha = create_vector_mask(tiles, output_path)
198
+
199
+ st.write("Vector mask created successfully!")
200
+ st.write(f"Total area occupied by polygons: {area_ha:.2f} hectares")
201
+
202
+ # Offer the shapefile for download
203
+ shp_files = [f for f in os.listdir() if
204
+ f.startswith("output_mask") and f.endswith((".shp", ".shx", ".dbf", ".prj"))]
205
+
206
+ with io.BytesIO() as zip_buffer:
207
+ with zipfile.ZipFile(zip_buffer, 'a', zipfile.ZIP_DEFLATED, False) as zip_file:
208
+ for file in shp_files:
209
+ zip_file.write(file)
210
+
211
+ zip_buffer.seek(0)
212
+ st.download_button(
213
+ label="Download shapefile",
214
+ data=zip_buffer,
215
+ file_name="output_mask.zip",
216
+ mime="application/zip"
217
+ )
218
+
219
+ # Clean up temporary files
220
+ os.remove("temp.tif")
221
+ for file in shp_files:
222
+ os.remove(file)
223
+
224
 
225
  if __name__ == "__main__":
226
  main()