babelbox / backend.py
malen-h's picture
Add index.html and worker.js for RF-DETR object detection interface
3c9e0f6
Raw
History Blame Contribute Delete
5.54 kB
#!/usr/bin/env python3
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
import os
import tempfile
import threading
from pathlib import Path
from urllib.parse import urlparse
MODEL_ID = "Roboflow/rf-detr-medium"
HOST = os.environ.get("HOST", "0.0.0.0")
PORT = int(os.environ.get("PORT", "7860"))
ROOT = Path(__file__).resolve().parent
detector = None
detector_lock = threading.Lock()
def get_detector():
global detector
if detector is None:
with detector_lock:
if detector is None:
from transformers import pipeline
detector = pipeline(
"object-detection",
model=MODEL_ID,
device_map="auto",
)
return detector
def box_area(result):
box = result.get("box") or {}
width = max(0, box.get("xmax", 0) - box.get("xmin", 0))
height = max(0, box.get("ymax", 0) - box.get("ymin", 0))
return width * height
class Handler(BaseHTTPRequestHandler):
def end_headers(self):
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
self.send_header(
"Access-Control-Allow-Headers",
"Content-Type, X-Confidence-Threshold, X-Focus-Mode",
)
self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
self.send_header("Cross-Origin-Opener-Policy", "same-origin")
self.send_header("Cross-Origin-Resource-Policy", "cross-origin")
super().end_headers()
def do_OPTIONS(self):
self.send_response(HTTPStatus.NO_CONTENT)
self.end_headers()
def do_HEAD(self):
if not self.send_static(include_body=False):
self.send_response(HTTPStatus.NOT_FOUND)
self.end_headers()
def do_GET(self):
path = urlparse(self.path).path
if path == "/health":
self.send_json({
"ok": True,
"model": MODEL_ID,
"loaded": detector is not None,
})
return
if self.send_static():
return
self.send_json({"error": "not found"}, status=HTTPStatus.NOT_FOUND)
def do_POST(self):
if urlparse(self.path).path != "/detect":
self.send_json({"error": "not found"}, status=404)
return
length = int(self.headers.get("Content-Length", "0"))
image_bytes = self.rfile.read(length)
threshold = float(self.headers.get("X-Confidence-Threshold", "0.75"))
focus_mode = self.headers.get("X-Focus-Mode", "true") == "true"
image_path = None
try:
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as image_file:
image_file.write(image_bytes)
image_path = Path(image_file.name)
raw_results = get_detector()(str(image_path), threshold=threshold)
results = []
for item in raw_results:
result = {
"label": item["label"],
"score": float(item["score"]),
"box": item["box"],
}
result["area"] = box_area(result)
results.append(result)
if focus_mode:
results = [item for item in results if item["label"] != "person"]
results = [
item for item in results
if item["area"] >= 2500
]
results.sort(key=lambda item: item["score"] * item["area"], reverse=True)
results = results[:1 if focus_mode else 3]
self.send_json({"results": results})
except Exception as exc:
self.send_json({"error": str(exc)}, status=HTTPStatus.INTERNAL_SERVER_ERROR)
finally:
if image_path is not None:
try:
image_path.unlink(missing_ok=True)
except Exception:
pass
def send_json(self, data, status=200):
body = json.dumps(data).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def send_static(self, include_body=True):
path = urlparse(self.path).path
files = {
"/": ("index.html", "text/html; charset=utf-8"),
"/index.html": ("index.html", "text/html; charset=utf-8"),
"/worker.js": ("worker.js", "application/javascript; charset=utf-8"),
}
if path not in files:
return False
filename, content_type = files[path]
file_path = ROOT / filename
if not file_path.is_file():
return False
body = file_path.read_bytes()
self.send_response(HTTPStatus.OK)
self.send_header("Content-Type", content_type)
self.send_header("Content-Length", str(len(body)))
self.end_headers()
if include_body:
self.wfile.write(body)
return True
def log_message(self, format, *args):
print("%s - %s" % (self.address_string(), format % args))
if __name__ == "__main__":
print(f"Starting RF-DETR backend on http://{HOST}:{PORT}")
print(f"Model will load on first /detect request: {MODEL_ID}")
ThreadingHTTPServer((HOST, PORT), Handler).serve_forever()