ocr

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 3, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package ocr 封装 ddddocr 验证码识别能力。

使用 //go:embed 将模型文件直接嵌入二进制,无需运行时下载。 首次调用 Recognize 时会自动将模型文件提取到临时目录。

跨平台支持:原生库(onnxruntime)按 (GOOS, GOARCH) 用 build tag 隔离 嵌入到不同的源文件(onnx_win_amd64.go 等),每个平台只携带自己那份。

Index

Constants

This section is empty.

Variables

View Source
var OnnxRuntimeDLL []byte

Functions

This section is empty.

Types

type OCR

type OCR struct {
	// contains filtered or unexported fields
}

OCR 是验证码识别器,一旦初始化可重复使用。 多 Client 推荐共享同一个 Pool 实例(见 client.New 的 WithOCRConcurrency), 避免重复解压模型。

func New

func New() *OCR

New 创建独立的 OCR 识别器(惰性初始化,首次调用时才提取模型文件)。 业务代码一般用 Pool 实例共享单例引擎;测试可以用 New() 创建隔离实例。

func (*OCR) Close

func (o *OCR) Close() error

Close 释放 OCR 资源并清理临时文件。 返回任何清理过程中遇到的错误(Windows AV 持锁、Linux 权限拒绝等场景), 让调用方知情,避免临时目录永久泄漏到 %TEMP%。

Close 后再次调用 Recognize 会返回 "OCR 已关闭" 错误,而不是触发 nil panic。

closed 改 atomic.Bool,Close 内先 Store(true),让所有持 o.mu 阻塞在

Classification 之前的 goroutine 立即在二次检查中失败(永不访问已关闭 ddddocr
session)。这是 use-after-close 窗口的修复核心。

func (*OCR) Recognize

func (o *OCR) Recognize(imageData []byte) (string, error)

Recognize 对图片字节进行验证码识别,返回识别出的文本。 imageData 应为 JPEG 或 PNG 编码的字节。

type Pool added in v0.2.0

type Pool struct {
	// contains filtered or unexported fields
}

Pool 是多个 OCR 实例的池,允许并发识别(默认 1 实例,兼容单例行为)。

ONNX Runtime session 不是线程安全的(一个 session 同一时刻只能一个线程调用), 所以单实例下并发请求会被 sync.Mutex 串行化,N 并发 Login 的 wall time = N x 单次延迟。

启用并发:NewPool(n) 暗示期望 n 个独立 session 实例,具体预热由首次 Recognize 的惰性初始化完成。 内存代价:每个实例约 50MB(ONNX 模型 + 原生库解压到独立 tempDir),n=4 ≈ 200MB。 业务场景:批量调用 Login() 时才需要调高;单 Login 调一次用 1 实例足够。

inits 字段用 sync.Map 存储已注册实例,原因:

  • 99 次串行 Recognize = 99 次 trackInit(同一 *OCR)
  • sync.Map.LoadOrStore 在 key 已存在时是 lock-free 路径, 避免 mutex.Lock + map 写入的固定开销
  • sync.Map 读写并发安全,无需额外的 initsMu 保护
  • Close 路径用 Range 迭代,配合 closeOnce 仍保证只跑一次 Close 工作

用 closeMu 保护整个「read closed + Get + trackInit」

原子临界区 + Close 的「Range(inits) + 翻 closed」原子临界区。
两个临界区互斥(同一把 mutex),保证并发 Recognize 不会被 close window 切断。
为什么不用 atomic.Bool.Load:atomic.Load + Get + trackInit 在 Go 内存模型下
不是原子的(Load 之后到后续语句之间 goroutine 可被调度走,Close 在此期间
完成 Range + 翻 closed,但 goroutine 已被 Load(false) 误导,仍会 trackInit
到 inits map 内 -> 泄漏)。所以需要 mutex 临界区。
简化:closeOnce 仍然存在(保证 Close 关键路径只跑一次 + 错误聚合),
closeMu 在 Close 内现在只保护临界区入口(进/出 closeMu),与 closeOnce 配合。

func NewPool added in v0.2.0

func NewPool(preload int) *Pool

NewPool 创建 OCR 实例池。 preload 参数已废弃,保留以保持 API 向后兼容。不再同步预热 ONNX session。 惰性初始化在首次 Recognize 时触发。

func (*Pool) Close added in v0.3.1

func (p *Pool) Close() error

Close 释放池中所有已完成惰性初始化的 OCR 实例 (ONNX session + 临时目录)。

注意:sync.Pool 持有的是结构体, 真正需要 Close 的是初始化过 session 的实例。 池内只跟踪首次完成初始化 (Recognize 后) 的实例, 避免漏释放或重复释放。

多实例池 (NewPool(N>1)) 下, 每个实例对应独立 tempDir, Close 会释放全部。

并发安全:用 sync.Once 保证"排空 map + 迭代 Close 实例"这一段 关键路径只跑一次。即使多个 goroutine 同时调 Close,第一次调用的协程 负责全部释放工作,后续调用立即返回 nil,避免同一实例被 Close 两次。

closeMu 保护「Range(inits) + 翻 closed」原子临界区,与 Recognize

路径的「读 closed + Get + trackInit」临界区互斥。任何并发 Recognize 要么:
  1) 在 Close 临界区之前完成 trackInit -> 被 Range 清理
  2) 在 Close 临界区之后拿 closeMu -> 看到 closed=true -> 直接返回错误
不会有"漏网"的 trackInit 留下幽灵实例。

Pool.inits 是 sync.Map(无独立 initsMu),Close 路径用 Range 原子快照迭代——sync.Map.Range 在迭代期间对后续 Load/Store 安全, 配合 sync.Once 保证排空分支只跑一次。

func (*Pool) Recognize added in v0.2.0

func (p *Pool) Recognize(imageData []byte) (result string, err error)

Recognize 从池中取一个 OCR 实例识别图片,用完归还。 不同实例并发安全(每个实例内部有独立 mu 保护 Classification)。

Pool.Close 后调用 Recognize 返回"OCR 池已关闭"错误,防止新创建的 OCR 实例泄漏 tempDir(Pool.Close 的 inits.Range 排空后再创建的实例 不会被 Close 路径清理)。

close 检查 + pool.Get + trackInit 必须在同一 mutex 临界区内。

否则并发 Recognize 可穿过 Close 的 Range 完成窗口,向 inits map
注册新实例但 Close 已不会再次访问 -> tempDir 永久泄漏。
具体场景:
  T0 Close 进入 closeOnce -> 拿 closeMu -> Range(inits) -> 翻 closed -> 放 closeMu
  T1 Recognize 拿 closeMu(在 T0 之后)-> 看到 closed=true -> 直接返回错误
  T1' Recognize 拿 closeMu(在 T0 之前)-> 看到 closed=false -> Get + trackInit
       -> 放 closeMu -> Close 路径(拿 closeMu)-> Range 包含 T1' 注册的实例
保证:T1 要么被 Close 之前完整处理(被 Close 清理),要么被 Close 之后拒绝。

o.Recognize 在 closeMu 外执行,但 OCR 级别有 atomic closed 二次检查 (在 o.mu 临界区内、Classification 前),形成两层防御:

  • 层 1(Pool):closeMu 保证 trackInit 窗口不泄漏
  • 层 2(OCR):atomic closed 二次检查保证永不访问已关闭 session

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL