Spaces:
Sleeping
Sleeping
File size: 6,183 Bytes
2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e 488b041 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e d307ed5 2fd5a2e | 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 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | # app.py
import os
import io
from pathlib import Path
from dotenv import load_dotenv
from flask import Flask, request, jsonify
from flask_cors import CORS
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from ultralytics import YOLO
from PIL import Image
# Load .env when running locally (do not commit .env)
load_dotenv()
# -----------------------
# Config from environment
# -----------------------
FLASK_ENV = os.getenv("FLASK_ENV", "production")
SECRET_KEY = os.getenv("SECRET_KEY") # must be set in host secrets
CORS_ORIGINS = os.getenv("CORS_ORIGINS", "") # comma separated list of allowed origins
# Per-model API keys (set these in the host as secrets)
API_KEY_AADHAAR = os.getenv("HF_AADHAAR_API_KEY") # token for Aadhaar endpoint
API_KEY_PAN = os.getenv("HF_PAN_API_KEY") # token for PAN endpoint
API_KEY_DL = os.getenv("HF_DRIVING_LICENSE_API_KEY") # token for DL endpoint
# Model paths (relative to repo; put .pt files under models/)
AADHAAR_MODEL_PATH = os.getenv("AADHAAR_MODEL_PATH", "./models/best.pt")
PAN_MODEL_PATH = os.getenv("PAN_MODEL_PATH", "./models/Pan_best.pt")
DL_MODEL_PATH = os.getenv("DL_MODEL_PATH", "./models/DL_best.pt")
# Rate limiting - adjust as needed
DEFAULT_RATE = os.getenv("DEFAULT_RATE", "30/minute") # 30 requests per minute per IP
# -----------------------
# App creation & security
# -----------------------
app = Flask(__name__)
app.secret_key = SECRET_KEY or os.urandom(24) # fallback for local dev only
# Configure CORS
allowed_origins = [o.strip() for o in CORS_ORIGINS.split(",") if o.strip()]
if not allowed_origins:
# safer default: no origins allowed unless explicitly set
cors = CORS(app, resources={r"/*": {"origins": []}})
else:
cors = CORS(app, resources={r"/*": {"origins": allowed_origins}}, supports_credentials=True)
# Rate limiter (prevents simple abuse)
limiter = Limiter(
key_func=get_remote_address,
default_limits=[DEFAULT_RATE],
storage_uri="memory://", # default in-memory store (ok for small apps)
)
limiter.init_app(app)
# -----------------------
# Load models at startup
# -----------------------
models = {}
model_map = {
"aadhaar": Path(AADHAAR_MODEL_PATH),
"pan": Path(PAN_MODEL_PATH),
"dl": Path(DL_MODEL_PATH),
}
def safe_load_model(key, path: Path):
if not path.exists():
app.logger.warning(f"Model file not found for {key}: {path}")
return None
try:
app.logger.info(f"Loading model for {key} from {path.name}")
model = YOLO(str(path))
return model
except Exception as e:
app.logger.error(f"Failed to load model {key}: {e}")
return None
for k,p in model_map.items():
models[k] = safe_load_model(k, p)
# -----------------------
# Utility: inference
# -----------------------
def run_inference(model, pil_image):
"""Run ultralytics YOLO inference on a PIL image and return simple JSON."""
if model is None:
return []
results = model(pil_image) # ultralytics lets you pass PIL Image
r = results[0] # first (and usually only) result
boxes = getattr(r, "boxes", None)
names = getattr(r, "names", {})
detections = []
if boxes is None:
return detections
for box in boxes:
# ultralytics Box object fields depend on package version; defensive access:
try:
xyxy = box.xyxy.cpu().numpy().tolist()
if isinstance(xyxy[0], list):
xyxy = xyxy[0]
except Exception:
xyxy = getattr(box, "xyxy", None)
if xyxy is None:
continue
try:
conf = float(box.conf.cpu().numpy()) if hasattr(box, "conf") else None
except Exception:
conf = None
try:
cls = int(box.cls.cpu().numpy()) if hasattr(box, "cls") else None
except Exception:
cls = None
label = names.get(cls, str(cls)) if cls is not None else None
detections.append({
"box": [float(x) for x in xyxy],
"confidence": conf,
"class": label
})
return detections
# -----------------------
# Auth helper
# -----------------------
def check_api_key(model_key):
"""Return True if request has a valid x-api-key header for the given model_key."""
incoming = request.headers.get("x-api-key", "")
if model_key == "aadhaar":
return bool(incoming and API_KEY_AADHAAR and incoming == API_KEY_AADHAAR)
if model_key == "pan":
return bool(incoming and API_KEY_PAN and incoming == API_KEY_PAN)
if model_key == "dl":
return bool(incoming and API_KEY_DL and incoming == API_KEY_DL)
return False
# -----------------------
# Routes
# -----------------------
@app.route("/", methods=["GET"])
def health():
loaded = [k for k,v in models.items() if v is not None]
return jsonify({"status": "ok", "models_loaded": loaded})
@app.route("/predict/<model_key>", methods=["POST"])
@limiter.limit(DEFAULT_RATE)
def predict(model_key):
model_key = model_key.lower()
if model_key not in models:
return jsonify({"error": "unknown model key"}), 404
# Authentication: must provide x-api-key header matching the env secret
if not check_api_key(model_key):
return jsonify({"error": "unauthorized"}), 401
if "image" not in request.files:
return jsonify({"error": "no image provided; use multipart form field 'image'"}), 400
file = request.files["image"]
try:
img = Image.open(io.BytesIO(file.read())).convert("RGB")
except Exception as e:
return jsonify({"error": "invalid image", "detail": str(e)}), 400
try:
detections = run_inference(models[model_key], img)
return jsonify({"predictions": detections})
except Exception as e:
app.logger.exception("Inference failed")
return jsonify({"error": "inference failed", "detail": str(e)}), 500
# -----------------------
# Run (for local dev)
# -----------------------
if __name__ == "__main__":
# Port 7860 is common on HF Spaces; change if needed
app.run(host="0.0.0.0", port=int(os.getenv("PORT", 7860))) |