Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, File, UploadFile, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import StreamingResponse, JSONResponse | |
| from ultralytics import YOLO | |
| from PIL import Image | |
| import numpy as np | |
| import cv2 | |
| import io | |
| import mediapipe as mp | |
| app = FastAPI() | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| model = YOLO("app/pigmentnew.pt") | |
| print("✅ YOLO Model Loaded. Classes:", model.names) | |
| def detect_and_zoom_face(image_pil, zoom_scale=3.0): | |
| image_rgb = np.array(image_pil) | |
| image_bgr = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR) | |
| with mp.solutions.face_detection.FaceDetection(model_selection=1, min_detection_confidence=0.6) as detector: | |
| results = detector.process(image_bgr) | |
| if not results.detections: | |
| return None, "No face detected" | |
| detection = results.detections[0] | |
| box = detection.location_data.relative_bounding_box | |
| h, w, _ = image_bgr.shape | |
| x = int(box.xmin * w) | |
| y = int(box.ymin * h) | |
| box_w = int(box.width * w) | |
| box_h = int(box.height * h) | |
| pad_x = int(box_w * 0.4) | |
| pad_y_top = int(box_h * 1.0) | |
| pad_y_bottom = int(box_h * 0.15) | |
| x1 = max(0, x - pad_x) | |
| y1 = max(0, y - pad_y_top) | |
| x2 = min(w, x + box_w + pad_x) | |
| y2 = min(h, y + box_h + pad_y_bottom) | |
| face_crop = image_rgb[y1:y2, x1:x2] | |
| zoomed = cv2.resize(face_crop, None, fx=zoom_scale, fy=zoom_scale, interpolation=cv2.INTER_CUBIC) | |
| zoomed_pil = Image.fromarray(zoomed) | |
| return zoomed_pil, None | |
| async def zoom_face(image: UploadFile = File(...)): | |
| try: | |
| contents = await image.read() | |
| image_pil = Image.open(io.BytesIO(contents)).convert('RGB') | |
| zoomed_face, error = detect_and_zoom_face(image_pil) | |
| if error: | |
| return JSONResponse(content={"error": error}, status_code=400) | |
| buf = io.BytesIO() | |
| zoomed_face.save(buf, format='JPEG') | |
| buf.seek(0) | |
| return StreamingResponse(buf, media_type="image/jpeg") | |
| except Exception as e: | |
| return JSONResponse(content={"error": str(e)}, status_code=500) | |
| async def predict(image: UploadFile = File(...)): | |
| if not image: | |
| raise HTTPException(status_code=400, detail="No image provided") | |
| try: | |
| contents = await image.read() | |
| img = Image.open(io.BytesIO(contents)).convert("RGB") | |
| except Exception as e: | |
| raise HTTPException(status_code=400, detail=f"Invalid image: {str(e)}") | |
| results = model.predict(img, conf=0.09, imgsz=1024) | |
| result = results[0] | |
| detections = [] | |
| if result.boxes is not None: | |
| for box in result.boxes: | |
| cls_id = int(box.cls[0].item()) | |
| label = result.names[cls_id] | |
| confidence = round(float(box.conf[0].item()), 2) | |
| coords = box.xyxy[0].tolist() | |
| box_coords = [round(c, 2) for c in coords] | |
| detections.append({"label": label, "confidence": confidence, "box": box_coords}) | |
| return {"detections": detections} | |