n0v33n commited on
Commit
287ab0c
·
1 Parent(s): 4dc492e

initail commit

Browse files
Files changed (4) hide show
  1. DockerFile +31 -0
  2. main.py +58 -0
  3. model.py +51 -0
  4. requirement.txt +4 -0
DockerFile ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use official Python slim image
2
+ FROM python:3.11-slim
3
+
4
+ # Set working dir
5
+ WORKDIR /app
6
+
7
+ # Install system deps needed for Pillow
8
+ RUN apt-get update && apt-get install -y --no-install-recommends \
9
+ build-essential \
10
+ libjpeg-dev \
11
+ zlib1g-dev \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
14
+ # Copy requirements first for better caching
15
+ COPY requirements.txt /app/
16
+
17
+ # Install python deps
18
+ RUN pip install --no-cache-dir -r requirements.txt
19
+
20
+ # Copy app
21
+ COPY . /app
22
+
23
+ # Expose port (Spaces usually route; commonly 8080 works)
24
+ EXPOSE 8080
25
+
26
+ # Use a non-root user (optional but nice)
27
+ RUN useradd --create-home appuser && chown -R appuser /app
28
+ USER appuser
29
+
30
+ # Run with Uvicorn
31
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080", "--workers", "1"]
main.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, File, UploadFile, HTTPException
2
+ from fastapi.responses import JSONResponse
3
+ from pydantic import BaseModel
4
+ from typing import Dict, Any
5
+ from model import DummyGenderModel
6
+ from PIL import Image
7
+ import io
8
+ import time
9
+ import uuid
10
+
11
+ app = FastAPI(title="Radiograph Gender Predictor (placeholder)",
12
+ description="Returns a random gender prediction for an uploaded radiograph image. Replace the DummyGenderModel with your trained model when ready.",
13
+ version="0.1.0")
14
+
15
+ model = DummyGenderModel() # placeholder. replace with your real model object when available.
16
+
17
+ class PredictResponse(BaseModel):
18
+ id: str
19
+ prediction: str
20
+ confidence: float
21
+ probabilities: Dict[str, float]
22
+ model_version: str
23
+ runtime_ms: int
24
+ timestamp: float
25
+
26
+ @app.get("/health")
27
+ def health():
28
+ return {"status": "ok", "model_loaded": model.is_loaded(), "model_version": model.version}
29
+
30
+ @app.post("/predict", response_model=PredictResponse)
31
+ async def predict(file: UploadFile = File(...)):
32
+ """
33
+ Accepts an image file (radiograph). Returns a JSON with a random gender prediction.
34
+ Content-type should be one of common image types: image/jpeg, image/png, image/tiff, etc.
35
+ """
36
+ start = time.time()
37
+ # basic content-type check (optional)
38
+ if not file.content_type.startswith("image/"):
39
+ raise HTTPException(status_code=400, detail="Uploaded file must be an image.")
40
+
41
+ contents = await file.read()
42
+ try:
43
+ img = Image.open(io.BytesIO(contents)).convert("RGB")
44
+ except Exception as e:
45
+ raise HTTPException(status_code=400, detail=f"Unable to parse image: {e}")
46
+
47
+ # call the placeholder model (random)
48
+ result = model.predict(img)
49
+
50
+ runtime_ms = int((time.time() - start) * 1000)
51
+ response = {
52
+ "id": str(uuid.uuid4()),
53
+ "prediction": result["label"],
54
+ "model_version": model.version,
55
+ "runtime_ms": runtime_ms,
56
+ "timestamp": time.time()
57
+ }
58
+ return JSONResponse(content=response)
model.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Dummy model module.
3
+
4
+ This file contains a placeholder model class `DummyGenderModel` that returns
5
+ random predictions. Replace the `predict` method or the whole class with your
6
+ trained model loading + inference logic when ready.
7
+ """
8
+
9
+ import random
10
+ from typing import Dict
11
+ from PIL.Image import Image
12
+
13
+ class DummyGenderModel:
14
+ def __init__(self):
15
+ self.version = "dummy-v0.1"
16
+ self._loaded = True
17
+
18
+ def is_loaded(self) -> bool:
19
+ return self._loaded
20
+
21
+ def predict(self, img: Image) -> Dict:
22
+ """
23
+ Return a random prediction. Expects a PIL Image.
24
+
25
+ Returns a dict:
26
+ {
27
+ "label": "male" or "female",
28
+ "confidence": float between 0.5 and 1.0,
29
+ "probabilities": {"male": p_male, "female": p_female}
30
+ }
31
+ """
32
+ # random base probability
33
+ p_male = random.random()
34
+ p_female = 1.0 - p_male
35
+
36
+ # calibrate to avoid extremely low confidences — push to [0.5, 1.0]
37
+ p_male = 0.5 + 0.5 * p_male
38
+ p_female = 1.0 - p_male
39
+
40
+ if p_male >= p_female:
41
+ label = "male"
42
+ # confidence = p_male
43
+ else:
44
+ label = "female"
45
+ # confidence = p_female
46
+
47
+ return {
48
+ "label": label,
49
+ # "confidence": round(confidence, 4),
50
+ # "probabilities": {"male": round(p_male, 4), "female": round(p_female, 4)}
51
+ }
requirement.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ pillow
4
+ python-multipart