Skip to content

feat(OCR): 全局OCR单例队列,GPU请求串行化#2043

Open
kawayiYokami wants to merge 6 commits into
OneDragon-Anything:mainfrom
kawayiYokami:global-ocr-queue-serialization
Open

feat(OCR): 全局OCR单例队列,GPU请求串行化#2043
kawayiYokami wants to merge 6 commits into
OneDragon-Anything:mainfrom
kawayiYokami:global-ocr-queue-serialization

Conversation

@kawayiYokami
Copy link
Copy Markdown
Contributor

@kawayiYokami kawayiYokami commented Feb 25, 2026

变更内容

  • 新增全局 OCR 执行器 ocr_executor(单线程队列)
  • OnnxOcrMatcher 在 GPU 模式下统一走串行队列(un_ocr / un_ocr_single_line / ocr)
  • CvStepOcr 去掉外层 gpu_executor 包装,避免重复调度
  • 应用关闭时新增 ocr_executor.shutdown\n\n## 目的
  • 避免 OCR 在多线程场景下对 GPU 并发访问导致的不稳定
  • 保持上层调用接口不变,兼容现有业务调用
  • CPU 模式保持原路径,不走 OCR 队列
  • 验证

  • python -m compileall 相关文件通过
  • 并发压测下总耗时接近串行理论值,验证队列串行生效

Summary by CodeRabbit

发布说明

  • 新功能
    • 增加专用 OCR 执行器,支持异步提交与受控同步调用(含超时与线程检测)。
  • 增强
    • OCR 匹配流程实现 GPU 感知分发,GPU/CPU 路径行为更一致并强化输入校验与失败保护。
    • 应用关闭时新增 OCR 执行器的清理步骤。
  • 重构
    • 统一并简化部分 OCR 执行路径,移除旧的 GPU 异步分支以降低竞态风险并统一执行模型。

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 25, 2026

Walkthrough

移除 step_ocr 中的 GPU 异步分支,新增独立的单线程 ocr_executor 并在 OnnxOcrMatcher 中添加 GPU 感知的委派私有实现;应用关闭时新增对 ocr_executor.shutdown 的调用。(50 字内)

Changes

Cohort / File(s) Summary
OCR 执行器
src/one_dragon/utils/ocr_executor.py
新增单线程 ThreadPoolExecutor:线程上下文标记、submitrun_sync(带超时与取消)、is_executor_threadshutdownOCRTimeoutError
OnnxOcrMatcher GPU 路由
src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
为 GPU 情况增加委派路径,新增私有实现 _run_ocr_single_line_impl_run_ocr_impl_ocr_impl;在 is_use_gpu() 为真时通过 ocr_executor 调用并加入输入/模型校验与早退逻辑。
OCR 步骤简化
src/one_dragon/base/cv_process/steps/step_ocr.py
移除对 gpu_executor 的导入与 GPU 异步分支,改为始终同步调用 context.ocr.run_ocr(context.display_image)
应用关闭流程
src/one_dragon/base/operation/one_dragon_context.py
after_app_shutdown 中新增对 ocr_executor.shutdown(wait=False) 的调用以清理 OCR 线程池资源。

Sequence Diagram(s)

sequenceDiagram
    participant Caller as 调用者
    participant OnnxOcr as OnnxOcrMatcher
    participant Executor as ocr_executor
    participant ThreadPool as od_ocr 线程池

    Caller->>OnnxOcr: run_ocr(image)
    alt GPU 已启用
        OnnxOcr->>Executor: run_sync/_submit(_ocr_impl, image)
        Executor->>ThreadPool: 提交任务(标记为 OCR 线程)
        ThreadPool->>ThreadPool: 执行 OCR 模型推理(det/rec/cls)
        ThreadPool-->>Executor: 返回结果 (Future 完成)
        Executor-->>OnnxOcr: 返回解析后的结果
    else GPU 未启用
        OnnxOcr->>OnnxOcr: 直接调用本地实现 _ocr_impl/_run_ocr_impl
    end
    OnnxOcr-->>Caller: 返回 OCR 结果
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • fix: 修复ocr的gpu调用 #1559:对 OnnxOcrMatcher 的 GPU 执行路径与提供者选择进行修改,与本次在 OCR GPU 路由/执行器引入上的改动高度相关。

Suggested reviewers

  • DoctorReid

Poem

🐰 轻跃线间忙,
线程独舞夜未央,
GPU 分路更分明,
同步异步共成行,
代码林中又一芳。

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题准确描述了主要变更:引入全局OCR单例队列和GPU请求串行化机制,这正是整个PR的核心目标。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py (1)

