FlanChanXwO commited on
Commit
8210731
·
verified ·
1 Parent(s): 1b180c3

Upload docs/INFERENCE.md with huggingface_hub

Browse files
Files changed (1) hide show
  1. docs/INFERENCE.md +149 -48
docs/INFERENCE.md CHANGED
@@ -1,12 +1,10 @@
1
- # 推理指南 (INFERENCE.md)
2
 
3
- > **English summary**: Model I/O — input `[batch,3,64,160]` float32 RGB (values 0..1),
4
- > output `[batch,4,10]` logits; argmax per position gives the 4 digits. Preprocessing is only
5
- > three steps: convert to RGB, resize to 160x64 (bilinear), divide by 255. No grayscale, no
6
- > denoise, no mean/std normalization. Runs in ~10-30ms on CPU. Go integration uses
7
- > `onnxruntime_go` with NCHW layout (all R, then all G, then all B).
8
 
9
- ## 规格速览
 
 
10
 
11
  | 项 | 值 |
12
  |---|---|
@@ -18,14 +16,10 @@
18
  | 解码 | 每位置 argmax → 数字 |
19
  | ONNX opset | 18 |
20
 
21
- **预处理只有 3 步,无其他**:
22
- 1. 转 RGB
23
- 2. 缩放到 160x64(双线性)
24
- 3. 除以 255
25
-
26
- 不做灰度、不做去噪、不做归一化均值方差。
27
 
28
- ## Python
29
 
30
  ```bash
31
  pip install onnxruntime pillow numpy
@@ -52,13 +46,12 @@ def solve_captcha(img_bytes_or_path, model_path="model.onnx"):
52
  logits = sess.run(None, {"input": x})[0] # (1,4,10)
53
  return "".join(str(int(logits[0, p].argmax())) for p in range(4))
54
 
55
- # 用法
56
  code = solve_captcha("captcha.png") # "4821"
57
  ```
58
 
59
- ## Go (onnxruntime_go)
60
 
61
- 依赖: `github.com/yalue/onnxruntime_go` + onnxruntime 共享库 (.dylib/.so)。
62
 
63
  ```go
64
  package main
@@ -85,12 +78,8 @@ type Solver struct {
85
  }
86
 
87
  func NewSolver(modelPath, libPath string) (*Solver, error) {
88
- if libPath != "" {
89
- ort.SetSharedLibraryPath(libPath)
90
- }
91
- if err := ort.InitializeEnvironment(); err != nil {
92
- return nil, err
93
- }
94
  in, err := ort.NewEmptyTensor[float32](ort.NewShape(1, 3, inH, inW))
95
  if err != nil { return nil, err }
96
  out, err := ort.NewEmptyTensor[float32](ort.NewShape(1, nDigits, 10))
@@ -105,48 +94,160 @@ func NewSolver(modelPath, libPath string) (*Solver, error) {
105
  func (s *Solver) Solve(png []byte) (string, error) {
106
  src, _, err := image.Decode(bytes.NewReader(png))
107
  if err != nil { return "", err }
108
- // RGB 缩放到 160x64 (双线性)
109
  dst := image.NewRGBA(image.Rect(0, 0, inW, inH))
110
  draw.ApproxBiLinear.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Over, nil)
111
 
112
  data := s.in.GetData()
113
  idx := 0
114
- for y := 0; y < inH; y++ {
115
- for x := 0; x < inW; x++ {
116
- r, g, b, _ := dst.At(x, y).RGBA()
117
- data[idx+0] = float32(r>>8) / 255.0
118
- data[idx+1] = float32(g>>8) / 255.0
119
- data[idx+2] = float32(b>>8) / 255.0
120
- idx += 3
121
- }
122
- }
123
- if err := s.sess.Run(); err != nil { return "", err }
124
 
 
125
  got := s.out.GetData()
126
  code := make([]byte, nDigits)
127
  for p := 0; p < nDigits; p++ {
128
  best := 0
129
- for c := 1; c < 10; c++ {
130
- if got[p*10+c] > got[p*10+best] { best = c }
131
- }
132
  code[p] = '0' + byte(best)
133
  }
134
  return string(code), nil
135
  }
136
  ```
137
 
138
- > 注意: `onnxruntime_go.NewSession` 从文件读取模型字节。若用 `go:embed`
139
- > 内嵌模型,改用 `NewSessionWithONNXData` 传字节。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
- ## 与 southplus-cli 集成
 
142
 
143
- 项目内已有 `internal/captcha/onnx_solver.go` 骨架,需按上面的 Go 示例适配:
144
- - 输入 3 通道 (非 1)
145
- - 去掉旧 CTC/去噪逻辑
146
- - 输出 4 位置 argmax (非 CTC 解码)
147
 
148
- ## 性能
 
 
149
 
