Utkarshres32 commited on
Commit
998bc6e
Β·
1 Parent(s): 7a5bb5d

Initial commit for Hugging Face deployment

Browse files
.dockerignore ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python / Backend
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ .venv/
6
+ venv/
7
+ env/
8
+ ENV/
9
+ .env
10
+
11
+ # Large Files / Data
12
+ data/images/
13
+ data/masks/
14
+ *.keras
15
+ *.h5
16
+ *.pkl
17
+
18
+ # Project
19
+ # frontend/ # Remove this to allow frontend build in multi-stage Dockerfile
20
+ node_modules/
21
+ .git
22
+ .github
23
+ .gitignore
24
+ .dockerignore
25
+ implementation_plan.md
26
+ task.md
27
+ walkthrough.md
Dockerfile ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stage 1: Build the frontend
2
+ FROM node:20-slim AS frontend-builder
3
+ WORKDIR /app/frontend
4
+ COPY frontend/package*.json ./
5
+ RUN npm install
6
+ COPY frontend/ ./
7
+ RUN npm run build
8
+
9
+ # Stage 2: Final image with Python backend
10
+ FROM python:3.10-slim
11
+
12
+ WORKDIR /app
13
+
14
+ # Install system dependencies for OpenCV/Pillow
15
+ RUN apt-get update && apt-get install -y \
16
+ libglib2.0-0 \
17
+ libsm6 \
18
+ libxext6 \
19
+ libxrender-dev \
20
+ && rm -rf /var/lib/apt/lists/*
21
+
22
+ # Copy requirements and install
23
+ COPY backend/requirements.txt .
24
+ RUN pip install --no-cache-dir -r requirements.txt
25
+
26
+ # Copy built frontend from Stage 1
27
+ COPY --from=frontend-builder /app/frontend/dist ./frontend/dist
28
+
29
+ # Copy backend and supporting code
30
+ COPY backend/ ./backend/
31
+ COPY model/ ./model/
32
+ COPY utils/ ./utils/
33
+
34
+ # Hugging Face Spaces uses port 7860 by default
35
+ ENV PORT=7860
36
+ EXPOSE 7860
37
+
38
+ # We use the PORT environment variable in main.py
39
+ CMD ["python", "-m", "uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "7860"]
backend/inference.py CHANGED
@@ -8,6 +8,10 @@ import sys
8
  sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
9
  from model.unet import build_unet
10
  from model.loss_metrics import get_metrics, bce_dice_loss
 
 
 
 
11
 
12
  # Path to the saved model weights
13
  MODEL_PATH = os.path.join(os.path.dirname(__file__), "../model/saved_models/oil_spill_unet_best.keras")
 
8
  sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
9
  from model.unet import build_unet
10
  from model.loss_metrics import get_metrics, bce_dice_loss
11
+ from utils.model_fetcher import download_if_missing
12
+
13
+ # Force download if missing before initialization
14
+ download_if_missing()
15
 
16
  # Path to the saved model weights
17
  MODEL_PATH = os.path.join(os.path.dirname(__file__), "../model/saved_models/oil_spill_unet_best.keras")
backend/main.py CHANGED
@@ -1,5 +1,6 @@
1
  from fastapi import FastAPI, UploadFile, File, HTTPException
2
  from fastapi.responses import Response, JSONResponse
 
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from PIL import Image
5
  import io
@@ -22,6 +23,11 @@ app.add_middleware(
22
  allow_headers=["*"],
23
  )
24
 
 
 
 
 
 
25
  @app.get("/health")
26
  def health_check():
27
  """Health check endpoint to ensure API and Model are ready."""
@@ -69,4 +75,6 @@ async def predict_spill(file: UploadFile = File(...)):
69
 
70
  if __name__ == "__main__":
71
  import uvicorn
72
- uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
 
 
 
1
  from fastapi import FastAPI, UploadFile, File, HTTPException
2
  from fastapi.responses import Response, JSONResponse
3
+ from fastapi.staticfiles import StaticFiles
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from PIL import Image
6
  import io
 
23
  allow_headers=["*"],
24
  )
25
 
26
+ # Serves the built frontend files
27
+ # Ensure 'frontend/dist' exists after the build stage in Docker
28
+ if os.path.exists("frontend/dist"):
29
+ app.mount("/", StaticFiles(directory="frontend/dist", html=True), name="static")
30
+
31
  @app.get("/health")
32
  def health_check():
33
  """Health check endpoint to ensure API and Model are ready."""
 
75
 
76
  if __name__ == "__main__":
77
  import uvicorn
78
+ # Hugging Face Spaces defaults to port 7860
79
+ port = int(os.environ.get("PORT", 7860))
80
+ uvicorn.run("main:app", host="0.0.0.0", port=port, reload=True)
backend/utils/model_fetcher.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import urllib.request
3
+ import sys
4
+
5
+ def download_if_missing():
6
+ """
7
+ Checks if the model weights exist locally.
8
+ If not, attempts to download them from the URL provided in environment variables.
9
+ """
10
+ model_url = os.getenv("MODEL_URL")
11
+ # Default local path relative to backend directory
12
+ model_path = os.path.join(os.path.dirname(__file__), "../../model/saved_models/oil_spill_unet_best.keras")
13
+
14
+ # Ensure directory exists
15
+ os.makedirs(os.path.dirname(model_path), exist_ok=True)
16
+
17
+ if not os.path.exists(model_path):
18
+ if not model_url or model_url == "https://your-r2-public-url/oil_spill_unet_best.keras":
19
+ print("⚠️ MODEL_URL not configured. Skipping download.")
20
+ print("πŸ’‘ Please provide a valid URL in your docker-compose.yml to enable automated fetching.")
21
+ return
22
+
23
+ print(f"πŸš€ Model weights missing. Initializing download from: {model_url}")
24
+ try:
25
+ # Using urllib as it's built-in and sufficient for this simple GET
26
+ urllib.request.urlretrieve(model_url, model_path)
27
+ print("βœ… Model weights downloaded successfully.")
28
+ except Exception as e:
29
+ print(f"❌ Failed to download model: {e}")
30
+ print("πŸ’‘ Fallback: The system will use an untrained stub model for now.")
31
+ else:
32
+ print("πŸ“¦ Production model weights detected locally. Skipping download.")
33
+
34
+ if __name__ == "__main__":
35
+ # Test run
36
+ download_if_missing()
docker-compose.yml CHANGED
@@ -12,6 +12,7 @@ services:
12
  - ./model:/app/model
13
  environment:
14
  - PYTHONUNBUFFERED=1
 
15
 
16
  frontend:
17
  build:
 
12
  - ./model:/app/model
13
  environment:
14
  - PYTHONUNBUFFERED=1
15
+ - MODEL_URL=https://pub-b6e6ec55d6e84dcdb100466edde874a2.r2.dev/oil_spill_unet_best.keras
16
 
17
  frontend:
18
  build:
frontend/.dockerignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ node_modules
2
+ dist
3
+ build
4
+ .env
5
+ .env.*
6
+ !.env.example
7
+ .git
8
+ .gitignore
9
+ .dockerignore
10
+ README.md
frontend/src/App.tsx CHANGED
@@ -181,7 +181,7 @@ function Dashboard() {
181
  formData.append("file", selectedFile);
182
 
183
  try {
184
- const response = await fetch("http://localhost:8000/predict", {
185
  method: "POST",
186
  body: formData,
187
  });
 
181
  formData.append("file", selectedFile);
182
 
183
  try {
184
+ const response = await fetch("/predict", {
185
  method: "POST",
186
  body: formData,
187
  });
upload_to_r2.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import boto3
2
+ import os
3
+
4
+ # 1. Credentials (Ready to use from your screenshot)
5
+ ACCESS_KEY = "a9f685d13afd9a290e54dca612ef83d2"
6
+ SECRET_KEY = "8284c338275d0e6ebb3e6e697ba5999f6409908cb92c08fe8351da4af728c875"
7
+ ACCOUNT_ID = "659b46b7d1773d3742cd10b2c9194dd3"
8
+ BUCKET_NAME = "oil-spill-detection"
9
+
10
+ # 2. Path to your model file
11
+ # Note: Ensure oil_spill_unet_best.keras is in this same folder!
12
+ FILE_PATH = "oil_spill_unet_best.keras"
13
+
14
+ def upload():
15
+ # Setup the Cloudflare R2 Client via S3 API
16
+ s3 = boto3.client(
17
+ service_name='s3',
18
+ endpoint_url=f'https://{ACCOUNT_ID}.r2.cloudflarestorage.com',
19
+ aws_access_key_id=ACCESS_KEY,
20
+ aws_secret_access_key=SECRET_KEY,
21
+ region_name='auto'
22
+ )
23
+
24
+ print(f"Starting high-speed API upload of {FILE_PATH}...")
25
+
26
+ if not os.path.exists(FILE_PATH):
27
+ print(f"ERROR: File '{FILE_PATH}' not found in the current directory.")
28
+ print("FIX: Make sure you have downloaded it from Google Drive to this folder.")
29
+ return
30
+
31
+ try:
32
+ s3.upload_file(FILE_PATH, BUCKET_NAME, "oil_spill_unet_best.keras")
33
+ print("\n" + "="*40)
34
+ print("SUCCESS! Your model is now on Cloudflare R2.")
35
+ print("="*40)
36
+ print("You can now run 'docker-compose up --build' and the system will pull the model automatically.")
37
+ except Exception as e:
38
+ print(f"UPLOAD FAILED: {e}")
39
+
40
+ if __name__ == "__main__":
41
+ upload()