ojasrohatgi commited on
Commit
f397798
·
verified ·
1 Parent(s): 5339b55

Upload 4 files

Browse files
Files changed (4) hide show
  1. .env +4 -0
  2. app.py +27 -0
  3. backend.py +155 -0
  4. deforestation_unet_full_model.pt +3 -0
.env ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ project-id=deforestation-detection-459814
2
+ MODEL_URL=https://drive.google.com/uc?id=1fuofy10g5UxPIOqvoo6zVFIgSYjSmCV8
3
+ SERVICE_KEY_ID=render-deforestation-detection@deforestation-detection-459814.iam.gserviceaccount.com
4
+ PRIVATE_KEY={"type": "service_account", "project_id": "deforestation-detection-459814", "private_key_id": "dd01483fd123c11f98dc9bac47e65d0c2382135b", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDVV1ueZ5KccMO7\nrj6Wdr5gtWt+XZ6SXUU3Bts5jFlgDndrOyxVuaaHCSz0cIvC31rwHAybOQR6lqLv\nf3hO7XQEDKkbWzrSgRKRt2Uzsp3Hu0R+XB2G8Tscp1vu6rP0xtj78VA8mx7SWjxs\nPqcn2bta6QxXjndFV5qT9J1jrRY/ehWC7YRE23IUmt5ecnehTC/J+VDltD17ATpz\nHgeGaKBpdz6y2+ekgHHaNFjULb2osfkdaAyyck1P9+FkctcZd6bSG3Q7qAF7IRLV\nxD7t8tlWIVLB/k1M2BxL7glmRYqmmS8/TDOgPbJBYgBNXALg+/vP29pYs45HKTak\noj/4ugzxAgMBAAECggEAE22mcA1GFn2VcaLLW9f5++Qhoysi7PjV/A4hikLcA8ml\neew0XCUxQ2RkRel27Nr61Nl1E3C0lfZgZbehzOxGb7T4dH+RIojzGDaPno7iXAVT\nlj9MyBRxWelqz53rn/u42G7QLAjDXIwvqvrkrZYgQAXvpAybE4NIFjfFWoWxfDu3\nJu0tYPLKAEmkMpN/R5Y+im626WcXrUcsPByuvewY63v3RouVus8ik3k+cDUR1AAT\n1AxFhuLIjUhx9r9BC2PrlWahv3LGna1oRgZKx6Kx9w0m4ciQB8H8W67qZWYviV9q\nDRtuQulM56IWi9ENvA+C7XiQZrL3RwZ4mAH7lHlQ3QKBgQDsUncEZgmcSkDaOmdJ\n1ZtqPE/WNKUtV7h+0qkjRo66jgoFegeZiuPymUY4VCo0HcDYAaUEpUuMt2VnDzsu\n9aUorfv+SgABfvQE07BBZdQF3B6mJy1l5GxFX56tfcp31SRzw1nL7joh9ypx/W8a\n6gBtCWqElVZ1U2c44wRwpu7zzQKBgQDnGwX8sjw5aFrf6yMLbyr7HrToCIIRdl1l\neTDmlSbuUH0CCfEbaj8XIqetjBPK3eQ8hdghxSQd9j3GFeWkcePjIZJ7/89lHpud\nfYMwlsfHo1rbD7Qem+dWCrh3a++AFlWmARbshbZ92/WObGyIsW5G+5EdLNWIed1l\nKb+KvANhtQKBgCVL0nrAO84NrfSC+SAe9RssD5GH13WzfWuOhaEKlqX8mrpIiwCB\nef4kkH99UPfOpkuw3sE/8Q9xNjCwp69+lyU3aCi2tw+FYK+OVSfNEUwndDLWxgRp\nq2i7cYiB7L1CxzD56KcVntkTcABzdeByg8Sxkrz/8JgtpIHG2kGJJvcVAoGBALNt\nVa6lqwBfNv7WjnTYMKSbaJUl1eY84bJg70h20K0CLKwij+FbEfSiYVDqiotcz1D2\nEaHWb34bqkZaxdpw2h+D9zjymVDG/Ma/pdVZm24yM94USSHipS82T5XYZTArJwAl\npGiqP89jsTiMkY9nQlk2A6qFHpxBEVTzntTVuEJpAoGBAJYauM4yZ4WcaKv47Vmf\njcXBLfpx0cNfMSlMt/3OQs+FLej9JpfOBI021NFlfCaMNG0rQ55UA+AXqwrvatZn\nWYyI/6Ts0hCWC1V0nCpFcfDc+QcEYC8fNCthbVt6Br/qXtxzGoeQp7zd10mgr8VZ\nSHJPrEldpqlE5ELa4Kmq8MuQ\n-----END PRIVATE KEY-----\n", "client_email": "render-deforestation-detection@deforestation-detection-459814.iam.gserviceaccount.com", "client_id": "108238598515799130608", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/render-deforestation-detection%40deforestation-detection-459814.iam.gserviceaccount.com", "universe_domain": "googleapis.com"}
app.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify, render_template
2
+ from backend import run_deforestation_pipeline
3
+
4
+ app = Flask(__name__)
5
+
6
+ @app.route('/')
7
+ def index():
8
+ return render_template("index.html")
9
+
10
+ @app.route('/predict', methods=['POST'])
11
+ def predict():
12
+ data = request.get_json()
13
+
14
+ result = run_deforestation_pipeline(
15
+ lat_min=data['lat_min'],
16
+ lat_max=data['lat_max'],
17
+ lon_min=data['lon_min'],
18
+ lon_max=data['lon_max'],
19
+ start_year=data['start_year'],
20
+ end_year=data['end_year']
21
+ )
22
+
23
+ return jsonify(result)
24
+
25
+ if __name__ == '__main__':
26
+ app.run(host='0.0.0.0', port=5000)
27
+
backend.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import ee
3
+ import numpy as np
4
+ import requests
5
+ import io
6
+ import base64
7
+ from rasterio.io import MemoryFile
8
+ import torch
9
+ import segmentation_models_pytorch as smp
10
+ import matplotlib.pyplot as plt
11
+ import gdown
12
+ from dotenv import load_dotenv
13
+
14
+ load_dotenv()
15
+
16
+ PRIVATE_KEY = os.getenv("PRIVATE_KEY")
17
+ with open("private-key.json", "w") as f:
18
+ f.write(PRIVATE_KEY)
19
+ service_account = os.getenv("SERVICE_KEY_ID")
20
+ credentials = ee.ServiceAccountCredentials(service_account, 'private-key.json')
21
+ ee.Initialize(credentials)
22
+
23
+ MODEL_PATH = "deforestation_unet_full_model.pt"
24
+ MODEL_URL = os.getenv("MODEL_URL")
25
+
26
+ # Download model only if it doesn't exist
27
+ if not os.path.exists(MODEL_PATH):
28
+ print("Model not found. Downloading from Google Drive...")
29
+ gdown.download(MODEL_URL, MODEL_PATH, quiet=False)
30
+
31
+ # ee.Initialize(project=os.environ["project-id"])
32
+
33
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
34
+
35
+ # Load model once
36
+ model = smp.Unet(
37
+ encoder_name="resnet34",
38
+ encoder_weights=None,
39
+ in_channels=4,
40
+ classes=1,
41
+ activation=None,
42
+ ).to(DEVICE)
43
+ model = torch.load("deforestation_unet_full_model.pt", map_location=DEVICE, weights_only=False)
44
+ model.eval()
45
+
46
+ def apply_scale_factors(image):
47
+ optical_bands = image.select('SR_B.').multiply(0.0000275).add(-0.2)
48
+ thermal_bands = image.select('ST_B.*').multiply(0.00341802).add(149.0)
49
+ return image.addBands(optical_bands, None, True).addBands(thermal_bands, None, True)
50
+
51
+ def fetch_rgb_ndvi(region, year, scale=30):
52
+ start = ee.Date.fromYMD(year, 1, 1)
53
+ end = ee.Date.fromYMD(year, 12, 31)
54
+ col = (ee.ImageCollection("LANDSAT/LC08/C02/T1_L2")
55
+ .filterBounds(region)
56
+ .filterDate(start, end)
57
+ .filterMetadata('CLOUD_COVER', 'less_than', 10)
58
+ .map(apply_scale_factors))
59
+ image = col.median().clip(region)
60
+ ndvi = image.normalizedDifference(['SR_B5', 'SR_B4']).rename('NDVI')
61
+ image = image.addBands(ndvi)
62
+ return image.select(['SR_B4', 'SR_B3', 'SR_B2']), image.select('NDVI')
63
+
64
+ def download_geotiff_array(img, region, bands, scale=30):
65
+ url = img.getThumbURL({
66
+ 'scale': scale,
67
+ 'region': region,
68
+ 'format': 'GeoTIFF',
69
+ 'bands': bands
70
+ })
71
+ response = requests.get(url)
72
+ with MemoryFile(response.content) as memfile:
73
+ with memfile.open() as src:
74
+ arr = src.read().astype(np.float32)
75
+ if arr.max() > 1.5:
76
+ arr /= 255.0
77
+ return arr
78
+
79
+ def predict_from_arrays(rgb_arr, ndvi_arr):
80
+ rgb_arr = rgb_arr[:3, :, :]
81
+ ndvi_arr = ndvi_arr[:1, :, :]
82
+ input_arr = np.concatenate([rgb_arr, ndvi_arr], axis=0)
83
+ input_tensor = torch.tensor(input_arr).unsqueeze(0).to(DEVICE)
84
+ with torch.no_grad():
85
+ pred = torch.sigmoid(model(input_tensor))
86
+ return (pred > 0.5).float().squeeze().cpu().numpy()
87
+
88
+ def get_deforestation_color_map(mask_t0, mask_t1):
89
+ H, W = mask_t0.shape
90
+ color_map = np.zeros((H, W, 3), dtype=np.uint8)
91
+
92
+ retained = (mask_t0 == 1) & (mask_t1 == 1)
93
+ lost = (mask_t0 == 1) & (mask_t1 == 0)
94
+ gained = (mask_t0 == 0) & (mask_t1 == 1)
95
+ none = (mask_t0 == 0) & (mask_t1 == 0)
96
+
97
+ color_map[retained] = [0, 255, 0] # Green
98
+ color_map[lost] = [255, 0, 0] # Red
99
+ color_map[gained] = [65, 168, 255] # Blue (gain)
100
+ color_map[none] = [255, 255, 255] # White (no change)
101
+
102
+ return color_map
103
+
104
+ def run_deforestation_pipeline(lat_min, lat_max, lon_min, lon_max, start_year, end_year):
105
+ region = ee.Geometry.Rectangle([lon_min, lat_min, lon_max, lat_max])
106
+
107
+ rgb_t0_ee, ndvi_t0_ee = fetch_rgb_ndvi(region, start_year)
108
+ rgb_t0 = download_geotiff_array(rgb_t0_ee, region, ['SR_B4', 'SR_B3', 'SR_B2'])
109
+ ndvi_t0 = download_geotiff_array(ndvi_t0_ee, region, ['NDVI'])
110
+
111
+ rgb_t1_ee, ndvi_t1_ee = fetch_rgb_ndvi(region, end_year)
112
+ rgb_t1 = download_geotiff_array(rgb_t1_ee, region, ['SR_B4', 'SR_B3', 'SR_B2'])
113
+ ndvi_t1 = download_geotiff_array(ndvi_t1_ee, region, ['NDVI'])
114
+
115
+ mask_t0 = predict_from_arrays(rgb_t0, ndvi_t0)
116
+ mask_t1 = predict_from_arrays(rgb_t1, ndvi_t1)
117
+
118
+ deforested_pixels = ((mask_t0 == 1) & (mask_t1 == 0)).sum()
119
+ gained_pixels = ((mask_t0 == 0) & (mask_t1 == 1)).sum()
120
+ total_vegetation_t0 = (mask_t0 == 1).sum()
121
+
122
+ percent_loss = (deforested_pixels / total_vegetation_t0) * 100 if total_vegetation_t0 > 0 else 0
123
+ percent_gain = (gained_pixels / mask_t0.size) * 100 # relative to total area
124
+
125
+ color_mask = get_deforestation_color_map(mask_t0, mask_t1)
126
+
127
+ # Generate figure in memory
128
+ fig, axes = plt.subplots(1, 3, figsize=(12, 4))
129
+
130
+ axes[0].imshow(mask_t0, cmap="Greens")
131
+ axes[0].set_title(f"Vegetation in {start_year}")
132
+ axes[0].axis("off")
133
+
134
+ axes[1].imshow(mask_t1, cmap="Greens")
135
+ axes[1].set_title(f"Vegetation in {end_year}")
136
+ axes[1].axis("off")
137
+
138
+ axes[2].imshow(color_mask)
139
+ axes[2].set_title(f"Vegetation Change")
140
+ axes[2].axis("off")
141
+
142
+ plt.tight_layout()
143
+
144
+ buf = io.BytesIO()
145
+ plt.savefig(buf, format="png")
146
+ plt.close(fig)
147
+ buf.seek(0)
148
+ img_base64 = base64.b64encode(buf.read()).decode('utf-8')
149
+
150
+ return {
151
+ "percent_deforested": round(percent_loss, 2),
152
+ "percent_regrowth": round(percent_gain, 2),
153
+ "image_base64": img_base64
154
+ }
155
+
deforestation_unet_full_model.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c0552d6fa7db02329b96462065b4c69590b5f5c0daeef32dc5f8ff98d51a83e0
3
+ size 97968507