-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi.py
More file actions
162 lines (141 loc) · 6.01 KB
/
Copy pathapi.py
File metadata and controls
162 lines (141 loc) · 6.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import io
import sys
import cv2
import numpy as np
from fastapi import FastAPI, HTTPException, UploadFile, File
from fastapi.middleware.cors import CORSMiddleware
from pathlib import Path
from typing import Dict, Any, Optional
import json
from loguru import logger
# Configure Loguru for JSON output to stdout
logger.remove()
logger.add(sys.stdout, format="{message}", serialize=True)
# ── Compatibility shims for models trained with custom YOLO fork ──────────────
from ultralytics.nn.tasks import DetectionModel
from ultralytics.utils.loss import E2ELoss, v8DetectionLoss
class WeightedDetectionLoss(v8DetectionLoss): pass
class WeightedE2ELoss(E2ELoss): pass
class WeightedDetectionModel(DetectionModel): pass
import __main__
__main__.WeightedDetectionLoss = WeightedDetectionLoss
__main__.WeightedE2ELoss = WeightedE2ELoss
__main__.WeightedDetectionModel = WeightedDetectionModel
# ─────────────────────────────────────────────────────────────────────────────
# Import the services
from pipelines.ibr.service import load_models as load_ibr_models, predict_ibr_realtime, validate_ibr_step
from pipelines.flat_ribbon.service import load_models as load_fr_models, predict_fr_realtime, validate_fr_step
from pipelines.multi_tube.service import load_models as load_mt_models, predict_mt_realtime, validate_mt_step
app = FastAPI(title="HFCL OTDR Real-time API", description="High-performance in-memory inference for OTDR cables.")
# Enable CORS for all origins, methods, and headers
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
BASE_DIR = Path(__file__).resolve().parent
MODELS_DIR = BASE_DIR / "models"
# Global config (could be moved to a file)
CONFIG = {
"ibr": {
"RIBBON_VALUE_MAP": {"0": 5, "1": 1}
}
}
@app.on_event("startup")
async def startup_event():
"""Load all models into memory once at startup."""
logger.info("Initializing inference models...")
load_ibr_models(MODELS_DIR)
load_fr_models(MODELS_DIR)
load_mt_models(MODELS_DIR)
logger.info("All models loaded successfully.")
async def process_image_upload(file: UploadFile) -> np.ndarray:
"""Helper to decode uploaded image buffer into OpenCV array."""
if not file.content_type.startswith("image/"):
logger.warning(f"Invalid content type: {file.content_type}")
raise HTTPException(status_code=400, detail="File must be an image.")
try:
contents = await file.read()
nparr = np.frombuffer(contents, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
if img is None:
raise ValueError("Failed to decode image buffer.")
return img
except Exception as e:
logger.error(f"Image processing error: {e}")
raise HTTPException(status_code=400, detail="Invalid image encoding.")
@app.post("/api/v1/predict/ibr")
async def predict_ibr(
file: UploadFile = File(...)
):
logger.info(f"Received IBR prediction request: {file.filename}")
try:
img = await process_image_upload(file)
# Validation
try:
with open(BASE_DIR / "configs/ibr/6912F.json") as f:
val_cfg = json.load(f)
# IBR requires config for markings logic
results = predict_ibr_realtime(img, val_cfg)
validate_ibr_step(val_cfg, results, file.filename)
results["validation"] = {"status": "success"}
except Exception as e:
results["validation"] = {"status": "failed", "error": str(e)}
logger.info(f"IBR Inference complete. Status: {results.get('status')}")
return results
except HTTPException:
raise
except Exception as e:
logger.exception(f"Unexpected error during IBR inference: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/v1/predict/flat_ribbon")
async def predict_flat_ribbon(
file: UploadFile = File(...)
):
logger.info(f"Received Flat-Ribbon prediction request: {file.filename}")
try:
img = await process_image_upload(file)
results = predict_fr_realtime(img)
# Validation
try:
with open(BASE_DIR / "configs/flat_ribbon/config.json") as f:
val_cfg = json.load(f)
validate_fr_step(val_cfg, results, file.filename)
results["validation"] = {"status": "success"}
except Exception as e:
results["validation"] = {"status": "failed", "error": str(e)}
logger.info(f"Flat-Ribbon Inference complete. Status: {results.get('status')}")
return results
except HTTPException:
raise
except Exception as e:
logger.exception(f"Unexpected error during Flat-Ribbon inference: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/v1/predict/multi_tube")
async def predict_multi_tube(
file: UploadFile = File(...)
):
logger.info(f"Received Multi-Tube prediction request: {file.filename}")
try:
img = await process_image_upload(file)
results = predict_mt_realtime(img)
# Validation
try:
with open(BASE_DIR / "configs/multi_tube/config.json") as f:
val_cfg = json.load(f)
validate_mt_step(val_cfg, results, file.filename)
results["validation"] = {"status": "success"}
except Exception as e:
results["validation"] = {"status": "failed", "error": str(e)}
logger.info(f"Multi-Tube Inference complete. Status: {results.get('status')}")
return results
except HTTPException:
raise
except Exception as e:
logger.exception(f"Unexpected error during Multi-Tube inference: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/health")
async def health_check():
return {"status": "healthy"}