150
- - 单张推理: 约 5-15ms (CPU, M1/现代 x86)
151
- - 全流程 (加载 + 预处理 + 推理): 约 10-30ms
152
- - 无 GPU 依赖, 内存占用 < 50MB
 
1
+ # 推理指南 / Inference Guide
2
 
3
+ ---
 
 
 
 
4
 
5
+ ## 中文
6
+
7
+ ### 规格速览
8
 
9
  | 项 | 值 |
10
  |---|---|
 
16
  | 解码 | 每位置 argmax → 数字 |
17
  | ONNX opset | 18 |
18
 
19
+ 预处理只有 3 步,无其他:RGB → 缩放到 160x64(双线性)→ 除以 255。
20
+ 不做灰度、不去噪、不做均值方差归一化。
 
 
 
 
21
 
22
+ ### Python
23
 
24
  ```bash
25
  pip install onnxruntime pillow numpy
 
46
  logits = sess.run(None, {"input": x})[0] # (1,4,10)
47
  return "".join(str(int(logits[0, p].argmax())) for p in range(4))
48
 
 
49
  code = solve_captcha("captcha.png") # "4821"
50
  ```
51
 
52
+ ### Go (onnxruntime_go)
53
 
54
+ 依赖:`github.com/yalue/onnxruntime_go` + onnxruntime 共享库(.dylib/.so)。
55
 
56
  ```go
57
  package main
 
78
  }
79
 
80
  func NewSolver(modelPath, libPath string) (*Solver, error) {
81
+ if libPath != "" { ort.SetSharedLibraryPath(libPath) }
82
+ if err := ort.InitializeEnvironment(); err != nil { return nil, err }
 
 
 
 
83
  in, err := ort.NewEmptyTensor[float32](ort.NewShape(1, 3, inH, inW))
84
  if err != nil { return nil, err }
85
  out, err := ort.NewEmptyTensor[float32](ort.NewShape(1, nDigits, 10))
 
94
  func (s *Solver) Solve(png []byte) (string, error) {
95
  src, _, err := image.Decode(bytes.NewReader(png))
96
  if err != nil { return "", err }
 
97
  dst := image.NewRGBA(image.Rect(0, 0, inW, inH))
98
  draw.ApproxBiLinear.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Over, nil)
99
 
100
  data := s.in.GetData()
101
  idx := 0
102
+ // 模型输入为 NCHW:先写全部 R,再 G,再 B
103
+ for y := 0; y < inH; y++ { for x := 0; x < inW; x++ { r,_,_,_ := dst.At(x,y).RGBA(); data[idx]=float32(r>>8)/255; idx++ } }
104
+ for y := 0; y < inH; y++ { for x := 0; x < inW; x++ { _,g,_,_ := dst.At(x,y).RGBA(); data[idx]=float32(g>>8)/255; idx++ } }
105
+ for y := 0; y < inH; y++ { for x := 0; x < inW; x++ { _,_,b,_ := dst.At(x,y).RGBA(); data[idx]=float32(b>>8)/255; idx++ } }
 
 
 
 
 
 
106
 
107
+ if err := s.sess.Run(); err != nil { return "", err }
108
  got := s.out.GetData()
109
  code := make([]byte, nDigits)
110
  for p := 0; p < nDigits; p++ {
111
  best := 0
112
+ for c := 1; c < 10; c++ { if got[p*10+c] > got[p*10+best] { best = c } }
 
 
113
  code[p] = '0' + byte(best)
114
  }
115
  return string(code), nil
116
  }
117
  ```
118
 
