junaid17 commited on
Commit
07526ce
·
verified ·
1 Parent(s): 67464a6

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +202 -0
app.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import shutil
4
+ import logging
5
+ from contextlib import asynccontextmanager
6
+
7
+ from PIL import Image
8
+ from fastapi import FastAPI, UploadFile, File, HTTPException, Query
9
+ from fastapi.staticfiles import StaticFiles
10
+ from fastapi.middleware.cors import CORSMiddleware
11
+ from dotenv import load_dotenv
12
+
13
+ from scripts.gradcam import get_resnet_gradcam
14
+ from scripts.yolo_predict import get_yolo_damage_boxes
15
+ from scripts.load_models import initialize_models
16
+ from scripts.prediction_helper import ResnetONNXPredictor, FusionONNXPredictor
17
+
18
+ logging.basicConfig(
19
+ level=logging.INFO,
20
+ format="%(asctime)s - %(levelname)s - %(name)s - %(message)s"
21
+ )
22
+ logger = logging.getLogger(__name__)
23
+
24
+ load_dotenv()
25
+
26
+ UPLOAD_DIR = "static/uploads"
27
+ RESULT_DIR = "static/results"
28
+ os.makedirs(UPLOAD_DIR, exist_ok=True)
29
+ os.makedirs(RESULT_DIR, exist_ok=True)
30
+
31
+ CLASS_MAP = {
32
+ 0: "Front Breakage",
33
+ 1: "Front Crushed",
34
+ 2: "Front Normal",
35
+ 3: "Rear Breakage",
36
+ 4: "Rear Crushed",
37
+ 5: "Rear Normal"
38
+ }
39
+
40
+ # Unified dictionary to hold all loaded models
41
+ ml_models = {}
42
+
43
+ @asynccontextmanager
44
+ async def lifespan(app: FastAPI):
45
+ logger.info("Loading models at startup...")
46
+ try:
47
+ models = initialize_models(CLASS_MAP)
48
+ ml_models["resnet_onnx"] = ResnetONNXPredictor(models["resnet_onnx"], CLASS_MAP)
49
+ ml_models["fusion_onnx"] = FusionONNXPredictor(models["fusion_onnx"], CLASS_MAP)
50
+ ml_models["resnet_pt"] = models["resnet_pt"]
51
+ ml_models["yolo_onnx"] = models["yolo_onnx"]
52
+
53
+ logger.info("All models loaded successfully.")
54
+ except Exception as e:
55
+ logger.exception("Model loading failed.")
56
+ raise RuntimeError(str(e))
57
+
58
+ yield
59
+ ml_models.clear()
60
+ logger.info("Application shutdown.")
61
+
62
+ app = FastAPI(lifespan=lifespan)
63
+
64
+ app.add_middleware(
65
+ CORSMiddleware,
66
+ allow_origins=["*"],
67
+ allow_credentials=True,
68
+ allow_methods=["*"],
69
+ allow_headers=["*"],
70
+ )
71
+
72
+ app.mount("/static", StaticFiles(directory="static"), name="static")
73
+
74
+ def validate_image(upload_file: UploadFile):
75
+ if not upload_file.content_type.startswith("image/"):
76
+ raise HTTPException(status_code=400, detail="Uploaded file must be an image.")
77
+
78
+ def save_upload(upload_file: UploadFile):
79
+ unique_id = str(uuid.uuid4())
80
+ filename = f"{unique_id}_input.jpg"
81
+ file_path = os.path.join(UPLOAD_DIR, filename)
82
+
83
+ with open(file_path, "wb") as buffer:
84
+ shutil.copyfileobj(upload_file.file, buffer)
85
+
86
+ return unique_id, filename, file_path
87
+
88
+ # Endpoints
89
+ @app.get("/")
90
+ def api_status():
91
+ return {"status": "API is running"}
92
+
93
+ @app.post("/predict/resnet")
94
+ async def resnet_prediction(file: UploadFile = File(...)):
95
+ validate_image(file)
96
+ try:
97
+ pil_image = Image.open(file.file).convert("RGB")
98
+ result = ml_models["resnet_onnx"].predict(pil_image)
99
+ return {"status": "success", "prediction": result}
100
+ except Exception as e:
101
+ logger.exception("ResNet ONNX prediction failed.")
102
+ raise HTTPException(status_code=500, detail=str(e))
103
+
104
+ @app.post("/predict/fusion")
105
+ async def fusion_prediction(file: UploadFile = File(...)):
106
+ validate_image(file)
107
+ try:
108
+ pil_image = Image.open(file.file).convert("RGB")
109
+ result = ml_models["fusion_onnx"].predict(pil_image)
110
+ return {"status": "success", "prediction": result}
111
+ except Exception as e:
112
+ logger.exception("Fusion ONNX prediction failed.")
113
+ raise HTTPException(status_code=500, detail=str(e))
114
+
115
+ @app.post("/predict/yolo")
116
+ async def yolo_detection(file: UploadFile = File(...)):
117
+ validate_image(file)
118
+ try:
119
+ unique_id, input_filename, input_path = save_upload(file)
120
+ output_name = f"{unique_id}_yolo.jpg"
121
+ output_path = os.path.join(RESULT_DIR, output_name)
122
+
123
+ result = get_yolo_damage_boxes(input_path, ml_models["yolo_onnx"], output_path)
124
+
125
+ return {
126
+ "status": "success",
127
+ "original_image": f"/static/uploads/{input_filename}",
128
+ "yolo_image": f"/static/results/{output_name}",
129
+ "detections": result["detections"],
130
+ "total_detections": result["total_detections"],
131
+ "message": result["message"]
132
+ }
133
+ except Exception as e:
134
+ logger.exception("YOLO ONNX detection failed.")
135
+ raise HTTPException(status_code=500, detail=str(e))
136
+
137
+ @app.post("/predict/gradcam")
138
+ async def gradcam_generation(file: UploadFile = File(...)):
139
+ validate_image(file)
140
+ try:
141
+ unique_id, input_filename, input_path = save_upload(file)
142
+ output_name = f"{unique_id}_gradcam.jpg"
143
+ output_path = os.path.join(RESULT_DIR, output_name)
144
+
145
+ get_resnet_gradcam(input_path, ml_models["resnet_pt"], output_path)
146
+
147
+ return {
148
+ "status": "success",
149
+ "original_image": f"/static/uploads/{input_filename}",
150
+ "gradcam_image": f"/static/results/{output_name}"
151
+ }
152
+ except Exception as e:
153
+ logger.exception("Grad-CAM generation failed.")
154
+ raise HTTPException(status_code=500, detail=str(e))
155
+
156
+ @app.post("/predict/comprehensive")
157
+ async def comprehensive_prediction(
158
+ file: UploadFile = File(...),
159
+ mode: str = Query("fusion", description="Classification model to use: 'resnet' or 'fusion'")
160
+ ):
161
+ validate_image(file)
162
+ mode = mode.lower()
163
+
164
+ if mode not in {"resnet", "fusion"}:
165
+ raise HTTPException(status_code=400, detail="mode must be 'resnet' or 'fusion'")
166
+
167
+ try:
168
+ unique_id, input_filename, input_path = save_upload(file)
169
+ pil_image = Image.open(input_path).convert("RGB")
170
+
171
+ # 1. Classification
172
+ if mode == "resnet":
173
+ classification_result = ml_models["resnet_onnx"].predict(pil_image)
174
+ else:
175
+ classification_result = ml_models["fusion_onnx"].predict(pil_image)
176
+
177
+ # 2. YOLO Bounding Boxes
178
+ yolo_output_name = f"{unique_id}_yolo.jpg"
179
+ yolo_output_path = os.path.join(RESULT_DIR, yolo_output_name)
180
+ yolo_result = get_yolo_damage_boxes(input_path, ml_models["yolo_onnx"], yolo_output_path)
181
+
182
+ # 3. Grad-CAM
183
+ gradcam_output_name = f"{unique_id}_gradcam.jpg"
184
+ gradcam_output_path = os.path.join(RESULT_DIR, gradcam_output_name)
185
+ get_resnet_gradcam(input_path, ml_models["resnet_pt"], gradcam_output_path)
186
+
187
+ return {
188
+ "status": "success",
189
+ "mode": mode,
190
+ "original_image": f"/static/uploads/{input_filename}",
191
+ "classification": classification_result,
192
+ "yolo": {
193
+ "image": f"/static/results/{yolo_output_name}",
194
+ "detections": yolo_result["detections"],
195
+ "total_detections": yolo_result["total_detections"]
196
+ },
197
+ "gradcam": f"/static/results/{gradcam_output_name}"
198
+ }
199
+
200
+ except Exception as e:
201
+ logger.exception("Comprehensive prediction pipeline failed.")
202
+ raise HTTPException(status_code=500, detail=str(e))