Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,8 @@ async def openai_vision_endpoint(
try:
# Remove data URI prefix if present
header, encoded = img_data.split(",", 1) if "," in img_data else (None, img_data)
contents = base64.b64decode(encoded)
img = Image.open(io.BytesIO(contents))
contents = await asyncio.to_thread(base64.b64decode, encoded)
img = await asyncio.to_thread(Image.open, io.BytesIO(contents))

# Dimension check
if img.width * img.height > MAX_PIXELS:
Expand Down Expand Up @@ -321,7 +321,7 @@ async def _get_image_from_request(request: Request, file: Optional[UploadFile]):
raise HTTPException(status_code=499, detail="Client closed connection")
if len(contents) > MAX_IMAGE_SIZE:
raise HTTPException(status_code=413, detail="File too large")
img = Image.open(io.BytesIO(contents))
img = await asyncio.to_thread(Image.open, io.BytesIO(contents))
filename = file.filename or "uploaded_image.jpg"
else:
# Handle JSON body (Base64)
Expand All @@ -346,8 +346,8 @@ async def _get_image_from_request(request: Request, file: Optional[UploadFile]):
ocr_req = OCRRequest(**body)
# Remove data URI prefix if present
header, encoded = ocr_req.image.split(",", 1) if "," in ocr_req.image else (None, ocr_req.image)
contents = base64.b64decode(encoded)
img = Image.open(io.BytesIO(contents))
contents = await asyncio.to_thread(base64.b64decode, encoded)
img = await asyncio.to_thread(Image.open, io.BytesIO(contents))
filename = "base64_image.jpg"
except HTTPException:
raise
Expand Down
21 changes: 15 additions & 6 deletions src/core/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,17 @@ def __init__(
self.recognizer50 = None

# ThreadPoolExecutor for parallelizing character recognition across lines
# Allow override via environment variable OCR_MAX_WORKERS, or use a bounded default.
# Too many concurrent threads calling ORT sessions on CPU causes high contention and thrashing.
max_workers_env = os.getenv("OCR_MAX_WORKERS")
if max_workers_env:
max_workers = int(max_workers_env)
else:
cpu_count = os.cpu_count() or 4
max_workers = min(4, cpu_count)

self.executor = ThreadPoolExecutor(
max_workers=os.cpu_count() or 4,
max_workers=max_workers,
thread_name_prefix="ocr_worker"
)

Expand Down Expand Up @@ -221,14 +230,14 @@ def ocr(self, pil_image: Image.Image, img_name: str = "image.jpg") -> Dict[str,
classeslist = list(self.detector.classes.values())

# Prepare data for NDL-style XML conversion
resultobj = [dict(), dict()]
resultobj[0][0] = list()
for i in range(17):
resultobj[1][i] = []
resultobj = [
{0: []},
{i: [] for i in range(17)}
]
for det in detections:
xmin, ymin, xmax, ymax = det["box"]
conf = det["confidence"]
char_count = det.get("pred_char_count", 100.0) # v1.2.1 uses char_count in resultobj
char_count = det.get("pred_char_count", 100.0) # v1.2.1 uses char_count in resultobj
if det["class_index"] == 0:
resultobj[0][0].append([xmin, ymin, xmax, ymax])
# v1.2.1 adds char_count here
Expand Down
Loading