365-381: ⚠️ Potential issue | 🔴 Critical

_ocr_impl 缺少 self._model is Noneimage is None 的防御检查,可能引发 AttributeError

_run_ocr_impl(第 246-250 行)有以下两项防御:

if image is None:
    log.warning('OCR输入的图片为None')
    return {}
if self._model is None and not self.init_model():
    return {}

_ocr_impl 均未做此检查,第 381 行直接调用 self._model.ocr(...)_modelNone 将抛出 AttributeError

🛠️ 建议修复
 def _ocr_impl(self, image: MatLike, threshold: float = 0,
               merge_line_distance: float = -1) -> list[OcrMatchResult]:
     ...
     start_time = time.time()
+    if image is None:
+        log.warning('OCR输入的图片为None')
+        return []
+    if self._model is None and not self.init_model():
+        return []
     ocr_result_list: list[OcrMatchResult] = []
     scan_result_list: list = self._model.ocr(image, cls=False)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py` around lines 365 - 381,
The _ocr_impl method calls self._model.ocr(image, ...) without defensive checks
and can raise AttributeError if image or self._model is None; mirror the guards
used in _run_ocr_impl by first checking if image is None (log warning and return
empty list) and then ensure the model is available (if self._model is None and
not self.init_model(): return empty list), so modify _ocr_impl to validate image
and initialize/check self._model before calling self._model.ocr.
🧹 Nitpick comments (1)
src/one_dragon/utils/ocr_executor.py (1)

45-45: Ruff TRY003:异常消息建议内置于异常类中

Ruff 建议将较长的异常消息封装进自定义异常类,而非直接在 raise 处传递字符串。鉴于目前消息内容简单,可忽略或通过 # noqa: TRY003 标注。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/utils/ocr_executor.py` at line 45, The current raise statement
uses a long message directly in raise TimeoutError(...) which triggers Ruff
TRY003; either define and use a custom exception (e.g., class
OCRTimeoutError(TimeoutError): pass) and raise OCRTimeoutError(f"OCR task timed
out after {timeout} seconds") from e (update any callers accordingly), or keep
the built-in TimeoutError but suppress the lint rule by appending a noqa (e.g.,
the raise line: raise TimeoutError(...)  # noqa: TRY003); locate the raise
TimeoutError(...) in ocr_executor.py (the OCR task executor function) and apply
one of these two fixes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/one_dragon/utils/ocr_executor.py`:
- Line 26: The functions _submit_internal, submit, and run_sync lack proper type
annotations for the fn parameter and the run_sync return type; add a TypeVar
(e.g., T), annotate fn as Callable[..., T] (or Callable[..., Awaitable[T]] if
appropriate), update _submit_internal and submit to return Future[T] and
annotate their fn parameter accordingly, and set run_sync to return T (or
Awaitable[T]→T if it awaits); also import TypeVar, Callable, Awaitable, and
Future from typing/asyncio as needed to satisfy the annotations and keep
signatures consistent (refer to _submit_internal, submit, and run_sync in
ocr_executor.py).
- Around line 40-44: When catching FutureTimeoutError from the future returned
by _submit_internal (the variable f), add a warning log that the future timed
out and may still be running on GPU (include f.cancel() result and f.state() or
str(f) / repr(f) plus the timeout value) so ops can detect leaked GPU work; keep
the existing f.cancel() call but log that cancel may fail for RUNNING futures
and that the underlying GPU task could still occupy resources, using the
module/logger used elsewhere in this file to emit the warning.

---

Outside diff comments:
In `@src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py`:
- Around line 365-381: The _ocr_impl method calls self._model.ocr(image, ...)
without defensive checks and can raise AttributeError if image or self._model is
None; mirror the guards used in _run_ocr_impl by first checking if image is None
(log warning and return empty list) and then ensure the model is available (if
self._model is None and not self.init_model(): return empty list), so modify
_ocr_impl to validate image and initialize/check self._model before calling
self._model.ocr.

---

Nitpick comments:
In `@src/one_dragon/utils/ocr_executor.py`:
- Line 45: The current raise statement uses a long message directly in raise
TimeoutError(...) which triggers Ruff TRY003; either define and use a custom
exception (e.g., class OCRTimeoutError(TimeoutError): pass) and raise
OCRTimeoutError(f"OCR task timed out after {timeout} seconds") from e (update
any callers accordingly), or keep the built-in TimeoutError but suppress the
lint rule by appending a noqa (e.g., the raise line: raise TimeoutError(...)  #
noqa: TRY003); locate the raise TimeoutError(...) in ocr_executor.py (the OCR
task executor function) and apply one of these two fixes.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 313b6cd and 5471d30.

📒 Files selected for processing (4)
  • src/one_dragon/base/cv_process/steps/step_ocr.py
  • src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
  • src/one_dragon/base/operation/one_dragon_context.py
  • src/one_dragon/utils/ocr_executor.py

Comment thread src/one_dragon/utils/ocr_executor.py Outdated
Comment thread src/one_dragon/utils/ocr_executor.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/one_dragon/utils/ocr_executor.py (1)

14-16: 可以把超时文案收敛到 OCRTimeoutError 内部,减少重复字符串。

这能同时消除 TRY003 告警并让异常语义更集中。

♻️ 建议修改
 class OCRTimeoutError(TimeoutError):
     """OCR executor timed out while waiting for result."""
-    pass
+    def __init__(self, timeout: float | None):
+        super().__init__(f"OCR task timed out after {timeout} seconds")
...
-        raise OCRTimeoutError(f"OCR task timed out after {timeout} seconds") from e
+        raise OCRTimeoutError(timeout) from e

Also applies to: 61-61

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/utils/ocr_executor.py` around lines 14 - 16, Replace repeated
timeout strings by centralizing them in the OCRTimeoutError class: add a default
message (e.g. DEFAULT_MESSAGE or via __init__ that sets a default message like
"OCR executor timed out while waiting for result.") and allow an optional custom
message, then update all code that raises OCRTimeoutError (references around the
raise sites that previously passed the literal string) to raise
OCRTimeoutError() (or OCRTimeoutError() without duplicating the text). This
collapses duplicated literals into the OCRTimeoutError class and removes the
TRY003 warning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py`:
- Around line 220-223: The GPU branch currently calls ocr_executor.run_sync(...)
without specifying timeout, causing the executor's default (60s) to count queue
wait time and produce OCRTimeoutError for requests that haven't started; to fix,
explicitly pass timeout=None to ocr_executor.run_sync when delegating to the GPU
path (e.g., where is_use_gpu() leads to
ocr_executor.run_sync(self._run_ocr_single_line_impl, ...)) so the executor
won't apply the enqueue wait timeout, and apply the same change to the other
GPU-run_sync call sites mentioned (the two similar GPU branches around the other
public OCR methods); leave existing OCRTimeoutError handling unchanged.
- Around line 224-229: 函数 _run_ocr_single_line_impl 在 strict_one_line=True 时直接调用
_run_ocr_without_det,缺少对空图像的保护,会触发底层异常;请在 _run_ocr_single_line_impl 开头复用与
_run_ocr_impl/run_ocr 相同的空输入检查(如判断 image 为 None 或尺寸为0),在检测到空图像时直接返回空字符串并避免调用
_run_ocr_without_det,确保行为与非 strict 分支一致。

In `@src/one_dragon/utils/ocr_executor.py`:
- Around line 64-65: The shutdown function is missing a return type annotation;
update the function signature for shutdown to include an explicit "-> None"
return type (i.e., def shutdown(wait: bool = True) -> None:) so it complies with
the project's type hinting rules; ensure you modify the definition that calls
_executor.shutdown(wait=wait) and keep the existing behavior.

---

Nitpick comments:
In `@src/one_dragon/utils/ocr_executor.py`:
- Around line 14-16: Replace repeated timeout strings by centralizing them in
the OCRTimeoutError class: add a default message (e.g. DEFAULT_MESSAGE or via
__init__ that sets a default message like "OCR executor timed out while waiting
for result.") and allow an optional custom message, then update all code that
raises OCRTimeoutError (references around the raise sites that previously passed
the literal string) to raise OCRTimeoutError() (or OCRTimeoutError() without
duplicating the text). This collapses duplicated literals into the
OCRTimeoutError class and removes the TRY003 warning.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5471d30 and 820d2bf.

📒 Files selected for processing (2)
  • src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
  • src/one_dragon/utils/ocr_executor.py

Comment thread src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
Comment thread src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
Comment thread src/one_dragon/utils/ocr_executor.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py`:
- Around line 388-393: In _ocr_impl, the guard only checks "if image is None" so
empty image arrays (size == 0) still reach self._model.ocr; update the check in
the _ocr_impl method to mirror the earlier protection by treating images with
zero pixels as invalid (e.g., check image is None or getattr(image, "size",
None) == 0) and return [] before attempting to init_model or call
self._model.ocr, keeping the existing init_model() / self._model flow intact.

In `@src/one_dragon/utils/ocr_executor.py`:
- Around line 50-63: 在捕获 FutureTimeoutError 后不要盲目将所有超时类异常转换为 OCRTimeoutError;检查
future f 是否已完成(f.done()):如果 f.done() 为真,说明异常来自任务内部,应重新抛出原始异常(raise e)以保留任务内部的
TimeoutError;如果 f.done() 为假,才视为真正的等待超时,像现在一样调用 f.cancel()、记录日志并抛出
OCRTimeoutError(timeout);在实现中保留对 f.result(timeout=timeout) 的原用法并在 except
FutureTimeoutError as e 分支中按上述逻辑分流。

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 820d2bf and 02f826d.

📒 Files selected for processing (2)
  • src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
  • src/one_dragon/utils/ocr_executor.py

Comment thread src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py Outdated
Comment thread src/one_dragon/utils/ocr_executor.py
@kawayiYokami kawayiYokami force-pushed the global-ocr-queue-serialization branch from bc81ca9 to b840ff7 Compare March 2, 2026 10:03
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py (1)

246-257: ⚠️ Potential issue | 🟠 Major

_run_ocr_impl 仍缺少空图保护,run_ocr 路径存在输入异常风险

Line 255 目前只判断了 image is None。当输入是空数组(size == 0)时,仍会继续进入模型调用,和本文件其他 OCR 路径的防护不一致。

🛠️ 建议修改
-        if image is None:
-            log.warning('OCR输入的图片为None')
+        if image is None or getattr(image, 'size', 0) == 0:
+            log.warning('OCR输入的图片为None或空图')
             return {}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py` around lines 246 - 257,
The _run_ocr_impl function currently only checks for image is None and should
also guard against empty images to match other OCR paths; update _run_ocr_impl
(and ensure consistency with run_ocr call sites) to detect an empty image (e.g.,
image size == 0 or equivalent for MatLike) and return an empty dict/log a
warning like the existing None branch instead of proceeding to model calls;
reference the _run_ocr_impl function and run_ocr pathways when making the change
so all OCR entry points have the same empty-image protection.
🧹 Nitpick comments (1)
src/one_dragon/utils/ocr_executor.py (1)

56-63: 日志字符串格式可与仓库规范统一为 f-string

Line 57 当前使用 % 模板,占位符参数可以直接改为 f-string,保持与仓库规范一致。

♻️ 建议修改
         log.warning(
-            "OCR task timeout after %.2fs; cancel=%s running=%s done=%s future=%r",
-            timeout if timeout is not None else -1.0,
-            cancelled,
-            f.running(),
-            f.done(),
-            f,
+            f"OCR task timeout after {(timeout if timeout is not None else -1.0):.2f}s; "
+            f"cancel={cancelled} running={f.running()} done={f.done()} future={f!r}"
         )

As per coding guidelines: Use f-strings for string formatting instead of other formatting methods.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/utils/ocr_executor.py` around lines 56 - 63, Replace the
percent-style formatted log.warning call with an f-string: in the OCR timeout
logging site (the log.warning that references timeout, cancelled, f.running(),
f.done(), and f), build a single f-string that interpolates timeout (use -1.0 if
None), cancelled, f.running(), f.done(), and the future object f directly, and
use that f-string as the first argument to log.warning (keeping the same log
level and message semantics).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/one_dragon/utils/ocr_executor.py`:
- Around line 16-17: The constructor OCRTimeoutError.__init__ is missing its
return type annotation; update the signature of OCRTimeoutError.__init__(self,
timeout: float | None) to include the explicit return type -> None to match
project typing conventions and other constructors in the codebase.

---

Outside diff comments:
In `@src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py`:
- Around line 246-257: The _run_ocr_impl function currently only checks for
image is None and should also guard against empty images to match other OCR
paths; update _run_ocr_impl (and ensure consistency with run_ocr call sites) to
detect an empty image (e.g., image size == 0 or equivalent for MatLike) and
return an empty dict/log a warning like the existing None branch instead of
proceeding to model calls; reference the _run_ocr_impl function and run_ocr
pathways when making the change so all OCR entry points have the same
empty-image protection.

---

Nitpick comments:
In `@src/one_dragon/utils/ocr_executor.py`:
- Around line 56-63: Replace the percent-style formatted log.warning call with
an f-string: in the OCR timeout logging site (the log.warning that references
timeout, cancelled, f.running(), f.done(), and f), build a single f-string that
interpolates timeout (use -1.0 if None), cancelled, f.running(), f.done(), and
the future object f directly, and use that f-string as the first argument to
log.warning (keeping the same log level and message semantics).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 02f826d and 881d369.

📒 Files selected for processing (2)
  • src/one_dragon/base/matcher/ocr/onnx_ocr_matcher.py
  • src/one_dragon/utils/ocr_executor.py

Comment on lines +16 to +17
def __init__(self, timeout: float | None):
super().__init__(f"OCR task timed out after {timeout} seconds")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and read the relevant section
if [ -f "src/one_dragon/utils/ocr_executor.py" ]; then
  echo "=== File content around lines 16-17 ===" 
  sed -n '10,25p' "src/one_dragon/utils/ocr_executor.py" | cat -n
else
  echo "File not found, searching for it..."
  find . -name "ocr_executor.py" -type f
fi

Repository: OneDragon-Anything/ZenlessZoneZero-OneDragon

Length of output: 671


🏁 Script executed:

# Check other __init__ methods in Python files to see if -> None is consistently used
echo "=== Checking __init__ return type patterns ===" 
rg "def __init__\(" --type py -A 1 | head -30

Repository: OneDragon-Anything/ZenlessZoneZero-OneDragon

Length of output: 1963


🏁 Script executed:

# Check specifically for __init__ methods with and without -> None in the codebase
echo "=== Count of __init__ with -> None ===" 
rg "def __init__\([^)]*\)\s*->\s*None:" --type py | wc -l
echo "=== Count of __init__ without -> None ===" 
rg "def __init__\([^)]*\):" --type py | grep -v "-> None" | wc -l

Repository: OneDragon-Anything/ZenlessZoneZero-OneDragon

Length of output: 301


补齐 OCRTimeoutError.__init__ 的返回类型注解

__init__ 在 Line 16 缺少 -> None,与当前仓库类型规范和代码库现有模式不一致。

🛠️ 建议修改
 class OCRTimeoutError(TimeoutError):
     """OCR executor timed out while waiting for result."""
-    def __init__(self, timeout: float | None):
+    def __init__(self, timeout: float | None) -> None:
         super().__init__(f"OCR task timed out after {timeout} seconds")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/one_dragon/utils/ocr_executor.py` around lines 16 - 17, The constructor
OCRTimeoutError.__init__ is missing its return type annotation; update the
signature of OCRTimeoutError.__init__(self, timeout: float | None) to include
the explicit return type -> None to match project typing conventions and other
constructors in the codebase.

@ShadowLemoon ShadowLemoon changed the title feat: 全局OCR单例队列,GPU请求串行化 feat(OCR): 全局OCR单例队列,GPU请求串行化 Mar 6, 2026
Copy link
Copy Markdown
Collaborator

@DoctorReid DoctorReid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

有点忘记当初为什么没有在ocr matcher里统一做这个事情了 (论留下文档的重要性)

但看代码,感觉是因为多个onnx模型,并发调用也可能出现问题,可以写个比较简单的测试脚本,让ocr模型和flash模型(或者其他的)一起跑个1分钟看看。

如果是的话,还是得统一用一个gpu_executor,只不过可以在各个matcher里统一处理。

@A-nony-mous
Copy link
Copy Markdown
Contributor

我写了一个压测脚本:

stress_test_directdml.py
配置好环境变量后放在repo根,用uv run python stress_test_directdml.py --runs 5 --loops 500 --yolo-workers 4 --ocr-workers 0运行即可复现

问题应该还是onnxruntime-directml自己的多线程不安全问题。
image
是以yolo自己交替跑模型复现的。

我注意到yolo底层有的没走gpu_executor.py

def inference(self, input_tensor: np.ndarray):
"""
图片输入到模型中进行推理
:param input_tensor: 输入模型的图片 RGB通道
:return: onnx模型推理得到的结果
"""
outputs = self.session.run(self.output_names, {self.input_names[0]: input_tensor})
return outputs

def inference(self, input_tensor: np.ndarray):
"""
图片输入到模型中进行推理
:param input_tensor: 输入模型的图片 RGB通道
:return: onnx模型推理得到的结果
"""
outputs = self.session.run(self.output_names, {self.input_names[0]: input_tensor})
return outputs

这可能就导致yolo自己挂了。之前的UnicodeDecodeError: 'utf-8' codec can't decode可能就是yolo出的东西有问题,但巧了还是送入python的业务逻辑了,转文字的时候出现问题。

总结:此pr可能方向错了(不需要单独给ocr维护一套),是把所有的session.run()都兜底走gpu_executor.py

ref: #874 #1675

@ShadowLemoon
Copy link
Copy Markdown
Collaborator

压测可以做成插件app的形式 plugins\README.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants