BugFreeAli commited on
Commit
032a09b
·
verified ·
1 Parent(s): 887f997

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +29 -0
  2. main.py +120 -0
  3. requirements.txt +7 -0
Dockerfile ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 1. Base Image
2
+ FROM python:3.10-slim
3
+
4
+ # 2. Set Directory
5
+ WORKDIR /app
6
+
7
+ # 3. Install System Dependencies (Needed for OpenCV)
8
+ RUN apt-get update && apt-get install -y \
9
+ libgl1 \
10
+ libglib2.0-0 \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ # 4. User Setup (Hugging Face Requirement)
14
+ RUN useradd -m -u 1000 user
15
+ USER user
16
+ ENV PATH="/home/user/.local/bin:$PATH"
17
+
18
+ # 5. Copy Files
19
+ COPY --chown=user . .
20
+
21
+ # 6. Install Python Libs
22
+ RUN pip install --no-cache-dir --upgrade pip && \
23
+ pip install --no-cache-dir -r requirements.txt
24
+
25
+ # 7. Expose Port
26
+ EXPOSE 7860
27
+
28
+ # 8. Start Server
29
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
main.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, File, UploadFile, HTTPException
2
+ from fastapi.responses import FileResponse
3
+ from fastapi.staticfiles import StaticFiles
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+ import tensorflow as tf
6
+ from tensorflow.keras.models import load_model
7
+ import numpy as np
8
+ import cv2
9
+ import io
10
+ import os
11
+
12
+ # 1. INITIALIZE FASTAPI
13
+ app = FastAPI()
14
+
15
+ # 2. CORS (Allow everything for safety)
16
+ app.add_middleware(
17
+ CORSMiddleware,
18
+ allow_origins=["*"],
19
+ allow_methods=["*"],
20
+ allow_headers=["*"],
21
+ )
22
+
23
+ # 3. LOAD MODEL
24
+ MODEL_PATH = "retina_model_deploy.keras" # We use the optimized version
25
+ model = None
26
+
27
+ @app.on_event("startup")
28
+ async def startup_event():
29
+ global model
30
+ if os.path.exists(MODEL_PATH):
31
+ print("🏥 Loading Medical Model...")
32
+ model = load_model(MODEL_PATH, compile=False)
33
+ print("✅ Model Ready.")
34
+ else:
35
+ print("❌ Model not found! Upload retina_model_deploy.keras")
36
+
37
+ # 4. BEN GRAHAM PREPROCESSING (MUST MATCH TRAINING EXACTLY)
38
+ def crop_image_from_gray(img, tol=7):
39
+ """
40
+ Crops the black circle borders.
41
+ """
42
+ if img.ndim == 2:
43
+ mask = img > tol
44
+ return img[np.ix_(mask.any(1),mask.any(0))]
45
+ elif img.ndim == 3:
46
+ gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
47
+ mask = gray_img > tol
48
+
49
+ check_shape = img[:,:,0][np.ix_(mask.any(1),mask.any(0))].shape[0]
50
+ if (check_shape == 0): return img
51
+ else:
52
+ img1=img[:,:,0][np.ix_(mask.any(1),mask.any(0))]
53
+ img2=img[:,:,1][np.ix_(mask.any(1),mask.any(0))]
54
+ img3=img[:,:,2][np.ix_(mask.any(1),mask.any(0))]
55
+ img = np.stack([img1,img2,img3],axis=-1)
56
+ return img
57
+
58
+ def preprocess_eye(image_bytes):
59
+ """
60
+ Full Pipeline: Decode -> RGB -> Crop -> Resize -> Gaussian Blur
61
+ """
62
+ # Decode bytes to OpenCV Image
63
+ nparr = np.frombuffer(image_bytes, np.uint8)
64
+ img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
65
+
66
+ # Convert BGR (OpenCV) to RGB (TensorFlow)
67
+ img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
68
+
69
+ # 1. Crop
70
+ img = crop_image_from_gray(img)
71
+
72
+ # 2. Resize (260x260 - Same as Training)
73
+ img = cv2.resize(img, (260, 260))
74
+
75
+ # 3. Ben Graham Smoothing (The Secret Sauce)
76
+ # This highlights the veins/hemorrhages
77
+ img = cv2.addWeighted(img, 4, cv2.GaussianBlur(img, (0,0), 30), -4, 128)
78
+
79
+ # Prepare for Model (Expand dims)
80
+ # Note: Model has Rescaling(1./255) layer inside, so we send raw pixels
81
+ img_array = np.expand_dims(img, axis=0)
82
+
83
+ return img_array
84
+
85
+ # 5. API ENDPOINT
86
+ @app.post("/api/predict")
87
+ async def predict(file: UploadFile = File(...)):
88
+ if not model:
89
+ raise HTTPException(status_code=500, detail="Model not loaded")
90
+
91
+ try:
92
+ contents = await file.read()
93
+ processed_image = preprocess_eye(contents)
94
+
95
+ prediction = model.predict(processed_image)
96
+
97
+ # Binary Classification (Sigmoid output: 0 to 1)
98
+ # Closer to 0 = Healthy, Closer to 1 = Disease
99
+ score = float(prediction[0][0])
100
+
101
+ # Logic: We set threshold at 0.5
102
+ label = "Disease Detected" if score >= 0.5 else "Healthy Eye"
103
+ confidence = score if score >= 0.5 else 1 - score
104
+
105
+ return {
106
+ "diagnosis": label,
107
+ "confidence": round(confidence * 100, 2),
108
+ "raw_score": score
109
+ }
110
+ except Exception as e:
111
+ return {"error": str(e)}
112
+
113
+ # 6. SERVE FRONTEND (The "One Place" Magic)
114
+ # We will serve the React 'dist' folder
115
+ if os.path.exists("dist"):
116
+ app.mount("/", StaticFiles(directory="dist", html=True), name="static")
117
+ else:
118
+ @app.get("/")
119
+ def home():
120
+ return {"message": "API is running. Upload React 'dist' folder to serve UI."}
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ python-multipart
4
+ tensorflow-cpu>=2.16.0
5
+ opencv-python-headless
6
+ numpy
7
+ pillow