119
+ > 注意:`onnxruntime_go.NewSession` 从文件读模型。若用 `go:embed` 内嵌模型,
120
+ > 改用 `NewSessionWithONNXData` 传字节。
121
+
122
+ ### 性能
123
+
124
+ - 单张推理:约 5-15ms(CPU,M1/现代 x86)
125
+ - 全流程(加载+预处理+推理):约 10-30ms
126
+ - 无 GPU 依赖,内存占用 < 50MB
127
+
128
+ ---
129
+
130
+ ## English
131
+
132
+ ### Specification
133
+
134
+ | Item | Value |
135
+ |---|---|
136
+ | Input name | `input` |
137
+ | Input shape | `[batch, 3, 64, 160]` |
138
+ | Input type | float32, range [0,1] (RGB) |
139
+ | Output name | `logits` |
140
+ | Output shape | `[batch, 4, 10]` |
141
+ | Decode | argmax per position → digit |
142
+ | ONNX opset | 18 |
143
+
144
+ Preprocessing is only three steps: RGB → resize to 160x64 (bilinear) → divide by 255.
145
+ No grayscale, no denoising, no mean/std normalization.
146
+
147
+ ### Python
148
+
149
+ ```bash
150
+ pip install onnxruntime pillow numpy
151
+ ```
152
+
153
+ ```python
154
+ import numpy as np
155
+ import onnxruntime as ort
156
+ from PIL import Image
157
+
158
+ _SESSION = None
159
+
160
+ def get_session(model_path="model.onnx"):
161
+ global _SESSION
162
+ if _SESSION is None:
163
+ _SESSION = ort.InferenceSession(model_path, providers=["CPUExecutionProvider"])
164
+ return _SESSION
165
+
166
+ def solve_captcha(img_bytes_or_path, model_path="model.onnx"):
167
+ sess = get_session(model_path)
168
+ im = Image.open(img_bytes_or_path).convert("RGB").resize((160, 64), Image.BILINEAR)
169
+ x = np.asarray(im, dtype=np.float32) / 255.0
170
+ x = x.transpose(2, 0, 1)[None] # (1,3,64,160)
171
+ logits = sess.run(None, {"input": x})[0] # (1,4,10)
172
+ return "".join(str(int(logits[0, p].argmax())) for p in range(4))
173
+
174
+ code = solve_captcha("captcha.png") # "4821"
175
+ ```
176
+
177
+ ### Go (onnxruntime_go)
178
+
179
+ Deps: `github.com/yalue/onnxruntime_go` + the onnxruntime shared library (.dylib/.so).
180
+
181
+ ```go
182
+ package main
183
+
184
+ import (
185
+ "bytes"
186
+ "fmt"
187
+ "image"
188
+ _ "image/png"
189
+
190
+ "golang.org/x/image/draw"
191
+ ort "github.com/yalue/onnxruntime_go"
192
+ )
193
+
194
+ const (
195
+ inW, inH = 160, 64
196
+ nDigits = 4
197
+ )
198
+
199
+ type Solver struct {
200
+ sess *ort.Session[float32]
201
+ in *ort.Tensor[float32]
202
+ out *ort.Tensor[float32]
203
+ }
204
+
205
+ func NewSolver(modelPath, libPath string) (*Solver, error) {
206
+ if libPath != "" { ort.SetSharedLibraryPath(libPath) }
207
+ if err := ort.InitializeEnvironment(); err != nil { return nil, err }
208
+ in, err := ort.NewEmptyTensor[float32](ort.NewShape(1, 3, inH, inW))
209
+ if err != nil { return nil, err }
210
+ out, err := ort.NewEmptyTensor[float32](ort.NewShape(1, nDigits, 10))
211
+ if err != nil { in.Destroy(); return nil, err }
212
+ sess, err := ort.NewSession[float32](modelPath,
213
+ []string{"input"}, []string{"logits"},
214
+ []*ort.Tensor[float32]{in}, []*ort.Tensor[float32]{out})
215
+ if err != nil { in.Destroy(); out.Destroy(); return nil, err }
216
+ return &Solver{sess, in, out}, nil
217
+ }
218
+
219
+ func (s *Solver) Solve(png []byte) (string, error) {
220
+ src, _, err := image.Decode(bytes.NewReader(png))
221
+ if err != nil { return "", err }
222
+ dst := image.NewRGBA(image.Rect(0, 0, inW, inH))
223
+ draw.ApproxBiLinear.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Over, nil)
224
+
225
+ data := s.in.GetData()
226
+ idx := 0
227
+ // NCHW layout: all R, then all G, then all B
228
+ for y := 0; y < inH; y++ { for x := 0; x < inW; x++ { r,_,_,_ := dst.At(x,y).RGBA(); data[idx]=float32(r>>8)/255; idx++ } }
229
+ for y := 0; y < inH; y++ { for x := 0; x < inW; x++ { _,g,_,_ := dst.At(x,y).RGBA(); data[idx]=float32(g>>8)/255; idx++ } }
230
+ for y := 0; y < inH; y++ { for x := 0; x < inW; x++ { _,_,b,_ := dst.At(x,y).RGBA(); data[idx]=float32(b>>8)/255; idx++ } }
231
+
232
+ if err := s.sess.Run(); err != nil { return "", err }
233
+ got := s.out.GetData()
234
+ code := make([]byte, nDigits)
235
+ for p := 0; p < nDigits; p++ {
236
+ best := 0
237
+ for c := 1; c < 10; c++ { if got[p*10+c] > got[p*10+best] { best = c } }
238
+ code[p] = '0' + byte(best)
239
+ }
240
+ return string(code), nil
241
+ }
242
+ ```
243
 
244
+ > Note: `onnxruntime_go.NewSession` reads the model from a file. If you embed it with
245
+ > `go:embed`, use `NewSessionWithONNXData` with the bytes instead.
246
 
247
+ ### Performance
 
 
 
248
 
249
+ - Single inference: ~5-15ms (CPU, M1/modern x86)
250
+ - Full flow (load + preprocess + infer): ~10-30ms
251
+ - No GPU needed, memory < 50MB
252
 
253
+ ---