File size: 4,660 Bytes
6dda80b c97b6b6 6dda80b c97b6b6 6dda80b c97b6b6 6dda80b c97b6b6 6dda80b c97b6b6 6dda80b cd37ee9 6dda80b c97b6b6 6dda80b 23f1a0d c97b6b6 6dda80b c97b6b6 6dda80b 23f1a0d c97b6b6 23f1a0d 6dda80b c97b6b6 6dda80b 23f1a0d c97b6b6 6dda80b 23f1a0d 6dda80b 23f1a0d 6dda80b 23f1a0d 6dda80b 23f1a0d 6dda80b 23f1a0d 6dda80b 23f1a0d 6dda80b 23f1a0d 8143697 23f1a0d 6dda80b 23f1a0d c97b6b6 23f1a0d c97b6b6 6dda80b | 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 | 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
@app.get("/")
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"
}
}
@app.get("/health")
def health():
return {"status": "ok", "model": MODEL_FILE}
@app.post("/detect")
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) |