AdarshRajDS commited on
Commit
8cc2137
·
1 Parent(s): 1387cb8

Add mold detection FastAPI backend

Browse files
Files changed (6) hide show
  1. Dockerfile +30 -0
  2. README.md +75 -3
  3. app.py +68 -0
  4. decision.py +29 -0
  5. model.py +20 -0
  6. requirements.txt +9 -0
Dockerfile ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install system dependencies
6
+ RUN apt-get update && apt-get install -y \
7
+ build-essential \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ # Copy requirements first for better caching
11
+ COPY requirements.txt .
12
+
13
+ # Install Python dependencies
14
+ RUN pip install --no-cache-dir --upgrade pip && \
15
+ pip install --no-cache-dir torch torchvision --index-url https://download.pytorch.org/whl/cpu && \
16
+ pip install --no-cache-dir -r requirements.txt
17
+
18
+ # Copy application files
19
+ COPY app.py model.py decision.py ./
20
+
21
+ # Copy model file
22
+ COPY resnet50_multitask_bio.pth ./
23
+
24
+ # Expose port (HuggingFace Spaces uses port 7860)
25
+ EXPOSE 7860
26
+
27
+ # Run the application
28
+ CMD uvicorn app:app --host 0.0.0.0 --port 7860
29
+
30
+
README.md CHANGED
@@ -1,10 +1,82 @@
1
  ---
2
- title: Mold Detection Api
3
- emoji: 💻
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: docker
 
7
  pinned: false
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Mold Detection API
3
+ emoji: 🦠
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: docker
7
+ app_file: app.py
8
  pinned: false
9
+ license: mit
10
  ---
11
 
