Spaces:
Sleeping
Sleeping
Update main.py
Browse files
main.py
CHANGED
|
@@ -13,9 +13,11 @@ from scipy.optimize import curve_fit, minimize
|
|
| 13 |
from ultralytics import YOLO
|
| 14 |
from fastapi import FastAPI, UploadFile, File, Query, Form
|
| 15 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
| 16 |
import uvicorn
|
| 17 |
from skimage import color
|
| 18 |
import hashlib
|
|
|
|
| 19 |
|
| 20 |
# OOM PREVENTION
|
| 21 |
torch.set_num_threads(1)
|
|
@@ -796,36 +798,41 @@ app.add_middleware(
|
|
| 796 |
|
| 797 |
processor = WatermelonProcessor(MODEL_PATH)
|
| 798 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 799 |
@app.get("/")
|
| 800 |
-
def read_root(): return {"status": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 801 |
|
| 802 |
@app.post("/process_single")
|
| 803 |
async def process_single(
|
| 804 |
file: UploadFile = File(...),
|
| 805 |
include_image: bool = Query(True),
|
| 806 |
-
password: str = Form("")
|
|
|
|
| 807 |
):
|
|
|
|
| 808 |
request_t = time.perf_counter()
|
| 809 |
|
| 810 |
-
# --- SECURE PASSWORD CHECK ---
|
| 811 |
expected_hash = "9139eb3676d5dfafced7613f044d86d9e7c84f40a04c83ddce062878621315d0"
|
| 812 |
if hashlib.sha256(password.encode('utf-8')).hexdigest() != expected_hash:
|
| 813 |
-
return ProcessResult(success=False, message="Unauthorized
|
| 814 |
|
| 815 |
contents, img = None, None
|
| 816 |
|
| 817 |
try:
|
| 818 |
contents = await file.read()
|
| 819 |
-
if not contents:
|
| 820 |
-
res = ProcessResult(success=False, message="Empty upload.", filename=file.filename)
|
| 821 |
-
res.processing_ms = int(round((time.perf_counter() - request_t) * 1000))
|
| 822 |
-
return res.__dict__
|
| 823 |
|
| 824 |
img = cv2.imdecode(np.frombuffer(contents, np.uint8), cv2.IMREAD_COLOR)
|
| 825 |
-
if img is None:
|
| 826 |
-
res = ProcessResult(success=False, message="Could not decode image.", filename=file.filename)
|
| 827 |
-
res.processing_ms = int(round((time.perf_counter() - request_t) * 1000))
|
| 828 |
-
return res.__dict__
|
| 829 |
|
| 830 |
scale_ratio = 1.0
|
| 831 |
h, w = img.shape[:2]
|
|
@@ -833,19 +840,31 @@ async def process_single(
|
|
| 833 |
scale_ratio = MAX_IMAGE_SIZE / float(max(h, w))
|
| 834 |
img = cv2.resize(img, (int(w * scale_ratio), int(h * scale_ratio)), interpolation=cv2.INTER_AREA)
|
| 835 |
|
| 836 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 837 |
res.processing_ms = int(round((time.perf_counter() - request_t) * 1000))
|
| 838 |
return res.__dict__
|
| 839 |
|
| 840 |
except Exception as exc:
|
| 841 |
traceback.print_exc()
|
| 842 |
-
|
| 843 |
-
success=False,
|
| 844 |
-
message=f"Server error: {type(exc).__name__}: {exc}",
|
| 845 |
-
filename=file.filename,
|
| 846 |
-
processing_ms=int(round((time.perf_counter() - request_t) * 1000)),
|
| 847 |
-
)
|
| 848 |
-
return res.__dict__
|
| 849 |
|
| 850 |
finally:
|
| 851 |
del img, contents
|
|
|
|
| 13 |
from ultralytics import YOLO
|
| 14 |
from fastapi import FastAPI, UploadFile, File, Query, Form
|
| 15 |
from fastapi.middleware.cors import CORSMiddleware
|
| 16 |
+
from fastapi.concurrency import run_in_threadpool
|
| 17 |
import uvicorn
|
| 18 |
from skimage import color
|
| 19 |
import hashlib
|
| 20 |
+
import asyncio
|
| 21 |
|
| 22 |
# OOM PREVENTION
|
| 23 |
torch.set_num_threads(1)
|
|
|
|
| 798 |
|
| 799 |
processor = WatermelonProcessor(MODEL_PATH)
|
| 800 |
|
| 801 |
+
# --- CONCURRENCY & QUEUE MANAGEMENT ---
|
| 802 |
+
dev_lock = asyncio.Lock()
|
| 803 |
+
gen_lock = asyncio.Lock()
|
| 804 |
+
dev_queue_count = 0
|
| 805 |
+
gen_queue_count = 0
|
| 806 |
+
|
| 807 |
@app.get("/")
|
| 808 |
+
def read_root(): return {"status": "Watermelon API is awake and running!"}
|
| 809 |
+
|
| 810 |
+
@app.get("/queue_status")
|
| 811 |
+
def get_queue_status():
|
| 812 |
+
return {"dev_queue": dev_queue_count, "gen_queue": gen_queue_count}
|
| 813 |
|
| 814 |
@app.post("/process_single")
|
| 815 |
async def process_single(
|
| 816 |
file: UploadFile = File(...),
|
| 817 |
include_image: bool = Query(True),
|
| 818 |
+
password: str = Form(""),
|
| 819 |
+
username: str = Form("")
|
| 820 |
):
|
| 821 |
+
global dev_queue_count, gen_queue_count
|
| 822 |
request_t = time.perf_counter()
|
| 823 |
|
|
|
|
| 824 |
expected_hash = "9139eb3676d5dfafced7613f044d86d9e7c84f40a04c83ddce062878621315d0"
|
| 825 |
if hashlib.sha256(password.encode('utf-8')).hexdigest() != expected_hash:
|
| 826 |
+
return ProcessResult(success=False, message="Unauthorized.", filename=file.filename).__dict__
|
| 827 |
|
| 828 |
contents, img = None, None
|
| 829 |
|
| 830 |
try:
|
| 831 |
contents = await file.read()
|
| 832 |
+
if not contents: return ProcessResult(success=False, message="Empty.", filename=file.filename).__dict__
|
|
|
|
|
|
|
|
|
|
| 833 |
|
| 834 |
img = cv2.imdecode(np.frombuffer(contents, np.uint8), cv2.IMREAD_COLOR)
|
| 835 |
+
if img is None: return ProcessResult(success=False, message="Decode error.", filename=file.filename).__dict__
|
|
|
|
|
|
|
|
|
|
| 836 |
|
| 837 |
scale_ratio = 1.0
|
| 838 |
h, w = img.shape[:2]
|
|
|
|
| 840 |
scale_ratio = MAX_IMAGE_SIZE / float(max(h, w))
|
| 841 |
img = cv2.resize(img, (int(w * scale_ratio), int(h * scale_ratio)), interpolation=cv2.INTER_AREA)
|
| 842 |
|
| 843 |
+
# --- CPU CORE ROUTING & QUEUE LOGIC ---
|
| 844 |
+
is_dev = (username.strip().lower() == 'devtest')
|
| 845 |
+
|
| 846 |
+
# run_in_threadpool prevents OpenCV/YOLO from freezing the API so /queue_status can still answer
|
| 847 |
+
if is_dev:
|
| 848 |
+
dev_queue_count += 1
|
| 849 |
+
try:
|
| 850 |
+
async with dev_lock:
|
| 851 |
+
res = await run_in_threadpool(processor.process_image, img, file.filename, scale_ratio, include_image)
|
| 852 |
+
finally:
|
| 853 |
+
dev_queue_count -= 1
|
| 854 |
+
else:
|
| 855 |
+
gen_queue_count += 1
|
| 856 |
+
try:
|
| 857 |
+
async with gen_lock:
|
| 858 |
+
res = await run_in_threadpool(processor.process_image, img, file.filename, scale_ratio, include_image)
|
| 859 |
+
finally:
|
| 860 |
+
gen_queue_count -= 1
|
| 861 |
+
|
| 862 |
res.processing_ms = int(round((time.perf_counter() - request_t) * 1000))
|
| 863 |
return res.__dict__
|
| 864 |
|
| 865 |
except Exception as exc:
|
| 866 |
traceback.print_exc()
|
| 867 |
+
return ProcessResult(success=False, message=str(exc), filename=file.filename).__dict__
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 868 |
|
| 869 |
finally:
|
| 870 |
del img, contents
|