diff --git a/api.py b/api.py index bbdd276..dd87c57 100644 --- a/api.py +++ b/api.py @@ -137,7 +137,8 @@ def process_unwrap(net, payload: bytes) -> bytes: result = np.clip(result, 0, 255).astype(np.uint8) result_bgr = np.ascontiguousarray(result[:, :, ::-1]) - ok, buf = cv2.imencode(".png", result_bgr) + # ИЗМЕНЕНО: кодирование в JPEG с качеством 100% + ok, buf = cv2.imencode(".jpg", result_bgr, [int(cv2.IMWRITE_JPEG_QUALITY), 100]) if not ok: raise HTTPException(status_code=500, detail="Failed to encode result image") return buf.tobytes() @@ -263,7 +264,8 @@ def process_docres(model, payload: bytes, task: str) -> bytes: except Exception as exc: raise HTTPException(status_code=500, detail=f"{task} failed: {exc}") from exc - ok, buf = cv2.imencode(".png", np.ascontiguousarray(result)) + # ИЗМЕНЕНО: кодирование в JPEG с качеством 100% + ok, buf = cv2.imencode(".jpg", np.ascontiguousarray(result), [int(cv2.IMWRITE_JPEG_QUALITY), 100]) if not ok: raise HTTPException(status_code=500, detail="Failed to encode result image") return buf.tobytes() @@ -316,50 +318,51 @@ def health(): } } - @app.post("/unwrap") -def unwrap(request: Request, file: UploadFile = File(...)): - try: - file.file.seek(0) - payload = file.file.read() - except Exception as exc: - raise HTTPException(status_code=400, detail="Cannot read uploaded file") from exc +async def unwrap(request: Request, file: UploadFile = File(...)): + # Читаем асинхронно — Starlette сам корректно обработает SpooledTemporaryFile + payload = await file.read() if not payload: raise HTTPException(status_code=400, detail="Uploaded file is empty") - - result_png = process_unwrap(request.app.state.docscanner_net, payload) + + # Тяжёлую работу отправляем в threadpool, чтобы не блокировать event loop + import asyncio + loop = asyncio.get_running_loop() + result_jpeg = await loop.run_in_executor( + None, process_unwrap, request.app.state.docscanner_net, payload + ) return Response( - content=result_png, - media_type="image/png", - headers={"Content-Disposition": 'inline; filename="unwrap.png"', "Cache-Control": "no-store"}, + content=result_jpeg, + media_type="image/jpeg", + headers={"Content-Disposition": 'inline; filename="unwrap.jpg"', "Cache-Control": "no-store"}, ) -def _docres_endpoint(request: Request, file: UploadFile, task: str): - try: - file.file.seek(0) - payload = file.file.read() - except Exception as exc: - raise HTTPException(status_code=400, detail="Cannot read uploaded file") from exc +async def _docres_endpoint_async(request: Request, file: UploadFile, task: str): + payload = await file.read() if not payload: raise HTTPException(status_code=400, detail="Uploaded file is empty") - - result_png = process_docres(request.app.state.docres_model, payload, task) + + import asyncio + loop = asyncio.get_running_loop() + result_jpeg = await loop.run_in_executor( + None, process_docres, request.app.state.docres_model, payload, task + ) return Response( - content=result_png, - media_type="image/png", - headers={"Content-Disposition": f'inline; filename="{task}.png"', "Cache-Control": "no-store"}, + content=result_jpeg, + media_type="image/jpeg", + headers={"Content-Disposition": f'inline; filename="{task}.jpg"', "Cache-Control": "no-store"}, ) @app.post("/deblur") -def deblur(request: Request, file: UploadFile = File(...)): - return _docres_endpoint(request, file, "deblur") +async def deblur(request: Request, file: UploadFile = File(...)): + return await _docres_endpoint_async(request, file, "deblur") @app.post("/illumination_correct") -def illumination_correct(request: Request, file: UploadFile = File(...)): - return _docres_endpoint(request, file, "illumination_correct") +async def illumination_correct(request: Request, file: UploadFile = File(...)): + return await _docres_endpoint_async(request, file, "illumination_correct") if __name__ == "__main__":