12
+ # Mold Detection API
13
+
14
+ FastAPI backend for mold detection using multi-task ResNet50 deep learning model, deployed with Docker.
15
+
16
+ # Mold Detection API
17
+
18
+ FastAPI backend for mold detection using multi-task ResNet50 deep learning model.
19
+
20
+ ## Features
21
+
22
+ - **Multi-task Learning**: Classifies mold types and detects biological material
23
+ - **3-Level Decision System**:
24
+ - High confidence (≥80%): "Mold"
25
+ - Medium confidence (50-80%) + biological detection: "Possible Mold"
26
+ - Low confidence: "Not Mold"
27
+ - **RESTful API**: Easy integration with any frontend
28
+
29
+ ## API Endpoints
30
+
31
+ ### `GET /`
32
+ Health check and API information
33
+
34
+ ### `GET /health`
35
+ Simple health check
36
+
37
+ ### `POST /predict`
38
+ Predict mold detection from an image
39
+
40
+ **Request:**
41
+ - Content-Type: `multipart/form-data`
42
+ - File: Image file (jpg, png, jpeg)
43
+
44
+ **Response:**
45
+ ```json
46
+ {
47
+ "decision": "Mold" | "Possible Mold" | "Not Mold",
48
+ "mold_probability": 0.0-1.0,
49
+ "biological_probability": 0.0-1.0
50
+ }
51
+ ```
52
+
53
+ ## Usage
54
+
55
+ ### Using curl:
56
+ ```bash
57
+ curl -X POST "https://YOUR_USERNAME-SPACE_NAME.hf.space/predict" \
58
+ -F "file=@/path/to/your/image.jpg"
59
+ ```
60
+
61
+ ### Using Python:
62
+ ```python
63
+ import requests
64
+
65
+ url = "https://YOUR_USERNAME-SPACE_NAME.hf.space/predict"
66
+ with open("test_image.jpg", "rb") as f:
67
+ response = requests.post(url, files={"file": f})
68
+ print(response.json())
69
+ ```
70
+
71
+ ## Documentation
72
+
73
+ Interactive API documentation available at `/docs` endpoint.
74
+
75
+ ## Model
76
+
77
+ - **Architecture**: ResNet50 with multi-task heads
78
+ - **Input**: RGB images (224x224)
79
+ - **Output**:
80
+ - Classification head: 9 classes (mold class at index 4)
81
+ - Biological detection head: 2 classes (binary)
82
+
app.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, UploadFile
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from PIL import Image
4
+ import torch
5
+ import io
6
+ from pathlib import Path
7
+ from torchvision import transforms
8
+
9
+ from model import MultiTaskResNet50
10
+ from decision import final_decision
11
+
12
+ app = FastAPI(
13
+ title="Mold Detection API",
14
+ description="FastAPI backend for mold detection using multi-task ResNet50",
15
+ version="1.0.0"
16
+ )
17
+
18
+ # Add CORS middleware for frontend
19
+ app.add_middleware(
20
+ CORSMiddleware,
21
+ allow_origins=["*"], # In production, replace with specific frontend URL
22
+ allow_credentials=True,
23
+ allow_methods=["*"],
24
+ allow_headers=["*"],
25
+ )
26
+
27
+ device = "cuda" if torch.cuda.is_available() else "cpu"
28
+
29
+ # Model path for HuggingFace Spaces (flat structure)
30
+ model_path = Path("resnet50_multitask_bio.pth")
31
+
32
+ print(f"Loading model from: {model_path.absolute()}")
33
+ print(f"Model exists: {model_path.exists()}")
34
+
35
+ model = MultiTaskResNet50()
36
+ model.load_state_dict(torch.load(str(model_path), map_location=device))
37
+ model.eval().to(device)
38
+ print("✅ Model loaded successfully")
39
+
40
+ transform = transforms.Compose([
41
+ transforms.Resize((224,224)),
42
+ transforms.ToTensor(),
43
+ transforms.Normalize(
44
+ mean=[0.485,0.456,0.406],
45
+ std=[0.229,0.224,0.225]
46
+ )
47
+ ])
48
+
49
+ @app.get("/")
50
+ async def root():
51
+ return {
52
+ "status": "healthy",
53
+ "message": "Mold Detection API is running",
54
+ "endpoint": "/predict",
55
+ "method": "POST",
56
+ "docs": "/docs"
57
+ }
58
+
59
+ @app.get("/health")
60
+ async def health():
61
+ return {"status": "healthy"}
62
+
63
+ @app.post("/predict")
64
+ async def predict(file: UploadFile):
65
+ image_bytes = await file.read()
66
+ img = Image.open(io.BytesIO(image_bytes)).convert("RGB")
67
+ img_tensor = transform(img).to(device)
68
+ return final_decision(model, img_tensor)
decision.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn.functional as F
3
+
4
+ MOLD_HIGH_CONF = 0.80
5
+ MOLD_LOW_CONF = 0.50
6
+ BIO_CONF = 0.60
7
+
8
+ def final_decision(model, img_tensor, mold_idx=4):
9
+ with torch.no_grad():
10
+ out = model(img_tensor.unsqueeze(0))
11
+ class_probs = F.softmax(out["class"], dim=1)[0]
12
+ bio_probs = F.softmax(out["bio"], dim=1)[0]
13
+
14
+ mold_p = class_probs[mold_idx].item()
15
+ bio_p = bio_probs[1].item()
16
+
17
+ if mold_p >= MOLD_HIGH_CONF:
18
+ decision = "Mold"
19
+ elif mold_p >= MOLD_LOW_CONF and bio_p >= BIO_CONF:
20
+ decision = "Possible Mold"
21
+ else:
22
+ decision = "Not Mold"
23
+
24
+ return {
25
+ "decision": decision,
26
+ "mold_probability": round(mold_p, 3),
27
+ "biological_probability": round(bio_p, 3)
28
+ }
29
+
model.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from torchvision import models
4
+
5
+ class MultiTaskResNet50(nn.Module):
6
+ def __init__(self, num_classes=9):
7
+ super().__init__()
8
+ self.backbone = models.resnet50(weights=None)
9
+ feat_dim = self.backbone.fc.in_features
10
+ self.backbone.fc = nn.Identity()
11
+ self.class_head = nn.Linear(feat_dim, num_classes)
12
+ self.bio_head = nn.Linear(feat_dim, 2)
13
+
14
+ def forward(self, x):
15
+ feats = self.backbone(x)
16
+ return {
17
+ "class": self.class_head(feats),
18
+ "bio": self.bio_head(feats)
19
+ }
20
+
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ torch
2
+ torchvision
3
+ fastapi
4
+ uvicorn
5
+ pillow
6
+ numpy<2
7
+ python-multipart
8
+
9
+