Download app.py from newtechdevng/construction-detection-api: direct link, hf CLI and curl.
- Browser
- Download file 4.66 kB
-
https://huggingface.co/spaces/newtechdevng/construction-detection-api/resolve/main/app.py
- Command line
-
hf download hf://spaces/newtechdevng/construction-detection-api/app.py
-
curl -L -o app.py https://huggingface.co/spaces/newtechdevng/construction-detection-api/resolve/main/app.py
4.66 kB
| from fastapi import FastAPI, File, UploadFile, Form | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from huggingface_hub import hf_hub_download | |
| from ultralytics import YOLO | |
| import cv2 | |
| import numpy as np | |
| import base64 | |
| import time | |
| import os | |
| app = FastAPI(title="Construction Detection API") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Load YOLO model | |
| HF_REPO_ID = "newtechdevng/construction_detection_fine_tune" | |
| MODEL_FILE = "best_v2_finetune.pt" | |
| model_path = hf_hub_download(repo_id=HF_REPO_ID, filename=MODEL_FILE) | |
| model = YOLO(model_path) | |
| # ArUco setup | |
| ARUCO_DICT = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_4X4_50) | |
| ARUCO_PARAMS = cv2.aruco.DetectorParameters() | |
| ARUCO_DETECTOR = cv2.aruco.ArucoDetector(ARUCO_DICT, ARUCO_PARAMS) | |
| CLASS_COLORS = { | |
| "beam": (255, 100, 0), | |
| "column": ( 0, 255, 255), | |
| "door": (255, 0, 255), | |
| "floor": ( 0, 255, 0), | |
| "stairs": (255, 255, 0), | |
| "wall": ( 0, 100, 255), | |
| "window": (100, 0, 255), | |
| } | |
| def detect_aruco_scale(img, marker_size_cm=10.0): | |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | |
| corners, ids, _ = ARUCO_DETECTOR.detectMarkers(gray) | |
| if ids is None: | |
| return None, None | |
| marker_corners = corners[0][0] | |
| w_px = np.linalg.norm(marker_corners[0] - marker_corners[1]) | |
| h_px = np.linalg.norm(marker_corners[1] - marker_corners[2]) | |
| pixels_per_cm = (w_px + h_px) / 2 / marker_size_cm | |
| return float(pixels_per_cm), corners | |
| def root(): | |
| return { | |
| "model": MODEL_FILE, | |
| "classes": list(CLASS_COLORS.keys()), | |
| "calibration": "Auto via ArUco marker on hard hat (10cm x 10cm)", | |
| "endpoints": { | |
| "POST /detect": "Send image → get detections in cm", | |
| "GET /health": "Health check" | |
| } | |
| } | |
| def health(): | |
| return {"status": "ok", "model": MODEL_FILE} | |
| async def detect( | |
| file: UploadFile = File(...), | |
| marker_size_cm: float = Form(10.0), | |
| confidence: float = Form(0.2), | |
| iou: float = Form(0.3) | |
| ): | |
| start = time.time() | |
| contents = await file.read() | |
| nparr = np.frombuffer(contents, np.uint8) | |
| img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) | |
| # ArUco auto-calibration | |
| pixels_per_cm, aruco_corners = detect_aruco_scale(img, marker_size_cm) | |
| calibrated = pixels_per_cm is not None | |
| # Draw ArUco marker highlight | |
| if calibrated: | |
| cv2.aruco.drawDetectedMarkers(img, aruco_corners) | |
| # Run YOLO with lower confidence + iou for more detections | |
| results = model(img, conf=confidence, iou=iou)[0] | |
| detections = [] | |
| for box in results.boxes: | |
| x1, y1, x2, y2 = map(int, box.xyxy[0]) | |
| cls = results.names[int(box.cls[0])] | |
| conf = round(float(box.conf[0]), 2) | |
| w_px = x2 - x1 | |
| h_px = y2 - y1 | |
| color = CLASS_COLORS.get(cls, (0, 255, 0)) | |
| w_cm = round(float(w_px) / pixels_per_cm, 1) if calibrated else None | |
| h_cm = round(float(h_px) / pixels_per_cm, 1) if calibrated else None | |
| # Draw bounding box | |
| cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) | |
| # Label | |
| label = f"{cls} {conf:.2f}" | |
| if calibrated: | |
| label += f" | {w_cm}x{h_cm}cm" | |
| # Background for label text | |
| (tw, th), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.55, 2) | |
| cv2.rectangle(img, (x1, y1 - th - 10), (x1 + tw, y1), color, -1) | |
| cv2.putText(img, label, (x1, y1 - 5), | |
| cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 0), 2) | |
| detections.append({ | |
| "class": cls, | |
| "confidence": conf, | |
| "bbox": [int(x1), int(y1), int(x2), int(y2)], | |
| "width_px": int(w_px), | |
| "height_px": int(h_px), | |
| "width_cm": round(float(w_cm), 1) if w_cm is not None else None, | |
| "height_cm": round(float(h_cm), 1) if h_cm is not None else None, | |
| }) | |
| # Encode result image | |
| _, buf = cv2.imencode(".jpg", img) | |
| img_b64 = base64.b64encode(buf).decode() | |
| return { | |
| "success": True, | |
| "calibrated": bool(calibrated), | |
| "pixels_per_cm": round(pixels_per_cm, 2) if calibrated else None, | |
| "marker_size_cm": float(marker_size_cm), | |
| "inference_time_s": round(float(time.time() - start), 3), | |
| "total": int(len(detections)), | |
| "detections": detections, | |
| "image_base64": img_b64, | |
| } | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=7860) |