Muhammad Ahad Hassan Khan commited on
Commit
336fa4a
·
1 Parent(s): 11b39b6

ndvi_pred only

Browse files
Files changed (4) hide show
  1. app.py +41 -0
  2. dockerfile +25 -0
  3. ndvi_predictor.py +76 -0
  4. requirements.txt +9 -0
app.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ from fastapi import FastAPI, File, UploadFile
3
+ from fastapi.responses import StreamingResponse, JSONResponse
4
+ from ndvi_predictor import load_model, normalize_rgb, predict_ndvi, create_visualization
5
+ from PIL import Image
6
+ import numpy as np
7
+ import io
8
+ import os
9
+
10
+ app = FastAPI()
11
+ model = load_model("ndvi_model.keras")
12
+
13
+ @app.post("/predict/")
14
+ async def predict_ndvi_api(file: UploadFile = File(...)):
15
+ img = Image.open(await file.read()).convert("RGB")
16
+ rgb_np = np.array(img)
17
+ rgb_norm = normalize_rgb(rgb_np)
18
+ ndvi = predict_ndvi(model, rgb_norm)
19
+
20
+ # Generate visual output
21
+ vis_image = create_visualization(rgb_np, ndvi)
22
+ vis_filename = "ndvi_visualization.png"
23
+
24
+ # Generate GeoTIFF in memory
25
+ geotiff_buffer = io.BytesIO()
26
+ import rasterio
27
+ profile = {
28
+ 'driver': 'GTiff',
29
+ 'height': ndvi.shape[0],
30
+ 'width': ndvi.shape[1],
31
+ 'count': 1,
32
+ 'dtype': 'float32'
33
+ }
34
+ with rasterio.open(geotiff_buffer, 'w', **profile) as dst:
35
+ dst.write(ndvi, 1)
36
+ geotiff_buffer.seek(0)
37
+
38
+ return {
39
+ "ndvi_tiff": StreamingResponse(geotiff_buffer, media_type="image/tiff", headers={"Content-Disposition": "attachment; filename=predicted_ndvi.tif"}),
40
+ "visualization": StreamingResponse(vis_image, media_type="image/png", headers={"Content-Disposition": "attachment; filename=ndvi_visualization.png"})
41
+ }
dockerfile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10
2
+
3
+ # Avoid interactive prompts
4
+ ENV DEBIAN_FRONTEND=noninteractive
5
+
6
+ # Install system dependencies
7
+ RUN apt-get update && apt-get install -y \
8
+ libgl1-mesa-glx \
9
+ libglib2.0-0 \
10
+ gdal-bin \
11
+ python3-gdal \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
14
+ # Set work directory
15
+ WORKDIR /code
16
+
17
+ # Install Python packages
18
+ COPY requirements.txt .
19
+ RUN pip install --upgrade pip && pip install -r requirements.txt
20
+
21
+ # Copy all files
22
+ COPY . .
23
+
24
+ # Launch FastAPI app with Uvicorn
25
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
ndvi_predictor.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ndvi_predictor.py
2
+ import os
3
+ os.environ["TF_ENABLE_ONEDNN_OPTS"] = "0"
4
+ os.environ["SM_FRAMEWORK"] = "tf.keras"
5
+
6
+ import segmentation_models as sm
7
+ import tensorflow as tf
8
+ import numpy as np
9
+ import rasterio
10
+ import matplotlib.pyplot as plt
11
+ from PIL import Image
12
+ import io
13
+
14
+ def load_model(model_path):
15
+ return tf.keras.models.load_model(model_path, compile=False)
16
+
17
+ def normalize_rgb(rgb):
18
+ rgb_norm = rgb.copy().astype(np.float32)
19
+ for b in range(3):
20
+ band = rgb_norm[:, :, b]
21
+ min_val, max_val = np.percentile(band, [1, 99])
22
+ if min_val < max_val:
23
+ rgb_norm[:, :, b] = np.clip((band - min_val) / (max_val - min_val), 0, 1)
24
+ return rgb_norm
25
+
26
+ def predict_ndvi(model, rgb_np):
27
+ height, width = rgb_np.shape[:2]
28
+ tile_size = 512
29
+ stride = int(tile_size * 0.7)
30
+
31
+ ndvi_pred = np.zeros((height, width), dtype=np.float32)
32
+ weight_map = np.zeros((height, width), dtype=np.float32)
33
+
34
+ if height < tile_size or width < tile_size:
35
+ pad_height = max(0, tile_size - height)
36
+ pad_width = max(0, tile_size - width)
37
+ rgb_padded = np.pad(rgb_np, ((0, pad_height), (0, pad_width), (0, 0)), mode='reflect')
38
+ height_padded, width_padded = rgb_padded.shape[0], rgb_padded.shape[1]
39
+ else:
40
+ rgb_padded = rgb_np
41
+ height_padded, width_padded = height, width
42
+
43
+ for i in range(0, height_padded - tile_size + 1, stride):
44
+ for j in range(0, width_padded - tile_size + 1, stride):
45
+ tile = rgb_padded[i:i+tile_size, j:j+tile_size, :]
46
+ y, x = np.mgrid[0:tile_size, 0:tile_size]
47
+ weights = np.minimum(np.minimum(x, tile_size - x - 1), np.minimum(y, tile_size - y - 1))
48
+ weights = np.clip(weights, 0, 50) / 50
49
+ tile_pred = model.predict(np.expand_dims(tile, axis=0), verbose=0)[0, :, :, 0]
50
+ valid_height = min(tile_size, height - i)
51
+ valid_width = min(tile_size, width - j)
52
+ ndvi_pred[i:i+valid_height, j:j+valid_width] += tile_pred[:valid_height, :valid_width] * weights[:valid_height, :valid_width]
53
+ weight_map[i:i+valid_height, j:j+valid_width] += weights[:valid_height, :valid_width]
54
+
55
+ mask = weight_map > 0
56
+ ndvi_pred[mask] = ndvi_pred[mask] / weight_map[mask]
57
+ return ndvi_pred
58
+
59
+ def create_visualization(rgb, ndvi):
60
+ fig, axes = plt.subplots(1, 2, figsize=(12, 6))
61
+ rgb_disp = np.clip(rgb / 255 if rgb.max() > 1 else rgb, 0, 1)
62
+ axes[0].imshow(rgb_disp)
63
+ axes[0].set_title("RGB Input")
64
+ axes[0].axis("off")
65
+
66
+ im = axes[1].imshow(ndvi, cmap='RdYlGn', vmin=-1, vmax=1)
67
+ axes[1].set_title("Predicted NDVI")
68
+ axes[1].axis("off")
69
+ fig.colorbar(im, ax=axes[1])
70
+
71
+ buf = io.BytesIO()
72
+ plt.tight_layout()
73
+ plt.savefig(buf, format="png")
74
+ plt.close(fig)
75
+ buf.seek(0)
76
+ return buf
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ tensorflow
2
+ segmentation-models
3
+ rasterio
4
+ numpy
5
+ matplotlib
6
+ Pillow
7
+ tqdm
8
+ fastapi
9
+ uvicorn