File size: 3,983 Bytes
5e14b6c
 
3739346
 
 
 
 
5e14b6c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# 推理指南

## 使用范围

本指南仅适用于自有或已获明确授权测试的 PHPWind 部署。下列示例仅执行本地图片推理,
不用于自动化账户登录或绕过访问控制。

## 规格速览

| 项 | 值 |
|---|---|
| 输入名 | `input` |
| 输入形状 | `[batch, 3, 64, 160]` |
| 输入类型 | float32, 值域 [0,1] (RGB) |
| 输出名 | `logits` |
| 输出形状 | `[batch, 4, 10]` |
| 解码 | 每位置 argmax → 数字 |
| ONNX opset | 18 |

预处理只有 3 步,无其他:RGB → 缩放到 160x64(双线性)→ 除以 255。
不做灰度、不去噪、不做均值方差归一化。

## Python

```bash
pip install onnxruntime pillow numpy
```

```python
import numpy as np
import onnxruntime as ort
from PIL import Image

_SESSION = None

def get_session(model_path="model.onnx"):
    global _SESSION
    if _SESSION is None:
        _SESSION = ort.InferenceSession(model_path, providers=["CPUExecutionProvider"])
    return _SESSION

def solve_captcha(img_bytes_or_path, model_path="model.onnx"):
    sess = get_session(model_path)
    im = Image.open(img_bytes_or_path).convert("RGB").resize((160, 64), Image.BILINEAR)
    x = np.asarray(im, dtype=np.float32) / 255.0
    x = x.transpose(2, 0, 1)[None]          # (1,3,64,160)
    logits = sess.run(None, {"input": x})[0] # (1,4,10)
    return "".join(str(int(logits[0, p].argmax())) for p in range(4))

code = solve_captcha("captcha.png")   # "4821"
```

## Go (onnxruntime_go)

依赖:`github.com/yalue/onnxruntime_go` + onnxruntime 共享库(.dylib/.so)。

```go
package main

import (
    "bytes"
    "image"
    _ "image/png"

    "golang.org/x/image/draw"
    ort "github.com/yalue/onnxruntime_go"
)

const (
    inW, inH = 160, 64
    nDigits  = 4
)

type Solver struct {
    sess *ort.Session[float32]
    in   *ort.Tensor[float32]
    out  *ort.Tensor[float32]
}

func NewSolver(modelPath, libPath string) (*Solver, error) {
    if libPath != "" { ort.SetSharedLibraryPath(libPath) }
    if err := ort.InitializeEnvironment(); err != nil { return nil, err }
    in, err := ort.NewEmptyTensor[float32](ort.NewShape(1, 3, inH, inW))
    if err != nil { return nil, err }
    out, err := ort.NewEmptyTensor[float32](ort.NewShape(1, nDigits, 10))
    if err != nil { in.Destroy(); return nil, err }
    sess, err := ort.NewSession[float32](modelPath,
        []string{"input"}, []string{"logits"},
        []*ort.Tensor[float32]{in}, []*ort.Tensor[float32]{out})
    if err != nil { in.Destroy(); out.Destroy(); return nil, err }
    return &Solver{sess, in, out}, nil
}

func (s *Solver) Solve(png []byte) (string, error) {
    src, _, err := image.Decode(bytes.NewReader(png))
    if err != nil { return "", err }
    dst := image.NewRGBA(image.Rect(0, 0, inW, inH))
    draw.ApproxBiLinear.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Over, nil)

    data := s.in.GetData()
    idx := 0
    // 模型输入为 NCHW:先写全部 R,再 G,再 B
    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++ } }
    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++ } }
    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++ } }

    if err := s.sess.Run(); err != nil { return "", err }
    got := s.out.GetData()
    code := make([]byte, nDigits)
    for p := 0; p < nDigits; p++ {
        best := 0
        for c := 1; c < 10; c++ { if got[p*10+c] > got[p*10+best] { best = c } }
        code[p] = '0' + byte(best)
    }
    return string(code), nil
}
```

> 注意:`onnxruntime_go.NewSession` 从文件读模型。若用 `go:embed` 内嵌模型,改用 `NewSessionWithONNXData` 传字节。

## 性能

- 单张推理:约 5-15ms(CPU,M1/现代 x86)
- 全流程(加载+预处理+推理):约 10-30ms
- 无 GPU 依赖,内存占用 < 50MB