Update app.py
Browse files
app.py
CHANGED
|
@@ -43,21 +43,20 @@ def detect_aruco_scale(img, marker_size_cm=10.0):
|
|
| 43 |
corners, ids, _ = ARUCO_DETECTOR.detectMarkers(gray)
|
| 44 |
if ids is None:
|
| 45 |
return None, None
|
| 46 |
-
# Use first detected marker
|
| 47 |
marker_corners = corners[0][0]
|
| 48 |
w_px = np.linalg.norm(marker_corners[0] - marker_corners[1])
|
| 49 |
h_px = np.linalg.norm(marker_corners[1] - marker_corners[2])
|
| 50 |
pixels_per_cm = (w_px + h_px) / 2 / marker_size_cm
|
| 51 |
-
return pixels_per_cm, corners
|
| 52 |
|
| 53 |
@app.get("/")
|
| 54 |
def root():
|
| 55 |
return {
|
| 56 |
"model": MODEL_FILE,
|
| 57 |
"classes": list(CLASS_COLORS.keys()),
|
| 58 |
-
"calibration": "Auto via ArUco marker on hard hat (10cm
|
| 59 |
"endpoints": {
|
| 60 |
-
"POST /detect": "Send image → get detections in cm
|
| 61 |
"GET /health": "Health check"
|
| 62 |
}
|
| 63 |
}
|
|
@@ -70,7 +69,8 @@ def health():
|
|
| 70 |
async def detect(
|
| 71 |
file: UploadFile = File(...),
|
| 72 |
marker_size_cm: float = Form(10.0),
|
| 73 |
-
confidence: float = Form(0.
|
|
|
|
| 74 |
):
|
| 75 |
start = time.time()
|
| 76 |
|
|
@@ -78,7 +78,7 @@ async def detect(
|
|
| 78 |
nparr = np.frombuffer(contents, np.uint8)
|
| 79 |
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
| 80 |
|
| 81 |
-
#
|
| 82 |
pixels_per_cm, aruco_corners = detect_aruco_scale(img, marker_size_cm)
|
| 83 |
calibrated = pixels_per_cm is not None
|
| 84 |
|
|
@@ -86,54 +86,58 @@ async def detect(
|
|
| 86 |
if calibrated:
|
| 87 |
cv2.aruco.drawDetectedMarkers(img, aruco_corners)
|
| 88 |
|
| 89 |
-
# Run YOLO
|
| 90 |
-
results = model(img, conf=confidence)[0]
|
| 91 |
detections = []
|
| 92 |
|
| 93 |
for box in results.boxes:
|
| 94 |
x1, y1, x2, y2 = map(int, box.xyxy[0])
|
| 95 |
cls = results.names[int(box.cls[0])]
|
| 96 |
-
conf = round(float(box.conf[0]),
|
| 97 |
w_px = x2 - x1
|
| 98 |
h_px = y2 - y1
|
| 99 |
color = CLASS_COLORS.get(cls, (0, 255, 0))
|
| 100 |
|
| 101 |
-
w_cm = round(w_px / pixels_per_cm, 1) if calibrated else None
|
| 102 |
-
h_cm = round(h_px / pixels_per_cm, 1) if calibrated else None
|
| 103 |
|
| 104 |
# Draw bounding box
|
| 105 |
cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
|
| 106 |
|
| 107 |
-
# Label
|
| 108 |
label = f"{cls} {conf:.2f}"
|
| 109 |
if calibrated:
|
| 110 |
label += f" | {w_cm}x{h_cm}cm"
|
| 111 |
-
|
| 112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
detections.append({
|
| 115 |
"class": cls,
|
| 116 |
-
"confidence":
|
| 117 |
"bbox": [int(x1), int(y1), int(x2), int(y2)],
|
| 118 |
"width_px": int(w_px),
|
| 119 |
"height_px": int(h_px),
|
| 120 |
-
"width_cm": float(w_cm) if w_cm is not None else None,
|
| 121 |
-
"height_cm": float(h_cm) if h_cm is not None else None,
|
| 122 |
})
|
| 123 |
|
| 124 |
# Encode result image
|
| 125 |
-
_, buf
|
| 126 |
-
img_b64
|
| 127 |
|
| 128 |
return {
|
| 129 |
-
"success":
|
| 130 |
-
"calibrated":
|
| 131 |
-
"pixels_per_cm":
|
| 132 |
-
"marker_size_cm":
|
| 133 |
-
"inference_time_s":
|
| 134 |
-
"total":
|
| 135 |
-
"detections":
|
| 136 |
-
"image_base64":
|
| 137 |
}
|
| 138 |
|
| 139 |
if __name__ == "__main__":
|
|
|
|
| 43 |
corners, ids, _ = ARUCO_DETECTOR.detectMarkers(gray)
|
| 44 |
if ids is None:
|
| 45 |
return None, None
|
|
|
|
| 46 |
marker_corners = corners[0][0]
|
| 47 |
w_px = np.linalg.norm(marker_corners[0] - marker_corners[1])
|
| 48 |
h_px = np.linalg.norm(marker_corners[1] - marker_corners[2])
|
| 49 |
pixels_per_cm = (w_px + h_px) / 2 / marker_size_cm
|
| 50 |
+
return float(pixels_per_cm), corners
|
| 51 |
|
| 52 |
@app.get("/")
|
| 53 |
def root():
|
| 54 |
return {
|
| 55 |
"model": MODEL_FILE,
|
| 56 |
"classes": list(CLASS_COLORS.keys()),
|
| 57 |
+
"calibration": "Auto via ArUco marker on hard hat (10cm x 10cm)",
|
| 58 |
"endpoints": {
|
| 59 |
+
"POST /detect": "Send image → get detections in cm",
|
| 60 |
"GET /health": "Health check"
|
| 61 |
}
|
| 62 |
}
|
|
|
|
| 69 |
async def detect(
|
| 70 |
file: UploadFile = File(...),
|
| 71 |
marker_size_cm: float = Form(10.0),
|
| 72 |
+
confidence: float = Form(0.2),
|
| 73 |
+
iou: float = Form(0.3)
|
| 74 |
):
|
| 75 |
start = time.time()
|
| 76 |
|
|
|
|
| 78 |
nparr = np.frombuffer(contents, np.uint8)
|
| 79 |
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
| 80 |
|
| 81 |
+
# ArUco auto-calibration
|
| 82 |
pixels_per_cm, aruco_corners = detect_aruco_scale(img, marker_size_cm)
|
| 83 |
calibrated = pixels_per_cm is not None
|
| 84 |
|
|
|
|
| 86 |
if calibrated:
|
| 87 |
cv2.aruco.drawDetectedMarkers(img, aruco_corners)
|
| 88 |
|
| 89 |
+
# Run YOLO with lower confidence + iou for more detections
|
| 90 |
+
results = model(img, conf=confidence, iou=iou)[0]
|
| 91 |
detections = []
|
| 92 |
|
| 93 |
for box in results.boxes:
|
| 94 |
x1, y1, x2, y2 = map(int, box.xyxy[0])
|
| 95 |
cls = results.names[int(box.cls[0])]
|
| 96 |
+
conf = round(float(box.conf[0]), 2)
|
| 97 |
w_px = x2 - x1
|
| 98 |
h_px = y2 - y1
|
| 99 |
color = CLASS_COLORS.get(cls, (0, 255, 0))
|
| 100 |
|
| 101 |
+
w_cm = round(float(w_px) / pixels_per_cm, 1) if calibrated else None
|
| 102 |
+
h_cm = round(float(h_px) / pixels_per_cm, 1) if calibrated else None
|
| 103 |
|
| 104 |
# Draw bounding box
|
| 105 |
cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
|
| 106 |
|
| 107 |
+
# Label
|
| 108 |
label = f"{cls} {conf:.2f}"
|
| 109 |
if calibrated:
|
| 110 |
label += f" | {w_cm}x{h_cm}cm"
|
| 111 |
+
|
| 112 |
+
# Background for label text
|
| 113 |
+
(tw, th), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.55, 2)
|
| 114 |
+
cv2.rectangle(img, (x1, y1 - th - 10), (x1 + tw, y1), color, -1)
|
| 115 |
+
cv2.putText(img, label, (x1, y1 - 5),
|
| 116 |
+
cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 0), 2)
|
| 117 |
|
| 118 |
detections.append({
|
| 119 |
"class": cls,
|
| 120 |
+
"confidence": conf,
|
| 121 |
"bbox": [int(x1), int(y1), int(x2), int(y2)],
|
| 122 |
"width_px": int(w_px),
|
| 123 |
"height_px": int(h_px),
|
| 124 |
+
"width_cm": round(float(w_cm), 1) if w_cm is not None else None,
|
| 125 |
+
"height_cm": round(float(h_cm), 1) if h_cm is not None else None,
|
| 126 |
})
|
| 127 |
|
| 128 |
# Encode result image
|
| 129 |
+
_, buf = cv2.imencode(".jpg", img)
|
| 130 |
+
img_b64 = base64.b64encode(buf).decode()
|
| 131 |
|
| 132 |
return {
|
| 133 |
+
"success": True,
|
| 134 |
+
"calibrated": bool(calibrated),
|
| 135 |
+
"pixels_per_cm": round(pixels_per_cm, 2) if calibrated else None,
|
| 136 |
+
"marker_size_cm": float(marker_size_cm),
|
| 137 |
+
"inference_time_s": round(float(time.time() - start), 3),
|
| 138 |
+
"total": int(len(detections)),
|
| 139 |
+
"detections": detections,
|
| 140 |
+
"image_base64": img_b64,
|
| 141 |
}
|
| 142 |
|
| 143 |
if __name__ == "__main__":
|